added everything

This commit is contained in:
Metario
2017-04-17 06:17:10 -06:00
commit 9c6ff74f19
6121 changed files with 1625704 additions and 0 deletions

1100
engine/ts/tsAnimate.cc Executable file

File diff suppressed because it is too large Load Diff

398
engine/ts/tsCollision.cc Executable file
View 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
View 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
View 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
View 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
View 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
View 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
View 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,&center);
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
View 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
View 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

File diff suppressed because it is too large Load Diff

328
engine/ts/tsMesh.h Executable file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

639
engine/ts/tsShape.h Executable file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

1035
engine/ts/tsShapeInstance.h Executable file

File diff suppressed because it is too large Load Diff

1523
engine/ts/tsShapeOldRead.cc Executable file

File diff suppressed because it is too large Load Diff

286
engine/ts/tsSortedMesh.cc Executable file
View 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
View 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
View 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
View 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
View 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