-
Notifications
You must be signed in to change notification settings - Fork 0
Extension | Vector Operations
Nobody likes doing vector math in TorqueScript. Inevitably, you will have some nightmare nest of vector operations that is difficult to read, even if you split the function calls between multiple lines. Eggscript provides a special vector operations mode for performing vector operations using standard mathematical operators.
The vector operations mode is entered by using the backtick characters. For instance, adding two vectors may look like this:
%final = `%vector1 + %vector2`;
Any operator inside of the backticks is specified as a special vector operation overload. The supported operators, sorted by precedence, are listed here:
Operator | Description |
---|---|
(...) | functions as standard parentheses |
{...} | exits vector operations mode temporarily and allows scalar calculations to be made |
||...|| | gets the vector length of the expression inside of it |
^%vector | get the unit of a vector |
%vector # %vector | find the cross product of two vectors |
%vector * %scalar | multiply a vector by a scalar |
%vector / %scalar | multiply a vector by a scalar |
%vector . %vector | find the dot product between two vectors |
%vector + %vector | add together two vectors |
%vector - %vector | subtract together two vectors |
As suggested by the list, vector operations mode can be escaped while still inside of the backticks. For instance, if we want to calculate the muzzle velocity of a projectile and want to do it all inside of one line of code, we could write:
%muzzleVelocity = `%aimVector * {%muzzleVelocity - %speedLoss + %client.getSpeedMod()}`;
The %muzzleVelocity - %speedLoss + %client.getSpeedMod()
calculation is performed as a scalar mathematics operation. Any operations performed of the vector operation escape operators are treated as scalar operations.
Even though there is no vector distance operator, we are able to use the definition of euclidean distance to find the distance between two vectors using the available syntax:
%distance = `||%point1 - %point2||`;
This calculates the distance using the euclidean distance formula. Additionally, eggscript is smart enough to recognize this specific syntax as a vector distance calculation and will polyfill this expression down using vectorDist(...)
, instead of vectorLen(vectorSub(...)))
.
Calculating the unit vector is simple and uses the same notation as linear algebra. The unit vector is designated by the hat character ^
. To get the unit of a vector, simply type %unit = `^%vector`;
. This will transpile to %unit = vectorNormalize(%vector);
. You can use the unit vector operator in-front of parenthesis: %unit = `^(%point - %origin)`;
.
Using the vector operations escape operators, we can explicitly specify the type of operands in division and multiplication expressions. Without the escape operators, eggscript assumes that the scalar will be on the right hand side of the /
or *
operator, and will print a warning to the console. This right hand scalar rule matches the order of vectorScale
's arguments. However, when using the escape operators, we can have the scalar on any side of the operator that we want. For instance, %vector = `{%scalar} * %eyeVector`;
will not throw an error or a warning and will be correctly polyfilled down to %vector = vectorScale(%eyeVector, %scalar);
. Eggscript only assumes the type of variables, not literals. So, if you write %vector = `4 * %eyeVector`;
, eggscript will correctly identify 4 as a scalar.
We are able to access components of a vector using .xyzw
, or any combination thereof. For instance, if we want to get the xy
coordinates from a vector, we could write: %components = `%vector.xy`;
. This will transpile to %components = getWord(%vector, 0) SPC getWord(%vector, 1);
. We can also rearrange the components and it will rearrange the result: %components = `%vector.yx`;
will transpile to %components = getWord(%vector, 1) SPC getWord(%vector, 0);
. There are some situations were you might want to 0 out one of the components or enforce a specific order. For instance, if you wanted to have a vector equal to %xz = getWord(%vector, 0) SPC "0" SPC getWord(%vector, 1);
will require additional syntax in the eggscript vector operation version of the statement. To zero out a component, we use the _
character. For instance, to build the expression mentioned, we could write: %xz = `%vector.x_z`;
to achieve the same effect.
Here are some examples of more complicated vector operations, along with their polyfills:
%worldBoxCorner1 = `getWords(%objectBox, 0, 2) / 4 + %objectPosition`;
%worldBoxCorner2 = `getWords(%objectBox, 3, 6) / 4 + %objectPosition`;
transpiles to
%worldBoxCorner1 = vectorAdd(
vectorScale(
getWords(%objectBox, 0, 2),
1 / 4
),
%objectPosition
);
%worldBoxCorner2 = vectorAdd(
vectorScale(
getWords(%objectBox, 3, 6),
1 / 4
),
%objectPosition
);
Another example:
%obj.antiGravityZone.setTransform(`%obj.getPosition() + "-2 2 -2"`);
transpiles to
%obj.antiGravityZone.setTransform(
vectorAdd(
%obj.getPosition(),
"-2 2 -2"
)
);
Another example:
if(`||%this.getPosition() - %brick.getPosition()||` < 15 && !isObject(%brick.bot)) {
%brick.getDatablock().spawnBot(%brick);
}
transpiles to
if(vectorDist(%this.getPosition(), %brick.getPosition()) < 15 && !isObject(%brick.bot)) {
%brick.getDatablock().spawnBot(%brick);
}
Creating a basis matrix:
%rightVector = `^(%obj.getEyeVector() # "0 0 1")`;
%upVector = `%rightVector # %obj.getEyeVector()`;
%upVectorOneLine = `^(%obj.getEyeVector() # "0 0 1") # %obj.getEyeVector()`;
transpiles to
%rightVector = vectorNormalize(
vectorCross(
%obj.getEyeVector(),
"0 0 1"
)
);
%upVector = vectorCross(
%rightVector,
%obj.getEyeVector()
);
%upVectorOneLine = vectorCross(
vectorNormalize(
vectorCross(
%obj.getEyeVector(),
"0 0 1"
)
),
%obj.getEyeVector()
);