tge/engine/ts/tsShape.h
2025-02-17 23:17:30 -06:00

632 lines
22 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// 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;
Vector<U32> mReflectanceMaps;
Vector<U32> mBumpMaps;
Vector<U32> mDetailMaps;
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();
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]); }
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);
/// @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