tge/engine/ts/tsShapeInstance.h
2017-04-17 06:17:10 -06:00

1036 lines
37 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _TSSHAPEINSTANCE_H_
#define _TSSHAPEINSTANCE_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TSSHAPE_H_
#include "ts/tsShape.h"
#endif
#ifndef _TSINTEGERSET_H_
#include "ts/tsIntegerSet.h"
#endif
#ifndef _CONSOLE_H_
#include "console/console.h"
#endif
#ifndef _GBITMAP_H_
#include "dgl/gBitmap.h"
#endif
#ifndef _NETSTRINGTABLE_H_
#include "sim/netStringTable.h"
#endif
class RenderItem;
class TSThread;
class TSLastDetail;
class ConvexFeature;
//-------------------------------------------------------------------------------------
// Instance versions of shape objects
//-------------------------------------------------------------------------------------
struct TSVertex
{
Point3F p; ///< Position
ColorI color; ///< Color
Point2F t1; ///< Texture coordinate 1
Point2F t2; ///< Texture coordinate 2
};
/// An instance of a 3space shape.
///
/// @section TSShapeInstance_intro Introduction
///
/// A 3space model represents a significant amount of data. There are multiple meshes,
/// skeleton information, as well as animation data. Some of this, like the skeletal
/// transforms, are unique for each instance of the model (as different instances are
/// likely to be in different states of animation), while most of it, like texturing
/// information and vertex data, is the same amongst all instances of the shape.
///
/// To keep this data from being replicated for every instance of a 3shape object, Torque
/// uses the ResManager to instantiate and track TSShape objects. TSShape handles reading
/// and writing 3space models, as well as keeping track of static model data, as discussed
/// above. TSShapeInstance keeps track of all instance specific data, such as the currently
/// playing sequences or the active node transforms.
///
/// TSShapeInstance contains all the functionality for 3space models, while TSShape acts as
/// a repository for common data.
///
/// @section TSShapeInstance_functionality What Does TSShapeInstance Do?
///
/// TSShapeInstance handles several areas of functionality:
/// - Collision.
/// - Rendering.
/// - Animation.
/// - Updating skeletal transforms.
/// - Ballooning (see setShapeBalloon() and getShapeBalloon())
///
/// For an excellent example of how to render a TSShape in game, see TSStatic. For examples
/// of how to procedurally animate models, look at Player::updateLookAnimation().
class TSShapeInstance
{
public:
struct ObjectInstance;
friend class TSThread;
friend class TSLastDetail;
friend class TSPartInstance;
static void init();
static void destroy();
/// Base class for all renderable objects, including mesh objects and decal objects.
///
/// An ObjectInstance points to the renderable items in the shape...
struct ObjectInstance
{
/// this needs to be set before using an objectInstance...tells us where to
/// look for the transforms...gets set be shape instance 'setStatics' method
static MatrixF * smTransforms;
S32 nodeIndex;
/// Gets the transform of this object
MatrixF * getTransform();
/// @name Render Functions
/// @{
/// Render! This draws the base-textured object.
virtual void render(S32 objectDetail, TSMaterialList *);
/// Renders the environment map
virtual void renderEnvironmentMap(S32 objectDetail, TSMaterialList *);
/// Renders the detail map
virtual void renderDetailMap(S32 objectDetail, TSMaterialList *);
/// Renders the fog texture
virtual void renderFog(S32 objectDetail, TSMaterialList*);
/// Renders the lightmap
virtual void renderLightMap(S32 objectDetail, TSMaterialList *);
/// @}
/// @name Collision Routines
/// @{
virtual bool buildPolyList(S32 objectDetail, AbstractPolyList *, U32 & surfaceKey);
virtual bool getFeatures(S32 objectDetail, const MatrixF& mat, const Point3F& n, ConvexFeature*, U32 & surfaceKey);
virtual void support(S32 od, const Point3F& v, F32* currMaxDP, Point3F* currSupport);
/// Ray cast for collision detection
virtual bool castRay(S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo *);
/// @}
};
/// These are set up by default based on shape data
struct MeshObjectInstance : ObjectInstance
{
TSMesh * const * meshList; ///< one mesh per detail level... Null entries allowed.
const TSObject * object;
S32 frame;
S32 matFrame;
F32 visible;
S32 getSizeVB(S32 size);
bool hasMergeIndices();
/// @name Vertex Buffer functions
/// Fills in the mesh's vertex buffer
/// @{
void fillVB(S32 vb, TSMaterialList *materials);
void morphVB(S32 vb, S32 &previousMerge, S32 objectDetail, TSMaterialList *materials);
void renderVB(S32 vb, S32 objectDetail, TSMaterialList *materials);
/// @}
/// @name Render Methods
/// This just selects the right detail level (mesh) and calls mesh's render
/// @{
void render(S32 objectDetail, TSMaterialList *);
void renderEnvironmentMap(S32 objectDetail, TSMaterialList *);
void renderDetailMap(S32 objectDetail, TSMaterialList *);
void renderShadow(S32 objectDetail, const MatrixF & mat, S32 dim, U32 * bits, TSMaterialList *);
void renderFog(S32 objectDetail, TSMaterialList*);
void renderLightMap(S32 objectDetail, TSMaterialList *);
/// @}
/// Gets the mesh with specified detail level
TSMesh * getMesh(S32 num) const { return num<object->numMeshes ? *(meshList+num) : NULL; }
/// @name Collision Routines
/// @{
bool buildPolyList(S32 objectDetail, AbstractPolyList *, U32 & surfaceKey);
bool getFeatures(S32 objectDetail, const MatrixF& mat, const Point3F& n, ConvexFeature*, U32 & surfaceKey);
void support(S32 od, const Point3F& v, F32* currMaxDP, Point3F* currSupport);
bool castRay(S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo *);
/// @}
};
/// Also set up based on shape data...they refer to mesh object instances
struct DecalObjectInstance : ObjectInstance
{
TSDecalMesh * const * decalList;
const MeshObjectInstance * targetObject;
const TSShape::Decal * decalObject;
S32 frame;
void render(S32 objectDetail, TSMaterialList *);
TSDecalMesh * getDecalMesh(S32 num) const { return num<decalObject->numMeshes ? *(decalList+num) : NULL; }
// we don't do these things...
// void renderEnvironmentMap(S32,TSMaterialList*) {}
// void renderDecalMap(S32,TSMaterialList*) {}
// bool getFeatures(S32 objectDetail, const MatrixF& mat, const Point3F& n, ConvexFeature*, U32 & surfaceKey);
bool buildPolyList(S32, AbstractPolyList *, U32 &) { return false; }
bool castRay(S32, const Point3F &, const Point3F &, RayInfo *) { return false; }
};
/// IFL objects ... controlled by animation but also can be controlled by user
struct IflMaterialInstance
{
const TSShape::IflMaterial * iflMaterial;
S32 frame;
};
//-------------------------------------------------------------------------------------
// Lists used for storage of transforms, nodes, objects, etc...
//-------------------------------------------------------------------------------------
public:
Vector<MeshObjectInstance> mMeshObjects;
Vector<DecalObjectInstance> mDecalObjects;
Vector<IflMaterialInstance> mIflMaterialInstances;
/// storage space for node transforms
Vector<MatrixF> mNodeTransforms;
/// @name Reference Transform Vectors
/// unused until first transition
/// @{
Vector<Quat16> mNodeReferenceRotations;
Vector<Point3F> mNodeReferenceTranslations;
Vector<F32> mNodeReferenceUniformScales;
Vector<Point3F> mNodeReferenceScaleFactors;
Vector<Quat16> mNodeReferenceArbitraryScaleRots;
/// @}
/// @name Workspace for Node Transforms
/// @{
static Vector<QuatF> smNodeCurrentRotations;
static Vector<Point3F> smNodeCurrentTranslations;
static Vector<F32> smNodeCurrentUniformScales;
static Vector<Point3F> smNodeCurrentAlignedScales;
static Vector<TSScale> smNodeCurrentArbitraryScales;
/// @}
/// @name Threads
/// keep track of who controls what on currently animating shape
/// @{
static Vector<TSThread*> smRotationThreads;
static Vector<TSThread*> smTranslationThreads;
static Vector<TSThread*> smScaleThreads;
/// @}
//-------------------------------------------------------------------------------------
// Misc.
//-------------------------------------------------------------------------------------
protected:
/// @name Ground Transform Data
/// @{
MatrixF mGroundTransform;
TSThread * mGroundThread;
/// @}
bool mScaleCurrentlyAnimated;
S32 mCurrentDetailLevel;
/// 0-1, how far along from current to next (higher) detail level...
///
/// 0=at this dl, 1=at higher detail level, where higher means bigger size on screen
/// for dl=0, we use twice detail level 0's size as the size of the "next" dl
F32 mCurrentIntraDetailLevel;
Resource<TSShape> hShape;
TSShape * mShape;
TSMaterialList* mMaterialList; ///< by default, points to hShape material list
bool mOwnMaterialList; ///< Does this own the material list pointer?
TextureHandle mEnvironmentMap;
bool mEnvironmentMapOn;
F32 mEnvironmentMapAlpha;
bool mAllowTwoPassEnvironmentMap;
bool mAlphaIsReflectanceMap;
bool mAllowTwoPassDetailMap;
S32 mMaxEnvironmentMapDL;
S32 mMaxDetailMapDL;
S32 mMaxLightMapDL;
bool mAlphaAlways;
F32 mAlphaAlwaysValue;
bool mDrawFog;
bool mBalloonShape; ///< Is this shape ballooned?
F32 mBalloonValue; ///< How much is it ballooned?
bool mUseOverrideTexture;
TextureHandle mOverrideTexture;
U32 debrisRefCount;
// the threads...
Vector<TSThread*> mThreadList;
Vector<TSThread*> mTransitionThreads;
/// @name Transition nodes
/// keep track of nodes that are involved in a transition
///
/// @note this only tracks nodes we're transitioning from...
/// nodes we're transitioning to are implicitly handled
/// (i.e., we don't need to keep track of them)
/// @{
TSIntegerSet mTransitionRotationNodes;
TSIntegerSet mTransitionTranslationNodes;
TSIntegerSet mTransitionScaleNodes;
/// @}
/// keep track of nodes with animation restrictions put on them
TSIntegerSet mMaskRotationNodes;
TSIntegerSet mMaskPosXNodes;
TSIntegerSet mMaskPosYNodes;
TSIntegerSet mMaskPosZNodes;
TSIntegerSet mDisableBlendNodes;
TSIntegerSet mHandsOffNodes; ///< Nodes that aren't animated through threads automatically
TSIntegerSet mCallbackNodes;
/// state variables
U32 mTriggerStates;
bool initGround();
void addPath(TSThread * gt, F32 start, F32 end, MatrixF * mat = NULL);
/// @name Render functions
/// @{
void setupTexturing(S32 dl, F32 intraDL);
bool twoPassEnvironmentMap();
bool twoPassDetailMap();
bool twoPassFog();
bool twoPassLightMap();
void renderEnvironmentMap();
void renderDetailMap();
void renderFog();
void renderLightMap();
/// @}
/// @name DirectX render functions
/// @{
bool renderMeshesX(S32 ss, S32 od);
bool renderDecalsX(S32 ss, S32 od);
///@}
public:
bool fillVB();
TSShape* getShape() { return mShape; }
/// @name Statics
/// set non-rendering static variables used for accessing shape data
/// @{
void setStatics(S32 dl = 0, F32 interDL = 0.0f, const Point3F * shapeScale = NULL);
void clearStatics();
/// @}
TSMaterialList* getMaterialList() { return mMaterialList; }
void setMaterialList(TSMaterialList*); ///< we won't own the material list unless we clone it (see below)
void cloneMaterialList(); ///< call this to own the material list -- i.e., we'll make a copy of the currently
///< set material list and be responsible for deleting it...if we don't own the list,
///< then the application better not delete the set list out from under us (all this
///< is handled automatically when using the shape's original list).
bool ownMaterialList() const { return mOwnMaterialList; }
void reSkin(StringHandle& newBaseHandle);
enum
{
MaskNodeRotation = 0x01,
MaskNodePosX = 0x02,
MaskNodePosY = 0x04,
MaskNodePosZ = 0x08,
MaskNodeBlend = 0x10,
MaskNodeAll = MaskNodeRotation|MaskNodePosX|MaskNodePosY|MaskNodePosZ|MaskNodeBlend,
MaskNodeAllButBlend = MaskNodeRotation|MaskNodePosX|MaskNodePosY|MaskNodePosZ,
MaskNodeAllButRotation = MaskNodePosX|MaskNodePosY|MaskNodePosZ|MaskNodeBlend,
MaskNodeAllButPosX = MaskNodeRotation|MaskNodePosY|MaskNodePosZ|MaskNodeBlend,
MaskNodeAllButPosY = MaskNodeRotation|MaskNodePosX|MaskNodePosZ|MaskNodeBlend,
MaskNodeAllButPosZ = MaskNodeRotation|MaskNodePosX|MaskNodePosY|MaskNodeBlend,
MaskNodeHandsOff = 0x20, ///< meaning, don't even set to default, programmer controls it (blend still applies)
MaskNodeCallback = 0x40 ///< meaning, get local transform via callback function (see setCallback)
///< callback data2 is node index, callback return value is pointer to local transform
///< Note: won't get this callback everytime you animate...application responsibility
///< to make sure matrix pointer continues to point to valid and updated local transform
};
/// @name Node Masking
/// set node masking...
/// @{
void setNodeAnimationState(S32 nodeIndex, U32 animationState);
U32 getNodeAnimationState(S32 nodeIndex);
/// @}
/// @name Trigger states
/// check trigger value
/// @{
bool getTriggerState(U32 stateNum, bool clearState = true);
void setTriggerState(U32 stateNum, bool on);
void setTriggerStateBit(U32 stateBit, bool on);
/// @}
/// @name Callback Functions
/// @{
typedef void (*CallbackFunction)(TSShapeInstance*, MatrixF*, S32 node, U32 data);
CallbackFunction mCallback;
U32 mCallbackData;
void setCallback(CallbackFunction cb, U32 data1) { mCallback = cb; mCallbackData = data1; }
/// @}
/// @name Debris Management
/// @{
void incDebrisRefCount();
void decDebrisRefCount();
U32 getDebrisRefCount();
/// @}
/// @name AlphaAlways
/// AlphaAlways allows the entire model to become translucent at the same value
/// @{
void setAlphaAlways(F32 value) { mAlphaAlways = (value<0.99f); mAlphaAlwaysValue = value; }
F32 getAlphaAlwaysValue() { return mAlphaAlways ? mAlphaAlwaysValue : 1.0f; }
bool getAlphaAlways() { return mAlphaAlways; }
/// @}
/// @name Ballooning
/// Blow up the model
/// @{
void setShapeBalloon(F32 value) { mBalloonShape = value > 1; mBalloonValue = value; }
F32 getBalloonValue() const { return mBalloonShape ? mBalloonValue : 1.0f; }
/// @}
/// @name Override Texture
/// @{
void setOverrideTexture(TextureHandle override) { mOverrideTexture = override; mUseOverrideTexture = true; }
void clearOverrideTexture() { mOverrideTexture = NULL; mUseOverrideTexture = false; }
/// @}
enum
{
NO_ENVIRONMENT_MAP, ///< don't render environment map
ENVIRONMENT_MAP_MULTI_1, ///< render with multi-texturing (+1 texture units), shape alpha = reflectance map
ENVIRONMENT_MAP_MULTI_3, ///< render with multi-texturing (+3 texture units), reflectance map separate texture
ENVIRONMENT_MAP_TWO_PASS, ///< render in two passes -- mAllowTwoPassEnvironmentMap must be true
///
/// @note if reflectance map is separate from shape texture then won't render unless card has 4 texture units
/// However, translucency won't work quite right if reflection map not separated -- probably ok though.
/// Bottom line: previous 2 items probably only used for special shapes...
NO_DETAIL_MAP,
DETAIL_MAP_MULTI_1,
DETAIL_MAP_MULTI_2,
DETAIL_MAP_TWO_PASS,
NO_FOG,
FOG_MULTI_1,
FOG_MULTI_1_TEXGEN,
FOG_TWO_PASS,
FOG_TWO_PASS_TEXGEN,
NO_LIGHT_MAP,
LIGHT_MAP_MULTI,
LIGHT_MAP_TWO_PASS
};
const TextureHandle & getEnvironmentMap() { return mEnvironmentMap; }
F32 getEnvironmentMapAlpha() { return mEnvironmentMapAlpha; }
void setEnvironmentMap(const TextureHandle& map) { mEnvironmentMap = map; }
void setEnvironmentMapOn(bool on, F32 alpha = 0.25f) { mEnvironmentMapOn = on; mEnvironmentMapAlpha = alpha; }
//-------------------------------------------------------------------------------------
// private methods for setting up and affecting animation
//-------------------------------------------------------------------------------------
private:
/// @name Private animation methods
/// These are private methods for setting up and affecting animation
/// @{
void updateTransitions();
void handleDefaultScale(S32 a, S32 b, TSIntegerSet & scaleBeenSet);
void handleTransitionNodes(S32 a, S32 b);
void handleNodeScale(S32 a, S32 b);
void handleAnimatedScale(TSThread *, S32 a, S32 b, TSIntegerSet &);
void handleMaskedPositionNode(TSThread *, S32 nodeIndex, S32 offset);
void handleBlendSequence(TSThread *, S32 a, S32 b);
void checkScaleCurrentlyAnimated();
/// @}
//-------------------------------------------------------------------------------------
// animate, render, & detail control
//-------------------------------------------------------------------------------------
public:
struct RenderData
{
MatrixF * currentTransform;
S32 detailLevel;
F32 intraDetailLevel;
S32 environmentMapMethod;
S32 detailMapMethod;
S32 detailMapTE;
S32 environmentMapTE;
F32 environmentMapAlpha;
U32 environmentMapGLName;
S32 baseTE;
F32 detailTextureScale;
F32 detailMapAlpha;
S32 lightMapMethod;
S32 lightMapTE;
bool fadeSet;
bool lightingOn;
bool alwaysAlpha;
F32 alwaysAlphaValue;
bool balloonShape;
F32 balloonValue;
U32 materialFlags;
S32 materialIndex;
const Point3F * objectScale;
bool fogOn;
S32 fogMethod;
S32 fogTE;
Point4F fogColor;
Point4F fogTexGenS;
Point4F fogTexGenT;
TextureHandle * fogMapHandle; ///< used by texgen fog
bool useOverride;
TextureHandle override;
bool textureMatrixPushed;
bool fogTexture;
GBitmap *fogBitmap;
TextureHandle *fogHandle;
bool renderDecals;
struct VertexAlpha
{
/// @name Vertex Data
/// Track various contributors to vertex alpha
/// @{
F32 vis;
F32 emap;
F32 fog;
F32 always;
/// @}
/// current result...
F32 current;
void init() { current=vis=emap=fog=always=1.0f; }
bool set() { F32 old = current; current =vis*emap*fog*always; return (mFabs(old-current)>0.001f); }
} vertexAlpha;
};
static RenderData smRenderData;
/// if true, skip these objects
static bool smNoRenderTranslucent;
static bool smNoRenderNonTranslucent;
/// when taking hiQuality snapshot, scale intermediate bitmaps up to this amount
static S32 smMaxSnapshotScale;
/// scale pixel size by this amount when selecting detail levels
static F32 smDetailAdjust;
/// a different error metrix used by newer shapes (screen error from hi detail)
static F32 smScreenError;
static bool smFogExemptionOn;
/// never choose detail level number below this value (except if
/// only way to get a visible detail)
static S32 smNumSkipRenderDetails;
static bool smSkipFirstFog;
static bool smSkipFog;
virtual void render(const Point3F * objectScale = NULL);
virtual void render(S32 dl, F32 intraDL = 0.0f, const Point3F * objectScale = NULL);
void renderShadow(S32 dl, const MatrixF & mat, S32 dim, U32 * bits);
static void setupFog(F32 fogAmount, const ColorF & fogColor);
void setupFog(F32 fogAmount, TextureHandle * fogMap, Point4F & s, Point4F & t);
GBitmap * snapshot(U32 width, U32 height, bool mipmap, MatrixF & cameraMatrix, bool hiQuality);
GBitmap * snapshot(U32 width, U32 height, bool mip, MatrixF &cameraPos, S32 dl, F32 intraDL, bool hiQuality);
static GBitmap * snapshot(TSShape *, U32 width, U32 height, bool mipmap, MatrixF & cameraMatrix, S32 dl, F32 intraDL = 0.0f, bool hiQuality = false);
void animate();
void animate(S32 dl);
void animateNodes(S32 ss);
void animateVisibility(S32 ss);
void animateFrame(S32 ss);
void animateMatFrame(S32 ss);
void animateDecals(S32 ss);
void animateIfls();
void animateSubtrees(bool forceFull = true);
void animateNodeSubtrees(bool forceFull = true);
bool hasTranslucency();
bool hasSolid();
/// @name Animation Scale
/// Query about animated scale
/// @{
bool animatesScale() { return (mShape->mFlags & TSShape::AnyScale) != 0; }
bool animatesUniformScale() { return (mShape->mFlags & TSShape::UniformScale) != 0; }
bool animatesAlignedScale() { return (mShape->mFlags & TSShape::AlignedScale) != 0; }
bool animatesArbitraryScale() { return (mShape->mFlags & TSShape::ArbitraryScale) != 0; }
bool scaleCurrentlyAnimated() { return mScaleCurrentlyAnimated; }
/// @}
//
bool inTransition() { return !mTransitionThreads.empty(); }
/// @name Alpha Reflectance Mapping
/// open up mAlphaIsReflectanceMap for custom rendering
/// @{
bool queryAlphaIsReflectanceMap(){ return mAlphaIsReflectanceMap; }
void setAlphaIsReflectanceMap( bool val ){ mAlphaIsReflectanceMap = val; }
/// @}
/// @name Ground Transforms
/// These are hardly ever used. The animator of a model can make the bounding box
/// animate along with the object. Doing so will move the object with the bounding box.
/// The ground transform turns the world bounding box into the post-animation bounding box
/// when such a technique is used. However, few models actually use this technique.
/// @{
void animateGround(); ///< clears previous ground transform
MatrixF & getGroundTransform() { return mGroundTransform; }
void deltaGround(TSThread *, F32 start, F32 end, MatrixF * mat = NULL);
void deltaGround1(TSThread *, F32 start, F32 end, MatrixF& mat);
/// @}
U32 getNumDetails();
S32 getCurrentDetail();
F32 getCurrentIntraDetail();
void setCurrentDetail(S32 dl, F32 intraDL=1.0f);
S32 selectCurrentDetail(bool ignoreScale = false);
S32 selectCurrentDetail(const Point3F &offset, F32 invScale = 1.0f);
S32 selectCurrentDetail(F32 pixelSize);
S32 selectCurrentDetail2(F32 adjustedDist);
/// fancy detail selection -- uses screen error
S32 selectCurrentDetailEx(bool ignoreScale = false);
S32 selectCurrentDetail2Ex(F32 adjustedDist);
S32 selectCurrentDetailEx(F32 errorTOL);
enum
{
TransformDirty = BIT(0),
VisDirty = BIT(1),
FrameDirty = BIT(2),
MatFrameDirty = BIT(3),
DecalDirty = BIT(4),
IflDirty = BIT(5),
ThreadDirty = BIT(6),
AllDirtyMask = TransformDirty | VisDirty | FrameDirty | MatFrameDirty | DecalDirty | IflDirty | ThreadDirty
};
U32 * mDirtyFlags;
void setDirty(U32 dirty);
void clearDirty(U32 dirty);
//-------------------------------------------------------------------------------------
// collision interface routines
//-------------------------------------------------------------------------------------
public:
bool buildPolyList(AbstractPolyList *, S32 dl);
bool getFeatures(const MatrixF& mat, const Point3F& n, ConvexFeature*, S32 dl);
bool castRay(const Point3F & start, const Point3F & end, RayInfo *,S32 dl);
bool quickLOS(const Point3F & start, const Point3F & end, S32 dl) { return castRay(start,end,NULL,dl); }
Point3F support(const Point3F & v, S32 dl);
void computeBounds(S32 dl, Box3F & bounds); ///< uses current transforms to compute bounding box around a detail level
///< see like named method on shape if you want to use default transforms
//-------------------------------------------------------------------------------------
// Thread Control
//-------------------------------------------------------------------------------------
/// @name Thread Control
/// Threads! In order to animate an object, first you need to have an animation in the object.
/// Then, you need to get the TSShape of the object:
/// @code
/// TSShape* shape = mShapeInstance->getShape());
/// @endcode
/// Next, get the sequence and store::
/// @code
/// S32 seq = shape->findSequence("foo"));
/// @endcode
/// Create a new thread (if needed):
/// @code
/// TSThread* thread = mShapeInstance->addThread();
/// @endcode
/// Finally, set the position in the sequence:
/// @code
/// mShapeInstance->setSequence(thread, seq, 0)
/// @endcode
/// @{
public:
TSThread * addThread(); ///< Create a new thread
inline TSThread * getThread(S32 threadNumber) const; ///< @note threads can change order, best to hold
///< onto a thread from the start
void destroyThread(TSThread * thread); ///< Destroy a thread!
inline U32 threadCount() const; ///< How many threads are there?
void setSequence(TSThread *, S32 seq, F32 pos);///< Get the thread a sequence
void transitionToSequence(TSThread *, S32 seq, F32 pos, F32 duration, bool continuePlay); ///< Transition to a sequence
void clearTransition(TSThread *); ///< Stop transitions
inline U32 getSequence(TSThread *) const; ///< Get the sequence of the thread
inline void setBlendEnabled(TSThread *thread, bool blendOn); ///< Set whether or not the thread will blend
inline bool getBlendEnabled(TSThread *thread) const; ///< Does this thread blend?
inline F32 getTime(TSThread * thread) const; ///< Get how long the thread has been playing
inline F32 getPos(TSThread * thread) const; ///< Get the position in the thread
inline void setTime(TSThread * thread, F32 time); ///< Set how long into the thread to use
inline void setPos(TSThread * thread, F32 pos); ///< Set the position of the thread
inline bool isInTransition(TSThread * thread) const; ///< Is this thread in transition?
inline F32 getTimeScale(TSThread * thread) const; ///< Get the time scale of the thread
inline void setTimeScale(TSThread * thread, F32); ///< Set the time scale of the thread
inline F32 getDuration(TSThread * thread) const; ///< Get the duration of the thread
inline F32 getScaledDuration(TSThread * thread) const; ///< Get the duration of the thread with the scale factored in
inline S32 getKeyframeCount(TSThread * thread) const; ///< Get the number of keyframes
inline S32 getKeyframeNumber(TSThread * thread) const; ///< Get which keyframe the thread is on
inline void setKeyframeNumber(TSThread * thread, S32 kf); ///< Set which keyframe the thread is on
inline void advanceTime(F32 delta, TSThread *); ///< advance time on a particular thread
void advanceTime(F32 delta); ///< advance time on all threads
inline void advancePos(F32 delta, TSThread *); ///< advance pos on a particular thread
void advancePos(F32 delta); ///< advance pos on all threads
/// @}
//-------------------------------------------------------------------------------------
// constructors, destructors, initialization, io
//-------------------------------------------------------------------------------------
TSShapeInstance( const Resource<TSShape> & shape, bool loadMaterials = true);
TSShapeInstance( TSShape * pShape, bool loadMaterials = true);
~TSShapeInstance();
void buildInstanceData(TSShape *, bool loadMaterials);
void dump(Stream &);
void dumpNode(Stream &, S32 level, S32 nodeIndex, Vector<S32> & detailSizes);
void dumpDecals(Stream &, S32 indent, MeshObjectInstance *);
void *mData; ///< available for use by app...initialized to 0
};
//-------------------------------------------------------------------------------------
// Thread class
//-------------------------------------------------------------------------------------
/// 3space animation thread.
///
/// An animation thread: runtime data associated with a single sequence that is
/// running (or two sequences if in transition between them).
///
/// A shape instance can have multiple threads running. When multiple threads are running,
/// which thread/sequence controls which node or object is determined based
/// on priority of the sequence.
///
/// @note all thread data and methods are private (but TSShapeInstance is a friend).
/// Users should treat thread pointers like keys -- they are used to ID
/// the thread when interfacing with the shape, but are not manipulated
/// by anything but the TSShapeInstance. See "Thread control" methods
/// for more info on controlling threads.
class TSThread
{
friend class TSShapeInstance;
S32 priority;
TSShapeInstance * mShapeInstance; ///< Instance of the shape that this thread animates
const TSSequence * sequence; ///< Sequence this thread will perform
F32 pos;
F32 timeScale; ///< How fast to play through the sequence
S32 keyNum1; ///< Keyframe at or before current position
S32 keyNum2; ///< Keyframe at or after current position
F32 keyPos;
bool blendDisabled; ///< Blend with other sequences?
/// if in transition...
struct TransitionData
{
bool inTransition;
F32 duration;
F32 pos;
F32 direction;
F32 targetScale; ///< time scale for sequence we are transitioning to (during transition only)
///< this is either 1 or 0 (if 1 target sequence plays as we transition, if 0 it doesn't)
TSIntegerSet oldRotationNodes; ///< nodes controlled by this thread pre-transition
TSIntegerSet oldTranslationNodes; ///< nodes controlled by this thread pre-transition
TSIntegerSet oldScaleNodes; ///< nodes controlled by this thread pre-transition
U32 oldSequence; ///< sequence that was set before transition began
F32 oldPos; ///< position of sequence before transition began
} transitionData;
struct
{
F32 start;
F32 end;
S32 loop;
} path;
bool makePath;
/// given a position on the thread, choose correct keyframes
/// slight difference between one-shot and cyclic sequences -- see comments below for details
void selectKeyframes(F32 pos, const TSSequence * seq, S32 * k1, S32 * k2, F32 * kpos);
void getGround(F32 p, MatrixF * pMat);
/// @name Triggers
/// Triggers are used to do something once a certain animation point has been reached.
///
/// For example, when the player's foot animation hits the ground, a foot puff and
/// foot print are triggered from the thread.
///
/// These are called by advancePos()
/// @{
void animateTriggers();
void activateTriggers(F32 a, F32 b);
/// @}
// methods -- accessible through shape only
void setSequence(S32 seq, F32 pos);
void transitionToSequence(S32 seq, F32 pos, F32 duration, bool continuePlay);
inline void advanceTime(F32 delta);
void advancePos(F32 delta);
inline F32 getTime() const;
inline void setTime(F32 time);
inline F32 getPos() const;
inline void setPos(F32 pos);
inline bool isInTransition() const;
inline F32 getTimeScale() const;
inline void setTimeScale(F32 ts);
inline F32 getDuration() const;
inline F32 getScaledDuration() const;
inline S32 getKeyframeCount() const;
inline S32 getKeyframeNumber() const;
void setKeyframeNumber(S32 kf);
TSThread(TSShapeInstance*);
TSThread() {}
public:
S32 operator<(const TSThread &) const;
};
typedef TSShapeInstance::ObjectInstance TSObjectInstance;
// TSThread inlines
inline void TSThread::advanceTime(F32 delta)
{
advancePos(timeScale * delta / getDuration());
}
inline F32 TSThread::getTime() const
{
return transitionData.inTransition ? transitionData.pos * transitionData.duration : pos * sequence->duration;
}
inline void TSThread::setTime(F32 time)
{
setPos(timeScale * time/getDuration());
}
inline F32 TSThread::getPos() const
{
return transitionData.inTransition ? transitionData.pos : pos;
}
inline void TSThread::setPos(F32 pos)
{
advancePos(pos-getPos());
}
inline bool TSThread::isInTransition() const
{
return transitionData.inTransition;
}
inline F32 TSThread::getTimeScale() const
{
return timeScale;
}
inline void TSThread::setTimeScale(F32 ts)
{
timeScale = ts;
}
inline F32 TSThread::getDuration() const
{
return transitionData.inTransition ? transitionData.duration : sequence->duration;
}
inline F32 TSThread::getScaledDuration() const
{
return getDuration() / mFabs(timeScale);
}
inline S32 TSThread::getKeyframeCount() const
{
AssertFatal(!transitionData.inTransition,"TSThread::getKeyframeCount: not while in transition");
return sequence->numKeyframes + 1;
}
inline S32 TSThread::getKeyframeNumber() const
{
const TSShape * shape = mShapeInstance->mShape;
AssertFatal(!transitionData.inTransition,"TSThread::getKeyframeNumber: not while in transition");
return keyNum1;
}
// TSShapeInstance inlines
inline MatrixF * TSShapeInstance::ObjectInstance::getTransform()
{
return nodeIndex<0 ? NULL : smTransforms + nodeIndex;
}
inline TSThread * TSShapeInstance::getThread(S32 threadNumber) const
{
AssertFatal(threadNumber < mThreadList.size() && threadNumber>=0,"TSShapeInstance::getThread: threadNumber out of bounds.");
return mThreadList[threadNumber];
}
inline U32 TSShapeInstance::threadCount() const
{
return mThreadList.size();
}
inline U32 TSShapeInstance::getSequence(TSThread * thread) const
{
AssertFatal( (thread->sequence - mShape->sequences.address())>=0, "TSShapeInstance::getSequence: range error A");
AssertFatal( (thread->sequence - mShape->sequences.address())<mShape->sequences.size(), "TSShapeInstance::getSequence: range error B");
return (U32) (thread->sequence - mShape->sequences.address());
}
inline void TSShapeInstance::setBlendEnabled(TSThread * thread, bool blendOn)
{
thread->blendDisabled = !blendOn;
}
inline bool TSShapeInstance::getBlendEnabled(TSThread * thread) const
{
return !thread->blendDisabled;
}
F32 TSShapeInstance::getTime(TSThread * thread) const
{
return thread->getTime();
}
inline F32 TSShapeInstance::getPos(TSThread * thread) const
{
return thread->getPos();
}
inline void TSShapeInstance::setTime(TSThread * thread, F32 time)
{
thread->setTime(time);
}
inline void TSShapeInstance::setPos(TSThread * thread, F32 pos)
{
thread->setPos(pos);
}
inline bool TSShapeInstance::isInTransition(TSThread * thread) const
{
return thread->isInTransition();
}
inline F32 TSShapeInstance::getTimeScale(TSThread * thread) const
{
return thread->getTimeScale();
}
inline void TSShapeInstance::setTimeScale(TSThread * thread, F32 timeScale)
{
thread->setTimeScale(timeScale);
}
inline F32 TSShapeInstance::getDuration(TSThread * thread) const
{
return thread->getDuration();
}
inline F32 TSShapeInstance::getScaledDuration(TSThread * thread) const
{
return thread->getScaledDuration();
}
inline S32 TSShapeInstance::getKeyframeCount(TSThread * thread) const
{
return thread->getKeyframeCount();
}
inline S32 TSShapeInstance::getKeyframeNumber(TSThread * thread) const
{
return thread->getKeyframeNumber();
}
inline void TSShapeInstance::setKeyframeNumber(TSThread * thread, S32 kf)
{
thread->setKeyframeNumber(kf);
}
inline void TSShapeInstance::advanceTime(F32 delta, TSThread * thread)
{
thread->advanceTime(delta);
}
inline void TSShapeInstance::advancePos(F32 delta, TSThread * thread)
{
thread->advancePos(delta);
}
#endif