Blend4Webs m_trans.set_matrix does not respect linear algebra I tried to scale an object alongside a specific dimension with the set_matrix function from the transform module in Blend4Web.

Phenomenon

Here is an example matrix applied by set_matrix

a 0 0 0
0 1 0 0
0 0 1 0 
0 0 0 1


Let a be 0.1

Expectation: It should be scaled to 0.1 on X.

Result: It was scaled down approximately 0.8 on X, Y and Z.



Left is the original cube, the right is transformed with the matrix as defined above.

then I assigned different values to a, and find many anomalies, the two most noticeable:


The  transformation is not linear,
and happens on all 3D axes. (it should be carried out only on X) 


The funniest case when a is zero, because in this setting the transformation matrix spans only to 2 dimensions, which should collapse the cube to a square... well it makes it a slightly smaller cube, but at least it collapsed my mind to the null space.

Reproduction

I made a self contained example in a project, you can extract it into your project folder. This reproduces what I described, the only difference that it changes 2 basis vectors to null vector instead of one.

Blender file

I changed two things on the default Blend4Web starter file:


Reseted the cube transformation to  the identity. (it is translated on Z by 1 on default scene )  
turned on the physics on the cube object, so it can be transformed.


Code

The following code was written into the default Blend4Web JavaScript scaffolding, under the place your code here comment

var cube = m_scenes.get_object_by_name("cube");
// The cube object on default b4w scene is transformed by the identity.
var mat = m_trans.get_matrix(cube);
// Create a global function to update the cube transformation matrix
// from the inspector.
updateMatfromWindow(mat, cube);


console.log("identity: ");
mat4_pprint(mat);
mat[0] = 0;
mat[5] = 0;
console.log('collapsing to one dimension');
mat4_pprint(mat);
/**
* after this the cube should disaper. since it transformed by a matrix with a
* zero determinant.
*/
m_trans.set_matrix(cube, mat);


I also added some extra features for testing, so you can update the transformation matrix of the cube from the inspector, using the updateMat(ind, val), function, for example:

updateMat(12, 1) // will translate the cube 1 unit on x.


and a matrix pretty printer function.

My environment:


NixOS 17.09 “Hummingbird” (GNU/Linux)
Blender  2.79a
Blend4Web 17.12.0
As it turned out recent Blend4Web (17.12.0) does not support non-uniform scaling (it will be added with the next release). Which also means: its 4D matrix module does not expose the real transformation entity, which it usess at its deepest abstraction.

General workaround:

However Blend4Web supports non-uniform scale animations exported from blender, and it is also possible to jump to a specific animation frame. So make a appropriate scale animation and wrote a code which interpolates between its frames.

Example:

I wrote a window-scaleresponsive input range GUI element with this method, the relevant code snippet should be rather simple for someone with a basic knowledge about the Blend4Web SDK.

function registerSliderResize(railObj, knobObj, frameNum) {

  var etalonRatio = 16 / 9;

  function resize() {

    var ratio = window.innerWidth / window.innerHeight;
    var frameInd = Math.floor(ratio / etalonRatio * frameNum);

    if (frameInd &gt; frameNum)
      frameInd = frameNum;
    else if (frameInd &lt; 1)
      frameInd = 1;

    // invert frameInd
    frameInd = frameNum - frameInd;

    m_anim.apply(railObj, "rail-resize", m_anim.SLOT_0);
    m_anim.set_behavior(railObj, m_anim.AB_FINISH_STOP, m_anim.SLOT_0);
    m_anim.play(railObj, null, m_anim.SLOT_0);
    console.log(frameInd);
    m_anim.set_frame(railObj, frameInd, m_anim.SLOT_0);
    m_anim.stop(railObj, m_anim.SLOT_0);

    m_anim.apply(knobObj, "knob-resize", m_anim.SLOT_0);
    m_anim.set_behavior(railObj, m_anim.AB_FINISH_STOP, m_anim.SLOT_0);
    m_anim.play(railObj, null, m_anim.SLOT_0);
    console.log(frameInd);
    m_anim.set_frame(railObj, frameInd, m_anim.SLOT_0);
    m_anim.stop(railObj, m_anim.SLOT_0);
  };

  resize();

  window.addEventListener("resize", resize);
}


The result:



Two non-uniformly scaled version of the same slider adapted to the window size, changed by Chromium inspector.

The whole project:

You can download the whole source code and the corresponding .blend file.