• Create Account

## How does WoW's terrain generation work?

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

18 replies to this topic

### #1Beather  Members

103
Like
0Likes
Like

Posted 09 January 2010 - 07:27 AM

Hello, I'm very curious how World of Warcraft implements the terrain rendering. It does not need to load terrains while you are playing, why is this? I would be very grateful if someone could explain this concept to me! Thank you in advance.

### #2batabek  Members

148
Like
0Likes
Like

Posted 09 January 2010 - 07:29 AM

probably they are using some kind of terrain lod based on quadtrees or clipmaps.

### #30BZEN  Members

2194
Like
0Likes
Like

Posted 09 January 2010 - 08:19 AM

some form of LOD heightmap.

Everything is better with Metal.

### #4JTippetts  Moderators

12317
Like
0Likes
Like

Posted 10 January 2010 - 12:20 AM

With streaming, so that chunks are continuously being loaded as you move around.

### #5ItsDan  Members

104
Like
0Likes
Like

Posted 10 January 2010 - 01:19 PM

### #6Beather  Members

103
Like
0Likes
Like

Posted 10 January 2010 - 06:47 PM

I have a few more questions:

Does this mean that WoW uses multithreading to load the maps in the background?

What happens when you change from chunk?

What's the best way for me to visualize these chunks?

Do you know of an example that use these chunks to generate seamless terrain?

Thank you.

### #7Anntor  Members

254
Like
0Likes
Like

Posted 10 January 2010 - 08:51 PM

Quote:
 Original post by BeatherDoes this mean that WoW uses multithreading to load the maps in the background?

I don't know WoW (i know it, but never played it), but i know it must use multithreading for loading in the background, otherwise the main thread would stall when there is a huge chunk to load.

Quote:
 Original post by BeatherWhat happens when you change from chunk?What's the best way for me to visualize these chunks?

??? I don't know what you mean.

Quote:
 Original post by BeatherDo you know of an example that use these chunks to generate seamless terrain?

There was a nice article about Dungeon Siege's seamless terrain (from 2002) describing it in detail. I don't find it anymore, but Google gave me links to the slides at least.
The Continuous World of Dungeon Siege - Powerpoint slides

### #8Beather  Members

103
Like
0Likes
Like

Posted 10 January 2010 - 09:47 PM

Thanks a lot Anntor, I read the slides and that cleared things up a lot!

Suppose I have a heightmap, and I generate terrain from it.
What happens when I reach the border of that terrain?

This would mean there are 9 height maps loaded at all times.
9 height maps means 9 meshes in memory. Am I correct? This seems a lot of memory to me. Especially because WoW's zones are very big and the game runs very smooth.

My other question: What happens to my coordinate? How do I represent my character's coordinates?

I would say, to measure one's coordinates, that you have four members;

float X;
float Y;
float Z;

int mapID;

In this case mapID would be the current height map where the player is standing on. The other thread reads this mapID and loads all adjacent maps in silence. Is this correct?

Thank you again!
Nick

### #9Anntor  Members

254
Like
0Likes
Like

Posted 10 January 2010 - 10:27 PM

Quote:
 Original post by Beather...I guess there should always be another thread running to load all adjacent maps.This would mean there are 9 height maps loaded at all times.9 height maps means 9 meshes in memory. Am I correct? This seems a lot of memory to me. Especially because WoW's zones are very big and the game runs very smooth.

At first, yes, you have to load the adjacent nodes/maps/chunks/whatever. Hold them in memory and unload them, when you get to far away from the particular chunks.
Now to the memory size. Don't overestimate the needed size per chunk data. If its still to big, make your map chunks smaller! For a huge terrain, you just have the terrain mesh in memory (thousands of vertices still needs less memory than some big textures), but not the texture on a particular map patch far far away. You won't see it on your screen, so you don't need it in memory! You have to do the right partitioning of your data (textures eat up most of available memory)!

Quote:
 Original post by BeatherMy other question: ...I would say, to measure one's coordinates, that you have four members;...The other thread reads this mapID and loads all adjacent maps in silence. Is this correct?

Sounds okay. But take notice of the following:
If you move from one mapID to another, you load the 3 new adjacent map chunks, but wait with unloading of the old 3 chunks behind you. The usual approach is that the circle around you, where to start loading new map chunks is smaller than the one which determines when to unload a map chunk. That is because of the fact, when you are standing close to a mapID border and moving back and forward (hence passing over the border again and again), you would continually load and unload the same map chunks.

### #10Codeka  Members

1239
Like
0Likes
Like

Posted 10 January 2010 - 10:39 PM

Quote:
 Original post by AnntorThere was a nice article about Dungeon Siege's seamless terrain (from 2002) describing it in detail. I don't find it anymore, but Google gave me links to the slides at least.The Continuous World of Dungeon Siege - Powerpoint slides
All hail the Wayback Machine: The article.

### #11Beather  Members

103
Like
0Likes
Like

Posted 10 January 2010 - 10:42 PM

I think I'm starting to get it! Thanks a lot for your help!

### #12ItsDan  Members

104
Like
0Likes
Like

Posted 11 January 2010 - 05:48 AM

You also don't necessarily HAVE to store a mapID, your X/Y coordinates could represent a global value, although depending on the world you may need more precision. I'd likely use the mapID myself, although I'd use an X/Y value for that too, assuming I split up the terrain based on size and not complexity.

Also consider that this is a great place to implement LOD for your terrain. Anntor told you to imagine circles around your player which decide when you load/unload elements of the map. Also consider that you could have a VERY large circle for when to load a very low-detail terrain with a low-res texture, and another for when you want to load higher level data.

### #13Shuun  Members

123
Like
1Likes
Like

Posted 11 January 2010 - 06:44 AM

I have made exact copy of WoW like terrain renderer that can also display WoW terrain in past. Basically you have chunks of 9x9 vertices arranged in vertex buffer like VertexBuffer contains (chunk1Vertices, chunk2Vertices, etc) and have variations of index buffers for each type of transition so you can display lower triangle counts using same vertex buffers. Note the X pattern.

Each chunk have maximum of 4 textures assigned for it - they are using for splat mapping (multitexturing).

Also i can suggest that you store all the information you need in just one single Vector3 Position like this:

CPU - main memory will have information for each chunk like:
4 textures (ID, hash or similar lookup)
Vector3 base position - this will be position where X and Z coordinates (left handed system) are your chunk top left corner in game world and Y coordinate is average vertex Y position in chunk

GPU - video memory will have information like this:
VertexBuffer - only one Position (Vector3) is needed - you store your Vertice Y position RELATIVE TO BASE Y in X component and have texture coodrdinates, ranging from 0 to 1 stored in Y and Z components of your position so in vertex shader you can construct your position like this:

       float3 outPos;       Pos.Y = shaderInputBasePos.Y + inPos.X; //relative height stored in X of Position       Pos.X = shaderInputBasePos.X + inPos.Y * CHUNK_SCALE; //Texcoord component used to offset vertex position X       Pos.Z = shaderInputBasePos.Z + inPos.Z * CHUNK_SCALE; //Texcoord component used to offset vertex position Z       float2 outTexcoodrd;       outTexcoodrd.X = inPos.Y * TEXTURE_SCALE;       outTexcoodrd.Y = inPos.Z * TEXTURE_SCALE;

For loading/unloading you group say 8x8 chunks together and load/unload them when you are in range. DONT create or destroy vertex buffers in runtime, you will have say, 2x2 or 3x3 groups in sight range at once so you will only need to replace old data in vertex buffer (only X value of your position if you use technique i told) with new one when old disappears and new appears. Also you replace your old splat texture with new one for texturing so the rendering of stuff comes together like this:

     foreach visible chunkGroup           set splat texture to chunkGroup splat texture                    foreach visible chunk in current chunkGroup              set basePosition for vertex shader              set 4 diffuse textures for pixel shader              RenderCHUNK(): vertexBuffer = chunkGroup vertexBuffer,                             indexBuffer = calculated LOD for current chunk,                             vertexOffset = current chunk vertex offset,                             vertex & tris count = chunk vertex & tris counts (constant)

### #14Beather  Members

103
Like
0Likes
Like

Posted 11 January 2010 - 09:56 AM

Thank you very much for your replies.

Quote:
 Original post by ItsDanYou also don't necessarily HAVE to store a mapID, your X/Y coordinates could represent a global value, although depending on the world you may need more precision. I'd likely use the mapID myself, although I'd use an X/Y value for that too, assuming I split up the terrain based on size and not complexity.

I think I understand. Do you think that using a mapID gives trouble when transitioning from one mapID to the other? It seems easier to use a global X and Y coordinate.

Quote:
 Original post by SemeiBasically you have chunks of 9x9 vertices arranged in vertex buffer like VertexBuffer contains (chunk1Vertices, chunk2Vertices, etc) and have variations of index buffers for each type of transition so you can display lower triangle counts using same vertex buffers. Note the X pattern.

What is a "chunk" exactly? Is it a 3D mesh in memory?

Why 9x9 ?
Would it possible for you to show me a piece of your source code in DirectX C++, and/or the shader you used? (I understand if not)

Also, what's with the X pattern? I don't see the point.

Quote:
 Original post by SemeiEach chunk have maximum of 4 textures assigned for it - they are using for splat mapping (multitexturing).

Why 4 textures? Do you mean to assign a control texture: one with red, green and blue in it to determine which of three textures to draw (e.g. rock, sand, grass) ?

Quote:
 Original post by SemeiGPU - video memory will have information like this:VertexBuffer - only one Position (Vector3) is needed - you store your Vertice Y position RELATIVE TO BASE Y in X component and have texture coodrdinates, ranging from 0 to 1 stored in Y and Z components of your position so in vertex shader you can construct your position like this: float3 outPos; Pos.Y = shaderInputBasePos.Y + inPos.X; //relative height stored in X of Position Pos.X = shaderInputBasePos.X + inPos.Y * CHUNK_SCALE; //Texcoord component used to offset vertex position X Pos.Z = shaderInputBasePos.Z + inPos.Z * CHUNK_SCALE; //Texcoord component used to offset vertex position Z float2 outTexcoodrd; outTexcoodrd.X = inPos.Y * TEXTURE_SCALE; outTexcoodrd.Y = inPos.Z * TEXTURE_SCALE;

you store your Vertice Y position RELATIVE TO BASE Y in X component and have texture coodrdinates

Why vertex Y into X component? What has this to do with texture coordinates?
(I'm assuming that Y-axis is up)

Is shaderInputBasePos the top left corner of the mesh (chunk)?
Is inPos the actual vertex position being processed?

Why do you add the vertex X value to the shaderInputBasePos Y position?

What is CHUNK_SCALE and TEXTURE_SCALE used for more specifically?

Quote:
 Original post by SemeiFor loading/unloading you group say 8x8 chunks together and load/unload them when you are in range. DONT create or destroy vertex buffers in runtime, you will have say, 2x2 or 3x3 groups in sight range at once so you will only need to replace old data in vertex buffer (only X value of your position if you use technique i told) with new one when old disappears and new appears. Also you replace your old splat texture with new one for texturing so the rendering of stuff comes together like this: foreach visible chunkGroup set splat texture to chunkGroup splat texture foreach visible chunk in current chunkGroup set basePosition for vertex shader set 4 diffuse textures for pixel shader RenderCHUNK(): vertexBuffer = chunkGroup vertexBuffer, indexBuffer = calculated LOD for current chunk, vertexOffset = current chunk vertex offset, vertex & tris count = chunk vertex & tris counts (constant)

I'm sorry, I read it a few times but I still don't understand very well.
I also have to admit I am inexperienced with vertex and index buffers, so bear with me.

Thank you very much!

### #15ItsDan  Members

104
Like
0Likes
Like

Posted 11 January 2010 - 03:44 PM

Don't get too hung up on the details yet. Some of the comments are more related to how WoW specifically handles things (or appears to anyways) when your question seems to be more along the lines of "how do I create a seamless, large-scale world without loading screens".

The vertex buffer comment relates to the fact that creating/destroying memory on the GPU is "expensive" and so you should reuse the existing buffers with new data and not create a new buffer when you load a new chunk, since you're likely replacing an old chunk. But it all boils down to implementation details.

Practice with whatever you're comfortable with first. Then refactor, optimize, rewrite, whatever needs to be done.

### #16Shuun  Members

123
Like
0Likes
Like

Posted 11 January 2010 - 07:51 PM

*What is a "chunk" exactly? Is it a 3D mesh in memory?

Chunk is one piece (9x9) vertices, as it could be chunk of meat this is chunk of vertices so simple as it is there is no hidden meaning.

*Why 9x9 ?

Because its 256 triangles. Cache, blah blah, then its power of 2 lower tiles - usfefull for lod. Gives you 8x8 4 triangle tiles and hey we all like 8 and this allows you to create 4 lod levels starting from 8x8 to 4x4, 2x2 and 1x1

*Also, what's with the X pattern? I don't see the point.

LOD. Try to create some basic lod transitions and see how it comes together also note that this way you wont have one way repeating pattern as you would if you used regular approach - this way each triangle is more square, less rectangle so it looks better. You can create LOD transitions without using X pattern, you dont HAVE to use it. Im talking about WoW now. If you look at the picture and try to find lower detail representations of X pattern using the same vertices as higher lod you will be surprised how nice it all falls together ;)

*Why 4 textures? Do you mean to assign a control texture: one with red, green and blue in it to determine which of three textures to draw (e.g. rock, sand, grass) ?

I do, 4 because there is 4 texture channels in one ARGB texture and having 8 is a bit harsh on gpu.

*Why vertex Y into X component? What has this to do with texture coordinates?
(I'm assuming that Y-axis is up)

Information stored in vertex buffer for each vertex:
1) Y position of vertex (as like height field)
2) Texture coodrinates ,ranging from 0 to 1 with 0,0 being top left corner of tile and 1,1 bot right.

Layout of data:
1) Use only one POSITION
2) Store Y position (you can call it Height if you are confused with that "Y") in X component
3) Store texcoords in remaining (Y & Z) channels.

Why? Because its simple. First height, then 2x texcoord.

*Is shaderInputBasePos the top left corner of the mesh (chunk)?

It is.

*Is inPos the actual vertex position being processed?

"in"Pos - this is data that you have in vertex buffer.

*Why do you add the vertex X value to the shaderInputBasePos Y position?

Because there is Y Position - Height stored in X component of vertex buffers positions.

*What is CHUNK_SCALE and TEXTURE_SCALE used for more specifically?

CHUNK_SCALE will determine how big are dimensions of one chunk. for example, 8.

TEXTURE_SCALE will determine how many times texture will repeat thro one chunk, for example, 4.

Aint that obvious? :D

*I'm sorry, I read it a few times but I still don't understand very well.
I also have to admit I am inexperienced with vertex and index buffers, so bear with me.

You must understand vertex and index buffers in and out in middle of night to implement this, but hey, its not hard to learn it.

Note that this would be EXCELLENT lesson for you, i highly suggest you to make it as it contains tons and tons of useful learning stuff in it!

### #17Anntor  Members

254
Like
0Likes
Like

Posted 11 January 2010 - 07:51 PM

Quote:
 Original post by ItsDanDon't get too hung up on the details yet...

I second that [grin]

Quote:
 Original post by Beather...What is a "chunk" exactly? Is it a 3D mesh in memory?...

'Chunk' is just a block of data (Wikipedia: block, Google: memory chunks). What data is in is up to you and your application requirements. Chunks are fixed-size refering to the mapping of their carried data.

For example, person A writes a 3D terrain renderer and defines a terrain chunk as a mesh dataset (part of the whole map mesh) plus 2 textures. Person B writes a 2D isometric map renderer and thinks of a chunk as a set of 10 tile images, a list of objects inside that part of the map and some statistical data.
Both talk to each other and saying: "I load my adjacent chunks, when i'm getting close to that part of the map." And both are right, despite they have different chunk definitions. [grin]

### #18phresnel  Members

949
Like
0Likes
Like

Posted 11 January 2010 - 08:40 PM

Little nitpick: The thread title reads "terrain generation", the topic is "terrain rendering". Those are overlapping, yet different problems.

### #19Beather  Members

103
Like
0Likes
Like

Posted 12 January 2010 - 01:33 AM

Thank you for all your help!

I am planning to make this project soon, so this was very helpful!

I am going to study first more about vertex and index buffers, so that I know better how things work internally.

Nick

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.