🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Building An Isometric Game Using Horde3D (Part 4)

Published August 07, 2011
Advertisement
Despite what I said at the end of the previous post, I'm not going to fiddle with the scene manager in Horde3D right now. I looked at it, but I'm not going to muck around with trying to build a custom one at this point. Rather, I'll try to make-do with what I have, and optimize later if it really proves necessary.

So... To review a little bit, I've been experimenting with setting up the pipeline, rendering stages, etc... in order to re-use the pre-rendered and anti-aliased sprites I have made for Goblinson Crusoe. I've been attempting to come up with a drawing order that works, at least for most cases. At this point, I haven't completely ruled out re-doing all the artwork, eliminating intermediate alpha values and re-scaling the affected pixels. I could easily enough write a script to travel the directories and process each sprite. However, I'll stick with the anti-aliased sprites for now and only do that if it proves necessary for performance reasons.

I have also decided upon acceptable constraints for the decal layers. Rather than allow arbitrary decal placement as in the existing GC engine, which is really an under-used feature anyway, I can accept the constraint of not allowing decals on the same "layer", or offset, to overlap, avoiding the issue of flickering and z-fighting. I have settled upon applying decals as wall or floor primitives with transformation offsets from the base grid to offset it away from the geometry being decal'ed; this allows me to combine decals and wall pieces into the same geometry rendering pass, saving at least a little bit of overhead. This now puts me at the following pipeline:

[source]













[/source]

There's an addition: the CHARACTER context pass, associated with Character class materials. Doing skeletally animated characters is the topic of this article.

The whole point of switching to Horde3D was to allow skeletally animated characters. The reason for this is twofold: 1) I can reduce texture requirements, since storing 32 facing directions of pre-rendered animations per character, not to mention the layers required for a paper-doll type equipment system, gets horrendous very quickly. 2) Implementing a 2D paper-doll system is hairy. Sure, I could use depth-sprite information and a custom shader to handle the layering, but that would serve only to inflate the texture usage even further. A fully skinned 3D character, of course, bypasses that.

In order to add the Character material class, I wrote a new set of shaders for the CHARACTER context. At the moment, the pixel shader is the same as the shaders for the other stages; however, the vertex shader implements the skinning from the Horde3D sample shaders, minus the normal-mapping and lighting/shading calculations. Since my weak little lappy can't handle normal-mapping anyway, I stripped them. I'm not really going for high-def here anyway. Of course, with the guts ripped out of the shaders I now have no lighting at all to speak of, but I'll fix that down the road a little. In the meantime, I wanted to get the Character material class in and start experimenting with making characters work.

So today's project was wrestling with Blender to master the workflow from Blender to Horde3D. According to something I read at http://www.horde3d.org/forums/viewtopic.php?f=11&t=1551 , Collada animation export has been disabled in 2.58 pending some re-factoring. So to be safe I reverted to a copy of 2.57 for the purpose of this section.

I started with a skeleton. I call him HordeMan!

hordeman1.jpg



The idea is that each component that can be hung on a character is attached to a particular bone or set of bones in the skeleton. From reading the docs, I understand that each vertex in a mesh can have up to 4 bone weights assigned to it, so that's cool. That should allow for plenty of smoothness in animation. (I mean, it's not like I'm any kind of animation master here.) I named the bones in the skeleton so they'd be easier to assign. In Blender, of course, you can create a mesh, set a number of vertex groups within the mesh, and name those groups according to the bones in the skeleton so that they match up. Newer release of Blender allow for creating the vertex groups automatically from the nearest bone influences, which can work if you are particularly lazy. While this will assign a bone weight for every bone in the armature to a given bone (with many weighted to 0), the Horde3D ColladaConv utility will clean-up after your sloppy ass by simply normalizing the 4 highest weights and using those.

Now that I have HordeMan's bony little skeleton (complete with a bone to allow him to wield a sword) in place, I'll flesh out (heh heh) FrankenHordeMan from assorted component meshes. I want to assemble him from many different meshes instead of a single mesh, in order to ensure that paper-dolling is as easy as I believe it shall be. So I created a Torso/Abdomen mesh, two Arm meshes, two Leg meshes, a Head mesh and a Sword mesh. They're crude, of course. What HordeMan lacks in style, detail, and elegance he makes up for in... uh... ummmm....

Look! It's HordeMan walking!

hordeman2.jpg


He's not pretty, but being pretty is not his job. Mess with the HordeMan and you will get the Guns, both the right Gun, and the left Gun. Blammo! Smash! And that right leg? Yeah...

Of course, the animation is done via key-framing. I created a basic 8-frame walk cycle to animate the mesh, then I went through the process of exporting the skeleton and the individual pieces, then converting them via ColladaConv.

I gotta say, I'm not a huge fan of this pipeline. I mean, everything seems to work okay for the most part, but I'm just not in favor of having the conversion step. First, Collada is kind of a difficult format to work with. It's complex, it's apparently a whole-scene format (so you can only have one model per .blend file, or else all models will end up in the same geometry file) and it seems as if Blender is having a difficult time nailing down their implementation of the exporter. The format is very verbose, and the Collada files that Blender exports are not very human-readable. My ideal situation is to have available a direct exporter to the given format, whether it's export to the Horde3D format, or Horde3D being able to import a set of standard formats directly. It's mostly just a personal preference, I guess.

So, anyway, I got all the pieces exported then returned to my test app to get them set up. And here I ran into a bit of a hitch. I set up all of the resources:

[source]
-- components
self.headres = h3dAddResource(H3DResTypes.SceneGraph, "hordemanhead.scene.xml", 0)
self.bodyres = h3dAddResource(H3DResTypes.SceneGraph, "hordemanbody.scene.xml", 0)
self.rarmres = h3dAddResource(H3DResTypes.SceneGraph, "hordemanrarm.scene.xml", 0)
self.larmres = h3dAddResource(H3DResTypes.SceneGraph, "hordemanlarm.scene.xml", 0)
self.rlegres = h3dAddResource(H3DResTypes.SceneGraph, "hordemanrleg.scene.xml", 0)
self.llegres = h3dAddResource(H3DResTypes.SceneGraph, "hordemanlleg.scene.xml", 0)
self.swordres = h3dAddResource(H3DResTypes.SceneGraph, "hordemansword.scene.xml", 0)

self.hordemananim = h3dAddResource(H3DResTypes.Animation, "hordeman.anim", 0)
[/source]

Then when I went to instance the character, I tried to do this:

[source]
self.hordemannode=h3dAddNodes(H3DRootNode, self.bodyres)
self.head=h3dAddNodes(self.hordemannode, self.headres)
self.rarm=h3dAddNodes(self.hordemannode, self.rarmres)
self.larm=h3dAddNodes(self.hordemannode, self.larmres)
self.rleg=h3dAddNodes(self.hordemannode, self.rlegres)
self.lleg=h3dAddNodes(self.hordemannode, self.llegres)
self.sword=h3dAddNodes(self.hordemannode, self.swordres)

h3dSetNodeTransform(self.hordemannode, 3, 0, 3, 0,90,0, 1,1,1)
h3dSetupModelAnimStage( self.hordemannode, 0, self.hordemananim, 0, "", false );
[/source]

Then, in the addFrameTime function of the state context I do the following:

[source]
self.hordemantime=self.hordemantime+elapsed
h3dSetModelAnimParams( self.hordemannode, 0, self.hordemantime, 1 );
[/source]

Unfortunately, adding the component nodes as children doesn't seem to add any kind of link between the parent node's animation/skeleton and the children, so while the children receive the node transformations of their parent (ie, they move, rotate, etc... just as they are supposed to) the individual componentes are not animated by the skeleton. Only the initial mesh used in creating the base node, the Body, is animated.

Now, I figured out how to get around this:

[source]
h3dSetupModelAnimStage( self.head, 0, self.hordemananim, 0, "", false)
h3dSetupModelAnimStage( self.rarm, 0, self.hordemananim, 0, "", false)
h3dSetupModelAnimStage( self.larm, 0, self.hordemananim, 0, "", false)
h3dSetupModelAnimStage( self.rleg, 0, self.hordemananim, 0, "", false)
h3dSetupModelAnimStage( self.lleg, 0, self.hordemananim, 0, "", false)
h3dSetupModelAnimStage( self.sword, 0, self.hordemananim, 0, "", false)
[/source]

And in addFrameTime:

[source]
h3dSetModelAnimParams( self.sword, 0, self.hordemantime, 1)
h3dSetModelAnimParams( self.head, 0, self.hordemantime, 1)
h3dSetModelAnimParams( self.rarm, 0, self.hordemantime, 1)
h3dSetModelAnimParams( self.larm, 0, self.hordemantime, 1)
h3dSetModelAnimParams( self.rleg, 0, self.hordemantime, 1)
h3dSetModelAnimParams( self.lleg, 0, self.hordemantime, 1)
[/source]

This works. All of the pieces are animated, everything works great. However, I worry about this, for it feels like I am instancing 7 different copies of the skeleton, and animating seven different copies of the skeleton. Ideally, I would like to animate a single skeleton, but have that one instance be used to animate all 7 component meshes. Figuring out the exact way to do this might take a little bit of digging around. At any rate, though, I have gotten it working:

hordeman3.jpg

He marches in an endless circle, and the camera endlessly follows.

I did have to fiddle around with the animation timing. I exported 8 frames of animation at 25 FPS, which apparently results in an animation that is (as determined by trial and error) about 6 seconds long. Determining the exact way that Horde3D interprets frames and timing may take a little experimentation.

I am pretty pleased that it worked so well. I do need to figure out the skeleton issue (I've got an open topic on the Horde3D forum about it), but other than that I'm happy with it. I even managed to squeeze a few more FPS out of this groaning old piece-o-crap lappy by revising the pipeline, so I'm happy with that. I'm getting around 40 to 43 FPS with sorted blended sprites and a single animated HordeMan, and that's even after bumping the resolution up to 1024x768.

And here, I need to scrap everything. At this point, I've figured out how most of the bits and pieces work, enough that I can start evaluating it from a performance standpoint, at least. In order to construct a valid performance test, I need to start abstracting out the interfaces to simplify the construction of levels in a generic fashion. Right now, everything is hard-coded and stuck in the state context, but I need to implement proper spawning and construction. And I'll need to test it on a "real" computer, because this crashing, BSOD-ing hunk of landfill-bound rubbish is hardly representative of even the midrange gamer computer these days.
1 likes 2 comments

Comments

shdpl
Is it how people usually do this?

I didn't tried to make this kind of system yet (in horde3d btw.), but i thought about adding naked model as one mesh, then add a joint per every equipment slot (so artist can fine-tune ways of wearing things for every kind of character).

Actually, fully 3d games are in my interest.
August 23, 2011 11:48 PM
JTippetts
I don't think a joint per equipment slot would work that well, because what about pants? The way I am doing it, I assign the verts to vertex groups named parallel to the joints that affect them, and weight them for smoother animation. But I've done simple roll-your-own skeletal anim systems in the past, and it is trivial to be able to animated multiple meshes from a single skeleton, if you can get access to the skeleton, and it saves on redundant calculations. Consider that if each component mesh has its own instance of a skeleton as above, and you have thirteen or so pieces of equipment, that is thirteen times per visible character that the skeleton tree is recursed and joint matrix tables are built. Just seems like a waste, so I hope marciano gives us a convenient way of sharing skeleton data soon in H3D.
August 25, 2011 08:52 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement