mirror of
https://github.com/fadden/6502bench.git
synced 2025-02-20 06:29:04 +00:00
Add rotation and backface culling
Also, correctly update the thumbnail when leaving the visualization editor.
This commit is contained in:
parent
eec847d5f1
commit
b686d2d208
125
CommonUtil/Matrix44.cs
Normal file
125
CommonUtil/Matrix44.cs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 faddenSoft
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CommonUtil {
|
||||||
|
/// <summary>
|
||||||
|
/// Simple 4x4 matrix.
|
||||||
|
/// </summary>
|
||||||
|
public class Matrix44 {
|
||||||
|
public double[,] Val {
|
||||||
|
get { return mVal; }
|
||||||
|
private set { mVal = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private double[,] mVal;
|
||||||
|
|
||||||
|
public Matrix44() {
|
||||||
|
Val = new double[4, 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear() {
|
||||||
|
for (int col = 0; col < 4; col++) {
|
||||||
|
for (int row = 0; row < 4; row++) {
|
||||||
|
Val[col, row] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetToIdentity() {
|
||||||
|
Clear();
|
||||||
|
Val[0, 0] = Val[1, 1] = Val[2, 2] = Val[3, 3] = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the matrix to perform rotation about Euler angles in the order X, Y, Z.
|
||||||
|
/// </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) {
|
||||||
|
const double degToRad = Math.PI / 180.0;
|
||||||
|
double xrad = xdeg * degToRad;
|
||||||
|
double yrad = ydeg * degToRad;
|
||||||
|
double zrad = zdeg * degToRad;
|
||||||
|
|
||||||
|
double cx = Math.Cos(xrad);
|
||||||
|
double sx = Math.Sin(xrad);
|
||||||
|
double cy = Math.Cos(yrad);
|
||||||
|
double sy = Math.Sin(yrad);
|
||||||
|
double cz = Math.Cos(zrad);
|
||||||
|
double sz = Math.Sin(zrad);
|
||||||
|
double sycx = sy * cx;
|
||||||
|
double sysx = sy * sx;
|
||||||
|
|
||||||
|
bool useXyz = false;
|
||||||
|
if (useXyz) {
|
||||||
|
// R = Rz * Ry * Rx (from wikipedia)
|
||||||
|
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;
|
||||||
|
} else {
|
||||||
|
// R = Rx * Ry * Rz (from Arc3D)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
//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.
|
||||||
|
/// </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];
|
||||||
|
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]);
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -51,5 +51,20 @@ namespace CommonUtil {
|
|||||||
mY *= len_r;
|
mY *= len_r;
|
||||||
mZ *= len_r;
|
mZ *= len_r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Multiply(double sc) {
|
||||||
|
mX *= sc;
|
||||||
|
mY *= sc;
|
||||||
|
mZ *= sc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Dot(Vector3 v0, Vector3 v1) {
|
||||||
|
return v0.X * v1.X + v0.Y * v1.Y + v0.Z * v1.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
return string.Format("|{0,8:N3} {1,8:N3} {2,8:N3}|", X, Y, Z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -333,11 +333,12 @@ namespace PluginCommon {
|
|||||||
/// All objects will have vertices and edges. Face normals are optional.
|
/// All objects will have vertices and edges. Face normals are optional.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The face-normal stuff is designed specifically for Elite, which is one of the very
|
/// The face-normal stuff is designed specifically for Elite. Besides being one of the
|
||||||
/// few games to use backface removal.
|
/// very few 6502-based games to use backface culling, it extended the concept to allow
|
||||||
|
/// convex shapes to have protrusions.
|
||||||
///
|
///
|
||||||
/// We favor multiple arrays over compound objects for this interface to avoid having
|
/// We favor multiple arrays over compound objects for this interface to avoid making
|
||||||
/// to define those at the plugin interface level.
|
/// such objects part of the plugin interface.
|
||||||
///
|
///
|
||||||
/// TODO(maybe): specify colors for edges. Not widely used?
|
/// TODO(maybe): specify colors for edges. Not widely used?
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
@ -26,7 +26,7 @@ namespace PluginCommon {
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class VisWireframe : IVisualizationWireframe {
|
public class VisWireframe : IVisualizationWireframe {
|
||||||
public const string P_IS_PERSPECTIVE = "_isPerspective";
|
public const string P_IS_PERSPECTIVE = "_isPerspective";
|
||||||
public const string P_IS_BACKFACE_REMOVED = "_isBackfaceRemoved";
|
public const string P_IS_BFC_ENABLED = "_isBfcEnabled";
|
||||||
public const string P_EULER_ROT_X = "_eulerRotX";
|
public const string P_EULER_ROT_X = "_eulerRotX";
|
||||||
public const string P_EULER_ROT_Y = "_eulerRotY";
|
public const string P_EULER_ROT_Y = "_eulerRotY";
|
||||||
public const string P_EULER_ROT_Z = "_eulerRotZ";
|
public const string P_EULER_ROT_Z = "_eulerRotZ";
|
||||||
@ -34,8 +34,8 @@ namespace PluginCommon {
|
|||||||
public static VisParamDescr Param_IsPerspective(string uiLabel, bool defaultVal) {
|
public static VisParamDescr Param_IsPerspective(string uiLabel, bool defaultVal) {
|
||||||
return new VisParamDescr(uiLabel, P_IS_PERSPECTIVE, typeof(bool), 0, 0, 0, defaultVal);
|
return new VisParamDescr(uiLabel, P_IS_PERSPECTIVE, typeof(bool), 0, 0, 0, defaultVal);
|
||||||
}
|
}
|
||||||
public static VisParamDescr Param_IsBackfaceRemoved(string uiLabel, bool defaultVal) {
|
public static VisParamDescr Param_IsBfcEnabled(string uiLabel, bool defaultVal) {
|
||||||
return new VisParamDescr(uiLabel, P_IS_BACKFACE_REMOVED, typeof(bool), 0, 0, 0, defaultVal);
|
return new VisParamDescr(uiLabel, P_IS_BFC_ENABLED, typeof(bool), 0, 0, 0, defaultVal);
|
||||||
}
|
}
|
||||||
public static VisParamDescr Param_EulerX(string uiLabel, int defaultVal) {
|
public static VisParamDescr Param_EulerX(string uiLabel, int defaultVal) {
|
||||||
return new VisParamDescr(uiLabel, P_EULER_ROT_X, typeof(int), 0, 359, 0, defaultVal);
|
return new VisParamDescr(uiLabel, P_EULER_ROT_X, typeof(int), 0, 359, 0, defaultVal);
|
||||||
@ -183,6 +183,9 @@ namespace PluginCommon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(maybe): confirm that every face has a vertex. Not strictly necessary
|
||||||
|
// since you can do orthographic-projection BFC without it... but who does that?
|
||||||
|
|
||||||
msg = string.Empty;
|
msg = string.Empty;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -43,10 +43,11 @@ namespace WireframeTest {
|
|||||||
P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0),
|
P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0),
|
||||||
|
|
||||||
// These are interpreted by the main app.
|
// These are interpreted by the main app.
|
||||||
VisWireframe.Param_EulerX("Euler rotation X", 0),
|
VisWireframe.Param_EulerX("Rotation about X", 0),
|
||||||
VisWireframe.Param_EulerY("Euler rotation Y", 0),
|
VisWireframe.Param_EulerY("Rotation about Y", 0),
|
||||||
VisWireframe.Param_EulerZ("Euler rotation Z", 0),
|
VisWireframe.Param_EulerZ("Rotation about Z", 0),
|
||||||
VisWireframe.Param_IsPerspective("Perspective projection", true),
|
VisWireframe.Param_IsPerspective("Perspective projection", true),
|
||||||
|
VisWireframe.Param_IsBfcEnabled("Backface culling", true),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Binary file not shown.
@ -29,21 +29,21 @@ vertices
|
|||||||
|
|
||||||
; List of edges (vertex0, vertex1, face0, face1).
|
; List of edges (vertex0, vertex1, face0, face1).
|
||||||
edges
|
edges
|
||||||
dfb 0,1, 0,1 ;0
|
dfb 0,1, 0,5 ;0
|
||||||
dfb 1,2, 0,3 ;1
|
dfb 1,2, 0,3 ;1
|
||||||
dfb 2,3, 0,4 ;2
|
dfb 2,3, 0,4 ;2
|
||||||
dfb 3,0, 0,2 ;3
|
dfb 3,0, 0,2 ;3
|
||||||
dfb 4,5, 1,5 ;4
|
dfb 4,5, 1,5 ;4
|
||||||
dfb 5,6, 1,3 ;5
|
dfb 5,6, 1,3 ;5
|
||||||
dfb 6,7, 1,4 ;6
|
dfb 6,7, 1,4 ;6
|
||||||
dfb 7,4, 1,2 ;7
|
dfb 7,4, 1,2 ;7
|
||||||
dfb 0,4, 2,5 ;8
|
dfb 0,4, 2,5 ;8
|
||||||
dfb 1,5, 3,5 ;9
|
dfb 1,5, 3,5 ;9
|
||||||
dfb 2,6, 3,4 ;10
|
dfb 2,6, 3,4 ;10
|
||||||
dfb 3,7, 2,4 ;11
|
dfb 3,7, 2,4 ;11
|
||||||
|
|
||||||
dfb 8,9, 0,0 ;12
|
dfb 8,9, 0,0 ;12
|
||||||
dfb 9,10,0,0 ;13
|
dfb 9,10, 0,0 ;13
|
||||||
dfb $80
|
dfb $80
|
||||||
|
|
||||||
; List of faces (surface normal X,Y,Z).
|
; List of faces (surface normal X,Y,Z).
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
"_ContentVersion":3,
|
"_ContentVersion":3,
|
||||||
"FileDataLength":120,
|
"FileDataLength":120,
|
||||||
"FileDataCrc32":1432202158,
|
"FileDataCrc32":1015994132,
|
||||||
"ProjectProps":{
|
"ProjectProps":{
|
||||||
"CpuName":"6502",
|
"CpuName":"6502",
|
||||||
"IncludeUndocumentedInstr":false,
|
"IncludeUndocumentedInstr":false,
|
||||||
@ -245,10 +245,11 @@
|
|||||||
"VisGenIdent":"wireframe-test",
|
"VisGenIdent":"wireframe-test",
|
||||||
"VisGenParams":{
|
"VisGenParams":{
|
||||||
"offset":10,
|
"offset":10,
|
||||||
"_eulerRotX":0,
|
"_eulerRotX":15,
|
||||||
"_eulerRotY":0,
|
"_eulerRotY":30,
|
||||||
"_eulerRotZ":0,
|
"_eulerRotZ":0,
|
||||||
"_isPerspective":true}},
|
"_isPerspective":true,
|
||||||
|
"_isBfcEnabled":true}},
|
||||||
|
|
||||||
{
|
{
|
||||||
"Tag":"bmp_data",
|
"Tag":"bmp_data",
|
||||||
|
@ -37,6 +37,8 @@ namespace SourceGen {
|
|||||||
/// that weren't changed while sitting in the undo buffer.
|
/// that weren't changed while sitting in the undo buffer.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class Visualization {
|
public class Visualization {
|
||||||
|
public const double THUMBNAIL_DIM = 64;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unique user-specified tag. This may be any valid string that is at least two
|
/// Unique user-specified tag. This may be any valid string that is at least two
|
||||||
/// characters long after the leading and trailing whitespace have been trimmed.
|
/// characters long after the leading and trailing whitespace have been trimmed.
|
||||||
@ -181,7 +183,7 @@ namespace SourceGen {
|
|||||||
ReadOnlyDictionary<string, object> parms) {
|
ReadOnlyDictionary<string, object> parms) {
|
||||||
Debug.Assert(visWire != null);
|
Debug.Assert(visWire != null);
|
||||||
Debug.Assert(parms != null);
|
Debug.Assert(parms != null);
|
||||||
CachedImage = GenerateWireframeImage(visWire, parms, 64);
|
CachedImage = GenerateWireframeImage(visWire, parms, THUMBNAIL_DIM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -322,7 +324,6 @@ namespace SourceGen {
|
|||||||
// bounds, then draw lines with coordinates from 0.5 to 7.5.
|
// bounds, then draw lines with coordinates from 0.5 to 7.5.
|
||||||
|
|
||||||
GeometryGroup geo = new GeometryGroup();
|
GeometryGroup geo = new GeometryGroup();
|
||||||
Debug.WriteLine("using max=" + dim);
|
|
||||||
|
|
||||||
// Draw invisible line segments to establish Path bounds.
|
// Draw invisible line segments to establish Path bounds.
|
||||||
Point topLeft = new Point(0, 0);
|
Point topLeft = new Point(0, 0);
|
||||||
@ -330,12 +331,14 @@ namespace SourceGen {
|
|||||||
geo.Children.Add(new LineGeometry(topLeft, topLeft));
|
geo.Children.Add(new LineGeometry(topLeft, topLeft));
|
||||||
geo.Children.Add(new LineGeometry(botRight, botRight));
|
geo.Children.Add(new LineGeometry(botRight, botRight));
|
||||||
|
|
||||||
// Generate a list of clip-space line segments. Coordinate values are [-1,1].
|
// Generate a list of clip-space line segments. Coordinate values are in the
|
||||||
|
// range [-1,1], with +X to the right and +Y upward.
|
||||||
WireframeObject wireObj = WireframeObject.Create(visWire);
|
WireframeObject wireObj = WireframeObject.Create(visWire);
|
||||||
List<WireframeObject.LineSeg> segs = wireObj.Generate(parms);
|
List<WireframeObject.LineSeg> segs = wireObj.Generate(parms);
|
||||||
|
|
||||||
// Convert clip-space coords to screen. We need to scale up, round them to the
|
// Convert clip-space coords to screen. We need to translate to [0,2] with +Y
|
||||||
// nearest whole pixel, and add +0.5 to make the thumbnails look crisp.
|
// toward the bottom of the screen, scale up, round to the nearest whole pixel,
|
||||||
|
// and add +0.5 to make thumbnail-size bitmaps look crisp.
|
||||||
double scale = (dim - 0.5) / 2;
|
double scale = (dim - 0.5) / 2;
|
||||||
double adj = 0.5;
|
double adj = 0.5;
|
||||||
foreach (WireframeObject.LineSeg seg in segs) {
|
foreach (WireframeObject.LineSeg seg in segs) {
|
||||||
|
@ -67,11 +67,17 @@ namespace SourceGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class Face {
|
private class Face {
|
||||||
|
// Surface normal.
|
||||||
public Vector3 Normal { get; private set; }
|
public Vector3 Normal { get; private set; }
|
||||||
|
// One vertex on the face, for BFC.
|
||||||
|
public Vertex Vert { get; set; }
|
||||||
|
// Flag set during BFC calculation.
|
||||||
|
public bool IsVisible { get; set; }
|
||||||
|
|
||||||
public Face(double x, double y, double z) {
|
public Face(double x, double y, double z) {
|
||||||
Normal = new Vector3(x, y, z);
|
Normal = new Vector3(x, y, z);
|
||||||
Normal.Normalize();
|
Normal.Normalize(); // not necessary, but easier to read in debug output
|
||||||
|
IsVisible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +160,11 @@ namespace SourceGen {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
wireObj.mVertices[vindex].Faces.Add(wireObj.mFaces[findex]);
|
Face face = wireObj.mFaces[findex];
|
||||||
|
wireObj.mVertices[vindex].Faces.Add(face);
|
||||||
|
if (face.Vert == null) {
|
||||||
|
face.Vert = wireObj.mVertices[vindex];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IntPair[] efaces = visWire.GetEdgeFaces();
|
IntPair[] efaces = visWire.GetEdgeFaces();
|
||||||
@ -168,7 +178,11 @@ namespace SourceGen {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
wireObj.mEdges[eindex].Faces.Add(wireObj.mFaces[findex]);
|
Face face = wireObj.mFaces[findex];
|
||||||
|
wireObj.mEdges[eindex].Faces.Add(face);
|
||||||
|
if (face.Vert == null) {
|
||||||
|
face.Vert = wireObj.mEdges[eindex].Vertex0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -197,33 +211,89 @@ namespace SourceGen {
|
|||||||
/// was especially successful.</returns>
|
/// was especially successful.</returns>
|
||||||
public List<LineSeg> Generate(ReadOnlyDictionary<string, object> parms) {
|
public List<LineSeg> Generate(ReadOnlyDictionary<string, object> parms) {
|
||||||
List<LineSeg> segs = new List<LineSeg>(mEdges.Count);
|
List<LineSeg> segs = new List<LineSeg>(mEdges.Count);
|
||||||
|
bool doPersp = Util.GetFromObjDict(parms, VisWireframe.P_IS_PERSPECTIVE, false);
|
||||||
|
bool doBfc = Util.GetFromObjDict(parms, VisWireframe.P_IS_BFC_ENABLED, false);
|
||||||
|
|
||||||
// Perspective distance adjustment.
|
// Camera Z coordinate adjustment, used to control how perspective projections
|
||||||
|
// appear. The larger the value, the farther the object appears to be. Very
|
||||||
|
// large values approximate an orthographic projection.
|
||||||
const double zadj = 3.0;
|
const double zadj = 3.0;
|
||||||
|
|
||||||
// Scale values to [-1,1].
|
// Scale coordinate values to [-1,1].
|
||||||
bool doPersp = Util.GetFromObjDict(parms, VisWireframe.P_IS_PERSPECTIVE, false);
|
|
||||||
double scale = 1.0 / mBigMag;
|
double scale = 1.0 / mBigMag;
|
||||||
if (doPersp) {
|
if (doPersp) {
|
||||||
scale = (scale * zadj) / (zadj + 1);
|
// objects closer to camera are bigger; reduce scale slightly
|
||||||
|
scale = (scale * zadj) / (zadj + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
int eulerX = Util.GetFromObjDict(parms, VisWireframe.P_EULER_ROT_X, 0);
|
||||||
|
int eulerY = Util.GetFromObjDict(parms, VisWireframe.P_EULER_ROT_Y, 0);
|
||||||
|
int eulerZ = Util.GetFromObjDict(parms, VisWireframe.P_EULER_ROT_Z, 0);
|
||||||
|
Matrix44 rotMat = new Matrix44();
|
||||||
|
rotMat.SetRotationEuler(eulerX, eulerY, eulerZ);
|
||||||
|
|
||||||
|
if (doBfc) {
|
||||||
|
// Mark faces as visible or not. This is determined with the surface normal,
|
||||||
|
// rather than by checking whether a transformed triangle is clockwise.
|
||||||
|
foreach (Face face in mFaces) {
|
||||||
|
// Transform the surface normal.
|
||||||
|
Vector3 rotNorm = rotMat.Multiply(face.Normal);
|
||||||
|
if (doPersp) {
|
||||||
|
// Transform one vertex to get a vector from the camera to the
|
||||||
|
// surface. We want (V0 - C), where C is the camera; since we're
|
||||||
|
// at the origin, we just need -C.
|
||||||
|
if (face.Vert == null) {
|
||||||
|
Debug.WriteLine("GLITCH: no vertex for face");
|
||||||
|
face.IsVisible = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Vector3 camVec = rotMat.Multiply(face.Vert.Vec);
|
||||||
|
camVec.Multiply(-scale); // scale to [-1,1] and negate to get -C
|
||||||
|
camVec.Z += zadj; // translate
|
||||||
|
|
||||||
|
// Now compute the dot product of the camera vector.
|
||||||
|
double dot = Vector3.Dot(camVec, rotNorm);
|
||||||
|
face.IsVisible = (dot >= 0);
|
||||||
|
//Debug.WriteLine(string.Format(
|
||||||
|
// "Face {0} vis={1,-5} dot={2,-8:N2}: camVec={3} rotNorm={4}",
|
||||||
|
// index++, face.IsVisible, dot, camVec, rotNorm));
|
||||||
|
} else {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Edge edge in mEdges) {
|
foreach (Edge edge in mEdges) {
|
||||||
|
if (doBfc) {
|
||||||
|
// To be visible, vertices and edges must either not specify any
|
||||||
|
// faces, or must specify a visible face.
|
||||||
|
if (!IsVertexVisible(edge.Vertex0) ||
|
||||||
|
!IsVertexVisible(edge.Vertex1) ||
|
||||||
|
!IsEdgeVisible(edge)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 trv0 = rotMat.Multiply(edge.Vertex0.Vec);
|
||||||
|
Vector3 trv1 = rotMat.Multiply(edge.Vertex1.Vec);
|
||||||
double x0, y0, x1, y1;
|
double x0, y0, x1, y1;
|
||||||
|
|
||||||
if (doPersp) {
|
if (doPersp) {
|
||||||
// +Z is closer to the viewer, so we negate it here
|
// +Z on the shape is closer to the viewer, so we negate it here
|
||||||
double z0 = -edge.Vertex0.Vec.Z * scale;
|
double z0 = -trv0.Z * scale;
|
||||||
double z1 = -edge.Vertex1.Vec.Z * scale;
|
double z1 = -trv1.Z * scale;
|
||||||
x0 = (edge.Vertex0.Vec.X * scale * zadj) / (zadj + z0);
|
x0 = (trv0.X * scale * zadj) / (zadj + z0);
|
||||||
y0 = (edge.Vertex0.Vec.Y * scale * zadj) / (zadj + z0);
|
y0 = (trv0.Y * scale * zadj) / (zadj + z0);
|
||||||
x1 = (edge.Vertex1.Vec.X * scale * zadj) / (zadj + z1);
|
x1 = (trv1.X * scale * zadj) / (zadj + z1);
|
||||||
y1 = (edge.Vertex1.Vec.Y * scale * zadj) / (zadj + z1);
|
y1 = (trv1.Y * scale * zadj) / (zadj + z1);
|
||||||
} else {
|
} else {
|
||||||
x0 = edge.Vertex0.Vec.X * scale;
|
x0 = trv0.X * scale;
|
||||||
y0 = edge.Vertex0.Vec.Y * scale;
|
y0 = trv0.Y * scale;
|
||||||
x1 = edge.Vertex1.Vec.X * scale;
|
x1 = trv1.X * scale;
|
||||||
y1 = edge.Vertex1.Vec.Y * scale;
|
y1 = trv1.Y * scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
segs.Add(new LineSeg(x0, y0, x1, y1));
|
segs.Add(new LineSeg(x0, y0, x1, y1));
|
||||||
@ -231,5 +301,29 @@ namespace SourceGen {
|
|||||||
|
|
||||||
return segs;
|
return segs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsVertexVisible(Vertex vert) {
|
||||||
|
if (vert.Faces.Count == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
foreach (Face face in vert.Faces) {
|
||||||
|
if (face.IsVisible) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsEdgeVisible(Edge edg) {
|
||||||
|
if (edg.Faces.Count == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
foreach (Face face in edg.Faces) {
|
||||||
|
if (face.IsVisible) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,8 @@ namespace SourceGen.WpfGui {
|
|||||||
private SortedList<int, VisualizationSet> mEditedList;
|
private SortedList<int, VisualizationSet> mEditedList;
|
||||||
private Visualization mOrigVis;
|
private Visualization mOrigVis;
|
||||||
|
|
||||||
|
public BitmapSource mThumbnail;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Visualization generation identifier for the last visualizer we used, for the benefit
|
/// Visualization generation identifier for the last visualizer we used, for the benefit
|
||||||
/// of "new".
|
/// of "new".
|
||||||
@ -320,7 +322,7 @@ namespace SourceGen.WpfGui {
|
|||||||
string trimTag = Visualization.TrimAndValidateTag(TagString, out bool isTagValid);
|
string trimTag = Visualization.TrimAndValidateTag(TagString, out bool isTagValid);
|
||||||
Debug.Assert(isTagValid);
|
Debug.Assert(isTagValid);
|
||||||
NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict, mOrigVis);
|
NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict, mOrigVis);
|
||||||
NewVis.CachedImage = (BitmapSource)previewImage.Source;
|
NewVis.CachedImage = mThumbnail;
|
||||||
|
|
||||||
sLastVisIdent = NewVis.VisGenIdent;
|
sLastVisIdent = NewVis.VisGenIdent;
|
||||||
|
|
||||||
@ -446,6 +448,8 @@ namespace SourceGen.WpfGui {
|
|||||||
BitmapDimensions = "?";
|
BitmapDimensions = "?";
|
||||||
if (!IsValid || item == null) {
|
if (!IsValid || item == null) {
|
||||||
previewImage.Source = sBadParamsImage;
|
previewImage.Source = sBadParamsImage;
|
||||||
|
previewGrid.Background = null;
|
||||||
|
wireframePath.Data = new GeometryGroup();
|
||||||
} else {
|
} else {
|
||||||
// Invoke the plugin.
|
// Invoke the plugin.
|
||||||
PluginErrMessage = string.Empty;
|
PluginErrMessage = string.Empty;
|
||||||
@ -491,12 +495,17 @@ namespace SourceGen.WpfGui {
|
|||||||
wireframePath.Data = new GeometryGroup();
|
wireframePath.Data = new GeometryGroup();
|
||||||
BitmapDimensions = string.Format("{0}x{1}",
|
BitmapDimensions = string.Format("{0}x{1}",
|
||||||
previewImage.Source.Width, previewImage.Source.Height);
|
previewImage.Source.Width, previewImage.Source.Height);
|
||||||
|
|
||||||
|
mThumbnail = (BitmapSource)previewImage.Source;
|
||||||
} else {
|
} else {
|
||||||
previewGrid.Background = Brushes.Black;
|
previewGrid.Background = Brushes.Black;
|
||||||
previewImage.Source = Visualization.BLANK_IMAGE;
|
previewImage.Source = Visualization.BLANK_IMAGE;
|
||||||
wireframePath.Data = Visualization.GenerateWireframePath(visWire, parms,
|
wireframePath.Data = Visualization.GenerateWireframePath(visWire, parms,
|
||||||
previewImage.ActualWidth / 2);
|
previewImage.ActualWidth / 2);
|
||||||
BitmapDimensions = "n/a";
|
BitmapDimensions = "n/a";
|
||||||
|
|
||||||
|
mThumbnail = Visualization.GenerateWireframeImage(visWire, parms,
|
||||||
|
Visualization.THUMBNAIL_DIM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user