Skip to content

Commit 9bc4b45

Browse files
committed
Automatic merge of T1.5.1-757-gef6c1a8c8 and 17 pull requests
- Pull request #570 at 3539862: Experimental glTF 2.0 support with PBR lighting - Pull request #839 at d00beb9: First phase of https://blueprints.launchpad.net/or/+spec/additional-cruise-control-parameters - Pull request #874 at f8dbeab: Dynamic brake controller refactoring - Pull request #875 at 43bf33e: Bug fix for https://bugs.launchpad.net/or/+bug/2036346 Player train switching doesn't work with 3D cabs - Pull request #876 at f92de76: docs: add source for documents previously on website to source Documentation folder - Pull request #882 at a055bca: Blueprint/train car operations UI window - Pull request #885 at c81447b: feat: Add notifications to Menu - Pull request #886 at 1b88d7a: Scene viewer extension to TrackViewer - Pull request #888 at b20b888: docs: Document player application model - Pull request #892 at 1f5ba4c: Signal Function OPP_SIG_ID_TRAINPATH - Pull request #893 at bf8876b: Signal errors - Pull request #894 at 5ff1e73: Correct Decrease Colour - Pull request #896 at 5866028: First implementation of https://blueprints.launchpad.net/or/+spec/specific-sounds-for-ai-trains - Pull request #897 at 0a9d939: feat: Improved system information collection - Pull request #898 at e271395: Extra line with all the arguments for debugging purposes in logfile - Pull request #899 at be2319c: Duplex steam engines - Booster Engine addition - Pull request #900 at 42ea7ad: DMI updates
19 parents 573d2e4 + ef6c1a8 + 3539862 + d00beb9 + f8dbeab + 43bf33e + f92de76 + a055bca + c81447b + 1b88d7a + b20b888 + 1f5ba4c + bf8876b + 5ff1e73 + 5866028 + 0a9d939 + e271395 + be2319c + 42ea7ad commit 9bc4b45

File tree

3 files changed

+270
-62
lines changed

3 files changed

+270
-62
lines changed

Source/Contrib/TrackViewer/UserInterface/SceneView.xaml

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,38 @@
1010
<CommandBinding Command="Undo" Executed="UndoCommand" CanExecute="UndoRedoCanExecute"/>
1111
<CommandBinding Command="Redo" Executed="RedoCommand" CanExecute="UndoRedoCanExecute"/>
1212
<CommandBinding Command="Stop" Executed="CancelCommand"/>
13-
<CommandBinding Command="MoveLeft" Executed="RotateCommand"/>
14-
<CommandBinding Command="MoveRight" Executed="MoveCommand"/>
15-
<CommandBinding Command="MoveUp" Executed="MoveHandleCommand"/>
13+
<CommandBinding Command="{x:Static local:SceneView.Rotate}" Executed="RotateCommand"/>
14+
<CommandBinding Command="{x:Static local:SceneView.Move}" Executed="MoveCommand"/>
15+
<CommandBinding Command="{x:Static local:SceneView.MoveHandle}" Executed="MoveHandleCommand"/>
16+
<CommandBinding Command="{x:Static local:SceneView.ToggleOrtho}" Executed="ToggleOrthoCommand"/>
17+
<CommandBinding Command="{x:Static local:SceneView.ToggleElevationLock}" Executed="ToggleElevationLockCommand"/>
18+
<CommandBinding Command="{x:Static local:SceneView.ToggleObjectSnap}" Executed="ToggleObjectSnapCommand"/>
1619
</Window.CommandBindings>
1720
<Window.InputBindings>
18-
<KeyBinding Key="Esc" Command="Stop"/><!--This doesn't work-->
19-
<KeyBinding Key="M" Command="MoveRight"/>
20-
<KeyBinding Key="R" Command="MoveLeft"/>
21-
<KeyBinding Key="H" Command="MoveUp"/>
21+
<KeyBinding Key="Esc" Command="Stop"/><!--This one doesn't work, Escape is eaten somewhere, need to workaround it in code-->
22+
<KeyBinding Key="R" Command="{x:Static local:SceneView.Rotate}"/>
23+
<KeyBinding Key="M" Command="{x:Static local:SceneView.Move}"/>
24+
<KeyBinding Key="H" Command="{x:Static local:SceneView.MoveHandle}"/>
25+
<KeyBinding Key="F3" Command="{x:Static local:SceneView.ToggleObjectSnap}"/>
26+
<KeyBinding Key="F7" Command="{x:Static local:SceneView.ToggleElevationLock}"/>
27+
<KeyBinding Key="F8" Command="{x:Static local:SceneView.ToggleOrtho}"/>
2228
</Window.InputBindings>
2329
<DockPanel LastChildFill="True">
2430
<Menu DockPanel.Dock="Top" Height="22" Name="menuMain" Width="Auto">
2531
<MenuItem Header="_File">
2632
<MenuItem Header="Nothing to see here" Name="menuNothing" />
2733
</MenuItem>
2834
<MenuItem Header="_Edit">
29-
<MenuItem Header="_Undo" Name="menuUndo" Command="Undo" InputGestureText="Ctrl+Z"/>
30-
<MenuItem Header="_Redo" Name="menuRedo" Command="Redo" InputGestureText="Ctrl+Y"/>
35+
<MenuItem Header="_Undo" Command="Undo" InputGestureText="Ctrl+Z"/>
36+
<MenuItem Header="_Redo" Command="Redo" InputGestureText="Ctrl+Y"/>
3137
<Separator/>
32-
<MenuItem Header="_Move" Name="menuName" Command="MoveRight" InputGestureText="M"/>
33-
<MenuItem Header="_Rotate" Name="menuRotate" Command="MoveLeft" InputGestureText="R"/>
34-
<MenuItem Header="Move _handle" Name="menuHandle" Command="MoveUp" InputGestureText="H"/>
38+
<MenuItem Header="_Move" Command="{x:Static local:SceneView.Move}" InputGestureText="M"/>
39+
<MenuItem Header="_Rotate" Command="{x:Static local:SceneView.Rotate}" InputGestureText="R"/>
40+
<MenuItem Header="Move _Handle" Command="{x:Static local:SceneView.MoveHandle}" InputGestureText="H"/>
41+
<Separator/>
42+
<MenuItem Header="Ortho Mode X-Z" Command="{x:Static local:SceneView.ToggleOrtho}" InputGestureText="F8"/>
43+
<MenuItem Header="Elevation Lock Y" Command="{x:Static local:SceneView.ToggleElevationLock}" InputGestureText="F7"/>
44+
<MenuItem Header="Object Snap" Command="{x:Static local:SceneView.ToggleObjectSnap}" InputGestureText="F3" IsCheckable="True"/>
3545
</MenuItem>
3646
<MenuItem Header="_View">
3747
<MenuItem Header="Nothing to see here" Name="menuNothing3" />

Source/Contrib/TrackViewer/UserInterface/SceneView.xaml.cs

Lines changed: 118 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,32 @@ public partial class SceneView : Window
3333
EditorState EditorState;
3434
EditorMoveState EditorMoveState;
3535
StaticShape SelectedObject;
36+
StaticShape SnappedObject;
3637
WorldFile SelectedWorldFile;
3738
Orts.Formats.Msts.WorldObject SelectedWorldObject;
3839
StaticShape MovedObject;
3940
WorldPosition MovedObjectOriginalPosition;
4041
WorldPosition HandlePosition;
4142
WorldPosition HandleOriginalPosition;
4243
float DeltaX, DeltaY, DeltaZ;
44+
Vector3 ConstructionX, ConstructionY, ConstructionZ;
45+
float ConstructionAngle;
4346
UndoDataSet DeltaContext;
4447
WorldLocation CursorLocation;
4548
readonly List<(int TileX, int TileZ)> FlaggedTiles = new List<(int, int)>();
4649

50+
bool OrthoMode { get; set; }
51+
bool ElevationLock { get; set; }
52+
bool ObjectSnap { get; set; }
53+
54+
// Editing commands
55+
public static RoutedCommand Rotate = new RoutedCommand();
56+
public static RoutedCommand Move = new RoutedCommand();
57+
public static RoutedCommand MoveHandle = new RoutedCommand();
58+
public static RoutedCommand ToggleOrtho = new RoutedCommand();
59+
public static RoutedCommand ToggleElevationLock = new RoutedCommand();
60+
public static RoutedCommand ToggleObjectSnap = new RoutedCommand();
61+
4762
public SceneView(IntPtr hostWindow)
4863
{
4964
InitializeComponent();
@@ -64,31 +79,21 @@ public void Update(GameTime gameTime)
6479
{
6580
ApplicationCommands.Stop.Execute(null, null);
6681
}
67-
68-
if (EditorState == EditorState.Default || EditorState == EditorState.ObjectSelected)
69-
{
70-
if (UserInput.IsMouseLeftButtonPressed && UserInput.ModifiersMaskShiftCtrlAlt(false, false, false))
71-
{
72-
if (Camera.PickByMouse(out var selectedObject))
73-
{
74-
SelectedObject = selectedObject;
75-
SelectedObjectChanged();
76-
EditorState = EditorState.ObjectSelected;
77-
}
78-
}
79-
}
80-
if (EditorState == EditorState.HandleMoving)
81-
{
82-
if (UserInput.IsMouseLeftButtonPressed)
83-
{
84-
ApplyHandleMove();
85-
}
86-
}
87-
if (EditorState == EditorState.ObjectMoving)
82+
if (UserInput.IsMouseLeftButtonPressed)
8883
{
89-
if (UserInput.IsMouseLeftButtonPressed)
84+
switch (EditorState)
9085
{
91-
ApplyObjectMove();
86+
case EditorState.Default:
87+
case EditorState.ObjectSelected:
88+
if (UserInput.ModifiersMaskShiftCtrlAlt(false, false, false) && Camera.PickByMouse(out var selectedObject))
89+
{
90+
SelectedObject = selectedObject;
91+
SelectedObjectChanged();
92+
EditorState = EditorState.ObjectSelected;
93+
}
94+
break;
95+
case EditorState.HandleMoving: ApplyHandleMove(); break;
96+
case EditorState.ObjectMoving: ApplyObjectMove(); break;
9297
}
9398
}
9499

@@ -102,9 +107,30 @@ public void Update(GameTime gameTime)
102107
// A second pass after user input handled, do the effective work
103108
if (EditorState == EditorState.ObjectMoving)
104109
{
110+
if (ObjectSnap && Camera.PickByMouse(out var snappedObject))
111+
{
112+
if (snappedObject != SnappedObject)
113+
Viewer.EditorShapes.BoundingBoxShapes.RemoveAll(s => s == SnappedObject);
114+
SnappedObject = snappedObject;
115+
if (!Viewer.EditorShapes.BoundingBoxShapes.Contains(SnappedObject))
116+
Viewer.EditorShapes.BoundingBoxShapes.Add(SnappedObject);
117+
}
118+
else
119+
{
120+
Viewer.EditorShapes.BoundingBoxShapes.RemoveAll(s => s == SnappedObject);
121+
SnappedObject = null;
122+
}
105123
MovedObject.Location.XNAMatrix = GetMovingMatrix(MovedObjectOriginalPosition, HandleOriginalPosition, HandlePosition);
106124
Viewer.EditorShapes.MovedObject = MovedObject;
107125
Viewer.EditorShapes.MovedObjectLocation = MovedObject.Location;
126+
Viewer.EditorShapes.HandleLocation = HandlePosition;
127+
Viewer.EditorShapes.ConstructionOriginalTranslation = (HandleOriginalPosition ?? MovedObjectOriginalPosition).XNAMatrix.Translation;
128+
Viewer.EditorShapes.ConstructionLinesEnabled = true;
129+
Viewer.EditorShapes.ConsturcionLineAngleStyle = EditorMoveState == EditorMoveState.Rotate;
130+
Viewer.EditorShapes.ConstructionLineX = ConstructionX;
131+
Viewer.EditorShapes.ConstructionLineY = ConstructionY;
132+
Viewer.EditorShapes.ConstructionLineZ = ConstructionZ;
133+
Viewer.EditorShapes.ConstructionAngle = ConstructionAngle;
108134
}
109135
else
110136
{
@@ -116,6 +142,18 @@ public void Update(GameTime gameTime)
116142
{
117143
HandlePosition.XNAMatrix = GetMovingMatrix(HandleOriginalPosition);
118144
Viewer.EditorShapes.HandleLocation = HandlePosition;
145+
Viewer.EditorShapes.ConstructionOriginalTranslation = HandleOriginalPosition.XNAMatrix.Translation;
146+
Viewer.EditorShapes.ConstructionLinesEnabled = true;
147+
Viewer.EditorShapes.ConsturcionLineAngleStyle = EditorMoveState == EditorMoveState.Rotate;
148+
Viewer.EditorShapes.ConstructionLineX = ConstructionX;
149+
Viewer.EditorShapes.ConstructionLineY = ConstructionY;
150+
Viewer.EditorShapes.ConstructionLineZ = ConstructionZ;
151+
Viewer.EditorShapes.ConstructionAngle = ConstructionAngle;
152+
}
153+
154+
if (EditorState != EditorState.ObjectMoving && EditorState != EditorState.HandleMoving)
155+
{
156+
Viewer.EditorShapes.ConstructionLinesEnabled = false;
119157
}
120158

121159
FillDeltaStatus();
@@ -198,39 +236,59 @@ public async Task SetCameraLocation(WorldLocation worldLocation)
198236

199237
Matrix GetMovingMatrix(in WorldPosition originalPosition, in WorldPosition handleOriginalPosition = null, WorldPosition handlePosition = null)
200238
{
201-
var handle = handleOriginalPosition ?? originalPosition;
239+
var handle = handlePosition ?? originalPosition;
202240
var xnaMatrix = originalPosition.XNAMatrix;
203241

204242
if (EditorMoveState == EditorMoveState.Rotate)
205243
{
206-
var distance = WorldLocation.GetDistance(handle.WorldLocation, CursorLocation);
207-
distance.Z *= -1;
244+
float constructionRadius;
245+
double targetAngle;
246+
if (ObjectSnap && SnappedObject != null)
247+
{
248+
var distance = WorldLocation.GetDistance(handle.WorldLocation, SnappedObject.Location.WorldLocation);
249+
distance.Z *= -1;
250+
distance.Y = 0;
251+
constructionRadius = distance.Length();
252+
targetAngle = Math.Atan2(SnappedObject.Location.XNAMatrix.M13, SnappedObject.Location.XNAMatrix.M33);
253+
}
254+
else
255+
{
256+
var distance = WorldLocation.GetDistance(handle.WorldLocation, CursorLocation);
257+
distance.Z *= -1;
258+
distance.Y = 0;
259+
constructionRadius = distance.Length();
260+
targetAngle = Math.Atan2(distance.Z, distance.X);
261+
}
208262

209-
var angle = MathHelper.WrapAngle((float)(Math.Atan2(originalPosition.XNAMatrix.M13, originalPosition.XNAMatrix.M33) - Math.Atan2(distance.Z, distance.X)));
263+
var angle = MathHelper.WrapAngle((float)(Math.Atan2(originalPosition.XNAMatrix.M13, originalPosition.XNAMatrix.M33) - targetAngle));
210264
var rotation = Matrix.CreateFromYawPitchRoll(angle, 0, 0);
211265
var translation = handle.XNAMatrix.Translation;
212266
xnaMatrix.Translation -= translation;
213267
xnaMatrix *= rotation;
214268
xnaMatrix.Translation += translation;
215269

270+
DeltaX = 0;
271+
DeltaY = MathHelper.ToDegrees(angle);
272+
DeltaZ = 0;
273+
ConstructionAngle = angle;
274+
ConstructionX = constructionRadius * Vector3.UnitX;
275+
ConstructionZ = constructionRadius * Vector3.Transform(Vector3.UnitX, Matrix.CreateFromYawPitchRoll(-angle, 0, 0));
276+
216277
if (handlePosition != null && handleOriginalPosition != null)
217278
{
218-
angle = MathHelper.WrapAngle((float)(Math.Atan2(handleOriginalPosition.XNAMatrix.M13, handleOriginalPosition.XNAMatrix.M33) - Math.Atan2(distance.Z, distance.X)));
279+
angle = MathHelper.WrapAngle((float)(Math.Atan2(handleOriginalPosition.XNAMatrix.M13, handleOriginalPosition.XNAMatrix.M33) - targetAngle));
219280
rotation = Matrix.CreateFromYawPitchRoll(angle, 0, 0);
220281
var handleMatrix = handleOriginalPosition.XNAMatrix;
221282
handleMatrix.Translation -= translation;
222283
handleMatrix *= rotation;
223284
handleMatrix.Translation += translation;
224285
handlePosition.XNAMatrix = handleMatrix;
225286
}
226-
227-
DeltaX = 0;
228-
DeltaY = MathHelper.ToDegrees(angle);
229-
DeltaZ = 0;
230287
}
231288
else if (EditorMoveState == EditorMoveState.Move)
232289
{
233-
var distance = WorldLocation.GetDistance(originalPosition.WorldLocation, CursorLocation);
290+
var targetLocation = ObjectSnap && SnappedObject != null ? SnappedObject.Location.WorldLocation : CursorLocation;
291+
var distance = WorldLocation.GetDistance(originalPosition.WorldLocation, targetLocation);
234292
distance.Z *= -1;
235293

236294
var axisX = Vector3.Normalize(handle.XNAMatrix.Right);
@@ -239,7 +297,7 @@ Matrix GetMovingMatrix(in WorldPosition originalPosition, in WorldPosition handl
239297

240298
var tileLocation = xnaMatrix.Translation;
241299

242-
if (UserInput.IsDown(UserCommand.EditorLockOrthogonal))
300+
if (OrthoMode)
243301
{
244302
var distanceX = Vector3.Dot(axisX, distance);
245303
var distanceZ = Vector3.Dot(axisZ, distance);
@@ -252,7 +310,7 @@ Matrix GetMovingMatrix(in WorldPosition originalPosition, in WorldPosition handl
252310
tileLocation.Z += distance.Z;
253311
}
254312

255-
if (!UserInput.IsDown(UserCommand.EditorLockElevation))
313+
if (!ElevationLock)
256314
{
257315
tileLocation.Y = Viewer.Tiles.GetElevation(handle.TileX, handle.TileZ, tileLocation.X, -tileLocation.Z);
258316
}
@@ -270,6 +328,9 @@ Matrix GetMovingMatrix(in WorldPosition originalPosition, in WorldPosition handl
270328
DeltaX = Vector3.Dot(axisX, distance);
271329
DeltaY = Vector3.Dot(axisY, distance);
272330
DeltaZ = Vector3.Dot(axisZ, distance);
331+
ConstructionX = DeltaX * Vector3.UnitX;
332+
ConstructionY = DeltaY * Vector3.UnitY;
333+
ConstructionZ = DeltaZ * Vector3.UnitZ;
273334
}
274335
return xnaMatrix;
275336
}
@@ -376,6 +437,11 @@ void CancelObjectMove()
376437
{
377438
MovedObject.Location.CopyFrom(MovedObjectOriginalPosition);
378439
MovedObject = null;
440+
if (HandleOriginalPosition != null)
441+
HandlePosition.CopyFrom(HandleOriginalPosition);
442+
else
443+
HandlePosition = null;
444+
Viewer.EditorShapes.BoundingBoxShapes.Clear();
379445
EditorState = EditorState.ObjectSelected;
380446
}
381447

@@ -395,6 +461,7 @@ void ApplyObjectMove()
395461

396462
DeltaContext = UndoStack.Peek();
397463
MovedObject = null;
464+
Viewer.EditorShapes.BoundingBoxShapes.Clear();
398465
EditorState = EditorState.ObjectSelected;
399466
}
400467

@@ -410,12 +477,14 @@ void CancelHandleMove()
410477
{
411478
HandlePosition = null;
412479
HandleOriginalPosition = null;
480+
Viewer.EditorShapes.BoundingBoxShapes.Clear();
413481
EditorState = EditorState.ObjectSelected;
414482
}
415483

416484
void ApplyHandleMove()
417485
{
418486
HandleOriginalPosition = new WorldPosition(HandlePosition);
487+
Viewer.EditorShapes.BoundingBoxShapes.Clear();
419488
EditorState = EditorState.ObjectSelected;
420489
}
421490

@@ -424,8 +493,10 @@ void SelectedObjectChanged()
424493
Viewer.EditorShapes.SelectedObject = SelectedObject;
425494
Viewer.EditorShapes.MovedObject = null;
426495
Viewer.EditorShapes.HandleLocation = null;
496+
Viewer.EditorShapes.BoundingBoxShapes.Clear();
427497
HandlePosition = null;
428498
HandleOriginalPosition = null;
499+
SnappedObject = null;
429500

430501
SelectedWorldFile = Viewer.World.Scenery.WorldFiles.SingleOrDefault(w => w.TileX == SelectedObject?.Location.TileX && w.TileZ == SelectedObject?.Location.TileZ);
431502
SelectedWorldObject = SelectedWorldFile?.MstsWFile?.Tr_Worldfile?.SingleOrDefault(o => o.UID == SelectedObject?.Uid);
@@ -477,11 +548,6 @@ protected override void OnClosing(CancelEventArgs e)
477548
Hide();
478549
}
479550

480-
private void IntValidationTextBox(object sender, TextCompositionEventArgs e)
481-
{
482-
e.Handled = int.TryParse(e.Text, out var _);
483-
}
484-
485551
private void UndoRedoCanExecute(object sender, CanExecuteRoutedEventArgs e)
486552
{
487553
e.CanExecute = EditorState == EditorState.Default || EditorState == EditorState.ObjectSelected;
@@ -500,25 +566,34 @@ private void CancelCommand(object sender, ExecutedRoutedEventArgs e)
500566
private void RotateCommand(object sender, ExecutedRoutedEventArgs e)
501567
{
502568
EditorMoveState = EditorMoveState.Rotate;
503-
504569
if (EditorState == EditorState.ObjectSelected)
505570
StartObjectMove();
571+
Keyboard.Focus(DeltaYBlock);
506572
}
507573

508574
private void MoveCommand(object sender, ExecutedRoutedEventArgs e)
509575
{
510576
EditorMoveState = EditorMoveState.Move;
511-
512577
if (EditorState == EditorState.ObjectSelected)
513578
StartObjectMove();
579+
Keyboard.Focus(DeltaXBlock);
514580
}
515581

516582
private void MoveHandleCommand(object sender, ExecutedRoutedEventArgs e)
517583
{
518584
EditorMoveState = EditorMoveState.Move;
519-
520585
if (EditorState == EditorState.ObjectSelected)
521586
StartHandleMove();
587+
Keyboard.Focus(DeltaXBlock);
588+
}
589+
590+
private void ToggleOrthoCommand(object sender, ExecutedRoutedEventArgs e) => OrthoMode = !OrthoMode;
591+
private void ToggleElevationLockCommand(object sender, ExecutedRoutedEventArgs e) => ElevationLock = !ElevationLock;
592+
private void ToggleObjectSnapCommand(object sender, ExecutedRoutedEventArgs e) => ObjectSnap = !ObjectSnap;
593+
594+
private void IntValidationTextBox(object sender, TextCompositionEventArgs e)
595+
{
596+
e.Handled = int.TryParse(e.Text, out var _);
522597
}
523598

524599
private void UintValidationTextBox(object sender, TextCompositionEventArgs e)

0 commit comments

Comments
 (0)