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