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!
|