Ron's picture

[Solved] Parent/Child (PushMatrix/PopMatrix) Issue

I am having an issue in the order the shapes are drawn (Parent/Child Relationship).

I have a TreeView which list the shapes. The order of the nodes establishes the Parent/Child relationship. So for example, take a 2 arms. (Shoulder, Elbow, Wrist)

LEFT ARM
-----Shoulder
----------Elbow
---------------Wrist

RIGHT ARM
-----Shoulder
----------Elbow
---------------Wrist

Obviously, when the Right Arm Elbow is rotated, only the Right Elbow should rotate with the Wrist following as a child.

Well, this is not working. I know that I am missing something obvious. I have been reading Beginning Opengl Game Programming 2nd edition (There is an arm example in there), and reading thru the forums.

Here is my code:

        private void Render(eWINDOW window)
        {
            try
            {
                glControl[(int)window].MakeCurrent();
                GL.MatrixMode(MatrixMode.Modelview);
                Matrix4 m = cameraMatrix[(int)window].Transformation;
                GL.LoadMatrix(ref m);
                GL.ClearColor(Color.LightGray);
                GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
                GL.Enable(EnableCap.DepthTest);
 
                DrawGrid(window);
                DrawObjectCoordinate(pGridCoordinateObject, 200f, 200f, 200f);
                int nPreviousLevel = 0;
                int nCurrentLevel = 0;
                foreach (object o in alRenderTable)
                {
                    nPreviousLevel = nCurrentLevel;
                    GL.PushMatrix();
                    nCurrentLevel = ((Primitive.Box)o).Level; 
 
                    GL.Translate(p.X, p.Y, p.Z);
                    GL.Rotate(p.Heading, Vector3.UnitY);
                    GL.Rotate(p.Attitude, Vector3.UnitZ);
                    GL.Rotate(p.Bank, Vector3.UnitX);
                    GL.Scale(p.UniformScale, p.UniformScale, p.UniformScale);
 
                    GL.Begin(BeginMode.Quads);
                    for (int i = 0; i < p.Verticies.Length; i++)
                        {
                            if (!bSelectionRenderPass)
                                GL.Color4(p.ColorArray[i / 12]);
                            GL.Vertex3(p.Verticies[i], p.Verticies[++i], p.Verticies[++i]);
                            GL.Vertex3(p.Verticies[++i], p.Verticies[++i], p.Verticies[++i]);
                            GL.Vertex3(p.Verticies[++i], p.Verticies[++i], p.Verticies[++i]);
                            GL.Vertex3(p.Verticies[++i], p.Verticies[++i], p.Verticies[++i]);
                        }
                    GL.End();
 
                    for (int i = 0; i <= (nPreviousLevel - nCurrentLevel); i++)
                        GL.PopMatrix(); 
                }
                glControl[(int)window].SwapBuffers();
            }
            catch (Exception em) { AddMessage(eMESSAGE_LEVEL.FAULT, "Render", em.Message, ""); }
        }

In this example, I push the matrix each time I draw the Box stored in alRenderTable. Then, I check to see where the next node Box I am drawing is in relationship to the previous node Box. Then I decide if I need to pop the matrix depending on how many levels the node is from the previous node.

No Worky. Describing how is does not work is a bit tricky. But it seems to be a node level off. However, when I change when I pop the matrix, there is no change. I realize these are vague statement, so I am looking for more of a "Don't do it that way... This is a better what of doing it..." answer.

According to the example in the Beginning Opengl Game Programming, this should work (even tho it was done in C++). Also, I have read in the forums here that the PushMatrix should go before the GL.LoadMatrix. However, this was not evident in the book example.

So, I think my approach/understanding of writing a Parent/Child relationship is incorrect.

Any pointers from the group? Sample code to view?

Thanks!
Ron Lindsey


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

This might be a case of unbalanced push-pop calls. Call GL.GetError() right before SwapBuffers() and check whether it reports anything out of order.

jrwatts's picture

I'm rather tired right now, so apologies if I'm completely off base and/or not making sense. :P

The basic problem is that you're popping the matrix stack in the wrong place. Consider--if your current object is at level 1, and your previous object was at level 3, you want to pop the matrix 3 times (to get back to the base translation) before drawing your object. You're doing it after.

Essentially, you just need to move your PopMatrix operation to just before the PushMatrix operation (and load the new nCurrentLevel before the PopMatrix, not after the PushMatrix).

Here's what your logic flow should look like:
First object at level 1 -- nPreviousLevel = 0, nCurrentLevel = 1, Prev - Current = -1 -- doesn't pop anything, pushes the matrix, translates, draws.
2nd object at level 2 -- nPreviousLevel = 1, nCurrentLevel = 2, Prev - Current = -1 -- doesn't pop anything, pushes the matrix, translates, draws.
3rd object at level 3 -- nPreviousLevel = 2, nCurrentLevel = 3, Prev - Current = -1 -- doesn't pop anything, pushes the matrix, translates, draws.
4th object at level 1 -- nPreviousLevel = 3, nCurrentLevel = 1, Prev - Current = 2 -- pops the matrix 3 times (back to base translation), pushes the matrix, translates, draws.
5th object at level 2 -- nPreviousLevel = 1, nCurrentLevel = 2, Prev - Current = -1 -- doesn't pop, pushes, translates, draws.
6th object at level 3 -- nPreviousLevel = 2, nCurrentLevel = 3, Prev - Current = -1 -- doesn't pop, pushes, translates, draws.
7th object at level 2 -- nPreviousLevel = 3, nCurrentLevel = 2, Prev - Current = 1 -- pops the matrix twice (back to 4th object's translation), pushes, translates, draws.
8th object at level 2 -- nPreviousLevel = 2, nCurrentLevel = 2, Prev - Current = 0 -- pops the matrix once (back to 4th object's translation), pushes, translates, draws.
etc.

I am also assuming your objects start at level 1; if they start at level 0, you need to initialize nPreviousLevel to -1, instead, to avoid an extraneous matrix pop before drawing the first object.

Hope this helps!

Edit: I'm also confused by your references to object "p". Your foreach loop is using object "o"; what is "p"? As that code stands, you're drawing the same object (p) over and over again.

Ron's picture

@ Fiddler - Thanks. It was StackUnderFlow.

@ jrwatts - Thanks. Once I modified the code to match the method you shown... it worked. Also, I had merged some code as my example and forgot to change the 'p'.

As a side note... I am very impressed with the OpenTK project. It is obvious that a ton of hours have went into it. Also, this is one of the best communities I have worked with before. Helpful to say the least.

Ron