And to set those global coordinates, you can use the net transform of the NodePath:
mat = nodeP.getNetTransform().getMat()
r = range(4)
net_transform = Mat4D(*[mat.getCell(i, j) for i in r for j in r])
egg_group.transform(net_transform)
As to the entry, in the eggSyntax.txt file it states:
which seems to be correct, while in the API for EggTransform it says:
which is probably a mistake then.
Instead of worrying about the order in which the component transforms need to be applied, you could just set the overall transform on the EggGroup:
mat = nodeP.getTransform().getMat()
r = range(4)
local_transform = Mat4D(*[mat.getCell(i, j) for i in r for j in r])
egg_group.setTransform3d(local_transform)
The following code sample illustrates the use of the above to export a hierarchy to an egg file:
from panda3d.core import *
from panda3d.egg import *
from direct.showbase.ShowBase import ShowBase
class Cube(object):
def __init__(self, parent_np, name):
polys = []
normals = []
positions = []
uvs = []
vertex_format = GeomVertexFormat.getV3n3cpt2()
vertex_data = GeomVertexData("cube_data", vertex_format, Geom.UHStatic)
tris_prim = GeomTriangles(Geom.UHStatic)
pos_writer = GeomVertexWriter(vertex_data, "vertex")
normal_writer = GeomVertexWriter(vertex_data, "normal")
uv_writer = GeomVertexWriter(vertex_data, "texcoord")
vertex_count = 0
for direction in (-1, 1):
for i in range(3):
normal = VBase3()
normal[i] = direction
for a, b in ( (-1., -1.), (-1., 1.), (1., 1.), (1., -1.) ):
pos = VBase3()
pos[i] = direction
pos[(i - direction) % 3] = a
pos[(i - direction * 2) % 3] = b
uv = VBase2(1. - max(0., a), max(0., b))
positions.append(pos)
uvs.append(uv)
pos_writer.addData3f(pos)
normal_writer.addData3f(normal)
uv_writer.addData2f(uv)
vertex_count += 4
indices = [vertex_count - i for i in range(4, 0, -1)]
tris_prim.addVertices(indices[0], indices[1], indices[2])
tris_prim.addVertices(indices[2], indices[3], indices[0])
poly = [(positions[i], uvs[i]) for i in indices]
polys.append(poly)
normals.append(normal)
cube_geom = Geom(vertex_data)
cube_geom.addPrimitive(tris_prim)
cube_node = GeomNode(name)
cube_node.addGeom(cube_geom)
self.origin = parent_np.attachNewNode(cube_node)
self.polys = polys
self.normals = normals
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
# set up a light source
p_light = PointLight("point_light")
p_light.setColor(VBase4(1., 1., 1., 1.))
self.light = self.camera.attachNewNode(p_light)
self.light.setPos(5., -10., 7.)
self.render.setLight(self.light)
do_export = True # set to False after the egg file has been generated
egg_filename = "cubes.egg"
if do_export:
# create cube1
cube1 = Cube(self.render, "cube1")
origin = cube1.origin
origin.setColor(VBase4(1., 0., 0., 1.))
origin.setScale(1.5)
# create cube2 as a child of cube1
cube2 = Cube(origin, "cube2")
origin = cube2.origin
origin.setColor(VBase4(0., 1., 0., 1.))
origin.setPos(10., 15., 12.)
origin.setScale(2., 1., 1.5)
origin.setHpr(30., 50., 10.)
tex = self.loader.loadTexture("my_texture.png")
tex_stage = TextureStage.getDefault()
origin.setTexture(tex_stage, tex)
origin.setTexRotate(tex_stage, 65.)
origin.setTexScale(tex_stage, 1.5, .8)
self.cubes = [cube1, cube2]
print "Original hierarchy:\n", cube1.origin.ls()
self.export(egg_filename)
else:
cubes = self.loader.loadModel(egg_filename)
cubes.reparentTo(self.render)
print "Loaded hierarchy:\n", [cubes.ls()]
self.run()
def export(self, filename):
egg_data = EggData()
egg_data.addChild(EggCoordinateSystem(CSZupRight))
parent_grp = egg_data
for i, cube in enumerate(self.cubes):
origin = cube.origin
name = "cube%d" % (i + 1)
group = EggGroup(name)
parent_grp.addChild(group)
vertex_pool = EggVertexPool(name)
group.addChild(vertex_pool)
color = cube.origin.getColor()
tex_stage = TextureStage.getDefault()
if origin.hasTexture(tex_stage):
tex = origin.getTexture(tex_stage)
tex_filename = tex.getFilename()
egg_tex = EggTexture("tex", tex_filename)
egg_data.addChild(egg_tex)
mat = origin.getTexTransform(tex_stage).getMat()
r = range(3)
tex_transform = Mat3D(*[mat.getCell(i, j) for i in r for j in r])
egg_tex.setTransform2d(tex_transform)
else:
egg_tex = None
for poly, normal in zip(cube.polys, cube.normals):
egg_poly = EggPolygon()
egg_poly.setColor(color)
if egg_tex:
egg_poly.addTexture(egg_tex)
for pos, uv in poly:
egg_vert = EggVertex()
egg_vert.setPos(Point3D(*pos))
egg_vert.setUv(Point2D(*uv))
egg_vert.setNormal(Vec3D(*normal))
vertex_pool.addVertex(egg_vert)
egg_poly.addVertex(egg_vert)
group.addChild(egg_poly)
r = range(4)
# the net transform is needed to give the vertices global coordinates
# (i.e. coordinates relative to render/the world)
mat = origin.getNetTransform().getMat()
net_transform = Mat4D(*[mat.getCell(i, j) for i in r for j in r])
group.transform(net_transform)
# the local transform (i.e. relative to the parent NodePath) needs to be
# set for the EggGroup, since all of these will be post multiplied in the
# order they are encountered to produce a net transformation matrix, with
# which the geometry assigned to this group will be inverse transformed to
# move its vertices to the local space
mat = origin.getTransform().getMat()
local_transform = Mat4D(*[mat.getCell(i, j) for i in r for j in r])
group.setTransform3d(local_transform)
parent_grp = group
egg_data.writeEgg(Filename.fromOsSpecific(filename))
MyApp()
EDIT:
updated above code to include texture transforms.
Yes, that seems to be correct.
NOTE:
After loading the egg file, the vertices are always at the global coordinates, no matter what transforms were set for the EggGroup. That doesn’t surprise me (e.g. if no transforms were set, it’s like baking the net transform into the vertices), but what I find funny is that the normals do seem to be affected.