What’s the best way to compute the normals of a procedurally-generated Geom, given only the vertices and primitives? Ordinarily I would do this in python data structures before writing the Geom, as is done here: sites.google.com/site/dlampetest … sing-numpy However, I know there’s also a recomputeNormals() method in EggData. My apologies if this has been posted a million times.
There are different ways to procedurally create geoemtry:
(1) Egg-API
This way is most useful if you are generating the geometry offline, and after generation save it as .egg file. The method you mention will do the dirty work for you. Also have a look at the methods for tangent/binormal calculation.
(2) GeomVertexWriter/GeomPrimitive/…
This way is faster, but you have to go down to the dirty details of how geometry is stored in Panda3D, e. g. you have no “polygons”. Instead you have to mangle with triangles, triangle strips or fans. This way does not (up to my knowledge) offer a method for automatic computation of vertex normals - you have to do it yourself.
Thanks, enn0x. I had been doing it the hard way as usual. The GeomVertexWriter method was covered in the manual. Is there a demo or sample somewhere that covers the Egg API method?
Never mind - I found an old post on the forums ([url]Collision data with code-generated EggData]) that got me started on the right track. Thanks for the tip, this will make a lot of features easier.
OK, back to the drawing board…I am using the Egg API now, but having problems with lighting. The code below generates a simple cube around the origin. The vertex normals should be there, and I’ve added an ambient and directional light to apply shading. Is there anything I’m missing?
import direct.directbase.DirectStart
from pandac.PandaModules import EggData, EggVertex, EggPolygon, loadEggData, EggPoint, EggTriangleStrip, EggVertexPool,EggGroup
from panda3d.core import \
DirectionalLight,\
AmbientLight,\
Vec3,\
LVector3d,\
Vec4,\
Point2D,\
Point3D
color=Vec4(1,0,0,1)
data=EggData()
group=EggGroup()
vp=EggVertexPool('vertex_pool')
group.addChild(vp)
data.addChild(group)
l=100 #Length of one side of cube
rectangle=[(-l, -l), (-l, l), (l, l), (l, -l) ]
#Draw sides
poly=EggTriangleStrip()
group.addChild(poly)
for point in rectangle+[rectangle[0]]:
for elevation in [-l, l]:
v=EggVertex()
v.setColor(color)
v.setNormal(LVector3d(0,0,1))
v.setPos(Point3D(point[0], point[1], elevation))
poly.addVertex(vp.addVertex(v))
#Draw top & bottom
for elevation in [-l, l]:
if elevation==l: rectangle.reverse()
poly=EggPolygon()
group.addChild(poly)
for point in rectangle:
v=EggVertex()
v.setColor(color)
v.setNormal(LVector3d(0,0,1))
v.setPos(Point3D(point[0], point[1], elevation))
poly.addVertex(vp.addVertex(v))
data.removeUnusedVertices(True)
data.recomputePolygonNormals()
data.recomputeVertexNormals(1)
camera.setPos(0,-5*l,0)
cube_np=render.attachNewNode(loadEggData(data))
dlight = DirectionalLight('light')
dlight.setColor(Vec4(.8,.8,.5,1))
dlnp = render.attachNewNode(dlight)
cube_np.setLight(dlnp)
alight = AmbientLight('alight')
alight.setColor(Vec4(0.2, 0.2, 0.2, 1))
alnp = render.attachNewNode(alight)
render.setLight(alnp)
run()
A few more observations:
It’s better to use EggVertexPool.createUniqueVertex() than EggVertexPool.addVertex, since the former first searches the pool for a matching vertex and returns it if it finds one. This prevents duplicate vertices from getting into the pool.
EggData.recomputeVertexNormals() doesn’t seem to compute the normals correctly. Take the cube above, which is made of a single triangle strip, with quads for the top and bottom. If I set the correct normals manually, the shading is displayed correctly (left cube). If I then call recomputeVertexNormals(), it adds 8 extra vertices to the top and bottom whose normals point straight up and down, making those polygons flat-shaded (right cube). If I leave the normals blank or set them to (0,0,0), the whole cube is flat-shaded.
I’ll probably calculate normals myself and just chalk this one up to another cryptic Panda3D feature. Still a lot easier than using the GeomVertexWriter method.
import direct.directbase.DirectStart
from pandac.PandaModules import EggData, EggVertex, EggLine, EggPolygon, loadEggData, EggPoint, EggTriangleStrip, EggVertexPool,EggGroup
from panda3d.core import \
DirectionalLight,\
AmbientLight,\
Vec3,\
LVector3d,\
LPoint3d,\
Vec4,\
Point2D,\
Point3D
color=Vec4(1,0,0,1)
data=EggData()
vp=EggVertexPool('vertex_pool')
data.addChild(vp)
l=100 #Length of one side of cube
rectangle=[(-l, -l), (-l, l), (l, l), (l, -l) ]
#Draw sides
poly=EggTriangleStrip()
data.addChild(poly)
for point in rectangle+[rectangle[0]]:
for elevation in [-l, l]:
v=EggVertex()
v.setColor(color)
v.setPos(Point3D(point[0], point[1], elevation))
poly.addVertex(vp.createUniqueVertex(v))
#Draw top & bottom
for elevation in [-l, l]:
if elevation==l: rectangle.reverse()
poly=EggPolygon()
data.addChild(poly)
for point in rectangle:
v=EggVertex()
v.setColor(color)
v.setPos(Point3D(point[0], point[1], elevation))
poly.addVertex(vp.createUniqueVertex(v))
for i in range(vp.size()):
v=vp.getVertex(i)
normal=LVector3d(0,0,0)+LVector3d(v.getPos3())
normal.normalize()
v.setNormal(normal)
print "vertex",i,"has normal",v.getNormal()
print "Recomputing vertex normals."
data.recomputeVertexNormals(.1)
for i in range(vp.size()):
v=vp.getVertex(i)
print "vertex",i,"has normal",v.getNormal()
camera.setPos(0,-5*l,0)
cube_np=render.attachNewNode(loadEggData(data))
dlight = DirectionalLight('light')
dlight.setColor(Vec4(.8,.8,.5,1))
dlnp = render.attachNewNode(dlight)
cube_np.setLight(dlnp)
alight = AmbientLight('alight')
alight.setColor(Vec4(.5,.5,.5, 1))
alnp = render.attachNewNode(alight)
render.setLight(alnp)
run()
I had a similar problem with normal calculations in the obj2egg script created by Treeform. That script only uses addVertex, and doesn’t use computeUniqueVertex. I fixed the normal calculation program using
egg.recomputeVertexNormals(float(a))
with the variable “a” being the degree limit for smoothing. I also used
egg.recomputeTangentBinormal(GlobPattern(""))
to create tangents and binormals for normal mapping.
I’m not sure exactly why I didn’t run into the problem you’re facing, but if memory serves my output did have “duplicate” vertices where the position of the vertex needed to be the same, but the UVs or normals were different.
It’s possible that the reason for the straight up or down normals you’re getting is because the recomputeVertexNormals method is getting confused by the unique vertices you’re manually creating, which it would normally generate itself? I don’t know, but it might be worth testing.
It may also be worth trying calculateTangentBinormal as well, to see if that’s fixing the problem some how.
There’s no need to be too concerned about adding extra vertices to the VertexPool, since these aren’t real-time vertices. When the real-time geometry is created from the EggData, any redundant vertices are automatically coalesced at that time.
recomputeVertexNormals() is behaving exactly as it is intended to. When you call it on a cube, it is creating smooth vertices around the corners of the cube, which looks weird. You have to pick an angle that is the correct value to exclude any sharp angles such as the ones on your cube; usually 45 is a good choice if you don’t have a better value in mind for your particular model. Or just call recomputePolygonNormals() instead if you have an entirely faceted model like a cube.
David