Tiling Various Textures

I am interested in creating a relatively large flat quad to use as the floor of a map which I am creating. This quad would have dynamically generated tiles rendered onto it at different coordinates, for example

XXXYYYYXXX

YXYYYXXXXY

YXYYXYYYYX

where y is one texture and x is the other. These individual textures would be likely separate image files in my assets folder.



I was wondering how one would combine these images into a larger image which could be used as a texture for the entire quad. I have tried using multiple quads (one for each tile), but as you can guess, when the map got as large as I was hoping it would it began to show massive inefficiencies. Is there any means of combining textures in this way in jme3? If there is not, how would you suggest I go about combining the textures/accomplishing the same effect?



Thanks

What do you mean by “as large as I hoping it would”? The problem is probably not the geometry per se but the number of quad objects (i.e. Spatials) JME has to process…



You could create on atlas texture where you have all the tile graphics inside and then you could create your geometry by hand. This means you have a vertex buffer where you create your vertices, your index buffer and your texcoord buffer. You will need 4 vertices for every quad because every (overlapping) vertex will have another texture coordinates. When during the game a tile changes its texture you jump into the textcoord buffer and change the corresponding values.



Look here how to create custom meshes: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:custom_meshes

This way it will be definitely faster than you can look :wink:

1 Like

You can use a texture atlas or you can just combine the quads of tiles with the same material. The second way is the easiest… especially since there is already a batching class in JME.

1 Like
Dodikles said:
What do you mean by "as large as I hoping it would"? The problem is probably not the geometry per se but the number of quad objects (i.e. Spatials) JME has to process...

You could create on atlas texture where you have all the tile graphics inside and then you could create your geometry by hand. This means you have a vertex buffer where you create your vertices, your index buffer and your texcoord buffer. You will need 4 vertices for every quad because every (overlapping) vertex will have another texture coordinates. When during the game a tile changes its texture you jump into the textcoord buffer and change the corresponding values.

Look here how to create custom meshes: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:custom_meshes
This way it will be definitely faster than you can look ;)

That looks like exactly what I would want to do. Thank you very much.
I had never thought of doing a custom mesh and remapping it to a texture atlas, instead I thought I should use a basic quad and texture it with a single image which I had created. This sounds like it would be far more efficient and easier, thank you.

I am quite certain that the problem was likely the large number of spatials which JME3 would have to process in order to render the scene. When I reached about a 100^2 grid my computer began to lag immensely. I hope that JME3 would be able to handle a grid of that size using atlas textures.

Thank you for your help.

Hi,



glad I could help. Yes, if you have 10.000 spatials then JME is definitely crawling, but this would be the same for a lot of other engines, too. You could try to also implement pspeeds advice because it would be easier for a beginner (if you are one, that is :stuck_out_tongue: ). I don’t know how much you would gain speedwise by this batching but changing materials is only half of the problem. Running through 10.000 objects in a list every frame is another :smiley:

1 Like
Dodikles said:
Hi,

glad I could help. Yes, if you have 10.000 spatials then JME is definitely crawling, but this would be the same for a lot of other engines, too. You could try to also implement pspeeds advice because it would be easier for a beginner (if you are one, that is :P ). I don't know how much you would gain speedwise by this batching but changing materials is only half of the problem. Running through 10.000 objects in a list every frame is another :D

I can say quite confidently that running through 10,000 objects is my problem.
I think I am definitely a beginner, however, the custom model system which you linked me to seems to make sense to me, so I am going to take a shot at it. Even if I screw up, its a good learning experience, right? :P

As far as I can tell, one of the big things I need to make sure that I do is to draw the triangles counterclockwise. This is to calculate the normals, correct?

I am currently going to spend a bit of time working out my plane, and if I have any problems I will likely post again in this thread. Thank you very much for your help, I was lost before :s.

Also, if you don't mind me clarifying, the vertices array is simply a list of vertices, the texture coordinates array is an array of texture coordinates which line up with each of the vertices, and the indexes array is a list of what vertices to use to draw each triangle, correct?
mystor said:
I can say quite confidently that running through 10,000 objects is my problem.
I think I am definitely a beginner, however, the custom model system which you linked me to seems to make sense to me, so I am going to take a shot at it. Even if I screw up, its a good learning experience, right? :P


Yes, running through such a big number of spatials is definitely the culprit here, nothing else! And yeah, it's not sooo difficult really. Your vertex buffer must have the size of 4*number_of_tiles, your index buffer the size of 3*number_of_triangles and texcoords buffer the size of 2*number_of_vertices. And then it's just filling the buffers at the right position with the right value ;)


As far as I can tell, one of the big things I need to make sure that I do is to draw the triangles counterclockwise. This is to calculate the normals, correct?

Errm, could be. It is definitely needed for backface culling which is done in hardware.



I am currently going to spend a bit of time working out my plane, and if I have any problems I will likely post again in this thread. Thank you very much for your help, I was lost before :s.

Also, if you don't mind me clarifying, the vertices array is simply a list of vertices, the texture coordinates array is an array of texture coordinates which line up with each of the vertices, and the indexes array is a list of what vertices to use to draw each triangle, correct?

No problem, ask away. And yes, correct! :)
1 Like

Also make sure that your mesh is drawn with triangles, like this:

[java]

m.setMode(Mesh.Mode.Triangles);

[/java]



I don’t know what the standard mode is but the index buffer will be interpreted differently depending on the mode set. Keep this in mind to avoid (allegedly) nasty bugs :wink:

1 Like
Dodikles said:
Also make sure that your mesh is drawn with triangles, like this:
[java]
m.setMode(Mesh.Mode.Triangles);
[/java]

I don't know what the standard mode is but the index buffer will be interpreted differently depending on the mode set. Keep this in mind to avoid (allegedly) nasty bugs ;)

Ah, thank you very much.

I can imagine that using a different mode would mess it up quite a bit. :S

Alright, I am having issues unfortunately.

I have made a class (BlockFacePlane, I know, so creative) which is meant to handle the creation of a custom mesh and texture it with the second texture on my atlas texture. The atlas texture is loaded by MaterialManager, and I am fairly confident that that is not where the issue lies (it uses the same code as another game which I had been using earlier, and it functioned perfectly then).



The following is the constructor:

[java] public BlockFacePlane(String name, int width, int height, int elevation) {

super(name);



this.width = width;

this.height = height;

trans = new Vector3f(0,elevation + 1,0);



int arrayLength = widthheight4; //length of verticies, texCoords arrays

int indexesArrayLength = widthheight6;

System.out.println(arrayLength + "-" + indexesArrayLength);



facesMesh = new Mesh();



verticies = new Vector3f[arrayLength];

texCoords = new Vector2f[arrayLength];

indexes = new int[indexesArrayLength];



for (int y = 0; y < height; y++) {

for (int x = 0; x < width; x++) {

int startCnt = x4 + y(width4);

int startCntIndexes = x
6 + y*(width6);

/

  • 0—1 +ve X
  • | |
  • | |
  • 2—3
  • +ve Z

    */



    verticies[startCnt] = new Vector3f(x,0,y);

    verticies[startCnt+1] = new Vector3f(x+1, 0, y);

    verticies[startCnt+2] = new Vector3f(x, 0, y+1);

    verticies[startCnt+3] = new Vector3f(x+1, 0, y+1);



    texCoords[startCnt] = new Vector2f(.1f,0f);

    texCoords[startCnt+1] = new Vector2f(.2f, 0f);

    texCoords[startCnt+2] = new Vector2f(.1f, .1f);

    texCoords[startCnt+3] = new Vector2f(.2f, .1f);



    //lower left hand triangle

    indexes[startCntIndexes] = startCnt+3;

    indexes[startCntIndexes+1] = startCnt;

    indexes[startCntIndexes+2] = startCnt+2;



    //uper right hand triangle

    indexes[startCntIndexes+3] = startCnt+3;

    indexes[startCntIndexes+4] = startCnt+1;

    indexes[startCntIndexes+5] = startCnt;

    }

    }



    facesMesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(verticies));

    facesMesh.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoords));

    facesMesh.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indexes));

    facesMesh.setMode(Mesh.Mode.Triangles);



    setMesh(facesMesh);

    setMaterial(MaterialManager.getBlockMaterial());

    setLocalTranslation(trans);

    }[/java]



    BlockFacePlane extends Geometry by the way.

    When I add blockfaceplane to the root node, the following image is what appears: http://screencast.com/t/cZMvRVCr

    why is the full face not being filled in?



    Thank you so much for your help.

How many different textures do you have? If you have one geometry per texture that’s going to be plenty fast and will hardly require any code at all.



Build your scene just like you are with separate quads and then pass the parent node to:

http://hub.jmonkeyengine.org/javadoc/jme3tools/optimize/GeometryBatchFactory.html#optimize(com.jme3.scene.Node)



Done! :slight_smile:

1 Like
pspeed said:
How many different textures do you have? If you have one geometry per texture that's going to be plenty fast and will hardly require any code at all.

Build your scene just like you are with separate quads and then pass the parent node to:
http://hub.jmonkeyengine.org/javadoc/jme3tools/optimize/GeometryBatchFactory.html#optimize(com.jme3.scene.Node)

Done! :)

Currently I am only using one texture as a test texture, however I plan to have a very large number more textures in the future, as well as transparency. The purpose of the altitude parameter is to allow for multiple BlockFacePlanes to be stacked on top of eachother, with other quads (which I have not yet added) as the sides, making a blocky, dynamic terrain which could be affected by explosions and the like (destroying all tiles in an area and replacing with air).

The optimize function seems like it could be quite powerful, however, it also seems as though it could have the limitation of requiring that it be run again whenever the terrain is deformed, with the entire terrain being re-created. My hope with BlockFacePlanes is that by changing texture coordinates that I can change the textures on the fly rather than having to recreate everything from scratch.

I hope that you can understand what I am asking, if you cannot I will try to explain better :D
Thanks

EDIT: After fiddling around a bit with my work, there may be a bit of an issue with my material loading that I should point out.
In that screenshot, the texture's wrap mode was set to Texture.WrapMode.Repeat. When I remove that call or change it the object appears to disappear completely.

After noticing this I feel as though I should post the relevant parts of the Material Manager class.

[java]public static Material getBlockMaterial () {
if (blockMat == null)
throw new NullPointerException();

return blockMat;
}

private static void loadBlockMaterialsTextures(AssetManager assetManager) {
blockMat = new Material(assetManager, "Common/MatDefs/Misc/SimpleTextured.j3md");
blockText = assetManager.loadTexture("Textures/blockAtlas.png");
blockText.setWrap(Texture.WrapMode.Repeat);
blockMat.setTexture("ColorMap", blockText);
}[/java]

Thanks again for your help

I guess that you are aiming for a minecraft-esque game? Then you have found already the right person to talk to :wink: As for your code: I see that you are missing the updateBounds() call but I hardly think this has something to do with your problem. Try to create one or two tiles first and go trough the debugger. Maybe your texture coordinates are not correct…

1 Like
Dodikles said:
I guess that you are aiming for a minecraft-esque game? Then you have found already the right person to talk to ;) As for your code: I see that you are missing the updateBounds() call but I hardly think this has something to do with your problem. Try to create one or two tiles first and go trough the debugger. Maybe your texture coordinates are not correct...

I suppose you could call the game minecraft-esque, however I plan to make the gameplay much different :P
I reconstructed with only a single tile, and the same issue persisted (of course) however I did come upon a discovery.
Upon filling the atlas completely with color, the full tile showed up just fine. This points to bad texture coordinates.
From what I can tell, only the top pixel's row from my tile is being rendered, with the rest being transparency.

That is my current Atlas Texture. The texture coordinates I am using are meant to be for the second texture (the green one).
And no, these are not final textures, they are random blocks of color with random pixels so I can see orientation :P

Thanks again!

EDIT: So I fiddled around with it a bit more, and it seems that the texture coordinates:
[java] texCoords[0] = new Vector2f(.1f,.9f);
texCoords[1] = new Vector2f(.2f, .9f);
texCoords[2] = new Vector2f(.1f, 1);
texCoords[3] = new Vector2f(.2f, 1);[/java]
work... kinda.
I have a question, why does it start with 0 on the left for X and 1 on the top for Y? That seems needlessly confusing.

One last question. Color from adjacent textures seems to 'leak' into this texture due to some type of anti-aliasing. Is there any way to turn that type of thing off?
mystor said:
The optimize function seems like it could be quite powerful, however, it also seems as though it could have the limitation of requiring that it be run again whenever the terrain is deformed, with the entire terrain being re-created. My hope with BlockFacePlanes is that by changing texture coordinates that I can change the textures on the fly rather than having to recreate everything from scratch.


No matter what you do you will have to break up your world into sections... though there is a more overhead with the Quad -> GeometryBatchFactory approach. I think Minecraft calls these chunks. Mythruna has "leafs". Determining which quads to actually render is usually what takes the most time, really.

Regarding the texture atlas bleeding, you either have to create buffer zones around your textures so that the mipmapping doesn't bleed or you can do like minecraft and turn off mipmapping completely and be left with block textures.

Regarding texture coordinates, like a standard Cartesian plane, 0,0 is in the lower left.
1 Like
mystor said:
I have a question, why does it start with 0 on the left for X and 1 on the top for Y? That seems needlessly confusing.

What @pspeed said. But it's also (probably for the same reason) the way openGL coordinate system works. Most people view things from top left to bottom right, openGL goes from bottom left to top right.
madjack said:
What @pspeed said. But it's also (probably for the same reason) the way openGL coordinate system works. Most people view things from top left to bottom right, openGL goes from bottom left to top right.

Looking at it, bottom left to top right makes more sense in terms of the cartesian plane, however I am used to the 2d programming style of top left to bottom right.

pspeed said:
No matter what you do you will have to break up your world into sections... though there is a more overhead with the Quad -> GeometryBatchFactory approach. I think Minecraft calls these chunks. Mythruna has "leafs". Determining which quads to actually render is usually what takes the most time, really.

Regarding the texture atlas bleeding, you either have to create buffer zones around your textures so that the mipmapping doesn't bleed or you can do like minecraft and turn off mipmapping completely and be left with block textures.

Regarding texture coordinates, like a standard Cartesian plane, 0,0 is in the lower left.


Unlike in minecraft, I plan to make my maps far more limited in scope and viewed from a top-downish perspective. Because of that, I hope that chunks will not be necessary, however, if they are I will be certain to implement them.

How does one go about disabling mipmapping? I will likely end up creating buffer zones around my textures, as I don't mind mipmapping, but it would be nice for testing.