//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #ifndef _SCENEOBJECT_H_ #define _SCENEOBJECT_H_ #ifndef _PLATFORM_H_ #include "platform/platform.h" #endif #ifndef _NETOBJECT_H_ #include "sim/netObject.h" #endif #ifndef _COLLISION_H_ #include "collision/collision.h" #endif #ifndef _POLYHEDRON_H_ #include "collision/polyhedron.h" #endif #ifndef _ABSTRACTPOLYLIST_H_ #include "collision/abstractPolyList.h" #endif #ifndef _OBJECTTYPES_H_ #include "game/objectTypes.h" #endif #ifndef _COLOR_H_ #include "core/color.h" #endif #include "lightingSystem/sgLightManager.h" //-------------------------------------- Forward declarations... class SceneObject; class SceneGraph; class SceneState; class SceneRenderImage; class Box3F; class Point3F; class LightManager; class Convex; //---------------------------------------------------------------------------- /// Extension of the collision structore to allow use with raycasting. /// /// @see Collision struct RayInfo: public Collision { // The collision struct has object, point, normal & material. F32 t; }; //-------------------------------------------------------------------------- /// Reference to a scene object. /// /// @note There are two indiscretions here. First is the name, which refers rather /// blatantly to the container bin system. A hygiene issue. Next is the /// user defined U32, which is added solely for the zoning system. This should /// properly be split up into two structures, for the disparate purposes, especially /// since it's not nice to force the container bin to use 20 bytes structures when /// it could get away with a 16 byte version. class SceneObjectRef { public: SceneObject* object; SceneObjectRef* nextInBin; SceneObjectRef* prevInBin; SceneObjectRef* nextInObj; U32 zone; }; /// A scope frustum describes a pyramid to clip new portals against. It is /// rooted at the root position of the scoping query, which is not stored /// here. class ScopeFrustum { public: enum Constants { TopLeft = 0, TopRight = 1, BottomLeft = 2, BottomRight = 3 }; Point3F frustumPoints[4]; }; //---------------------------------------------------------------------------- class Container { public: struct Link { Link* next; Link* prev; Link(); void unlink(); void linkAfter(Link* ptr); }; struct CallbackInfo { AbstractPolyList* polyList; Box3F boundingBox; SphereF boundingSphere; void *key; }; static const U32 csmNumBins; static const F32 csmBinSize; static const F32 csmTotalBinSize; static const U32 csmRefPoolBlockSize; static U32 smCurrSeqKey; private: Link mStart,mEnd; SceneObjectRef* mFreeRefPool; Vector mRefPoolBlocks; SceneObjectRef* mBinArray; SceneObjectRef mOverflowBin; public: Container(); ~Container(); /// @name Basic database operations /// @{ /// typedef void (*FindCallback)(SceneObject*,void *key); void findObjects(U32 mask, FindCallback, void *key = NULL); void findObjects(const Box3F& box, U32 mask, FindCallback, void *key = NULL); void polyhedronFindObjects(const Polyhedron& polyhedron, U32 mask, FindCallback, void *key = NULL); /// @} /// @name Line intersection /// @{ /// bool castRay(const Point3F &start, const Point3F &end, U32 mask, RayInfo* info); bool collideBox(const Point3F &start, const Point3F &end, U32 mask, RayInfo* info); /// @} /// @name Poly list /// @{ /// bool buildPolyList(const Box3F& box, U32 mask, AbstractPolyList*,FindCallback=0,void *key = NULL); bool buildCollisionList(const Box3F& box, const Point3F& start, const Point3F& end, const VectorF& velocity, U32 mask,CollisionList* collisionList,FindCallback = 0,void *key = NULL,const Box3F *queryExpansion = 0); bool buildCollisionList(const Polyhedron& polyhedron, const Point3F& start, const Point3F& end, const VectorF& velocity, U32 mask, CollisionList* collisionList, FindCallback callback = 0, void *key = NULL); /// @} /// bool addObject(SceneObject*); bool removeObject(SceneObject*); void addRefPoolBlock(); SceneObjectRef* allocateObjectRef(); void freeObjectRef(SceneObjectRef*); void insertIntoBins(SceneObject*); void removeFromBins(SceneObject*); /// Checkbins makes sure that we're not just sticking the object right back /// where it came from. The overloaded insertInto is so we don't calculate /// the ranges twice. void checkBins(SceneObject*); void insertIntoBins(SceneObject*, U32, U32, U32, U32); private: Vector*> mSearchList;///< Object searches to support console querying of the database. ONLY WORKS ON SERVER S32 mCurrSearchPos; Point3F mSearchReferencePoint; void cleanupSearchVectors(); public: void initRadiusSearch(const Point3F& searchPoint, const F32 searchRadius, const U32 searchMask); U32 containerSearchNext(); F32 containerSearchCurrDist(); F32 containerSearchCurrRadiusDist(); }; //---------------------------------------------------------------------------- /// For simple queries. Simply creates a vector of the objects class SimpleQueryList { public: Vector mList; public: SimpleQueryList() { VECTOR_SET_ASSOCIATION(mList); } void insertObject(SceneObject* obj) { mList.push_back(obj); } static void insertionCallback(SceneObject* obj, void *key); }; //---------------------------------------------------------------------------- /// A 3D object. /// /// @section SceneObject_intro Introduction /// /// SceneObject exists as a foundation for 3D objects in Torque. It provides the /// basic functionality for: /// - A scene graph (in the Zones and Portals sections), allowing efficient /// and robust rendering of the game scene. /// - Various helper functions, including functions to get bounding information /// and momentum/velocity. /// - Collision detection, as well as ray casting. /// - Lighting. SceneObjects can register lights both at lightmap generation time, /// and dynamic lights at runtime (for special effects, such as from flame or /// a projectile, or from an explosion). /// - Manipulating scene objects, for instance varying scale. /// /// @section SceneObject_example An Example /// /// Melv May has written a most marvelous example object deriving from SceneObject. /// Unfortunately this page is too small to contain it. /// /// @see http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=3217 /// for a copy of Melv's example. class SceneObject : public NetObject, public Container::Link { typedef NetObject Parent; friend class Container; friend class SceneGraph; friend class SceneState; //-------------------------------------- Public constants public: enum { MaxObjectZones = 128 }; enum TraversalState { Pending = 0, Working = 1, Done = 2 }; enum SceneObjectMasks { ScaleMask = BIT(0), NextFreeMask = BIT(1) }; //-------------------------------------- Public interfaces // C'tors and D'tors private: SceneObject(const SceneObject&); ///< @deprecated disallowed public: SceneObject(); virtual ~SceneObject(); // forward declared for TSStatic and StaticShape // not used or transmited in any way by other classes // don't worry this is light-weight and network aware // // we need to override the options in all but the // objects that expose these to script - this is // static and NOT transmitted across the network... bool overrideOptions; bool receiveLMLighting; bool receiveSunLight; bool useAdaptiveSelfIllumination; bool useCustomAmbientLighting; bool customAmbientForSelfIllumination; ColorF customAmbientLighting; StringTableEntry lightGroupName; Vector lightIds; bool useLightingOcclusion; U32 moveSnapshotId; void findLightGroup(NetConnection *con); void findLights(const char *name, NetConnection *con); /// Returns a value representing this object which can be passed to script functions. const char* scriptThis(); public: /// @name Collision and transform related interface /// /// The Render Transform is the interpolated transform with respect to the /// frame rate. The Render Transform will differ from the object transform /// because the simulation is updated in fixed intervals, which controls the /// object transform. The framerate is, most likely, higher than this rate, /// so that is why the render transform is interpolated and will differ slightly /// from the object transform. /// /// @{ /// Disables collisions for this object including raycasts virtual void disableCollision(); /// Enables collisions for this object virtual void enableCollision(); /// Returns true if collisions are enabled bool isCollisionEnabled() const { return mCollisionCount == 0; } /// Returns true if this object allows itself to be displaced /// @see displaceObject virtual bool isDisplacable() const; /// Returns the momentum of this object virtual Point3F getMomentum() const; /// Sets the momentum of this object /// @param momentum Momentum virtual void setMomentum(const Point3F &momentum); /// Returns the mass of this object virtual F32 getMass() const; /// Displaces this object by a vector /// @param displaceVector Displacement vector virtual bool displaceObject(const Point3F& displaceVector); /// Returns the transform which can be used to convert object space /// to world space const MatrixF& getTransform() const { return mObjToWorld; } /// Returns the transform which can be used to convert world space /// into object space const MatrixF& getWorldTransform() const { return mWorldToObj; } /// Returns the scale of the object const VectorF& getScale() const { return mObjScale; } /// Returns the bounding box for this object in local coordinates const Box3F& getObjBox() const { return mObjBox; } /// Returns the bounding box for this object in world coordinates const Box3F& getWorldBox() const { return mWorldBox; } /// Returns the bounding sphere for this object in world coordinates const SphereF& getWorldSphere() const { return mWorldSphere; } /// Returns the center of the bounding box in world coordinates Point3F getBoxCenter() const { return (mWorldBox.min + mWorldBox.max) * 0.5f; } /// Sets the Object -> World transform /// /// @param mat New transform matrix virtual void setTransform(const MatrixF & mat); /// Sets the scale for the object /// @param scale Scaling values virtual void setScale(const VectorF & scale); /// This sets the render transform for this object /// @param mat New render transform virtual void setRenderTransform(const MatrixF &mat); /// Returns the render transform const MatrixF& getRenderTransform() const { return mRenderObjToWorld; } /// Returns the render transform to convert world to local coordinates const MatrixF& getRenderWorldTransform() const { return mRenderWorldToObj; } /// Returns the render world box const Box3F& getRenderWorldBox() const { return mRenderWorldBox; } /// Builds a convex hull for this object. /// /// Think of a convex hull as a low-res mesh which covers, as tightly as /// possible, the object mesh, and is used as a collision mesh. /// @param box /// @param convex Convex mesh generated (out) virtual void buildConvex(const Box3F& box,Convex* convex); /// Builds a list of polygons which intersect a bounding volume. /// /// This will use either the sphere or the box, not both, the /// SceneObject implimentation ignores sphere. /// /// @see AbstractPolyList /// @param polyList Poly list build (out) /// @param box Box bounding volume /// @param sphere Sphere bounding volume virtual bool buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere); /// Builds a collision tree of all the polygons which collide with a bounding volume. /// /// @note Not implemented in SceneObject. @see TerrainBlock::buildCollisionBSP /// @param tree BSP tree built (out) /// @param box Box bounding volume /// @param sphere Sphere bounding volume virtual BSPNode* buildCollisionBSP(BSPTree *tree, const Box3F &box, const SphereF &sphere); /// Casts a ray and obtain collision information, returns true if RayInfo is modified. /// /// @param start Start point of ray /// @param end End point of ray /// @param info Collision information obtained (out) virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info); virtual bool collideBox(const Point3F &start, const Point3F &end, RayInfo* info); /// Returns the position of the object. Point3F getPosition() const; /// Returns the render-position of the object. /// /// @see getRenderTransform Point3F getRenderPosition() const; /// Sets the position of the object void setPosition(const Point3F &pos); /// Gets the velocity of the object virtual Point3F getVelocity() const; /// Sets the velocity of the object /// @param v Velocity virtual void setVelocity(const Point3F &v); /// @} public: /// @name Zones /// /// A zone is a portalized section of an InteriorInstance, and an InteriorInstance can manage more than one zone. /// There is always at least one zone in the world, zone 0, which represens the whole world. Any /// other zone will be in an InteriorInstance. Torque keeps track of the zones containing an object /// as it moves throughout the world. An object can exists in multiple zones at once. /// @{ /// Returns true if this object is managing zones. /// /// This is only true in the case of InteriorInstances which have zones in them. bool isManagingZones() const; /// Gets the index of the first zone this object manages in the collection of zones. U32 getZoneRangeStart() const { return mZoneRangeStart; } /// Gets the number of zones containing this object. U32 getNumCurrZones() const { return mNumCurrZones; } /// Returns the nth zone containing this object. U32 getCurrZone(const U32 index) const; /// If an object exists in multiple zones, this method will give you the /// number and indices of these zones (storing them in the provided variables). /// /// @param obj Object in question. /// @param zones Indices of zones containing the object. (out) /// @param numZones Number of elements in the returned array. (out) virtual bool getOverlappingZones(SceneObject* obj, U32* zones, U32* numZones); /// Returns the zone containing p. /// /// @param p Point to test. virtual U32 getPointZone(const Point3F& p); /// This is called on a zone managing object to scope all the zones managed. /// /// @param rootPosition Camera position /// @param rootDistance Camera visible distance /// @param zoneScopeState Array of booleans which line up with the collection of zones, marked true if that zone is scoped (out) virtual bool scopeObject(const Point3F& rootPosition, const F32 rootDistance, bool* zoneScopeState); /// @} /// Called when the object is supposed to render itself. /// /// @param state Current rendering state. /// @see SceneState /// @param image Image associated with this object to render. /// @see SceneRenderImage virtual void renderObject(SceneState *state, SceneRenderImage *image); virtual void renderShadow( SceneState *state, SceneRenderImage *image){} /// Called when the SceneGraph is ready for the registration of RenderImages. /// /// @see SceneState /// /// @param state SceneState /// @param stateKey State key of the current SceneState /// @param startZone Base zone index /// @param modifyBaseZoneState If true, the object needs to modify the zone state. virtual bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState = false); /// Adds object to the client or server container depending on the object void addToScene(); /// Removes the object from the client/server container void removeFromScene(); //-------------------------------------- Derived class interface // Overrides protected: bool onAdd(); void onRemove(); // Overrideables protected: /// Called when this is added to the SceneGraph. /// /// @param graph SceneGraph this is getting added to virtual bool onSceneAdd(SceneGraph *graph); /// Called when this is removed from the SceneGraph virtual void onSceneRemove(); /// Called when the size of the object changes virtual void onScaleChanged(); /// @name Portals /// @{ /// This is used by a portal controling object to transform the base-modelview /// used by the scenegraph for rendering to the modelview it needs to render correctly. /// /// @see MirrorSubObject /// /// @param portalIndex Index of portal in the list of portals controlled by the object. /// @param oldMV Current modelview matrix used by the SceneGraph (in) /// @param newMV New modelview to be used by the SceneGraph (out) virtual void transformModelview(const U32 portalIndex, const MatrixF& oldMV, MatrixF* newMV); /// Used to tranform the position of a point based on a portal. /// /// @param portalIndex Index of a portal to transform by. /// @param point Point to transform. virtual void transformPosition(const U32 portalIndex, Point3F& point); /// Returns a new view frustum for the portal. /// /// @param portalIndex Which portal in the list of portals the object controls /// @param oldFrustum Current frustum. /// @param nearPlane Near clipping plane. /// @param farPlane Far clipping plane. /// @param oldViewport Current viewport. /// @param newFrustum New view frustum to use. (out) /// @param newViewport New viewport to use. (out) /// @param flippedMatrix Should the object should use a flipped matrix to calculate viewport and frustum? virtual bool computeNewFrustum(const U32 portalIndex, const F64* oldFrustum, const F64 nearPlane, const F64 farPlane, const RectI& oldViewport, F64* newFrustum, RectI& newViewport, const bool flippedMatrix); /// Called before things are to be rendered from the portals point of view, to set up /// everything the portal needs to render correctly. /// /// @param portalIndex Index of portal to use. /// @param pCurrState Current SceneState /// @param pParentState SceneState used before this portal was activated virtual void openPortal(const U32 portalIndex, SceneState* pCurrState, SceneState* pParentState); /// Called after rendering of a portal is complete, this resets the states /// the previous call to openPortal() changed. /// /// @param portalIndex Index of portal to use. /// @param pCurrState Current SceneState /// @param pParentState SceneState used before this portal was activated virtual void closePortal(const U32 portalIndex, SceneState* pCurrState, SceneState* pParentState); public: /// Returns the plane of the portal in world space. /// /// @param portalIndex Index of portal to use. /// @param plane Plane of the portal in world space (out) virtual void getWSPortalPlane(const U32 portalIndex, PlaneF *plane); /// @} protected: /// Sets the mLastState and mLastStateKey. /// /// @param state SceneState to set as the last state /// @param key Key to set as the last state key void setLastState(SceneState *state, U32 key); /// Returns true if the provided SceneState and key are set as this object's /// last state and key. /// /// @param state SceneState in question /// @param key State key in question bool isLastState(SceneState *state, U32 key) const; /// @name Traversal State /// /// The SceneGraph traversal is recursive and the traversal state of an object /// can be one of three things: /// - Pending - The object has not yet been examined for zone traversal. /// - Working - The object is currently having its zones traversed. /// - Done - The object has had all of its zones traversed or doesn't manage zones. /// /// @note These states were formerly referred to as TraverseColor, with White, Black, and /// Gray; this was changed in Torque 1.2 by Pat "KillerBunny" Wilson. This code is /// only used internal to this class /// @{ // These two replaced by TraversalState because that makes more sense -KB //void setTraverseColor(TraverseColor); //TraverseColor getTraverseColor() const; // ph34r teh muskrat! - Travis Colure /// This sets the traversal state of the object. /// /// @note This is used internally; you should not normally need to call it. /// @param s Traversal state to assign void setTraversalState( TraversalState s ); /// Returns the traversal state of this object TraversalState getTraversalState() const; /// @} /// @name Lighting /// @{ struct LightingInfo { LightingInfo(); bool mUseInfo; bool mDirty; ColorF mDefaultColor; //ColorF mAlarmColor; //SimObjectPtr mInterior; bool mHasLastColor; ColorF mLastColor; U32 mLastTime; static LightInfo smAmbientLight; //enum //{ // Interior = 0, // Terrain, // }; //U32 mLightSource; }; struct posinfo { Point2I lexelspace; ColorF lexel; }; public: /// Gets the color of the ambient light in the area of the object and /// stores it in the provided ColorF. /// /// @param col Ambient color (out) virtual bool getLightingAmbientColor(ColorF * col); LightingInfo mLightingInfo; ///< Lighting info for this object /// @} protected: /// @name Transform and Collision Members /// @{ /// Container* mContainer; MatrixF mObjToWorld; ///< Transform from object space to world space MatrixF mWorldToObj; ///< Transform from world space to object space (inverse) Point3F mObjScale; ///< Object scale Box3F mObjBox; ///< Bounding box in object space Box3F mWorldBox; ///< Bounding box in world space SphereF mWorldSphere; ///< Bounding sphere in world space MatrixF mRenderObjToWorld; ///< Render matrix to transform object space to world space MatrixF mRenderWorldToObj; ///< Render matrix to transform world space to object space Box3F mRenderWorldBox; ///< Render bounding box in world space SphereF mRenderWorldSphere; ///< Render bounxing sphere in world space /// Regenerates the world-space bounding box and bounding sphere void resetWorldBox(); /// Regenerates the render-world-space bounding box and sphere void resetRenderWorldBox(); SceneObjectRef* mZoneRefHead; SceneObjectRef* mBinRefHead; U32 mBinMinX; U32 mBinMaxX; U32 mBinMinY; U32 mBinMaxY; /// @} /// @name Container Interface /// /// When objects are searched, we go through all the zones and ask them for /// all of their objects. Because an object can exist in multiple zones, the /// container sequence key is set to the id of the current search. Then, while /// searching, we check to see if an object's sequence key is the same as the /// current search key. If it is, it will NOT be added to the list of returns /// since it has already been processed. /// /// @{ U32 mContainerSeqKey; ///< Container sequence key /// Returns the container sequence key U32 getContainerSeqKey() const { return mContainerSeqKey; } /// Sets the container sequence key void setContainerSeqKey(const U32 key) { mContainerSeqKey = key; } /// @} public: /// Returns a pointer to the container that contains this object Container* getContainer() { return mContainer; } protected: S32 mCollisionCount; bool mGlobalBounds; public: /// Returns the type mask for this object U32 getTypeMask() { return(mTypeMask); } const bool isGlobalBounds() const { return mGlobalBounds; } /// If global bounds are set to be true, then the object is assumed to /// have an infinitely large bounding box for collision and rendering /// purposes. /// /// They can't be toggled currently. void setGlobalBounds() { if(mContainer) mContainer->removeFromBins(this); mGlobalBounds = true; mObjBox.min.set(-1e10, -1e10, -1e10); mObjBox.max.set( 1e10, 1e10, 1e10); if(mContainer) mContainer->insertIntoBins(this); } /// @name Rendering Members /// @{ protected: SceneGraph* mSceneManager; ///< SceneGraph that controls this object U32 mZoneRangeStart; ///< Start of range of zones this object controls, 0xFFFFFFFF == no zones U32 mNumCurrZones; ///< Number of zones this object exists in private: TraversalState mTraversalState; ///< State of this object in the SceneGraph traversal - DON'T MESS WITH THIS SceneState* mLastState; ///< Last SceneState that was used to render this object. U32 mLastStateKey; ///< Last state key that was used to render this object. /// @} /// @name Persist and console /// @{ public: static void initPersistFields(); void inspectPostApply(); DECLARE_CONOBJECT(SceneObject); /// @} }; //-------------------------------------------------------------------------- extern Container gServerContainer; extern Container gClientContainer; //-------------------------------------------------------------------------- //-------------------------------------- Inlines // inline bool SceneObject::isManagingZones() const { return mZoneRangeStart != 0xFFFFFFFF; } inline void SceneObject::setLastState(SceneState* state, U32 key) { mLastState = state; mLastStateKey = key; } inline bool SceneObject::isLastState(SceneState* state, U32 key) const { return (mLastState == state && mLastStateKey == key); } inline void SceneObject::setTraversalState( TraversalState s ) { mTraversalState = s; } inline SceneObject::TraversalState SceneObject::getTraversalState() const { return mTraversalState; } inline U32 SceneObject::getCurrZone(const U32 index) const { // Not the most efficient way to do this, walking the list, // but it's an uncommon call... SceneObjectRef* walk = mZoneRefHead; for (U32 i = 0; i < index; i++) { walk = walk->nextInObj; AssertFatal(walk!=NULL, "Error, too few object refs!"); } AssertFatal(walk!=NULL, "Error, too few object refs!"); return walk->zone; } //-------------------------------------------------------------------------- inline SceneObjectRef* Container::allocateObjectRef() { if (mFreeRefPool == NULL) { addRefPoolBlock(); } AssertFatal(mFreeRefPool!=NULL, "Error, should always have a free reference here!"); SceneObjectRef* ret = mFreeRefPool; mFreeRefPool = mFreeRefPool->nextInObj; ret->nextInObj = NULL; return ret; } inline void Container::freeObjectRef(SceneObjectRef* trash) { trash->object = NULL; trash->nextInBin = NULL; trash->prevInBin = NULL; trash->nextInObj = mFreeRefPool; mFreeRefPool = trash; } inline void Container::findObjects(U32 mask, FindCallback callback, void *key) { for (Link* itr = mStart.next; itr != &mEnd; itr = itr->next) { SceneObject* ptr = static_cast(itr); if ((ptr->getType() & mask) != 0 && !ptr->mCollisionCount) (*callback)(ptr,key); } } #endif // _H_SCENEOBJECT_