События

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

2014-05-08

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

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

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

Далее следует выбрать Projects[+new].

Далее, заполнив поля как показано ниже, мы зададим имя нового проекта (simple_app), укажем, что проект будет создан вместе с минимально необходимыми для полноценного приложения ресурсами, а также для простоты потребуем, чтобы все файлы проекта были расположены в одной директории. Также можно ввести отображаемый в браузере заголовок приложения (Interactive Web Application).

Осталось только нажать Create для непосредственно создания приложения. После этого открыть его можно будет по ссылке со страницы менеджера проектов:

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

Созданное нами приложение расположено внутри SDK по пути ./apps_dev/ИМЯ_ПРОЕКТА/. В нашем случае это будет ./apps_dev/simple_app/. В директории проекта в числе других будут находиться основные файлы приложения:

- simple_app.html - главный html-файл приложения

- simple_app.js - js-скрипт, содержащий логику приложения

- simple_app.css - файл стилей

- simple_app.blend - основной blend-файл приложения

- simple_app.json/simple_app.bin - файлы экспортированной 3D-сцены

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

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

Создадим в Blender'е сцену с объектами на ней, источником освещения и камерой.



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



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



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

Шаблон приложения

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

Исходный текст simple_app.js:

"use strict"

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

// import modules used by the app
var m_app       = require("app");
var m_data      = require("data");

/**
 * 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: true,
        console_verbose: true,
        autoresize: true
    });
}

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

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

    load();
}

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

/**
 * callback executed when the scene is loaded
 */
function load_cb(data_id) {
    m_app.enable_controls();
    m_app.enable_camera_controls();

    // place your code here

}


});

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

Разберём подробнее работу этого скрипта:

а) Регистрация пользовательских модулей приложения

Типичное приложение на движке имеет модульную структуру, при которой пользовательские скрипты оформляются в виде модулей и регистрируются движком:

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

Здесь важны 2 параметра:

- exports - глобальный массив; служит для доступа к функциям модуля извне

- require - функция для подгрузки других модулей


б) Подключение модулей движка

По умолчанию загружаются необходимые модули движка:

b4w.register("simple_app", function(exports, require) {
    ...
// import modules used by the app
var m_app       = require("app");
var m_data      = require("data");
    ...
});

- app.js - служит для упрощения инициализации приложения

- data.js - API для загрузки данных 3D-сцены


в) Инициализация приложения

Старт приложения начинается с функции exports.init(). Она объявлена через массив exports для вызова извне модуля:

b4w.register("simple_app", function(exports, require) {
    ...
/**
 * 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: true,
        console_verbose: true,
        autoresize: true
    });
}
    ...
}); 

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

Используемая здесь функция m_app.init() создаст HTML-элемент Canvas и выполнит все необходимые действия для инициализации WebGL. Она может принимать множество настроек, в частности следующие:

- canvas_container_id - id HTML-элемента, внутри которого будет создан Canvas; по умолчанию используется элемент с id main_canvas_container, присутствующий в файле simple_app.html

- callback - функция, вызываемая по завершении инициализации

- show_fps - отображать счетчик кадров

- console_verbose - выводить служебные сообщения в консоль

- autoresize - автоматическое изменение размеров Canvas-элемента согласно размерам окна браузера

Примечание

Вызов exports.init() произойдет асинхронно, во время загрузки страницы. Функция m_app.init() назначит инициализацию на событие window.onload, соответственно после этого будет полностью доступно все DOM-дерево.


г) Загрузка сцены

По завершении инициализации будет вызвана функция init_cb(), указанная в настройке callback, как описано в предыдущем пункте. В этот момент основная сцена ещё не загружена, но уже можно осуществлять какие-либо подготовительные действия:

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

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

    load();
}

Параметры функции:

- canvas_elem - созданный HTML-элемент Canvas

- success - флаг успешности создания и инициализации элемента Canvas

Здесь же в функции load() происходит и загрузка сцены:

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

Здесь с использованием модуля data.js загружается сцена simple_app.json. Первым параметром является путь к JSON-файлу относительно HTML-файла, вторым - функция, вызываемая после загрузки данных 3D-сцены.

Вызов функции load_cb() означает переход от загрузки к началу работы приложения и соответственно началу рендеринга сцены. По умолчанию здесь включается обработка пользовательского ввода и управление камерой:

/**
 * callback executed when the scene is loaded
 */
function load_cb(data_id) {
    m_app.enable_controls();
    m_app.enable_camera_controls();

    // place your code here

}

Так как этот момент - самый ранний, когда будут доступны данные 3D-сцены, то это подходящее место для инициализации и подготовки всего, что связано со сценой, объектами, анимацией и т.д.


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

Имея шаблон нашего будущего приложения, реализуем простое взаимодействие пользователя с загруженной сценой.

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

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

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

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

// import modules used by the app
var m_anim      = require("animation");
var m_app       = require("app");
var m_data      = require("data");
var m_scenes    = require("scenes");

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

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

Основная логика содержится в функции main_canvas_click(). Алгоритм в общих чертах будет следующим:

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

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

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.

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

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

m_anim.apply_def(obj);

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

m_anim.play(obj);

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

"use strict"

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

// import modules used by the app
var m_anim      = require("animation");
var m_app       = require("app");
var m_data      = require("data");
var m_scenes    = require("scenes");

var _previous_selected_obj = null;

/**
 * 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: true,
        console_verbose: true,
        autoresize: true
    });
}

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

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

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

    load();
}

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

/**
 * callback executed when the scene is loaded
 */
function load_cb(data_id) {
    m_app.enable_controls();
    m_app.enable_camera_controls();

    // place your code here

}

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


});

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


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

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

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

Изменения

[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] Статья переписана с учетом системы управления проектами.