XNA Creators Club Online
Page 1 of 1 (10 items)
Sort Posts: Previous Next

How to figure out in what order to rotate in a 3D world without trial and error?

Last post 29/06/2009 22:02 by Yuyuyami. 9 replies.
  • 28/06/2009 12:11

    How to figure out in what order to rotate in a 3D world without trial and error?

    I wanted to create an arc ball camera and found the code for a camera like that on the wiki. I did not copy & paste the code straight up, but entered it manually. I did change the order in which the rotation was made though, and needless to say, it did not result in coolness.

    This is my code
    if (Mouse.GetState().ScrollWheelValue != prevMouse.ScrollWheelValue)  
        zoom += (Mouse.GetState().ScrollWheelValue - prevMouse.ScrollWheelValue) / 2;  
     
    if (Mouse.GetState().LeftButton == ButtonState.Pressed)  
    {  
        horizontalAngle += (prevMouse.X - Mouse.GetState().X);  
        if (horizontalAngle > 360)  
            horizontalAngle -= 360;  
        else if (horizontalAngle < -360)  
            horizontalAngle += 360;  
     
        verticalAngle += (Mouse.GetState().Y - prevMouse.Y);  
        verticalAngle = MathHelper.Clamp(verticalAngle, -89, 89);  
    }  
     
    Vector3 position = new Vector3(0, 0, zoom);  
     
    position = Vector3.Transform(position, Matrix.CreateRotationX(MathHelper.ToRadians(verticalAngle)));  
    position = Vector3.Transform(position, Matrix.CreateRotationY(MathHelper.ToRadians(horizontalAngle)));  
     
    pos = position; 

    So I use the mouse to control the camera. I am curious, how would I be able to figure out in what order to rotate the camera without trial and error? I understand why it matters, but I cannot figure out which axis to use for the first rotation.
  • 29/06/2009 2:08 In reply to

    Re: How to figure out in what order to rotate in a 3D world without trial and error?

    The mnemonic device you can use to remember the matrix transformation order (at least in the right hand) is S.R.O.T., which stands for Scale Rotate Orbit Translate.

    The only one that's a bit tricky is Orbit. It's like translating, then rotating. Imagine a yo-yo, okay? If you let out the string a little, that's the translation. Then you rotate the string to orbit the yo-yo around your hand. The more string you let out, the bigger the circle the yo-yo moves in.

    As for which axis to rotate first on, I would imagine X, Y, then Z. I don't know because I wrote my own Rotation class, a sort of hybrid between Quaternion and Euler angles. When it creates a rotation matrix, it uses the Matrix.CreateFromQuaternion() code.

            public static Matrix CreateMatrix(Quaternion r)
            {
                float num9 = r.x * r.x;
                float num8 = r.y * r.y;
                float num7 = r.z * r.z;
                float num6 = r.x * r.y;
                float num5 = r.z * r.w;
                float num4 = r.z * r.x;
                float num3 = r.y * r.w;
                float num2 = r.y * r.z;
                float num = r.x * r.w;

                r.cMatrix.M11 = 1f - (2f * (num8 + num7));
                r.cMatrix.M12 = 2f * (num6 + num5);
                r.cMatrix.M13 = 2f * (num4 - num3);
                r.cMatrix.M14 = 0f;
                r.cMatrix.M21 = 2f * (num6 - num5);
                r.cMatrix.M22 = 1f - (2f * (num7 - num9));
                r.cMatrix.M23 = 2f * (num2 + num3);
                r.cMatrix.M24 = 0f;
                r.cMatrix.M31 = 2f * (num4 + num3);
                r.cMatrix.M32 = 2f * (num2 - num);
                r.cMatrix.M33 = 1f - (2f * (num8 + num9));
                r.cMatrix.M34 = 0f;
                r.cMatrix.M41 = 0f;
                r.cMatrix.M42 = 0f;
                r.cMatrix.M43 = 0f;
                r.cMatrix.M44 = 1f;

                return r.cMatrix;
            }


    While I agree that typing it in while reading it is better because it helps you understand the code (I also do this), I would try copying and pasting the code in from the source, to see if it was a typing error on your part. It's happened to me enough times to make this the case. When I was typing in the SkySphere.fx shader, I could have sworn that it was exactly the same, but when I pasted it in, it suddenly worked. Figures.
    Freefall Game Engine - In Development
  • 29/06/2009 3:25 In reply to

    Re: How to figure out in what order to rotate in a 3D world without trial and error?

    Thanks. I know I need to do the matrix multiplication in a certain order. And the code I pasted above works just fine. If I were to change the rotation order and rotate around the y-axis first though, it would not work very well. My question was, how do I figure out what the order of rotation without trial and error? Maybe your answer is in your post, but I did not manage to find it. Just woke up though. =)
  • 29/06/2009 3:57 In reply to

    Re: How to figure out in what order to rotate in a 3D world without trial and error?

    What's wrong with trial and error? That's how we learn. How many combinations can there be, anyway?

    XYZ 1
    XZY 2
    YXZ 3
    YZX 4
    ZYX 5
    ZXY 6

    In any case, it should probably be either XYZ or YXZ. Try it.
    Freefall Game Engine - In Development
  • 29/06/2009 5:39 In reply to

    Re: How to figure out in what order to rotate in a 3D world without trial and error?

    ShadowyCore:
    What's wrong with trial and error? That's how we learn. How many combinations can there be, anyway?


    But what if there had been 125 combinations? Using trial and error to reach working code does not only take up valuable time, it is also very error prone in many cases, especially when there are numerous possible outcomes.

    In this particular case it is not hard to "trial and error" it, but there is still a reason to why it should be a certain order and I want to understand why. It is the name of the freaking topic! =)

    And I do not have to try it. As I said in my previous post, the code works. I just want to know why it has to be in that order.
  • 29/06/2009 12:44 In reply to

    Re: How to figure out in what order to rotate in a 3D world without trial and error?

    Well, there weren't 125 combinations. The next time you're unsure as to how many possibilities for something there are, compare that against the time involved in trying one out, and do the math. Don't come to the forums asking for a solution with out trial-and-error when so little work is involved. You come across as someone trying to get other people to do your work for you.

    Just a bit of advice. And anyway, why are you so afraid of trail and error? You're on this site in the first place, so I assume you're writing a game or game engine; both involve HUGE amounts of trial-and-error. That's one of the reasons why developers (professional and indie both) write scripting engines, so they don't have to recompile their game engine every time they change something about the game.

    In any case, the phenomenon you're about to experience regarding rotation order is known as Gimbal lock. This undesirable side effect of Euler (pronounced Oiler) angles happens when one angle of rotation (yaw, pitch, roll) turns 90 degrees, and becomes coplanar to another angle of rotation, resulting in the loss of one degree of motion. To account for this, you must use a structure such as the Quaternion, which does not exhibit Gimbal lock. What I did was create a wrapper for the Quaternion struct that still allowed me to use yaw, pitch, roll, but with the preciseness of the Quaternion. As an added bonus, I used the .NET Reflector to take the code out of the Matrix class and use it in my Matrix class.
    Freefall Game Engine - In Development
  • 29/06/2009 17:35 In reply to

    Re: How to figure out in what order to rotate in a 3D world without trial and error?

    I am not afraid of trial and error, but once I get something working I want to understand why, since I then will now it if really is working (in all scenarios and so forth). And on top of that, knowing why will make sure I can write code faster and less buggy in the future, as well as potentially figure out new ways to do things. Building a strong base if you will.

    I had to trial and error to get this code to work since I initially had the rotation in the wrong order. I managed to get it to work through trial and error (and some vague memory about the order in which you do things possibly having an impact on the end result). What I did not manage to do, though, was to use theory to figure out what order to use. For instance, in this particular case I would like to be able to know (or at least be able to figure out) the order I have to use before-hand, not having to go "ok, I will just try this and if that does not work, change it until it works".

    I also rather have someone point in the direction to where I can find the info I am looking for (since I could not find it searching the forum). That should be kind of obvious since I came here searching for information regarding an issue that is working just fine; I am trying to understand it thoroughly. Right now my main goal is not to get something working and be happy with it. My main goal right now is learning.

    Thanks for poiting out why it behaves so weirdly. I recall having read about gimbal lock a while ago, putting it aside since it did not concern me at that point in time. And also since it felt a bit above my skill level at that point too. Off to read up on gimbal lock and study matrices further. And quite possibly get my feet wet when it comes to quaternion, since that seems to have some advantages over matrices.

    And if I came off as a douche I apologize.

  • 29/06/2009 19:26 In reply to

    Re: How to figure out in what order to rotate in a 3D world without trial and error?

    Well, sorry if I came across as a bit gruff. I've just run into too many people wanting to someone to write their code for them. It drives me insane! So, you weren't a douche. Your intentions just weren't immediately clear.

    Anyway, I agree that learning the purpose behind the code, rather than just copying it, is a far superior way to learn. It's the reason (well, the main reason) that I won't use JigLibX or some other physics library. I need to understand how it works, rather than just relying on it to work. Besides, if the people playing my game have a great way to make it better, I won't be able to do it because I don't understand how it works.

    Gimbal lock is a pretty bad phenomenon, and it's why you shouldn't use Euler angles. If you liker, you can try my Rotation class, though I offer no guarantees or warranties. That isn't to say that it's a shoddily written piece of code, but I'm only human. With this, you can use Yaw Pitch Roll without Gimbal lock, as it translates this into Quaternion logic. I never liked Quaternions because of their bizzare system, but this makes it much more understandable:
    (more info below code)

    1 using System; 
    2 using System.Collections.Generic; 
    3 using Microsoft.Xna.Framework; 
    4 using Freefall.Engine.Support; 
    5  
    6 namespace Freefall.Engine.Support 
    7
    8     public struct Rotation 
    9     { 
    10         private Matrix cMatrix; 
    11         private Quaternion cQuaternion; 
    12  
    13         private Vector3 up; // = Vector3.Up; 
    14         private Vector3 down; // = Vector3.Down; 
    15         private Vector3 left; // = Vector3.Left; 
    16         private Vector3 right; // = Vector3.Right; 
    17         private Vector3 forward; // = Vector3.Forward; 
    18         private Vector3 backward; // = Vector3.Backward; 
    19
    20         #region Fields 
    21  
    22         private float yaw; 
    23         private float pitch; 
    24         private float roll; 
    25  
    26         private ClampPair pitchClamp; // = new ClampPair(-180f, 180f, false); 
    27         private ClampPair yawClamp; // = new ClampPair(-180f, 180f, false); 
    28         private ClampPair rollClamp; // = new ClampPair(-180f, 180f, false); 
    29  
    30         private float x; 
    31         private float y; 
    32         private float z; 
    33         private float w; 
    34  
    35         public Matrix Matrix 
    36         { 
    37             get { return CreateMatrix(); } 
    38         } 
    39  
    40         public Quaternion Quaternion 
    41         { 
    42             get { return CreateQuaternion(); } 
    43         } 
    44  
    45         public float Yaw 
    46         { 
    47             get { return yaw; } 
    48             set { yaw = value; CalcXYZW(); } 
    49         } 
    50  
    51         public float Pitch 
    52         { 
    53             get { return pitch; } 
    54             set { pitch = value; CalcXYZW(); } 
    55         } 
    56  
    57         public float Roll 
    58         { 
    59             get { return roll; } 
    60             set { roll = value; CalcXYZW(); } 
    61         } 
    62  
    63         public ClampPair PitchClamp 
    64         { 
    65             get { return pitchClamp; } 
    66             set { pitchClamp = value; ClampValues(); } 
    67         } 
    68  
    69         public ClampPair YawClamp 
    70         { 
    71             get { return yawClamp; } 
    72             set { yawClamp = value; ClampValues(); } 
    73         } 
    74  
    75         public ClampPair RollClamp 
    76         { 
    77             get { return rollClamp; } 
    78             set { rollClamp = value; ClampValues(); } 
    79         } 
    80  
    81         public float X { get { return x; } } 
    82         public float Y { get { return y; } } 
    83         public float Z { get { return z; } } 
    84         public float W { get { return w; } } 
    85
    86         #endregion 
    87
    88         #region Constructors 
    89  
    90         public Rotation(float yaw, float pitch, float roll) 
    91         { 
    92             this.yaw = yaw; 
    93             this.pitch = pitch; 
    94             this.roll = roll; 
    95  
    96             cQuaternion = Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll); 
    97             up = Vector3.Transform(Vector3.Up, cQuaternion); 
    98             down = Vector3.Transform(Vector3.Down, cQuaternion); 
    99             left = Vector3.Transform(Vector3.Left, cQuaternion); 
    100             right = Vector3.Transform(Vector3.Right, cQuaternion); 
    101             forward = Vector3.Transform(Vector3.Forward, cQuaternion); 
    102             backward = Vector3.Transform(Vector3.Backward, cQuaternion); 
    103             cMatrix = Matrix.CreateFromQuaternion(cQuaternion); 
    104  
    105             x = cQuaternion.X; 
    106             y = cQuaternion.Y; 
    107             z = cQuaternion.Z; 
    108             w = cQuaternion.W; 
    109  
    110             pitchClamp = new ClampPair(-180f, 180f, false); 
    111             yawClamp = new ClampPair(-180f, 180f, false); 
    112             rollClamp = new ClampPair(-180f, 180f, false); 
    113  
    114             CalcXYZW(); 
    115         } 
    116  
    117         public Rotation(float x, float y, float z, float w) 
    118         { 
    119             this.x = x; 
    120             this.y = y; 
    121             this.z = z; 
    122             this.w = w; 
    123  
    124             Rotation xyzw = Rotation.CreateFromQuaternion(new Quaternion(x, y, z, w)); 
    125             this.yaw = xyzw.yaw; 
    126             this.pitch = xyzw.pitch; 
    127             this.roll = xyzw.roll; 
    128  
    129             up = Vector3.Transform(Vector3.Up, xyzw.Quaternion); 
    130             down = Vector3.Transform(Vector3.Down, xyzw.Quaternion); 
    131             left = Vector3.Transform(Vector3.Left, xyzw.Quaternion); 
    132             right = Vector3.Transform(Vector3.Right, xyzw.Quaternion); 
    133             forward = Vector3.Transform(Vector3.Forward, xyzw.Quaternion); 
    134             backward = Vector3.Transform(Vector3.Backward, xyzw.Quaternion); 
    135  
    136             pitchClamp = new ClampPair(-180f, 180f, false); 
    137             yawClamp = new ClampPair(-180f, 180f, false); 
    138             rollClamp = new ClampPair(-180f, 180f, false); 
    139  
    140             cQuaternion = new Quaternion(x, y, z, w); 
    141             cMatrix = Microsoft.Xna.Framework.Matrix.CreateFromQuaternion(cQuaternion); 
    142         } 
    143         
    144         #endregion 
    145  
    146         public static Rotation Identity 
    147         { 
    148             get { return new Rotation(0f, 0f, 0f); } 
    149         } 
    150  
    151         //public Vector3 Up { get { return Vector3.Transform(Vector3.Up, CreateQuaternion()); } } 
    152         //public Vector3 Down { get { return Vector3.Transform(Vector3.Down, CreateQuaternion()); } } 
    153         //public Vector3 Left { get { return Vector3.Transform(Vector3.Left, CreateQuaternion()); } } 
    154         //public Vector3 Right { get { return Vector3.Transform(Vector3.Right, CreateQuaternion()); } } 
    155         //public Vector3 Forward { get { return Vector3.Transform(Vector3.Forward, CreateQuaternion()); } } 
    156         //public Vector3 Backward { get { return Vector3.Transform(Vector3.Backward, CreateQuaternion()); } } 
    157  
    158         public Vector3 Up { get { return up; } } 
    159         public Vector3 Down { get { return down; } } 
    160         public Vector3 Left { get { return left; } } 
    161         public Vector3 Right { get { return right; } } 
    162         public Vector3 Forward { get { return forward; } } 
    163         public Vector3 Backward { get { return backward; } } 
    164  
    165         public Vector3 Transform(Vector3 source) { return Transform(source, this); } 
    166         public static Vector3 Transform(Vector3 source, Rotation rotation)  
    167         {  
    168             return Vector3.Transform(source, rotation.CreateQuaternion()); 
    169         } 
    170  
    171         public static Rotation Lerp(Rotation value1, Rotation value2, float amount) 
    172         { 
    173             Rotation ret = new Rotation(); 
    174             ret.yaw = value1.yaw + ((value2.yaw - value1.yaw) * amount); 
    175             ret.pitch = value1.pitch + ((value2.pitch - value1.pitch) * amount); 
    176             ret.roll = value1.roll + ((value2.roll - value1.roll) * amount); 
    177             ret.CalcXYZW(); 
    178             return ret; 
    179         } 
    180  
    181         public static Rotation CreateFromLookAt(Vector3 pos, Vector3 look) { return CreateFromLookAt(pos, look, Vector3.Up); } 
    182         public static Rotation CreateFromLookAt(Vector3 pos, Vector3 look, Vector3 up) 
    183         { 
    184             float pitch = 0f; 
    185             float yaw = 0f; 
    186             float roll = 0f; 
    187  
    188             pitch = (float)Math.Atan((pos.Y - look.Y) / (pos.X - look.Y)); 
    189             yaw = (float)Math.Atan((pos.Z - look.Z) / (pos.X - look.X)); 
    190             roll = (float)Math.Atan((Vector3.Up.Z - look.Z) / (Vector3.Up.Y - look.Y)); 
    191  
    192             return new Rotation(yaw, pitch, roll); 
    193         } 
    194  
    195         public Vector3 CreateEulerAngles() { return CreateEulerAngles(this); } 
    196         public static Vector3 CreateEulerAngles(Rotation rotation) 
    197         { 
    198             return new Vector3(rotation.yaw, rotation.pitch, rotation.roll); 
    199         } 
    200  
    201         public Quaternion CreateQuaternion() { return CreateQuaternion(this); } 
    202         public static Quaternion CreateQuaternion(Rotation r) 
    203         { 
    204             r.CalcXYZW(); 
    205             r.cQuaternion.X = r.X; r.cQuaternion.Y = r.y; r.cQuaternion.Z = r.Z; r.cQuaternion.W = r.w; 
    206             return r.cQuaternion; 
    207         } 
    208  
    209         public Matrix CreateMatrix() { return CreateMatrix(this); } 
    210         public static Matrix CreateMatrix(Rotation r) 
    211         { 
    212             float num9 = r.x * r.x; 
    213             float num8 = r.y * r.y; 
    214             float num7 = r.z * r.z; 
    215             float num6 = r.x * r.y; 
    216             float num5 = r.z * r.w; 
    217             float num4 = r.z * r.x; 
    218             float num3 = r.y * r.w; 
    219             float num2 = r.y * r.z; 
    220             float num = r.x * r.w; 
    221  
    222             r.cMatrix.M11 = 1f - (2f * (num8 + num7)); 
    223             r.cMatrix.M12 = 2f * (num6 + num5); 
    224             r.cMatrix.M13 = 2f * (num4 - num3); 
    225             r.cMatrix.M14 = 0f; 
    226             r.cMatrix.M21 = 2f * (num6 - num5); 
    227             r.cMatrix.M22 = 1f - (2f * (num7 - num9)); 
    228             r.cMatrix.M23 = 2f * (num2 + num3); 
    229             r.cMatrix.M24 = 0f; 
    230             r.cMatrix.M31 = 2f * (num4 + num3); 
    231             r.cMatrix.M32 = 2f * (num2 - num); 
    232             r.cMatrix.M33 = 1f - (2f * (num8 + num9)); 
    233             r.cMatrix.M34 = 0f; 
    234             r.cMatrix.M41 = 0f; 
    235             r.cMatrix.M42 = 0f; 
    236             r.cMatrix.M43 = 0f; 
    237             r.cMatrix.M44 = 1f; 
    238  
    239             return r.cMatrix; 
    240         } 
    241  
    242         public static Rotation CreateFromYawPitchRoll(float yaw, float pitch, float roll) 
    243         { 
    244             Rotation ret = new Rotation(0f, 0f, 0f); 
    245             ret.yaw = yaw; 
    246             ret.pitch = pitch; 
    247             ret.roll = roll; 
    248             ret.ClampValues(); 
    249             return ret; 
    250         } 
    251  
    252         public static Rotation CreateFromQuaternion(Quaternion q) 
    253         { 
    254             Rotation ret = new Rotation(0f, 0f, 0f); 
    255  
    256             ret.yaw = (float)Math.Atan((2f * (q.W * q.X + q.Y * q.Z)) / (1 - 2 * (q.X * q.X + q.Y * q.Y))); 
    257             ret.pitch = (float)Math.Asin(2f * (q.W * q.Y - q.Z * q.X)); 
    258             ret.roll = (float)Math.Atan((2f * (q.W * q.Z + q.X * q.Y)) / (1 - 2 * (q.Y * q.Y + q.Z * q.Z))); 
    259  
    260             ret.ClampValues(); 
    261             return ret; 
    262         } 
    263
    264         #region Operators 
    265  
    266         public static bool operator ==(Rotation r1, Rotation r2) 
    267         { 
    268             return ((r1.yaw == r2.yaw) && (r1.pitch == r2.pitch) && (r1.roll == r2.roll)); 
    269         } 
    270  
    271         public static bool operator !=(Rotation r1, Rotation r2) 
    272         { 
    273             return !((r1.yaw == r2.yaw) && (r1.pitch == r2.pitch) && (r1.roll == r2.roll)); 
    274         } 
    275  
    276         public static Rotation operator +(Rotation r1, Rotation r2) 
    277         { 
    278             Rotation ret = new Rotation(); 
    279             ret.pitch = r1.pitch + r2.pitch; 
    280             ret.yaw = r1.yaw + r2.yaw; 
    281             ret.roll = r1.roll + r2.roll; 
    282             ret.ClampValues(); 
    283             return ret; 
    284         } 
    285  
    286         public static Rotation operator -(Rotation r1, Rotation r2) 
    287         { 
    288             Rotation ret = new Rotation(); 
    289             ret.pitch = r1.pitch - r2.pitch; 
    290             ret.yaw = r1.yaw - r2.yaw; 
    291             ret.roll = r1.roll - r2.roll; 
    292             ret.ClampValues(); 
    293             return ret; 
    294         } 
    295  
    296         public static Rotation operator *(Rotation r1, float amt) 
    297         { 
    298             Rotation ret = new Rotation(); 
    299             ret.pitch = r1.pitch * amt; 
    300             ret.yaw = r1.yaw * amt; 
    301             ret.roll = r1.roll * amt; 
    302             ret.ClampValues(); 
    303             return ret; 
    304         } 
    305  
    306         public static Rotation operator /(Rotation r1, float amt) 
    307         { 
    308             Rotation ret = new Rotation(); 
    309             ret.pitch = r1.pitch / amt; 
    310             ret.yaw = r1.yaw / amt; 
    311             ret.roll = r1.roll / amt; 
    312             ret.ClampValues(); 
    313             return ret; 
    314         } 
    315
    316         #endregion 
    317  
    318         private void CalcXYZW() 
    319         { 
    320             float num9 = roll * 0.5f; 
    321             float num6 = (float)Math.Sin((double)num9); 
    322             float num5 = (float)Math.Cos((double)num9); 
    323             float num8 = pitch * 0.5f; 
    324             float num4 = (float)Math.Sin((double)num8); 
    325             float num3 = (float)Math.Cos((double)num8); 
    326             float num7 = yaw * 0.5f; 
    327             float num2 = (float)Math.Sin((double)num7); 
    328             float num = (float)Math.Cos((double)num7); 
    329  
    330             x = ((num * num4) * num5) + ((num2 * num3) * num6); 
    331             y = ((num2 * num3) * num5) - ((num * num4) * num6); 
    332             z = ((num * num3) * num6) - ((num2 * num4) * num5); 
    333             w = ((num * num3) * num5) + ((num2 * num4) * num6); 
    334  
    335             Quaternion mod = Allocation.Allocate<Quaternion>(new Quaternion(x,y,z,w)); 
    336             //Quaternion mod = new Quaternion(x, y, z, w); 
    337  
    338             up = Vector3.Transform(Vector3.Up, mod); 
    339             down = Vector3.Transform(Vector3.Down, mod); 
    340             left = Vector3.Transform(Vector3.Left, mod); 
    341             right = Vector3.Transform(Vector3.Right, mod); 
    342             forward = Vector3.Transform(Vector3.Forward, mod); 
    343             backward = Vector3.Transform(Vector3.Backward, mod); 
    344  
    345             Allocation.Deallocate<Quaternion>(mod); 
    346         } 
    347  
    348         private void ClampValues() 
    349         { 
    350             yaw = pitchClamp.Clamp(yaw); 
    351             pitch = pitchClamp.Clamp(pitch); 
    352             roll = pitchClamp.Clamp(roll); 
    353             CalcXYZW(); 
    354         } 
    355  
    356         public override string ToString() 
    357         { 
    358             return String.Format("Yaw: {0}, Pitch: {1}, Roll: {2}", yaw, pitch, roll); 
    359         } 
    360     } 
    361  
    362     public struct ClampPair 
    363     { 
    364         public bool Active; 
    365         public float Min; 
    366         public float Max; 
    367         public ClampPair(float min, float max) { this.Min = min; this.Max = max; Active = false; } 
    368         public ClampPair(float min, float max, bool active) { this.Min = min; this.Max = max; Active = active; } 
    369  
    370         public float Clamp(float value) 
    371         { 
    372             if (Active) 
    373                 return MathHelper.Clamp(value, Min, Max); 
    374             else 
    375                 return value; 
    376         } 
    377     } 
    378
    379  


    To use this class, you will also need my Allocation class. Basically, this class uses Dictionaries, Lists, and Stacks to better manage object creation. For example, if you spawn a bullet, the bullet ceases to exist when it hits a wall. When the gun fires again, instead of creating a new bullet, it simply re-uses the old one. You don't have to use this system if you don't want to, but it works pretty good (as far as I've seen). You can edit the Allocation code out of the Rotation class, but I don't know that the performance impact would be (probably not very much, if at all. The GC might work it all out).

    1 using System; 
    2 using System.Collections.Generic; 
    3  
    4 namespace Freefall.Engine.Support 
    5
    6     public static class Allocation 
    7     { 
    8         private static List<Type> types = new List<Type>(); 
    9         private static Dictionary<Type, Stack<object>> spares = new Dictionary<Type, Stack<object>>(); 
    10         private static Dictionary<Type, List<object>> active = new Dictionary<Type, List<object>>(); 
    11  
    12         public static uint UpdatesBetweenInstanceCleanup = 350; 
    13         public static uint UpdatesBetweenTypeCleanup = 3500; 
    14         private static uint updateCount; 
    15  
    16         public static void ResetUpdateCount() { ResetUpdateCount(0); } 
    17         public static void ResetUpdateCount(uint newValue) 
    18         { 
    19             updateCount = newValue; 
    20         } 
    21  
    22         public static void Update() 
    23         { 
    24             if (++updateCount == uint.MaxValue) 
    25                 updateCount = 0; 
    26  
    27             bool instClean = updateCount % UpdatesBetweenInstanceCleanup == 0; 
    28             bool typeClean = updateCount % UpdatesBetweenTypeCleanup == 0; 
    29  
    30             foreach (Type type in types) 
    31             { 
    32                 if (instClean) removeSpares(type); 
    33                 if (typeClean && active[type].Count == 0) removeType(type); 
    34             } 
    35         } 
    36  
    37         public static T Allocate<T>(T value) 
    38         { 
    39             Type type = typeof(T); 
    40             if (!types.Contains(type)) registerNewType(type); 
    41  
    42             if (spares[type].Count > 0) 
    43             { 
    44                 active[type].Add(spares[type].Pop()); 
    45                 active[type][active[type].Count - 1] = value; 
    46             } 
    47             else 
    48             { 
    49                 active[type].Add(value); 
    50             } 
    51             return (T)active[type][active[type].Count - 1]; 
    52         } 
    53  
    54         public static void Deallocate<T>(T obj) 
    55         { 
    56             Type type = typeof(T); 
    57  
    58             if (!types.Contains(type)) 
    59                 throw new Exception("No objects of this type have been allocated."); 
    60  
    61             if (!active[type].Contains(obj)) 
    62                 throw new Exception("This object was never allocated."); 
    63  
    64             active[type].Remove(obj); 
    65             spares[type].Push(obj); 
    66         } 
    67
    68         #region Internal Methods 
    69  
    70         private static void registerNewType(Type type) 
    71         { 
    72             types.Add(type); 
    73             spares.Add(type, new Stack<object>()); 
    74             active.Add(type, new List<object>()); 
    75         } 
    76  
    77         private static void removeType(Type type) 
    78         { 
    79             types.Remove(type); 
    80             spares.Remove(type); 
    81             active.Remove(type); 
    82         } 
    83  
    84         private static void removeSpares(Type type) 
    85         { 
    86             for (int i = spares[type].Count; i < 0; i--) 
    87                 spares[type].Pop(); 
    88         } 
    89
    90         #endregion 
    91  
    92     } 
    93
    94  



    Freefall Game Engine - In Development
  • 29/06/2009 19:40 In reply to

    Re: How to figure out in what order to rotate in a 3D world without trial and error?

    Thanks to some very nice videos on the subject think I understand why I had trouble figuring things out earlier. I always thought that the axes were "locked", as in, the y-axis always being up/down, the x-axis always being left/right etc. Now I know that when rotating an object around an object, the other axes can rotate as well (for two out of the three axes).

    Now I just have to figure out why this is so...
  • 29/06/2009 22:02 In reply to

    Re: How to figure out in what order to rotate in a 3D world without trial and error?

    From what I understand, it's not that the world axises rotate, it's that the relative axises rotate.

    Take, for example, a pen with a clip on the back:


    Hold the front of the pen in your fingers with the clip facing straight up.

    Imagine the X axis goes straight through the pen the long way, the Z axis is straight up and down, and the Y axis is perpendicular to the X axis along a plane level with the floor. Imagine the origin is at the pen's tip.

    Now, rotate the pen straight up around the Y axis. Note that the clip should be facing your arm now.

    Next, rotate the pen around the Z axis.

    Next, return the pen to its original position and rotate the pen around the X axis. The movement will be identical to when you rotated the pen around the Z axis.

    That's because the pen's personal X axis was rotated so it coincided with the world's Z axis. That is Gimbal Lock.


    I hope this helps.
Page 1 of 1 (10 items) Previous Next