Skip to content

Commit 7145b3c

Browse files
authored
Harden Lua matrix and ParticleEffectAction against non-orthonormal inputs (scp-fs2open#7490)
Two related fixes flagged by an audit of vm_vector_2_matrix_norm callers: ParticleEffectAction: locals.direction defaulted to vmd_zero_vector, which would trip vm_vector_2_matrix_norm's unit-length assertion (and divide by zero in release) when an action program emitted a particle effect without first running a Set Direction action. Default to vmd_z_vector so the unset-direction path always feeds a unit vector. matrix_h / Lua orientation type: the element indexer (m[1..9] = x) wrote arbitrary floats straight into mtx.a1d[idx] and the resulting non-orthonormal matrix could flow into obj->orient via the Orientation virt-vars on object/modelinstance/subsystem, breaking downstream engine code that assumes the basis vectors are unit length. getInterpolated had the same problem by design (pure linear interp, no normalization). Adds a fourth MatrixState, NeedsOrthonormalize, which the indexer setter and getInterpolated use to flag their output. ValidateMatrix and ValidateAngles now call vm_orthogonalize_matrix when they observe that state, so any consumer that reads through GetMatrix/GetAngles gets a valid orientation without each call site needing its own guard. getInterpolated is also marked ADE_FUNC_DEPRECATED at FSO 26.0 in favor of rotationalInterpolate; mods targeting older versions keep the legacy linear blend (now safely orthonormalized on use).
1 parent 4246b76 commit 7145b3c

3 files changed

Lines changed: 23 additions & 11 deletions

File tree

code/actions/common.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ expression::ProgramVariables getDefaultTableVariables()
1919
expression::ProgramVariables vars;
2020

2121
vars.setValue({"locals", "position"}, expression::Value(vmd_zero_vector));
22-
vars.setValue({"locals", "direction"}, expression::Value(vmd_zero_vector));
22+
vars.setValue({"locals", "direction"}, expression::Value(vmd_z_vector));
2323

2424
return vars;
2525
}

code/scripting/api/objs/vecmath.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,22 @@ namespace api {
2828
ADE_OBJ(l_Vector, vec3d, "vector", "Vector object");
2929

3030
void matrix_h::ValidateAngles() {
31+
if (status == MatrixState::NeedsOrthonormalize) {
32+
vm_orthogonalize_matrix(&mtx);
33+
status = MatrixState::AnglesOutOfDate;
34+
}
3135
if (status == MatrixState::AnglesOutOfDate) {
3236
vm_extract_angles_matrix(&ang, &mtx);
3337
status = MatrixState::Fine;
3438
}
3539
}
3640
void matrix_h::ValidateMatrix() {
37-
if (status == MatrixState::MatrixOutOfdate) {
41+
if (status == MatrixState::MatrixOutOfDate) {
3842
vm_angles_2_matrix(&mtx, &ang);
3943
status = MatrixState::Fine;
44+
} else if (status == MatrixState::NeedsOrthonormalize) {
45+
vm_orthogonalize_matrix(&mtx);
46+
status = MatrixState::AnglesOutOfDate; // matrix valid; angles still stale
4047
}
4148
}
4249
matrix_h::matrix_h() {
@@ -49,7 +56,7 @@ matrix_h::matrix_h(const matrix* in) {
4956
}
5057
matrix_h::matrix_h(const angles* in) {
5158
ang = *in;
52-
status = MatrixState::MatrixOutOfdate;
59+
status = MatrixState::MatrixOutOfDate;
5360
}
5461
matrix_h::matrix_h(const vec3d *fvec, const vec3d *uvec, const vec3d *rvec) {
5562
vm_vector_2_matrix(&mtx, fvec, uvec, rvec);
@@ -143,9 +150,9 @@ ADE_INDEXER(l_Matrix,
143150
//and recalculating every time.
144151

145152
if (idx < 0) {
146-
mh->SetStatus(MatrixState::MatrixOutOfdate);
153+
mh->SetStatus(MatrixState::MatrixOutOfDate);
147154
} else {
148-
mh->SetStatus(MatrixState::AnglesOutOfDate);
155+
mh->SetStatus(MatrixState::NeedsOrthonormalize);
149156
}
150157

151158
//Might as well put this here
@@ -205,12 +212,14 @@ ADE_FUNC(copy,
205212
return ade_set_args(L, "o", l_Matrix.Set(*mh));
206213
}
207214

208-
ADE_FUNC(getInterpolated,
215+
ADE_FUNC_DEPRECATED(getInterpolated,
209216
l_Matrix,
210217
"orientation Final, number Factor",
211-
"Returns orientation that has been interpolated to Final by Factor (0.0-1.0). This is a pure linear interpolation with no consideration given to matrix validity or normalization. You may want 'rotationalInterpolate' instead.",
218+
"Returns orientation that has been interpolated to Final by Factor (0.0-1.0). This is a pure linear interpolation; the result is not generally a valid orientation matrix, though the engine will orthonormalize it before use. Prefer 'rotationalInterpolate'.",
212219
"orientation",
213-
"Interpolated orientation, or null orientation on failure") {
220+
"Interpolated orientation, or null orientation on failure",
221+
gameversion::version(26, 0),
222+
"Use rotationalInterpolate instead. Pure linear interpolation does not preserve matrix orthonormality.") {
214223
matrix_h* oriA = NULL;
215224
matrix_h* oriB = NULL;
216225
float factor = 0.0f;
@@ -227,7 +236,9 @@ ADE_FUNC(getInterpolated,
227236
final.a1d[i] = A->a1d[i] + (B->a1d[i] - A->a1d[i]) * factor;
228237
}
229238

230-
return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&final)));
239+
matrix_h result(&final);
240+
result.SetStatus(MatrixState::NeedsOrthonormalize);
241+
return ade_set_args(L, "o", l_Matrix.Set(result));
231242
}
232243

233244
ADE_FUNC(rotationalInterpolate,

code/scripting/api/objs/vecmath.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ DECLARE_ADE_OBJ(l_Vector, vec3d);
1818
//I initially store the matrix in this struct.
1919
enum class MatrixState {
2020
Fine,
21-
MatrixOutOfdate,
22-
AnglesOutOfDate
21+
MatrixOutOfDate,
22+
AnglesOutOfDate,
23+
NeedsOrthonormalize // matrix is not orthonormal; orthogonalize before use; angles are also stale
2324
};
2425
struct matrix_h {
2526
private:

0 commit comments

Comments
 (0)