Tile an image across a given x,y,height,width - slick2d

Been trying to figure this out. I have an ArrayList of my own Sprite class. The sprite class simply contains x, y, width, height, and a type Image from Slick. In my gameplaystate, I loop through the arraylist that contains all the ground sprites. I just have one in there for now. To pull in my ground image, i have it in the same .png as my player sprite. I just use the getSubImage() method to separate it. The problem is, when im using GL11 code to tile out the image, it has both the ground and character images in the tile, even though im referring to the subbed image. I might just be using it wrong, or perhaps there’s a better way with slick methods to accomplish this. Here’s my loop to draw out the ground objects:

ground.getGraphic().draw(ground.getX(), ground.getY(), ground.getSizeX(), ground.getSizeY()); will draw out the correct image, but it stretches it. While my current ground image is just a single colour, learning to tile now will be better. Plus the stretched image doesn’t look as good.

Seems to me like you’ve got change the texcoord values. I don’t really know Slick but it sounds like you’ve got an atlas and getSubImage() does some kind of magic to change the texture coordinates for the next draw call.

As such, reduce the texcoord values. I don’t know what scale you’ve got there, but usually I use values between 0 and 1, so that 0.5 represents halfway, 0.75 is 3/4, etc. Looks like maybe you’re doing it per sprite, so you would put 2 instead of 3.

Hmm, think i might crack open my book on opengl and read a bit about texture mapping, as this is starting to go over my head. I have tried messing around with the values. With 0,3 i see the image tiled 3 times across the rectangular area. Switching it to 0, 1 has it once. Now if i switch it to 0.5f, 1, I just see the character image once, not the one for my ground at all. This image just has a stickman, and then to the right of it, the ground.

Looks like you’ve got the values I would expect.

Say your atlas looks like this:


__________
| 1    2 |
|        |
| 3    4 |
|________|

Let’s add some lines in (purely imaginary, not part of the image data) to make it easier to visualize.


__________
| 1 || 2 |
|___||___|
| 3 || 4 |
|___||___|

So our different sprites are the 1, the 2, the 3, and the 4. Pretend that they’re more centered in the quadrants of the image.

If we wanted to draw the entire image, our texture coordinates would be:


GL11.glTexCoord2f(0,0); //Top left
GL11.glTexCoord2f(0,1); //Top right
GL11.glTexCoord2f(1,1); //Bottom right
GL11.glTexCoord2f(1,0); //Bottom left

Pretty simple, right? You’re making your texture coordinates match your vertex coordinates in terms of which index you’re looking at.

Now when you want to show just the 1, you keep the same vertex values but change your texture coordinates.


GL11.glTexCoord2f(0,0); //Top left
GL11.glTexCoord2f(0,0.5f); //Top right-side of '1'
GL11.glTexCoord2f(0.5f,0.5f); //Bottom right-side of '1'
GL11.glTexCoord2f(0.5f,0); //Bottom left-side of '1'

So, to show the 2 you would use:


GL11.glTexCoord2f(0.5f,0); //Starts to the right of '1'
GL11.glTexCoord2f(1,0); //Then goes to the end of the image
GL11.glTexCoord2f(1,0.5f); //Goes down half the image
GL11.glTexCoord2f(0.5f,0.5f); //Ends at the center

From there, you should be able to figure out how to show 3 and 4.

An easy way to find the tex coords of any rect in an image would be something like this:


public Vector2f[] getTexCoords(int imageX, int imageY, int imageWidth, int imageHeight, int atlasWidth, int atlasHeight)
{
    Vector2f[] results = new Vector2f[4];
    results[0] = new Vector2f( ((float)imageX) / atlasWidth, ((float)imageY) / atlasHeight );
    results[1] = new Vector2f( results[0].x + ((float)imageWidth) / atlasWidth, results[0].y );
    results[2] = new Vector2f( results[1].x, results[0].y + ((float)imageHeight) / atlasHeight );
    results[3] = new Vector2f( results[0].x, results[2].y  );
    return results;
}

Where all those values are related to the atlas image itself - where, in pixels, the image you want starts, how big it is, and how big your whole atlas is.

Thanks a lot, that really gave me a clear picture of how the textcoords work. It’s still not perfect. I may want to modify my image so it has the sprites in the quadrants evenly. Make it more of a spritesheet.

[quote="agm_ultimatex,post:5,topic:35385"]
Thanks a lot, that really gave me a clear picture of how the textcoords work. It's still not perfect. I may want to modify my image so it has the sprites in the quadrants evenly. Make it more of a spritesheet.
[/quote]
Well an uneven atlas gives you a hell of a lot more flexibility, if you can get around to doing it. I have an atlas class that has an image coupled with a text file like this:


SpriteA 45 68 110 110
Monkey 155 200 250 250



Which is:

SpriteName SpriteX SpriteY SpriteWidth SpriteHeight



Then you just read in each line, split it via spaces, and parse out the data into an object. Store that object in a HashMap and then access it via its SpriteName, then you draw it sort of like this:


SpriteAtlas atlas;
atlas.draw(“SpriteName”, x, y, width, height);



Was working on it a bit last night, and I found that since the texture coords took the image and stretched it across, I ended up with the same problem. So i put together a method to essentially tile it, but I don’t feel like it’s very efficient or perfect.


for(int i = 0; i < levels.size(); i++) {
			ArrayList<Sprite> tempGrounds = levels.get(i).getGrounds();
			for(int g = 0; g < tempGrounds.size(); g++) {
				Sprite ground = tempGrounds.get(g);
				Image groundGraphic = ground.getGraphic();
				int gWidth = groundGraphic.getWidth();
				int gHeight = groundGraphic.getHeight();
				int xTimes = (int)ground.getSizeX() / gWidth;
				int yTimes = (int)ground.getSizeY() / gHeight;
				if(xTimes <= 0) {
					xTimes = 1;
				}
				if(yTimes <= 0) {
					yTimes = 1;
				}
				for(int x = 0; x < xTimes; x++) {
					for(int y = 0; y < yTimes; y++) {
						groundGraphic.draw(ground.getX() + (gWidth * x), ground.getY() + (gHeight * y), gWidth, gHeight);
					}
				}				
			}
		}

Due to the fact im dividing intergers, I know there will be a loss of accuracy, and therefore rendered images will actually be in some cases shorter than the actual block. I think this has a bigger problem of leading to a lot of computation when more and more gets added as a process.