Blog

Basic Manipulations in 3D Space

2016-08-02

You can learn a lot from the numerous demo applications included in the Blend4Web SDK. But, for a novice developer, it can be difficult to understand the intricacies of program code and find answers to questions he may have. Sometimes, it takes a lot of time to understand basic principles. I hope that the experience I've gained will help beginners develop their projects faster.

In this lesson I will focus on basic object manipulations in 3D space. It may seem strange, but this is what people have trouble with most often.

Let's start from the very beginning. All objects in a scene can, from a Blend4Web standpoint, be separated into two categories: dynamic and static objects. By default, all objects in a scene are static. And this is done for a very important reason.

A static object is still, unmoving, invariable. Thanks to this, Blend4Web can render it much faster using various tricks.

A dynamic object, on the other hand, is completely at your service, but you have to prepare it first. This is performed while creating the scene in Blender. All you have to do is select an object and enable the Force Dynamic Object option found on the Object panel.

Simply check this option, and the object is ready to use.

Blend4Web offers you a vast number of functions separated into several thematic groups known as modules. The transform and physics modules are used for manipulating objects in 3D space.

The first module contains somewhat low-level functions for accessing object parameters, and the second one was made to use the physics engine. The physics module offers convenient methods of controlling objects of physics world and it should be used to imitate real physical movement but this is a subject of a separate article. In this lesson, we will be looking exclusively at the transform functions.

In order to work with the API functions, you first have to plug in the module you need to the script where you are planning to use it. Here's how it is done:

var m_trans = require("transform");
Now you can address transform methods using the newly created m_trans variable.

Moving Objects

Let's start with the easiest task. Introducing the «teleport» effect!

Hop-hop! Portal at work!

The procedure to instantaneously transport an object to a different place is ridiculously easy. A dedicated function set_translation_v() is used to do this. However, before doing so you have to take certain steps: find an object in the scene and create the transform vector.

The most convenient way to find an object is to use its name. To do this, we will need the get_object_by_name() function that can be found in the scenes module.

Considering everything mentioned above, the procedure of translating an object should look like this:

// import modules
var m_trans = require("transform");
var m_scene = require("scenes");

// move
var obj = m_scene.get_object_by_name("My object");
var trans = [0,1,0];
m_trans.set_translation_v(obj, trans);

Executing this code will instantaneously move an object to the new coordinates specified by the trans variable. If at the beginning its coordinates are [-1,1,0], it will move alongside X axis, that is to the right. Keep in mind that Blender's and Blend4Web's coordinate systems are different. The description can be found here.

Simple movement.

Let's make the task a bit more complicated. Let's assume that we need to move an object alongside certain coordinate axis at a certain speed. It looks simple, but don't hurry. We will have to guarantee the stability of movement regardless of the system's power. Applications, in which objects move at different velocities depending on a system's performance, tend to be disappointing.

Blend4Web's sensors and manifolds are perfectly suited for this task. A very powerful event system, created by the authors of the engine, is hidden behind these scary words. But in this lesson we will limit ourselves to using a ready-made construction with an elapsed sensor.

// import modules
var m_trans  = require("transform");
var m_scene  = require("scenes");
var m_ctl    = require("controls");

//create sensor
var elapsed_sensor = m_ctl.create_elapsed_sensor();
m_ctl.create_sensor_manifold(null, "MAIN", m_ctl.CT_CONTINUOUS, [elapsed_sensor], null, main_cb);

function main_cb (obj, id) {
    var elapsed = m_ctl.get_sensor_value(obj, id, 0);

    //move
}

This code constantly fires the main_cb callback after rendering each frame. This template is all-purpose and is perfectly suited for cyclical tasks. Take note of it!

To move an object along a certain coordinate axis at a constant velocity, you can add the following code to the main_cb function:

//variables initialization
var speed = 0.1;
var obj = m_scene.get_object_by_name("My object");

function main_cb (obj, id) {
    var elapsed = m_ctl.get_sensor_value(obj, id, 0); 

    //move    
    m_trans.move_local(obj, speed*elapsed, 0, 0);	
}

This procedure changes one of the coordinates based on the speed step and the elapsed correction value. Try to use this code, and you will see that the object obediently crawls across the screen at an unchanged speed. Please note that the move_local() function works in the local coordinate system.

Hey, wait! I'm falling behind...

And now let's try to make objects play tag, and recall some school algebra. Directional vector to an object equals… hmmm… Well, here are the prepared formulas:

delta_trans = (target - position) * speed
position = position + delta_trans

We will have to perform various mathematical operations to calculate the vectors, so the list of the modules we use now looks more impressive:

// import modules
var m_trans = require("transform");
var m_scene = require("scenes");
var m_ctl   = require("controls");
var m_vec3  = require("vec3");

In the transform module, there is the get_translation() function, which is the polar opposite of the set_translation_v() method. This function returns the object's coordinates. Let's add several more mathematical calculations, and the formulas we mentioned will be transformed into the program code below:

//variables initialization
var delta_trans = new Float32Array(3); 
var speed = 0.7; 
var obj = m_scene.get_object_by_name("My object"); 
var obj_pos = new Float32Array(3);
var target = m_scene.get_object_by_name("Target");
var target_pos = new Float32Array(3);

function main_cb (obj, id) {
    var elapsed = m_ctl.get_sensor_value(obj, id, 0); 

    //move
    m_trans.get_translation(obj, obj_pos); 
    m_trans.get_translation(target, target_pos); 
    m_vec3.subtract(target_pos, obj_pos, delta_trans); 
    m_vec3.normalize(delta_trans,delta_trans); 
    m_vec3.scale(delta_trans, elapsed * speed, delta_trans);
    m_vec3.add(obj_pos, delta_trans, obj_pos); 
    m_trans.set_translation_v(obj, obj_pos); 
}

This code makes an object move at a constant speed. But if you want to smoothly slow down its movement, remove the lines that call normalize.

And here’s a little advice. To improve the system's performance, try to always initialize your variables outside of your main loop.

Rotating and Scaling

When we discuss the subject of rotating an object, many start feeling lost. Euler angles, degrees, radians an even, oh my goodness, quaternions. But this is a lesson for beginners, so we will take a look at the easiest and most effective way of rotating things.

First of all, remember that every rotation function accepts values measured in radians. But I am a simple person, and since middle school I’ve liked angles in good ol’ degrees. So, we have to convert our data to the scale of notation that we need. Luckily, there is the util module in the Blend4Web API, and this module contains the converters we need so much. Let's consider all of this in a concrete example.

Let's assume that we need to rotate an object by 45 degrees along the X axis. There is a rotate_x_local() function that slightly resembles the move_local() function we are already familiar with. Most of the time, it is used to constantly rotate an object inside a cycle, but it is also perfectly suitable to instantaneously rotate an object by an angle you need. Here is an example of the code:

var angle_deg = 45;
var angle_rad = m_util.deg_to_rad(angle_deg);
var obj = m_scene.get_object_by_name("My object");

m_trans.rotate_x_local(obj, angle_rad); 

I think you have already guessed that there are similar functions for rotating along other axes: rotate_y_local() and rotate_z_local(). And, of course, no one can forbid you from using other methods, even the infamous quaternions.

There is still one thing in the line of 3D space manipulations that we have to consider and that is scaling. There are the get_scale() and set_scale() functions for it. Here, for example, is how you can double the object's size:

var obj = m_scene.get_object_by_name("My object");
var scale = m_trans.get_scale(obj);
m_trans.set_scale(obj, scale*2);

Take into account that scale can only be changed proportionally along every axis.

And that’s all. Now you know enough to freely control objects in your scene. See you later!

Comments
28 oct. 2016 15:15
Hello,
I needed the scene or world to have zero gravity (the objects, or the balls I created from Blender should not fall).
If I set the objects as static in Blender, the set_translation does not work because the object should be dynamic.
But if I use dynamic/rigid, the objects fall (with gravity). I followed the example here.
28 oct. 2016 15:44
Hi!
In order to use translation methods you should enable the Force Dynamic Object flag. You don't need to change physics settings.
Actually, Andrey has pointed this in the following screenshot:
01 nov. 2016 11:13
thank you. I'll try this.
04 nov. 2016 16:52
Hello, I tried again, but the yellowball (in the attached json file) does not collide with red ball, because they are static and I used "move local". And if I use rigid, they fall.
The balls should not fall since they are planets.
And then I decided to push up the blueball by 9.8 to achieve equilibrium but it looks like the engine is not working as it should be. There is a push going forward (y), I do not know where did it come from. I guess it is an engine bug.
Here is my code and it is also in the attachment.
————————————————————————–
"use strict"

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

var m_app = require("app");
var m_data = require("data");
var m_scs = require("scenes");
var m_obj = require("objects");
var m_trans = require("transform");
var m_phy = require("physics");
var m_ctl = require("controls");
var m_cfg = require("config");
var m_version = require("version");

var DEBUG = (m_version.type() === "DEBUG");

var obj;
var yellowplanet;
var blueplanet;
var elapsed_sensor;


exports.init = function() {
m_app.init({
canvas_container_id: "canvas_cont",
callback: init_cb,
physics_enabled: true,
show_fps: true,
alpha: false,
autoresize: true,
assets_dds_available: !DEBUG,
assets_min50_available: !DEBUG,
console_verbose: true
});
}

function init_cb(canvas_elem, success) {

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

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

}

var speed = 1;

function main_cb (obj, id) {
var elapsed = m_ctl.get_sensor_value(obj, id, 0); 
//m_trans.set_translation(planet,2, 0, 0);
m_phy.apply_force(blueplanet,0,0,9.81,true) 
m_trans.move_local(yellowplanet, speed*elapsed, 0, 0);	
}

function load_cb(data_id) {
m_app.enable_camera_controls();
elapsed_sensor = m_ctl.create_elapsed_sensor();
m_ctl.create_sensor_manifold(null, "MAIN", m_ctl.CT_CONTINUOUS, [elapsed_sensor], null, main_cb);
yellowplanet = m_scs.get_object_by_name("Yellow");
blueplanet = m_scs.get_object_by_name("Blue");
//m_phy.apply_force_world(obj, 5, -1, 0)
//m_phy.apply_force(src_obj,0,9.8,9.8,false)
//m_trans.set_translation(obj,5, 0, 0);
}

//var loop = function () {
//	requestAnimationFrame(loop);
//m_trans.set_translation(obj,2, 0, 0);
//m_phy.apply_force(src_obj,10,0,0,false)
//};

//	requestAnimationFrame(loop);


});

b4w.require("gravity_simulation").init();
———————————————————————
04 nov. 2016 16:54
I forgot to tell, the "force dynamic object" did not solve the problem. Because I need an object that moves and at the same time collides (with physics), and does not fall.
04 nov. 2016 17:07
how to delete a comment?
07 nov. 2016 11:31
Change it to something like "delete this comment"
14 nov. 2016 11:52
Thank you. But I decided to just use Threejs. I will just use Blend4Web in my later projects.
01 aug. 2017 12:06
Hi, there.

Why quaternions not work, how you expect. This is my file, where I wrote the code, where quaternions work not properly.

Zip file: http://www.filedropper.com/testproject_1.

Thank you every one, who help me.
14 feb. 2022 18:30
The Blend4Web team has recently presented a series of tutorials explaining the basics of 3D graphics development. This article is the first one of a new series explaining the principles of programming and providing tips for working during college. The article covers such topics as importing ready models, duplicating objects and their groups, the use of logic nodes and the API to control object's appearance. Some tips on work efficiency are given as well.
Please register or log in to leave a reply.