mirror of
https://github.com/fadden/6502bench.git
synced 2024-05-31 22:41:37 +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 {
|
try {
|
||||||
byte foo = data[offset];
|
byte foo = data[offset];
|
||||||
} catch (Exception ex) {
|
} catch (Exception) {
|
||||||
throw new AddressTranslateException("FAILED at srcOff=$" + srcOffset.ToString("x4") +
|
throw new AddressTranslateException("FAILED at srcOff=$" + srcOffset.ToString("x4") +
|
||||||
" addr=$" + address.ToString("x4"));
|
" addr=$" + address.ToString("x4"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,9 +326,9 @@ namespace PluginCommon {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds raw vertex/edge/normal data collected from a 2D or 3D wireframe mesh. We use
|
/// 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
|
/// a left-handed coordinate system (+Z goes into the screen). If the project being
|
||||||
/// expectation that the mesh will be presented such that the object is right-side up
|
/// disassembled uses different definitions for the axes, it's probably best to convert.
|
||||||
/// and facing toward the viewer (nose toward +Z).
|
/// 2D data should use X/Y with Z=0.
|
||||||
///
|
///
|
||||||
/// All objects will have vertices and edges. Face normals are optional.
|
/// All objects will have vertices and edges. Face normals are optional.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -21,20 +21,22 @@ namespace PluginCommon {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Simple 4x4 matrix.
|
/// Simple 4x4 matrix.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Matrix44 {
|
public class Matrix33 {
|
||||||
|
private const int DIM = 3;
|
||||||
|
|
||||||
public double[,] Val {
|
public double[,] Val {
|
||||||
get { return mVal; }
|
get { return mVal; }
|
||||||
private set { mVal = value; }
|
private set { mVal = value; }
|
||||||
}
|
}
|
||||||
private double[,] mVal;
|
private double[,] mVal;
|
||||||
|
|
||||||
public Matrix44() {
|
public Matrix33() {
|
||||||
Val = new double[4, 4];
|
Val = new double[DIM, DIM];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear() {
|
public void Clear() {
|
||||||
for (int col = 0; col < 4; col++) {
|
for (int col = 0; col < DIM; col++) {
|
||||||
for (int row = 0; row < 4; row++) {
|
for (int row = 0; row < DIM; row++) {
|
||||||
Val[col, row] = 0.0;
|
Val[col, row] = 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,18 +44,23 @@ namespace PluginCommon {
|
||||||
|
|
||||||
public void SetToIdentity() {
|
public void SetToIdentity() {
|
||||||
Clear();
|
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>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="xdeg">Rotation about the X axis, in degrees.</param>
|
/// <param name="xdeg">Rotation about the X axis, in degrees.</param>
|
||||||
/// <param name="ydeg">Rotation about the Y 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>
|
/// <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;
|
const double degToRad = Math.PI / 180.0;
|
||||||
double xrad = xdeg * degToRad;
|
double xrad = xdeg * degToRad;
|
||||||
double yrad = ydeg * degToRad;
|
double yrad = ydeg * degToRad;
|
||||||
|
@ -68,10 +75,9 @@ namespace PluginCommon {
|
||||||
double sycx = sy * cx;
|
double sycx = sy * cx;
|
||||||
double sysx = sy * sx;
|
double sysx = sy * sx;
|
||||||
|
|
||||||
RotMode rm = RotMode.ZYX;
|
switch (mode) {
|
||||||
switch (rm) {
|
case RotMode.ZYX_RRR:
|
||||||
case RotMode.ZYX:
|
// R = Rz * Ry * Rx, right-handed
|
||||||
// R = Rz * Ry * Rx (from wikipedia)
|
|
||||||
Val[0, 0] = cz * cy;
|
Val[0, 0] = cz * cy;
|
||||||
Val[0, 1] = sz * cy;
|
Val[0, 1] = sz * cy;
|
||||||
Val[0, 2] = -sy;
|
Val[0, 2] = -sy;
|
||||||
|
@ -84,8 +90,8 @@ namespace PluginCommon {
|
||||||
Val[2, 1] = sz * sycx - cz * sx;
|
Val[2, 1] = sz * sycx - cz * sx;
|
||||||
Val[2, 2] = cy * cx;
|
Val[2, 2] = cy * cx;
|
||||||
break;
|
break;
|
||||||
case RotMode.XYZ:
|
case RotMode.ZYX_LLL:
|
||||||
// R = Rx * Ry * Rz (from Arc3D)
|
// R = Rz * Ry * Rx, left-handed
|
||||||
Val[0, 0] = cz * cy;
|
Val[0, 0] = cz * cy;
|
||||||
Val[0, 1] = -sz * cy;
|
Val[0, 1] = -sz * cy;
|
||||||
Val[0, 2] = sy;
|
Val[0, 2] = sy;
|
||||||
|
@ -98,8 +104,22 @@ namespace PluginCommon {
|
||||||
Val[2, 1] = sz * sycx + cz * sx;
|
Val[2, 1] = sz * sycx + cz * sx;
|
||||||
Val[2, 2] = cy * cx;
|
Val[2, 2] = cy * cx;
|
||||||
break;
|
break;
|
||||||
case RotMode.ZXY:
|
case RotMode.XYZ_RRR:
|
||||||
// R = Rz * Rx * Ry (from Arc3D)
|
// 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;
|
double cysx = cy * sx;
|
||||||
Val[0, 0] = cz * cy + sz * sysx;
|
Val[0, 0] = cz * cy + sz * sysx;
|
||||||
Val[0, 1] = -sz * cy + cz * sysx;
|
Val[0, 1] = -sz * cy + cz * sysx;
|
||||||
|
@ -114,29 +134,27 @@ namespace PluginCommon {
|
||||||
Val[2, 2] = cy * cx;
|
Val[2, 2] = cy * cx;
|
||||||
break;
|
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>
|
/// <summary>
|
||||||
/// Multiplies a 3-element vector. The vector's 4th element is implicitly set to 1.
|
/// Multiplies a 3-element vector.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="vec">Column vector to multiply.</param>
|
/// <param name="vec">Column vector to multiply.</param>
|
||||||
/// <returns>Result vector.</returns>
|
/// <returns>Result vector.</returns>
|
||||||
public Vector3 Multiply(Vector3 vec) {
|
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 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] + Val[3, 1];
|
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] + Val[3, 2];
|
double rz = vec.X * Val[0, 2] + vec.Y * Val[1, 2] + vec.Z * Val[2, 2];
|
||||||
return new Vector3(rx, ry, rz);
|
return new Vector3(rx, ry, rz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (int row = 0; row < 4; row++) {
|
for (int row = 0; row < DIM; 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]);
|
|
||||||
sb.AppendLine();
|
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();
|
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.
|
<p>The wireframe generator may offer the choice of perspective vs.
|
||||||
orthographic projection, and whether or not to enable backface
|
orthographic projection, and whether or not to enable backface
|
||||||
culling. If the generator doesn't provide them, the default is to
|
culling. These are declared in the visualization generator script,
|
||||||
render with a perspective projection and without culling.</p>
|
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
|
<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
|
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
|
going into the screen. The object will be placed a short distance
|
||||||
rotation when looking down the axis in the positive direction. The
|
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
|
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
|
subject to gimbal lock (e.g. if you set Y to 90 degrees, X and Z rotate
|
||||||
about the same axis).</p>
|
about the same axis).</p>
|
||||||
|
|
Binary file not shown.
|
@ -3,6 +3,9 @@
|
||||||
;
|
;
|
||||||
; Assembler: Merlin 32
|
; 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
|
org $1000
|
||||||
|
|
||||||
lda vertices
|
lda vertices
|
||||||
|
@ -12,19 +15,19 @@
|
||||||
|
|
||||||
; List of vertices (X,Y,Z).
|
; List of vertices (X,Y,Z).
|
||||||
vertices
|
vertices
|
||||||
dfb -32,32,32 ;0
|
dfb -32,32,-32 ;0
|
||||||
dfb -32,-32,32 ;1
|
dfb -32,-32,-32 ;1
|
||||||
dfb 32,-32,32 ;2
|
dfb 32,-32,-32 ;2
|
||||||
dfb 32,32,32 ;3
|
dfb 32,32,-32 ;3
|
||||||
dfb -32,32,-32 ;4
|
dfb -32,32,32 ;4
|
||||||
dfb -32,-32,-32 ;5
|
dfb -32,-32,32 ;5
|
||||||
dfb 32,-32,-32 ;6
|
dfb 32,-32,32 ;6
|
||||||
dfb 32,32,-32 ;7
|
dfb 32,32,32 ;7
|
||||||
|
|
||||||
; put a decoration on the front face; should look like a '7'
|
; Put a decoration on the front face.
|
||||||
dfb -20,-20,32 ;8
|
dfb -20,-20,-32 ;8
|
||||||
dfb 20,20,32 ;9
|
dfb 20,20,-32 ;9
|
||||||
dfb 10,20,32 ;10
|
dfb 10,20,-32 ;10
|
||||||
dfb $80
|
dfb $80
|
||||||
|
|
||||||
; List of edges (vertex0, vertex1, face0, face1).
|
; List of edges (vertex0, vertex1, face0, face1).
|
||||||
|
@ -48,8 +51,8 @@ edges
|
||||||
|
|
||||||
; List of faces (surface normal X,Y,Z).
|
; List of faces (surface normal X,Y,Z).
|
||||||
faces
|
faces
|
||||||
dfb 0,0,1 ;0 front
|
dfb 0,0,-1 ;0 front
|
||||||
dfb 0,0,-1 ;1 back
|
dfb 0,0,1 ;1 back
|
||||||
dfb 0,1,0 ;2 top
|
dfb 0,1,0 ;2 top
|
||||||
dfb 0,-1,0 ;3 bottom
|
dfb 0,-1,0 ;3 bottom
|
||||||
dfb 1,0,0 ;4 right
|
dfb 1,0,0 ;4 right
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
{
|
{
|
||||||
"_ContentVersion":3,
|
"_ContentVersion":3,
|
||||||
"FileDataLength":120,
|
"FileDataLength":120,
|
||||||
"FileDataCrc32":1015994132,
|
"FileDataCrc32":-604155558,
|
||||||
"ProjectProps":{
|
"ProjectProps":{
|
||||||
"CpuName":"6502",
|
"CpuName":"6502",
|
||||||
"IncludeUndocumentedInstr":false,
|
"IncludeUndocumentedInstr":false,
|
||||||
|
@ -274,7 +274,7 @@
|
||||||
"_deltaRotY":6,
|
"_deltaRotY":6,
|
||||||
"_deltaRotZ":0,
|
"_deltaRotZ":0,
|
||||||
"_frameCount":120,
|
"_frameCount":120,
|
||||||
"_frameDelayMsec":100}},
|
"_frameDelayMsec":33}},
|
||||||
|
|
||||||
{
|
{
|
||||||
"Tag":"bmp_data",
|
"Tag":"bmp_data",
|
||||||
|
|
|
@ -246,8 +246,18 @@ namespace SourceGen {
|
||||||
scale = (scale * zadj) / (zadj + 0.3);
|
scale = (scale * zadj) / (zadj + 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix44 rotMat = new Matrix44();
|
// In a left-handed coordinate system, +Z is away from the viewer. The
|
||||||
rotMat.SetRotationEuler(eulerX, eulerY, eulerZ);
|
// 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) {
|
if (doBfc) {
|
||||||
// Mark faces as visible or not. This is determined with the surface normal,
|
// Mark faces as visible or not. This is determined with the surface normal,
|
||||||
|
@ -264,9 +274,9 @@ namespace SourceGen {
|
||||||
face.IsVisible = true;
|
face.IsVisible = true;
|
||||||
continue;
|
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.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.
|
// Now compute the dot product of the camera vector.
|
||||||
double dot = Vector3.Dot(camVec, rotNorm);
|
double dot = Vector3.Dot(camVec, rotNorm);
|
||||||
|
@ -278,7 +288,7 @@ namespace SourceGen {
|
||||||
// For orthographic projection, the camera is essentially looking
|
// For orthographic projection, the camera is essentially looking
|
||||||
// down the Z axis at every X,Y, so we can trivially check the
|
// down the Z axis at every X,Y, so we can trivially check the
|
||||||
// value of Z in the transformed normal.
|
// 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;
|
double x0, y0, x1, y1;
|
||||||
|
|
||||||
if (doPersp) {
|
if (doPersp) {
|
||||||
// +Z on the shape is closer to the viewer, so we negate it here
|
// Left-handed system, so +Z is away from viewer.
|
||||||
double z0 = -trv0.Z * scale;
|
double z0 = trv0.Z * scale;
|
||||||
double z1 = -trv1.Z * scale;
|
double z1 = trv1.Z * scale;
|
||||||
x0 = (trv0.X * scale * zadj) / (zadj + z0);
|
x0 = (trv0.X * scale * zadj) / (zadj + z0);
|
||||||
y0 = (trv0.Y * scale * zadj) / (zadj + z0);
|
y0 = (trv0.Y * scale * zadj) / (zadj + z0);
|
||||||
x1 = (trv1.X * scale * zadj) / (zadj + z1);
|
x1 = (trv1.X * scale * zadj) / (zadj + z1);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user