mirror of
https://github.com/fadden/6502bench.git
synced 2025-02-02 11:31:57 +00:00
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).
This commit is contained in:
parent
8ff1fa7034
commit
b3dacc2613
@ -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"));
|
||||
}
|
||||
|
@ -326,9 +326,9 @@ namespace PluginCommon {
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
|
@ -21,20 +21,22 @@ namespace PluginCommon {
|
||||
/// <summary>
|
||||
/// Simple 4x4 matrix.
|
||||
/// </summary>
|
||||
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 };
|
||||
/// <summary>
|
||||
/// Rotation mode. Determines the order in which axes are rotated, and whether the
|
||||
/// rotation is for a right-handed or left-handed system.
|
||||
/// </summary>
|
||||
public enum RotMode { XYZ_RRR, ZYX_RRR, ZYX_LLL, ZXY_RRR };
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="xdeg">Rotation about the X axis, in degrees.</param>
|
||||
/// <param name="ydeg">Rotation about the Y axis, in degrees.</param>
|
||||
/// <param name="zdeg">Rotation about the Z axis, in degrees.</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a 3-element vector. The vector's 4th element is implicitly set to 1.
|
||||
/// Multiplies a 3-element vector.
|
||||
/// </summary>
|
||||
/// <param name="vec">Column vector to multiply.</param>
|
||||
/// <returns>Result vector.</returns>
|
||||
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();
|
||||
}
|
@ -105,13 +105,17 @@ If you enter an invalid value, the parameter description will turn red.</p>
|
||||
|
||||
<p>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.</p>
|
||||
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.</p>
|
||||
<p>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).</p>
|
||||
|
Binary file not shown.
@ -3,6 +3,9 @@
|
||||
;
|
||||
; Assembler: Merlin 32
|
||||
|
||||
; Cube with a decoration on the front (should look like a "7").
|
||||
; Defined in a left-handed coordinate system (+Z away from viewer).
|
||||
|
||||
org $1000
|
||||
|
||||
lda vertices
|
||||
@ -12,19 +15,19 @@
|
||||
|
||||
; List of vertices (X,Y,Z).
|
||||
vertices
|
||||
dfb -32,32,32 ;0
|
||||
dfb -32,-32,32 ;1
|
||||
dfb 32,-32,32 ;2
|
||||
dfb 32,32,32 ;3
|
||||
dfb -32,32,-32 ;4
|
||||
dfb -32,-32,-32 ;5
|
||||
dfb 32,-32,-32 ;6
|
||||
dfb 32,32,-32 ;7
|
||||
dfb -32,32,-32 ;0
|
||||
dfb -32,-32,-32 ;1
|
||||
dfb 32,-32,-32 ;2
|
||||
dfb 32,32,-32 ;3
|
||||
dfb -32,32,32 ;4
|
||||
dfb -32,-32,32 ;5
|
||||
dfb 32,-32,32 ;6
|
||||
dfb 32,32,32 ;7
|
||||
|
||||
; put a decoration on the front face; should look like a '7'
|
||||
dfb -20,-20,32 ;8
|
||||
dfb 20,20,32 ;9
|
||||
dfb 10,20,32 ;10
|
||||
; Put a decoration on the front face.
|
||||
dfb -20,-20,-32 ;8
|
||||
dfb 20,20,-32 ;9
|
||||
dfb 10,20,-32 ;10
|
||||
dfb $80
|
||||
|
||||
; List of edges (vertex0, vertex1, face0, face1).
|
||||
@ -48,8 +51,8 @@ edges
|
||||
|
||||
; List of faces (surface normal X,Y,Z).
|
||||
faces
|
||||
dfb 0,0,1 ;0 front
|
||||
dfb 0,0,-1 ;1 back
|
||||
dfb 0,0,-1 ;0 front
|
||||
dfb 0,0,1 ;1 back
|
||||
dfb 0,1,0 ;2 top
|
||||
dfb 0,-1,0 ;3 bottom
|
||||
dfb 1,0,0 ;4 right
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
"_ContentVersion":3,
|
||||
"FileDataLength":120,
|
||||
"FileDataCrc32":1015994132,
|
||||
"FileDataCrc32":-604155558,
|
||||
"ProjectProps":{
|
||||
"CpuName":"6502",
|
||||
"IncludeUndocumentedInstr":false,
|
||||
@ -274,7 +274,7 @@
|
||||
"_deltaRotY":6,
|
||||
"_deltaRotZ":0,
|
||||
"_frameCount":120,
|
||||
"_frameDelayMsec":100}},
|
||||
"_frameDelayMsec":33}},
|
||||
|
||||
{
|
||||
"Tag":"bmp_data",
|
||||
|
@ -246,8 +246,18 @@ namespace SourceGen {
|
||||
scale = (scale * zadj) / (zadj + 0.3);
|
||||
}
|
||||
|
||||
Matrix44 rotMat = new Matrix44();
|
||||
rotMat.SetRotationEuler(eulerX, eulerY, eulerZ);
|
||||
// In a left-handed coordinate system, +Z is away from the viewer. The
|
||||
// visualizer expects a left-handed system with the "nose" aimed toward +Z,
|
||||
// which leaves us looking at the back end of things. We can add a 180 degree
|
||||
// rotation about Y so we're looking at the front instead, though this
|
||||
// effectively reverses the direction of rotation about X. We can compensate
|
||||
// for it by reversing the handedness of the X rotation.
|
||||
//eulerY = (eulerY + 180) % 360;
|
||||
|
||||
// Form rotation matrix.
|
||||
Matrix33 rotMat = new Matrix33();
|
||||
rotMat.SetRotationEuler(eulerX, eulerY, eulerZ, Matrix33.RotMode.ZYX_LLL);
|
||||
//Debug.WriteLine("ROT: " + rotMat);
|
||||
|
||||
if (doBfc) {
|
||||
// Mark faces as visible or not. This is determined with the surface normal,
|
||||
@ -264,9 +274,9 @@ namespace SourceGen {
|
||||
face.IsVisible = true;
|
||||
continue;
|
||||
}
|
||||
Vector3 camVec = rotMat.Multiply(face.Vert.Vec);
|
||||
Vector3 camVec = rotMat.Multiply(face.Vert.Vec); // transform
|
||||
camVec = camVec.Multiply(-scale); // scale to [-1,1] and negate to get -C
|
||||
camVec = camVec.Add(new Vector3(0, 0, zadj)); // translate
|
||||
camVec = camVec.Add(new Vector3(0, 0, -zadj)); // translate
|
||||
|
||||
// Now compute the dot product of the camera vector.
|
||||
double dot = Vector3.Dot(camVec, rotNorm);
|
||||
@ -278,7 +288,7 @@ namespace SourceGen {
|
||||
// For orthographic projection, the camera is essentially looking
|
||||
// down the Z axis at every X,Y, so we can trivially check the
|
||||
// value of Z in the transformed normal.
|
||||
face.IsVisible = (rotNorm.Z >= 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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user