Simple Panda3D tutorial - terrain from height map

Return to General Discussion

Simple Panda3D tutorial - terrain from height map

Postby chombee » Mon Feb 06, 2006 6:04 pm

Download:

http://www.homepages.inf.ed.ac.uk/s0094060/terrain.tar.gz

Screenshot:

Image

# Author: Sean Hammond
# Contact: s0094060@sms.ed.ac.uk
#
# terrain.py, version 0.001, the first ever version.
#
# I release this code to the public. You can do whatever you want with it.
# I hope that others will take this simple and inefficient example and build on
# it, improve it, fix my mistakes, etc.
# If you do make improvements to this code, I'd appreciate if you would let me
# know, and send me a copy of your new version.
#
# This tutorial demonstrates how to construct a mesh from a height map and
# apply a texture to it in Panda3D. Various other basic functions of Panda3D
# are present also, such as using keyboard events to move the terrain around
# and displaying a text overlay on the Panda3D rendering window. This code
# *does not* do procedural terrain generation, it uses a set of height maps and
# textures that are packed along with this code (five height maps and
# corresponding textures are provided). However it should be easy to write
# height map and texture generation algorithms and plug them into this code.
#
# The height maps and textures
# ----------------------------
#
# The provided height maps and textures were produced using terraform:
#
# http://terraform.sourceforge.net/
#
# All of the height maps are of size 100x100, resulting in a comfortable frame
# rate on my (less than impressive) system, except terrain 1 which is 250x250
# and slows down my system significantly.
#
# All of the textures are of size 1000x1000 (actually the texture for terrain 2
# is only 100x100).
#
# The code should be able to handle height maps and textures of any
# size. They shouldn't even need to be square (although I have only tried it
# with squares). However note that there are no efficiency considerations for
# high-poly terrains in this code, and see Known issues below.
#
# The main shortcoming of this demo so far as a terrain system, aside from its
# overall lack of sophistication (it's my first Panda3D program) is that the
# terrain it generates doesn't look great because it's not that detailed. This
# is due to the height maps and textures provided - if larger height maps and
# much larger textures could be provided I think things would look much better.
# However, see Known issues below.
#
# Known issues
# ------------
#
# This code can only handle height maps (and therefore terrain) up to a certain
# size. The largest height map I have rendered successfully is 250x250 (terrain
# 1 provided). With a height map of 300x300 or above the code gives the
# following error:
#
# Assertion failed: *(PN_uint16 *)pointer == a at line 1073 of panda/src/gobj/geom VertexColumn.cxx
# Traceback (most recent call last):
# File "terrain.py", line 72, in ?
# triangles.addVertex(x+heightMap.getXSize()+1)
# AssertionError: *(PN_uint16 *)pointer == a at line 1073 of panda/src/gobj/geomVe rtexColumn.cxx
#
# I don't know what the exact cut-off point at which this error occurs is, but
# it's somewhere between 250x250 and 300x300.
#
# I think this error may be fixed by improving the code to split the terrain up
# into multiple GeomTriangles objects. At the moment the entire terrain mesh is
# created in a single GeomTriangles object regardless of its size. I don't know
# if doing that is good or bad. Just a guess.
#
# In any case, a height map of size 250*250 results in (I think) a terrain mesh
# of 125,000 triangles (each pixel in the height map corresponds to a single
# square of the mesh, each square of the mesh is constructed as two triangles)
# which is enough to slow Panda3D down too much on my system. It's obvious that
# to create larger or higher detail terrains some form of LOD (controlling the
# detail level of terrain depending on how far from the camera it is) or other
# efficiency improvement is needed.[/img]
chombee
 
Posts: 244
Joined: Fri Jan 13, 2006 10:09 am

Postby zpavlov » Tue Feb 07, 2006 9:56 pm

Yay for height maps!

All right, so the reason why you can't go passed 250x250 is a bit of a doozy (I may be way off on this too, but it seems right):


So if you use pdb.pm() at the crash, you'll see it dying around vertex index 65535, which is in line with the assertion error (in this case, its asking if the newest index is indexArray[65536],
and since uint16(65536)=0x0000, its triggering the assertion error)

Okay, so the heart of this lies in the GeometryPrimitive's cycleDataWriter. Its based off of CycleData, which is a generic object used by many panda classes to store data. Each class usually implements their own version, and the GeomPrimitive unfortunately uses uint16s to store the vertex indices (check
panda/src/gobj/geomPrimitive.i line 367. CDWriter and Reader are built off of CDATA).

I'm curious if this is a hardware limitation?
(by the way, geomPrimitive.cxx line 247 is the add_vertex function)

Anywho, there's a funny solution that may be nigh impossible (other than
splitting into multiple geomPrimitives to avoid the 65k vertex limit):

So the GeomPrimitive only builds an index off of the vertex list if it perceives the need to. If your vertex indices in the primitive are strictly ordered (1,2,3,4,5, etc). then it keeps the primitive unindexed. As a result, it avoids using the cycle data at all, preventing the assertion. You can test this with the following:

myGeom=GeomTriangles(Geom.UHStatic)
Code: Select all
for i in range(100000):
   myGeom.addVertex(3*i)
   myGeom.addVertex(3*i+1)
   myGeom.addVertex(3*i+2)
   myGeom.closePrimitive()


will make 1000000 polygons. You can kill the whole thing by following up with:

Code: Select all
myGeom.addVertex(1)  #BEWAAAAAAARE


This will force indexing, and subsequently cause roughly 3000000 assertion errors to trigger. whee. So yeah, if you find a way to order the triangles to the same order as the verticess, you can do larger than 250x250 height maps.
zpavlov
 
Posts: 60
Joined: Tue Apr 05, 2005 4:41 pm

Postby drwr » Wed Feb 08, 2006 2:24 am

Err, yeah, so the fact that GeomPrimitive doesn't actually create the index until it's needed allows you to "work around" this hardware limitation--in OpenGL, for certain drivers, in certain rendering modes. But for the most part, you're not supposed to do that. You can't really go beyond 16-bit indexes on current-generation hardware (although some OpenGL drivers will automatically remap this for you). DirectX simply doesn't allow it.

Really, you're just not supposed to stuff more than 65536 vertices into a single GeomPrimitive. Even though it works in some limited cases, it's not optimal. One day this will be supported transparently, especially when there is hardware that can handle it.

The CycleData bit, by the way, is just the standard way that Panda stores data in its classes to implement pipelining--different versions of an object for the different stages of the graphics pipeline visible in different threads (not yet fully implemented). For the most part, you can treat things within a CycleData object as if they are things within the class itself.

David
drwr
 
Posts: 11425
Joined: Fri Feb 13, 2004 12:42 pm
Location: Glendale, CA

Postby drwr » Wed Feb 08, 2006 2:41 am

Actually, you know what? I'm mistaken. DirectX does support more than 16-bit indexes.

So it might be safe to use this feature of Panda. But to do this, you have to explicitly tell your GeomPrimitive that you intend to use 32-bit indexes, like this:
Code: Select all
primitive.setIndexType(GeomPrimitive.NTUint32)

Still, strictly speaking, you should not create a GeomPrimitive with more than base.win.getGsg().getMaxVerticesPerPrimitive() different vertices in it; and you should not create a GeomVertexArray with more than base.win.getGsg().getMaxVerticesPerArray() vertices in it. These limits are imposed by your hardware, and they may be different for different cards (or different drivers).

David
drwr
 
Posts: 11425
Joined: Fri Feb 13, 2004 12:42 pm
Location: Glendale, CA

Postby chombee » Wed Feb 08, 2006 6:20 am

Great, thanks guys.

I actually started working on some improvements to the code, mainly for efficiency (in terms of framerate) which happen to include building the mesh out of multiple GeomPrimitive's anyway. The mesh is so big (I'm now scaling it to 10 or 100 times the size it was in the currently released code, that's scaling, not increasing the no. of triangles) that it can't all fit on screen at once, so it is not the most efficient thing to have the whole mesh as one GeomPrimitive, even regardless of this index error.

So, it may be that the current development version I have can handle much larger height maps, I'll try it sometime.
chombee
 
Posts: 244
Joined: Fri Jan 13, 2006 10:09 am


Return to General Discussion

Who is online

Users browsing this forum: No registered users and 1 guest