Hoi,
Ik heb een Viewport3D in een WPF applicatie (C#) waarin een kubus afgebeeld wordt. Met behulp van een aantal Mouse events kan ik nu de camera met de muis om deze kubus laten draaien. Dit krijg ik voor elkaar met behulp van deze stappen:
Wat ik eigenlijk wil is dat de muis onzichtbaar wordt zodra ik de rechtermuis inhoud (camera roteren kan alleen met rechtermuis ingedrukt) en dat de gebruiker de muis dan 'oneindig lang' kan blijven bewegen zonder dat deze ooit uit het venster raakt en de rotatie stopt.
Ik dacht dat ik dat wel even kon implementeren door deze logica toe te voegen:
Op deze manier zou de muis dus oneindig lang kunnen blijven bewegen, alleen in plaats van bijvoorbeeld van pixels 1, 2, 3, 4, 5... gaat hij nu 1,2,1,2,1,2,1,2...
(Als de muis dus als een gek van links naar rechts gegooid wordt, zo snel dat de MouseMove event maar 1 keer aangeroepen wordt, zou dit nog steeds mis gaan aangezien de muis dan alsnog buiten het scherm kan komen, maar dat is wel erg overdreven...)
Maar goed, dit werkt dus niet helemaal lekker. Het principe werkt echter wel, maar het werkt heel schokkerig. Ik kan niet precies vinden waar het nu aan ligt, misschien dat de muis positie niet exact genoeg terug gezet wordt ofzo
Ik heb er maar even een video van gemaakt, dan is in ieder geval duidelijk wat er gebeurt:
Hier gaat het mis: YouTube: Mouse Fail
Hier gaat het goed: YouTube: Mouse Fail 2
In het eerste filmpje zet ik de muis dus wel steeds terug (ik heb de muis maar een zichtbaar gelaten om het effect te kunnen zien), in het tweede niet, daar loopt alles vloeiend.
De relevante code is als volgt:
De Rotate code is niet relevant (die werkt prima zoals je kan zien in het tweede filmpje).
De MouseUtilities klasse roept simpelweg de SetCursorPos en GetCursorPos API's aan:
Wat doe ik fout? Is er iets fout met de logica die ik gebruik, of gaat dit gewoon mis omdat het mouse event niet snel genoeg gaat, of iets dergelijks..? Kan dit uberhaupt wel lukken op deze manier, of moet ik het in een heel andere hoek zoeken?
Bedankt!
Ik heb een Viewport3D in een WPF applicatie (C#) waarin een kubus afgebeeld wordt. Met behulp van een aantal Mouse events kan ik nu de camera met de muis om deze kubus laten draaien. Dit krijg ik voor elkaar met behulp van deze stappen:
- In het MouseDown event: sla de positie van de muis op (relatief, t.o.v. de viewport), en zet een flag dat de muisknop ingedrukt is
- In het MouseMove event, als de muisknop is ingedrukt:
- Bereken verschil tussen huidige en vorige muis positie
- Voeg dit verschil toe aan een 'totalDx' en 'totalDy' waarden, die in principe de yaw en pitch van de rotatie voorstellen (op een factor na)
- Roteer de camera mbv deze yaw en pitch
- Sla de huidige muis positie op als de vorige muispositie (voor het volgende event)
- In het MouseUp event: Reset de flag dat de muisknop is ingedrukt
Wat ik eigenlijk wil is dat de muis onzichtbaar wordt zodra ik de rechtermuis inhoud (camera roteren kan alleen met rechtermuis ingedrukt) en dat de gebruiker de muis dan 'oneindig lang' kan blijven bewegen zonder dat deze ooit uit het venster raakt en de rotatie stopt.
Ik dacht dat ik dat wel even kon implementeren door deze logica toe te voegen:
- In het MouseDown event: sla ook nog de absolute muis positie op (relatief t.o.v. het scherm)
- In het MouseMove event:
- Lees de huidige absolute muis positie uit en sla lokaal op
- Voer de rotatie uit zoals gewoonlijk
- Zet de absolute muis positie gelijk aan de vorige absolute muis positie
- Sla de huidige absolute muis positie op als de vorige absolute muis positie (voor het volgende event)
Op deze manier zou de muis dus oneindig lang kunnen blijven bewegen, alleen in plaats van bijvoorbeeld van pixels 1, 2, 3, 4, 5... gaat hij nu 1,2,1,2,1,2,1,2...
(Als de muis dus als een gek van links naar rechts gegooid wordt, zo snel dat de MouseMove event maar 1 keer aangeroepen wordt, zou dit nog steeds mis gaan aangezien de muis dan alsnog buiten het scherm kan komen, maar dat is wel erg overdreven...)
Maar goed, dit werkt dus niet helemaal lekker. Het principe werkt echter wel, maar het werkt heel schokkerig. Ik kan niet precies vinden waar het nu aan ligt, misschien dat de muis positie niet exact genoeg terug gezet wordt ofzo
Ik heb er maar even een video van gemaakt, dan is in ieder geval duidelijk wat er gebeurt:
Hier gaat het mis: YouTube: Mouse Fail
Hier gaat het goed: YouTube: Mouse Fail 2
In het eerste filmpje zet ik de muis dus wel steeds terug (ik heb de muis maar een zichtbaar gelaten om het effect te kunnen zien), in het tweede niet, daar loopt alles vloeiend.
De relevante code is als volgt:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
| private bool mouseDown; private Point previousRelativePosition; private Point previousAbsolutePosition; private double totalDx; private double totalDy; private void Grid_MouseDown(object sender, MouseButtonEventArgs e) { if (this.CanMoveCamera) { if (e.RightButton != MouseButtonState.Pressed) return; mouseDown = true; Point pos = Mouse.GetPosition(viewport); previousRelativePosition = new Point(pos.X - viewport.ActualWidth/2, viewport.ActualHeight / 2 - pos.Y); previousAbsolutePosition = MouseUtilities.GetPosition(); //this.Cursor = Cursors.None; } } private void Grid_MouseUp(object sender, MouseButtonEventArgs e) { mouseDown = false; this.Cursor = Cursors.Arrow; } private void Grid_MouseMove(object sender, MouseEventArgs e) { if (!mouseDown) return; // Get absolute mouse position Point absolutePos = MouseUtilities.GetPosition(); // Get mouse position relative to viewport and transform it according to viewport size Point relativePos = Mouse.GetPosition(viewport); Point actualRelativePos = new Point(relativePos.X - viewport.ActualWidth / 2, viewport.ActualHeight / 2 - relativePos.Y); // Calculate difference between previous and current position double dx = actualRelativePos.X - previousRelativePosition.X; double dy = actualRelativePos.Y - previousRelativePosition.Y; totalDx += dx; totalDy += dy; // Rotate this.Rotate(); // Set absolute mouse position back to previous position MouseUtilities.SetPosition(previousAbsolutePosition); // Store current mouse position as previous mouse position for the next event previousRelativePosition = actualRelativePos; previousAbsolutePosition = absolutePos; } |
De Rotate code is niet relevant (die werkt prima zoals je kan zien in het tweede filmpje).
De MouseUtilities klasse roept simpelweg de SetCursorPos en GetCursorPos API's aan:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| public static class MouseUtilities { public static Point GetPosition() { Win32Point w32Mouse = new Win32Point(); GetCursorPos(ref w32Mouse); return new Point(w32Mouse.X, w32Mouse.Y); } public static void SetPosition(Point pt) { SetPosition(pt.X, pt.Y); } public static void SetPosition(double x, double y) { SetCursorPos((int) x, (int) y); } [StructLayout(LayoutKind.Sequential)] internal struct Win32Point { public Int32 X; public Int32 Y; }; [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool GetCursorPos(ref Win32Point pt); [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "SetCursorPos")] [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)] internal static extern bool SetCursorPos(int x, int y); } |
Wat doe ik fout? Is er iets fout met de logica die ik gebruik, of gaat dit gewoon mis omdat het mouse event niet snel genoeg gaat, of iets dergelijks..? Kan dit uberhaupt wel lukken op deze manier, of moet ik het in een heel andere hoek zoeken?
Bedankt!