Procedurally Generating animated 3D Models (and textures)?

I dont have animations yet, I just got a handle to a joint (exposeJoint())and transformed a joint like that and then moved the Actor, in the latter the bones didnt move with the actor. What I mean by that is the visual representations of the bones didnt move: [Visualise actor bones (joints))

Did you mean controlJoint() instead of exposeJoint()? controlJoint() is used to get a node that you can transform to move the joint. exposeJoint() is the opposite, it gets a node that moves when the joint moves due to its own animation, but transforming the node that exposeJoint() returns will have no effect on anything.

David

Yeah, I got the names wrong in this topic.
Anyway, Ill try and post

okay, coming back to this, the difference in the code is only in following:

gnode = GeomNode('gnode')
gnode.addGeom(geom)
ch.addChild(gnode)

I, to be able to assign textures, have to make a nodepath from the geomnode:

gnode = GeomNode('gnode')
gnode.addGeom(geom)
np = NodePath(gnode)
np.setTexture(tex)
ch.addChild(np.node())

I don’t think theres a difference here, right?
I’m rewriting the rest of the code, but just in case.

EDIT: The bones still remain in the same place after I rotate the Actor. I’ll try to assign the weigths properly and see if it persists, but I think this isnt related.

You’re right, there’s no difference between those two code blocks, other than the fact that the texture gets applied in the second. You’re saying that one of them works and the other one doesn’t?

I don’t know what you mean when you say the bones “remain in the same place”. Are they not parented to the Actor? All nodes should move with their parent, regardless of any transform weighting.

David

Actually, looks like they both don’t.
Here is your code with the joint renderer snippet:

from direct.directbase.DirectStart import *
from pandac.PandaModules import *
from direct.actor.Actor import Actor

from random import *

# Create a character.
ch = Character('simplechar')
bundle = ch.getBundle(0)
skeleton = PartGroup(bundle, '<skeleton>')

# Create the joint hierarchy.
root = CharacterJoint(ch, bundle, skeleton, 'root',
                      Mat4.identMat())
hjoint = CharacterJoint(ch, bundle, root, 'hjoint',
                        Mat4.translateMat(Vec3(10, 0, 0)))
vjoint = CharacterJoint(ch, bundle, hjoint, 'vjoint',
                        Mat4.translateMat(Vec3(0, 0, 10)))

# Create a TransformBlendTable, listing all the different combinations
# of joint assignments we will require for our vertices.
root_trans = JointVertexTransform(root)
hjoint_trans = JointVertexTransform(hjoint)
vjoint_trans = JointVertexTransform(vjoint)

tbtable = TransformBlendTable()
t0 = tbtable.addBlend(TransformBlend())
t1 = tbtable.addBlend(TransformBlend(root_trans, 1.0))
t2 = tbtable.addBlend(TransformBlend(hjoint_trans, 1.0))
t3 = tbtable.addBlend(TransformBlend(vjoint_trans, 1.0))
t4 = tbtable.addBlend(TransformBlend(hjoint_trans, 0.7, vjoint_trans, 0.3))

# Create a GeomVertexFormat to represent the vertices.  We can store
# the regular vertex data in the first array, but we also need a
# second array to hold the transform blend index, which associates
# each vertex with one row in the above tbtable, to give the joint
# assignments for that vertex.
array1 = GeomVertexArrayFormat()
array1.addColumn(InternalName.make('vertex'),
                3, Geom.NTFloat32, Geom.CPoint)
array2 = GeomVertexArrayFormat()
array2.addColumn(InternalName.make('transform_blend'),
                 1, Geom.NTUint16, Geom.CIndex)
format = GeomVertexFormat()
format.addArray(array1)
format.addArray(array2)
aspec = GeomVertexAnimationSpec()
aspec.setPanda()
format.setAnimation(aspec)
format = GeomVertexFormat.registerFormat(format)

# Create a GeomVertexData and populate it with vertices.
vdata = GeomVertexData('vdata', format, Geom.UHStatic)
vdata.setTransformBlendTable(tbtable)
vwriter = GeomVertexWriter(vdata, 'vertex')
twriter = GeomVertexWriter(vdata, 'transform_blend')

vwriter.addData3f(0, 0, 0)
twriter.addData1i(t1)

vwriter.addData3f(10, 0, 0)
twriter.addData1i(t2)

vwriter.addData3f(10, 0, 10)
twriter.addData1i(t3)

vwriter.addData3f(8, 0, 2)
twriter.addData1i(t4)

# Be sure to tell the tbtable which of those vertices it will be
# animating (in this example, all of them).
tbtable.setRows(SparseArray.lowerOn(vdata.getNumRows()))

# Create a GeomTriangles to render the geometry
tris = GeomTriangles(Geom.UHStatic)
tris.addVertices(2, 3, 1)
tris.closePrimitive()
tris.addVertices(1, 3, 0)
tris.closePrimitive()

# Create a Geom and a GeomNode to store that in the scene graph.
geom = Geom(vdata)
geom.addPrimitive(tris)
gnode = GeomNode('gnode')
gnode.addGeom(geom)
ch.addChild(gnode)

# Now create the animation tables.  (We could also load just this part
# from an egg file, if we already have a compatible table ready.)
bundle = AnimBundle('simplechar', 5.0, 10)
skeleton = AnimGroup(bundle, '<skeleton>')
root = AnimChannelMatrixXfmTable(skeleton, 'root')

hjoint = AnimChannelMatrixXfmTable(root, 'hjoint')
table = [10, 11, 12, 13, 14, 15, 14, 13, 12, 11]
data = PTAFloat.emptyArray(len(table))
for i in range(len(table)):
    data.setElement(i, table[i])
hjoint.setTable(ord('x'), CPTAFloat(data))

vjoint = AnimChannelMatrixXfmTable(hjoint, 'vjoint')
table = [10, 9, 8, 7, 6, 5, 6, 7, 8, 9]
data = PTAFloat.emptyArray(len(table))
for i in range(len(table)):
    data.setElement(i, table[i])
vjoint.setTable(ord('z'), CPTAFloat(data))

wiggle = AnimBundleNode('wiggle', bundle)

# Finally, wrap the whole thing in a NodePath and pass it to the
# Actor.
np = NodePath(ch)
anim = NodePath(wiggle)
a = Actor(np, {'simplechar' : anim})
a.reparentTo(render)
a.setTwoSided(True)
a.setPos(0, 50, 0)
a.setP(-45)
a.loop('simplechar')

def renderBones(actor, part, parentNode = None, indent = ""):
    if isinstance(part, CharacterJoint):
        np = actor.exposeJoint(None, 'modelRoot', part.getName())

        if parentNode and parentNode.getName() != "root":
            lines = LineSegs()
            lines.setThickness(3.0)
            lines.setColor(random(), random(), random())
            lines.moveTo(0, 0, 0)
            lines.drawTo(np.getPos(parentNode))
            lnp = parentNode.attachNewNode(lines.create())
            lnp.setBin("fixed", 40)
            lnp.setDepthWrite(False)
            lnp.setDepthTest(False)

        parentNode = np

    for child in part.getChildren():
        renderBones(actor, child, parentNode, indent + "  ")

renderBones(a, a.getPartBundle('modelRoot'), None) 


run()

Now it works here, I rotated the actor and the bones rotated with it. But they don’t in my case.

So, you’re saying my code works and yours doesn’t. What do you want me to do?

David

Well I found that out in my last post, so nothing.

OK, i think i found the difference and the problem.

My actor consists of multiple pieces (geom(nodes)), unlike in the sample.
I did

actor.clearModelNodes()
actor.flattenStrong()

and the bones now follow the actor. But I’m afraid if flattening an actor like that will bring up other problems.

Right now I have geoms, which I assign to geomNodes and then make a nodePath to set a texture on it, I then add each GeomNode to the Character object with

character.addChild(nodePath.node())

one at a time. Might there be a problem with adding more than 1 geomNode to a Character like that? Ive read that a geomNode can have more than 1 geoms, maybe I am only allowed to add all the geoms to a single geomNode and add it as child to the Character?

Also, this is the code which sets the weights and assign bones to vertices, right?

vdata = GeomVertexData('data', format, Geom.UHStatic)
transform = GeomVertexWriter(vdata, 'transform_blend')
tbtable = TransformBlendTable()

transform.addData1i(tbtable.addBlend(TransformBlend(bonename_trans, weight)))

assuming I have made a JointVertexTransform for each bone and pass it to “bonename_trans” and weigth is the weight value from 0.0 to 1.0

There’s no problem with having multiple GeomNodes and multiple Geoms under a Character node. I still don’t see anything wrong in the code you’re posting, and I have difficulty conceiving of a situation in which a character doesn’t animate, but flattening it works.

But, you are using the Actor interface, which is designed for models loaded from egg files, not for models generated at runtime. Could be something in that interface is not aware of all of the joints you’ve added or something? I still don’t understand what you mean about the bones not following the actor.

David

but Im using animations loaded from a file, I thought I needed the Actor interface to be able to set an animation. …youre using an Actor too in the snippets, right?

Well, I’m just grasping at straws here. Unless you show me a complete (but small) program that demonstrates the problem, I have nothing really to go on.

You never have to use Actor. That’s all just a convenience layer around the lower-level Character interface, which provides all the functionality you need.

David