14.7MeshExamples 239
One side of the argument contends that without state, vertices are simple ob-
jects, and the entire system is easier to understand. The problem with this ap-
proach, however, is that combination effects can often get dicey, and we have to
recompute modifications on a vertex many more times than we would if there
was state.
The argument for having state is quite persuasive. If a vertex contains a des-
tination set of properties (position, color, etc.) and uses mesh parameters to
morph from their current states toward the destination states, then we end up with
a very consistent and predictable set of movements. It allows easy control of ver-
tex physics and provides the ability to apply a mesh-wide set of physical proper-
ties. It’s a “fire-and-forget” mentality where a force gets applied to the mesh, and
the mesh is responsible for reacting. Our result can be quite interesting consider-
ing that we can alter a texture’s physics on a mesh-wide basis. Take a texture that
appears as rubble and a texture that appears as cloth. Both can now react to the
same force in very different ways. A texture of rubble would be rigid and morph
little given a physics force, while a cloth texture would have drastic mesh-
morphing results. While both have their uses, for simplicity, our version uses the
stateless implementation, and we recompute our effects each rendering cycle. In
accordance with that, the burden of vertex modification is passed from vertex
physics onto modifier objects.
Modifiers
Modifiers are what the mesh is all about. After we split an image into a mesh, it
still looks exactly the same to the player. It’s when we apply modifier objects to
the mesh that we change its appearance with effects that amaze.
Modifier objects are given the mesh each frame update and are allowed to
modify the vertex properties prior to rendering. Each mesh holds onto a collec-
tion of these modifiers, which are sorted into priorities since some may need to
happen prior to others. For example, if deforming a vertex, one may want the
deformation to happen before normal colored lighting so that the darkening effect
of simulated shadow happens last.
14.7MeshExamples
Below are several simple examples of mesh manipulation. These are just scratch-
ing the surface of what can be done, and following the patterns of these samples,
adding new modifier objects should be quick and easy.
240 14.2DMagic
float mPosition[2]; // 2D position (in world space)
float mWH[2]; // width, height
Mesh *mMesh; // points to our parent mesh
uint32 mID; // unique ID for this modifier
uint32 mDeathTime; // world time we're scheduled to perish
uint16 mPriority; // used to sort our modifiers
bool mDirty; // if true, recheck our params
Listing 14.1. Data in base modifier class.
Modifiers derive from a modifier base class, which consists of the data mem-
bers shown in Listing 14.1.
PointLight
We often color our vertices. A simple way to achieve this is to develop a light
source similar to a 3D point light. A point light contains a position, radius, color,
and fall-off function, as shown in Listing 14.2. The fall-off function determines
how hard or soft the light’s boundary is at its radius.
Modifiers that use lighting derive from a common
MeshModifierLightBase
class that handles generic point-light lighting, as shown in Listing 14.3.
class PointLight
{
protected:
float mColor[4]; // RGBA color
float mXY[2]; // our position
float mScreenRadius; // 0-1
float mScreenRadiusAsDistance; // world space distance
float mFallOffPercent; // 0-1, multiplies radius
uint32 mID; // our unique ID
};
Listing 14.2. Data from a point light.
14.7MeshExamples 241
bool MeshModifierLightBase::LightVertex(uint32 inFrameID,
MeshVertex& ioV, const PointLight *inL)
{
// Get our distance sq, another closer check.
float theDistSq = DistanceSqTo(inL->GetX(), inL->GetY(),
ioV.GetWorldX(), ioV.GetWorldY());
float theRadiusDistSq = inL->GetRadiusDistSq();
if (theDistSq > theRadiusDistSq)
{
// Too far away to modify this vertex.
return (false);
}
// Now we need the real distance.
float theDist = FastSqrt(theDistSq);
// Compute our fall off.
float fallDist = inL->GetRadiusDist() * inL->GetFallOffPercent();
// Find our strength (of lighting edge).
float perStr = 1.0F;
if (theDist >= fallDist)
{
// Inverse left over falloff amount.
float leftOver = (inL->RadiusAsDist() - fallDist);
// Compute percent str, clamp it to a 0-1 range.
perStr = clamp((1.0F - ((theDist - fallDist) / leftOver)),
0.0F, 1.0F);
}
// Blending.
if (!ioV.TouchedThisColorFrame(inFrameID))
{
// First time through, use the base color of our object.
LerpColors(mMesh->GetRGBA(), inL->GetRGBA(), perStr, ioV.GetRGBA());
// Mark this vertex as having its color changed this frame.
ioV.MarkColorFrameID(inFrameID);
}
else
{
242 14.2DMagic
// Already colored this frame, blend with it.
LerpColors(ioV.GetRGBA(), inL->GetRGBA(), perStr, ioV.GetRGBA());
}
return (true);
}
Listing 14.3. Example of using a point light to light a vertex.
During the mesh’s UpdateModifiers() phase prior to rendering, each modi-
fier has its
ProcessMesh() method called. Our MeshModifierLightGroups::
ProcessMesh()
method, as shown in Listing 14.4, performs the following steps:
Examines the dirty flags and updates itself as needed.
Caches the base light color to be applied.
Loops over each vertex and does the following:
Applies the base color for this modifier or mesh. If the mesh and modifi-
er have different default colors, we mark this vertex as color changed.
Determines whether the vertex is in our bounding rectangle (as an opti-
mization).
Loops over each point light and calls
LightVertex().
bool MeshModifierLightGroup::ProcessMesh(uint32 inFrame)
{
bool res = false;
// Update dirty.
UpdateDirtyIfNeeded();
// Used as assert checking now, but could make thread safe.
ListLocker theLock(mProtectLightList);
size_t theLightMax = mLights.size();
// Virtual call, gets us the modifier (or mesh) color for each vertex.
const float *theBaseColor = GetBaseColor();
// If this is a unique base color, mark this as having
// a different color.
14.7MeshExamples 243
bool theDiffBaseColor = !ColorMatches(theBaseColor, mMesh->GetRGBA());
// Loop over each and process it.
uint32 theRowSize = mMesh->GetRowSize();
for (uint32 theX = 0; theX < theRowSize; ++theX)
{
for (uint32 theY = 0; theY < theRowSize; ++theY)
{
// Modify this vertex by the modifier object.
MeshVertex& vert = mMesh->GetVertex(theX, theY);
// Set the default color.
vert.SetRGBA(theBaseColor);
// Is the modifier's default color different than the mesh color?
if (theDiffBaseColor)
vert.MarkColorFrameID(inFrameID);
// Is it in the bounds?
if (!Contains(vert.GetWorldX(), vert.GetWorldY()))
continue;
// Yes, this modifier altered the mesh.
res = true;
// For each light, light up the vertex.
for (size_t lightC = 0; lightLoop < lightMax; ++lightC)
res |= LightVertex(inFrame, vert, mLights[lightC]);
}
}
return (res);
}
Listing 14.4. Example of how a modifier with many lights interacts with a mesh.
Ultimately, our mesh makes many effects possible with minimal coding. It’s
designed around building modifier objects, similar to functors in the standard
template library. Certainly, instead of a mesh, unique systems can be generated
for many of the effects listed below. Particles work for some, while independent-
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.176.228