Panda3D Manual: Creating Geometry from Scratch
  <<prev top next>>  

Creating Geometry of Your Own

Making your own geometry on the fly can be broken down into 3 steps:
1)Create a GeomVertexData object to hold the vertices of your geometry. Add the data for all the vertices you will need.
2)Create GeomPrimitive objects and assign vertices to them
3)Create a Geom object with the object created in step 1 and add all the primitives you created. You can know put this Geom in a GeomNode and render the geometry.

The following sections explains these processes in detail.

The Geom and GeomPrimitive Class

GeomPrimitive is the base class for all the primary shapes that Panda can draw (GeomLines, GeomLinestrips, GeomPoints, GeomTriangles, GeomTrifans, and GeomTristrips). Any geometry that is rendered is broken down into these shapes. You cannot mix objects of different fundamental types, for instance, GeomLines, GeomTriangles, and GeomPoints, in the same Geom; but you can (and should) put GeomTriangles, GeomTrifans, and GeomTristrips together, or GeomLines and GeomLinestrips together.

Each kind of GeomPrimitive constructor accepts a UsageHint parameter. The list of possible UsageHints is the same as that given for GeomVertexData objects.

GeomPrimitive objects keep track of what vertices to use by storing a list of integers. These integers tell the object which row in the GeomVertexData object it will be associated with has the vertex it needs. This is why when you add a vertex to a GeomPrimitive object, using addVertex(rowNum), you have to supply the row of the vertex.

There are also some convenience functions built on top of addVertex. addConsecutiveVertices(int start, int vertNum) adds vertNum vertices from the GeomVertexData object starting from the row start. There is also addNextVertices(numAdd) which acts like addConsecutiveVertices but uses the last vertex you have added as start.

When you are done adding the vertices to a GeomPrimitive object for each primitive you must call the closePrimitive() function. This makes sure that the number of vertices you have added to the primitive define it properly. Note that each GeomPrimitive object can store more than one of its particular kind of primitive; for instance, you can store many triangles in a single GeomTriangles object, and many triangle strips in a single GeomTristrips. You do this by adding all the vertex numbers in order, calling closePrimitive() after you have added the vertices for each primitive.

When this process is done you have to place these GeomPrimitive objects in a Geom object. You must pass the GeomVertexData object to the Geom constructor. You can add many GeomPrimitives to a single Geom, but primitives in the same Geom object must take their vertices from the same GeomVertexData. Furthermore, several Geom objects can share the same GeomVertexData object. You can add primitives to Geom objects by using addPrimitive(myPrimitive) where myPrimitive is the GeomPrimitive you want to add.

When the Geom object is setup, you can now place it in a GeomNode object and place it in the scene graph using addGeom(myGeom) where myGeom is the Geom object you want to add.

Usage tip: modern PC graphics cards can render many thousands of triangles in a frame, but the PC bus can only handle a few hundred triangle batches in a 60Hz frame. In Panda, each GeomPrimitive is sent to the graphics card as a single batch, therefore, it is important to minimize the number of GeomPrimitive objects in your scene graph, and to put as many triangles as possible within a single GeomPrimitive--assuming they are all going to be onscreen at the same time anyway. On the other hand, if you put too many unrelated triangles in the same GeomPrimitive, with both offscreen and onscreen triangles in the same primitive, then you will be wasting time drawing all of the offscreen triangles. Finding the best balance point is difficult and depends on the quality of graphics hardware your application is targeting. As a general rule of thumb, it is best to use as few GeomPrimitives as possible for small, atomic objects, but also to break up your scene into many individual objects (i.e. GeomNodes) that can easily be culled when they go offscreen.

An example to clarify things:

 #in this example, we'll show how to make a square from scratch
#There is no "GeomQuad" class so we have to use two triangles.

#step 1) create GeomVertexData and add vertex information
format=GeomVertexFormat.getV3()
vdata=GeomVertexData("vertices", format, Geom.UHStatic)

vertexWriter=GeomVertexWriter(vdata, "vertex")
vertexWriter.addData3f(0,0,0)
vertexWriter.addData3f(1,0,0)
vertexWriter.addData3f(1,0,1)
vertexWriter.addData3f(0,0,1)

#step 2) make primitives and assign vertices to them
tris=GeomTriangles(Geom.UHStatic)

#have to add vertices one by one since they are not in order
tris.addVertex(0)
tris.addVertex(1)
tris.addVertex(3)

#indicates that we have finished adding vertices for the first triangle.
tris.closePrimitive()

#since the coordinates are in order we can use this convenience function.
tris.addConsecutiveVertices(1,3) #add vertex 1, 2 and 3
tris.closePrimitive()

#step 3) make a Geom object to hold the primitives
squareGeom=Geom(vdata)
squareGeom.addPrimitive(tris)

#now put squareGeom in a GeomNode. You can now position your geometry in the scene graph.
squareGN=GeomNode("square")
squareGN.addGeom(squareGeom)
render.attachNewNode(squareGN)

  <<prev top next>>