Isometric Tile Cutting

I’m porting my Java2D Isometric engine to LWJGL, and am considering cutting diamond-shaped tiles from a bigger texture using glTexCoord2f and glVertex2f. In Java2D I just use drawimage with a rectangle containing the tile - the rest of the rectangle being transparent. This is a lot of wasted space and if I’m drawing diamond-shaped quads I can also take advantage of glColor3f on the vertexes to create a dynamical lighting.
With my original test-image:(scaled up)
http://www.javaengines.dk/test/testoriginal.gif
I got the following result:
http://www.javaengines.dk/test/testfail.gif
using this code:


GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f(0, 8.0f/tex.getImageHeight());
GL11.glVertex2f(0, 8);
GL11.glTexCoord2f(16.0f/tex.getImageWidth(),16.0f/tex.getImageHeight());
GL11.glVertex2f(16,16);
GL11.glTexCoord2f(32.0f/tex.getImageWidth(),8.0f/tex.getImageHeight());
GL11.glVertex2f(32, 8);
GL11.glTexCoord2f(16.0f/tex.getImageWidth(), 0.0f);
GL11.glVertex2f(16, 0);
}
GL11.glEnd();

Problem: The diamond being cut was being shifted 1 pixel to the right.
So I subtracted 1 from all x-values:


GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f((0.0f-1)/tex.getImageWidth(), 8.0f/tex.getImageHeight());
GL11.glVertex2f(-1, 8);
GL11.glTexCoord2f((16.0f-1)/tex.getImageWidth(),16.0f/tex.getImageHeight());
GL11.glVertex2f(15,16);
GL11.glTexCoord2f((32.0f-1)/tex.getImageWidth(),8.0f/tex.getImageHeight());
GL11.glVertex2f(31, 8);
GL11.glTexCoord2f((16.0f-1)/tex.getImageWidth(), 0.0f/tex.getImageHeight());
GL11.glVertex2f(15, 0);
}
GL11.glEnd();

And got the following result, exactly what I wanted:

http://www.javaengines.dk/test/testsuccess.gif

Now the million dollar question is: Can I trust this tile cutting to work pixel perfect on all cards/systems or is the exact set of pixels defined by a quad somewhat undefined?

Try to turn FSAA on to be absolutely sure, take a screenshot and check for ‘sharp’ edges.

IIRC it should be like this:


float x0 = 0.0f + 1.0f / tex.getImageWidth(); // maybe multiply by 0.5f, not too sure
float x1 = 1.0f - 1.0f / tex.getImageWidth();
float y0 = 0.0f + 1.0f / tex.getImageHeight();
float y1 = 1.0f - 1.0f / tex.getImageHeight();

GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f(x0, y0);
GL11.glVertex2f(-1, 8);
GL11.glTexCoord2f(x1, y0);
GL11.glVertex2f(15,16);
GL11.glTexCoord2f(x1, y1);
GL11.glVertex2f(31, 8);
GL11.glTexCoord2f(x0, y1);
GL11.glVertex2f(15, 0);
}
GL11.glEnd();

Yes, this is the way OpenGL is setup. This version should work on ‘every’ card…

On a sidenote:
If you ever want to draw LINES or POINTS in OpenGL, be sure to translate the matrix by 0.375 in both directions (2D) to get them pixel-perfect. :-X

Could you clarify the FSAA bit? I definitely don’t want to antialias the jagged edges as they’re supposed to fit the jags of neighbor tiles:)

PS: I have turned the Y-Axis, so the translation should be like this? :

GL11.glTranslatef(0.375f, getHeight()+0.375f, 0.0f);
GL11.glScalef(1, -1, 1);

When you enable FSAA you can see whether or not the pixels are “a bit off” in screenspace.

So if you got it all setup properly, even 16xFSAA will not smoothen anything.

Ok thanks - could you tell me how to turn it on? Do I need to go fullscreen first as the name suggests?. I tried translating by 0.375 and now I get antialiased edges - am I supposed to apply that translation only when actually drawing lines or points?
BTW, this is the full drawing code, I use the Texture class from Matzon/Kevglass:



public void paintGL() {
				try {
					if(tex == null)
						tex = loader.getTexture("tex0.gif");
		
					makeCurrent();
					
					GL11.glViewport(0, 0, getWidth(), getHeight());
					GL11.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
				
					GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
					GL11.glMatrixMode(GL11.GL_PROJECTION);
					GL11.glLoadIdentity();
					GLU.gluOrtho2D(0.0f, (float) getWidth(), 0.0f, (float) getHeight());
					GL11.glMatrixMode(GL11.GL_MODELVIEW);
					GL11.glPushMatrix();
					GL11.glTranslatef(0, getHeight(), 0.0f);
					GL11.glScalef(1, -1, 1);
					//tex.bind();
		
					
					
					GL11.glEnable(GL11.GL_TEXTURE_2D);
				
					
					GL11.glBegin(GL11.GL_QUADS);
					{
						GL11.glTexCoord2f((0.0f-1)/tex.getImageWidth(), 8.0f/tex.getImageHeight());
						GL11.glVertex2f(-1, 8);
						GL11.glTexCoord2f((16.0f-1)/tex.getImageWidth(),16.0f/tex.getImageHeight());
						GL11.glVertex2f(15,16);
						GL11.glTexCoord2f((32.0f-1)/tex.getImageWidth(),8.0f/tex.getImageHeight());
						GL11.glVertex2f(31, 8);
						GL11.glTexCoord2f((16.0f-1)/tex.getImageWidth(), 0.0f/tex.getImageHeight());
						GL11.glVertex2f(15, 0);
						
						
					}
					GL11.glEnd();
		
					
	
	
					
					GL11.glPopMatrix();
					swapBuffers();
				} catch (Exception e) {
					throw new RuntimeException(e);
				}


PS: Is there any way I can avoid dividing by the texture dimensions and just use absolute pixel values instead?

FSAA = full SCENE aa (not full screen)

as mentioned, you only need to translate 0.375 when drawing LINES and POINTS

FSAA can be turned on with Display.create(…, …, …, check the javadocs, …, …, …);

After you applied the FSAA, cuold you please try to spot the diff between your texcoord-corrections and mine? (they are not equal)

ah… I had to google fsaa didn’t see it, thanks :slight_smile:

I’m using AWTGLCanvas in a Frame atm so I’ll have to wait unti I have a little more time to do the test with Display.
Anyway I can’t see how “float x1 = 1.0f - 1.0f / tex.getImageWidth();” is anywhere near “(16.0f-1)/tex.getImageWidth()”?

Display is a LWJGL feature, sorry.

I’m puzzled about your texcoords too… I can’t see how they are correct, I think you’re just being lucky with them.

The coordinates are the corners of the diamond:

http://www.javaengines.dk/test/texcoords.gif

Where do you get your coordinates from?

btw, I enabled FSAA I think ? (int samples in PixelFormat constructor?) And I get some smoothing.

Well, it doesn’t surprise me you get smoothing, as the texcoords just make no sense :wink:


float x0 = 0.0f + 1.0f / tex.getImageWidth();
float x1 = 1.0f - 1.0f / tex.getImageWidth();
float y0 = 0.0f + 1.0f / tex.getImageHeight();
float y1 = 1.0f - 1.0f / tex.getImageHeight();

This was code for the 4 corners, not the 4 sides, sorry.


float x1 = 0.0f + 1.0f / tex.getImageWidth();
float x2 = 0.5f;
float x3 = 1.0f - 1.0f / tex.getImageWidth();
float x4 = 0.5f;

float y1 = 0.5f;
float y2 = 1.0f - 1.0f / tex.getImageHeight();
float y3 = 0.0f + 1.0f / tex.getImageHeight();
float y4 = 0.5f;

texcoord #1 = x1,y1
texcoord #2 = x2,y2
texcoord #3 = x3,y3
texcoord #4 = x4,y4

I’m not so sure about the 0.5f values though, as the exact color at 0.5 would be exactly between the 2 center pixels.

Maybe I’m wrong, but IIRC the above methods only work for rectangles (not any quad-shape)

You’re probably right that this isn’t a feasible way to do isometric tiling.
I’ll probably have to either use alpha textures or opaque textures combined with a bitmask (can this be done in OpenGL?).
sniff I had it all figured out how I was gonna implement dynamic colored lighting by setting colors on the diamond corners - but if diamond corners don’t correspond to texture corners I have to find another way to make it work - any ideas?

why dont you just render quads (easy enough), rotate the view and use an isometric projection ?

DP

I thought about that too - I’m just afraid it wold look “textured” that way?
Anyway I wouldn’t really need to rotate the view I think, I could just do something like:


GL11.glBegin(GL11.GL_QUADS);
{
GL11.glTexCoord2f(0,0);
GL11.glVertex2f(-1, 8);
GL11.glTexCoord2f(0,1);
GL11.glVertex2f(15,16);
GL11.glTexCoord2f(1,1);
GL11.glVertex2f(31, 8);
GL11.glTexCoord2f(1,0);
GL11.glVertex2f(15, 0);
}
GL11.glEnd();



But I would either A: lose detail to stretching or B: waste texture memory due to the texture being shrinked on a diagonal