Skip to content

Commit 93a5fa9

Browse files
committed
Added a selection base coordinate handle
1 parent 34e8cae commit 93a5fa9

File tree

3 files changed

+239
-60
lines changed

3 files changed

+239
-60
lines changed

Source/Contrib/TrackViewer/SceneViewer.cs

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,13 @@ public void Update(GameTime gameTime)
120120

121121
UpdateViewUndoState();
122122

123-
if (UserInput.IsMouseLeftButtonPressed && UserInput.ModifiersMaskShiftCtrlAlt(false, false, false)
124-
&& Camera.PickByMouse(out var selectedObject))
123+
if (UserInput.IsMouseLeftButtonPressed && UserInput.ModifiersMaskShiftCtrlAlt(false, false, false))
125124
{
126-
SelectedObject = selectedObject;
127-
SelectedObjectChanged();
125+
if (Camera.PickByMouse(out var selectedObject))
126+
{
127+
SelectedObject = selectedObject;
128+
SelectedObjectChanged();
129+
}
128130
}
129131
if (UserInput.IsPressed(UserCommand.EditorUnselectAll))
130132
{
@@ -289,10 +291,11 @@ void UndoRedo(UndoDataSet undoDataSet, bool undo)
289291
void SelectedObjectChanged()
290292
{
291293
Viewer.EditorShapes.SelectedObject = SelectedObject;
292-
SelectedWorldFile = Viewer.World.Scenery.WorldFiles
293-
.SingleOrDefault(w => w.TileX == SelectedObject?.Location.TileX && w.TileZ == SelectedObject?.Location.TileZ);
294+
295+
SelectedWorldFile = Viewer.World.Scenery.WorldFiles.SingleOrDefault(w => w.TileX == SelectedObject?.Location.TileX && w.TileZ == SelectedObject?.Location.TileZ);
294296
SelectedWorldObject = SelectedWorldFile?.MstsWFile?.Tr_Worldfile?.SingleOrDefault(o => o.UID == SelectedObject?.Uid);
295297

298+
// XAML binding doesn't work for fields (as opposed to properties), so doing it programmatically
296299
SceneWindow.Filename.Text = SelectedObject != null ? System.IO.Path.GetFileName(SelectedObject.SharedShape.FilePath) : "";
297300
SceneWindow.TileX.Text = SelectedObject?.Location.TileX.ToString(CultureInfo.InvariantCulture).Replace(",", "");
298301
SceneWindow.TileZ.Text = SelectedObject?.Location.TileZ.ToString(CultureInfo.InvariantCulture).Replace(",", "");
@@ -304,48 +307,42 @@ void SelectedObjectChanged()
304307
if (SelectedWorldObject?.Matrix3x3 != null)
305308
{
306309
var yaw = (float)Math.Atan2(SelectedWorldObject.Matrix3x3.AZ, SelectedWorldObject.Matrix3x3.CZ);
307-
SceneWindow.RotX.Text = yaw.ToString("N3", CultureInfo.InvariantCulture).Replace(",", "");
310+
var pitch = (float)Math.Asin(-SelectedWorldObject.Matrix3x3.BZ);
311+
var roll = (float)Math.Atan2(SelectedWorldObject.Matrix3x3.BX, SelectedWorldObject.Matrix3x3.BY);
312+
SceneWindow.RotX.Text = pitch.ToString("N3", CultureInfo.InvariantCulture).Replace(",", "");
313+
SceneWindow.RotY.Text = yaw.ToString("N3", CultureInfo.InvariantCulture).Replace(",", "");
314+
SceneWindow.RotZ.Text = roll.ToString("N3", CultureInfo.InvariantCulture).Replace(",", "");
308315
}
309-
if (SelectedWorldObject?.QDirection != null)
316+
else if (SelectedWorldObject?.QDirection != null)
310317
{
311318
var x = SelectedWorldObject.QDirection.A;
312319
var y = SelectedWorldObject.QDirection.B;
313320
var z = SelectedWorldObject.QDirection.C;
314321
var w = SelectedWorldObject.QDirection.D;
315322

316-
var yaw = Math.Atan2(y, w) * 2 / Math.PI * 180;
317-
SceneWindow.RotX.Text = yaw.ToString("N3", CultureInfo.InvariantCulture).Replace(",", "");
318-
}
319-
320-
var q = new Quaternion();
321-
if (SelectedObject?.Location.XNAMatrix.Decompose(out var _, out q, out var _) ?? false)
322-
{
323-
var mag = Math.Sqrt(q.W * q.W + q.Y * q.Y);
324-
var w = q.W / mag;
325-
var ang = 2.0 * Math.Acos(w) / Math.PI * 180;
326-
SceneWindow.RotY.Text = ang.ToString("N3", CultureInfo.InvariantCulture).Replace(",", "");
323+
//var yaw = Math.Atan2(y, w) * 2 / Math.PI * 180;
324+
var yaw = Math.Atan2(2.0f * (y * w + x * z), 1.0f - 2.0f * (x * x + y * y)) / Math.PI * 180;
325+
var pitch = Math.Asin(2.0f * (x * w - y * z)) / Math.PI * 180;
326+
var roll = Math.Atan2(2.0f * (x * y + z * w), 1.0f - 2.0f * (x * x + z * z)) / Math.PI * 180;
327+
SceneWindow.RotX.Text = pitch.ToString("N3", CultureInfo.InvariantCulture).Replace(",", "");
328+
SceneWindow.RotY.Text = yaw.ToString("N3", CultureInfo.InvariantCulture).Replace(",", "");
329+
SceneWindow.RotZ.Text = roll.ToString("N3", CultureInfo.InvariantCulture).Replace(",", "");
327330
}
328331
else
329332
{
333+
SceneWindow.RotX.Text = "";
330334
SceneWindow.RotY.Text = "";
335+
SceneWindow.RotZ.Text = "";
331336
}
332337

333-
if (SelectedObject is StaticShape ppp)
334-
{
335-
var sb = new StringBuilder();
336-
var aaa = SelectedWorldFile?.MstsWFile?.Tr_Worldfile;
337-
aaa.Serialize(sb);
338-
var ccc = sb.ToString();
339-
}
340-
}
341-
342-
public void ExtractYawPitchRoll(Matrix matrix, out float yaw, out float pitch, out float roll)
343-
{
344-
yaw = (float)Math.Atan2(matrix.M13, matrix.M33);
345-
pitch = (float)Math.Asin(-matrix.M23);
346-
roll = (float)Math.Atan2(matrix.M21, matrix.M22);
338+
//if (SelectedObject is StaticShape ppp)
339+
//{
340+
// var sb = new StringBuilder();
341+
// var aaa = SelectedWorldFile?.MstsWFile?.Tr_Worldfile;
342+
// aaa.Serialize(sb);
343+
// var ccc = sb.ToString();
344+
//}
347345
}
348-
349346
}
350347

351348
public class UndoDataSet

Source/RunActivity/Viewer3D/EditorPrimitives.cs

Lines changed: 162 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
using Microsoft.Xna.Framework.Graphics;
2222
using ORTS.Common;
2323
using System;
24+
using System.Collections.Concurrent;
2425
using System.Collections.Generic;
26+
using System.Collections.ObjectModel;
27+
using System.Linq;
2528

2629
namespace Orts.Viewer3D
2730
{
@@ -30,51 +33,89 @@ public class EditorShapes : StaticShape, IDisposable
3033
public readonly MouseCrosshair MouseCrosshair;
3134
public bool MouseCrosshairEnabled { get; set; }
3235
public bool CrosshairPositionUpdateEnabled { get; set; } = true;
33-
public StaticShape SelectedObject { get; set; }
36+
37+
public readonly HandleX HandleX;
38+
public readonly HandleY HandleY;
39+
public readonly HandleZ HandleZ;
40+
public bool HandleEnabled { get; set; } = true;
41+
public WorldPosition HandleLocation;
42+
3443
StaticShape _selectedObject;
35-
readonly List<BoundingBoxPrimitive> SelectedObjectBoundingBoxPrimitives = new List<BoundingBoxPrimitive>();
36-
readonly List<EditorPrimitive> UnusedPrimitives = new List<EditorPrimitive>();
44+
public StaticShape SelectedObject { get; set; }
45+
46+
public ConcurrentBag<StaticShape> BoundingBoxShapes = new ConcurrentBag<StaticShape>();
47+
readonly ConcurrentDictionary<(int tileX, int tileZ, int uid, Matrix matrix, int number), BoundingBoxPrimitive> BoundingBoxPrimitives = new ConcurrentDictionary<(int, int, int, Matrix, int), BoundingBoxPrimitive>();
48+
readonly ConcurrentBag<EditorPrimitive> UnusedPrimitives = new ConcurrentBag<EditorPrimitive>();
3749
Vector3 CrosshairPosition;
3850

3951
public EditorShapes(Viewer viewer) : base(viewer, "", null, ShapeFlags.None, null, -1)
4052
{
4153
MouseCrosshair = new MouseCrosshair(Viewer, Color.GreenYellow);
54+
HandleX = new HandleX(Viewer, Color.Red);
55+
HandleY = new HandleY(Viewer, Color.Blue);
56+
HandleZ = new HandleZ(Viewer, Color.Green);
4257
}
4358

4459
public override void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
4560
{
4661
if (_selectedObject != SelectedObject)
4762
{
63+
if (_selectedObject != null)
64+
for (var i = 0; i < 5; i++)
65+
BoundingBoxPrimitives.TryRemove((_selectedObject.Location.TileX, _selectedObject.Location.TileZ, _selectedObject.Uid, _selectedObject.Location.XNAMatrix, i), out _);
66+
4867
_selectedObject = SelectedObject;
49-
if (UnusedPrimitives.Count > 0)
68+
69+
if (_selectedObject?.BoundingBox?.Length > 0)
5070
{
51-
foreach (var primitive in UnusedPrimitives)
71+
for (var i = 0; i < _selectedObject.BoundingBox.Length; i++)
5272
{
53-
//primitive.Dispose();
73+
BoundingBoxPrimitives.TryAdd((_selectedObject.Location.TileX, _selectedObject.Location.TileZ, _selectedObject.Uid, _selectedObject.Location.XNAMatrix, i),
74+
new BoundingBoxPrimitive(Viewer, _selectedObject.BoundingBox[i], Color.CornflowerBlue));
5475
}
55-
//UnusedPrimitives.Clear();
5676
}
57-
UnusedPrimitives.AddRange(SelectedObjectBoundingBoxPrimitives);
58-
SelectedObjectBoundingBoxPrimitives.Clear();
59-
if (_selectedObject?.BoundingBox?.Length > 0)
77+
}
78+
for (var i = 0; i < BoundingBoxPrimitives.Count; i++)
79+
{
80+
var bb = BoundingBoxPrimitives.Keys.ElementAtOrDefault(i);
81+
if (bb != default((int, int, int, Matrix, int))
82+
&& !BoundingBoxShapes.Any(s => s?.Location.TileX == bb.tileX && s?.Location.TileZ == bb.tileZ && s?.Uid == bb.uid))
6083
{
61-
foreach (var boundingBox in _selectedObject.BoundingBox)
84+
if (bb.tileX != _selectedObject?.Location.TileX && bb.tileZ != _selectedObject?.Location.TileZ && bb.uid != _selectedObject?.Uid)
6285
{
63-
SelectedObjectBoundingBoxPrimitives.Add(new BoundingBoxPrimitive(Viewer, boundingBox, Color.CornflowerBlue));
86+
if (BoundingBoxPrimitives.TryRemove(bb, out var primitive))
87+
UnusedPrimitives.Add(primitive);
6488
}
6589
}
6690
}
67-
if (SelectedObjectBoundingBoxPrimitives?.Count > 0)
91+
92+
for (var j = 0; j < BoundingBoxShapes.Count; j++)
6893
{
69-
foreach (var boundingBox in SelectedObjectBoundingBoxPrimitives)
94+
var s = BoundingBoxShapes.ElementAtOrDefault(j);
95+
if (s?.BoundingBox == null || s.BoundingBox.Length == 0)
96+
continue;
97+
98+
if (!BoundingBoxPrimitives.Keys.Any(bb => s.Location.TileX == bb.tileX && s.Location.TileZ == bb.tileZ && s.Uid == bb.uid))
7099
{
71-
var dTileX = _selectedObject.Location.TileX - Viewer.Camera.TileX;
72-
var dTileZ = _selectedObject.Location.TileZ - Viewer.Camera.TileZ;
73-
var xnaDTileTranslation = _selectedObject.Location.XNAMatrix;
100+
for (var i = 0; i < s.BoundingBox.Length; i++)
101+
{
102+
BoundingBoxPrimitives.TryAdd((s.Location.TileX, s.Location.TileZ, s.Uid, s.Location.XNAMatrix, i),
103+
new BoundingBoxPrimitive(Viewer, s.BoundingBox[i], Color.MediumVioletRed));
104+
}
105+
}
106+
}
107+
108+
if (BoundingBoxPrimitives?.Count > 0)
109+
{
110+
foreach (var boundingBox in BoundingBoxPrimitives.Keys)
111+
{
112+
var dTileX = boundingBox.tileX - Viewer.Camera.TileX;
113+
var dTileZ = boundingBox.tileZ - Viewer.Camera.TileZ;
114+
var xnaDTileTranslation = boundingBox.matrix;
74115
xnaDTileTranslation.M41 += dTileX * 2048;
75116
xnaDTileTranslation.M43 -= dTileZ * 2048;
76-
xnaDTileTranslation = boundingBox.ComplexTransform * xnaDTileTranslation;
77-
frame.AddPrimitive(boundingBox.Material, boundingBox, RenderPrimitiveGroup.Labels, ref xnaDTileTranslation);
117+
xnaDTileTranslation = BoundingBoxPrimitives[boundingBox].ComplexTransform * xnaDTileTranslation;
118+
frame.AddPrimitive(BoundingBoxPrimitives[boundingBox].Material, BoundingBoxPrimitives[boundingBox], RenderPrimitiveGroup.Labels, ref xnaDTileTranslation);
78119
}
79120
}
80121
if (MouseCrosshairEnabled)
@@ -86,28 +127,43 @@ public override void PrepareFrame(RenderFrame frame, ElapsedTime elapsedTime)
86127
var mouseCrosshairMatrix = Matrix.CreateTranslation(CrosshairPosition);
87128
frame.AddPrimitive(MouseCrosshair.Material, MouseCrosshair, RenderPrimitiveGroup.World, ref mouseCrosshairMatrix);
88129
}
130+
if (HandleEnabled)
131+
{
132+
var handleLocation = HandleLocation ?? _selectedObject?.Location;
133+
if (handleLocation != null)
134+
{
135+
var dTileX = handleLocation.TileX - Viewer.Camera.TileX;
136+
var dTileZ = handleLocation.TileZ - Viewer.Camera.TileZ;
137+
var xnaDTileTranslation = handleLocation.XNAMatrix;
138+
xnaDTileTranslation.M41 += dTileX * 2048;
139+
xnaDTileTranslation.M43 -= dTileZ * 2048;
140+
frame.AddPrimitive(HandleX.Material, HandleX, RenderPrimitiveGroup.Overlay, ref xnaDTileTranslation);
141+
frame.AddPrimitive(HandleY.Material, HandleY, RenderPrimitiveGroup.Overlay, ref xnaDTileTranslation);
142+
frame.AddPrimitive(HandleZ.Material, HandleZ, RenderPrimitiveGroup.Overlay, ref xnaDTileTranslation);
143+
}
144+
}
89145
}
90146

91147
internal override void Mark()
92148
{
93149
MouseCrosshair.Mark();
94-
if (SelectedObjectBoundingBoxPrimitives?.Count > 0)
150+
if (BoundingBoxPrimitives?.Count > 0)
95151
{
96-
foreach (var selectedObject in SelectedObjectBoundingBoxPrimitives)
152+
foreach (var selectedObject in BoundingBoxPrimitives)
97153
{
98-
selectedObject.Mark();
154+
selectedObject.Value.Mark();
99155
}
100156
}
101157
}
102158

103159
public void Dispose()
104160
{
105161
MouseCrosshair?.Dispose();
106-
if (SelectedObjectBoundingBoxPrimitives?.Count > 0)
162+
if (BoundingBoxPrimitives?.Count > 0)
107163
{
108-
foreach (var selectedObject in SelectedObjectBoundingBoxPrimitives)
164+
foreach (var selectedObject in BoundingBoxPrimitives)
109165
{
110-
selectedObject.Dispose();
166+
selectedObject.Value.Dispose();
111167
}
112168
}
113169
}
@@ -162,7 +218,7 @@ public BoundingBoxPrimitive(Viewer viewer, BoundingBox boundingBox, Color color)
162218
IndexBuffer = BoundingBoxIndexBuffer;
163219
PrimitiveCount = IndexBuffer.IndexCount / 2;
164220
PrimitiveType = PrimitiveType.LineList;
165-
Material = viewer.MaterialManager.Load("DebugNormals");
221+
Material = viewer.MaterialManager.Load("EditorPrimitive");
166222
ComplexTransform = boundingBox.ComplexTransform;
167223
}
168224
}
@@ -188,4 +244,84 @@ public MouseCrosshair(Viewer viewer, Color color)
188244
Material = viewer.MaterialManager.Load("DebugNormals");
189245
}
190246
}
247+
248+
[CallOnThread("Loader")]
249+
public class HandleX : EditorPrimitive
250+
{
251+
public HandleX(Viewer viewer, Color color)
252+
{
253+
var vertexData = GetVertexData(color);
254+
VertexBuffer = new VertexBuffer(viewer.GraphicsDevice, typeof(VertexPositionColor), vertexData.Length, BufferUsage.WriteOnly);
255+
VertexBuffer.SetData(vertexData);
256+
PrimitiveCount = VertexBuffer.VertexCount / 2;
257+
PrimitiveType = PrimitiveType.TriangleList;
258+
Material = viewer.MaterialManager.Load("EditorPrimitive");
259+
}
260+
261+
protected virtual VertexPositionColor[] GetVertexData(Color color) => GetVertexData(0, 1, 2, color);
262+
protected VertexPositionColor[] GetVertexData(int x, int y, int z, Color color)
263+
{
264+
var l = 5f;
265+
var d = 0.1f;
266+
var a = l / 5;
267+
var b = a / 4;
268+
var c = l - a;
269+
var data = new float[][]
270+
{
271+
new[] { 0, +d, 0 },
272+
new[] { c, +d, 0 },
273+
new[] { 0, -d, 0 },
274+
new[] { 0, -d, 0 },
275+
new[] { c, +d, 0 },
276+
new[] { c, -d, 0 },
277+
278+
new[] { 0, 0, +d },
279+
new[] { c, 0, +d },
280+
new[] { 0, 0, -d },
281+
new[] { 0, 0, -d },
282+
new[] { c, 0, +d },
283+
new[] { c, 0, -d },
284+
285+
new[] { l, 0, 0 },
286+
new[] { l - a, +b, +b },
287+
new[] { l - a, -b, +b },
288+
new[] { l, 0, 0 },
289+
new[] { l - a, -b, +b },
290+
new[] { l - a, -b, -b },
291+
new[] { l, 0, 0 },
292+
new[] { l - a, -b, -b },
293+
new[] { l - a, +b, -b },
294+
new[] { l, 0, 0 },
295+
new[] { l - a, +b, -b },
296+
new[] { l - a, +b, +b },
297+
298+
new[] { l - a, +b, +b },
299+
new[] { l - a, +b, -b },
300+
new[] { l - a, -b, -b },
301+
new[] { l - a, -b, -b },
302+
new[] { l - a, +b, -b },
303+
new[] { l - a, -b, +b },
304+
};
305+
var vertexData = new VertexPositionColor[data.Length];
306+
for (var i = 0; i < data.Length; i++)
307+
{
308+
vertexData[i] = new VertexPositionColor(new Vector3(data[i][x], data[i][y], data[i][z]), color);
309+
}
310+
return vertexData;
311+
}
312+
}
313+
314+
[CallOnThread("Loader")]
315+
public class HandleY : HandleX
316+
{
317+
public HandleY(Viewer viewer, Color color) : base(viewer, color) { }
318+
protected override VertexPositionColor[] GetVertexData(Color color) => GetVertexData(2, 0, 1, color);
319+
}
320+
321+
[CallOnThread("Loader")]
322+
public class HandleZ : HandleX
323+
{
324+
public HandleZ(Viewer viewer, Color color) : base(viewer, color) { }
325+
protected override VertexPositionColor[] GetVertexData(Color color) => GetVertexData(1, 2, 0, color);
326+
}
191327
}

0 commit comments

Comments
 (0)