lilith3d

 

Engine

Documentation

Notes

Tutorial 7: Models and Meshes

  • Creating models
  • Mesh
  • MobileMesh
  • StaticMesh
  • Reacting to Terrain Changes

In this tutorial, we'll see how to create and use the variety of Meshes that Lilith supports. A Mesh, remember, is an object in the world that draws. Buildings, planes, people, monsters, etc. In this tutorial:

  • shift-left click flattens land and creates a Pyramid.
  • left-clicking summons a meteor. If the meteor hits the Pyramid, it will remove it.
  • control-left-click places a flag. The flag moves as the terrain changes.

Before we even look at the code, it's time to discuss the art. Up to this point, the models have magically appeared from the lilith/graphics directory. And these will as well. This tutorial uses 3 new models - plane.ac, pyramid.ac, and flag.ac.

They are created in AC3D and saved in AC3D's natural file format: .ac files. .ac files are text based and straightforward to parse. Although Lilith has a generic file importer structure, the only importers that exists at this time is for AC3D files (static objects) and Quake MD2 files (animated objects). (Writing new ones would be much appreciated.) Blender (blender.org) is a very full featured 3D editor that can also export .ac files.

  • AC3D uses a different axis than Lilith. Your Front, Side, and Plan views are preserved by import, not the axis. The axis are "fixed" on import, so the AC3D "side", "plan" and "front" are preserved.
  • Scale is preserved. 1x1 in AC3D units in 1x1 units in Lilith3D.
  • The AC3D origin - 0,0,0 - is the origin of the Mesh in Lilith as well.
  • Lilith renders single sided polygons, so be careful that your surface normals face the correct direction. ("outward"). 2-sided rendering is ignored by Lilith so that won't help fix bad normals.
  • Lilith supports tris and quads from AC3D. I've never seen it export anything else, but if it does (and Lilith will complain) you can use "Surface->Triangulate" in the modelling package.
  • Lilith will load and use a texture from AC3D, as long as it is on the ImportPath.
  • An object can have any number of textures. However, switching textures has a modest performance cost. Commonly used models should be made from one texture when possible.
  • Creating good models is time consuming and takes practice. Mine aren't that great. I can't help you there. :)

Tutorial 7: Load new resources.

After AC3D, it's time to load the new resources.

resourcePool->LoadStaticRes( "flag.ac", "flag", 0 );				// hard shading
resourcePool->LoadStaticRes( "plane.ac", "plane", SMOOTH_SHADING );
resourcePool->LoadStaticRes( "pyramid.ac", "pyramid", 0 ); // hard shading

The new resources are loaded with the LoadStaticRes call. You can also load TreeResources and AnimationResources as needed. Resources can be hard shaded - like buildings - or smooth shaded. The SMOOTH_SHADING will turn on smooth shading.

The Airplane

Tutorial 7: Create an airplane

2 relevant lines of code:

const StaticResource* planeResource = ResourcePool::Instance()->GetStaticRes( "plane" );
planeMesh = new StaticMesh( planeResource );

And quite a bit to see. The first line gets a MeshReource from the ResourcePool. There can be only one ResourcePool, so you just need to get the instance and query for the MeshResource. (Error checking would be nice too.) The MeshResource is a const pointer - there is nothing to manage, create, or destroy.

The second line - new StaticMesh. But it moves? StaticMeshes aren't animated, but can still be moved around. An AnimatedMesh is for resources that have animation loops.

Destroying a Mesh is easy: delete mesh. The destructor of the Mesh automatically frees it from Lilith's data structures.

Tutorial 7: Move the Plane

A bunch of code to move the plane in a circle...pay heed! Lilith and OpenGL use degrees, while the standard C library uses radians. RAD_TO_DEG converts between units.

The Flag

The Flag is different from the Plane, because we want it to be attached to the terrain.

Tutorial 7: Creating a Flag

const StaticResource* flagResource = MeshResManager::Instance()->GetStaticRes( "flag" );
flagMesh = new StaticMesh( flagResource );
flagMesh->AttachToTerrain( true );

Just like the plane...except that:

  • The AttachToTerrain( true ) method places the flag on the terrain. If the terrain changes, the z value of Mesh will change to follow it. Very handy!

The Pyramid

The pyramid is the most sophisticated use. It follows the terrain, but is destroyed if the terrain it sets on changes.

Tutorial 7: Creating a Pyramid

The interesting thing to notice is when we create the Pyramid - in a FlattenDone() callback. We take advantage of the last tutorial, where the Game instance was registered to listen to Flatten Morph, and create the Pyramid when the ground is flat. (Although it doesn't have to be.)

const StaticResource* pyramidResource = MeshResManager::Instance()->GetStaticRes( "pyramid" );
pyramidMesh = lilith->GetTerrainMesh()->CreateBuilding( flatten->Bounds().min.x+2, flatten->Bounds().min.y+2, pyramidResource );

A BuildingMesh is a type of StaticMesh that will:

  • Clear terrain so that trees don't stick through the building.
  • Adjust the pather so Mobs don't path through buildings.
  • And, for a mesh intended to support walking, allow WalkingMobs to walk on and path through the building.

Both very useful properties! The BuildingMesh will clean up after itself when it is deleted.

Tutorial 7: Handle a terrain change

We want the Pyramid to be on flat terrain. If the terrain changes, the Pyramid should go away. Easily done! In the game constructor, we register the Game with the TerrainMesh:

lilith->GetTerrainMesh()->AddListener(   this );

And we will recieve TerrainChange() callbacks.

if ( pyramidMesh && fBounds.Intersect( pyramidMesh->AABB() ) )
{
delete pyramidMesh;
pyramidMesh = 0;
}

When the terrain change comes in, the bounds are checked against the Pyramid. If the intersect, then the Pyramid is deleted and the pather and trees will be restored.

Conclusion

Congratulations! You've covered the basics of creating a 3D game engine with Lilith!