Understanding The Code

Hello everyone,

Below is a PG containing a combination of code from @Raggar and @syntheticmagus. The PG works exactly the way it should, however, I’m having trouble incorporating it into my main project (it crashes :thinking:). I think the issue is that I really don’t understand most of the code or how it works. I’d like to learn why the code works, and hopefully fix the issues myself.

https://www.babylonjs-playground.com/#KZW5G8

After a lot of research, here are the lines of code that still I don’t understand:

BABYLON.Vector3.TransformNormalToRef(BABYLON.Axis.Z, boxArray[i].getWorldMatrix(), boxArray[i].metadata.velocity);boxArray[i].metadata.velocity.normalize().scaleInPlace(boxArray[i].metadata.speed);boxArray[i].position.addInPlace(boxArray[i].metadata.velocity);

-TransformNormalToRef? My guess is this is how the sphere knows to face and move along the Z axis?
-getWorldMatrix? I think this is getting the current position of the sphere.
-velocity.normalize() and scaleInPlace?
-position.addInPlace?

sphere.position.subtractToRef(box.position,directionVector);
directionVector.normalize().scaleInPlace(delta*0.0015);
box.position.addInPlace(directionVector);

subtractToRef? I think this is getting the distance between the box and sphere.
directionVector? I know this was defined as a constant, so is this for the sphere’s position?
directionVector normalize and scale in place? Again, I’m lost on these changes.:thinking:

Thanks in advance for any explanations! :smiley: :+1:

Hi Anderson34,

I think most of these questions resolve in one way or another back to questions about 3D math, which is definitely too large a topic to tackle all-up in a forum post. :slight_smile: However, let me see what I can do to add some context about what the code you’re asking about does, if not exactly how those mechanics work.

First, a few terms: transform matrix, position vector, direction vector. (You may already be familiar with some or all of these terms, so please feel free to skip this section if it’s not helpful.) A transform matrix (in the context of 3D graphics) is a mathematical construct which encodes all the information relating one 3D space to another. For the purposes of this conversation, you can think of it as containing both the position and the orientation (less precisely, rotation)* of an object in 3D space, like one of the spheres. You can use a transform matrix to transform a 3D vector, which means to move it from one space to another; that is, if you have a vector “in front of” a thing in one space, you can use a transform matrix to move that vector to be “in front of” something else whose position/orientation are described by the transform matrix in question. A position vector, unlike a matrix, encodes only position, not orientation; a position vector can contain any 3D value. Contrastingly, a direction vector encodes neither position nor rotation, but direction; it is also not free to contain just any 3D value, for a direction vector by definition must have a magnitude (i.e., vector length) of 1.

With that out of the way, let’s look at the first block of code you asked about.

BABYLON.Vector3.TransformNormalToRef(BABYLON.Axis.Z, boxArray[i].getWorldMatrix(), boxArray[i].metadata.velocity);
boxArray[i].metadata.velocity.normalize().scaleInPlace(boxArray[i].metadata.speed);
boxArray[i].position.addInPlace(boxArray[i].metadata.velocity);

The overarching purpose of these three lines is to update the position of a single box, which is exactly what the last line does: it takes the value currently stored in velocity and adds it to the current box’s position in an operation you might think of as +=. The purpose of the other two lines is simply to ensure that the value in velocity when it’s added is the correct value.

  1. The first line essentially says, “Set the velocity of the box to be the same as its forward direction (i.e., relative Z axis).” It does this by taking BABYLON.Axis.Z, which is a direction vector representing the forward direction in absolute space or “world space,” then transforms that direction vector using the box’s world transform matrix so that it becomes the relative forward direction for that sphere. It then stores this transformed direction vector in the box’s velocity variable; any time you see ToRef as part of the name of a Babylon math function, all that means is that the last argument of the function is the variable in which to store the answer. (This is done because this way of storing the answer is faster in JavaScript than returning a value, so doing this speeds up the program.)
  2. The second line essentially says, “Set the magnitude of the box’s velocity to be the same as its speed.” velocity.normalize() sets the magnitude of the velocity to 1 (which doesn’t actually do anything here because the prior line set velocity to be a direction vector, which as mentioned above already has a magnitude of 1 by definition). scaleInPlace() then simply scales the velocity vector by the speed, which isn’t a vector but is a scalar (i.e., ordinary number) representing (in this case) how far the box should travel per frame.

In combination, the two operations described above store in velocity a 3D vector which represents the movement the box should make on this frame; this vector is then added to the position in the final line, performing the movement. It’s worth noting that a fair bit of this could be simplified. If you’re interested, I created a modified version of the Playground that reduces all this to one shorter line, omitting some unnecessary operations and using some provided values like the forward property, which obviates the need to directly interact with the world matrix at all.

Now let’s take a look at the second block of code you asked about.

sphere.position.subtractToRef(box.position,directionVector);
directionVector.normalize().scaleInPlace(delta*0.0015);
box.position.addInPlace(directionVector);

As before, this code is performing the per-frame movement of a 3D entity in the scene, in this case a sphere. Also as before, the actual movement is done in the third line, and the preceding two lines exist to store the correct value in an intermediate variable, in this case directionVector. (Regarding your question about directionVector having been declared as a constant, while I’m not a JavaScript expert, I think constants work differently in JavaScript versus languages like Java or C++. Because it’s a constant, I think that means you’re not supposed to assign to it, but evidently you can still change some of the values it contains.)

  1. Here we have another ToRef function, which again is being used to store a value on its last argument, directionVector. The value being stored here is simply the difference between the position of the sphere and the position of the box in question. Note that, despite the fact that this is being stored in a variable called directionVector, it isn’t actually a direction vector yet because its magnitude is (probably) not 1. That problem gets addressed in the next line.
  2. These are the same operations done in the second line of the first block of code you asked about, and they’re done for the same reasons. However, unlike in that case, the normalize() call (which forces directionVector to have a magnitude of 1) is necessary in this case because we don’t know what magnitude the vector in directionVector has, and we need that magnitude to be 1 for the scaleInPlace() to produce the correct result. Once the magnitude is 1, scaleInPlace() changes it again to be a new length corresponding to the desired length of motion in a single frame. Confusingly, this once again makes directionVector (which very briefly was a real direction vector after normalize() was called) have a magnitude unequal to 1. The name is the only confusing part, though; the usage is simply to increment the position in the next line.

Hope some of that was helpful and/or interesting. Best of luck!

5 Likes

Thank you @syntheticmagus, this is exactly the kind of information I was looking for! I really appreciate the detailed explanations and the updated PG.

Thanks again! :smiley: :+1: