How do I Extract Tiles from a NPOT Sprite Sheet

Started by
2 comments, last by Lazy Foo 11 years, 8 months ago
Basically I have created a sprite sheet/tileset with dimentions of 320 x 2048, the tiles are each 32 x 32. I've been able to load individual files with textures in them but I don't seem to be able to load textures from a sprite sheet. Any help ?
Here is the code I'm struggling with:
This is my tiling function

[source lang="cpp"]float VertexX[4], VertexY[4];
int TexcoordX, TexcoordY;
for (int y = 0; y < WorldSizey; ++y)
{
for (int x = 0; x < WorldSizex; ++x)
{
CalculateTextureSheetCoords(TileData[0][y][x],
TilesetX,32,TexcoordX,TexcoordY);
CalculateTextureVertexCoords(TilesetX,TilesetY,TexcoordX,
TexcoordY,32,VertexX[0],VertexY[0],VERTEX_LOWER_LEFT);
CalculateTextureVertexCoords(TilesetX,TilesetY,TexcoordX,
TexcoordY,32,VertexX[1],VertexY[1],VERTEX_UPPER_LEFT);
CalculateTextureVertexCoords(TilesetX,TilesetY,TexcoordX,
TexcoordY,32,VertexX[2],VertexY[2],VERTEX_UPPER_RIGHT);
CalculateTextureVertexCoords(TilesetX,TilesetY,TexcoordX,
TexcoordY,32,VertexX[3],VertexY[3],VERTEX_LOWER_RIGHT);

glEnable(GL_TEXTURE_2D);
glColor4fv(TextureColour);
glBindTexture(GL_TEXTURE_2D,TILESET);
glBegin(GL_QUADS);
glTexCoord2f(VertexX[0],VertexY[0]);
glVertex2f(x/8,(WorldSizey-(y+1))/8); //Lower left
glTexCoord2f(VertexX[1],VertexY[1]);
glVertex2f(x/8,(WorldSizey-y)/8); //Upper left
glTexCoord2f(VertexX[2],VertexY[2]);
glVertex2f((x+1)/8,(WorldSizey-y)/8); //Upper Right
glTexCoord2f(VertexX[3],VertexY[3]);
glVertex2f((x+1)/8,(WorldSizey-(y+1))/8); //Lower Right
glEnd();
glDisable(GL_TEXTURE_2D);
}
}[/source]

And Here's my functions which calculate the coordinates that should work to find the textures but all I'm getting is blank white squares.


[source lang="cpp"]enum VertexMode {VERTEX_LOWER_LEFT, VERTEX_UPPER_LEFT, VERTEX_UPPER_RIGHT, VERTEX_LOWER_RIGHT};
void CalculateTextureVertexCoords(float in_ImageSizeX, float in_ImageSizeY,
float in_TextureCoordX, float in_TextureCoordY,
float in_TextureSize,
float &out_VertexCoordX, float &out_VertexCoordY,
int in_VertexMode)
{
switch (in_VertexMode)
{
case VERTEX_LOWER_LEFT:
out_VertexCoordX = (in_TextureSize * in_TextureCoordX)/in_ImageSizeX;
out_VertexCoordY = ( 1 - ((in_TextureSize * (in_TextureCoordY + 1))/in_ImageSizeY));
break;
case VERTEX_UPPER_LEFT:
out_VertexCoordX = (in_TextureSize * in_TextureCoordX)/in_ImageSizeX;
out_VertexCoordY = ( 1 - ((in_TextureSize * in_TextureCoordY)/in_ImageSizeY));
break;
case VERTEX_UPPER_RIGHT:
out_VertexCoordX = ((in_TextureSize * (in_TextureCoordX + 1))/in_ImageSizeX);
out_VertexCoordY = ( 1 - ((in_TextureSize * in_TextureCoordY)/in_ImageSizeY));
break;
case VERTEX_LOWER_RIGHT:
out_VertexCoordX = (in_TextureSize * (in_TextureCoordX + 1))/in_ImageSizeX;
out_VertexCoordY = ( 1 - ((in_TextureSize * (in_TextureCoordY + 1))/in_ImageSizeY));
break;
}
}
void CalculateTextureVertexCoordsABS(int in_ImageSizeX, int in_ImageSizeY,
int in_TextureCoordX, int in_TextureCoordY,
int in_TextureSize,
int &out_VertexCoordX, int &out_VertexCoordY,
int in_VertexMode)
{
switch (in_VertexMode)
{
case VERTEX_LOWER_LEFT:
out_VertexCoordX = (in_ImageSizeX - (in_ImageSizeX - in_TextureSize * in_TextureCoordX));
out_VertexCoordY = (in_ImageSizeY - in_TextureSize * (in_TextureCoordY + 1));
break;
case VERTEX_UPPER_LEFT:
out_VertexCoordX = (in_ImageSizeX - (in_ImageSizeX - in_TextureSize * in_TextureCoordX));
out_VertexCoordY = (in_ImageSizeY - in_TextureSize * in_TextureCoordY);
break;
case VERTEX_UPPER_RIGHT:
out_VertexCoordX = (in_ImageSizeX - (in_ImageSizeX - in_TextureSize * (in_TextureCoordX + 1)));
out_VertexCoordY = (in_ImageSizeY - in_TextureSize * in_TextureCoordY);
break;
case VERTEX_LOWER_RIGHT:
out_VertexCoordX = (in_ImageSizeX - (in_ImageSizeX - in_TextureSize * (in_TextureCoordX + 1)));
out_VertexCoordY = (in_ImageSizeY - in_TextureSize * (in_TextureCoordY + 1));
break;
}
}

void CalculateTextureSheetCoords(int in_TextureNumber, int in_ImageSizeX,
int in_TextureSize, int &out_TextureCoordX,
int &out_TextureCoordY)
{
if (in_TextureNumber != 0)
{
int TexturesPerRow = in_ImageSizeX/in_TextureSize;
out_TextureCoordY = floor(((double)in_TextureNumber - 1) / (double)TexturesPerRow);
out_TextureCoordX = (((in_TextureNumber - 1) - TexturesPerRow * out_TextureCoordY) % TexturesPerRow);

}
}[/source]
Advertisement
Your code looks extremely complicated for something so simple. Are you making a 2D game? Why are you using world coordinates instead of orthographic projection?

In addition to ortho, texture states, etc. you should also read up a little on classes and object oriented programming. For example, you might:

  • Define a "Texture" class which wraps OpenGL textures (bind, wrap modes, filtering, etc) and pads for NPOT
  • Define a "TextureRegion" class utility which determines normalized texture coordinates from un-normalized pixel coordinates
  • Define a "TextureAtlas" (or "SpriteSheet") class which splits a TextureRegion into an array of many TextureRegion objects

Also, the whole point of a sprite sheet is to reduce texture binds and state changes. Bind the texture atlas, glBegin quads, specify the vertices for all of your sprites within that sheet, then glEnd to push the data to the GPU.

Here's a very simple example, similar to what I use:
public class TextureRegion {
protected float normalizedWidth, normalizedHeight;
protected float width, height;
protected float centerX, centerY;
protected float textureOffsetX, textureOffsetY;
protected Texture texture;
public TextureRegion(Texture texture) {
this.texture = texture;

//e.g. a 240x240 PNG image would be loaded as a 256x256 texture
this.normalizedWidth = texture.getImageWidth() / (float) texture.getTextureWidth();
this.normalizedHeight = texture.getImageHeight() / (float) texture.getTextureHeight();
this.width = texture.getImageWidth();
this.height = texture.getImageHeight();
this.centerX = width / 2f;
this.centerY = height / 2f;
}
public TextureRegion copy() {
TextureRegion img = new TextureRegion(texture);
return img;
}
public TextureRegion getSubImage(float x, float y, float width, float height) {
float tx = ( x / this.width * normalizedWidth ) + textureOffsetX;
float ty = ( y / this.height * normalizedHeight ) + textureOffsetY;
float tw = width / this.width * normalizedWidth;
float th = height / this.height * normalizedHeight;

TextureRegion img = copy();
img.textureOffsetX = tx;
img.textureOffsetY = ty;
img.width = width;
img.height = height;
img.normalizedWidth = tw;
img.normalizedHeight = th;
img.centerX = width / 2f;
img.centerY = height / 2f;
return img;
}

// ideally you should use a modern solution like VBOs and place rendering elsewhere...

public void begin() {
GL11.glEnable(GL11.GL_TEXTURE_2D);
texture.bind();
GL11.glBegin(GL11.GL_QUADS);
}
public void end() {
GL11.glEnd();
GL11.glDisable(GL11.GL_TEXTURE_2D);
}
public void draw(float x, float y) {
GL11.glTexCoord2f(textureOffsetX, textureOffsetY; //TOP LEFT
GL11.glVertex2f(x, y);
GL11.glTexCoord2f(textureOffsetX, textureOffsetY+normalizedHeight); //BOTTOM LEFT
GL11.glVertex2f(x, y+height);
GL11.glTexCoord2f(textureOffsetX+normalizedWidth, textureOffsetY+normalizedHeight); //BOTTOM RIGHT
GL11.glVertex2f(x+width, y+height);
GL11.glTexCoord2f(textureOffsetX+normalizedWidth, textureOffsetY); //TOP RIGHT
GL11.glVertex2f(x+width, y);
}
}
Yeah I'm making a 2D game, I don't know how to use orthographic projection.

I generally don't use classes because other than private variables they really don't carry many advantages over just using functions.

Could you explain what your code does from the perspective of "draw" ?

Yeah I'm making a 2D game, I don't know how to use orthographic projection.

I generally don't use classes because other than private variables they really don't carry many advantages over just using functions.

Could you explain what your code does from the perspective of "draw" ?


Orthographic projection basically makes it so objects don't get smaller as they move further down the z axis, which is what they would do in a perspective projection. It's a useful projection when you want to render 2D.

When rendering a sprite sheet, you need to map the texture coordinates on a 0 - 1 scale.

Say you have a sprite sheet that's 128x128 and you have a sprite at the top left that's 64x64. To render it on a quad, you map it from 0 to 0.5 on the x and y axis (64 is 0.5 of 128).

And you will soon learn the value of OOP. Having code in loosely packed modules is vital when you have thousands upon thousands of lines of code to manage.

Learn to make games with my SDL 2 Tutorials

This topic is closed to new replies.

Advertisement