5
\$\begingroup\$

I am currently playing around with 3D transformations applied on a 2D context in LWJGL, to later be implemented in my game. So I managed to set up LWJGL well enough (scaling, translating, scaling and even more translating) to be able to use the "default" 2D-coordinate system again after I switched to glPerspective instead of glOrtho (otherwise you wouldn't be able to see the effect).

This works perfectly well and I can obscure and transform any 2D-object I desire. However, there is a problem with rendering images. In order to see the effect in more detail, any piece of geometry has to be cut down and rendered multiple times in smaller parts at different heights, the height is calculated with a function using x and y as parameters. With no rotation, this also works for images.

But when I try to do the same for images with rotation it doesn't work at first, obviously (if that's not so obvious: I take the current rendering coordinates as parameters for the z-function, which are of course incorrect in a rotation-transformed matrix). Therefore I changed the rendering function to also take rotation into account properly - and - doesn't work: The image still acts as though the function was applied to a non-rotated object, while the image itself is rendered with rotation. Looks weird, creates seams and is totally not what I intended.

I double- and triple-checked the values that are used as height, but my manual rotation transformations seem to be correct. The top left corner acts like the top bottom order if rotated around the center with 45 degrees for example, which is exactly where it will be in the final rendered version.

This is the code I use to render each part of the image; for debugging purposes I made the texture quad size (the size of the image parts its being split it into for said effect) to be exactly the image size, so just one quad per image at the moment. ZHandler is the class that calculates the z coordinate for a given x and y coordinate, absX and absY are the final coordinates at which it will be displayed.

GL.glTexCoord2f(curTexOffX, curTexOffY);
GL.glVertex3f(curX, curY, ZHandler.getZ(absX, absY)); 

GL.glTexCoord2f(curTexOffX, curTexOffY + curTexHeight); 
GL.glVertex3f(curX, curY + parHeight, ZHandler.getZ(absX, absEndY));

GL.glTexCoord2f(curTexOffX + curTexWidth, curTexOffY + curTexHeight);
GL.glVertex3f(curX + parWidth, curY + parHeight, ZHandler.getZ(absEndX, absEndY)); 

GL.glTexCoord2f(curTexOffX + curTexWidth, curTexOffY);
GL.glVertex3f(curX + parWidth, curY, ZHandler.getZ(absEndX, absY)); 

It looks basically like this:

Damn Duck

The white and gray area are background quads that are rendered to show what the function looks like, brighter areas are closer to you and darker ones further away. Obviously, this awesome duck image isn't tilted the way it should be, being exactly offset by the rotation it should have (what?).

When we look at the debug output however, it seems exactly like it should be:

0 400.0 550.0 0.60619825 //top left (all in un-rotated state)
1 400.0 250.0 0.60619825 //top right
2 700.0 250.0 -0.504535  //bottom right
3 700.0 550.0 -0.504535  //bottom left 

It's in the format corner absX absY z, so the last two corners (originally bottom left and right, now thanks to -90 degrees of rotation, top right and bottom right) should be further away and the first two (originally top left and right, now bottom left and top left) should be closer to the camera. But. They. Aren't.

I suspect it must have something to do with the way glRotate works, so here is how it is called for rotated texture rendering:

//rotate around image's rotation center
GL.glTranslatef(centerX, centerY, 0.0f);
GL.glRotatef(angle, 0.0f, 0.0f, 1.0f);
GL.glTranslatef(-centerX, -centerY, 0.0f); 

//draw texture... 

//undo rotation
GL.glTranslatef(centerX, centerY, 0.0f);
GL.glRotatef(-angle, 0.0f, 0.0f, 1.0f);
GL.glTranslatef(-centerX, -centerY, 0.0f);

Maybe the z-Axis is affected in some ways I don't know of so that my calls are weirdly rotated. It also happens to any other rotation, it is just the most obvious for 90 degrees.

As of writing this, I am totally aware of the rubber-duck-debugging irony here. The holy duck clearly deserves better than being rendered wrongly, so I am asking for any hints, help or advice that could point me in the right direction to solve this problem.

Edit

I tried adding glPushMatrix and glPopMatrix to surround the rendering code, nothing changed. I already back-translate after the translation and undo all the transformations / rotations.

Also, @concept3d requested the set-up code for camera and projection matrix, so here it is:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, WIDTH / HEIGHT, 0.001f, 1000.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -cameraDistance);
glScalef(1f / (WIDTH / 2.0f), 1f / -(HEIGHT / 2.0f), 1f);

I also calculate the actual and the rendered ratio to translate and scale it back to the "normal" orthographic system I used previously, so I can still use all the same coordinates. But on this, it is important to note that it works perfectly well for everything not-rotated, so there is no problem with the setup of the z-coordinates there.

Edit

As requested, the implementation of ZHandler.getZ:

return (float) (sin((x - 100) / 150 + t)) / 1.5f;

Edit

To clarify what I suspect is wrong, here is the rendered output with a rotation of +90, applied to the exact same configuration.

Other duck

As seen, the same (wrong) effect applies, the image is tilted in the wrong direction even though the outputed debug values (same respective corners as in previous debug output) are correct:

0 700.0 250.0 -0.504535
1 700.0 550.0 -0.504535
2 400.0 550.0 0.60619825
3 400.0 250.0 0.60619825

Edit

Also, I now added the z-Values I render the different parts of the images at, which should further clarify what I am talking about. The passed z-Values don't match the rendered scene.

Edit

As requested, the code on how I calculate the rendered position based on the actual position. It's basically just rotation around a fixed point but here it is:

if (angle != 0.0f)
{                       
    absX -= centerX + drawPos.x;
    absY -= centerY + drawPos.y;

    buffer.set(centerX + drawPos.x + (absX * cos - absY * sin), centerY + drawPos.y + (absX * sin + absY * cos));

    absX = buffer.x;
    absY = buffer.y; 
}

But I have to add that the calculated absolute values seem to be correct, as seen in the previous debug outputs :)

Edit

Here is how it (correctly) looks like with 0 rotation:

Correct duck

And here is how the final version looks like, also correct so far, with an un-rotated image and an texture quadrant size of 8.

Final version

\$\endgroup\$
  • 2
    \$\begingroup\$ This seems to fit here. You sure you want to move it? Is it just not getting enough attention? \$\endgroup\$ – MichaelHouse Mar 11 '15 at 18:18
  • \$\begingroup\$ @Byte56 Thanks for responding. To be perfectly honest, mostly the latter. But that is why I concluded it is probably the wrong site, the question is not bad (I hope) and my thought was if nobody cares about it it is probably in the wrong place (It just has nothing to do with game development, at least not directly. But if LWJGL counts as game development?). I will just wait then (?). Second thought was, on my main account on StackExchange I have more reputation and would place a bounty on it as I really need an answer. But I guess it would get even more easily burried there. \$\endgroup\$ – flotothemoon Mar 11 '15 at 19:11
  • 1
    \$\begingroup\$ I believe adding a bounty here solves all the problems. Good luck! \$\endgroup\$ – MichaelHouse Mar 11 '15 at 19:21
  • 3
    \$\begingroup\$ Quick question: have you tried using glPushMatrix before the rotation and then glPopMatrix after drawing to "undo" the rotation? It's been a while since I've used OpenGL though. \$\endgroup\$ – XNargaHuntress Mar 11 '15 at 19:44
  • \$\begingroup\$ @Byte56 You are an amazingly kind person. Thank you very much, it's people like you that keep this awesome site running so well :) \$\endgroup\$ – flotothemoon Mar 11 '15 at 19:45
2
+150
\$\begingroup\$

So with your provided problem image, the problem is that the top side of the quad is being affected, while the right side is supposed to have the majority of the effect? Is that correct? If that's the case then the problem is that your passing the wrong values to your ZHandler.getz function.

Your code, from what I'm seeing, is:

glTransform
glBegin
    ...
    glVertex(x, y, ZHandler.getz(x,y))
glEnd
glUntransform

What happens to your vertices with that is:

float z = ZHandler.getz(x, y); // your function happens here
glVertex(x, y, z) = {
    transformPoint(x, y, z); // your rotation happens here
    addVertex(x,y,z);
}

So that means your Z function gets calculated, and then the whole thing gets rotated. It sounds like you want that the other way around. Correct? To do that, you would need to create your own glRotate/glTranslate/glScale alternative. Not using the OpenGL provided functions, and using your own, you could transform your vertices first, then apply your function, and then pass the already-transformed vertices to OpenGL.

As a much faster, likely easier, possibly daunting, alternative, using GLSL 1.2 (Since you are, or seem to be, using OpenGL 2.1), you could move your ZHandle.getz function into a vertex-shader and calculate it there, then apply the transformation matrices.

\$\endgroup\$
  • \$\begingroup\$ Thanks for your answer. "...then the problem is you're passing the wrong values to your ZHandler.getZ function". This seems highly unlikely to me as the returned values are correct, as seen in the debug output in my answer. The values passed to the ZHandler.getZ function are already transformed (rotated) to match the actual rendered values. So the problem seems to be that the z-axis is not as it is in the un-rotated state because if it acted the same, the image would be displayed correctly (because it does in un-rotated state). \$\endgroup\$ – flotothemoon Mar 12 '15 at 16:06
  • \$\begingroup\$ How are the values passed to the ZHandler.getZ function rotated? could you show that code? \$\endgroup\$ – Wolfgang Skyler Mar 12 '15 at 19:12
  • \$\begingroup\$ Added that code. It's the code to calculate the top left coordinate, but the one for the bottom right is the exact same but with different variable names ;) \$\endgroup\$ – flotothemoon Mar 12 '15 at 19:45
  • \$\begingroup\$ You were right all along, therefore I asked byte56 to award his generous bounty to you :) There were other logical flaws in my assumptions, which I will be writing about later today. Thanks for your answer, in the end you were right :) \$\endgroup\$ – flotothemoon Mar 18 '15 at 5:43
1
\$\begingroup\$

Thanks to @WolfgangSkyler for his answer (and also @Byte56 for his generous bounty), the problem was resolved yesterday by following your advice. This would not fit into a comment and I want to share further information about how the issue was resolved, but - of course - I will not un-accept WolfgangSkylers answer, just add to it. So now, as promised, what exactly was wrong?

Well, in the question I stated (and repeated multiple times in the comments - sorry ;)) that I double- and triplechecked the z-Values I pass and that they are correct. Turns out they weren't. The issue was that I confused counter-clockwise counting with clockwise counting - everywhere else in my code (be it hitboxes or any other polygon I need to set up in any way) I set it up clockwise. Already see the flaw? Except in this piece of code, where I - for whatever reason - had decided to rotate counter-clockwise for a change (yeah..).

After re-reading Wolfgang Skyler in an attempt to find the solution to my problem I noticed that indeed I messed up the z-Value part - meaning that LWJGL was correct. Furthermore, I found out that my logical assumptions to calculate the transformed and rotated absolute values didn't make any sense: I had assumed that one min- and max value for x and y would suffice, because I always tested it with x*90 degrees of rotation (which is the one case where it would work).

0 400.0 550.0 0.60619825 //top left (all in un-rotated state)
1 400.0 250.0 0.60619825 //top right
2 700.0 250.0 -0.504535  //bottom right
3 700.0 550.0 -0.504535  //bottom left 

Here you can see the values in the order I thought they had. Looking at those, nothing seems off. However, if we correctly assign each corner a value in the same direction as I draw them, this is the real output:

0 400.0 550.0 0.60619825 //top left (all in un-rotated state)
1 400.0 250.0 0.60619825 //bottom left 
2 700.0 250.0 -0.504535  //bottom right
3 700.0 550.0 -0.504535  //top right

And from there on, it is obvious that the values are wrong and it is rendered exactly in the way I call it - incorrectly. I then took the same code but just rotated each corner individually (= 8 values/4xy-pairs instead of 4/2). And it works :)

Happy ducks

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.