//----------------------------------------------------------------------------- // 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 numnumMeshes ? *(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 numnumMeshes ? *(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 mMeshObjects; Vector mDecalObjects; Vector mIflMaterialInstances; /// storage space for node transforms Vector mNodeTransforms; /// @name Reference Transform Vectors /// unused until first transition /// @{ Vector mNodeReferenceRotations; Vector mNodeReferenceTranslations; Vector mNodeReferenceUniformScales; Vector mNodeReferenceScaleFactors; Vector mNodeReferenceArbitraryScaleRots; /// @} /// @name Workspace for Node Transforms /// @{ static Vector smNodeCurrentRotations; static Vector smNodeCurrentTranslations; static Vector smNodeCurrentUniformScales; static Vector smNodeCurrentAlignedScales; static Vector smNodeCurrentArbitraryScales; /// @} /// @name Threads /// keep track of who controls what on currently animating shape /// @{ static Vector smRotationThreads; static Vector smTranslationThreads; static Vector 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 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 mThreadList; Vector 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 & 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 & 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())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