[WPF] 3D orbit in plaats van rotatie

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 09-09 10:50
Hey,


Ik heb in WPF een aantal 3D objecten (als voorbeeld neem maar even een kubus aan) waar ik met een camera, met behulp van de muis, omheen wil draaien. Het belangrijke punt is dat ik alleen de pitch en yaw wil veranderen, en niet de roll van de camera.

Ik heb wat voorbeeldjes gezocht op google (ik kon er trouwens niet veel vinden?), maar de voorbeeldjes die ik wel vind zijn niet precies wat ik nodig heb.

Het duidelijkste voorbeeld van het roteren in WPF 3d dat ik heb kunnen vinden is dit artikel:
http://www.codeproject.com/KB/WPF/Wpf3DPrimer.aspx

Dat werkt prima, hoewel het voor zover ik kan zien het object roteert in plaats van de camera. Nou ja, door de transformaties gewoon op de camera toe te passen werkt het ook, zolang ik de rotatie as even negatief maak.

Maar deze rotate is niet precies wat ik nodig heb, hier kun je de camera ook rollen, en dat wil ik niet.

Ik kan het wat lastig onder woorden brengen, maar gelukkig heb ik een voorbeeldje gevonden:
http://pv3d.org/2008/11/19/dragging-mouse-for-camera-orbit/

De code in dat voorbeeldje telt gewoon dy bij de pitch op en dx bij de yaw, en gebruikt die waarden in een 'orbit' methode, waarvan ik geen idee heb wat die precies doet.


Ik probeer nu dit op dezelfde manier te doen, maar ik probeer nog steeds een Quaternion gebruiken om de rotatie uit te voeren. Misschien is er een andere manier om dit te doen, maar quaternions gebruiken is tot nu toe de enige manier die ik kan vinden om rotaties uit te voeren.
Ik weet wel ongeveer wat een quaternion is, maar hoe dit zich exact tot rotaties vertaald is me nog niet helemaal duidelijk.

In elk geval, ik wil dus een Quaternion maken vanuit de waarden van pitch, yaw en roll (= 0). Op Wikipedia: Conversion between quaternions and Euler angles vond ik deze formule om een quaternion q te maken uit de roll (phi), pitch (theta) en yaw (psi):
Afbeeldingslocatie: http://upload.wikimedia.org/math/8/f/2/8f24d7035f36dd398e6253a521d7ac0c.png
Ik dacht, ik maak gewoon een nieuwe Quaternion met deze waarden, waarbij ik de roll hoek (phi) gewoon 0 laat:
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
            yaw += dx;
            pitch += dy;

            yaw %= 360;
            pitch %= 360;

            pitch = pitch > 0 ? pitch : 0.000001;
            pitch = pitch < 90 ? pitch : 89.999999;

            var phi = roll / 2D;   // roll = 0
            var theta = pitch / 2D;
            var psi = yaw / 2D;

            var q = new Quaternion
                               {
                                   X = cos(phi) * cos(theta) * cos(psi) + sin(phi) * sin(theta) * sin(psi),
                                   Y = sin(phi) * cos(theta) * cos(psi) - cos(phi) * sin(theta) * sin(psi),
                                   Z = cos(phi) * sin(theta) * cos(psi) + sin(phi) * cos(theta) * sin(psi),
                                   W = cos(phi) * cos(theta) * sin(psi) - sin(phi) * sin(theta) * cos(psi)
                               };

            Transform3DGroup group = camera.Transform as Transform3DGroup;
            QuaternionRotation3D r = new QuaternionRotation3D(q);
            group.Children.Add(new RotateTransform3D(r));

cos en sin zijn beide functies die de hoek in graden accepteren, omrekenen naar radialen en daarna gewoon Math.Cos en Math.Sin aanroepen.

NB: ik weet niet of de volgorde (X,Y,Z,W) in mijn code juist overeenkomt met die van de formule in wikipedia, ik heb ook (W,X,Y,Z) geprobeerd met hetzelfde resultaat.

Ik weet, zolang roll (en dus phi) gelijk aan 0 is kan ik de helft van die berekeningen weglaten (sin(0) = 0 en cos(0) = 1), maar ik weet dus niet zeker of de roll wel 0 moet blijven (misschien moet hij juist 90 zijn ofzo), dus heb ik ze maar erin gelaten.


Anyway, dit werkt dus voor geen meter. Het lijkt alsof de camera nu met 6x de lichtsnelheid beweegt ( :+ ), elke pixel die ik beweeg vertaald zich in een ogenschijnlijke willekeurige rotatie, en nog steeds over alle drie de assen.


Ik snap er even niets meer van...

Hoe zorg ik er nou voor dat ik met de muis een rotatie kan uitvoeren, waarbij ik de roll constant houdt, zoals in het flash voorbeeldje?

Bedankt!

[ Voor 3% gewijzigd door NickThissen op 04-01-2011 20:07 ]

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • beany
  • Registratie: Juni 2001
  • Laatst online: 14-09 09:35

beany

Meeheheheheh

Volgens mij is deze wel interessant: http://viewport3d.com/trackball.htm

Dagelijkse stats bronnen: https://x.com/GeneralStaffUA en https://www.facebook.com/GeneralStaff.ua


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 09-09 10:50
Die had ik inderdaad al gevonden, maar die voegt dus ook een roll toe aan de rotatie (volgens mij werkt het principe daar exact hetzelfde als het CodeProject artikel).


Maar het is inmiddels opgelost.
Code voor iemand die hetzelfde probleem heeft:
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
59
60
61
        private bool mouseDown;
        private Point previousRelativePosition;
        private double totalDx;
        private double totalDy;

        private void Rotate()
        {
            if (!this.CanMoveCamera) return;

            double theta = totalDx / 3;
            double phi = totalDy / 3;

            if (phi < -90) phi = -90;
            if (phi > 90) phi = 90;

            Vector3D thetaAxis = new Vector3D(0, 1, 0);
            Vector3D phiAxis = new Vector3D(-1, 0, 0);

            Transform3DGroup transformGroup = camera.Transform as Transform3DGroup;
            transformGroup.Children.Clear();
            QuaternionRotation3D r = new QuaternionRotation3D(new Quaternion(-phiAxis, phi));
            transformGroup.Children.Add(new RotateTransform3D(r));
            r = new QuaternionRotation3D(new Quaternion(-thetaAxis, theta));
            transformGroup.Children.Add(new RotateTransform3D(r));
        }

        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);
            }
        }

        private void Grid_MouseMove(object sender, MouseEventArgs e)
        {
            if (!mouseDown) return;

            // 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();

            // Store current mouse position as previous mouse position for the next event
            previousRelativePosition = actualRelativePos;
        }

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:28

.oisyn

Moderator Devschuur®

Demotivational Speaker

Idd, je moet gewoon steeds een nieuwe rotatie opbouwen op basis van je pitch en yaw. Dit kan met zowel quaternions als matrices door ze gewoon met elkaar te vermenigvuldigen, of door WPF dat te laten doen door beide rotaties aan je object toe te voegen.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.