From b3dacc26132cb3a24dde80cc910d6c8da5222fda Mon Sep 17 00:00:00 2001
From: Andy McFadden
Date: Sat, 14 Mar 2020 10:25:17 -0700
Subject: [PATCH] Switch to left-handed coordinate system
There's no "standard" coordinate system, so the choice is arbitrary.
However, an examination of the Transporter mesh in Elite revealed
that the mesh was designed for a left-handed coordinate system. We
can compensate for that trivially in the Elite visualizer, but we
might as well match what they're doing. (The only change required
in the code is a couple of sign changes on the Z coordinate, and an
update to the rotation matrix.)
This also downsizes Matrix44 to Matrix33, exposes the rotation mode
enum, and adds a left-handed ZYX rotation mode.
This does mean that meshes that put the front at +Z will show their
backsides initially, since we're now oriented as if we're flying
the ships rather than facing them. I considered adding a 180-degree
Y rotation (with a tweak to the rotation matrix handedness to correct
the first rotation axis) to have them facing by default, but figured
that might be confusing since +Z is supposed to be away.
Anybody who really wants it to be the other way can trivially flip
the coordinates in their visualizer (negate xc/zc).
The Z coordinates in the visualization test project were flipped so
that the design is still facing the viewer at rotation (0,0,0).
---
PluginCommon/AddressTranslate.cs | 2 +-
PluginCommon/Interfaces.cs | 6 +-
PluginCommon/{Matrix44.cs => Matrix33.cs} | 70 +++++++++++-------
SourceGen/RuntimeData/Help/visualization.html | 14 ++--
.../SGTestData/Visualization/wireframe-test | Bin 120 -> 120 bytes
.../SGTestData/Visualization/wireframe-test.S | 31 ++++----
.../Visualization/wireframe-test.dis65 | 4 +-
SourceGen/WireframeObject.cs | 26 +++++--
8 files changed, 94 insertions(+), 59 deletions(-)
rename PluginCommon/{Matrix44.cs => Matrix33.cs} (70%)
diff --git a/PluginCommon/AddressTranslate.cs b/PluginCommon/AddressTranslate.cs
index fd6af4e..d57ea72 100644
--- a/PluginCommon/AddressTranslate.cs
+++ b/PluginCommon/AddressTranslate.cs
@@ -82,7 +82,7 @@ namespace PluginCommon {
}
try {
byte foo = data[offset];
- } catch (Exception ex) {
+ } catch (Exception) {
throw new AddressTranslateException("FAILED at srcOff=$" + srcOffset.ToString("x4") +
" addr=$" + address.ToString("x4"));
}
diff --git a/PluginCommon/Interfaces.cs b/PluginCommon/Interfaces.cs
index 857362c..8da317f 100644
--- a/PluginCommon/Interfaces.cs
+++ b/PluginCommon/Interfaces.cs
@@ -326,9 +326,9 @@ namespace PluginCommon {
///
/// Holds raw vertex/edge/normal data collected from a 2D or 3D wireframe mesh. We use
- /// a typical right-handed coordinate system (Z comes out of the screen), with the
- /// expectation that the mesh will be presented such that the object is right-side up
- /// and facing toward the viewer (nose toward +Z).
+ /// a left-handed coordinate system (+Z goes into the screen). If the project being
+ /// disassembled uses different definitions for the axes, it's probably best to convert.
+ /// 2D data should use X/Y with Z=0.
///
/// All objects will have vertices and edges. Face normals are optional.
///
diff --git a/PluginCommon/Matrix44.cs b/PluginCommon/Matrix33.cs
similarity index 70%
rename from PluginCommon/Matrix44.cs
rename to PluginCommon/Matrix33.cs
index 9e52184..f081bd9 100644
--- a/PluginCommon/Matrix44.cs
+++ b/PluginCommon/Matrix33.cs
@@ -21,20 +21,22 @@ namespace PluginCommon {
///
/// Simple 4x4 matrix.
///
- public class Matrix44 {
+ public class Matrix33 {
+ private const int DIM = 3;
+
public double[,] Val {
get { return mVal; }
private set { mVal = value; }
}
private double[,] mVal;
- public Matrix44() {
- Val = new double[4, 4];
+ public Matrix33() {
+ Val = new double[DIM, DIM];
}
public void Clear() {
- for (int col = 0; col < 4; col++) {
- for (int row = 0; row < 4; row++) {
+ for (int col = 0; col < DIM; col++) {
+ for (int row = 0; row < DIM; row++) {
Val[col, row] = 0.0;
}
}
@@ -42,18 +44,23 @@ namespace PluginCommon {
public void SetToIdentity() {
Clear();
- Val[0, 0] = Val[1, 1] = Val[2, 2] = Val[3, 3] = 1.0;
+ Val[0, 0] = Val[1, 1] = Val[2, 2] = 1.0;
}
- private enum RotMode { XYZ, ZYX, ZXY };
+ ///
+ /// Rotation mode. Determines the order in which axes are rotated, and whether the
+ /// rotation is for a right-handed or left-handed system.
+ ///
+ public enum RotMode { XYZ_RRR, ZYX_RRR, ZYX_LLL, ZXY_RRR };
///
- /// Sets the matrix to perform rotation about Euler angles in the order X, Y, Z.
+ /// Sets the matrix to perform rotation about Euler angles X/Y/Z, with a
+ /// configurable order.
///
/// Rotation about the X axis, in degrees.
/// Rotation about the Y axis, in degrees.
/// Rotation about the Z axis, in degrees.
- public void SetRotationEuler(int xdeg, int ydeg, int zdeg) {
+ public void SetRotationEuler(int xdeg, int ydeg, int zdeg, RotMode mode) {
const double degToRad = Math.PI / 180.0;
double xrad = xdeg * degToRad;
double yrad = ydeg * degToRad;
@@ -68,10 +75,9 @@ namespace PluginCommon {
double sycx = sy * cx;
double sysx = sy * sx;
- RotMode rm = RotMode.ZYX;
- switch (rm) {
- case RotMode.ZYX:
- // R = Rz * Ry * Rx (from wikipedia)
+ switch (mode) {
+ case RotMode.ZYX_RRR:
+ // R = Rz * Ry * Rx, right-handed
Val[0, 0] = cz * cy;
Val[0, 1] = sz * cy;
Val[0, 2] = -sy;
@@ -84,8 +90,8 @@ namespace PluginCommon {
Val[2, 1] = sz * sycx - cz * sx;
Val[2, 2] = cy * cx;
break;
- case RotMode.XYZ:
- // R = Rx * Ry * Rz (from Arc3D)
+ case RotMode.ZYX_LLL:
+ // R = Rz * Ry * Rx, left-handed
Val[0, 0] = cz * cy;
Val[0, 1] = -sz * cy;
Val[0, 2] = sy;
@@ -98,8 +104,22 @@ namespace PluginCommon {
Val[2, 1] = sz * sycx + cz * sx;
Val[2, 2] = cy * cx;
break;
- case RotMode.ZXY:
- // R = Rz * Rx * Ry (from Arc3D)
+ case RotMode.XYZ_RRR:
+ // R = Rx * Ry * Rz
+ Val[0, 0] = cz * cy;
+ Val[0, 1] = -sz * cy;
+ Val[0, 2] = sy;
+
+ Val[1, 0] = cz * sysx + sz * cx;
+ Val[1, 1] = -sz * sysx + cz * cx;
+ Val[1, 2] = -cy * sx;
+
+ Val[2, 0] = -cz * sycx + sz * sx;
+ Val[2, 1] = sz * sycx + cz * sx;
+ Val[2, 2] = cy * cx;
+ break;
+ case RotMode.ZXY_RRR:
+ // R = Rz * Rx * Ry
double cysx = cy * sx;
Val[0, 0] = cz * cy + sz * sysx;
Val[0, 1] = -sz * cy + cz * sysx;
@@ -114,29 +134,27 @@ namespace PluginCommon {
Val[2, 2] = cy * cx;
break;
}
- //Val[0, 3] = Val[1, 3] = Val[2, 3] = Val[3, 0] = Val[3, 1] = Val[3, 2] = 0.0;
- Val[3, 3] = 1.0;
}
///
- /// Multiplies a 3-element vector. The vector's 4th element is implicitly set to 1.
+ /// Multiplies a 3-element vector.
///
/// Column vector to multiply.
/// Result vector.
public Vector3 Multiply(Vector3 vec) {
- double rx = vec.X * Val[0, 0] + vec.Y * Val[1, 0] + vec.Z * Val[2, 0] + Val[3, 0];
- double ry = vec.X * Val[0, 1] + vec.Y * Val[1, 1] + vec.Z * Val[2, 1] + Val[3, 1];
- double rz = vec.X * Val[0, 2] + vec.Y * Val[1, 2] + vec.Z * Val[2, 2] + Val[3, 2];
+ double rx = vec.X * Val[0, 0] + vec.Y * Val[1, 0] + vec.Z * Val[2, 0];
+ double ry = vec.X * Val[0, 1] + vec.Y * Val[1, 1] + vec.Z * Val[2, 1];
+ double rz = vec.X * Val[0, 2] + vec.Y * Val[1, 2] + vec.Z * Val[2, 2];
return new Vector3(rx, ry, rz);
}
public override string ToString() {
StringBuilder sb = new StringBuilder();
- for (int row = 0; row < 4; row++) {
- sb.AppendFormat("|{0,8:N3} {1,8:N3} {2,8:N3} {3,8:N3}|",
- Val[0, row], Val[1, row], Val[2, row], Val[3, row]);
+ for (int row = 0; row < DIM; row++) {
sb.AppendLine();
+ sb.AppendFormat("|{0,8:N3} {1,8:N3} {2,8:N3}|",
+ Val[0, row], Val[1, row], Val[2, row]);
}
return sb.ToString();
}
diff --git a/SourceGen/RuntimeData/Help/visualization.html b/SourceGen/RuntimeData/Help/visualization.html
index cb5cd0d..4bf4c46 100644
--- a/SourceGen/RuntimeData/Help/visualization.html
+++ b/SourceGen/RuntimeData/Help/visualization.html
@@ -105,13 +105,17 @@ If you enter an invalid value, the parameter description will turn red.
The wireframe generator may offer the choice of perspective vs.
orthographic projection, and whether or not to enable backface
-culling. If the generator doesn't provide them, the default is to
-render with a perspective projection and without culling.
+culling. These are declared in the visualization generator script,
+but implemented in the viewer. If the generator doesn't
+declare them, the default is to render with a perspective projection
+and without culling.
The viewer allows you to rotate the image about the X, Y, and Z
-axes. The viewer provides a conventional right-handed coordinate system,
+axes. The viewer provides a left-handed coordinate system,
with +X toward the right, +Y toward the top of the screen, and +Z
-coming out of the screen. Positive rotations cause a counter-clockwise
-rotation when looking down the axis in the positive direction. The
+going into the screen. The object will be placed a short distance
+down the Z axis and scaled to fit the window.
+Positive rotations cause a counter-clockwise rotation when the axis
+about which rotations are performed points toward the viewer. The
rotations are performed with a matrix using Euler angles, and are
subject to gimbal lock (e.g. if you set Y to 90 degrees, X and Z rotate
about the same axis).
diff --git a/SourceGen/SGTestData/Visualization/wireframe-test b/SourceGen/SGTestData/Visualization/wireframe-test
index 8f644ca6a06fec53796937c05502d91d427060d3..46d5d056e5e606813437de4686871af216ed6810 100644
GIT binary patch
delta 67
zcmb=Z&|b?WuvSN4ZK^=R1BC}b03;L;SOEy$ym=rZ@_= 0);
+ face.IsVisible = (rotNorm.Z <= 0);
}
}
}
@@ -300,9 +310,9 @@ namespace SourceGen {
double x0, y0, x1, y1;
if (doPersp) {
- // +Z on the shape is closer to the viewer, so we negate it here
- double z0 = -trv0.Z * scale;
- double z1 = -trv1.Z * scale;
+ // Left-handed system, so +Z is away from viewer.
+ double z0 = trv0.Z * scale;
+ double z1 = trv1.Z * scale;
x0 = (trv0.X * scale * zadj) / (zadj + z0);
y0 = (trv0.Y * scale * zadj) / (zadj + z0);
x1 = (trv1.X * scale * zadj) / (zadj + z1);