Rendering world of cubes

Started by miker9, February 19, 2012, 13:29:01

Previous topic - Next topic

miker9

I have a 256x256x128 world divided on chunks(16x16 blocks) each.When the chunk changed or rendered first time, i create a display list and render the chunk.Then if it's not changed i just call the display list.Code looks like that:
(in world class)
public void render() {
		for(int x=0; x<16; x++)
			for(int z=0; z<16; z++) {
				GL11.glPushMatrix();
				GL11.glTranslatef(16*x, 0, 16*z);
				chunks[x][z].render();
				GL11.glPopMatrix();
			}
	}

(in chunk class)
public void render() {
		if(changed){
			GL11.glDeleteLists(DList, 1);
			GL11.glNewList(DList, GL11.GL_COMPILE_AND_EXECUTE);
			for(int cx=0; cx<sizeX; cx++)
		  		for(int cy=0;cy<sizeY; cy++)
		  			for(int cz=0;cz<sizeZ;cz++){
		  				if(blocks[cx][cy][cz].getBlock().getId() != 0) {
		  					BlockData data = blocks[cx][cy][cz];
		  					data.getBlock().getModel().render(data, cx, cy, cz, World.cur.getSurroundingBlocks(cx+x*16, cy, cz+z*16));
		  				}
		  			}
			GL11.glEndList();
			Info.info("chunk was changed");
		} else {
			GL11.glCallList(DList);
		}
		changed = false;
	}

(in ModelCube class(standard model))
public void render(BlockData block, int x, int y, int z, Block[] sur) {
			if(sur[0].getRenderID() != block.getBlock().getRenderID()) {
				GL11.glBindTexture(GL11.GL_TEXTURE_2D, block.getBlock().getTextureFromSide(0));
				GL11.glBegin(GL11.GL_TRIANGLE_FAN);
				  GL11.glTexCoord2f(0.0F, 1.0F); GL11.glVertex3f(x+0, y+0, z+1);
			      GL11.glTexCoord2f(1.0F, 1.0F); GL11.glVertex3f(x+1, y+0, z+1);
			      GL11.glTexCoord2f(1.0F, 0.0F); GL11.glVertex3f(x+1, y+1, z+1);
			      GL11.glTexCoord2f(0.0F, 0.0F); GL11.glVertex3f(x+0, y+1, z+1);
			    GL11.glEnd();
			}
			if(sur[1].getRenderID() != block.getBlock().getRenderID()) {
				GL11.glBindTexture(GL11.GL_TEXTURE_2D, block.getBlock().getTextureFromSide(1));
				GL11.glBegin(GL11.GL_TRIANGLE_FAN);
				  GL11.glTexCoord2f(1.0F, 1.0F); GL11.glVertex3f(x+0, y+0, z+0);
			      GL11.glTexCoord2f(1.0F, 0.0F); GL11.glVertex3f(x+0, y+1, z+0);
			      GL11.glTexCoord2f(0.0F, 0.0F); GL11.glVertex3f(x+1, y+1, z+0);
			      GL11.glTexCoord2f(0.0F, 1.0F); GL11.glVertex3f(x+1, y+0, z+0);
			    GL11.glEnd();
			}
			
			if(sur[2].getRenderID() != block.getBlock().getRenderID()) {
				GL11.glBindTexture(GL11.GL_TEXTURE_2D, block.getBlock().getTextureFromSide(2));
			    GL11.glBegin(GL11.GL_TRIANGLE_FAN); 
			      GL11.glTexCoord2f(0.0F, 1.0F); GL11.glVertex3f(x+0, y+1, z+0);
			      GL11.glTexCoord2f(0.0F, 0.0F); GL11.glVertex3f(x+0, y+1, z+1);
			      GL11.glTexCoord2f(1.0F, 0.0F); GL11.glVertex3f(x+1, y+1, z+1);
			      GL11.glTexCoord2f(1.0F, 1.0F); GL11.glVertex3f(x+1, y+1, z+0);
				GL11.glEnd(); 
			}
			
			if(sur[3].getRenderID() != block.getBlock().getRenderID()) {
				GL11.glBindTexture(GL11.GL_TEXTURE_2D, block.getBlock().getTextureFromSide(3));
			    GL11.glBegin(GL11.GL_TRIANGLE_FAN); 
			      GL11.glTexCoord2f(1.0F, 1.0F); GL11.glVertex3f(x+0, y+0, z+0);
			      GL11.glTexCoord2f(0.0F, 1.0F); GL11.glVertex3f(x+1, y+0, z+0);
			      GL11.glTexCoord2f(0.0F, 0.0F); GL11.glVertex3f(x+1, y+0, z+1);
			      GL11.glTexCoord2f(1.0F, 0.0F); GL11.glVertex3f(x+0, y+0, z+1);
				GL11.glEnd();
			}
			if(sur[4].getRenderID() != block.getBlock().getRenderID()) {
				GL11.glBindTexture(GL11.GL_TEXTURE_2D, block.getBlock().getTextureFromSide(4));
			    GL11.glBegin(GL11.GL_TRIANGLE_FAN); 
			      GL11.glTexCoord2f(1.0F, 1.0F); GL11.glVertex3f(x+1, y+0, z+0);
			      GL11.glTexCoord2f(1.0F, 0.0F); GL11.glVertex3f(x+1, y+1, z+0);
			      GL11.glTexCoord2f(0.0F, 0.0F); GL11.glVertex3f(x+1, y+1, z+1);
			      GL11.glTexCoord2f(0.0F, 1.0F); GL11.glVertex3f(x+1, y+0, z+1);
				GL11.glEnd();
			}
			if(sur[5].getRenderID() != block.getBlock().getRenderID()) {
				GL11.glBindTexture(GL11.GL_TEXTURE_2D, block.getBlock().getTextureFromSide(5));
			    GL11.glBegin(GL11.GL_TRIANGLE_FAN); 
				  GL11.glTexCoord2f(0.0F, 1.0F); GL11.glVertex3f(x+0, y+0, z+0);
			      GL11.glTexCoord2f(1.0F, 1.0F); GL11.glVertex3f(x+0, y+0, z+1);
			      GL11.glTexCoord2f(1.0F, 0.0F); GL11.glVertex3f(x+0, y+1, z+1);
			      GL11.glTexCoord2f(0.0F, 0.0F); GL11.glVertex3f(x+0, y+1, z+0);
			    GL11.glEnd();
			}
	}

This gives me only 14 fps.What should i do?

CodeBunny

At a quick glance, it looks like you are calling a glBind and glBegin/glEnd for every block.

If that's so, definitely stop doing that. Have the entire texture you need kept on a single texture, and call glBind once at the beginning of your render for the entire map. Additionally, structure the block geometry so that you can render everything from within a single glBegin/glEnd call.

Basically, try to package your entire terrain into a single geometric object.

miker9

Is there any possibility to do this with separate textures?
P.S.The FPS is now 36.But now i cant see any textures.

CodeBunny

Separate textures. No, not if you want it to be fast. The whole point is that you want to condense the render operation into as few, low-cost calls as possible. If you have multiple textures, you need to change the bound texture in the middle of your render, which is expensive. Additionally, you cannot change the bound texture in the middle of a glBegin/glEnd, so you need to stop and restart your vertex calls, which is also expensive.

I don't know of any way to get around these factors and still have it be well optimized. A real-world example: Minecraft has a single texture shared amongst every single block in the game.

RE: PS: Are textures enabled? Do you bind the texture at all? Do you set the texture coordinates?

RE: PS #2: Are you culling invisible faces? For example, are you only drawing block faces that are exposed to open air? This should vastly speed up your render time.

miker9

RE:PS:I just removed glbegin/glend from model and put them in world class, so texture bindings were between them.
RE:PS #2:Yes, it's done here:
if(sur[0].getRenderID() != block.getBlock().getRenderID()) {

sur[] is surrounding blocks array and render id is used for blocks like glass(you can't see the glass side through the glass, but you can see the wood).

CodeBunny

You'll need to use a single texture like I said. Does it work after you make this change?

Also, that looks like a bad way to handle culling. For one thing, I don't think it will give you the most effective culling possible. It looks like it only culls if the two blocks are of the same type. I can see how that applies to the glass-wood scenario, but you have to consider all cases. What happens if you have dirt, stone, iron ore, and gravel blocks all mixed up underground? This culling function would draw all of them, and that's a huge waste.

Also, once you have a better culling function, it's a good idea to "compile" the results (e.g., give each block surface an "isVisible" flag), and only update that value when a block touching it is modified.

miker9

You didn't understand me.renderID for all solid blocks(dirt, iron, wood...) is 1, for air it's 0, for glass - 2, for leaves - 3.But leaves also have renderType 1(wich makes all faces around visible)

CodeBunny

Okay, so it's like an "isTransparent" flag. Gotcha.

geekygenius

I smell Minecraft clone. I know in MC, each chunk has its own display list for each face of the block. (Up, down, north, south, east, west). So if you're a player in your west of a chunk, only the west faces are drawn. (Well, three faces actually, but one for now :)) When a block is modified, the entire list is re-compiled. If you render at a chunk level, and use only one glBegin(GL_QUADS) at the beginning of every display list, you could get some seriously fast stuff.

Good luck!

CodeBunny

...That's genius.

You'd need to separately render animated cubes, though, right? Fire cubes, for example, render all four faces at once. You'd have redundant rendering if you packaged that into each side map.

geekygenius

Quote from: CodeBunny on March 09, 2012, 16:55:54
...That's genius.

You'd need to separately render animated cubes, though, right? Fire cubes, for example, render all four faces at once. You'd have redundant rendering if you packaged that into each side map.

Well, you have notch to thank for that!

I know that Notch changes the texture to get the animation. So instead of rending with a different bound texture, and changing the pointer, he just changes the texture. In MC at least, its a 16x texture, so that's only 256 pixels, which is not very much at all.

miker9

QuoteSo instead of rending with a different bound texture, and changing the pointer, he just changes the texture
Nice idea.Now i am thinking about making a big texture "sheet" where the loaded textures will be placed.Then i can replace the fire texture with the second, third...So i wont have to make a separate render for animated blocks.