События

Создание интерактивного веб-приложения

2014-05-08

С помощью движка Blend4Web можно создавать как простые, так и относительно сложные, насыщенные интерактивные веб-приложения. Рассмотрим создание простого приложения с использованием API Blend4Web, в котором реализуем взаимодействие с объектами 3D-сцены, например, анимируя объект при выделении его мышью.

Создание нового приложения

Для создания нового приложения следует воспользоваться менеджером проектов, входящим в состав Blend4Web SDK. Для этого требуется сначала запустить Blender (с установленным аддоном), а затем зайти на главную страницу SDK по адресу http://localhost:6687/. Там менеджер проектов будет доступен по соответствующей ссылке:

Далее следует выбрать Create New Project:

Далее нужно будет выбрать имя нового проекта, к примеру, "simple_app". В случае, если вдруг проект с таким именем уже существует, будет предложено ввести другое имя. Также можно ввести отображаемый в браузере заголовок приложения, например, "Interactive Web Application":

Остальные настройки оставим по умолчанию. После этого нажмем кнопку Create Project внизу данной страницы для создания приложения. Затем необходимо дождаться завершения и нажать Back to Projects:

Она вернет нас на страницу менеджера проектов, из которой можно открыть новое приложение по ссылке:

В результате мы увидим базовую сцену с простым кубиком:

Созданное нами приложение расположено внутри SDK по пути ./projects/simple_app/. Теперь, когда каркас проекта готов, можно приступать к созданию 3D-сцены и написанию логики приложения.

Подготовка и экспорт 3D-сцены

Blend-файл с базовой сценой находится в директории проекта: ./projects/simple_app/simple_app.blend. Откроем его в Блендере и вместо того, что есть по умолчанию, создадим в нем сцену с объектами, источником освещения и камерой.



Для каждого из "интерактивных" объектов сделаем простую анимацию перемещения, поворота (в режимах XYZ Euler либо Quaternion WXYZ) или масштабирования.



Также включим им опцию Selectable во вкладке Object->Selection and Outlining. Это сделает объекты доступными для выбора при клике по ним мышью.



Настроив по вкусу камеру, освещение и материалы объектов, экспортируем сцену через команду меню File -> Export -> Blend4Web (.json). Путь экспорта следует указать таким: ./projects/simple_app/assets/simple_app.json.

Добавление функционала

В директории проекта ./projects/simple_app/ в числе других будет находиться главный JS-файл приложения simple_app.js, содержащий шаблонный код. В нем и будем реализовывать простое взаимодействие пользователя с загруженной сценой.

Для реализации задуманного функционала нам понадобятся дополнительные модули:

- animation.js - предоставляет API для управления анимацией объектов

- container.js - методы, относящиеся к Canvas-элементу и его родительскому HTML-элементу

- mouse.js - содержит ряд полезных методов, относящихся к мыши

- scenes.js - API для доступа к объектам сцены

Добавим их подключение в самом начале скрипта к остальным модулям:

// import modules used by the app
var m_app       = require("app");
var m_cfg       = require("config");
var m_data      = require("data");
var m_preloader = require("preloader");
var m_ver       = require("version");

var m_anim      = require("animation");
var m_cont      = require("container");
var m_mouse     = require("mouse");
var m_scenes    = require("scenes");

В нашем примере по клику на объекте должна воспроизводиться его анимация, также будем и отменять анимацию у предыдущего выделенного объекта. Для этого в конец функции load_cb() добавим обработчик события "mousedown" для нажатий мышью по элементу Canvas. Также при необходимости можно добавить обработчик "touchstart" для нажатий на сенсорном экране:

function load_cb(data_id, success) {
    ...
    var canvas_elem = m_cont.get_canvas();
    canvas_elem.addEventListener("mousedown", main_canvas_click, false);
    canvas_elem.addEventListener("touchstart", main_canvas_click, false);
}

Далее напишем соответствующую функцию-обработчик main_canvas_click():

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

    var x = m_mouse.get_coords_x(e);
    var y = m_mouse.get_coords_y(e);

    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);
    }
}

Основная логика приложения содержится именно в ней. Алгоритм в общих чертах будет следующим:

1) Получение объекта сцены, который находился под курсором.

var x = m_mouse.get_coords_x(e);
var y = m_mouse.get_coords_y(e);

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

2) Отмена анимации у предыдущего выделенного объекта.

Остановка анимации:

m_anim.stop(_previous_selected_obj);

И выставление нулевого кадра, т.е. возвращение к первоначальному положению:

m_anim.set_frame(_previous_selected_obj, 0);

Для сохранения предыдущего объекта использована глобальная переменная _previous_selected_obj. Её нужно объявить где-либо, например, сразу после подключения модулей следующим образом:

var _previous_selected_obj = null;

3) Применение анимации к объекту.

Загрузка и расчет анимации, назначенной на объект в Blender'е:

m_anim.apply_def(obj);

Воспроизведение анимации:

m_anim.play(obj);

Приведём код получившегося приложения целиком:

"use strict"

// register the application module
b4w.register("simple_app_main", function(exports, require) {

// import modules used by the app
var m_app       = require("app");
var m_cfg       = require("config");
var m_data      = require("data");
var m_preloader = require("preloader");
var m_ver       = require("version");

var m_anim      = require("animation");
var m_cont      = require("container");
var m_mouse     = require("mouse");
var m_scenes    = require("scenes");

var _previous_selected_obj = null;

// detect application mode
var DEBUG = (m_ver.type() == "DEBUG");

// automatically detect assets path
var APP_ASSETS_PATH = m_cfg.get_assets_path("simple_app");

/**
 * export the method to initialize the app (called at the bottom of this file)
 */
exports.init = function() {
    m_app.init({
        canvas_container_id: "main_canvas_container",
        callback: init_cb,
        show_fps: DEBUG,
        console_verbose: DEBUG,
        autoresize: true
    });
}

/**
 * callback executed when the app is initialized 
 */
function init_cb(canvas_elem, success) {

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

    m_preloader.create_preloader();

    // ignore right-click on the canvas element
    canvas_elem.oncontextmenu = function(e) {
        e.preventDefault();
        e.stopPropagation();
        return false;
    };

    load();
}

/**
 * load the scene data
 */
function load() {
    m_data.load(APP_ASSETS_PATH + "simple_app.json", load_cb, preloader_cb);
}

/**
 * update the app's preloader
 */
function preloader_cb(percentage) {
    m_preloader.update_preloader(percentage);
}

/**
 * callback executed when the scene data is loaded
 */
function load_cb(data_id, success) {

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

    m_app.enable_camera_controls();

    // place your code here
    var canvas_elem = m_cont.get_canvas();
    canvas_elem.addEventListener("mousedown", main_canvas_click, false);
    canvas_elem.addEventListener("touchstart", main_canvas_click, false);
}

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

    var x = m_mouse.get_coords_x(e);
    var y = m_mouse.get_coords_y(e);

    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);
    }
}


});

// import the app module and start the app by calling the init method
b4w.require("simple_app_main").init();


Веб-приложение, разобранное в статье, является простым, но в то же время интерактивным. Это минимальный пример того, что позволяет движок Blend4Web и его API.

Исходные файлы приложения и сцены находятся в составе бесплатного дистрибутива Blend4Web SDK по пути ./projects/simple_app/.

Ссылка на приложение в отдельном окне

Изменения

[2014-05-08] Изначальная публикация.

[2014-06-30] Уточнен текст об опции инициализации alpha.

[2014-07-07] Незначительные правки текста об исходных файлах приложения.

[2014-07-22] Изменен путь к приложению.

[2014-08-06] Обновлен код примера по причине изменения API получения объекта под курсором.

[2014-10-29] Обновлен текст статьи по причине изменения API.

[2014-11-28] Правки текста об исходных файлах приложения.

[2015-04-23] Правки текста об исходных файлах приложения.

[2015-05-08] Обновлен текст статьи по причине изменения API.

[2015-09-30] Небольшие изменения в статье.

[2015-10-02] Статья переписана с учетом системы управления проектами.

[2017-01-12] Исправлены некорректные/битые ссылки.

[2017-04-21] Обновлен текст статьи.