//----------------------------------------------------------------------------- // V12 Engine // // Copyright (c) 2001 GarageGames.Com // Portions Copyright (c) 2001 by Sierra Online, 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 class RenderItem; class TSThread; class ConvexFeature; //------------------------------------------------------------------------------------- // Instance versions of shape objects //------------------------------------------------------------------------------------- struct TSVertex { Point3F p; ColorI color; Point2F t1; Point2F t2; }; class TSShapeInstance { public: struct ObjectInstance; friend class TSThread; friend class TSPartInstance; static void init(); static void destroy(); // 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; MatrixF * getTransform(); virtual void render(S32 objectDetail, TSMaterialList *); virtual void renderEnvironmentMap(S32 objectDetail, TSMaterialList *); virtual void renderDetailMap(S32 objectDetail, TSMaterialList *); virtual void renderFog(S32 objectDetail, TSMaterialList*); // 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); 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(); void fillVB(S32 vb, TSMaterialList *materials); void morphVB(S32 vb, S32 &previousMerge, S32 objectDetail, TSMaterialList *materials); void renderVB(S32 vb, S32 objectDetail, TSMaterialList *materials); void render(S32 objectDetail, TSMaterialList *); // this just selects the right detail level (mesh) and calls meshes render 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*); TSMesh * getMesh(S32 num) const { return numnumMeshes ? *(meshList+num) : NULL; } // 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; // reference transform vectors -- unused until first transition Vector mNodeReferenceRotations; Vector mNodeReferenceTranslations; Vector mNodeReferenceUniformScales; Vector mNodeReferenceScaleFactors; Vector mNodeReferenceArbitraryScaleRots; // workspace for node transforms static Vector smNodeCurrentRotations; static Vector smNodeCurrentTranslations; static Vector smNodeCurrentUniformScales; static Vector smNodeCurrentAlignedScales; static Vector smNodeCurrentArbitraryScales; // keep track of who controls what on currently animating shape static Vector smRotationThreads; static Vector smTranslationThreads; static Vector smScaleThreads; //------------------------------------------------------------------------------------- // Misc. //------------------------------------------------------------------------------------- protected: // ground transform data MatrixF mGroundTransform; TSThread * mGroundThread; bool mScaleCurrentlyAnimated; S32 mCurrentDetailLevel; F32 mCurrentIntraDetailLevel; // 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 Resource hShape; TSShape * mShape; TSMaterialList* mMaterialList; // by default, points to hShape material list bool mOwnMaterialList; TextureHandle mEnvironmentMap; bool mEnvironmentMapOn; F32 mEnvironmentMapAlpha; bool mAllowTwoPassEnvironmentMap; bool mAlphaIsReflectanceMap; bool mAllowTwoPassDetailMap; S32 mMaxEnvironmentMapDL; S32 mMaxDetailMapDL; bool mAlphaAlways; F32 mAlphaAlwaysValue; bool mDrawFog; bool mBalloonShape; F32 mBalloonValue; bool mUseOverrideTexture; TextureHandle mOverrideTexture; U32 debrisRefCount; // the threads... Vector mThreadList; Vector mTransitionThreads; // 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; TSIntegerSet mCallbackNodes; // state variables U32 mTriggerStates; bool initGround(); void addPath(TSThread * gt, F32 start, F32 end, MatrixF * mat = NULL); // environment/detail map methods void setupTexturing(S32 dl, F32 intraDL); bool twoPassEnvironmentMap(); bool twoPassDetailMap(); bool twoPassFog(); void renderEnvironmentMap(); void renderDetailMap(); void renderFog(); // directX render bool renderMeshesX(S32 ss, S32 od); bool renderDecalsX(S32 ss, S32 od); public: TSShape* getShape() { return mShape; } // 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; } 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 }; // set node masking... void setNodeAnimationState(S32 nodeIndex, U32 animationState); U32 getNodeAnimationState(S32 nodeIndex); // check trigger values bool getTriggerState(U32 stateNum, bool clearState = true); void setTriggerState(U32 stateNum, bool on); void setTriggerStateBit(U32 stateBit, bool on); // callback functions... enum CallbackPurpose { LocalTransformCallback = 0 }; typedef void * (*CallbackFunction)(CallbackPurpose, U32 data1, U32 data2); CallbackFunction mCallback; U32 mCallbackData; void setCallback(CallbackFunction cb, U32 data1) { mCallback = cb; mCallbackData = data1; } // debris management... void incDebrisRefCount(); void decDebrisRefCount(); U32 getDebrisRefCount(); // alpha always void setAlphaAlways(F32 value) { mAlphaAlways = (value<0.99f); mAlphaAlwaysValue = value; } F32 getAlphaAlwaysValue() { return mAlphaAlways ? mAlphaAlwaysValue : 1.0f; } bool getAlphaAlways() { return mAlphaAlways; } // Balloon value void setShapeBalloon(F32 value) { mBalloonShape = value > 1; mBalloonValue = value; } F32 getBalloonValue() const { return mBalloonShape ? mBalloonValue : 1.0f; } // 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 }; 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: 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; 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 { // 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; // 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); 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); 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(); // 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(); } // open up mAlphaIsReflectanceMap for custom rendering bool queryAlphaIsReflectanceMap(){ return mAlphaIsReflectanceMap; } void setAlphaIsReflectanceMap( bool val ){ mAlphaIsReflectanceMap = val; } 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(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 = 1 << 0, VisDirty = 1 << 1, FrameDirty = 1 << 2, MatFrameDirty = 1 << 3, DecalDirty = 1 << 4, IflDirty = 1 << 5, ThreadDirty = 1 << 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 //------------------------------------------------------------------------------------- public: TSThread * addThread(); TSThread * getThread(S32 threadNumber); // Note: threads can change order, best to hold // onto a thread from the start void destroyThread(TSThread * thread); S32 threadCount(); void setSequence(TSThread *, S32 seq, F32 pos); void transitionToSequence(TSThread *, S32 seq, F32 pos, F32 duration, bool continuePlay); void clearTransition(TSThread *); S32 getSequence(TSThread *); void setBlendEnabled(TSThread *, bool blendOn); bool getBlendEnabled(TSThread *); F32 getTime(TSThread * thread); F32 getPos(TSThread * thread); void setTime(TSThread * thread, F32 time); void setPos(TSThread * thread, F32 pos); bool isInTransition(TSThread * thread); F32 getTimeScale(TSThread * thread); void setTimeScale(TSThread * thread, F32); F32 getDuration(TSThread * thread); F32 getScaledDuration(TSThread * thread); S32 getKeyframeCount(TSThread * thread); S32 getKeyframeNumber(TSThread * thread); void setKeyframeNumber(TSThread * thread, S32 kf); void advanceTime(F32 delta, TSThread *); // advance time on a particular thread void advanceTime(F32 delta); // advance time on all threads 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 *); U32 mData; // available for use by app...initialized to 0 }; inline MatrixF * TSShapeInstance::ObjectInstance::getTransform() { return nodeIndex<0 ? NULL : smTransforms + nodeIndex; } //------------------------------------------------------------------------------------- // Thread class //------------------------------------------------------------------------------------- // An animation thread: runtime data associated with a single sequence that is // running (or two sequences if in transition between them). // A shape 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 shapeInstance 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 shapeInstance. See "Thread control" methods // for more info on controlling threads. class TSThread { friend class TSShapeInstance; S32 priority; TSShapeInstance * mShapeInstance; const TSSequence * sequence; F32 pos; F32 timeScale; S32 keyNum1; S32 keyNum2; F32 keyPos; bool blendDisabled; // 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 S32 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; void selectKeyframes(F32 pos, const TSSequence * seq, S32 * k1, S32 * k2, F32 * kpos); void getGround(F32 p, MatrixF * pMat); // 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); void advanceTime(F32 delta); void advancePos(F32 delta); F32 getTime(); F32 getPos(); void setTime(F32); void setPos(F32); bool isInTransition(); F32 getTimeScale(); void setTimeScale(F32); F32 getDuration(); F32 getScaledDuration(); S32 getKeyframeCount(); S32 getKeyframeNumber(); void setKeyframeNumber(S32 kf); TSThread(TSShapeInstance*); TSThread() {} public: S32 operator<(const TSThread &) const; }; typedef TSShapeInstance::ObjectInstance TSObjectInstance; #endif