Skip to content

Extension | Vector Operations

bansheerubber edited this page Sep 16, 2020 · 19 revisions

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

Escaping Vector Mode

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.

Vector Distance

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(...))).

Unit Vectors

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)`;.

Scalar Type Implicit And Explicit Declaration

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.

Vector Component Access

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.

Examples

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()
);
Clone this wiki locally