added everything
This commit is contained in:
1100
engine/ts/tsAnimate.cc
Executable file
1100
engine/ts/tsAnimate.cc
Executable file
File diff suppressed because it is too large
Load Diff
398
engine/ts/tsCollision.cc
Executable file
398
engine/ts/tsCollision.cc
Executable file
@ -0,0 +1,398 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "sim/sceneObject.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Collision methods
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
bool TSShapeInstance::buildPolyList(AbstractPolyList * polyList, S32 dl)
|
||||
{
|
||||
// if dl==-1, nothing to do
|
||||
if (dl==-1)
|
||||
return false;
|
||||
|
||||
AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::buildPolyList");
|
||||
|
||||
// get subshape and object detail
|
||||
const TSDetail * detail = &mShape->details[dl];
|
||||
S32 ss = detail->subShapeNum;
|
||||
S32 od = detail->objectDetailNum;
|
||||
|
||||
// set up static data
|
||||
setStatics(dl);
|
||||
|
||||
// nothing emitted yet...
|
||||
bool emitted = false;
|
||||
U32 surfaceKey = 0;
|
||||
|
||||
S32 start = mShape->subShapeFirstObject[ss];
|
||||
S32 end = mShape->subShapeNumObjects[ss] + start;
|
||||
if (start<end)
|
||||
{
|
||||
MatrixF initialMat;
|
||||
Point3F initialScale;
|
||||
polyList->getTransform(&initialMat,&initialScale);
|
||||
|
||||
// set up for first object's node
|
||||
MatrixF mat;
|
||||
MatrixF scaleMat(true);
|
||||
F32* p = scaleMat;
|
||||
p[0] = initialScale.x;
|
||||
p[5] = initialScale.y;
|
||||
p[10] = initialScale.z;
|
||||
MatrixF * previousMat = mMeshObjects[start].getTransform();
|
||||
mat.mul(initialMat,scaleMat);
|
||||
mat.mul(*previousMat);
|
||||
polyList->setTransform(&mat,Point3F(1, 1, 1));
|
||||
|
||||
// run through objects and collide
|
||||
for (S32 i=start; i<end; i++)
|
||||
{
|
||||
MeshObjectInstance * mesh = &mMeshObjects[i];
|
||||
|
||||
if (od >= mesh->object->numMeshes)
|
||||
continue;
|
||||
|
||||
if (mesh->getTransform() != previousMat)
|
||||
{
|
||||
// different node from before, set up for this node
|
||||
previousMat = mesh->getTransform();
|
||||
|
||||
if (previousMat != NULL)
|
||||
{
|
||||
mat.mul(initialMat,scaleMat);
|
||||
mat.mul(*previousMat);
|
||||
polyList->setTransform(&mat,Point3F(1, 1, 1));
|
||||
}
|
||||
}
|
||||
// collide...
|
||||
emitted |= mesh->buildPolyList(od,polyList,surfaceKey);
|
||||
}
|
||||
|
||||
// restore original transform...
|
||||
polyList->setTransform(&initialMat,initialScale);
|
||||
}
|
||||
|
||||
clearStatics();
|
||||
|
||||
return emitted;
|
||||
}
|
||||
|
||||
bool TSShapeInstance::getFeatures(const MatrixF& mat, const Point3F& n, ConvexFeature* cf, S32 dl)
|
||||
{
|
||||
// if dl==-1, nothing to do
|
||||
if (dl==-1)
|
||||
return false;
|
||||
|
||||
AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::buildPolyList");
|
||||
|
||||
// get subshape and object detail
|
||||
const TSDetail * detail = &mShape->details[dl];
|
||||
S32 ss = detail->subShapeNum;
|
||||
S32 od = detail->objectDetailNum;
|
||||
|
||||
// set up static data
|
||||
setStatics(dl);
|
||||
|
||||
// nothing emitted yet...
|
||||
bool emitted = false;
|
||||
U32 surfaceKey = 0;
|
||||
|
||||
S32 start = mShape->subShapeFirstObject[ss];
|
||||
S32 end = mShape->subShapeNumObjects[ss] + start;
|
||||
if (start<end)
|
||||
{
|
||||
MatrixF final;
|
||||
MatrixF* previousMat = mMeshObjects[start].getTransform();
|
||||
final.mul(mat, *previousMat);
|
||||
|
||||
// run through objects and collide
|
||||
for (S32 i=start; i<end; i++)
|
||||
{
|
||||
MeshObjectInstance * mesh = &mMeshObjects[i];
|
||||
|
||||
if (od >= mesh->object->numMeshes)
|
||||
continue;
|
||||
|
||||
if (mesh->getTransform() != previousMat)
|
||||
{
|
||||
previousMat = mesh->getTransform();
|
||||
final.mul(mat, *previousMat);
|
||||
}
|
||||
emitted |= mesh->getFeatures(od, final, n, cf, surfaceKey);
|
||||
}
|
||||
}
|
||||
|
||||
clearStatics();
|
||||
|
||||
return emitted;
|
||||
}
|
||||
|
||||
bool TSShapeInstance::castRay(const Point3F & a, const Point3F & b, RayInfo * rayInfo, S32 dl)
|
||||
{
|
||||
// if dl==-1, nothing to do
|
||||
if (dl==-1)
|
||||
return false;
|
||||
|
||||
AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::buildPolyList");
|
||||
|
||||
// get subshape and object detail
|
||||
const TSDetail * detail = &mShape->details[dl];
|
||||
S32 ss = detail->subShapeNum;
|
||||
S32 od = detail->objectDetailNum;
|
||||
|
||||
// set up static data
|
||||
setStatics(dl);
|
||||
|
||||
S32 start = mShape->subShapeFirstObject[ss];
|
||||
S32 end = mShape->subShapeNumObjects[ss] + start;
|
||||
RayInfo saveRay;
|
||||
saveRay.t = 1.0f;
|
||||
const MatrixF * saveMat = NULL;
|
||||
bool found = false;
|
||||
if (start<end)
|
||||
{
|
||||
Point3F ta, tb;
|
||||
|
||||
// set up for first object's node
|
||||
MatrixF mat;
|
||||
MatrixF * previousMat = mMeshObjects[start].getTransform();
|
||||
mat = *previousMat;
|
||||
mat.inverse();
|
||||
mat.mulP(a,&ta);
|
||||
mat.mulP(b,&tb);
|
||||
|
||||
// run through objects and collide
|
||||
for (S32 i=start; i<end; i++)
|
||||
{
|
||||
MeshObjectInstance * mesh = &mMeshObjects[i];
|
||||
|
||||
if (od >= mesh->object->numMeshes)
|
||||
continue;
|
||||
|
||||
if (mesh->getTransform() != previousMat)
|
||||
{
|
||||
// different node from before, set up for this node
|
||||
previousMat = mesh->getTransform();
|
||||
|
||||
if (previousMat != NULL)
|
||||
{
|
||||
mat = *previousMat;
|
||||
mat.inverse();
|
||||
mat.mulP(a,&ta);
|
||||
mat.mulP(b,&tb);
|
||||
}
|
||||
}
|
||||
// collide...
|
||||
if (mesh->castRay(od,ta,tb,rayInfo))
|
||||
{
|
||||
if (!rayInfo)
|
||||
{
|
||||
clearStatics();
|
||||
return true;
|
||||
}
|
||||
if (rayInfo->t <= saveRay.t)
|
||||
{
|
||||
saveRay = *rayInfo;
|
||||
saveMat = previousMat;
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collide with any skins for this detail level...
|
||||
// TODO: if ever...
|
||||
|
||||
// finalize the deal...
|
||||
if (found)
|
||||
{
|
||||
*rayInfo = saveRay;
|
||||
saveMat->mulV(rayInfo->normal);
|
||||
rayInfo->point = b-a;
|
||||
rayInfo->point *= rayInfo->t;
|
||||
rayInfo->point += a;
|
||||
}
|
||||
clearStatics();
|
||||
return found;
|
||||
}
|
||||
|
||||
Point3F TSShapeInstance::support(const Point3F & v, S32 dl)
|
||||
{
|
||||
// if dl==-1, nothing to do
|
||||
AssertFatal(dl != -1, "Error, should never try to collide with a non-existant detail level!");
|
||||
AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::support");
|
||||
|
||||
// get subshape and object detail
|
||||
const TSDetail * detail = &mShape->details[dl];
|
||||
S32 ss = detail->subShapeNum;
|
||||
S32 od = detail->objectDetailNum;
|
||||
|
||||
// set up static data
|
||||
setStatics(dl);
|
||||
|
||||
S32 start = mShape->subShapeFirstObject[ss];
|
||||
S32 end = mShape->subShapeNumObjects[ss] + start;
|
||||
const MatrixF * saveMat = NULL;
|
||||
bool found = false;
|
||||
|
||||
F32 currMaxDP = -1e9f;
|
||||
Point3F currSupport = Point3F(0, 0, 0);
|
||||
MatrixF * previousMat = NULL;
|
||||
MatrixF mat;
|
||||
if (start<end)
|
||||
{
|
||||
Point3F va;
|
||||
|
||||
// set up for first object's node
|
||||
previousMat = mMeshObjects[start].getTransform();
|
||||
mat = *previousMat;
|
||||
mat.inverse();
|
||||
|
||||
// run through objects and collide
|
||||
for (S32 i=start; i<end; i++)
|
||||
{
|
||||
MeshObjectInstance * mesh = &mMeshObjects[i];
|
||||
|
||||
if (od >= mesh->object->numMeshes)
|
||||
continue;
|
||||
|
||||
TSMesh* physMesh = mesh->getMesh(od);
|
||||
if (physMesh && mesh->visible > 0.01f)
|
||||
{
|
||||
// collide...
|
||||
F32 saveMDP = currMaxDP;
|
||||
|
||||
if (mesh->getTransform() != previousMat)
|
||||
{
|
||||
// different node from before, set up for this node
|
||||
previousMat = mesh->getTransform();
|
||||
mat = *previousMat;
|
||||
mat.inverse();
|
||||
}
|
||||
mat.mulV(v, &va);
|
||||
physMesh->support(mesh->frame, va, &currMaxDP, &currSupport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearStatics();
|
||||
|
||||
if (currMaxDP != -1e9f)
|
||||
{
|
||||
previousMat->mulP(currSupport);
|
||||
return currSupport;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Point3F(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::computeBounds(S32 dl, Box3F & bounds)
|
||||
{
|
||||
// if dl==-1, nothing to do
|
||||
if (dl==-1)
|
||||
return;
|
||||
|
||||
AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::computeBounds");
|
||||
|
||||
// get subshape and object detail
|
||||
const TSDetail * detail = &mShape->details[dl];
|
||||
S32 ss = detail->subShapeNum;
|
||||
S32 od = detail->objectDetailNum;
|
||||
|
||||
// set up static data
|
||||
setStatics(dl);
|
||||
|
||||
S32 start = mShape->subShapeFirstObject[ss];
|
||||
S32 end = mShape->subShapeNumObjects[ss] + start;
|
||||
|
||||
// run through objects and updating bounds as we go
|
||||
bounds.min.set( 10E30f, 10E30f, 10E30f);
|
||||
bounds.max.set(-10E30f,-10E30f,-10E30f);
|
||||
Box3F box;
|
||||
for (S32 i=start; i<end; i++)
|
||||
{
|
||||
MeshObjectInstance * mesh = &mMeshObjects[i];
|
||||
|
||||
if (od >= mesh->object->numMeshes)
|
||||
continue;
|
||||
|
||||
if (mesh->getMesh(od))
|
||||
{
|
||||
mesh->getMesh(od)->computeBounds(*mesh->getTransform(),box);
|
||||
bounds.min.setMin(box.min);
|
||||
bounds.max.setMax(box.max);
|
||||
}
|
||||
}
|
||||
|
||||
clearStatics();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Object (MeshObjectInstance & PluginObjectInstance) collision methods
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
bool TSShapeInstance::ObjectInstance::buildPolyList(S32 objectDetail, AbstractPolyList * polyList, U32 & surfaceKey)
|
||||
{
|
||||
objectDetail,polyList,surfaceKey;
|
||||
AssertFatal(0,"TSShapeInstance::ObjectInstance::buildPolyList: no default method.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TSShapeInstance::ObjectInstance::getFeatures(S32 objectDetail, const MatrixF& mat, const Point3F& n, ConvexFeature* cf, U32& surfaceKey)
|
||||
{
|
||||
objectDetail,mat, n, cf, surfaceKey;
|
||||
AssertFatal(0,"TSShapeInstance::ObjectInstance::buildPolyList: no default method.");
|
||||
return false;
|
||||
}
|
||||
|
||||
void TSShapeInstance::ObjectInstance::support(S32, const Point3F&, F32*, Point3F*)
|
||||
{
|
||||
AssertFatal(0,"TSShapeInstance::ObjectInstance::supprt: no default method.");
|
||||
}
|
||||
|
||||
bool TSShapeInstance::ObjectInstance::castRay(S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo * rayInfo)
|
||||
{
|
||||
objectDetail,start,end,rayInfo;
|
||||
AssertFatal(0,"TSShapeInstance::ObjectInstance::castRay: no default method.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TSShapeInstance::MeshObjectInstance::buildPolyList(S32 objectDetail, AbstractPolyList * polyList, U32 & surfaceKey)
|
||||
{
|
||||
TSMesh * mesh = getMesh(objectDetail);
|
||||
if (mesh && visible>0.01f)
|
||||
return mesh->buildPolyList(frame,polyList,surfaceKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TSShapeInstance::MeshObjectInstance::getFeatures(S32 objectDetail, const MatrixF& mat, const Point3F& n, ConvexFeature* cf, U32& surfaceKey)
|
||||
{
|
||||
TSMesh* mesh = getMesh(objectDetail);
|
||||
if (mesh && visible > 0.01f)
|
||||
return mesh->getFeatures(frame, mat, n, cf, surfaceKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
void TSShapeInstance::MeshObjectInstance::support(S32 objectDetail, const Point3F& v, F32* currMaxDP, Point3F* currSupport)
|
||||
{
|
||||
TSMesh* mesh = getMesh(objectDetail);
|
||||
if (mesh && visible > 0.01f)
|
||||
mesh->support(frame, v, currMaxDP, currSupport);
|
||||
}
|
||||
|
||||
|
||||
bool TSShapeInstance::MeshObjectInstance::castRay(S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo * rayInfo)
|
||||
{
|
||||
TSMesh * mesh = getMesh(objectDetail);
|
||||
if (mesh && visible>0.01f)
|
||||
return mesh->castRay(frame,start,end,rayInfo);
|
||||
return false;
|
||||
}
|
248
engine/ts/tsDecal.cc
Executable file
248
engine/ts/tsDecal.cc
Executable file
@ -0,0 +1,248 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsDecal.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "core/frameAllocator.h"
|
||||
|
||||
// Not worth the effort, much less the effort to comment, but if the draw types
|
||||
// are consecutive use addition rather than a table to go from index to command value...
|
||||
#if ((GL_TRIANGLES+1==GL_TRIANGLE_STRIP) && (GL_TRIANGLE_STRIP+1==GL_TRIANGLE_FAN))
|
||||
#define getDrawType(a) (GL_TRIANGLES+(a))
|
||||
#else
|
||||
static U32 drawTypes[] = { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN };
|
||||
#define getDrawType(a) (drawTypes[a])
|
||||
#endif
|
||||
|
||||
// temp variables for saving and restoring parameters during render
|
||||
static S32 decalSaveFogMethod;
|
||||
static F32 decalSaveAlphaAlways;
|
||||
static F32 gSaveFadeVal;
|
||||
|
||||
// from tsmesh
|
||||
extern void forceFaceCamera();
|
||||
extern void forceFaceCameraZAxis();
|
||||
|
||||
void TSDecalMesh::render(S32 frame, S32 decalFrame, TSMaterialList * materials)
|
||||
{
|
||||
if (targetMesh == NULL || texgenS.empty() || texgenT.empty() || indices.empty())
|
||||
return;
|
||||
|
||||
// we need to face the camera just like our target does
|
||||
if (targetMesh->getFlags(TSMesh::Billboard))
|
||||
{
|
||||
if (targetMesh->getFlags(TSMesh::BillboardZAxis))
|
||||
forceFaceCameraZAxis();
|
||||
else
|
||||
forceFaceCamera();
|
||||
}
|
||||
|
||||
S32 firstVert = targetMesh->vertsPerFrame * frame;
|
||||
|
||||
// generate texture coords
|
||||
S32 sz = indices.size();
|
||||
Point4F s = texgenS[decalFrame];
|
||||
Point4F t = texgenT[decalFrame];
|
||||
Point3F * verts = &targetMesh->verts[firstVert];
|
||||
U32 waterMark = FrameAllocator::getWaterMark();
|
||||
F32 * ptr = (F32*)FrameAllocator::alloc(sizeof(F32)*2*targetMesh->vertsPerFrame);
|
||||
S16 * idx = (S16*)indices.address();
|
||||
S16 * idxEnd = idx + indices.size();
|
||||
for (; idx<idxEnd; idx++)
|
||||
{
|
||||
Point3F * v = &verts[*idx];
|
||||
Point2F & tv = (Point2F&)ptr[2*(*idx)];
|
||||
tv.x = v->x * s.x + v->y * s.y + v->z * s.z + s.w;
|
||||
tv.y = v->x * t.x + v->y * t.y + v->z * t.z + t.w;
|
||||
}
|
||||
|
||||
// set up vertex arrays
|
||||
glVertexPointer(3,GL_FLOAT,0,verts);
|
||||
glNormalPointer(GL_FLOAT,0,targetMesh->getNormals(firstVert));
|
||||
glTexCoordPointer(2,GL_FLOAT,0,ptr);
|
||||
|
||||
// NOTE: we don't lock these arrays since decals tend to use only a small subset of the vertices
|
||||
|
||||
// material change? We only need to do this once.
|
||||
if ( (TSShapeInstance::smRenderData.materialIndex ^ materialIndex) & (TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial))
|
||||
TSMesh::setMaterial(materialIndex,materials);
|
||||
|
||||
S32 i;
|
||||
S32 a = startPrimitive[decalFrame];
|
||||
S32 b = decalFrame+1<startPrimitive.size() ? startPrimitive[decalFrame+1] : primitives.size();
|
||||
for (i=a; i<b; i++)
|
||||
{
|
||||
TSDrawPrimitive & draw = primitives[i];
|
||||
AssertFatal((draw.matIndex & TSDrawPrimitive::Indexed)!=0,"TSMesh::render: rendering of non-indexed meshes no longer supported");
|
||||
|
||||
S32 drawType = getDrawType(draw.matIndex>>30);
|
||||
glDrawElements(drawType,draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]);
|
||||
}
|
||||
|
||||
// return frameAllocator memory
|
||||
FrameAllocator::setWaterMark(waterMark);
|
||||
}
|
||||
|
||||
void TSDecalMesh::initDecalMaterials()
|
||||
{
|
||||
TSShapeInstance::smRenderData.materialFlags = TSMaterialList::S_Wrap | TSMaterialList::T_Wrap;
|
||||
TSShapeInstance::smRenderData.materialIndex = TSDrawPrimitive::NoMaterial;
|
||||
TSShapeInstance::smRenderData.environmentMapMethod = TSShapeInstance::NO_ENVIRONMENT_MAP;
|
||||
TSShapeInstance::smRenderData.detailMapMethod = TSShapeInstance::NO_DETAIL_MAP;
|
||||
TSShapeInstance::smRenderData.lightMapMethod = TSShapeInstance::NO_LIGHT_MAP;
|
||||
TSShapeInstance::smRenderData.baseTE = 0;
|
||||
|
||||
// adjust for fading
|
||||
gSaveFadeVal = TSMesh::getOverrideFade();
|
||||
TSMesh::setOverrideFade( 1.0f );
|
||||
TSShapeInstance::smRenderData.vertexAlpha.always = gSaveFadeVal;
|
||||
// setMaterial will end up changing vertex color if needed...
|
||||
|
||||
// draw one-sided...
|
||||
glEnable(GL_CULL_FACE);
|
||||
glFrontFace(GL_CW);
|
||||
|
||||
// enable vertex arrays...
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
// bias depth values to fight z-fighting
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(-2,-2);
|
||||
|
||||
// when blending we modulate and draw using src_alpha, 1-src_alpha...
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
|
||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// but we don't blend by default...
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
// this should be off by default, but we'll end up turning it on asap...
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
// fog the decals (or rather, don't...we'll fog the whole shape later if
|
||||
// we are 2-passing it, otherwise we just want to fade the decal out)...
|
||||
decalSaveFogMethod = TSShapeInstance::smRenderData.fogMethod;
|
||||
if (decalSaveFogMethod != TSShapeInstance::NO_FOG)
|
||||
decalSaveAlphaAlways = TSShapeInstance::smRenderData.alwaysAlphaValue;
|
||||
TSShapeInstance::smRenderData.fogMethod = TSShapeInstance::FOG_TWO_PASS; // decal will be faded (since decal is translucent)
|
||||
}
|
||||
|
||||
void TSDecalMesh::resetDecalMaterials()
|
||||
{
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
TSMesh::setOverrideFade( gSaveFadeVal );
|
||||
|
||||
// fog the decals (or rather, don't...we'll fog the whole shape later if
|
||||
// we are 2-passing it, otherwise we just want to fade the decal out)...
|
||||
if (decalSaveFogMethod != TSShapeInstance::NO_FOG)
|
||||
TSShapeInstance::smRenderData.alwaysAlphaValue = decalSaveAlphaAlways;
|
||||
TSShapeInstance::smRenderData.fogMethod = decalSaveFogMethod;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
// TSDecalMesh assembly/dissembly methods
|
||||
// used for transfer to/from memory buffers
|
||||
//-----------------------------------------------------
|
||||
|
||||
#define alloc TSShape::alloc
|
||||
|
||||
void TSDecalMesh::assemble(bool)
|
||||
{
|
||||
if (TSShape::smReadVersion<20)
|
||||
{
|
||||
// read empty mesh...decals used to be derived from meshes
|
||||
alloc.checkGuard();
|
||||
alloc.getPointer32(15);
|
||||
}
|
||||
|
||||
S32 sz = alloc.get32();
|
||||
S32 * ptr32 = alloc.copyToShape32(0); // get current shape address w/o doing anything
|
||||
for (S32 i=0; i<sz; i++)
|
||||
{
|
||||
alloc.copyToShape16(2);
|
||||
alloc.copyToShape32(1);
|
||||
}
|
||||
alloc.align32();
|
||||
primitives.set(ptr32,sz);
|
||||
|
||||
sz = alloc.get32();
|
||||
S16 * ptr16 = alloc.copyToShape16(sz);
|
||||
alloc.align32();
|
||||
indices.set(ptr16,sz);
|
||||
|
||||
if (TSShape::smReadVersion<20)
|
||||
{
|
||||
// read more empty mesh stuff...decals used to be derived from meshes
|
||||
alloc.getPointer32(3);
|
||||
alloc.checkGuard();
|
||||
}
|
||||
|
||||
sz = alloc.get32();
|
||||
ptr32 = alloc.copyToShape32(sz);
|
||||
startPrimitive.set(ptr32,sz);
|
||||
|
||||
if (TSShape::smReadVersion>=19)
|
||||
{
|
||||
ptr32 = alloc.copyToShape32(sz*4);
|
||||
texgenS.set(ptr32,startPrimitive.size());
|
||||
ptr32 = alloc.copyToShape32(sz*4);
|
||||
texgenT.set(ptr32,startPrimitive.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
texgenS.set(NULL,0);
|
||||
texgenT.set(NULL,0);
|
||||
}
|
||||
|
||||
materialIndex = alloc.get32();
|
||||
|
||||
alloc.checkGuard();
|
||||
}
|
||||
|
||||
void TSDecalMesh::disassemble()
|
||||
{
|
||||
alloc.set32(primitives.size());
|
||||
for (S32 i=0; i<primitives.size(); i++)
|
||||
{
|
||||
alloc.copyToBuffer16((S16*)&primitives[i],2);
|
||||
alloc.copyToBuffer32(((S32*)&primitives[i])+1,1);
|
||||
}
|
||||
|
||||
alloc.set32(indices.size());
|
||||
alloc.copyToBuffer16((S16*)indices.address(),indices.size());
|
||||
|
||||
alloc.set32(startPrimitive.size());
|
||||
alloc.copyToBuffer32((S32*)startPrimitive.address(),startPrimitive.size());
|
||||
|
||||
alloc.copyToBuffer32((S32*)texgenS.address(),texgenS.size()*4);
|
||||
alloc.copyToBuffer32((S32*)texgenT.address(),texgenT.size()*4);
|
||||
|
||||
alloc.set32(materialIndex);
|
||||
|
||||
alloc.setGuard();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
50
engine/ts/tsDecal.h
Executable file
50
engine/ts/tsDecal.h
Executable file
@ -0,0 +1,50 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSDECAL_H_
|
||||
#define _TSDECAL_H_
|
||||
|
||||
#ifndef _TSMESH_H_
|
||||
#include "ts/tsMesh.h"
|
||||
#endif
|
||||
|
||||
/// Decals! The lovely detailing thingies, e.g. bullet hole marks.
|
||||
class TSDecalMesh
|
||||
{
|
||||
public:
|
||||
|
||||
/// The mesh that we are decaling
|
||||
TSMesh * targetMesh;
|
||||
|
||||
/// @name Topology
|
||||
/// @{
|
||||
ToolVector<TSDrawPrimitive> primitives;
|
||||
ToolVector<U16> indices;
|
||||
/// @}
|
||||
|
||||
/// @name Render Data
|
||||
/// indexed by decal frame...
|
||||
/// @{
|
||||
ToolVector<S32> startPrimitive;
|
||||
ToolVector<Point4F> texgenS;
|
||||
ToolVector<Point4F> texgenT;
|
||||
/// @}
|
||||
|
||||
/// We only allow 1 material per decal...
|
||||
S32 materialIndex;
|
||||
|
||||
/// override render function
|
||||
void render(S32 frame, S32 decalFrame, TSMaterialList *);
|
||||
|
||||
void disassemble();
|
||||
void assemble(bool skip);
|
||||
|
||||
static void initDecalMaterials();
|
||||
static void resetDecalMaterials();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
231
engine/ts/tsDump.cc
Executable file
231
engine/ts/tsDump.cc
Executable file
@ -0,0 +1,231 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsShapeInstance.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Dump shape structure:
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
#define dumpLine(buffer) {str = buffer; stream.write((int)dStrlen(str),str);}
|
||||
|
||||
void TSShapeInstance::dumpDecals(Stream & stream, S32 indent, MeshObjectInstance * mesh)
|
||||
{
|
||||
// search for any decals
|
||||
const char * str;
|
||||
for (S32 i=0; i<mDecalObjects.size(); i++)
|
||||
{
|
||||
DecalObjectInstance * decal = &mDecalObjects[i];
|
||||
if (decal->targetObject != mesh)
|
||||
continue;
|
||||
|
||||
// we have a decal
|
||||
|
||||
// get name
|
||||
const char *decalName = "";
|
||||
if (decal->decalObject->nameIndex!=-1)
|
||||
decalName = mShape->getName(decal->decalObject->nameIndex);
|
||||
|
||||
// indent...
|
||||
char buf[1024];
|
||||
dMemset(buf,' ',indent);
|
||||
buf[indent] = '\0';
|
||||
dumpLine(buf);
|
||||
|
||||
// dump object name
|
||||
dumpLine(avar(" Decal named \"%s\" on above object.\r\n",decalName));
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::dumpNode(Stream & stream ,S32 level, S32 nodeIndex, Vector<S32> & detailSizes)
|
||||
{
|
||||
if (nodeIndex<0)
|
||||
return;
|
||||
|
||||
S32 i;
|
||||
const char * str;
|
||||
char space[256];
|
||||
for (i = 0; i < level*3; i++)
|
||||
space[i] = ' ';
|
||||
space[level*3] = '\0';
|
||||
|
||||
const char *nodeName = "";
|
||||
const TSShape::Node & node = mShape->nodes[nodeIndex];
|
||||
if (node.nameIndex != -1)
|
||||
nodeName = mShape->getName(node.nameIndex);
|
||||
dumpLine(avar("%s%s", space, nodeName));
|
||||
|
||||
// find all the objects that hang off this node...
|
||||
Vector<ObjectInstance*> objectList;
|
||||
for (i=0; i<mMeshObjects.size(); i++)
|
||||
if (mMeshObjects[i].nodeIndex == nodeIndex)
|
||||
objectList.push_back(&mMeshObjects[i]);
|
||||
|
||||
if (objectList.size() == 0)
|
||||
dumpLine("\r\n");
|
||||
|
||||
S32 spaceCount = -1;
|
||||
for (S32 j=0;j<objectList.size(); j++)
|
||||
{
|
||||
// should be a dynamic cast, but MSVC++ has problems with this...
|
||||
MeshObjectInstance * obj = (MeshObjectInstance *)(objectList[j]);
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
// object name
|
||||
const char *objectName = "";
|
||||
if (obj->object->nameIndex!=-1)
|
||||
objectName = mShape->getName(obj->object->nameIndex);
|
||||
|
||||
// more spaces if this is the second object on this node
|
||||
if (spaceCount>0)
|
||||
{
|
||||
char buf[1024];
|
||||
dMemset(buf,' ',spaceCount);
|
||||
buf[spaceCount] = '\0';
|
||||
dumpLine(buf);
|
||||
}
|
||||
|
||||
// dump object name
|
||||
dumpLine(avar(" --> Object %s with following details: ",objectName));
|
||||
|
||||
// dump object detail levels
|
||||
for (S32 k=0; k<obj->object->numMeshes; k++)
|
||||
{
|
||||
S32 f = obj->object->startMeshIndex;
|
||||
if (mShape->meshes[f+k])
|
||||
dumpLine(avar(" %i",detailSizes[k]));
|
||||
}
|
||||
|
||||
dumpLine("\r\n");
|
||||
|
||||
// how many spaces should we prepend if we have another object on this node
|
||||
if (spaceCount<0)
|
||||
spaceCount = (S32)(dStrlen(space) + dStrlen(nodeName));
|
||||
|
||||
// dump any decals
|
||||
dumpDecals(stream, spaceCount+3, obj);
|
||||
}
|
||||
|
||||
// search for children
|
||||
for (S32 k=nodeIndex+1; k<mShape->nodes.size(); k++)
|
||||
{
|
||||
if (mShape->nodes[k].parentIndex == nodeIndex)
|
||||
// this is our child
|
||||
dumpNode(stream, level+1, k, detailSizes);
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::dump(Stream & stream)
|
||||
{
|
||||
S32 i,j,ss,od,sz;
|
||||
const char * name;
|
||||
const char * str;
|
||||
|
||||
dumpLine("\r\nShape Hierarchy:\r\n");
|
||||
|
||||
dumpLine("\r\n Details:\r\n");
|
||||
|
||||
for (i=0; i<mShape->details.size(); i++)
|
||||
{
|
||||
const TSDetail & detail = mShape->details[i];
|
||||
name = mShape->getName(detail.nameIndex);
|
||||
ss = detail.subShapeNum;
|
||||
od = detail.objectDetailNum;
|
||||
sz = (S32)detail.size;
|
||||
dumpLine(avar(" %s, Subtree %i, objectDetail %i, size %i\r\n",name,ss,od,sz));
|
||||
}
|
||||
|
||||
dumpLine("\r\n Subtrees:\r\n");
|
||||
|
||||
for (i=0; i<mShape->subShapeFirstNode.size(); i++)
|
||||
{
|
||||
S32 a = mShape->subShapeFirstNode[i];
|
||||
S32 b = a + mShape->subShapeNumNodes[i];
|
||||
dumpLine(avar(" Subtree %i\r\n",i));
|
||||
|
||||
// compute detail sizes for each subshape
|
||||
Vector<S32> detailSizes;
|
||||
for (S32 l=0;l<mShape->details.size(); l++)
|
||||
{
|
||||
if (mShape->details[l].subShapeNum==i)
|
||||
detailSizes.push_back((S32)mShape->details[l].size);
|
||||
}
|
||||
|
||||
for (j=a; j<b; j++)
|
||||
{
|
||||
const TSNode & node = mShape->nodes[j];
|
||||
// if the node has a parent, it'll get dumped via the parent
|
||||
if (node.parentIndex<0)
|
||||
dumpNode(stream,3,j,detailSizes);
|
||||
}
|
||||
}
|
||||
|
||||
bool foundSkin = false;
|
||||
for (i=0; i<mShape->objects.size(); i++)
|
||||
{
|
||||
if (mShape->objects[i].nodeIndex<0) // must be a skin
|
||||
{
|
||||
if (!foundSkin)
|
||||
dumpLine("\r\n Skins:\r\n");
|
||||
foundSkin=true;
|
||||
const char * skinName = "";
|
||||
S32 nameIndex = mShape->objects[i].nameIndex;
|
||||
if (nameIndex>=0)
|
||||
skinName = mShape->getName(nameIndex);
|
||||
dumpLine(avar(" Skin %s with following details: ",skinName));
|
||||
for (S32 num=0; num<mShape->objects[i].numMeshes; num++)
|
||||
{
|
||||
if (mShape->meshes[num])
|
||||
dumpLine(avar(" %i",(S32)mShape->details[num].size));
|
||||
}
|
||||
dumpLine("\r\n");
|
||||
}
|
||||
}
|
||||
if (foundSkin)
|
||||
dumpLine("\r\n");
|
||||
|
||||
dumpLine("\r\n Sequences:\r\n");
|
||||
for (i = 0; i < mShape->sequences.size(); i++)
|
||||
{
|
||||
const char *name = "(none)";
|
||||
if (mShape->sequences[i].nameIndex != -1)
|
||||
name = mShape->getName(mShape->sequences[i].nameIndex);
|
||||
dumpLine(avar(" %3d: %s\r\n",i,name));
|
||||
}
|
||||
|
||||
if (mShape->materialList)
|
||||
{
|
||||
TSMaterialList * ml = mShape->materialList;
|
||||
dumpLine("\r\n Material list:\r\n");
|
||||
for (i=0; i<(S32)ml->getMaterialCount(); i++)
|
||||
{
|
||||
U32 flags = ml->getFlags(i);
|
||||
const char * name = ml->getMaterialName(i);
|
||||
dumpLine(avar(" material #%i: \"%s\"%s.",i,name ? ml->getMaterialName(i) : "",flags&(TSMaterialList::S_Wrap|TSMaterialList::T_Wrap) ? "" : " not tiled"));
|
||||
if (flags & TSMaterialList::IflMaterial)
|
||||
dumpLine(" Place holder for ifl.");
|
||||
if (flags & TSMaterialList::IflFrame)
|
||||
dumpLine(" Ifl frame.");
|
||||
if (flags & TSMaterialList::DetailMapOnly)
|
||||
dumpLine(" Used as a detail map.");
|
||||
if (flags & TSMaterialList::BumpMapOnly)
|
||||
dumpLine(" Used as a bump map.");
|
||||
if (flags & TSMaterialList::ReflectanceMapOnly)
|
||||
dumpLine(" Used as a reflectance map.");
|
||||
if (flags & TSMaterialList::Translucent)
|
||||
{
|
||||
if (flags & TSMaterialList::Additive)
|
||||
dumpLine(" Additive-translucent.")
|
||||
else if (flags & TSMaterialList::Subtractive)
|
||||
dumpLine(" Subtractive-translucent.")
|
||||
else
|
||||
dumpLine(" Translucent.")
|
||||
}
|
||||
dumpLine("\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
220
engine/ts/tsIntegerSet.cc
Executable file
220
engine/ts/tsIntegerSet.cc
Executable file
@ -0,0 +1,220 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsIntegerSet.h"
|
||||
#include "platform/platform.h"
|
||||
#include "core/stream.h"
|
||||
|
||||
#define SETUPTO(upto) ( ((1<<(upto&31))-1)*2+1 ) // careful not to shift more than 31 times
|
||||
|
||||
void TSIntegerSet::clearAll(S32 upto)
|
||||
{
|
||||
AssertFatal(upto<=MAX_TS_SET_SIZE,"TSIntegerSet::clearAll: out of range");
|
||||
|
||||
dMemset(bits,0,(upto>>5)*4);
|
||||
if (upto&31)
|
||||
bits[upto>>5] &= ~SETUPTO(upto);
|
||||
}
|
||||
|
||||
void TSIntegerSet::setAll(S32 upto)
|
||||
{
|
||||
AssertFatal(upto<=MAX_TS_SET_SIZE,"TSIntegerSet::setAll: out of range");
|
||||
|
||||
dMemset(bits,0xFFFFFFFF,(upto>>5)*4);
|
||||
if (upto&31)
|
||||
bits[upto>>5] |= SETUPTO(upto);
|
||||
}
|
||||
|
||||
bool TSIntegerSet::testAll(S32 upto) const
|
||||
{
|
||||
AssertFatal(upto<=MAX_TS_SET_SIZE,"TSIntegerSet::testAll: out of range");
|
||||
S32 i;
|
||||
for (i=0; i<(upto>>5); i++)
|
||||
if (bits[i])
|
||||
return true;
|
||||
if (upto&31)
|
||||
return (bits[upto>>5] & SETUPTO(upto)) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
void TSIntegerSet::intersect(const TSIntegerSet & otherSet)
|
||||
{
|
||||
for (S32 i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
bits[i] &= otherSet.bits[i];
|
||||
}
|
||||
|
||||
void TSIntegerSet::overlap(const TSIntegerSet & otherSet)
|
||||
{
|
||||
for (S32 i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
bits[i] |= otherSet.bits[i];
|
||||
}
|
||||
|
||||
void TSIntegerSet::difference(const TSIntegerSet & otherSet)
|
||||
{
|
||||
for (S32 i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
bits[i] = (bits[i] | otherSet.bits[i]) & ~(bits[i] & otherSet.bits[i]);
|
||||
}
|
||||
|
||||
void TSIntegerSet::takeAway(const TSIntegerSet & otherSet)
|
||||
{
|
||||
for (S32 i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
bits[i] &= ~otherSet.bits[i];
|
||||
}
|
||||
|
||||
S32 TSIntegerSet::start() const
|
||||
{
|
||||
for (S32 i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
{
|
||||
// search for set bit one dword at a time
|
||||
U32 dword = bits[i];
|
||||
if (dword!=0)
|
||||
{
|
||||
// got dword, now search one byte at a time
|
||||
S32 j = 0;
|
||||
U32 mask = 0xFF;
|
||||
do
|
||||
{
|
||||
if (dword&mask)
|
||||
{
|
||||
// got byte, now search one bit at a time
|
||||
U32 bit = mask & ~(mask<<1); // grabs the smallest bit
|
||||
do
|
||||
{
|
||||
if (dword&bit)
|
||||
return (i<<5)+j;
|
||||
j++;
|
||||
bit <<= 1;
|
||||
} while (1);
|
||||
}
|
||||
mask <<= 8;
|
||||
j += 8;
|
||||
} while (1);
|
||||
}
|
||||
}
|
||||
|
||||
return MAX_TS_SET_SIZE;
|
||||
}
|
||||
|
||||
S32 TSIntegerSet::end() const
|
||||
{
|
||||
for (S32 i=MAX_TS_SET_DWORDS-1; i>=0; i--)
|
||||
{
|
||||
// search for set bit one dword at a time
|
||||
U32 dword = bits[i];
|
||||
if (bits[i])
|
||||
{
|
||||
// got dword, now search one byte at a time
|
||||
S32 j = 31;
|
||||
U32 mask = 0xFF000000;
|
||||
do
|
||||
{
|
||||
if (dword&mask)
|
||||
{
|
||||
// got byte, now one bit at a time
|
||||
U32 bit = mask & ~(mask>>1); // grabs the highest bit
|
||||
do
|
||||
{
|
||||
if (dword&bit)
|
||||
return (i<<5)+j+1;
|
||||
j--;
|
||||
bit >>= 1;
|
||||
} while (1);
|
||||
}
|
||||
mask >>= 8;
|
||||
j -= 8;
|
||||
} while (1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TSIntegerSet::next(S32 & i) const
|
||||
{
|
||||
i++;
|
||||
U32 idx = i>>5;
|
||||
U32 bit = 1 << (i&31);
|
||||
U32 dword = bits[idx] & ~(bit-1);
|
||||
while (dword==0)
|
||||
{
|
||||
i = (i+32) & ~31;
|
||||
if (i>=MAX_TS_SET_SIZE)
|
||||
return;
|
||||
dword=bits[++idx];
|
||||
bit = 1;
|
||||
}
|
||||
dword = bits[idx];
|
||||
while ( (bit & dword) == 0)
|
||||
{
|
||||
bit <<= 1;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Or would one byte at a time be better...
|
||||
void TSIntegerSet::next(S32 & i)
|
||||
{
|
||||
U32 idx = i>>3;
|
||||
U8 bit = 1 << (i&7);
|
||||
U8 byte = ((U8*)bits)[idx] & ~(bit*2-1);
|
||||
while (byte==0)
|
||||
{
|
||||
i = (i+8) & ~7;
|
||||
if (i>=MAX_TS_SET_SIZE)
|
||||
return;
|
||||
byte=((U8*)bits)[++idx];
|
||||
bit = 1;
|
||||
}
|
||||
byte = ((U8*)bits)[idx];
|
||||
while (bit & byte == 0)
|
||||
{
|
||||
bit <<= 1;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void TSIntegerSet::copy(const TSIntegerSet & otherSet)
|
||||
{
|
||||
dMemcpy(bits,otherSet.bits,MAX_TS_SET_DWORDS*sizeof(U32));
|
||||
}
|
||||
|
||||
TSIntegerSet::TSIntegerSet()
|
||||
{
|
||||
clearAll();
|
||||
}
|
||||
|
||||
TSIntegerSet::TSIntegerSet(const TSIntegerSet & otherSet)
|
||||
{
|
||||
copy(otherSet);
|
||||
}
|
||||
|
||||
void TSIntegerSet::read(Stream * s)
|
||||
{
|
||||
clearAll();
|
||||
|
||||
S32 numInts;
|
||||
s->read(&numInts); // don't care about this
|
||||
|
||||
S32 sz;
|
||||
s->read(&sz);
|
||||
AssertFatal(sz<=MAX_TS_SET_DWORDS,"TSIntegerSet:: set too large...increase max set size and re-compile");
|
||||
|
||||
for (S32 i=0; i<sz; i++) // now mirrors the write code...
|
||||
s->read(&(bits[i]));
|
||||
}
|
||||
|
||||
void TSIntegerSet::write(Stream * s)
|
||||
{
|
||||
s->write((S32)0); // don't do this anymore, keep in to avoid versioning
|
||||
S32 i,sz=0;
|
||||
for (i=0; i<MAX_TS_SET_DWORDS; i++)
|
||||
if (bits[i]!=0)
|
||||
sz=i+1;
|
||||
s->write(sz);
|
||||
for (i=0; i<sz; i++)
|
||||
s->write(bits[i]);
|
||||
}
|
||||
|
96
engine/ts/tsIntegerSet.h
Executable file
96
engine/ts/tsIntegerSet.h
Executable file
@ -0,0 +1,96 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSINTEGERSET_H_
|
||||
#define _TSINTEGERSET_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(TORQUE_LIB)
|
||||
#define MAX_TS_SET_DWORDS 32
|
||||
#else
|
||||
#define MAX_TS_SET_DWORDS 6
|
||||
#endif
|
||||
|
||||
#define MAX_TS_SET_SIZE (32*MAX_TS_SET_DWORDS)
|
||||
|
||||
class Stream;
|
||||
|
||||
/// The standard mathmatical set, where there are no duplicates. However,
|
||||
/// this set uses bits instead of numbers.
|
||||
class TSIntegerSet
|
||||
{
|
||||
/// The bits!
|
||||
U32 bits[MAX_TS_SET_DWORDS];
|
||||
|
||||
public:
|
||||
|
||||
/// Sets this bit to false
|
||||
void clear(S32 index);
|
||||
/// Set this bit to true
|
||||
void set(S32 index);
|
||||
/// Is this bit true?
|
||||
bool test(S32 index) const;
|
||||
|
||||
/// Sets all bits to false
|
||||
void clearAll(S32 upto = MAX_TS_SET_SIZE);
|
||||
/// Sets all bits to true
|
||||
void setAll(S32 upto = MAX_TS_SET_SIZE);
|
||||
/// Tests all bits for true
|
||||
bool testAll(S32 upto = MAX_TS_SET_SIZE) const;
|
||||
|
||||
/// intersection (a & b)
|
||||
void intersect(const TSIntegerSet&);
|
||||
/// union (a | b)
|
||||
void overlap(const TSIntegerSet&);
|
||||
/// xor (a | b) & ( !(a & b) )
|
||||
void difference(const TSIntegerSet&);
|
||||
/// subtraction (a - b)
|
||||
void takeAway(const TSIntegerSet&);
|
||||
|
||||
/// copy one integer set into another
|
||||
void copy(const TSIntegerSet&);
|
||||
|
||||
void operator=(const TSIntegerSet& otherSet) { copy(otherSet); }
|
||||
|
||||
S32 start() const;
|
||||
S32 end() const;
|
||||
void next(S32 & i) const;
|
||||
|
||||
void read(Stream *);
|
||||
void write(Stream *);
|
||||
|
||||
TSIntegerSet();
|
||||
TSIntegerSet(const TSIntegerSet&);
|
||||
};
|
||||
|
||||
inline void TSIntegerSet::clear(S32 index)
|
||||
{
|
||||
AssertFatal(index>=0 && index<MAX_TS_SET_SIZE,"TS::IntegerSet::clear");
|
||||
|
||||
bits[index>>5] &= ~(1 << (index & 31));
|
||||
}
|
||||
|
||||
inline void TSIntegerSet::set(S32 index)
|
||||
{
|
||||
AssertFatal(index>=0 && index<MAX_TS_SET_SIZE,"TS::IntegerSet::set");
|
||||
|
||||
bits[index>>5] |= 1 << (index & 31);
|
||||
}
|
||||
|
||||
inline bool TSIntegerSet::test(S32 index) const
|
||||
{
|
||||
AssertFatal(index>=0 && index<MAX_TS_SET_SIZE,"TS::IntegerSet::test");
|
||||
|
||||
return ((bits[index>>5] & (1 << (index & 31)))!=0);
|
||||
}
|
||||
|
||||
#endif
|
381
engine/ts/tsLastDetail.cc
Executable file
381
engine/ts/tsLastDetail.cc
Executable file
@ -0,0 +1,381 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsLastDetail.h"
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "ts/tsShape.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
|
||||
bool TSLastDetail::smDirtyMode = false;
|
||||
|
||||
Point2F TSLastDetail::smTVerts[4] = { Point2F(0,1), Point2F(1,1), Point2F(1,0), Point2F(0,0) };
|
||||
Point3F TSLastDetail::smNorms[4] = { Point3F(0,-1,0), Point3F(0,-1,0), Point3F(0,-1,0), Point3F(0,-1,0) };
|
||||
|
||||
TSLastDetail::TSLastDetail(TSShapeInstance * shape,
|
||||
U32 numEquatorSteps,
|
||||
U32 numPolarSteps,
|
||||
F32 polarAngle,
|
||||
bool includePoles,
|
||||
S32 dl, S32 dim)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mBitmaps);
|
||||
VECTOR_SET_ASSOCIATION(mTextures);
|
||||
|
||||
mNumEquatorSteps = numEquatorSteps;
|
||||
mNumPolarSteps = numPolarSteps;
|
||||
mPolarAngle = polarAngle;
|
||||
mIncludePoles = includePoles;
|
||||
|
||||
F32 equatorStepSize = M_2PI_F / (F32) numEquatorSteps;
|
||||
F32 polarStepSize = numPolarSteps>0 ? (0.5f * M_PI_F - polarAngle) / (F32)numPolarSteps : 0.0f;
|
||||
|
||||
U32 i;
|
||||
F32 rotZ = 0;
|
||||
for (i=0; i<numEquatorSteps; i++)
|
||||
{
|
||||
F32 rotX = numPolarSteps>0 ? polarAngle - 0.5f * M_PI_F : 0.0f;
|
||||
for (U32 j=0; j<2*numPolarSteps+1; j++)
|
||||
{
|
||||
MatrixF angMat;
|
||||
angMat.mul(MatrixF(EulerF(0,0,-M_PI_F+rotZ)),MatrixF(EulerF(rotX,0,0)));
|
||||
mBitmaps.push_back(shape->snapshot(dim,dim,true,angMat,dl,1.0f,true));
|
||||
rotX += polarStepSize;
|
||||
}
|
||||
rotZ += equatorStepSize;
|
||||
}
|
||||
|
||||
if (includePoles)
|
||||
{
|
||||
MatrixF m1( EulerF( M_PI_F / 2.0f, 0, 0 ) );
|
||||
MatrixF m2( EulerF( -M_PI_F / 2.0f, 0, 0 ) );
|
||||
mBitmaps.push_back(shape->snapshot(dim,dim,true,m1,dl,1.0f,true));
|
||||
mBitmaps.push_back(shape->snapshot(dim,dim,true,m2,dl,1.0f,true));
|
||||
}
|
||||
|
||||
mTextures.setSize(mBitmaps.size());
|
||||
for (i=0; i<(U32)mBitmaps.size(); i++)
|
||||
{
|
||||
if (mBitmaps[i]) // snapshot routine may refuse to give us a bitmap sometimes...
|
||||
mTextures[i] = new TextureHandle(NULL,mBitmaps[i],true);
|
||||
else
|
||||
mTextures[i] = NULL;
|
||||
}
|
||||
|
||||
mPoints[0].set(-shape->mShape->radius,0, shape->mShape->radius);
|
||||
mPoints[1].set( shape->mShape->radius,0, shape->mShape->radius);
|
||||
mPoints[2].set( shape->mShape->radius,0,-shape->mShape->radius);
|
||||
mPoints[3].set(-shape->mShape->radius,0,-shape->mShape->radius);
|
||||
|
||||
mCenter = shape->mShape->center;
|
||||
}
|
||||
|
||||
TSLastDetail::~TSLastDetail()
|
||||
{
|
||||
for (S32 i=0; i<mTextures.size(); i++) {
|
||||
delete mTextures[i];
|
||||
mTextures[i] = NULL;
|
||||
}
|
||||
|
||||
// mBitmaps...owned by mTextures
|
||||
}
|
||||
|
||||
void TSLastDetail::chooseView(const MatrixF & mat, const Point3F & scale)
|
||||
{
|
||||
scale; // ignore for now
|
||||
|
||||
// Following is called often enough that it's worth trying to optimize
|
||||
/*
|
||||
Point3F x,y,z,pos;
|
||||
mat.getColumn(0,&x);
|
||||
mat.getColumn(1,&y);
|
||||
mat.getColumn(2,&z);
|
||||
mat.getColumn(3,&pos);
|
||||
|
||||
F32 dotX = mDot(x,pos);
|
||||
F32 dotY = mDot(y,pos);
|
||||
F32 dotZ = mDot(z,pos);
|
||||
*/
|
||||
|
||||
const F32 * m = (const F32*)&mat;
|
||||
F32 dotX = m[3] * m[0];
|
||||
F32 dotY = m[3] * m[1];
|
||||
F32 dotZ = m[3] * m[2];
|
||||
dotX += m[7] * m[4];
|
||||
dotY += m[7] * m[5];
|
||||
dotZ += m[7] * m[6];
|
||||
dotX += m[11] * m[8];
|
||||
dotY += m[11] * m[9];
|
||||
dotZ += m[11] * m[10];
|
||||
|
||||
// which bmp to choose?
|
||||
F32 rotX = mIncludePoles || mNumPolarSteps ? mAcos(dotZ/mSqrt(dotX*dotX+dotY*dotY+dotZ*dotZ)) : 0.0f;
|
||||
F32 rotZ = 0.0f;
|
||||
AssertFatal(rotX>=0 && rotX<M_2PI,"TSLastDetail::chooseView: rotX out of range");
|
||||
if (mIncludePoles && (rotX<mPolarAngle || rotX>M_PI-mPolarAngle))
|
||||
{
|
||||
mBitmapIndex = mNumEquatorSteps * (2*mNumPolarSteps+1);
|
||||
if (rotX>mPolarAngle)
|
||||
mBitmapIndex++;
|
||||
// mRotY = mAtan(y.y,x.y);
|
||||
mRotY = mAtan(m[5],m[4]);
|
||||
}
|
||||
else
|
||||
{
|
||||
F32 equatorStepSize = M_2PI_F / (F32) mNumEquatorSteps;
|
||||
F32 polarStepSize = mNumPolarSteps>0 ? (0.5f * M_PI_F - mPolarAngle) / (F32) mNumPolarSteps : 0.0f;
|
||||
rotZ = 0.999f * (mAtan(dotX,dotY) + M_PI_F); // the 0.99f makes sure we are in range
|
||||
AssertFatal(rotZ>=0 && rotZ<M_2PI_F,"TSLastDetail::chooseView: rotZ out of range");
|
||||
mBitmapIndex = ((S32)(rotZ/equatorStepSize)) * (S32)(2*mNumPolarSteps+1);
|
||||
if (mNumPolarSteps>0)
|
||||
mBitmapIndex = (U32)(mBitmapIndex + ((rotX-mPolarAngle) / polarStepSize));
|
||||
// mRotY = mAtan(z.x,z.z);
|
||||
mRotY = mAtan(m[2],m[10]);
|
||||
}
|
||||
|
||||
// make sure we don<6F>t get invalid bitmap index!
|
||||
mBitmapIndex = mClamp(mBitmapIndex, 0, mTextures.size()-1);
|
||||
}
|
||||
|
||||
void TSLastDetail::render(F32 alpha, bool drawFog)
|
||||
{
|
||||
glPushMatrix();
|
||||
|
||||
// get camera matrix, adjust for shape center
|
||||
MatrixF mat;
|
||||
Point3F p,center;
|
||||
dglGetModelview(&mat);
|
||||
mat.getColumn(3,&p);
|
||||
mat.mulV(mCenter,¢er);
|
||||
p += center;
|
||||
mat.setColumn(3,p);
|
||||
|
||||
Point3F ones( 1, 1, 1 );
|
||||
chooseView(mat,ones);
|
||||
|
||||
// following is a quicker version of mat.set(EulerF(0,rotY,0));
|
||||
// note: we assume mat[12]=1 and mat[3]=mat[7]=mat[11]=0 to start with
|
||||
F32 * m = (F32*)mat; // because [] operator isn't implemented on MatrixF, so it finds mat[0] ambiguous (const)
|
||||
AssertFatal(mFabs(m[15]-1.0f)<0.01f && mFabs(m[14])<0.01f && mFabs(m[13])<0.01f && mFabs(m[12])<0.01f,"TSLastDetail::render");
|
||||
if (mRotY*mRotY>0.0001f)
|
||||
{
|
||||
m[0] = m[10] = mCos(mRotY);
|
||||
m[2] = mSin(mRotY); m[8] = -m[2];
|
||||
m[1] = m[4] = m[6] = m[9] = 0.0f;
|
||||
m[5] = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
m[0] = m[5] = m[10] = 1.0f;
|
||||
m[1] = m[2] = m[4] = m[6] = m[8] = m[9] = 0.0f;
|
||||
}
|
||||
|
||||
dglLoadMatrix(&mat);
|
||||
if (TSShapeInstance::smRenderData.objectScale)
|
||||
glScalef(
|
||||
TSShapeInstance::smRenderData.objectScale->x,
|
||||
TSShapeInstance::smRenderData.objectScale->y,
|
||||
TSShapeInstance::smRenderData.objectScale->z
|
||||
);
|
||||
|
||||
if (!drawFog)
|
||||
renderNoFog(alpha);
|
||||
else
|
||||
{
|
||||
|
||||
if (dglDoesSupportTextureEnvCombine() && dglDoesSupportARBMultitexture())
|
||||
renderFog_MultiCombine(alpha);
|
||||
// else if (dglDoesSupportTextureEnvCombine())
|
||||
// renderFog_Combine(alpha);
|
||||
else
|
||||
renderNoFog(alpha * TSShapeInstance::smRenderData.fogColor.w);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void TSLastDetail::renderNoFog(F32 alpha)
|
||||
{
|
||||
// set up arrays
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glVertexPointer (3, GL_FLOAT, 0, mPoints);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, smTVerts);
|
||||
glNormalPointer ( GL_FLOAT, 0, smNorms);
|
||||
|
||||
// light the material
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
|
||||
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,Point4F(1,1,1,alpha));
|
||||
|
||||
// additive transparency
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// texture
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
if (TSShapeInstance::smRenderData.useOverride == false)
|
||||
glBindTexture(GL_TEXTURE_2D, mTextures[mBitmapIndex]->getGLName());
|
||||
else
|
||||
glBindTexture(GL_TEXTURE_2D, TSShapeInstance::smRenderData.override.getGLName());
|
||||
|
||||
// set color state
|
||||
glColor4f(1,1,1,1);
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN,0,4);
|
||||
|
||||
if(!smDirtyMode)
|
||||
{
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TSLastDetail::renderFog_Combine(F32 alpha)
|
||||
{
|
||||
// set up arrays
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glVertexPointer (3, GL_FLOAT, 0, mPoints);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, smTVerts);
|
||||
glNormalPointer ( GL_FLOAT, 0, smNorms);
|
||||
|
||||
// light the material
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
|
||||
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,Point4F(1,1,1,alpha));
|
||||
glColor4f(1,1,1,1);
|
||||
|
||||
// additive transparency
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// texture
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
if (TSShapeInstance::smRenderData.useOverride == false)
|
||||
glBindTexture(GL_TEXTURE_2D, mTextures[mBitmapIndex]->getGLName());
|
||||
else
|
||||
glBindTexture(GL_TEXTURE_2D, TSShapeInstance::smRenderData.override.getGLName());
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN,0,4);
|
||||
|
||||
// now render the fog
|
||||
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
GLboolean wasLit = glIsEnabled(GL_LIGHTING);
|
||||
glDisable(GL_LIGHTING);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
glColor4fv(TSShapeInstance::smRenderData.fogColor);
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
|
||||
// color comes from constant color...
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE);
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_PRIMARY_COLOR);
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR);
|
||||
// alpha is product of texture and constant alpha...
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA,GL_MODULATE);
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA,GL_PRIMARY_COLOR);
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA,GL_SRC_ALPHA);
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_ALPHA,GL_TEXTURE);
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_ALPHA,GL_SRC_ALPHA);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN,0,4);
|
||||
|
||||
// restore state...
|
||||
if (wasLit)
|
||||
glEnable(GL_LIGHTING);
|
||||
|
||||
if(!smDirtyMode)
|
||||
{
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
void TSLastDetail::renderFog_MultiCombine(F32 alpha)
|
||||
{
|
||||
// set up materials
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glVertexPointer (3,GL_FLOAT, 0, mPoints);
|
||||
glTexCoordPointer(2,GL_FLOAT, 0, smTVerts);
|
||||
glNormalPointer ( GL_FLOAT, 0, smNorms);
|
||||
|
||||
// set color state
|
||||
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,Point4F(1,1,1,alpha));
|
||||
glColor4f(1,1,1,1);
|
||||
|
||||
// first TE applies lighting to billboard texture
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
if (TSShapeInstance::smRenderData.useOverride == false)
|
||||
glBindTexture(GL_TEXTURE_2D, mTextures[mBitmapIndex]->getGLName());
|
||||
else
|
||||
glBindTexture(GL_TEXTURE_2D, TSShapeInstance::smRenderData.override.getGLName());
|
||||
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
|
||||
|
||||
// second TE applies fog to the result
|
||||
glActiveTextureARB(GL_TEXTURE1_ARB);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR, TSShapeInstance::smRenderData.fogColor);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
|
||||
|
||||
// additive transparency
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
// render billboard+fog
|
||||
glDrawArrays(GL_TRIANGLE_FAN,0,4);
|
||||
|
||||
// restore state...
|
||||
if(!smDirtyMode)
|
||||
{
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
glActiveTextureARB(GL_TEXTURE0_ARB);
|
||||
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
else
|
||||
{
|
||||
glActiveTextureARB(GL_TEXTURE0_ARB);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
72
engine/ts/tsLastDetail.h
Executable file
72
engine/ts/tsLastDetail.h
Executable file
@ -0,0 +1,72 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSLASTDETAIL_H_
|
||||
#define _TSLASTDETAIL_H_
|
||||
|
||||
#ifndef _MATHTYPES_H_
|
||||
#include "math/mathTypes.h"
|
||||
#endif
|
||||
#ifndef _MPOINT_H_
|
||||
#include "math/mPoint.h"
|
||||
#endif
|
||||
#ifndef _MMATRIX_H_
|
||||
#include "math/mMatrix.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
|
||||
class TSShape;
|
||||
class TSShapeInstance;
|
||||
class GBitmap;
|
||||
class TextureHandle;
|
||||
|
||||
/// This neat little class renders the object to a texture so that when the object
|
||||
/// is far away, it can be drawn as a billboard instead of a mesh. This happens
|
||||
/// when the model is first loaded as to keep the realtime render as fast as possible.
|
||||
/// It also renders the model from a few different perspectives so that it would actually
|
||||
/// pass as a model instead of a silly old billboard.
|
||||
class TSLastDetail
|
||||
{
|
||||
U32 mNumEquatorSteps; ///< number steps around the equator of the globe
|
||||
U32 mNumPolarSteps; ///< number of steps to go from equator to each polar region (0 means equator only)
|
||||
F32 mPolarAngle; ///< angle in radians of sub-polar regions
|
||||
bool mIncludePoles; ///< include poles in snapshot?
|
||||
|
||||
Point3F mCenter;
|
||||
|
||||
/// remember these values so we can save some work next time we render...
|
||||
U32 mBitmapIndex;
|
||||
F32 mRotY;
|
||||
|
||||
Vector<GBitmap*> mBitmaps; ///< Rendered bitmaps
|
||||
Vector<TextureHandle*> mTextures;///< Texture handles for those bitmaps
|
||||
|
||||
Point3F mPoints[4]; ///< always draw poly defined by these points...
|
||||
static Point3F smNorms[4];
|
||||
static Point2F smTVerts[4];
|
||||
|
||||
public:
|
||||
|
||||
/// This indicates that the TSLastDetail need neither clear nor set gl render states.
|
||||
///
|
||||
/// If you're doing a more complex renderer this is a useful trick.
|
||||
static bool smDirtyMode;
|
||||
|
||||
TSLastDetail(TSShapeInstance * shape, U32 numEquatorSteps, U32 numPolarSteps, F32 polarAngle, bool includePoles, S32 dl, S32 dim);
|
||||
~TSLastDetail();
|
||||
|
||||
void render(F32 alpha, bool drawFog);
|
||||
void renderNoFog(F32 alpha);
|
||||
void renderFog_Combine(F32 alpha);
|
||||
void renderFog_MultiCombine(F32 alpha);
|
||||
void chooseView(const MatrixF &, const Point3F & scale);
|
||||
};
|
||||
|
||||
|
||||
#endif // _TS_LAST_DETAIL
|
||||
|
||||
|
316
engine/ts/tsMaterialList.cc
Executable file
316
engine/ts/tsMaterialList.cc
Executable file
@ -0,0 +1,316 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsShape.h"
|
||||
|
||||
|
||||
TSMaterialList::TSMaterialList(U32 materialCount,
|
||||
const char **materialNames,
|
||||
const U32 * materialFlags,
|
||||
const U32 * reflectanceMaps,
|
||||
const U32 * bumpMaps,
|
||||
const U32 * detailMaps,
|
||||
const F32 * detailScales,
|
||||
const F32 * reflectionAmounts)
|
||||
: MaterialList(materialCount,materialNames),
|
||||
mNamesTransformed(false)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mFlags);
|
||||
VECTOR_SET_ASSOCIATION(mReflectanceMaps);
|
||||
VECTOR_SET_ASSOCIATION(mBumpMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailMaps);
|
||||
VECTOR_SET_ASSOCIATION(mLightMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailScales);
|
||||
VECTOR_SET_ASSOCIATION(mReflectionAmounts);
|
||||
|
||||
allocate(getMaterialCount());
|
||||
|
||||
dMemcpy(mFlags.address(),materialFlags,getMaterialCount()*sizeof(U32));
|
||||
dMemcpy(mReflectanceMaps.address(),reflectanceMaps,getMaterialCount()*sizeof(U32));
|
||||
dMemcpy(mBumpMaps.address(),bumpMaps,getMaterialCount()*sizeof(U32));
|
||||
dMemcpy(mDetailMaps.address(),detailMaps,getMaterialCount()*sizeof(U32));
|
||||
for (U32 i = 0; i < getMaterialCount(); i++)
|
||||
mLightMaps[i] = 0xFFFFFFFF;
|
||||
dMemcpy(mDetailScales.address(),detailScales,getMaterialCount()*sizeof(F32));
|
||||
dMemcpy(mReflectionAmounts.address(),reflectionAmounts,getMaterialCount()*sizeof(F32));
|
||||
}
|
||||
|
||||
TSMaterialList::TSMaterialList(U32 materialCount,
|
||||
const char **materialNames,
|
||||
const U32 * materialFlags,
|
||||
const U32 * reflectanceMaps,
|
||||
const U32 * bumpMaps,
|
||||
const U32 * detailMaps,
|
||||
const U32 * lightMaps,
|
||||
const F32 * detailScales,
|
||||
const F32 * reflectionAmounts)
|
||||
: MaterialList(materialCount,materialNames),
|
||||
mNamesTransformed(false)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mFlags);
|
||||
VECTOR_SET_ASSOCIATION(mReflectanceMaps);
|
||||
VECTOR_SET_ASSOCIATION(mBumpMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailMaps);
|
||||
VECTOR_SET_ASSOCIATION(mLightMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailScales);
|
||||
VECTOR_SET_ASSOCIATION(mReflectionAmounts);
|
||||
|
||||
allocate(getMaterialCount());
|
||||
|
||||
dMemcpy(mFlags.address(),materialFlags,getMaterialCount()*sizeof(U32));
|
||||
dMemcpy(mReflectanceMaps.address(),reflectanceMaps,getMaterialCount()*sizeof(U32));
|
||||
dMemcpy(mBumpMaps.address(),bumpMaps,getMaterialCount()*sizeof(U32));
|
||||
dMemcpy(mDetailMaps.address(),detailMaps,getMaterialCount()*sizeof(U32));
|
||||
dMemcpy(mLightMaps.address(),lightMaps,getMaterialCount()*sizeof(U32));
|
||||
dMemcpy(mDetailScales.address(),detailScales,getMaterialCount()*sizeof(F32));
|
||||
dMemcpy(mReflectionAmounts.address(),reflectionAmounts,getMaterialCount()*sizeof(F32));
|
||||
}
|
||||
|
||||
TSMaterialList::TSMaterialList()
|
||||
: mNamesTransformed(false)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mFlags);
|
||||
VECTOR_SET_ASSOCIATION(mReflectanceMaps);
|
||||
VECTOR_SET_ASSOCIATION(mBumpMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailMaps);
|
||||
VECTOR_SET_ASSOCIATION(mLightMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailScales);
|
||||
VECTOR_SET_ASSOCIATION(mReflectionAmounts);
|
||||
}
|
||||
|
||||
TSMaterialList::TSMaterialList(const TSMaterialList* pCopy)
|
||||
: MaterialList(pCopy)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mFlags);
|
||||
VECTOR_SET_ASSOCIATION(mReflectanceMaps);
|
||||
VECTOR_SET_ASSOCIATION(mBumpMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailMaps);
|
||||
VECTOR_SET_ASSOCIATION(mDetailScales);
|
||||
VECTOR_SET_ASSOCIATION(mReflectionAmounts);
|
||||
VECTOR_SET_ASSOCIATION(mLightMaps);
|
||||
|
||||
mFlags = pCopy->mFlags;
|
||||
mReflectanceMaps = pCopy->mReflectanceMaps;
|
||||
mBumpMaps = pCopy->mBumpMaps;
|
||||
mDetailMaps = pCopy->mDetailMaps;
|
||||
mLightMaps = pCopy->mLightMaps;
|
||||
mDetailScales = pCopy->mDetailScales;
|
||||
mReflectionAmounts = pCopy->mReflectionAmounts;
|
||||
mNamesTransformed = pCopy->mNamesTransformed;
|
||||
}
|
||||
|
||||
TSMaterialList::~TSMaterialList()
|
||||
{
|
||||
free();
|
||||
}
|
||||
|
||||
void TSMaterialList::free()
|
||||
{
|
||||
// IflMaterials will duplicate names and textures found in other material slots
|
||||
// (In particular, IflFrame material slots).
|
||||
// Set the names to NULL now so our parent doesn't delete them twice.
|
||||
// Texture handles should stay as is...
|
||||
for (U32 i=0; i<getMaterialCount(); i++)
|
||||
if (mFlags[i] & TSMaterialList::IflMaterial)
|
||||
mMaterialNames[i] = NULL;
|
||||
|
||||
// these aren't found on our parent, clear them out here to keep in synch
|
||||
mFlags.clear();
|
||||
mReflectanceMaps.clear();
|
||||
mBumpMaps.clear();
|
||||
mDetailMaps.clear();
|
||||
mLightMaps.clear();
|
||||
mDetailScales.clear();
|
||||
mReflectionAmounts.clear();
|
||||
|
||||
Parent::free();
|
||||
}
|
||||
|
||||
void TSMaterialList::remap(U32 toIndex, U32 fromIndex)
|
||||
{
|
||||
AssertFatal(toIndex < size() && fromIndex < size(),"TSMaterial::remap");
|
||||
|
||||
// only remap texture handle...flags and maps should stay the same...
|
||||
|
||||
mMaterials[toIndex] = mMaterials[fromIndex];
|
||||
mMaterialNames[toIndex] = mMaterialNames[fromIndex];
|
||||
}
|
||||
|
||||
void TSMaterialList::push_back(const char * name, U32 flags, U32 rMap, U32 bMap, U32 dMap, F32 dScale, F32 emapAmount, U32 lMap)
|
||||
{
|
||||
Parent::push_back(name);
|
||||
mFlags.push_back(flags);
|
||||
if (rMap==0xFFFFFFFF)
|
||||
mReflectanceMaps.push_back(getMaterialCount()-1);
|
||||
else
|
||||
mReflectanceMaps.push_back(rMap);
|
||||
mBumpMaps.push_back(bMap);
|
||||
mDetailMaps.push_back(dMap);
|
||||
mLightMaps.push_back(lMap);
|
||||
mDetailScales.push_back(dScale);
|
||||
mReflectionAmounts.push_back(emapAmount);
|
||||
}
|
||||
|
||||
void TSMaterialList::allocate(U32 sz)
|
||||
{
|
||||
mFlags.setSize(sz);
|
||||
mReflectanceMaps.setSize(sz);
|
||||
mBumpMaps.setSize(sz);
|
||||
mDetailMaps.setSize(sz);
|
||||
mLightMaps.setSize(sz);
|
||||
mDetailScales.setSize(sz);
|
||||
mReflectionAmounts.setSize(sz);
|
||||
}
|
||||
|
||||
U32 TSMaterialList::getFlags(U32 index)
|
||||
{
|
||||
AssertFatal(index < getMaterialCount(),"TSMaterialList::getFlags: index out of range");
|
||||
return mFlags[index];
|
||||
}
|
||||
|
||||
void TSMaterialList::setFlags(U32 index, U32 value)
|
||||
{
|
||||
AssertFatal(index < getMaterialCount(),"TSMaterialList::getFlags: index out of range");
|
||||
mFlags[index] = value;
|
||||
}
|
||||
|
||||
void TSMaterialList::load(U32 index,const char* path)
|
||||
{
|
||||
AssertFatal(index < getMaterialCount(),"TSMaterialList::getFlags: index out of range");
|
||||
|
||||
if (mFlags[index] & TSMaterialList::NoMipMap)
|
||||
mTextureType = BitmapTexture;
|
||||
else if (mFlags[index] & TSMaterialList::MipMap_ZeroBorder)
|
||||
mTextureType = ZeroBorderTexture;
|
||||
else
|
||||
mTextureType = MeshTexture;
|
||||
|
||||
Parent::load(index,path);
|
||||
}
|
||||
|
||||
bool TSMaterialList::write(Stream & s)
|
||||
{
|
||||
if (!Parent::write(s))
|
||||
return false;
|
||||
|
||||
U32 i;
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.write(mFlags[i]);
|
||||
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.write(mReflectanceMaps[i]);
|
||||
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.write(mBumpMaps[i]);
|
||||
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.write(mDetailMaps[i]);
|
||||
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.write(mLightMaps[i]);
|
||||
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.write(mDetailScales[i]);
|
||||
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.write(mReflectionAmounts[i]);
|
||||
|
||||
return (s.getStatus() == Stream::Ok);
|
||||
}
|
||||
|
||||
bool TSMaterialList::read(Stream & s)
|
||||
{
|
||||
if (!Parent::read(s))
|
||||
return false;
|
||||
|
||||
allocate(getMaterialCount());
|
||||
|
||||
U32 i;
|
||||
if (TSShape::smReadVersion<2)
|
||||
{
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
setFlags(i,S_Wrap|T_Wrap);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.read(&mFlags[i]);
|
||||
}
|
||||
|
||||
if (TSShape::smReadVersion<5)
|
||||
{
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
{
|
||||
mReflectanceMaps[i] = i;
|
||||
mBumpMaps[i] = 0xFFFFFFFF;
|
||||
mDetailMaps[i] = 0xFFFFFFFF;
|
||||
mLightMaps[i] = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.read(&mReflectanceMaps[i]);
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.read(&mBumpMaps[i]);
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.read(&mDetailMaps[i]);
|
||||
if (TSShape::smReadVersion>24)
|
||||
{
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.read(&mLightMaps[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
mLightMaps[i] = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
if (TSShape::smReadVersion>11)
|
||||
{
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.read(&mDetailScales[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
mDetailScales[i] = 1.0f;
|
||||
}
|
||||
|
||||
if (TSShape::smReadVersion>20)
|
||||
{
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
s.read(&mReflectionAmounts[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
mReflectionAmounts[i] = 1.0f;
|
||||
}
|
||||
|
||||
if (TSShape::smReadVersion<16)
|
||||
{
|
||||
// make sure emapping is off for translucent materials on old shapes
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
if (mFlags[i] & TSMaterialList::Translucent)
|
||||
mFlags[i] |= TSMaterialList::NeverEnvMap;
|
||||
}
|
||||
|
||||
// get rid of name of any ifl material names
|
||||
for (i=0; i<getMaterialCount(); i++)
|
||||
{
|
||||
const char * str = dStrrchr(mMaterialNames[i],'.');
|
||||
if (mFlags[i] & TSMaterialList::IflMaterial ||
|
||||
(TSShape::smReadVersion<6 && str && dStricmp(str,".ifl")==0))
|
||||
{
|
||||
delete [] mMaterialNames[i];
|
||||
mMaterialNames[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return (s.getStatus() == Stream::Ok);
|
||||
}
|
||||
|
3400
engine/ts/tsMesh.cc
Executable file
3400
engine/ts/tsMesh.cc
Executable file
File diff suppressed because it is too large
Load Diff
328
engine/ts/tsMesh.h
Executable file
328
engine/ts/tsMesh.h
Executable file
@ -0,0 +1,328 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSMESH_H_
|
||||
#define _TSMESH_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _STREAM_H_
|
||||
#include "core/stream.h"
|
||||
#endif
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _MATERIALLIST_H_
|
||||
#include "dgl/materialList.h"
|
||||
#endif
|
||||
#ifndef _ABSTRACTPOLYLIST_H_
|
||||
#include "collision/abstractPolyList.h"
|
||||
#endif
|
||||
|
||||
// when working with 3dsmax, we want some things to be vectors that otherwise
|
||||
// are pointers to non-resizeable blocks of memory
|
||||
#if defined(TORQUE_LIB)
|
||||
#define ToolVector Vector
|
||||
#else
|
||||
template<class A> class ToolVector
|
||||
{
|
||||
public:
|
||||
A * addr;
|
||||
U32 sz;
|
||||
void increment(U32 p = 1) { sz += p; }
|
||||
void decrement(U32 p = 1) { sz -= p; }
|
||||
U32 size() const { return sz; }
|
||||
bool empty() const { return sz==0; }
|
||||
A & operator[](U32 idx) { return addr[idx]; }
|
||||
A const & operator[](U32 idx) const { return addr[idx]; }
|
||||
A * address() { return addr; }
|
||||
void set(void * _addr, U32 _sz) { addr = (A*)_addr; sz = _sz; }
|
||||
};
|
||||
#endif
|
||||
|
||||
class TSMaterialList;
|
||||
class TSShapeInstance;
|
||||
struct RayInfo;
|
||||
class ConvexFeature;
|
||||
|
||||
struct TSDrawPrimitive
|
||||
{
|
||||
enum
|
||||
{
|
||||
Triangles = 0 << 30, ///< bits 30 and 31 index element type
|
||||
Strip = 1 << 30, ///< bits 30 and 31 index element type
|
||||
Fan = 2 << 30, ///< bits 30 and 31 index element type
|
||||
Indexed = BIT(29), ///< use glDrawElements if indexed, glDrawArrays o.w.
|
||||
NoMaterial = BIT(28), ///< set if no material (i.e., texture missing)
|
||||
MaterialMask = ~(Strip|Fan|Triangles|Indexed|NoMaterial),
|
||||
TypeMask = Strip|Fan|Triangles
|
||||
};
|
||||
|
||||
S16 start;
|
||||
S16 numElements;
|
||||
S32 matIndex; ///< holds material index & element type (see above enum)
|
||||
};
|
||||
|
||||
class TSMesh
|
||||
{
|
||||
protected:
|
||||
U32 meshType;
|
||||
Box3F mBounds;
|
||||
Point3F mCenter;
|
||||
F32 mRadius;
|
||||
|
||||
static F32 overrideFadeVal;
|
||||
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
/// types...
|
||||
StandardMeshType = 0,
|
||||
SkinMeshType = 1,
|
||||
DecalMeshType = 2,
|
||||
SortedMeshType = 3,
|
||||
NullMeshType = 4,
|
||||
TypeMask = StandardMeshType|SkinMeshType|DecalMeshType|SortedMeshType|NullMeshType,
|
||||
|
||||
/// flags (stored with meshType)...
|
||||
Billboard = BIT(31), HasDetailTexture = BIT(30),
|
||||
BillboardZAxis = BIT(29), UseEncodedNormals = BIT(28), HasLightTexture = BIT(27),
|
||||
FlagMask = Billboard|BillboardZAxis|HasDetailTexture|UseEncodedNormals|HasLightTexture
|
||||
};
|
||||
|
||||
U32 getMeshType() { return meshType & TypeMask; }
|
||||
void setFlags(U32 flag) { meshType |= flag; }
|
||||
void clearFlags(U32 flag) { meshType &= ~flag; }
|
||||
U32 getFlags(U32 flag = 0xFFFFFFFF) { return meshType & flag; }
|
||||
|
||||
const Point3F * getNormals(S32 firstVert);
|
||||
|
||||
S32 parentMesh; ///< index into shapes mesh list
|
||||
S32 numFrames;
|
||||
S32 numMatFrames;
|
||||
S32 vertsPerFrame;
|
||||
|
||||
ToolVector<Point3F> verts;
|
||||
ToolVector<Point3F> norms;
|
||||
ToolVector<TSDrawPrimitive> primitives;
|
||||
ToolVector<U8> encodedNorms;
|
||||
ToolVector<U16> indices;
|
||||
ToolVector<U16> mergeIndices; ///< the last so many verts merge with these
|
||||
///< verts to form the next detail level
|
||||
///< NOT IMPLEMENTED YET
|
||||
|
||||
// Multiple UV interface
|
||||
enum UVType
|
||||
{
|
||||
tUnknown = 0,
|
||||
tDiffuse,
|
||||
tLightmap
|
||||
};
|
||||
|
||||
struct UVList
|
||||
{
|
||||
U32 type;
|
||||
ToolVector<Point2F> uvs;
|
||||
|
||||
UVList() { type = tUnknown; };
|
||||
};
|
||||
|
||||
Vector<UVList> mUVs;
|
||||
|
||||
bool getUVs(U32 type, ToolVector<Point2F>& uvs);
|
||||
bool setUVs(U32 type, ToolVector<Point2F> uvs);
|
||||
|
||||
/// billboard data
|
||||
Point3F billboardAxis;
|
||||
|
||||
/// @name Convex Hull Data
|
||||
/// Convex hulls are convex (no angles >= 180<38>) meshes used for collision
|
||||
/// @{
|
||||
|
||||
Vector<Point3F> planeNormals;
|
||||
Vector<F32> planeConstants;
|
||||
Vector<U32> planeMaterials;
|
||||
S32 planesPerFrame;
|
||||
S32 vbOffset;
|
||||
U32 mergeBufferStart;
|
||||
/// @}
|
||||
|
||||
/// @name Render Methods
|
||||
/// @{
|
||||
|
||||
virtual void fillVB(S32 vb, S32 frame, S32 matFrame, TSMaterialList *materials);
|
||||
virtual void morphVB(S32 vb, S32 morph, S32 frame, S32 matFrame, TSMaterialList *materials);
|
||||
virtual void renderVB(S32 frame, S32 matFrame, TSMaterialList *materials);
|
||||
virtual void render(S32 frame, S32 matFrame, TSMaterialList *);
|
||||
virtual void renderShadow(S32 frame, const MatrixF & mat, S32 dim, U32 * bits, TSMaterialList *);
|
||||
void renderEnvironmentMap(S32 frame, S32 matFrame, TSMaterialList *);
|
||||
void renderDetailMap(S32 frame, S32 matFrame, TSMaterialList *);
|
||||
void renderFog(S32 frame, TSMaterialList* materials);
|
||||
void renderLightMap(S32 frame, S32 matFrame, TSMaterialList *);
|
||||
/// @}
|
||||
|
||||
/// @name Material Methods
|
||||
/// @{
|
||||
|
||||
static void initMaterials();
|
||||
static void resetMaterials();
|
||||
static void initEnvironmentMapMaterials();
|
||||
static void resetEnvironmentMapMaterials();
|
||||
static void initDetailMapMaterials();
|
||||
static void resetDetailMapMaterials();
|
||||
static void initLightMapMaterials();
|
||||
static void resetLightMapMaterials();
|
||||
static void setMaterial(S32 matIndex, TSMaterialList *);
|
||||
static void setFade(F32 fadeValue);
|
||||
static void clearFade();
|
||||
static void setOverrideFade(F32 fadeValue){ overrideFadeVal = fadeValue; }
|
||||
static F32 getOverrideFade(){ return overrideFadeVal; }
|
||||
/// @}
|
||||
|
||||
/// @name Collision Methods
|
||||
/// @{
|
||||
|
||||
virtual bool buildPolyList(S32 frame, AbstractPolyList * polyList, U32 & surfaceKey);
|
||||
virtual bool getFeatures(S32 frame, const MatrixF&, const VectorF&, ConvexFeature*, U32& surfaceKey);
|
||||
virtual void support(S32 frame, const Point3F& v, F32* currMaxDP, Point3F* currSupport);
|
||||
virtual bool castRay(S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo);
|
||||
virtual bool buildConvexHull(); ///< returns false if not convex (still builds planes)
|
||||
bool addToHull(U32 idx0, U32 idx1, U32 idx2);
|
||||
/// @}
|
||||
|
||||
/// @name Bounding Methods
|
||||
/// calculate and get bounding information
|
||||
/// @{
|
||||
|
||||
void computeBounds();
|
||||
virtual void computeBounds(MatrixF & transform, Box3F & bounds, S32 frame = 0, Point3F * center = NULL, F32 * radius = NULL);
|
||||
void computeBounds(Point3F *, S32 numVerts, MatrixF & transform, Box3F & bounds, Point3F * center, F32 * radius);
|
||||
Box3F & getBounds() { return mBounds; }
|
||||
Point3F & getCenter() { return mCenter; }
|
||||
F32 getRadius() { return mRadius; }
|
||||
virtual S32 getNumPolys();
|
||||
|
||||
U8 encodeNormal(const Point3F & normal);
|
||||
const Point3F & decodeNormal(U8 ncode);
|
||||
/// @}
|
||||
|
||||
void saveMergeVerts(); ///< called by shapeinstance in setStatics
|
||||
void restoreMergeVerts(); ///< called by shapeinstance in clearStatics
|
||||
void saveMergeNormals(); ///< called by mesh at start of render (decals don't bother)
|
||||
void restoreMergeNormals(); ///< called by mesh at end of render
|
||||
|
||||
/// persist methods...
|
||||
virtual void assemble(bool skip);
|
||||
static TSMesh * assembleMesh(U32 meshType, bool skip);
|
||||
virtual void disassemble();
|
||||
|
||||
/// on load...optionally convert primitives to other form
|
||||
static bool smUseTriangles;
|
||||
static bool smUseOneStrip;
|
||||
static S32 smMinStripSize;
|
||||
static bool smUseEncodedNormals;
|
||||
|
||||
/// convert primitives on load...
|
||||
void convertToTris(S16 * primitiveDataIn, S32 * primitiveMatIn, S16 * indicesIn,
|
||||
S32 numPrimIn, S32 & numPrimOut, S32 & numIndicesOut,
|
||||
S32 * primitivesOut, S16 * indicesOut);
|
||||
void convertToSingleStrip(S16 * primitiveDataIn, S32 * primitiveMatIn, S16 * indicesIn,
|
||||
S32 numPrimIn, S32 & numPrimOut, S32 & numIndicesOut,
|
||||
S32 * primitivesOut, S16 * indicesOut);
|
||||
void leaveAsMultipleStrips(S16 * primitiveDataIn, S32 * primitiveMatIn, S16 * indicesIn,
|
||||
S32 numPrimIn, S32 & numPrimOut, S32 & numIndicesOut,
|
||||
S32 * primitivesOut, S16 * indicesOut);
|
||||
|
||||
/// methods used during assembly to share vertexand other info
|
||||
/// between meshes (and for skipping detail levels on load)
|
||||
S32 * getSharedData32(S32 parentMesh, S32 size, S32 ** source, bool skip);
|
||||
S8 * getSharedData8 (S32 parentMesh, S32 size, S8 ** source, bool skip);
|
||||
|
||||
/// @name Assembly Variables
|
||||
/// variables used during assembly (for skipping mesh detail levels
|
||||
/// on load and for sharing verts between meshes)
|
||||
/// @{
|
||||
|
||||
static Vector<Point3F*> smVertsList;
|
||||
static Vector<Point3F*> smNormsList;
|
||||
static Vector<U8*> smEncodedNormsList;
|
||||
static Vector<Point2F*> smTVertsList;
|
||||
static Vector<bool> smDataCopied;
|
||||
|
||||
static Vector<Point3F> smSaveVerts;
|
||||
static Vector<Point3F> smSaveNorms;
|
||||
static Vector<Point2F> smSaveTVerts;
|
||||
|
||||
static const Point3F smU8ToNormalTable[];
|
||||
/// @}
|
||||
|
||||
|
||||
TSMesh() : meshType(StandardMeshType) {
|
||||
VECTOR_SET_ASSOCIATION(planeNormals);
|
||||
VECTOR_SET_ASSOCIATION(planeConstants);
|
||||
VECTOR_SET_ASSOCIATION(planeMaterials);
|
||||
parentMesh = -1;
|
||||
}
|
||||
virtual ~TSMesh();
|
||||
};
|
||||
|
||||
inline const Point3F & TSMesh::decodeNormal(U8 ncode) { return smU8ToNormalTable[ncode]; }
|
||||
|
||||
class TSSkinMesh : public TSMesh
|
||||
{
|
||||
public:
|
||||
typedef TSMesh Parent;
|
||||
|
||||
/// vectors that define the vertex, weight, bone tuples
|
||||
ToolVector<F32> weight;
|
||||
ToolVector<S32> boneIndex;
|
||||
ToolVector<S32> vertexIndex;
|
||||
|
||||
/// vectors indexed by bone number
|
||||
ToolVector<S32> nodeIndex;
|
||||
ToolVector<MatrixF> initialTransforms;
|
||||
|
||||
/// initial values of verts and normals
|
||||
/// these get transformed into initial bone space,
|
||||
/// from there into world space relative to current bone
|
||||
/// pos, and then weighted by bone weights...
|
||||
ToolVector<Point3F> initialVerts;
|
||||
ToolVector<Point3F> initialNorms;
|
||||
|
||||
/// set verts and normals...
|
||||
void updateSkin();
|
||||
|
||||
// render methods..
|
||||
void render(S32 frame, S32 matFrame, TSMaterialList *);
|
||||
void renderShadow(S32 frame, const MatrixF & mat, S32 dim, U32 * bits, TSMaterialList *);
|
||||
|
||||
// collision methods...
|
||||
bool buildPolyList(S32 frame, AbstractPolyList * polyList, U32 & surfaceKey);
|
||||
bool castRay(S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo);
|
||||
bool buildConvexHull(); // does nothing, skins don't use this
|
||||
|
||||
void computeBounds(MatrixF & transform, Box3F & bounds, S32 frame, Point3F * center, F32 * radius);
|
||||
|
||||
/// persist methods...
|
||||
void assemble(bool skip);
|
||||
void disassemble();
|
||||
|
||||
/// variables used during assembly (for skipping mesh detail levels
|
||||
/// on load and for sharing verts between meshes)
|
||||
static Vector<MatrixF*> smInitTransformList;
|
||||
static Vector<S32*> smVertexIndexList;
|
||||
static Vector<S32*> smBoneIndexList;
|
||||
static Vector<F32*> smWeightList;
|
||||
static Vector<S32*> smNodeIndexList;
|
||||
|
||||
TSSkinMesh() { meshType = SkinMeshType; }
|
||||
};
|
||||
|
||||
#endif
|
498
engine/ts/tsPartInstance.cc
Executable file
498
engine/ts/tsPartInstance.cc
Executable file
@ -0,0 +1,498 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsPartInstance.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Constructors
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
MRandomR250 TSPartInstance::smRandom;
|
||||
|
||||
TSPartInstance::TSPartInstance(TSShapeInstance * sourceShape)
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mMeshObjects);
|
||||
VECTOR_SET_ASSOCIATION(mDecalObjects);
|
||||
|
||||
init(sourceShape);
|
||||
}
|
||||
|
||||
TSPartInstance::TSPartInstance(TSShapeInstance * sourceShape, S32 objectIndex)
|
||||
{
|
||||
init(sourceShape);
|
||||
addObject(objectIndex);
|
||||
}
|
||||
|
||||
void TSPartInstance::init(TSShapeInstance * sourceShape)
|
||||
{
|
||||
mSourceShape = sourceShape;
|
||||
mSizeCutoffs = NULL;
|
||||
mPolyCount = NULL;
|
||||
mNumDetails = 0;
|
||||
mCurrentObjectDetail = 0;
|
||||
mCurrentIntraDL = 1.0f;
|
||||
mData = 0;
|
||||
}
|
||||
|
||||
TSPartInstance::~TSPartInstance()
|
||||
{
|
||||
delete [] mPolyCount;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Methods for updating PartInstances
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void TSPartInstance::addObject(S32 objectIndex)
|
||||
{
|
||||
if (mSourceShape->mMeshObjects[objectIndex].visible<0.01f)
|
||||
// not visible, don't bother
|
||||
return;
|
||||
|
||||
mMeshObjects.push_back(&mSourceShape->mMeshObjects[objectIndex]);
|
||||
|
||||
// add any decals that are currently on?
|
||||
S32 decalIndex = mSourceShape->getShape()->objects[objectIndex].firstDecal;
|
||||
while (decalIndex>=0)
|
||||
{
|
||||
if (mSourceShape->mDecalObjects[decalIndex].frame>=0)
|
||||
mDecalObjects.push_back(&mSourceShape->mDecalObjects[decalIndex]);
|
||||
decalIndex = mSourceShape->mDecalObjects[decalIndex].decalObject->nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
void TSPartInstance::updateBounds()
|
||||
{
|
||||
mSourceShape->setStatics();
|
||||
|
||||
// run through meshes and brute force it?
|
||||
Box3F bounds;
|
||||
mBounds.min.set( 10E30f, 10E30f, 10E30f);
|
||||
mBounds.max.set(-10E30f,-10E30f,-10E30f);
|
||||
for (S32 i=0; i<mMeshObjects.size(); i++)
|
||||
{
|
||||
if (mMeshObjects[i]->getMesh(0))
|
||||
mMeshObjects[i]->getMesh(0)->computeBounds(*mMeshObjects[i]->getTransform(),bounds,mMeshObjects[i]->frame);
|
||||
mBounds.min.setMin(bounds.min);
|
||||
mBounds.max.setMax(bounds.max);
|
||||
}
|
||||
mCenter = mBounds.min + mBounds.max;
|
||||
mCenter *= 0.5f;
|
||||
Point3F r = mBounds.max-mCenter;
|
||||
mRadius = mSqrt(mDot(r,r));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Methods for breaking shapes into pieces
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void TSPartInstance::breakShape(TSShapeInstance * shape, S32 subShape, Vector<TSPartInstance*> & partList, F32 * probShatter, F32 * probBreak, S32 probDepth)
|
||||
{
|
||||
AssertFatal(subShape>=0 && subShape<shape->mShape->subShapeFirstNode.size(),"TSPartInstance::breakShape: subShape out of range.");
|
||||
|
||||
S32 start = shape->mShape->subShapeFirstNode[subShape];
|
||||
|
||||
TSPartInstance::breakShape(shape, NULL, start, partList, probShatter, probBreak, probDepth);
|
||||
|
||||
// update bounds (and get rid of empty parts)
|
||||
for (S32 i=0; i<partList.size(); i++)
|
||||
{
|
||||
if (partList[i]->mMeshObjects.size())
|
||||
partList[i]->updateBounds();
|
||||
else
|
||||
{
|
||||
partList.erase(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSPartInstance::breakShape(TSShapeInstance * shape, TSPartInstance * currentPart, S32 currentNode, Vector<TSPartInstance*> & partList, F32 * probShatter, F32 * probBreak, S32 probDepth)
|
||||
{
|
||||
AssertFatal( !probDepth || (probShatter && probBreak),"TSPartInstance::breakShape: probabilities improperly specified.");
|
||||
|
||||
const TSShape::Node * node = &shape->mShape->nodes[currentNode];
|
||||
S32 object = node->firstObject;
|
||||
S32 child = node->firstChild;
|
||||
|
||||
// copy off probabilities and update probability lists for next level
|
||||
F32 ps = probShatter ? *probShatter : 1.0f;
|
||||
F32 pb = probBreak ? *probBreak : 1.0f;
|
||||
if (probDepth>1 && probShatter && probBreak)
|
||||
{
|
||||
probShatter++;
|
||||
probBreak++;
|
||||
probDepth--;
|
||||
}
|
||||
|
||||
// what to do...depending on how the die roll, we can:
|
||||
// a) shatter the shape at this level -- meaning we make a part out of each object on this node and
|
||||
// we make parts out of all the children (perhaps breaking them up further still)
|
||||
// b) break the shape off at this level -- meaning we make a part out of the intact piece from here
|
||||
// on down (again, we might break the result further as we iterate through the nodes...what breaking
|
||||
// the shape really does is separate this piece from the parent piece).
|
||||
// c) add this piece to the parent -- meaning all objects on this node are added to the parent, and children
|
||||
// are also added (but children will be recursively sent through this routine, so if a parent gets option
|
||||
// (c) and the child option (a) or (b), then the child will be ripped from the parents grasp. Cruel
|
||||
// people us coders are.
|
||||
// Note: (a) is the only way that two objects on the same node can be separated...that is why both
|
||||
// option a and option b are needed.
|
||||
if (!probShatter || smRandom.randF() < ps)
|
||||
{
|
||||
// option a -- shatter the shape at this level
|
||||
|
||||
// iterate through the objects, make part out of each one
|
||||
while (object>=0)
|
||||
{
|
||||
partList.increment();
|
||||
partList.last() = new TSPartInstance(shape,object);
|
||||
object = shape->mShape->objects[object].nextSibling;
|
||||
}
|
||||
|
||||
// iterate through the child nodes, call ourselves on each one with currentPart = NULL
|
||||
while (child>=0)
|
||||
{
|
||||
TSPartInstance::breakShape(shape,NULL,child,partList,probShatter,probBreak,probDepth);
|
||||
child = shape->mShape->nodes[child].nextSibling;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!probBreak || smRandom.randF() < pb)
|
||||
// option b -- break the shape off at this level
|
||||
currentPart = NULL; // fall through to option C
|
||||
|
||||
// option c -- add this piece to the parent
|
||||
|
||||
if (!currentPart)
|
||||
{
|
||||
currentPart = new TSPartInstance(shape);
|
||||
partList.push_back(currentPart);
|
||||
}
|
||||
|
||||
// iterate through objects, add to currentPart
|
||||
while (object>=0)
|
||||
{
|
||||
currentPart->addObject(object);
|
||||
object = shape->mShape->objects[object].nextSibling;
|
||||
}
|
||||
|
||||
// iterate through child nodes, call ourselves on each one with currentPart as is
|
||||
while (child>=0)
|
||||
{
|
||||
TSPartInstance::breakShape(shape,currentPart,child,partList,probShatter,probBreak,probDepth);
|
||||
child = shape->mShape->nodes[child].nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// render methods -- we use TSShapeInstance code as much as possible
|
||||
// issues: setupTexturing expects a detail level, we give it an object detail level
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void TSPartInstance::render(S32 od, const Point3F * objectScale)
|
||||
{
|
||||
S32 i;
|
||||
|
||||
//
|
||||
mSourceShape->setStatics(0,mCurrentIntraDL,objectScale);
|
||||
mSourceShape->setupTexturing(od,mCurrentIntraDL); // use of od here is a bit sketchy...but should be ok
|
||||
TSMesh::initMaterials();
|
||||
|
||||
// render mesh objects
|
||||
TSShapeInstance::smRenderData.currentTransform = NULL;
|
||||
for (i=0; i<mMeshObjects.size(); i++)
|
||||
mMeshObjects[i]->render(od,mSourceShape->getMaterialList());
|
||||
|
||||
if (TSShapeInstance::smRenderData.currentTransform)
|
||||
glPopMatrix();
|
||||
|
||||
// restore gl state
|
||||
TSMesh::resetMaterials();
|
||||
|
||||
// render detail maps using a second pass?
|
||||
if (mSourceShape->twoPassDetailMap())
|
||||
renderDetailMap(od);
|
||||
|
||||
// render environment map using second pass?
|
||||
if (mSourceShape->twoPassEnvironmentMap())
|
||||
renderEnvironmentMap(od);
|
||||
|
||||
// render lightmap using second pass?
|
||||
if (mSourceShape->twoPassLightMap())
|
||||
renderLightMap(od);
|
||||
|
||||
// set up gl environment for decals...
|
||||
TSDecalMesh::initDecalMaterials();
|
||||
|
||||
// render decals...
|
||||
TSShapeInstance::smRenderData.currentTransform = NULL;
|
||||
for (i=0; i<mDecalObjects.size(); i++)
|
||||
mDecalObjects[i]->render(od,mSourceShape->mMaterialList);
|
||||
|
||||
// if we have a matrix pushed, pop it now
|
||||
if (TSShapeInstance::smRenderData.currentTransform)
|
||||
glPopMatrix();
|
||||
|
||||
// restore gl state
|
||||
TSDecalMesh::resetDecalMaterials();
|
||||
|
||||
// render fog if 2-passing it
|
||||
if (mSourceShape->twoPassFog())
|
||||
renderFog(od);
|
||||
|
||||
mSourceShape->clearStatics();
|
||||
}
|
||||
|
||||
void TSPartInstance::renderDetailMap(S32 od)
|
||||
{
|
||||
AssertFatal(TSShapeInstance::smRenderData.detailMapMethod==TSShapeInstance::DETAIL_MAP_TWO_PASS,"TSPartInstance::renderDetailMap");
|
||||
|
||||
// set up gl environment for the detail map
|
||||
TSMesh::initDetailMapMaterials();
|
||||
|
||||
// run through objects and render detail maps
|
||||
TSShapeInstance::smRenderData.currentTransform = NULL;
|
||||
for (S32 i=0; i<mMeshObjects.size(); i++)
|
||||
mMeshObjects[i]->renderDetailMap(od,mSourceShape->mMaterialList);
|
||||
|
||||
// if we have a matrix pushed, pop it now
|
||||
if (TSShapeInstance::smRenderData.currentTransform)
|
||||
glPopMatrix();
|
||||
|
||||
// restore gl state
|
||||
TSMesh::resetDetailMapMaterials();
|
||||
}
|
||||
|
||||
void TSPartInstance::renderEnvironmentMap(S32 od)
|
||||
{
|
||||
AssertFatal((TextureObject*)mSourceShape->mEnvironmentMap!=NULL,"TSPartInstance::renderEnvironmentMap (1)");
|
||||
AssertFatal(mSourceShape->mEnvironmentMapOn,"TSPartInstance::renderEnvironmentMap (2)");
|
||||
AssertFatal(dglDoesSupportARBMultitexture(),"TSPartInstance::renderEnvironmentMap (3)");
|
||||
AssertFatal(TSShapeInstance::smRenderData.environmentMapMethod==TSShapeInstance::ENVIRONMENT_MAP_TWO_PASS,"TSPartInstance::renderEnvironmentMap (4)");
|
||||
|
||||
// set up gl environment for emap...
|
||||
TSMesh::initEnvironmentMapMaterials();
|
||||
|
||||
// run through objects and render
|
||||
S32 i;
|
||||
TSShapeInstance::smRenderData.currentTransform = NULL;
|
||||
for (i=0; i<mMeshObjects.size(); i++)
|
||||
mMeshObjects[i]->renderEnvironmentMap(od,mSourceShape->getMaterialList());
|
||||
|
||||
// if we have a matrix pushed, pop it now
|
||||
if (TSShapeInstance::smRenderData.currentTransform)
|
||||
glPopMatrix();
|
||||
|
||||
// restore gl state
|
||||
TSMesh::resetEnvironmentMapMaterials();
|
||||
}
|
||||
|
||||
void TSPartInstance::renderFog(S32 od)
|
||||
{
|
||||
AssertFatal(TSShapeInstance::smRenderData.fogMethod==TSShapeInstance::FOG_TWO_PASS,"TSPartInstance::renderFog");
|
||||
|
||||
// just one fog color per shape...
|
||||
bool wasLit = glIsEnabled(GL_LIGHTING)!=0;
|
||||
glDisable(GL_LIGHTING);
|
||||
glColor4fv(TSShapeInstance::smRenderData.fogColor);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
// texture should be disabled already...
|
||||
|
||||
TSShapeInstance::smRenderData.currentTransform = NULL;
|
||||
for (S32 i=0; i<mMeshObjects.size(); i++)
|
||||
mMeshObjects[i]->renderFog(od, mSourceShape->getMaterialList());
|
||||
|
||||
// if we have a marix pushed, pop it now
|
||||
if (TSShapeInstance::smRenderData.currentTransform)
|
||||
glPopMatrix();
|
||||
|
||||
// reset gl state
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
if (wasLit)
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
void TSPartInstance::renderLightMap(S32 od)
|
||||
{
|
||||
AssertFatal(TSShapeInstance::smRenderData.lightMapMethod==TSShapeInstance::LIGHT_MAP_TWO_PASS,"TSPartInstance::renderLightMap");
|
||||
|
||||
// set up gl environment for the detail map
|
||||
TSMesh::initLightMapMaterials();
|
||||
|
||||
// run through objects and render detail maps
|
||||
TSShapeInstance::smRenderData.currentTransform = NULL;
|
||||
for (S32 i=0; i<mMeshObjects.size(); i++)
|
||||
mMeshObjects[i]->renderLightMap(od,mSourceShape->mMaterialList);
|
||||
|
||||
// if we have a matrix pushed, pop it now
|
||||
if (TSShapeInstance::smRenderData.currentTransform)
|
||||
glPopMatrix();
|
||||
|
||||
// restore gl state
|
||||
TSMesh::resetLightMapMaterials();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Detail selection
|
||||
// 2 methods:
|
||||
// method 1: use source shapes detail levels...
|
||||
// method 2: pass in our own table...
|
||||
// In either case, you can compute the pixel size on your own or let open gl do it.
|
||||
// If you want to use method 2, you have to call setDetailData sometime before selecting detail
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void TSPartInstance::setDetailData(F32 * sizeCutoffs, S32 numDetails)
|
||||
{
|
||||
if (mSizeCutoffs == sizeCutoffs && mNumDetails==numDetails)
|
||||
return;
|
||||
|
||||
mSizeCutoffs = sizeCutoffs;
|
||||
mNumDetails = numDetails;
|
||||
delete [] mPolyCount;
|
||||
mPolyCount = NULL;
|
||||
}
|
||||
|
||||
void TSPartInstance::selectCurrentDetail(bool ignoreScale)
|
||||
{
|
||||
if (mSizeCutoffs)
|
||||
{
|
||||
selectCurrentDetail(mSizeCutoffs,mNumDetails,ignoreScale);
|
||||
return;
|
||||
}
|
||||
|
||||
mSourceShape->selectCurrentDetail(ignoreScale);
|
||||
mCurrentObjectDetail = mSourceShape->getCurrentDetail();
|
||||
mCurrentIntraDL = mSourceShape->getCurrentIntraDetail();
|
||||
}
|
||||
|
||||
void TSPartInstance::selectCurrentDetail(F32 pixelSize)
|
||||
{
|
||||
if (mSizeCutoffs)
|
||||
{
|
||||
selectCurrentDetail(pixelSize,mSizeCutoffs,mNumDetails);
|
||||
return;
|
||||
}
|
||||
|
||||
mSourceShape->selectCurrentDetail(pixelSize);
|
||||
mCurrentObjectDetail = mSourceShape->getCurrentDetail();
|
||||
mCurrentIntraDL = mSourceShape->getCurrentIntraDetail();
|
||||
}
|
||||
|
||||
void TSPartInstance::selectCurrentDetail2(F32 adjustedDist)
|
||||
{
|
||||
if (mSizeCutoffs)
|
||||
{
|
||||
F32 pixelSize = dglProjectRadius(adjustedDist,mSourceShape->getShape()->radius) * dglGetPixelScale() * TSShapeInstance::smDetailAdjust;
|
||||
selectCurrentDetail(pixelSize,mSizeCutoffs,mNumDetails);
|
||||
return;
|
||||
}
|
||||
|
||||
mSourceShape->selectCurrentDetail2(adjustedDist);
|
||||
mCurrentObjectDetail = mSourceShape->getCurrentDetail();
|
||||
mCurrentIntraDL = mSourceShape->getCurrentIntraDetail();
|
||||
}
|
||||
|
||||
void TSPartInstance::selectCurrentDetail(F32 * sizeCutoffs, S32 numDetails, bool ignoreScale)
|
||||
{
|
||||
// compute pixel size
|
||||
MatrixF toCam;
|
||||
Point3F p;
|
||||
dglGetModelview(&toCam);
|
||||
toCam.mulP(mCenter,&p);
|
||||
F32 dist = mDot(p,p);
|
||||
F32 scale = 1.0f;
|
||||
if (!ignoreScale)
|
||||
{
|
||||
// any scale?
|
||||
Point3F x,y,z;
|
||||
toCam.getRow(0,&x);
|
||||
toCam.getRow(1,&y);
|
||||
toCam.getRow(2,&z);
|
||||
F32 scalex = mDot(x,x);
|
||||
F32 scaley = mDot(y,y);
|
||||
F32 scalez = mDot(z,z);
|
||||
scale = scalex;
|
||||
if (scaley > scale)
|
||||
scale = scaley;
|
||||
if (scalez > scale)
|
||||
scale = scalez;
|
||||
}
|
||||
dist /= scale;
|
||||
dist = mSqrt(dist);
|
||||
|
||||
F32 pixelRadius = dglProjectRadius(dist,mRadius) * dglGetPixelScale() * TSShapeInstance::smDetailAdjust;
|
||||
|
||||
selectCurrentDetail(pixelRadius,sizeCutoffs,numDetails);
|
||||
}
|
||||
|
||||
void TSPartInstance::selectCurrentDetail(F32 pixelSize, F32 * sizeCutoffs, S32 numDetails)
|
||||
{
|
||||
mCurrentObjectDetail = 0;
|
||||
while (numDetails)
|
||||
{
|
||||
if (pixelSize > *sizeCutoffs)
|
||||
return;
|
||||
mCurrentObjectDetail++;
|
||||
numDetails--;
|
||||
sizeCutoffs++;
|
||||
}
|
||||
mCurrentObjectDetail = -1;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Detail query methods...complicated because there are two ways that detail information
|
||||
// can be determined...1) using source shape, or 2) using mSizeCutoffs
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
F32 TSPartInstance::getDetailSize(S32 dl)
|
||||
{
|
||||
if (dl<0)
|
||||
return 0;
|
||||
else if (mSizeCutoffs && dl<mNumDetails)
|
||||
return mSizeCutoffs[dl];
|
||||
else if (!mSizeCutoffs && dl<=mSourceShape->getShape()->mSmallestVisibleDL)
|
||||
return mSourceShape->getShape()->details[dl].size;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
S32 TSPartInstance::getPolyCount(S32 dl)
|
||||
{
|
||||
if (!mPolyCount)
|
||||
computePolyCount();
|
||||
|
||||
if (dl<0 || dl>=mNumDetails)
|
||||
return 0;
|
||||
else
|
||||
return mPolyCount[dl];
|
||||
}
|
||||
|
||||
void TSPartInstance::computePolyCount()
|
||||
{
|
||||
if (!mSizeCutoffs)
|
||||
mNumDetails = mSourceShape->getShape()->mSmallestVisibleDL+1;
|
||||
|
||||
delete [] mPolyCount;
|
||||
mPolyCount = new S32[mNumDetails];
|
||||
|
||||
for (S32 i=0; i<mNumDetails; i++)
|
||||
{
|
||||
mPolyCount[i] = 0;
|
||||
for (S32 j=0; j<mMeshObjects.size(); j++)
|
||||
{
|
||||
if (mMeshObjects[j]->getMesh(i))
|
||||
mPolyCount[i] += mMeshObjects[j]->getMesh(i)->getNumPolys();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
121
engine/ts/tsPartInstance.h
Executable file
121
engine/ts/tsPartInstance.h
Executable file
@ -0,0 +1,121 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSPARTINSTANCE_H_
|
||||
#define _TSPARTINSTANCE_H_
|
||||
|
||||
#ifndef _TSSHAPEINSTANCE_H_
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#endif
|
||||
|
||||
class TSPartInstance
|
||||
{
|
||||
/// TSPartInstance assumes ownership (or shared ownership) of the source shape. This means that the source
|
||||
/// shape cannot be deleted so long as the part instance is still around. This also means that any change
|
||||
/// to source shapes's transforms or other animation properties will affect how the part instance displays.
|
||||
/// It is ok (even expected), however, to have many part instances accessing the same shape.
|
||||
TSShapeInstance * mSourceShape;
|
||||
|
||||
/// @name Bounding info
|
||||
/// @{
|
||||
|
||||
Box3F mBounds;
|
||||
Point3F mCenter;
|
||||
F32 mRadius;
|
||||
/// @}
|
||||
|
||||
/// detail selection uses the table pointed to by this member
|
||||
///
|
||||
/// if this member is blank, then it uses source shape to determine detail...
|
||||
///
|
||||
/// detail 0 draws up until size of shape is less than mSizeCutoffs[0], detail 1 until mSizeCutoffs[1], etc.
|
||||
F32 * mSizeCutoffs;
|
||||
S32 * mPolyCount;
|
||||
S32 mNumDetails;
|
||||
|
||||
/// @name Detail Levels
|
||||
/// detail levels on part instance correspond directly
|
||||
/// to object details on objects -- this is different
|
||||
/// from shape instances where dl corresponds to a
|
||||
/// subtree number and object detail. The reason
|
||||
/// for this is that partinstances are derived from
|
||||
/// a single subtree of a shape instance, so the subtree
|
||||
/// is implied (implied by which objects are in the part instance)...
|
||||
/// @{
|
||||
S32 mCurrentObjectDetail;
|
||||
F32 mCurrentIntraDL;
|
||||
/// @}
|
||||
|
||||
Vector<TSShapeInstance::MeshObjectInstance*> mMeshObjects;
|
||||
Vector<TSShapeInstance::DecalObjectInstance*> mDecalObjects;
|
||||
|
||||
static MRandomR250 smRandom;
|
||||
|
||||
void addObject(S32 objectIndex);
|
||||
void updateBounds();
|
||||
|
||||
void renderDetailMap(S32 od);
|
||||
void renderEnvironmentMap(S32 od);
|
||||
void renderFog(S32 od);
|
||||
void renderLightMap(S32 od);
|
||||
|
||||
void init(TSShapeInstance *);
|
||||
|
||||
static void breakShape(TSShapeInstance *, TSPartInstance *, S32 currentNode,
|
||||
Vector<TSPartInstance*> & partList, F32 * probShatter,
|
||||
F32 * probBreak, S32 probDepth);
|
||||
|
||||
/// @name Private Detail Selection Methods
|
||||
/// @{
|
||||
void selectCurrentDetail(F32 * sizeCutoffs, S32 numDetails, bool ignoreScale);
|
||||
void selectCurrentDetail(F32 pixelSize, F32 * sizeCutoffs, S32 numDetails);
|
||||
void computePolyCount();
|
||||
/// @}
|
||||
|
||||
public:
|
||||
|
||||
TSPartInstance(TSShapeInstance * source);
|
||||
TSPartInstance(TSShapeInstance * source, S32 objectIndex);
|
||||
~TSPartInstance();
|
||||
|
||||
const TSShape * getShape() { return mSourceShape->getShape(); }
|
||||
TSShapeInstance * getSourceShapeInstance(){ return mSourceShape; }
|
||||
|
||||
static void breakShape(TSShapeInstance *, S32 subShape, Vector<TSPartInstance*> & partList, F32 * probShatter, F32 * probBreak, S32 probDepth);
|
||||
|
||||
Point3F & getCenter() { return mCenter; }
|
||||
Box3F & getBounds() { return mBounds; }
|
||||
F32 & getRadius() { return mRadius; }
|
||||
|
||||
void render(const Point3F * objectScale = NULL) { render(mCurrentObjectDetail,objectScale); }
|
||||
void render(S32 dl, const Point3F * objectScale = NULL);
|
||||
|
||||
/// choose detail method -- pass in NULL for first parameter to just use shapes data
|
||||
void setDetailData(F32 * sizeCutoffs, S32 numDetails);
|
||||
|
||||
/// @name Detail Selection
|
||||
/// @{
|
||||
void selectCurrentDetail(bool ignoreScale = false);
|
||||
void selectCurrentDetail(F32 pixelSize);
|
||||
void selectCurrentDetail2(F32 adjustedDist);
|
||||
/// @}
|
||||
|
||||
/// @name Detail Information Accessors
|
||||
/// @{
|
||||
F32 getDetailSize(S32 dl);
|
||||
S32 getPolyCount(S32 dl);
|
||||
S32 getNumDetails() { return mSizeCutoffs ? mNumDetails : mSourceShape->getShape()->mSmallestVisibleDL+1; }
|
||||
|
||||
S32 getCurrentObjectDetail() { return mCurrentObjectDetail; }
|
||||
void setCurrentObjectDetail(S32 od) { mCurrentObjectDetail = od; }
|
||||
F32 getCurrentIntraDetail() { return mCurrentIntraDL; }
|
||||
void setCurrentIntraDetail(F32 intra) { mCurrentIntraDL = intra; }
|
||||
/// @}
|
||||
|
||||
void *mData; ///< for use by app
|
||||
};
|
||||
|
||||
#endif
|
||||
|
1723
engine/ts/tsShape.cc
Executable file
1723
engine/ts/tsShape.cc
Executable file
File diff suppressed because it is too large
Load Diff
639
engine/ts/tsShape.h
Executable file
639
engine/ts/tsShape.h
Executable file
@ -0,0 +1,639 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSSHAPE_H_
|
||||
#define _TSSHAPE_H_
|
||||
|
||||
#ifndef _TSMESH_H_
|
||||
#include "ts/tsMesh.h"
|
||||
#endif
|
||||
#ifndef _TSDECAL_H_
|
||||
#include "ts/tsDecal.h"
|
||||
#endif
|
||||
#ifndef _TSINTEGERSET_H_
|
||||
#include "ts/tsIntegerSet.h"
|
||||
#endif
|
||||
#ifndef _TSTRANSFORM_H_
|
||||
#include "ts/tsTransform.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPEALLOC_H_
|
||||
#include "ts/tsShapeAlloc.h"
|
||||
#endif
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _RESMANAGER_H_
|
||||
#include "core/resManager.h"
|
||||
#endif
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _STREAM_H_
|
||||
#include "core/stream.h"
|
||||
#endif
|
||||
|
||||
#define DTS_EXPORTER_CURRENT_VERSION 124
|
||||
|
||||
class TSMaterialList;
|
||||
class TSLastDetail;
|
||||
|
||||
|
||||
/// TSShape stores generic data for a 3space model.
|
||||
///
|
||||
/// TSShape and TSShapeInstance act in conjunction to allow the rendering and
|
||||
/// manipulation of a three dimensional model.
|
||||
///
|
||||
/// @note The material lists are the only thing that is not loaded in TSShape.
|
||||
/// instead, they are loaded in TSShapeInstance because of a former restriction
|
||||
/// on the resource manager where only one file could be opened at a time.
|
||||
/// The resource manager restriction has been resolved, but the material
|
||||
/// lists are still loaded in TSShapeInstance.
|
||||
///
|
||||
/// @see TSShapeInstance for a further discussion of the 3space system.
|
||||
class TSShape : public ResourceInstance
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
UniformScale = BIT(0),
|
||||
AlignedScale = BIT(1),
|
||||
ArbitraryScale = BIT(2),
|
||||
Blend = BIT(3),
|
||||
Cyclic = BIT(4),
|
||||
MakePath = BIT(5),
|
||||
IflInit = BIT(6),
|
||||
HasTranslucency= BIT(7),
|
||||
AnyScale = UniformScale | AlignedScale | ArbitraryScale
|
||||
};
|
||||
|
||||
/// Nodes hold the transforms in the shape's tree. They are the bones of the skeleton.
|
||||
struct Node
|
||||
{
|
||||
S32 nameIndex;
|
||||
S32 parentIndex;
|
||||
|
||||
// computed at runtime
|
||||
S32 firstObject;
|
||||
S32 firstChild;
|
||||
S32 nextSibling;
|
||||
};
|
||||
|
||||
/// Objects hold renderable items (in particular meshes).
|
||||
///
|
||||
/// Each object has a number of meshes associated with it.
|
||||
/// Each mesh corresponds to a different detail level.
|
||||
///
|
||||
/// meshIndicesIndex points to numMeshes consecutive indices
|
||||
/// into the meshList and meshType vectors. It indexes the
|
||||
/// meshIndexList vector (meshIndexList is merely a clearinghouse
|
||||
/// for the object's mesh lists). Some indices may correspond to
|
||||
/// no mesh -- which means no mesh will be drawn for the part for
|
||||
/// the given detail level. See comments on the meshIndexList
|
||||
/// for how null meshes are coded.
|
||||
///
|
||||
/// @note Things are stored this way so that there are no pointers.
|
||||
/// This makes serialization to disk dramatically simpler.
|
||||
struct Object
|
||||
{
|
||||
S32 nameIndex;
|
||||
S32 numMeshes;
|
||||
S32 startMeshIndex; ///< Index into meshes array.
|
||||
S32 nodeIndex;
|
||||
|
||||
// computed at load
|
||||
S32 nextSibling;
|
||||
S32 firstDecal;
|
||||
};
|
||||
|
||||
/// Decals hang off objects like objects hang off nodes. A decal is rendered on
|
||||
/// top of an object (normally will be translucent).
|
||||
///
|
||||
/// @note They hang off objects conceptually...however, in the shapeInstance they
|
||||
/// are in their own list and that list is rendered after all the objects are.
|
||||
struct Decal
|
||||
{
|
||||
S32 nameIndex;
|
||||
S32 numMeshes;
|
||||
S32 startMeshIndex; ///< Index into meshes array.
|
||||
S32 objectIndex;
|
||||
|
||||
// computed at load
|
||||
S32 nextSibling;
|
||||
};
|
||||
|
||||
/// IFL Materials are used to animate material lists -- i.e., run through a series
|
||||
/// of frames of a material.
|
||||
///
|
||||
/// They work by replacing a material in the material
|
||||
/// list so that it is transparent to the rest of the code.
|
||||
/// Offset time of each frame is stored in iflFrameOffsets vector, starting at index position
|
||||
/// firstFrameOffsetIndex..
|
||||
struct IflMaterial
|
||||
{
|
||||
S32 nameIndex; ///< File name with extension.
|
||||
S32 materialSlot;
|
||||
S32 firstFrame;
|
||||
S32 firstFrameOffTimeIndex;
|
||||
S32 numFrames;
|
||||
};
|
||||
|
||||
/// A Sequence holds all the information necessary to perform a particular animation (sequence).
|
||||
///
|
||||
/// Sequences index a range of keyframes. Keyframes are assumed to be equally spaced in time.
|
||||
///
|
||||
/// Each node and object is either a member of the sequence or not. If not, they are set to
|
||||
/// default values when we switch to the sequence unless they are members of some other active sequence.
|
||||
/// Blended sequences "add" a transform to the current transform of a node. Any object animation of
|
||||
/// a blended sequence over-rides any existing object state. Blended sequences are always
|
||||
/// applied after non-blended sequences.
|
||||
struct Sequence
|
||||
{
|
||||
S32 nameIndex;
|
||||
S32 numKeyframes;
|
||||
F32 duration;
|
||||
S32 baseRotation;
|
||||
S32 baseTranslation;
|
||||
S32 baseScale;
|
||||
S32 baseObjectState;
|
||||
S32 baseDecalState;
|
||||
S32 firstGroundFrame;
|
||||
S32 numGroundFrames;
|
||||
S32 firstTrigger;
|
||||
S32 numTriggers;
|
||||
F32 toolBegin;
|
||||
|
||||
/// @name Bitsets
|
||||
/// These bitsets code whether this sequence cares about certain aspects of animation
|
||||
/// e.g., the rotation, translation, or scale of node transforms,
|
||||
/// or the visibility, frame or material frame of objects.
|
||||
/// @{
|
||||
|
||||
TSIntegerSet rotationMatters; ///< Set of nodes
|
||||
TSIntegerSet translationMatters; ///< Set of nodes
|
||||
TSIntegerSet scaleMatters; ///< Set of nodes
|
||||
TSIntegerSet visMatters; ///< Set of objects
|
||||
TSIntegerSet frameMatters; ///< Set of objects
|
||||
TSIntegerSet matFrameMatters; ///< Set of objects
|
||||
TSIntegerSet decalMatters; ///< Set of decals
|
||||
TSIntegerSet iflMatters; ///< Set of IFLs
|
||||
/// @}
|
||||
|
||||
S32 priority;
|
||||
U32 flags;
|
||||
U32 dirtyFlags; ///< determined at load time
|
||||
|
||||
/// @name Flag Tests
|
||||
/// Each of these tests a different flag against the object's flag list
|
||||
/// to determine the attributes of the given object.
|
||||
/// @{
|
||||
|
||||
bool testFlags(U32 comp) const { return (flags&comp)!=0; }
|
||||
bool animatesScale() const { return testFlags(AnyScale); }
|
||||
bool animatesUniformScale() const { return testFlags(UniformScale); }
|
||||
bool animatesAlignedScale() const { return testFlags(AlignedScale); }
|
||||
bool animatesArbitraryScale() const { return testFlags(ArbitraryScale); }
|
||||
bool isBlend() const { return testFlags(Blend); }
|
||||
bool isCyclic() const { return testFlags(Cyclic); }
|
||||
bool makePath() const { return testFlags(MakePath); }
|
||||
/// @}
|
||||
|
||||
/// @name IO
|
||||
/// @{
|
||||
|
||||
void read(Stream *, bool readNameIndex = true);
|
||||
void write(Stream *, bool writeNameIndex = true);
|
||||
/// @}
|
||||
};
|
||||
|
||||
/// Describes state of an individual object. Includes everything in an object that can be
|
||||
/// controlled by animation.
|
||||
struct ObjectState
|
||||
{
|
||||
F32 vis;
|
||||
S32 frameIndex;
|
||||
S32 matFrameIndex;
|
||||
};
|
||||
|
||||
/// Describes state of a decal.
|
||||
struct DecalState
|
||||
{
|
||||
S32 frameIndex;
|
||||
};
|
||||
|
||||
/// When time on a sequence advances past a certain point, a trigger takes effect and changes
|
||||
/// one of the state variables to on or off. (State variables found in TSShapeInstance::mTriggerStates)
|
||||
struct Trigger
|
||||
{
|
||||
enum TriggerStates {
|
||||
StateOn = BIT(31),
|
||||
InvertOnReverse = BIT(30),
|
||||
StateMask = BIT(30)-1
|
||||
};
|
||||
|
||||
U32 state; ///< One of TriggerStates
|
||||
F32 pos;
|
||||
};
|
||||
|
||||
/// Details are used for render detail selection.
|
||||
///
|
||||
/// As the projected size of the shape changes,
|
||||
/// a different node structure can be used (subShape) and a different objectDetail can be selected
|
||||
/// for each object drawn. Either of these two parameters can also stay constant, but presumably
|
||||
/// not both. If size is negative then the detail level will never be selected by the standard
|
||||
/// detail selection process. It will have to be selected by name. Such details are "utility
|
||||
/// details" because they exist to hold data (node positions or collision information) but not
|
||||
/// normally to be drawn. By default there will always be a "Ground" utility detail.
|
||||
struct Detail
|
||||
{
|
||||
S32 nameIndex;
|
||||
S32 subShapeNum;
|
||||
S32 objectDetailNum;
|
||||
F32 size;
|
||||
F32 averageError;
|
||||
F32 maxError;
|
||||
S32 polyCount;
|
||||
};
|
||||
|
||||
/// @name Collision Accelerators
|
||||
///
|
||||
/// For speeding up buildpolylist and support calls.
|
||||
/// @{
|
||||
struct ConvexHullAccelerator {
|
||||
S32 numVerts;
|
||||
Point3F* vertexList;
|
||||
Point3F* normalList;
|
||||
U8** emitStrings;
|
||||
};
|
||||
ConvexHullAccelerator* getAccelerator(S32 dl);
|
||||
/// @}
|
||||
|
||||
|
||||
/// @name Shape Vector Data
|
||||
/// @{
|
||||
|
||||
ToolVector<Node> nodes;
|
||||
ToolVector<Object> objects;
|
||||
ToolVector<Decal> decals;
|
||||
ToolVector<IflMaterial> iflMaterials;
|
||||
ToolVector<ObjectState> objectStates;
|
||||
ToolVector<DecalState> decalStates;
|
||||
ToolVector<S32> subShapeFirstNode;
|
||||
ToolVector<S32> subShapeFirstObject;
|
||||
ToolVector<S32> subShapeFirstDecal;
|
||||
ToolVector<S32> detailFirstSkin;
|
||||
ToolVector<S32> subShapeNumNodes;
|
||||
ToolVector<S32> subShapeNumObjects;
|
||||
ToolVector<S32> subShapeNumDecals;
|
||||
ToolVector<Detail> details;
|
||||
ToolVector<Quat16> defaultRotations;
|
||||
ToolVector<Point3F> defaultTranslations;
|
||||
|
||||
/// @}
|
||||
|
||||
/// These are set up at load time, but memory is allocated along with loaded data
|
||||
/// @{
|
||||
|
||||
ToolVector<S32> subShapeFirstTranslucentObject;
|
||||
ToolVector<TSMesh*> meshes;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Alpha Vectors
|
||||
/// these vectors describe how to transition between detail
|
||||
/// levels using alpha. "alpha-in" next detail as intraDL goes
|
||||
/// from alphaIn+alphaOut to alphaOut. "alpha-out" current
|
||||
/// detail level as intraDL goes from alphaOut to 0.
|
||||
/// @note
|
||||
/// - intraDL is at 1 when if shape were any closer to us we'd be at dl-1
|
||||
/// - intraDL is at 0 when if shape were any farther away we'd be at dl+1
|
||||
/// @{
|
||||
|
||||
ToolVector<F32> alphaIn;
|
||||
ToolVector<F32> alphaOut
|
||||
;
|
||||
/// @}
|
||||
|
||||
/// @name Resizeable vectors
|
||||
/// @{
|
||||
|
||||
Vector<Sequence> sequences;
|
||||
Vector<Quat16> nodeRotations;
|
||||
Vector<Point3F> nodeTranslations;
|
||||
Vector<F32> nodeUniformScales;
|
||||
Vector<Point3F> nodeAlignedScales;
|
||||
Vector<Quat16> nodeArbitraryScaleRots;
|
||||
Vector<Point3F> nodeArbitraryScaleFactors;
|
||||
Vector<Quat16> groundRotations;
|
||||
Vector<Point3F> groundTranslations;
|
||||
Vector<Trigger> triggers;
|
||||
Vector<F32> iflFrameOffTimes;
|
||||
Vector<TSLastDetail*> billboardDetails;
|
||||
Vector<ConvexHullAccelerator*> detailCollisionAccelerators;
|
||||
Vector<const char *> names;
|
||||
/// @}
|
||||
|
||||
/// Memory block for data storage.
|
||||
///
|
||||
/// Most vectors are stored in a single memory block
|
||||
/// except when compiled using TORQUE_LIB defined.
|
||||
///
|
||||
/// in that case, ToolVector becomes Vector<> and the
|
||||
/// vectors are resizeable
|
||||
S8 * mMemoryBlock;
|
||||
|
||||
TSMaterialList * materialList;
|
||||
|
||||
/// @name Bounding
|
||||
/// @{
|
||||
|
||||
F32 radius;
|
||||
F32 tubeRadius;
|
||||
Point3F center;
|
||||
Box3F bounds;
|
||||
|
||||
/// @}
|
||||
|
||||
// various...
|
||||
U32 mExporterVersion;
|
||||
F32 mSmallestVisibleSize; ///< Computed at load time from details vector.
|
||||
S32 mSmallestVisibleDL; ///< @see mSmallestVisibleSize
|
||||
S32 mReadVersion; ///< File version that this shape was read from.
|
||||
U32 mFlags; ///< hasTranslucancy, iflInit
|
||||
U32 data; ///< User-defined data storage.
|
||||
|
||||
bool mSequencesConstructed;
|
||||
S32 mVertexBuffer;
|
||||
U32 mCallbackKey;
|
||||
bool mExportMerge;
|
||||
bool mMorphable;
|
||||
Vector<S32> mPreviousMerge;
|
||||
S32 mMergeBufferSize;
|
||||
|
||||
// shape class has few methods --
|
||||
// just constructor/destructor, io, and lookup methods
|
||||
|
||||
// constructor/destructor
|
||||
TSShape();
|
||||
~TSShape();
|
||||
void init();
|
||||
void initMaterialList(); ///< you can swap in a new material list, but call this if you do
|
||||
bool preloadMaterialList(); ///< called to preload and validate the materials in the mat list
|
||||
|
||||
void clearDynamicData();
|
||||
void setupBillboardDetails(TSShapeInstance *);
|
||||
|
||||
bool getSequencesConstructed() const { return mSequencesConstructed; }
|
||||
void setSequencesConstructed(const bool c) { mSequencesConstructed = c; }
|
||||
|
||||
/// @name Lookup Animation Info
|
||||
/// indexed by keyframe number and offset (which objecct/node/decal
|
||||
/// of the animated objects/nodes/decals you want information for).
|
||||
/// @{
|
||||
|
||||
QuatF & getRotation(const Sequence & seq, S32 keyframeNum, S32 rotNum, QuatF *) const;
|
||||
const Point3F & getTranslation(const Sequence & seq, S32 keyframeNum, S32 tranNum) const;
|
||||
F32 getUniformScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const;
|
||||
const Point3F & getAlignedScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const;
|
||||
TSScale & getArbitraryScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum, TSScale *) const;
|
||||
const ObjectState & getObjectState(const Sequence & seq, S32 keyframeNum, S32 objectNum) const;
|
||||
const DecalState & getDecalState(const Sequence & seq, S32 keyframeNum, S32 decalNum) const;
|
||||
/// @}
|
||||
|
||||
/// build LOS collision detail
|
||||
void computeAccelerator(S32 dl);
|
||||
bool buildConvexHull(S32 dl) const;
|
||||
void computeBounds(S32 dl, Box3F & bounds) const; // uses default transforms to compute bounding box around a detail level
|
||||
// see like named method on shapeInstance if you want to use animated transforms
|
||||
|
||||
/// @name Lookup Methods
|
||||
/// @{
|
||||
|
||||
S32 findName(const char *) const;
|
||||
const char * getName(S32) const;
|
||||
|
||||
S32 findNode(S32 nameIndex) const;
|
||||
S32 findNode(const char * name) const { return findNode(findName(name)); }
|
||||
|
||||
S32 findObject(S32 nameIndex) const;
|
||||
S32 findObject(const char * name) const { return findObject(findName(name)); }
|
||||
|
||||
S32 findDecal(S32 nameIndex) const;
|
||||
S32 findDecal(const char * name) const { return findDecal(findName(name)); }
|
||||
|
||||
S32 findIflMaterial(S32 nameIndex) const;
|
||||
S32 findIflMaterial(const char * name) const { return findIflMaterial(findName(name)); }
|
||||
|
||||
S32 findDetail(S32 nameIndex) const;
|
||||
S32 findDetail(const char * name) const { return findDetail(findName(name)); }
|
||||
|
||||
S32 findSequence(S32 nameIndex) const;
|
||||
S32 findSequence(const char * name) const { return findSequence(findName(name)); }
|
||||
|
||||
bool hasTranslucency() const { return (mFlags & HasTranslucency)!=0; }
|
||||
/// @}
|
||||
|
||||
/// @name Alpha Transitions
|
||||
/// These control default values for alpha transitions between detail levels
|
||||
/// @{
|
||||
|
||||
static F32 smAlphaOutLastDetail;
|
||||
static F32 smAlphaInBillboard;
|
||||
static F32 smAlphaOutBillboard;
|
||||
static F32 smAlphaInDefault;
|
||||
static F32 smAlphaOutDefault;
|
||||
/// @}
|
||||
|
||||
/// don't load this many of the highest detail levels (although we always
|
||||
/// load one renderable detail if there is one)
|
||||
static S32 smNumSkipLoadDetails;
|
||||
|
||||
/// by default we initialize shape when we read...
|
||||
static bool smInitOnRead;
|
||||
|
||||
/// @name Version Info
|
||||
/// @{
|
||||
|
||||
/// Most recent version...the one we write
|
||||
static S32 smVersion;
|
||||
/// Version currently being read, only valid during read
|
||||
static S32 smReadVersion;
|
||||
static const U32 smMostRecentExporterVersion;
|
||||
///@}
|
||||
|
||||
/// @name Persist Methods
|
||||
/// Methods for saving/loading shapes to/from streams
|
||||
/// @{
|
||||
|
||||
void write(Stream *);
|
||||
bool read(Stream *);
|
||||
void readOldShape(Stream * s, S32 * &, S16 * &, S8 * &, S32 &, S32 &, S32 &);
|
||||
void writeName(Stream *, S32 nameIndex);
|
||||
S32 readName(Stream *, bool addName);
|
||||
|
||||
void exportSequences(Stream *);
|
||||
bool importSequences(Stream *);
|
||||
|
||||
void readIflMaterials(const char* shapePath);
|
||||
/// @}
|
||||
|
||||
/// @name Persist Helper Functions
|
||||
/// @{
|
||||
|
||||
static TSShapeAlloc alloc;
|
||||
void fixEndian(S32 *, S16 *, S8 *, S32, S32, S32);
|
||||
/// @}
|
||||
|
||||
/// @name Memory Buffer Transfer Methods
|
||||
/// uses TSShape::Alloc structure
|
||||
/// @{
|
||||
|
||||
void assembleShape();
|
||||
void disassembleShape();
|
||||
///@}
|
||||
|
||||
/// mem buffer transfer helper (indicate when we don't want to include a particular mesh/decal)
|
||||
bool checkSkip(S32 meshNum, S32 & curObject, S32 & curDecal, S32 skipDL);
|
||||
|
||||
/// used when reading old shapes/sequences
|
||||
void rearrangeKeyframeData(Sequence &, S32 keyframeStart, U8 * pns32 = NULL, U8 * pns16 = NULL, U8 * pos = NULL, U8 * pds = NULL, S32 szNS32=-1, S32 szNS16=-1, S32 szOS32=-1, S32 szDS32=-1);
|
||||
void rearrangeStates(S32 start, S32 rows, S32 cols, U8 * data, S32 size);
|
||||
|
||||
void fixupOldSkins(S32 numMeshes, S32 numSkins, S32 numDetails, S32 * detailFirstSkin, S32 * detailNumSkins);
|
||||
};
|
||||
|
||||
/// Specialized material list for 3space objects
|
||||
///
|
||||
/// @note Reflectance amounts on 3space objects are determined by
|
||||
/// the alpha channel of the base material texture
|
||||
class TSMaterialList : public MaterialList
|
||||
{
|
||||
typedef MaterialList Parent;
|
||||
|
||||
Vector<U32> mFlags;
|
||||
// Additional textures
|
||||
Vector<U32> mReflectanceMaps;
|
||||
Vector<U32> mBumpMaps;
|
||||
Vector<U32> mDetailMaps;
|
||||
Vector<U32> mLightMaps;
|
||||
// Additional texture info
|
||||
Vector<F32> mDetailScales;
|
||||
Vector<F32> mReflectionAmounts;
|
||||
|
||||
bool mNamesTransformed;
|
||||
|
||||
void allocate(U32 sz);
|
||||
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
S_Wrap = BIT(0),
|
||||
T_Wrap = BIT(1),
|
||||
Translucent = BIT(2),
|
||||
Additive = BIT(3),
|
||||
Subtractive = BIT(4),
|
||||
SelfIlluminating = BIT(5),
|
||||
NeverEnvMap = BIT(6),
|
||||
NoMipMap = BIT(7),
|
||||
MipMap_ZeroBorder = BIT(8),
|
||||
IflMaterial = BIT(27),
|
||||
IflFrame = BIT(28),
|
||||
DetailMapOnly = BIT(29),
|
||||
BumpMapOnly = BIT(30),
|
||||
ReflectanceMapOnly = BIT(31),
|
||||
AuxiliaryMap = DetailMapOnly | BumpMapOnly | ReflectanceMapOnly
|
||||
};
|
||||
|
||||
TSMaterialList(U32 materialCount, const char **materialNames, const U32 * materialFlags,
|
||||
const U32 * reflectanceMaps, const U32 * bumpMaps, const U32 * detailMaps,
|
||||
const F32 * detailScales, const F32 * reflectionAmounts);
|
||||
TSMaterialList(U32 materialCount, const char **materialNames, const U32 * materialFlags,
|
||||
const U32 * reflectanceMaps, const U32 * bumpMaps, const U32 * detailMaps, const U32 * lightMaps,
|
||||
const F32 * detailScales, const F32 * reflectionAmounts);
|
||||
TSMaterialList();
|
||||
TSMaterialList(const TSMaterialList*);
|
||||
~TSMaterialList();
|
||||
void free();
|
||||
|
||||
void load(U32 index, const char* path = 0);
|
||||
bool load(TextureHandleType type, const char* path = 0,bool clampToEdge = false) { return Parent::load(type,path,clampToEdge); }
|
||||
|
||||
/// @name Lookups
|
||||
/// @{
|
||||
|
||||
TextureHandle * getReflectionMap(U32 index) { return mReflectanceMaps[index] == 0xFFFFFFFF ? NULL : &getMaterial(mReflectanceMaps[index]); }
|
||||
F32 getReflectionAmount(U32 index) { return mReflectionAmounts[index]; }
|
||||
TextureHandle * getBumpMap(U32 index) { return mBumpMaps[index] == 0xFFFFFFFF ? NULL : &getMaterial(mBumpMaps[index]); }
|
||||
TextureHandle * getDetailMap(U32 index) { return mDetailMaps[index] == 0xFFFFFFFF ? NULL : &getMaterial(mDetailMaps[index]); }
|
||||
TextureHandle * getLightMap(U32 index) { return mLightMaps[index] == 0xFFFFFFFF ? NULL : &getMaterial(mLightMaps[index]); }
|
||||
F32 getDetailMapScale(U32 index) { return mDetailScales[index]; }
|
||||
bool reflectionInAlpha(U32 index) { return mReflectanceMaps[index] == index; }
|
||||
/// @}
|
||||
|
||||
U32 getFlags(U32 index);
|
||||
void setFlags(U32 index, U32 value);
|
||||
|
||||
void remap(U32 toIndex, U32 fromIndex); ///< support for ifl sequences
|
||||
|
||||
/// pre-load only ... support for ifl sequences
|
||||
void push_back(const char * name, U32 flags,
|
||||
U32 a=0xFFFFFFFF, U32 b=0xFFFFFFFF, U32 c=0xFFFFFFFF,
|
||||
F32 dm=1.0f, F32 em=1.0f,
|
||||
U32 l=0xFFFFFFFF);
|
||||
|
||||
/// @name IO
|
||||
/// Functions for reading/writing to/from streams
|
||||
/// @{
|
||||
|
||||
bool write(Stream &);
|
||||
bool read(Stream &);
|
||||
/// @}
|
||||
};
|
||||
|
||||
extern ResourceInstance *constructTSShape(Stream &stream);
|
||||
|
||||
#define TSNode TSShape::Node
|
||||
#define TSObject TSShape::Object
|
||||
#define TSDecal TSShape::Decal
|
||||
#define TSSequence TSShape::Sequence
|
||||
#define TSDetail TSShape::Detail
|
||||
|
||||
inline QuatF & TSShape::getRotation(const Sequence & seq, S32 keyframeNum, S32 rotNum, QuatF * quat) const
|
||||
{
|
||||
return nodeRotations[seq.baseRotation + rotNum*seq.numKeyframes + keyframeNum].getQuatF(quat);
|
||||
}
|
||||
|
||||
inline const Point3F & TSShape::getTranslation(const Sequence & seq, S32 keyframeNum, S32 tranNum) const
|
||||
{
|
||||
return nodeTranslations[seq.baseTranslation + tranNum*seq.numKeyframes + keyframeNum];
|
||||
}
|
||||
|
||||
inline F32 TSShape::getUniformScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const
|
||||
{
|
||||
return nodeUniformScales[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum];
|
||||
}
|
||||
|
||||
inline const Point3F & TSShape::getAlignedScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const
|
||||
{
|
||||
return nodeAlignedScales[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum];
|
||||
}
|
||||
|
||||
inline TSScale & TSShape::getArbitraryScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum, TSScale * scale) const
|
||||
{
|
||||
nodeArbitraryScaleRots[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum].getQuatF(&scale->mRotate);
|
||||
scale->mScale = nodeArbitraryScaleFactors[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum];
|
||||
return *scale;
|
||||
}
|
||||
|
||||
inline const TSShape::ObjectState & TSShape::getObjectState(const Sequence & seq, S32 keyframeNum, S32 objectNum) const
|
||||
{
|
||||
return objectStates[seq.baseObjectState + objectNum*seq.numKeyframes + keyframeNum];
|
||||
}
|
||||
|
||||
inline const TSShape::DecalState & TSShape::getDecalState(const Sequence & seq, S32 keyframeNum, S32 decalNum) const
|
||||
{
|
||||
return decalStates[seq.baseDecalState + decalNum*seq.numKeyframes + keyframeNum];
|
||||
}
|
||||
|
||||
#endif
|
209
engine/ts/tsShapeAlloc.cc
Executable file
209
engine/ts/tsShapeAlloc.cc
Executable file
@ -0,0 +1,209 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsShapeAlloc.h"
|
||||
|
||||
#define readOnly() AssertFatal(mMode==TSShapeAlloc::ReadMode, "TSShapeAlloc: write-only function called when reading")
|
||||
#define writeOnly() AssertFatal(mMode==TSShapeAlloc::WriteMode,"TSShapeAlloc: read-only function called when writing")
|
||||
|
||||
void TSShapeAlloc::setRead(S32 * memBuffer32, S16 * memBuffer16, S8 * memBuffer8, bool clear)
|
||||
{
|
||||
mMemBuffer32 = memBuffer32;
|
||||
mMemBuffer16 = memBuffer16;
|
||||
mMemBuffer8 = memBuffer8 ;
|
||||
|
||||
mMemGuard32 = 0;
|
||||
mMemGuard16 = 0;
|
||||
mMemGuard8 = 0;
|
||||
|
||||
mSaveGuard32 = 0;
|
||||
mSaveGuard16 = 0;
|
||||
mSaveGuard8 = 0;
|
||||
|
||||
if (clear)
|
||||
{
|
||||
mDest = NULL;
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
setSkipMode(false);
|
||||
mMode = TSShapeAlloc::ReadMode;
|
||||
}
|
||||
|
||||
void TSShapeAlloc::setWrite()
|
||||
{
|
||||
mMemBuffer32 = 0;
|
||||
mMemBuffer16 = 0;
|
||||
mMemBuffer8 = 0;
|
||||
|
||||
mSize32 = mFullSize32 = 0;
|
||||
mSize16 = mFullSize16 = 0;
|
||||
mSize8 = mFullSize8 = 0;
|
||||
|
||||
mMemGuard32 = 0;
|
||||
mMemGuard16 = 0;
|
||||
mMemGuard8 = 0;
|
||||
|
||||
setSkipMode(false); // doesn't really do anything here...
|
||||
mMode = TSShapeAlloc::WriteMode;
|
||||
}
|
||||
|
||||
void TSShapeAlloc::doAlloc()
|
||||
{
|
||||
readOnly();
|
||||
|
||||
mDest = new S8[mSize];
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
void TSShapeAlloc::align32()
|
||||
{
|
||||
readOnly();
|
||||
|
||||
S32 aligned = mSize+3 & (~0x3);
|
||||
allocShape8(aligned-mSize);
|
||||
}
|
||||
|
||||
#define IMPLEMENT_ALLOC(suffix,type) \
|
||||
\
|
||||
type TSShapeAlloc::get##suffix() \
|
||||
{ \
|
||||
readOnly(); \
|
||||
return *(mMemBuffer##suffix++); \
|
||||
} \
|
||||
\
|
||||
void TSShapeAlloc::get##suffix(type * dest, S32 num) \
|
||||
{ \
|
||||
readOnly(); \
|
||||
dMemcpy(dest,mMemBuffer##suffix,sizeof(type)*num); \
|
||||
mMemBuffer##suffix += num; \
|
||||
} \
|
||||
\
|
||||
type * TSShapeAlloc::allocShape##suffix(S32 num) \
|
||||
{ \
|
||||
readOnly(); \
|
||||
type * ret = (type*) mDest; \
|
||||
if (mDest) \
|
||||
mDest += mMult*num*sizeof(type); \
|
||||
mSize += sizeof(type)*mMult*num; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
type * TSShapeAlloc::getPointer##suffix(S32 num) \
|
||||
{ \
|
||||
readOnly(); \
|
||||
type * ret = (type*)mMemBuffer##suffix; \
|
||||
mMemBuffer##suffix += num; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
type * TSShapeAlloc::copyToShape##suffix(S32 num, bool returnSomething) \
|
||||
{ \
|
||||
readOnly(); \
|
||||
type * ret = (!returnSomething || mDest) ? (type*)mDest : mMemBuffer##suffix; \
|
||||
if (mDest) \
|
||||
{ \
|
||||
dMemcpy((S8*)mDest,(S8*)mMemBuffer##suffix, \
|
||||
mMult*num*sizeof(type)); \
|
||||
mDest += mMult*num*sizeof(type); \
|
||||
} \
|
||||
mMemBuffer##suffix += num; \
|
||||
mSize += sizeof(type)*mMult*num; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
bool TSShapeAlloc::checkGuard##suffix() \
|
||||
{ \
|
||||
readOnly(); \
|
||||
mSaveGuard##suffix=get##suffix(); \
|
||||
bool ret = (mSaveGuard##suffix==mMemGuard##suffix); \
|
||||
mMemGuard##suffix += 1; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
type TSShapeAlloc::getPrevGuard##suffix() \
|
||||
{ \
|
||||
readOnly(); \
|
||||
return mMemGuard##suffix - 1; \
|
||||
} \
|
||||
\
|
||||
type TSShapeAlloc::getSaveGuard##suffix() \
|
||||
{ \
|
||||
readOnly(); \
|
||||
return mSaveGuard##suffix; \
|
||||
} \
|
||||
\
|
||||
type * TSShapeAlloc::getBuffer##suffix() \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
return mMemBuffer##suffix; \
|
||||
} \
|
||||
\
|
||||
S32 TSShapeAlloc::getBufferSize##suffix() \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
return mSize##suffix; \
|
||||
} \
|
||||
\
|
||||
type * TSShapeAlloc::extend##suffix(S32 add) \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
if (mSize##suffix+add>mFullSize##suffix) \
|
||||
{ \
|
||||
S32 numPages = 1+(mFullSize##suffix+add)/TSShapeAlloc::PageSize; \
|
||||
mFullSize##suffix = numPages*TSShapeAlloc::PageSize; \
|
||||
type * temp = new type[mFullSize##suffix]; \
|
||||
dMemcpy(temp,mMemBuffer##suffix, mSize##suffix * sizeof(type)); \
|
||||
delete [] mMemBuffer##suffix; \
|
||||
mMemBuffer##suffix = temp; \
|
||||
} \
|
||||
type * ret = mMemBuffer##suffix + mSize##suffix; \
|
||||
mSize##suffix += add; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
type TSShapeAlloc::set##suffix(type entry) \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
*extend##suffix(1) = entry; \
|
||||
return entry; \
|
||||
} \
|
||||
\
|
||||
void TSShapeAlloc::copyToBuffer##suffix(type * entries, S32 count) \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
dMemcpy((U8*)extend##suffix(count),(U8*)entries,count*sizeof(type)); \
|
||||
} \
|
||||
\
|
||||
void TSShapeAlloc::setGuard##suffix() \
|
||||
{ \
|
||||
writeOnly(); \
|
||||
set##suffix(mMemGuard##suffix); \
|
||||
mMemGuard##suffix += 1; \
|
||||
}
|
||||
|
||||
IMPLEMENT_ALLOC(32,S32)
|
||||
IMPLEMENT_ALLOC(16,S16)
|
||||
IMPLEMENT_ALLOC(8,S8)
|
||||
|
||||
void TSShapeAlloc::checkGuard()
|
||||
{
|
||||
bool check32 = checkGuard32();
|
||||
bool check16 = checkGuard16();
|
||||
bool check8 = checkGuard8();
|
||||
|
||||
AssertFatal(check32,avar("TSShapeAlloc::checkGuard32: found %i, wanted %i",getSaveGuard32(),getPrevGuard32()));
|
||||
AssertFatal(check16,avar("TSShapeAlloc::checkGuard16: found %i, wanted %i",getSaveGuard16(),getPrevGuard16()));
|
||||
AssertFatal(check8 ,avar("TSShapeAlloc::checkGuard8: found %i, wanted %i",getSaveGuard8() ,getPrevGuard8()));
|
||||
}
|
||||
|
||||
void TSShapeAlloc::setGuard()
|
||||
{
|
||||
setGuard32();
|
||||
setGuard16();
|
||||
setGuard8();
|
||||
}
|
||||
|
||||
|
139
engine/ts/tsShapeAlloc.h
Executable file
139
engine/ts/tsShapeAlloc.h
Executable file
@ -0,0 +1,139 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSSHAPEALLOC_H_
|
||||
#define _TSSHAPEALLOC_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
|
||||
/// Alloc structure used in the reading/writing of shapes.
|
||||
///
|
||||
/// In read mode we assemble contents of 32-bit, 16-bit, and 8-bit buffers
|
||||
/// into a single destination buffer.
|
||||
///
|
||||
/// In write mode we dissemble a stream of memory (which may be scattered in physical memory)
|
||||
/// into 32-bit, 16-bit, 8-bit, Point3F, and Point2F buffers using function calls similar
|
||||
/// to the read calls.
|
||||
///
|
||||
/// Read usage:
|
||||
/// 1. call "setRead" with each incoming memory buffers and clear=true.
|
||||
/// 2. run through set of operations for allocating and transfering memory to target buffer
|
||||
/// these are the operations under "DECLARE_ALLOC" that call readOnly in the .cc file.
|
||||
/// 3. call "doAlloc" to create buffer exactly as large as needed.
|
||||
/// 4. repeat step 1 & 2 to do the actual transfer of memory, except with clear=false
|
||||
/// (note: first time through nothing was copied to the shape, we only kept track
|
||||
/// of the size of the transfer).
|
||||
/// 5. call getBuffer to get the target (destination buffer)
|
||||
///
|
||||
/// write usage:
|
||||
/// 1. call "setWrite" (no parameters).
|
||||
/// 2. run through set of operations for allocating and transfering memory to internal buffers
|
||||
/// these are the operations under "DECLARE_ALLOC" that call writeOnly in the .cc file.
|
||||
/// 3. call getBuffer32 and getBufferSize32 to get 32-bit buffer and size. Similarly for
|
||||
/// 16-bit, 8-bit (getBuffer16, getBuffer8).
|
||||
///
|
||||
/// TSShape::assesmbleShape and TSShape::dissembleShape can be used as examples
|
||||
class TSShapeAlloc
|
||||
{
|
||||
S32 mMode; ///< read or write
|
||||
|
||||
/// reading and writing (when reading these are the input; when writing these are the output)
|
||||
S32 * mMemBuffer32;
|
||||
S16 * mMemBuffer16;
|
||||
S8 * mMemBuffer8;
|
||||
|
||||
/// for writing only...
|
||||
S32 mSize32;
|
||||
S32 mSize16;
|
||||
S32 mSize8;
|
||||
S32 mFullSize32;
|
||||
S32 mFullSize16;
|
||||
S32 mFullSize8;
|
||||
|
||||
/// reading and writing...
|
||||
S32 mMemGuard32;
|
||||
S16 mMemGuard16;
|
||||
S8 mMemGuard8;
|
||||
|
||||
/// reading
|
||||
S32 mSaveGuard32;
|
||||
S16 mSaveGuard16;
|
||||
S8 mSaveGuard8;
|
||||
|
||||
/// reading only...this is the output
|
||||
S8 * mDest;
|
||||
S32 mSize;
|
||||
S32 mMult; ///< mult incoming sizes by this (when 0, then mDest doesn't grow --> skip mode)
|
||||
|
||||
public:
|
||||
|
||||
enum { ReadMode = 0, WriteMode = 1, PageSize = 1024 }; ///< PageSize must be multiple of 4 so that we can always
|
||||
///< "over-read" up to next dword
|
||||
|
||||
void setRead(S32 * buff32, S16 * buff16, S8 * buff8, bool clear);
|
||||
void setWrite();
|
||||
|
||||
// reading only...
|
||||
void doAlloc();
|
||||
void align32(); ///< align on dword boundary
|
||||
S8 * getBuffer() { return mDest; }
|
||||
S32 getSize() { return mSize; }
|
||||
void setSkipMode(bool skip) { mMult = skip ? 0 : 1; }
|
||||
|
||||
/// @name Reading Operations:
|
||||
///
|
||||
/// get(): reads one or more entries of type from input buffer (doesn't affect output buffer)
|
||||
///
|
||||
/// copyToShape(): copies entries of type from input buffer to output buffer
|
||||
///
|
||||
/// allocShape(): creates room for entries of type in output buffer (no effect on input buffer)
|
||||
///
|
||||
/// getPointer(): gets pointer to next entries of type in input buffer (no effect on input buffer)
|
||||
///
|
||||
/// @note all operations advance current "position" of input and output buffers
|
||||
/// writing operations:
|
||||
///
|
||||
/// set(): adds one entry to appropriate buffer
|
||||
///
|
||||
/// copyToBuffer(): adds count entries to approrpiate buffer
|
||||
///
|
||||
/// getBuffer(): returns associated buffer (i.e., getBuffer32 gets 32bit buffer)
|
||||
///
|
||||
/// getBufferSize(): returns size of associated buffer
|
||||
///
|
||||
/// @{
|
||||
|
||||
#define DECLARE_ALLOC(suffix,type) \
|
||||
type get##suffix(); \
|
||||
void get##suffix(type*,S32); \
|
||||
type * copyToShape##suffix(S32,bool returnSomething=false); \
|
||||
type * getPointer##suffix(S32); \
|
||||
type * allocShape##suffix(S32); \
|
||||
bool checkGuard##suffix(); \
|
||||
type getPrevGuard##suffix(); \
|
||||
type getSaveGuard##suffix(); \
|
||||
type * getBuffer##suffix(); \
|
||||
S32 getBufferSize##suffix(); \
|
||||
void setGuard##suffix(); \
|
||||
type * extend##suffix(S32); \
|
||||
type set##suffix(type); \
|
||||
void copyToBuffer##suffix(type*,S32);
|
||||
|
||||
|
||||
DECLARE_ALLOC(32,S32)
|
||||
DECLARE_ALLOC(16,S16)
|
||||
DECLARE_ALLOC(8,S8)
|
||||
/// @}
|
||||
|
||||
void checkGuard();
|
||||
void setGuard();
|
||||
};
|
||||
|
||||
#endif // _H_TS_SHAPE_ALLOC_
|
190
engine/ts/tsShapeConstruct.cc
Executable file
190
engine/ts/tsShapeConstruct.cc
Executable file
@ -0,0 +1,190 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsShapeConstruct.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/fileStream.h"
|
||||
#include "core/bitStream.h"
|
||||
|
||||
#define TS_IS_RELATIVE_SEQUENCES 0
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(TSShapeConstructor);
|
||||
|
||||
S32 gNumTransforms = 0;
|
||||
S32 gRotTransforms = 0;
|
||||
S32 gTransTransforms = 0;
|
||||
|
||||
TSShapeConstructor::TSShapeConstructor()
|
||||
{
|
||||
mShape = NULL;
|
||||
for (S32 i = 0; i<MaxSequences; i++)
|
||||
mSequence[i] = NULL;
|
||||
}
|
||||
|
||||
TSShapeConstructor::~TSShapeConstructor()
|
||||
{
|
||||
}
|
||||
|
||||
bool TSShapeConstructor::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TSShapeConstructor::preload(bool server, char errorBuffer[256])
|
||||
{
|
||||
if(!Parent::preload(server, errorBuffer))
|
||||
return false;
|
||||
|
||||
bool error = false;
|
||||
hShape = ResourceManager->load(mShape);
|
||||
if(!bool(hShape))
|
||||
return false;
|
||||
|
||||
if (hShape->getSequencesConstructed())
|
||||
return true;
|
||||
|
||||
// We want to write into the resource:
|
||||
TSShape * shape = const_cast<TSShape*>((const TSShape*)hShape);
|
||||
|
||||
Stream * f;
|
||||
for (S32 i=0; i<MaxSequences; i++)
|
||||
{
|
||||
if (mSequence[i])
|
||||
{
|
||||
char fileBuf[256];
|
||||
dStrcpy(fileBuf, mSequence[i]);
|
||||
|
||||
// spaces and tabs indicate the end of the file name:
|
||||
char * terminate1 = dStrchr(fileBuf,' ');
|
||||
char * terminate2 = dStrchr(fileBuf,'\t');
|
||||
if (terminate1 && terminate2)
|
||||
// select the earliest one:
|
||||
*(terminate1<terminate2 ? terminate1 : terminate2) = '\0';
|
||||
else if (terminate1 || terminate2)
|
||||
// select the non-null one:
|
||||
*(terminate1 ? terminate1 : terminate2) = '\0';
|
||||
|
||||
f = ResourceManager->openStream(fileBuf);
|
||||
if (!f || f->getStatus() != Stream::Ok)
|
||||
{
|
||||
dSprintf(errorBuffer, 256, "Missing sequence %s for %s",mSequence[i],mShape);
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
if (!shape->importSequences(f) || f->getStatus()!=Stream::Ok)
|
||||
{
|
||||
ResourceManager->closeStream(f);
|
||||
dSprintf(errorBuffer, 256, "Load sequence %s failed for %s",mSequence[i],mShape);
|
||||
return false;
|
||||
}
|
||||
ResourceManager->closeStream(f);
|
||||
|
||||
// if there was a space or tab in mSequence[i], then a new name was supplied for it
|
||||
// change it here...will still be in fileBuf
|
||||
if (terminate1 || terminate2)
|
||||
{
|
||||
// select the latter one:
|
||||
char * nameStart = terminate1<terminate2 ? terminate2 : terminate1;
|
||||
do
|
||||
nameStart++;
|
||||
while ( (*nameStart==' ' || *nameStart=='\t') && *nameStart != '\0' );
|
||||
// find the name in the shape (but keep the old one there in case used by something else)
|
||||
shape->sequences.last().nameIndex = shape->findName(nameStart);
|
||||
if (shape->sequences.last().nameIndex == -1)
|
||||
{
|
||||
shape->sequences.last().nameIndex = shape->names.size();
|
||||
shape->names.increment();
|
||||
shape->names.last() = StringTable->insert(nameStart,false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if(!error)
|
||||
hShape->setSequencesConstructed(true);
|
||||
return !error;
|
||||
}
|
||||
|
||||
void TSShapeConstructor::packData(BitStream* stream)
|
||||
{
|
||||
char *subtext;
|
||||
|
||||
Parent::packData(stream);
|
||||
stream->writeString(mShape);
|
||||
|
||||
S32 count = 0;
|
||||
for (S32 b=0; b<MaxSequences; b++)
|
||||
if (mSequence[b])
|
||||
count++;
|
||||
|
||||
stream->writeInt(count,NumSequenceBits);
|
||||
|
||||
for (S32 i=0; i<MaxSequences; i++)
|
||||
if (mSequence[i]) {
|
||||
if (TS_IS_RELATIVE_SEQUENCES)
|
||||
{
|
||||
subtext = (char*)dStrrchr(mSequence[i],'/');
|
||||
stream->writeString(subtext);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->writeString(mSequence[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeConstructor::unpackData(BitStream* stream)
|
||||
{
|
||||
S32 strLen,endLen,pathLen;
|
||||
char shapePath[90],pathedSeq[90];
|
||||
char *subtext;
|
||||
|
||||
Parent::unpackData(stream);
|
||||
mShape = stream->readSTString();
|
||||
|
||||
if (TS_IS_RELATIVE_SEQUENCES)
|
||||
{
|
||||
strLen = (int)dStrlen(mShape);
|
||||
subtext = (char*)dStrrchr(mShape,'/');
|
||||
endLen = (int)dStrlen(subtext);
|
||||
pathLen = (strLen - endLen) ;
|
||||
dStrncpy(shapePath,mShape,pathLen);
|
||||
shapePath[pathLen] = '\0';
|
||||
}
|
||||
|
||||
S32 i = 0, count = stream->readInt(NumSequenceBits);
|
||||
for (; i<count; i++) {
|
||||
mSequence[i] = stream->readSTString();
|
||||
if (TS_IS_RELATIVE_SEQUENCES) {
|
||||
dSprintf(pathedSeq,90,"%s%s",shapePath,mSequence[i]);
|
||||
mSequence[i] = StringTable->insert(pathedSeq);
|
||||
}
|
||||
}
|
||||
while (i<MaxSequences)
|
||||
mSequence[i++] = NULL;
|
||||
}
|
||||
|
||||
void TSShapeConstructor::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
addGroup("Media");
|
||||
addField("baseShape", TypeFilename, Offset(mShape, TSShapeConstructor));
|
||||
endGroup("Media");
|
||||
|
||||
char buf[30];
|
||||
if (MaxSequences) addGroup("Sequences");
|
||||
for (S32 i=0; i<MaxSequences; i++)
|
||||
{
|
||||
dSprintf(buf,sizeof(buf),"sequence%i",i);
|
||||
addField(buf, TypeFilename, OffsetNonConst(mSequence[i], TSShapeConstructor));
|
||||
}
|
||||
if (MaxSequences) endGroup("Sequences");
|
||||
}
|
||||
|
||||
|
49
engine/ts/tsShapeConstruct.h
Executable file
49
engine/ts/tsShapeConstruct.h
Executable file
@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSSHAPECONSTRUCT_H_
|
||||
#define _TSSHAPECONSTRUCT_H_
|
||||
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
#ifndef _CONSOLEOBJECT_H_
|
||||
#include "console/consoleObject.h"
|
||||
#endif
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
/// This class allows an artist to export their animations for the model
|
||||
/// into the .dsq format. This class in particular matches up the model
|
||||
/// with the .dsqs to create a nice animated model.
|
||||
class TSShapeConstructor : public SimDataBlock
|
||||
{
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
enum {
|
||||
NumSequenceBits = 7,
|
||||
MaxSequences = (1 << NumSequenceBits) - 1
|
||||
};
|
||||
|
||||
StringTableEntry mShape;
|
||||
StringTableEntry mSequence[MaxSequences];
|
||||
|
||||
Resource<TSShape> hShape;
|
||||
|
||||
public:
|
||||
|
||||
TSShapeConstructor();
|
||||
~TSShapeConstructor();
|
||||
bool onAdd();
|
||||
bool preload(bool server, char errorBuffer[256]);
|
||||
void packData(BitStream* stream);
|
||||
void unpackData(BitStream* stream);
|
||||
|
||||
DECLARE_CONOBJECT(TSShapeConstructor);
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
#endif
|
2211
engine/ts/tsShapeInstance.cc
Executable file
2211
engine/ts/tsShapeInstance.cc
Executable file
File diff suppressed because it is too large
Load Diff
1035
engine/ts/tsShapeInstance.h
Executable file
1035
engine/ts/tsShapeInstance.h
Executable file
File diff suppressed because it is too large
Load Diff
1523
engine/ts/tsShapeOldRead.cc
Executable file
1523
engine/ts/tsShapeOldRead.cc
Executable file
File diff suppressed because it is too large
Load Diff
286
engine/ts/tsSortedMesh.cc
Executable file
286
engine/ts/tsSortedMesh.cc
Executable file
@ -0,0 +1,286 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsSortedMesh.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
|
||||
// Not worth the effort, much less the effort to comment, but if the draw types
|
||||
// are consecutive use addition rather than a table to go from index to command value...
|
||||
#if ((GL_TRIANGLES+1==GL_TRIANGLE_STRIP) && (GL_TRIANGLE_STRIP+1==GL_TRIANGLE_FAN))
|
||||
#define getDrawType(a) (GL_TRIANGLES+(a))
|
||||
#else
|
||||
U32 drawTypes[] = { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN };
|
||||
#define getDrawType(a) (drawTypes[a])
|
||||
#endif
|
||||
|
||||
// found in tsmesh
|
||||
extern void forceFaceCamera();
|
||||
extern void forceFaceCameraZAxis();
|
||||
|
||||
//-----------------------------------------------------
|
||||
// TSSortedMesh render methods
|
||||
//-----------------------------------------------------
|
||||
|
||||
void TSSortedMesh::render(S32 frame, S32 matFrame, TSMaterialList * materials)
|
||||
{
|
||||
if (getFlags(Billboard))
|
||||
{
|
||||
if (getFlags(BillboardZAxis))
|
||||
forceFaceCameraZAxis();
|
||||
else
|
||||
forceFaceCamera();
|
||||
}
|
||||
|
||||
MatrixF toCam;
|
||||
dglGetModelview(&toCam);
|
||||
toCam.inverse();
|
||||
Point3F cameraCenter;
|
||||
toCam.getColumn(3,&cameraCenter);
|
||||
|
||||
S32 firstVert = firstVerts[frame];
|
||||
S32 vertCount = numVerts[frame];
|
||||
// note on the following: a TSSortedMesh can animate frame or matFrame but not both.
|
||||
// The reason for this is simple: the number of verts change depending on frame, so
|
||||
// if both varied then there would have to be a new set of tverts for every (frame x matFrame)
|
||||
// combination, rather than for every matFrame as for other mesh types. However, either frame
|
||||
// or matFrame is allowed to vary, accounting for the following odd looking statement.
|
||||
S32 firstTVert = firstTVerts[matFrame ? matFrame : frame];
|
||||
|
||||
// set up vertex arrays -- already enabled in TSShapeInstance::render
|
||||
glVertexPointer(3,GL_FLOAT,0,&verts[firstVert]);
|
||||
glNormalPointer(GL_FLOAT,0,getNormals(firstVert));
|
||||
ToolVector<Point2F> diffuse;
|
||||
getUVs(tDiffuse, diffuse);
|
||||
glTexCoordPointer(2,GL_FLOAT,0,&diffuse[firstTVert]);
|
||||
if (TSShapeInstance::smRenderData.detailMapMethod == TSShapeInstance::DETAIL_MAP_MULTI_1 ||
|
||||
TSShapeInstance::smRenderData.detailMapMethod == TSShapeInstance::DETAIL_MAP_MULTI_2)
|
||||
{
|
||||
glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.detailMapTE);
|
||||
glTexCoordPointer(2,GL_FLOAT,0,&diffuse[firstTVert]);
|
||||
glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE);
|
||||
}
|
||||
if (TSShapeInstance::smRenderData.lightMapMethod == TSShapeInstance::LIGHT_MAP_MULTI)
|
||||
{
|
||||
ToolVector<Point2F> lightmap;
|
||||
getUVs(tLightmap, lightmap);
|
||||
glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.lightMapTE);
|
||||
glTexCoordPointer(2,GL_FLOAT,0,&lightmap[firstTVert]);
|
||||
glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE);
|
||||
}
|
||||
|
||||
// lock...
|
||||
bool lockArrays = dglDoesSupportCompiledVertexArray();
|
||||
if (lockArrays)
|
||||
glLockArraysEXT(0,vertCount);
|
||||
|
||||
if (alwaysWriteDepth)
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
Cluster * cluster;
|
||||
S32 nextCluster = startCluster[frame];
|
||||
do
|
||||
{
|
||||
// the cluster...
|
||||
cluster = &clusters[nextCluster];
|
||||
|
||||
// render the cluster...
|
||||
for (S32 i=cluster->startPrimitive; i<cluster->endPrimitive; i++)
|
||||
{
|
||||
TSDrawPrimitive & draw = primitives[i];
|
||||
AssertFatal((draw.matIndex & TSDrawPrimitive::Indexed)!=0,"TSSortedMesh::render: rendering of non-indexed meshes no longer supported");
|
||||
|
||||
// material change?
|
||||
if ( (TSShapeInstance::smRenderData.materialIndex ^ draw.matIndex) & (TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial))
|
||||
{
|
||||
setMaterial(draw.matIndex,materials);
|
||||
if (alwaysWriteDepth)
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
S32 drawType = getDrawType(draw.matIndex>>30);
|
||||
|
||||
glDrawElements(drawType,draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]);
|
||||
}
|
||||
|
||||
// determine next cluster...
|
||||
if (cluster->frontCluster!=cluster->backCluster)
|
||||
nextCluster = (mDot(cluster->normal,cameraCenter) > cluster->k) ? cluster->frontCluster : cluster->backCluster;
|
||||
else
|
||||
nextCluster = cluster->frontCluster;
|
||||
} while (nextCluster>=0);
|
||||
|
||||
// unlock...
|
||||
if (lockArrays)
|
||||
glUnlockArraysEXT();
|
||||
|
||||
if ((TSShapeInstance::smRenderData.materialFlags & TSMaterialList::Translucent) && alwaysWriteDepth)
|
||||
glDepthMask(GL_FALSE);
|
||||
}
|
||||
|
||||
void TSSortedMesh::renderFog(S32 frame)
|
||||
{
|
||||
if (getFlags(Billboard))
|
||||
{
|
||||
if (getFlags(BillboardZAxis))
|
||||
forceFaceCameraZAxis();
|
||||
else
|
||||
forceFaceCamera();
|
||||
}
|
||||
|
||||
MatrixF toCam;
|
||||
dglGetModelview(&toCam);
|
||||
toCam.inverse();
|
||||
Point3F cameraCenter;
|
||||
toCam.getColumn(3,&cameraCenter);
|
||||
|
||||
S32 firstVert = firstVerts[frame];
|
||||
S32 vertCount = numVerts[frame];
|
||||
|
||||
// set up vertex arrays -- already enabled in TSShapeInstance::render
|
||||
glVertexPointer(3,GL_FLOAT,0,&verts[firstVert]);
|
||||
|
||||
// lock...
|
||||
bool lockArrays = dglDoesSupportCompiledVertexArray();
|
||||
if (lockArrays)
|
||||
glLockArraysEXT(0,vertCount);
|
||||
|
||||
Cluster * cluster;
|
||||
S32 nextCluster = startCluster[frame];
|
||||
do
|
||||
{
|
||||
// the cluster...
|
||||
cluster = &clusters[nextCluster];
|
||||
|
||||
// render the cluster...
|
||||
for (S32 i=cluster->startPrimitive; i<cluster->endPrimitive; i++)
|
||||
{
|
||||
TSDrawPrimitive & draw = primitives[i];
|
||||
glDrawElements(getDrawType(draw.matIndex>>30),draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]);
|
||||
}
|
||||
|
||||
// determine next cluster...
|
||||
if (cluster->frontCluster!=cluster->backCluster)
|
||||
nextCluster = (mDot(cluster->normal,cameraCenter) > cluster->k) ? cluster->frontCluster : cluster->backCluster;
|
||||
else
|
||||
nextCluster = cluster->frontCluster;
|
||||
} while (nextCluster>=0);
|
||||
|
||||
// unlock...
|
||||
if (lockArrays)
|
||||
glUnlockArraysEXT();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
// TSSortedMesh collision methods
|
||||
//-----------------------------------------------------
|
||||
|
||||
bool TSSortedMesh::buildPolyList(S32 frame, AbstractPolyList * polyList, U32 & surfaceKey)
|
||||
{
|
||||
frame, polyList, surfaceKey;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TSSortedMesh::castRay(S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo)
|
||||
{
|
||||
frame, start, end, rayInfo;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TSSortedMesh::buildConvexHull()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
S32 TSSortedMesh::getNumPolys()
|
||||
{
|
||||
S32 count = 0;
|
||||
S32 cIdx = !clusters.size() ? -1 : 0;
|
||||
while (cIdx>=0)
|
||||
{
|
||||
Cluster & cluster = clusters[cIdx];
|
||||
for (S32 i=cluster.startPrimitive; i<cluster.endPrimitive; i++)
|
||||
{
|
||||
if (primitives[i].matIndex & TSDrawPrimitive::Triangles)
|
||||
count += primitives[i].numElements / 3;
|
||||
else
|
||||
count += primitives[i].numElements - 2;
|
||||
}
|
||||
cIdx = cluster.frontCluster; // always use frontCluster...we assume about the same no matter what
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
// TSSortedMesh assembly/dissembly methods
|
||||
// used for transfer to/from memory buffers
|
||||
//-----------------------------------------------------
|
||||
|
||||
#define alloc TSShape::alloc
|
||||
|
||||
void TSSortedMesh::assemble(bool skip)
|
||||
{
|
||||
bool save1 = TSMesh::smUseTriangles;
|
||||
bool save2 = TSMesh::smUseOneStrip;
|
||||
TSMesh::smUseTriangles = false;
|
||||
TSMesh::smUseOneStrip = false;
|
||||
|
||||
TSMesh::assemble(skip);
|
||||
|
||||
TSMesh::smUseTriangles = save1;
|
||||
TSMesh::smUseOneStrip = save2;
|
||||
|
||||
S32 numClusters = alloc.get32();
|
||||
S32 * ptr32 = alloc.copyToShape32(numClusters*8);
|
||||
clusters.set(ptr32,numClusters);
|
||||
|
||||
S32 sz = alloc.get32();
|
||||
ptr32 = alloc.copyToShape32(sz);
|
||||
startCluster.set(ptr32,sz);
|
||||
|
||||
sz = alloc.get32();
|
||||
ptr32 = alloc.copyToShape32(sz);
|
||||
firstVerts.set(ptr32,sz);
|
||||
|
||||
sz = alloc.get32();
|
||||
ptr32 = alloc.copyToShape32(sz);
|
||||
numVerts.set(ptr32,sz);
|
||||
|
||||
sz = alloc.get32();
|
||||
ptr32 = alloc.copyToShape32(sz);
|
||||
firstTVerts.set(ptr32,sz);
|
||||
|
||||
alwaysWriteDepth = alloc.get32()!=0;
|
||||
|
||||
alloc.checkGuard();
|
||||
}
|
||||
|
||||
void TSSortedMesh::disassemble()
|
||||
{
|
||||
TSMesh::disassemble();
|
||||
|
||||
alloc.set32(clusters.size());
|
||||
alloc.copyToBuffer32((S32*)clusters.address(),clusters.size()*8);
|
||||
|
||||
alloc.set32(startCluster.size());
|
||||
alloc.copyToBuffer32((S32*)startCluster.address(),startCluster.size());
|
||||
|
||||
alloc.set32(firstVerts.size());
|
||||
alloc.copyToBuffer32((S32*)firstVerts.address(),firstVerts.size());
|
||||
|
||||
alloc.set32(numVerts.size());
|
||||
alloc.copyToBuffer32((S32*)numVerts.address(),numVerts.size());
|
||||
|
||||
alloc.set32(firstTVerts.size());
|
||||
alloc.copyToBuffer32((S32*)firstTVerts.address(),firstTVerts.size());
|
||||
|
||||
alloc.set32(alwaysWriteDepth ? 1 : 0);
|
||||
|
||||
alloc.setGuard();
|
||||
}
|
||||
|
||||
|
||||
|
64
engine/ts/tsSortedMesh.h
Executable file
64
engine/ts/tsSortedMesh.h
Executable file
@ -0,0 +1,64 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSSORTEDMESH_H_
|
||||
#define _TSSORTEDMESH_H_
|
||||
|
||||
#ifndef _TSMESH_H_
|
||||
#include "ts/tsMesh.h"
|
||||
#endif
|
||||
|
||||
/// TSSortedMesh is for meshes that need sorting (obviously). Such meshes
|
||||
/// are usually partially or completely composed of translucent/parent polygons.
|
||||
class TSSortedMesh : public TSMesh
|
||||
{
|
||||
public:
|
||||
typedef TSMesh Parent;
|
||||
|
||||
/// This is a group of primitives that belong "together" in the rendering sequence.
|
||||
/// For example, if a player model had a helmet with a translucent visor, the visor
|
||||
/// would be a Cluster.
|
||||
struct Cluster
|
||||
{
|
||||
S32 startPrimitive;
|
||||
S32 endPrimitive;
|
||||
Point3F normal;
|
||||
F32 k;
|
||||
S32 frontCluster; ///< go to this cluster if in front of plane, if frontCluster<0, no cluster
|
||||
S32 backCluster; ///< go to this cluster if in back of plane, if backCluster<0, no cluster
|
||||
///< if frontCluster==backCluster, no plane to test against...
|
||||
};
|
||||
|
||||
ToolVector<Cluster> clusters; ///< All of the clusters of primitives to be drawn
|
||||
ToolVector<S32> startCluster; ///< indexed by frame number
|
||||
ToolVector<S32> firstVerts; ///< indexed by frame number
|
||||
ToolVector<S32> numVerts; ///< indexed by frame number
|
||||
ToolVector<S32> firstTVerts; ///< indexed by frame number or matFrame number, depending on which one animates (never both)
|
||||
|
||||
/// sometimes, we want to write the depth value to the frame buffer even when object is translucent
|
||||
bool alwaysWriteDepth;
|
||||
|
||||
// render methods..
|
||||
void render(S32 frame, S32 matFrame, TSMaterialList *);
|
||||
void renderFog(S32 frame);
|
||||
|
||||
bool buildPolyList(S32 frame, AbstractPolyList * polyList, U32 & surfaceKey);
|
||||
bool castRay(S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo);
|
||||
bool buildConvexHull(); ///< does nothing, skins don't use this
|
||||
///
|
||||
/// @returns false ALWAYS
|
||||
S32 getNumPolys();
|
||||
|
||||
void assemble(bool skip);
|
||||
void disassemble();
|
||||
|
||||
TSSortedMesh() {
|
||||
meshType = SortedMeshType;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _TS_SORTED_MESH
|
||||
|
||||
|
633
engine/ts/tsThread.cc
Executable file
633
engine/ts/tsThread.cc
Executable file
@ -0,0 +1,633 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsShapeInstance.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// This file contains the shape instance thread class (defined in tsShapeInstance.h)
|
||||
// and the tsShapeInstance functions to interface with the thread class.
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Thread class
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
// given a position on the thread, choose correct keyframes
|
||||
// slight difference between one-shot and cyclic sequences -- see comments below for details
|
||||
void TSThread::selectKeyframes(F32 pos, const TSSequence * seq, S32 * k1, S32 * k2, F32 * kpos)
|
||||
{
|
||||
const TSShape * shape = mShapeInstance->mShape;
|
||||
S32 numKF = seq->numKeyframes;
|
||||
F32 kf;
|
||||
|
||||
if (seq->isCyclic())
|
||||
{
|
||||
// cyclic sequence:
|
||||
// pos=0 and pos=1 are equivalent, so we don't have a keyframe at pos=1
|
||||
// last keyframe corresponds to pos=n/(n-1) up to (not including) pos=1
|
||||
// (where n == num keyframes)
|
||||
|
||||
AssertFatal(pos>=0.0f && pos<1.0f,"TSThread::selectKeyframes");
|
||||
|
||||
kf = pos * (F32) (numKF);
|
||||
|
||||
// set keyPos
|
||||
if (kpos)
|
||||
*kpos = kf - (S32) kf;
|
||||
|
||||
// make sure compiler doing what we want...
|
||||
AssertFatal(*kpos>=0.0f && *kpos<1.0f,"TSThread::selectKeyframes");
|
||||
|
||||
S32 kfIdx1 = (S32) kf;
|
||||
|
||||
// following assert could happen if pos1<1 && pos1==1...paradoxically...
|
||||
AssertFatal(kfIdx1<=seq->numKeyframes,"TSThread::selectKeyframes");
|
||||
|
||||
S32 kfIdx2 = (kfIdx1==seq->numKeyframes-1) ? 0 : kfIdx1+1;
|
||||
|
||||
if (k1)
|
||||
*k1 = kfIdx1;
|
||||
if (k2)
|
||||
*k2 = kfIdx2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// one-shot sequence:
|
||||
// pos=0 and pos=1 are now different, so we have a keyframe at pos=1
|
||||
// last keyframe corresponds to pos=1
|
||||
// rest of the keyframes are equally spaced (so 1/(n-1) pos units long)
|
||||
// (where n == num keyframes)
|
||||
|
||||
AssertFatal(pos>=0.0f && pos<=1.0f,"TSThread::selectKeyframes");
|
||||
|
||||
if (pos==1.0f)
|
||||
{
|
||||
if (kpos)
|
||||
*kpos = 0.0f;
|
||||
if (k1)
|
||||
*k1 = seq->numKeyframes-1;
|
||||
if (k2)
|
||||
*k2 = seq->numKeyframes-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
kf = pos * (F32) (numKF-1);
|
||||
|
||||
// set keyPos
|
||||
if (kpos)
|
||||
*kpos = kf - (S32) kf;
|
||||
|
||||
S32 kfIdx1 = (S32) kf;
|
||||
|
||||
// following assert could happen if pos1<1 && pos1==1...paradoxically...
|
||||
AssertFatal(kfIdx1<seq->numKeyframes,"TSThread::selectKeyFrames: invalid keyframe!");
|
||||
|
||||
S32 kfIdx2 = kfIdx1+1;
|
||||
|
||||
if (k1)
|
||||
*k1 = kfIdx1;
|
||||
if (k2)
|
||||
*k2 = kfIdx2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSThread::getGround(F32 t, MatrixF * pMat)
|
||||
{
|
||||
static const QuatF unitRotation(0.0f,0.0f,0.0f,1.0f);
|
||||
static const Point3F unitTranslation(0.0f,0.0f,0.0f);
|
||||
|
||||
const QuatF * q1, * q2;
|
||||
QuatF rot1,rot2;
|
||||
const Point3F * p1, * p2;
|
||||
|
||||
// if N = sequence->numGroundFrames, then there are N+1 positions we
|
||||
// interpolate betweeen: 0/N, 1/N ... N/N
|
||||
// we need to convert the p passed to us into 2 ground keyframes:
|
||||
|
||||
// the 0.99999f is in case 'p' is exactly 1.0f, which is legal but 'kf'
|
||||
// needs to be strictly less than 'sequence->numGroundFrames'
|
||||
F32 kf = 0.999999f * t * (F32) sequence->numGroundFrames;
|
||||
|
||||
// get frame number and interp param (kpos)
|
||||
S32 frame = (S32)kf;
|
||||
F32 kpos = kf - (F32)frame;
|
||||
|
||||
// now point pT1 and pT2 at transforms for keyframes 'frame' and 'frame+1'
|
||||
|
||||
// following a little strange: first ground keyframe (0/N in comment above) is
|
||||
// assumed to be ident. and not found in the list.
|
||||
if (frame)
|
||||
{
|
||||
p1 = &mShapeInstance->mShape->groundTranslations[sequence->firstGroundFrame + frame - 1];
|
||||
q1 = &mShapeInstance->mShape->groundRotations[sequence->firstGroundFrame + frame - 1].getQuatF(&rot1);
|
||||
}
|
||||
else
|
||||
{
|
||||
p1 = &unitTranslation;
|
||||
q1 = &unitRotation;
|
||||
}
|
||||
|
||||
// similar to above, ground keyframe number 'frame+1' is actually offset by 'frame'
|
||||
p2 = &mShapeInstance->mShape->groundTranslations[sequence->firstGroundFrame + frame];
|
||||
q2 = &mShapeInstance->mShape->groundRotations[sequence->firstGroundFrame + frame].getQuatF(&rot2);
|
||||
|
||||
QuatF q;
|
||||
Point3F p;
|
||||
TSTransform::interpolate(*q1,*q2,kpos,&q);
|
||||
TSTransform::interpolate(*p1,*p2,kpos,&p);
|
||||
TSTransform::setMatrix(q,p,pMat);
|
||||
}
|
||||
|
||||
void TSThread::setSequence(S32 seq, F32 toPos)
|
||||
{
|
||||
const TSShape * shape = mShapeInstance->mShape;
|
||||
|
||||
AssertFatal(shape && shape->sequences.size()>seq && toPos>=0.0f && toPos<=1.0f,
|
||||
"TSThread::setSequence: invalid shape handle, sequence number, or position.");
|
||||
|
||||
mShapeInstance->clearTransition(this);
|
||||
|
||||
sequence = &shape->sequences[seq];
|
||||
priority = sequence->priority;
|
||||
pos = toPos;
|
||||
makePath = sequence->makePath();
|
||||
|
||||
// 1.0f doesn't exist on cyclic sequences
|
||||
if (pos>0.9999f && sequence->isCyclic())
|
||||
pos = 0.9999f;
|
||||
|
||||
// select keyframes
|
||||
selectKeyframes(pos,sequence,&keyNum1,&keyNum2,&keyPos);
|
||||
}
|
||||
|
||||
void TSThread::transitionToSequence(S32 seq, F32 toPos, F32 duration, bool continuePlay)
|
||||
{
|
||||
AssertFatal(duration>=0.0f,"TSThread::transitionToSequence: negative duration not allowed");
|
||||
|
||||
const TSShape * shape = mShapeInstance->mShape;
|
||||
|
||||
// make sure these nodes are smoothly interpolated to new positions...
|
||||
// basically, any node we controlled just prior to transition, or at any stage
|
||||
// of the transition is interpolated. If we start to transtion from A to B,
|
||||
// but before reaching B we transtion to C, we interpolate all nodes controlled
|
||||
// by A, B, or C to their new position.
|
||||
if (transitionData.inTransition)
|
||||
{
|
||||
transitionData.oldRotationNodes.overlap(sequence->rotationMatters);
|
||||
transitionData.oldTranslationNodes.overlap(sequence->translationMatters);
|
||||
transitionData.oldScaleNodes.overlap(sequence->scaleMatters);
|
||||
}
|
||||
else
|
||||
{
|
||||
transitionData.oldRotationNodes = sequence->rotationMatters;
|
||||
transitionData.oldTranslationNodes = sequence->translationMatters;
|
||||
transitionData.oldScaleNodes = sequence->scaleMatters;
|
||||
}
|
||||
|
||||
// set time characteristics of transition
|
||||
transitionData.oldSequence = (S32)(sequence-&shape->sequences[0]);
|
||||
transitionData.oldPos = pos;
|
||||
transitionData.duration = duration;
|
||||
transitionData.pos = 0.0f;
|
||||
transitionData.direction = timeScale>0.0f ? 1.0f : -1.0f;
|
||||
transitionData.targetScale = continuePlay ? 1.0f : 0.0f;
|
||||
|
||||
// in transition...
|
||||
transitionData.inTransition = true;
|
||||
|
||||
// set target sequence data
|
||||
sequence = &shape->sequences[seq];
|
||||
priority = sequence->priority;
|
||||
pos = toPos;
|
||||
makePath = sequence->makePath();
|
||||
|
||||
// 1.0f doesn't exist on cyclic sequences
|
||||
if (pos>0.9999f && sequence->isCyclic())
|
||||
pos = 0.9999f;
|
||||
|
||||
// select keyframes
|
||||
selectKeyframes(pos,sequence,&keyNum1,&keyNum2,&keyPos);
|
||||
}
|
||||
|
||||
void TSThread::animateTriggers()
|
||||
{
|
||||
if (!sequence->numTriggers)
|
||||
return;
|
||||
|
||||
switch (path.loop)
|
||||
{
|
||||
case -1 :
|
||||
activateTriggers(path.start,0);
|
||||
activateTriggers(1,path.end);
|
||||
break;
|
||||
case 0 :
|
||||
activateTriggers(path.start,path.end);
|
||||
break;
|
||||
case 1 :
|
||||
activateTriggers(path.start,1);
|
||||
activateTriggers(0,path.end);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (path.loop>0)
|
||||
{
|
||||
activateTriggers(path.end,1);
|
||||
activateTriggers(0,path.end);
|
||||
}
|
||||
else
|
||||
{
|
||||
activateTriggers(path.end,0);
|
||||
activateTriggers(1,path.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSThread::activateTriggers(F32 a, F32 b)
|
||||
{
|
||||
S32 i;
|
||||
const TSShape * shape = mShapeInstance->mShape;
|
||||
S32 firstTrigger = sequence->firstTrigger;
|
||||
S32 numTriggers = sequence->numTriggers;
|
||||
|
||||
// first find triggers at position a and b
|
||||
// we assume there aren't many triggers, so
|
||||
// search is linear
|
||||
F32 lastPos = -1.0f;
|
||||
S32 aIndex = numTriggers+firstTrigger; // initialized to handle case where pos past all triggers
|
||||
S32 bIndex = numTriggers+firstTrigger; // initialized to handle case where pos past all triggers
|
||||
for (i=firstTrigger; i<numTriggers+firstTrigger; i++)
|
||||
{
|
||||
// is a between this trigger and previous one...
|
||||
if (a>lastPos && a<=shape->triggers[i].pos)
|
||||
aIndex = i;
|
||||
// is b between this trigger and previous one...
|
||||
if (b>lastPos && b<=shape->triggers[i].pos)
|
||||
bIndex = i;
|
||||
lastPos = shape->triggers[i].pos;
|
||||
}
|
||||
|
||||
// activate triggers between aIndex and bIndex (depends on direction)
|
||||
if (aIndex<=bIndex)
|
||||
{
|
||||
for (i=aIndex; i<bIndex; i++)
|
||||
{
|
||||
U32 state = shape->triggers[i].state;
|
||||
bool on = (state & TSShape::Trigger::StateOn)!=0;
|
||||
mShapeInstance->setTriggerStateBit(state & TSShape::Trigger::StateMask, on);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=aIndex-1; i>=bIndex; i--)
|
||||
{
|
||||
U32 state = shape->triggers[i].state;
|
||||
bool on = (state & TSShape::Trigger::StateOn)!=0;
|
||||
if (state & TSShape::Trigger::InvertOnReverse)
|
||||
on = !on;
|
||||
mShapeInstance->setTriggerStateBit(state & TSShape::Trigger::StateMask, on);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSThread::advancePos(F32 delta)
|
||||
{
|
||||
if (mFabs(delta)>0.00001f)
|
||||
{
|
||||
// make dirty what this thread changes
|
||||
U32 dirtyFlags = sequence->dirtyFlags | (transitionData.inTransition ? TSShapeInstance::TransformDirty : 0);
|
||||
for (S32 i=0; i<mShapeInstance->getShape()->subShapeFirstNode.size(); i++)
|
||||
mShapeInstance->mDirtyFlags[i] |= dirtyFlags;
|
||||
}
|
||||
|
||||
if (transitionData.inTransition)
|
||||
{
|
||||
transitionData.pos += transitionData.direction * delta;
|
||||
if (transitionData.pos<0 || transitionData.pos>=1.0f)
|
||||
{
|
||||
mShapeInstance->clearTransition(this);
|
||||
if (transitionData.pos<0.0f)
|
||||
// return to old sequence
|
||||
mShapeInstance->setSequence(this,transitionData.oldSequence,transitionData.oldPos);
|
||||
}
|
||||
// re-adjust delta to be correct time-wise
|
||||
delta *= transitionData.targetScale * transitionData.duration / sequence->duration;
|
||||
}
|
||||
|
||||
// even if we are in a transition, keep playing the sequence
|
||||
|
||||
if (makePath)
|
||||
{
|
||||
path.start = pos;
|
||||
pos += delta;
|
||||
if (!sequence->isCyclic())
|
||||
{
|
||||
pos = mClampF(pos , 0.0f, 1.0f);
|
||||
path.loop = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
path.loop = (S32)pos;
|
||||
if (pos < 0.0f)
|
||||
path.loop--;
|
||||
pos -= path.loop;
|
||||
// following necessary because of floating point roundoff errors
|
||||
if (pos < 0.0f) pos += 1.0f;
|
||||
if (pos >= 1.0f) pos -= 1.0f;
|
||||
}
|
||||
path.end = pos;
|
||||
|
||||
animateTriggers(); // do this automatically...no need for user to call it
|
||||
|
||||
AssertFatal(pos>=0.0f && pos<=1.0f,"TSThread::advancePos (1)");
|
||||
AssertFatal(!sequence->isCyclic() || pos<1.0f,"TSThread::advancePos (2)");
|
||||
}
|
||||
else
|
||||
{
|
||||
pos += delta;
|
||||
if (!sequence->isCyclic())
|
||||
pos = mClampF(pos, 0.0f, 1.0f);
|
||||
else
|
||||
{
|
||||
pos -= S32(pos);
|
||||
// following necessary because of floating point roundoff errors
|
||||
if (pos < 0.0f) pos += 1.0f;
|
||||
if (pos >= 1.0f) pos -= 1.0f;
|
||||
}
|
||||
AssertFatal(pos>=0.0f && pos<=1.0f,"TSThread::advancePos (3)");
|
||||
AssertFatal(!sequence->isCyclic() || pos<1.0f,"TSThread::advancePos (4)");
|
||||
}
|
||||
|
||||
// select keyframes
|
||||
selectKeyframes(pos,sequence,&keyNum1,&keyNum2,&keyPos);
|
||||
}
|
||||
|
||||
void TSThread::setKeyframeNumber(S32 kf)
|
||||
{
|
||||
const TSShape * shape = mShapeInstance->mShape;
|
||||
|
||||
AssertFatal(kf>=0 && kf<= sequence->numKeyframes,
|
||||
"TSThread::setKeyframeNumber: invalid frame specified.");
|
||||
AssertFatal(!transitionData.inTransition,"TSThread::setKeyframeNumber: not while in transition");
|
||||
|
||||
keyNum1 = keyNum2 = kf;
|
||||
keyPos = 0;
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
TSThread::TSThread(TSShapeInstance * _shapeInst)
|
||||
{
|
||||
timeScale = 1.0f;
|
||||
mShapeInstance = _shapeInst;
|
||||
transitionData.inTransition = false;
|
||||
blendDisabled = false;
|
||||
setSequence(0,0.0f);
|
||||
}
|
||||
|
||||
S32 TSThread::operator<(const TSThread & th2) const
|
||||
{
|
||||
if (sequence->isBlend() == th2.sequence->isBlend())
|
||||
{
|
||||
// both blend or neither blend, sort based on priority only -- higher priority first
|
||||
S32 ret = 0; // do it this way to (hopefully) take advantage of 'conditional move' assembly instruction
|
||||
if (priority > th2.priority)
|
||||
ret = -1;
|
||||
if (th2.priority > priority)
|
||||
ret = 1;
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
// one is blend, the other is not...sort based on blend -- non-blended first
|
||||
AssertFatal(!sequence->isBlend() || !th2.sequence->isBlend(),"compareThreads: unequal 'trues'");
|
||||
|
||||
S32 ret = -1; // do it this way to (hopefully) take advantage of 'conditional move' assembly instruction
|
||||
if (sequence->isBlend())
|
||||
ret = 1;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// TSShapeInstance Thread Interface -- more implemented in header file
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
TSThread * TSShapeInstance::addThread()
|
||||
{
|
||||
if (mShape->sequences.empty())
|
||||
return NULL;
|
||||
|
||||
mThreadList.increment();
|
||||
mThreadList.last() = new TSThread(this);
|
||||
setDirty(AllDirtyMask);
|
||||
return mThreadList.last();
|
||||
}
|
||||
|
||||
void TSShapeInstance::destroyThread(TSThread * thread)
|
||||
{
|
||||
if (!thread)
|
||||
return;
|
||||
|
||||
clearTransition(thread);
|
||||
|
||||
S32 i;
|
||||
for (i=0; i<mThreadList.size(); i++)
|
||||
if (thread==mThreadList[i])
|
||||
break;
|
||||
|
||||
AssertFatal(i<mThreadList.size(),"TSShapeInstance::destroyThread was requested to destroy a thread that this instance doesn't own!");
|
||||
|
||||
delete mThreadList[i];
|
||||
mThreadList.erase(i);
|
||||
setDirty(AllDirtyMask);
|
||||
checkScaleCurrentlyAnimated();
|
||||
}
|
||||
|
||||
void TSShapeInstance::setSequence(TSThread * thread, S32 seq, F32 pos)
|
||||
{
|
||||
if ( (thread->transitionData.inTransition && mTransitionThreads.size()>1) || mTransitionThreads.size()>0)
|
||||
{
|
||||
// if we have transitions, make sure transforms are up to date...
|
||||
animateNodeSubtrees();
|
||||
}
|
||||
|
||||
thread->setSequence(seq,pos);
|
||||
setDirty(AllDirtyMask);
|
||||
mGroundThread = NULL;
|
||||
|
||||
if (mScaleCurrentlyAnimated && !thread->sequence->animatesScale())
|
||||
checkScaleCurrentlyAnimated();
|
||||
else if (!mScaleCurrentlyAnimated && thread->sequence->animatesScale())
|
||||
mScaleCurrentlyAnimated=true;
|
||||
|
||||
updateTransitions();
|
||||
}
|
||||
|
||||
void TSShapeInstance::transitionToSequence(TSThread * thread, S32 seq, F32 pos, F32 duration, bool continuePlay)
|
||||
{
|
||||
// make sure all transforms on all detail levels are accurate
|
||||
animateNodeSubtrees();
|
||||
|
||||
thread->transitionToSequence(seq,pos,duration,continuePlay);
|
||||
setDirty(AllDirtyMask);
|
||||
mGroundThread = NULL;
|
||||
|
||||
if (mScaleCurrentlyAnimated && !thread->sequence->animatesScale())
|
||||
checkScaleCurrentlyAnimated();
|
||||
else if (!mScaleCurrentlyAnimated && thread->sequence->animatesScale())
|
||||
mScaleCurrentlyAnimated=true;
|
||||
|
||||
mTransitionRotationNodes.overlap(thread->transitionData.oldRotationNodes);
|
||||
mTransitionRotationNodes.overlap(thread->sequence->rotationMatters);
|
||||
|
||||
mTransitionTranslationNodes.overlap(thread->transitionData.oldTranslationNodes);
|
||||
mTransitionTranslationNodes.overlap(thread->sequence->translationMatters);
|
||||
|
||||
mTransitionScaleNodes.overlap(thread->transitionData.oldScaleNodes);
|
||||
mTransitionScaleNodes.overlap(thread->sequence->scaleMatters);
|
||||
|
||||
// if we aren't already in the list of transition threads, add us now
|
||||
S32 i;
|
||||
for (i=0; i<mTransitionThreads.size(); i++)
|
||||
if (mTransitionThreads[i]==thread)
|
||||
break;
|
||||
if (i==mTransitionThreads.size())
|
||||
mTransitionThreads.push_back(thread);
|
||||
|
||||
updateTransitions();
|
||||
}
|
||||
|
||||
void TSShapeInstance::clearTransition(TSThread * thread)
|
||||
{
|
||||
if (!thread->transitionData.inTransition)
|
||||
return;
|
||||
|
||||
// if other transitions are still playing,
|
||||
// make sure transforms are up to date
|
||||
if (mTransitionThreads.size()>1)
|
||||
animateNodeSubtrees();
|
||||
|
||||
// turn off transition...
|
||||
thread->transitionData.inTransition = false;
|
||||
|
||||
// remove us from transition list
|
||||
S32 i;
|
||||
if (mTransitionThreads.size() != 0) {
|
||||
for (i=0; i<mTransitionThreads.size(); i++)
|
||||
if (mTransitionThreads[i]==thread)
|
||||
break;
|
||||
AssertFatal(i!=mTransitionThreads.size(),"TSShapeInstance::clearTransition");
|
||||
mTransitionThreads.erase(i);
|
||||
}
|
||||
|
||||
// recompute transitionNodes
|
||||
mTransitionRotationNodes.clearAll();
|
||||
mTransitionTranslationNodes.clearAll();
|
||||
mTransitionScaleNodes.clearAll();
|
||||
for (i=0; i<mTransitionThreads.size(); i++)
|
||||
{
|
||||
mTransitionRotationNodes.overlap(mTransitionThreads[i]->transitionData.oldRotationNodes);
|
||||
mTransitionRotationNodes.overlap(mTransitionThreads[i]->sequence->rotationMatters);
|
||||
|
||||
mTransitionTranslationNodes.overlap(mTransitionThreads[i]->transitionData.oldTranslationNodes);
|
||||
mTransitionTranslationNodes.overlap(mTransitionThreads[i]->sequence->translationMatters);
|
||||
|
||||
mTransitionScaleNodes.overlap(mTransitionThreads[i]->transitionData.oldScaleNodes);
|
||||
mTransitionScaleNodes.overlap(mTransitionThreads[i]->sequence->scaleMatters);
|
||||
}
|
||||
|
||||
setDirty(ThreadDirty);
|
||||
|
||||
updateTransitions();
|
||||
}
|
||||
|
||||
void TSShapeInstance::updateTransitions()
|
||||
{
|
||||
if (mTransitionThreads.empty())
|
||||
return;
|
||||
|
||||
S32 i;
|
||||
mNodeReferenceRotations.setSize(mShape->nodes.size());
|
||||
mNodeReferenceTranslations.setSize(mShape->nodes.size());
|
||||
for (i=0; i<mShape->nodes.size(); i++)
|
||||
{
|
||||
if (mTransitionRotationNodes.test(i))
|
||||
mNodeReferenceRotations[i].set(smNodeCurrentRotations[i]);
|
||||
if (mTransitionTranslationNodes.test(i))
|
||||
mNodeReferenceTranslations[i] = smNodeCurrentTranslations[i];
|
||||
}
|
||||
|
||||
if (animatesScale())
|
||||
{
|
||||
if (animatesUniformScale())
|
||||
{
|
||||
mNodeReferenceUniformScales.setSize(mShape->nodes.size());
|
||||
for (i=0; i<mShape->nodes.size(); i++)
|
||||
{
|
||||
if (mTransitionScaleNodes.test(i))
|
||||
mNodeReferenceUniformScales[i] = smNodeCurrentUniformScales[i];
|
||||
}
|
||||
}
|
||||
else if (animatesAlignedScale())
|
||||
{
|
||||
mNodeReferenceScaleFactors.setSize(mShape->nodes.size());
|
||||
for (i=0; i<mShape->nodes.size(); i++)
|
||||
{
|
||||
if (mTransitionScaleNodes.test(i))
|
||||
mNodeReferenceScaleFactors[i] = smNodeCurrentAlignedScales[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mNodeReferenceScaleFactors.setSize(mShape->nodes.size());
|
||||
mNodeReferenceArbitraryScaleRots.setSize(mShape->nodes.size());
|
||||
for (i=0; i<mShape->nodes.size(); i++)
|
||||
{
|
||||
if (mTransitionScaleNodes.test(i))
|
||||
{
|
||||
mNodeReferenceScaleFactors[i] = smNodeCurrentArbitraryScales[i].mScale;
|
||||
mNodeReferenceArbitraryScaleRots[i].set(smNodeCurrentArbitraryScales[i].mRotate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset transition durations to account for new reference transforms
|
||||
for (i=0; i<mTransitionThreads.size(); i++)
|
||||
{
|
||||
TSThread * th = mTransitionThreads[i];
|
||||
if (th->transitionData.inTransition)
|
||||
{
|
||||
th->transitionData.duration *= 1.0f - th->transitionData.pos;
|
||||
th->transitionData.pos = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TSShapeInstance::checkScaleCurrentlyAnimated()
|
||||
{
|
||||
mScaleCurrentlyAnimated=true;
|
||||
for (S32 i=0; i<mThreadList.size(); i++)
|
||||
if (mThreadList[i]->sequence->animatesScale())
|
||||
return;
|
||||
mScaleCurrentlyAnimated=false;
|
||||
}
|
||||
|
||||
// advance time on all threads
|
||||
void TSShapeInstance::advanceTime(F32 delta)
|
||||
{
|
||||
for (S32 i=0; i<mThreadList.size(); i++)
|
||||
mThreadList[i]->advanceTime(delta);
|
||||
}
|
||||
|
||||
// advance pos on all threads
|
||||
void TSShapeInstance::advancePos(F32 delta)
|
||||
{
|
||||
for (S32 i=0; i<mThreadList.size(); i++)
|
||||
mThreadList[i]->advancePos(delta);
|
||||
}
|
||||
|
131
engine/ts/tsTransform.cc
Executable file
131
engine/ts/tsTransform.cc
Executable file
@ -0,0 +1,131 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ts/tsTransform.h"
|
||||
#include "core/stream.h"
|
||||
|
||||
void Quat16::identity()
|
||||
{
|
||||
x = y = z = 0;
|
||||
w = MAX_VAL;
|
||||
}
|
||||
|
||||
QuatF & Quat16::getQuatF( QuatF * q ) const
|
||||
{
|
||||
q->x = float( x ) / float(MAX_VAL);
|
||||
q->y = float( y ) / float(MAX_VAL);
|
||||
q->z = float( z ) / float(MAX_VAL);
|
||||
q->w = float( w ) / float(MAX_VAL);
|
||||
return *q;
|
||||
}
|
||||
|
||||
void Quat16::set( const QuatF & q )
|
||||
{
|
||||
x = (S16)(q.x * float(MAX_VAL));
|
||||
y = (S16)(q.y * float(MAX_VAL));
|
||||
z = (S16)(q.z * float(MAX_VAL));
|
||||
w = (S16)(q.w * float(MAX_VAL));
|
||||
}
|
||||
|
||||
S32 Quat16::operator==( const Quat16 & q ) const
|
||||
{
|
||||
return( x == q.x && y == q.y && z == q.z && w == q.w );
|
||||
}
|
||||
|
||||
void Quat16::read(Stream * s)
|
||||
{
|
||||
s->read(&x);
|
||||
s->read(&y);
|
||||
s->read(&z);
|
||||
s->read(&w);
|
||||
}
|
||||
|
||||
void Quat16::write(Stream * s)
|
||||
{
|
||||
s->write(x);
|
||||
s->write(y);
|
||||
s->write(z);
|
||||
s->write(w);
|
||||
}
|
||||
|
||||
QuatF & TSTransform::interpolate( const QuatF & q1, const QuatF & q2, F32 interp, QuatF * q )
|
||||
{
|
||||
F32 Dot;
|
||||
F32 Dist2;
|
||||
F32 OneOverL;
|
||||
F32 x1,y1,z1,w1;
|
||||
F32 x2,y2,z2,w2;
|
||||
|
||||
//
|
||||
// This is a linear interpolation with a fast renormalization.
|
||||
//
|
||||
x1 = q1.x;
|
||||
y1 = q1.y;
|
||||
z1 = q1.z;
|
||||
w1 = q1.w;
|
||||
x2 = q2.x;
|
||||
y2 = q2.y;
|
||||
z2 = q2.z;
|
||||
w2 = q2.w;
|
||||
|
||||
// Determine if quats are further than 90 degrees
|
||||
Dot = x1*x2 + y1*y2 + z1*z2 + w1*w2;
|
||||
|
||||
// If dot is negative flip one of the quaterions
|
||||
if( Dot < 0.0f )
|
||||
{
|
||||
x1 = -x1;
|
||||
y1 = -y1;
|
||||
z1 = -z1;
|
||||
w1 = -w1;
|
||||
}
|
||||
|
||||
// Compute interpolated values
|
||||
x1 = x1 + interp*(x2 - x1);
|
||||
y1 = y1 + interp*(y2 - y1);
|
||||
z1 = z1 + interp*(z2 - z1);
|
||||
w1 = w1 + interp*(w2 - w1);
|
||||
|
||||
// Get squared distance of new quaternion
|
||||
Dist2 = x1*x1 + y1*y1 + z1*z1 + w1*w1;
|
||||
|
||||
// Use home-baked polynomial to compute 1/sqrt(Dist2)
|
||||
// since we know the range is 0.707 >= Dist2 <= 1.0
|
||||
// we'll split in half.
|
||||
|
||||
if( Dist2<0.857f )
|
||||
OneOverL = (((0.699368f)*Dist2) + -1.819985f)*Dist2 + 2.126369f; //0.0000792
|
||||
else
|
||||
OneOverL = (((0.454012f)*Dist2) + -1.403517f)*Dist2 + 1.949542f; //0.0000373
|
||||
|
||||
// Renormalize
|
||||
q->x = x1*OneOverL;
|
||||
q->y = y1*OneOverL;
|
||||
q->z = z1*OneOverL;
|
||||
q->w = w1*OneOverL;
|
||||
|
||||
return *q;
|
||||
}
|
||||
|
||||
void TSTransform::applyScale(F32 scale, MatrixF * mat)
|
||||
{
|
||||
mat->scale(Point3F(scale,scale,scale));
|
||||
}
|
||||
|
||||
void TSTransform::applyScale(const Point3F & scale, MatrixF * mat)
|
||||
{
|
||||
mat->scale(scale);
|
||||
}
|
||||
|
||||
void TSTransform::applyScale(const TSScale & scale, MatrixF * mat)
|
||||
{
|
||||
MatrixF mat2;
|
||||
TSTransform::setMatrix(scale.mRotate,&mat2);
|
||||
MatrixF mat3(mat2);
|
||||
mat3.inverse();
|
||||
mat2.scale(scale.mScale);
|
||||
mat2.mul(mat3);
|
||||
mat->mul(mat2);
|
||||
}
|
90
engine/ts/tsTransform.h
Executable file
90
engine/ts/tsTransform.h
Executable file
@ -0,0 +1,90 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TSTRANSFORM_H_
|
||||
#define _TSTRANSFORM_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
|
||||
class Stream;
|
||||
|
||||
/// compressed quaternion class
|
||||
struct Quat16
|
||||
{
|
||||
enum { MAX_VAL = 0x7fff };
|
||||
|
||||
S16 x, y, z, w;
|
||||
|
||||
void read(Stream *);
|
||||
void write(Stream *);
|
||||
|
||||
void identity();
|
||||
QuatF &getQuatF( QuatF * q ) const;
|
||||
void set( const QuatF & q );
|
||||
S32 operator==( const Quat16 & q ) const;
|
||||
};
|
||||
|
||||
/// class to handle general scaling case
|
||||
///
|
||||
/// transform = rot * scale * inv(rot)
|
||||
struct TSScale
|
||||
{
|
||||
QuatF mRotate;
|
||||
Point3F mScale;
|
||||
|
||||
void identity() { mRotate.identity(); mScale.set( 1.0f,1.0f,1.0f ); }
|
||||
S32 operator==( const TSScale & other ) const { return mRotate==other.mRotate && mScale==other.mScale; }
|
||||
};
|
||||
|
||||
/// struct for encapsulating ts transform related static functions
|
||||
struct TSTransform
|
||||
{
|
||||
static Point3F & interpolate(const Point3F & p1, const Point3F & p2, F32 t, Point3F *);
|
||||
static F32 interpolate(F32 p1, F32 p2, F32 t);
|
||||
static QuatF & interpolate(const QuatF & q1, const QuatF & q2, F32 t, QuatF *);
|
||||
static TSScale & interpolate(const TSScale & s1, const TSScale & s2, F32 t, TSScale *);
|
||||
|
||||
static void setMatrix(const QuatF & q, MatrixF *);
|
||||
static void setMatrix(const QuatF & q, const Point3F & p, MatrixF *);
|
||||
|
||||
static void applyScale(F32 scale, MatrixF *);
|
||||
static void applyScale(const Point3F & scale, MatrixF *);
|
||||
static void applyScale(const TSScale & scale, MatrixF *);
|
||||
};
|
||||
|
||||
inline Point3F & TSTransform::interpolate(const Point3F & p1, const Point3F & p2, F32 t, Point3F * p)
|
||||
{
|
||||
p->x = p1.x + t * (p2.x-p1.x);
|
||||
p->y = p1.y + t * (p2.y-p1.y);
|
||||
p->z = p1.z + t * (p2.z-p1.z);
|
||||
return *p;
|
||||
}
|
||||
|
||||
inline F32 TSTransform::interpolate(F32 p1, F32 p2, F32 t)
|
||||
{
|
||||
return p1 + t*(p2-p1);
|
||||
}
|
||||
|
||||
inline TSScale & TSTransform::interpolate(const TSScale & s1, const TSScale & s2, F32 t, TSScale * s)
|
||||
{
|
||||
TSTransform::interpolate(s1.mRotate,s2.mRotate,t,&s->mRotate);
|
||||
TSTransform::interpolate(s1.mScale,s2.mScale,t,&s->mScale);
|
||||
return *s;
|
||||
}
|
||||
|
||||
inline void TSTransform::setMatrix( const QuatF & q, const Point3F & p, MatrixF * pDest )
|
||||
{
|
||||
q.setMatrix(pDest);
|
||||
pDest->setColumn(3,p);
|
||||
}
|
||||
|
||||
inline void TSTransform::setMatrix( const QuatF & q, MatrixF * pDest )
|
||||
{
|
||||
q.setMatrix(pDest);
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user