en
ru
en

Blog

Tutorial: Creating an Interactive Web Application

2014-05-08

0
0
0
0

Blend4Web makes it possible to create both simple and relatively complex interactive rich internet applications. Let take a look at creating a simple application using the Blend4Web API. In this application we will implement interactions with 3D objects, for example, animating them on mouse click.

We will keep all the application's resources in one directory for convenience.

In general the application will consist of:

1) main HTML file

2) engine modules required for launch and work

3) 3D scene files exported with the Blend4Web Blender addon

4) JavaScript files which constitute the application's logic

The HTML file and the engine modules

The HTML file will look rather simple:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <script type="text/javascript" src="b4w.min.js"></script>
    <script type="text/javascript" src="app.js"></script>
    <script type="text/javascript" src="example.js"></script>
    <style>
        body {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>
<div id="canvas3d"></div>
</body>
</html>

We will need to link the minified version of the engine b4w.min.js and the engine addon app.js which simplifies the application initialization. They are part of the Blend4Web SDK, and can be copied and placed near the HTML file, for example.

Note

The minified version of the engine is available here: external/deploy/apps/common/b4w.min.js.

The addons are located in the src/addons/ directory.

We also link the example.js script, containing the logic of the future application.

It is enough to create a container for the Canvas element inside the HTML page:

<div id="canvas3d"></div>

Preparing and exporting the 3D scene

We'll create a scene with objects, light sources and a camera in Blender.



For each of the "interactive" objects we'll make a simple animation of movement, turning (only in the Quaternion WXYZ mode) and scaling.



We'll also enable the Selectable checkbox under the Object->Blend4Web tab. This will make the objects selectable with a mouse click.



Having set the camera, lighting and the objects' materials to your liking, we'll export the scene using the File -> Export -> Blend4Web (.json) menu option to where the main HTML file is located.

Initialiazation, loading and the application's work logic

The loading and working of the application will be controlled by the example.js script. Lets look at it in more detail.

The source code of example.js:

"use strict";

b4w.register("example_main", function(exports, require) {

var m_anim   = require("animation");
var m_app    = require("app");
var m_data   = require("data");
var m_main   = require("main");
var m_scenes = require("scenes");

var _previous_selected_obj = null;


exports.init = function() {
    m_app.init({
        canvas_container_id: "canvas3d", 
        callback: init_cb,
        physics_enabled: false,
        alpha: false
    });
}

function init_cb(canvas_elem, success) {

    if (!success) {
        console.log("b4w init failure");
        return;
    }

    m_app.enable_controls(canvas_elem);
    canvas_elem.addEventListener("mousedown", main_canvas_click, false);

    window.onresize = on_resize;
    on_resize();
    load();
}

function load() {
    m_data.load("example.json", load_cb);
}

function load_cb(data_id) {
    m_app.enable_camera_controls();
}

function on_resize() {
    var w = window.innerWidth;
    var h = window.innerHeight;
    m_main.resize(w, h);
};

function main_canvas_click(e) {
    if (e.preventDefault)
        e.preventDefault();

    var x = e.clientX;
    var y = e.clientY;

    var obj = m_scenes.pick_object(x, y);

    if (obj) {
        if (_previous_selected_obj) {
            m_anim.stop(_previous_selected_obj);
            m_anim.set_frame(_previous_selected_obj, 0);
        }
        _previous_selected_obj = obj;

        m_anim.apply_def(obj);
        m_anim.play(obj);
    }
}

});

b4w.require("example_main").init();

a) Registering the module

Our script is actually a module of the engine, which allows us to interact conveniently with other modules. This is what the following construction serves for:

b4w.register("example_main", function(exports, require) {
    ...
});

Two parameters are important here:

- exports - the global array; serves for accessing the module functions from the outside

- require - the function for loading other modules


b) Loading the neccessary modules

We need several modules to implement the functionality:

- animation.js - provides the API for controlling the objects' animation

- app.js - as mentioned above, serves to simplify the application's initialization

- data.js - API for loading the 3D scene's data

- main.js - API for initializing and changing the global parameters of the application

- scenes.js - API for accessing the scene's objects


b4w.register("example_main", function(exports, require) {
    ...
    var m_anim   = require("animation");
    var m_app    = require("app");
    var m_data   = require("data");
    var m_main   = require("main");
    var m_scenes = require("scenes");
    ...
});

Note

The app.js addon is linked to the HTML file separately as described above, the other modules are inside the compressed version of the engine.


c) Initializing the application

The application is initialized with the exports.init() function, which can be called from the outside:

b4w.register("example_main", function(exports, require) {
    ...
    exports.init = function() {
        m_app.init({
            canvas_container_id: "canvas3d", 
            callback: init_cb,
            physics_enabled: false,
            alpha: false
        });
    }
    ...
}); 
b4w.require("example_main").init();

The m_app.init() function which is used here will create the Canvas HTML element and will initialize WebGL. It takes many settings but for our example only the following are important:

- canvas_container_id - id of the HTML element inside which the Canvas element will be created

- callback - function which is called upon the initialization end

- physics_enabled - whether the application will use the physics engine

- alpha - the Canvas transparency is not needed (the whole scene is inside the background black cube)

Note

The exports.init() call will happen asynchronously, upon page load. The m_app.init() function will assign the initialization to the window.onload event to make sure that the whole DOM tree is available.


d) Loading the scene and preparing the application

After initialization is finished, the init_cb() will be called. This function is specified in the callback setting as described in the previous paragraph. It is already possible to load the scene and perform other preparatory actions inside it.

function init_cb(canvas_elem, success) {

    if (!success) {
        console.log("b4w init failure");
        return;
    }

    m_app.enable_controls(canvas_elem);
    canvas_elem.addEventListener("mousedown", main_canvas_click, false);

    window.onresize = on_resize;
    on_resize();
    load();
}

Function arguments:

- canvas_elem - the created Canvas HTML element

- success - flag showing that Canvas is successfully created and initialized


Scene loading

Scene loading is performed using the call:

m_data.load("example.json", load_cb);

Here the example.json scene is loaded using the data.js module. The first argument is the path to the JSON file relative to the HTML file, and the second - the function which is called after the 3D scene data is loaded.

In the example loading is performed with the load() method:

function init_cb(canvas_elem, success) {
    ...
    load();
    ...
}

function load() {
    m_data.load("example.json", load_cb);
}

function load_cb(data_id) {
    ...
}

Setting up dynamic resolution for Canvas

"Fullscreen" applications, which need to occupy the whole browser window even if its dimensions are changing, require a dynamic resolution for Canvas.

The resolution can be set up using the following method:

m_main.resize(width, height);

To "react" to the browser's behavior we'll use the window.onresize event callback:

function init_cb(canvas_elem, success) {
    ...
    window.onresize = on_resize;
    on_resize();
    ...
}

function on_resize() {
    var w = window.innerWidth;
    var h = window.innerHeight;
    m_main.resize(w, h);
};

In our example the Canvas will always follow the dimensions of the window object.

Note

By Canvas resolution we mean its width and height attributes corresponding to the Canvas size, not the width and height CSS rules.


Setting up the user input

In the init_cb() we'll set up the standard engine's callbacks for the mouse and keyboard input events:

function init_cb(canvas_elem, success) {
    ...
    m_app.enable_controls(canvas_elem);
    ...
}

We'll also enable the camera controls. We'll do it in the load_cb(), which is called after the 3D scene data is loaded, because the camera as a scene's object will be available only after that moment:

function load_cb(data_id) {
    m_app.enable_camera_controls();
}

e) User interaction with the scene's objects

In our example an animation should be played upon clicking an object. We'll also cancel the animation for the previously selected object.

We'll create for example the "mousedown" event callback to interact with the scene objects with a mouse click:

function init_cb(canvas_elem, success) {
    ...
    canvas_elem.addEventListener("mousedown", main_canvas_click, false);
    ...
}        

function main_canvas_click(e) {
    if (e.preventDefault)
        e.preventDefault();

    var x = e.clientX;
    var y = e.clientY;

    var obj = m_scenes.pick_object(x, y);

    if (obj) {
        if (_previous_selected_obj) {
            m_anim.stop(_previous_selected_obj);
            m_anim.set_frame(_previous_selected_obj, 0);
        }
        _previous_selected_obj = obj;

        m_anim.apply_def(obj);
        m_anim.play(obj);
    }
}

The main logic is contained in the main_canvas_click() function. In short, the algorithm will be as follows:

1) Get the object under the cursor.

var x = e.clientX;
var y = e.clientY;

var obj = m_scenes.pick_object(x, y);

2) Cancel the animation for the previously selected object.

Stop the animation:

m_anim.stop(_previous_selected_obj);

Set the frame number to zero i.e. return to the original state:

m_anim.set_frame(_previous_selected_obj, 0);

The _previous_selected_obj global variable is used to save the previous object.

3) Apply the animation to the object.

Load and precompute the animation which is assigned to the object in Blender:

m_anim.apply_def(obj);

Animation playback:

m_anim.play(obj);


The web application described in this article is simple but still interactive. This is a minimal example of what is possible with Blend4Web and its API.

The source files of the application and the scene are part of the free Blend4Web SDK distribution.

Link to the standalone application

[Edit 2014-06-30] Refined text about the alpha initialization option.

[Edit 2014-07-07] Minor changes in text about the application source files.

[Edit 2014-07-22] Path to the application changed.

[Edit 2014-08-06] Updated the example code because of picking API change.