//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #ifndef _TERRDATA_H_ #define _TERRDATA_H_ #ifndef _PLATFORM_H_ #include "platform/platform.h" #endif #ifndef _MPOINT_H_ #include "math/mPoint.h" #endif #ifndef _SCENEOBJECT_H_ #include "sim/sceneObject.h" #endif #ifndef _RESMANAGER_H_ #include "core/resManager.h" #endif #ifndef _MATERIALLIST_H_ #include "dgl/materialList.h" #endif #ifndef _GTEXMANAGER_H_ #include "dgl/gTexManager.h" #endif #ifndef _CONVEX_H_ #include "collision/convex.h" #endif #include "dgl/gBitmap.h" class GBitmap; class TerrainFile; class TerrainBlock; class ColorF; class Blender; //-------------------------------------------------------------------------- class TerrainConvex: public Convex { friend class TerrainBlock; TerrainConvex *square; ///< Alternate convex if square is concave bool halfA; ///< Which half of square bool split45; ///< Square split pattern U32 squareId; ///< Used to match squares U32 material; Point3F point[4]; ///< 3-4 vertices VectorF normal[2]; Box3F box; ///< Bounding box public: TerrainConvex() { mType = TerrainConvexType; } TerrainConvex(const TerrainConvex& cv) { mType = TerrainConvexType; // Only a partial copy... mObject = cv.mObject; split45 = cv.split45; squareId = cv.squareId; material = cv.material; point[0] = cv.point[0]; point[1] = cv.point[1]; point[2] = cv.point[2]; point[3] = cv.point[3]; normal[0] = cv.normal[0]; normal[1] = cv.normal[1]; box = cv.box; } Box3F getBoundingBox() const; Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const; Point3F support(const VectorF& v) const; void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf); void getPolyList(AbstractPolyList* list); }; //-------------------------------------------------------------------------- struct GridSquare { U16 minHeight; U16 maxHeight; U16 heightDeviance; U16 flags; enum { Split45 = 1, Empty = 2, HasEmpty = 4, MaterialShift = 3, MaterialStart = 8, Material0 = 8, Material1 = 16, Material2 = 32, Material3 = 64, }; }; struct GridChunk { U16 heightDeviance[3]; // levels 0-1, 1-2, 2 U16 emptyFlags; }; //-------------------------------------------------------------------------- class TerrainBlock : public SceneObject { typedef SceneObject Parent; public: struct Material { enum Flags { Plain = 0, Rotate = 1, FlipX = 2, FlipXRotate = 3, FlipY = 4, FlipYRotate = 5, FlipXY = 6, FlipXYRotate = 7, RotateMask = 7, Empty = 8, Modified = BIT(7), // must not clobber TerrainFile::MATERIAL_GROUP_MASK bits! PersistMask = BIT(7) }; U8 flags; U8 index; }; enum { BlockSize = 256, BlockShift = 8, LightmapSize = 512, LightmapShift = 9, ChunkSquareWidth = 64, ChunkSize = 4, ChunkDownShift = 2, ChunkShift = BlockShift - ChunkDownShift, BlockSquareWidth = 256, SquareMaxPoints = 1024, BlockMask = 255, GridMapSize = 0x15555, FlagMapWidth = 128, ///< Flags that map is for 2x2 squares. FlagMapMask = 127, MaxMipLevel = 6, NumBaseTextures = 16, MaterialGroups = 8, MaxEmptyRunPairs = 100 }; enum UpdateMaskBits { InitMask = 1, VisibilityMask = 2, EmptyMask = 4, }; Blender* mBlender; TextureHandle baseTextures[NumBaseTextures]; TextureHandle mBaseMaterials[MaterialGroups]; TextureHandle mAlphaMaterials[MaterialGroups]; GBitmap *lightMap; GBitmap *whiteMap; TextureHandle lightMapTexture; void triggerLightmapReload() { if(whiteMap) delete whiteMap; whiteMap = NULL; lightMapTexture = NULL; } StringTableEntry *mMaterialFileName; ///< Array from the file. TextureHandle mDynLightTexture; U8 *mBaseMaterialMap; Material *materialMap; // fixed point height values U16 *heightMap; U16 *flagMap; StringTableEntry mDetailTextureName; TextureHandle mDetailTextureHandle; // Bumpmapping. StringTableEntry mBumpTextureName; TextureHandle mBumpTextureHandle; TextureHandle mInvertedBumpTextureHandle; F32 mBumpScale; F32 mBumpOffset; U32 mZeroBumpScale; StringTableEntry mTerrFileName; Vector mEmptySquareRuns; U32 mTextureCallbackKey; void processTextureEvent(const U32 eventCode); S32 mMPMIndex[TerrainBlock::MaterialGroups]; S32 mVertexBuffer; /// If true, we tile infinitely. bool mTile; private: Resource mFile; GridSquare *gridMap[BlockShift+1]; GridChunk *mChunkMap; U32 mCRC; public: TerrainBlock(); ~TerrainBlock(); void buildChunkDeviance(S32 x, S32 y); void buildGridMap(); U32 getCRC() { return(mCRC); } Resource getFile() { return mFile; }; bool onAdd(); void onRemove(); void refreshMaterialLists(); void onEditorEnable(); void onEditorDisable(); void rebuildEmptyFlags(); bool unpackEmptySquares(); void packEmptySquares(); TextureHandle getDetailTextureHandle(); TextureHandle getBumpTextureHandle(); TextureHandle getInvertedBumpTextureHandle(); Material *getMaterial(U32 x, U32 y); GridSquare *findSquare(U32 level, Point2I pos); GridSquare *findSquare(U32 level, S32 x, S32 y); GridChunk *findChunk(Point2I pos); void setHeight(const Point2I & pos, float height); void updateGrid(Point2I min, Point2I max); void updateGridMaterials(Point2I min, Point2I max); U16 getHeight(U32 x, U32 y) { return heightMap[(x & BlockMask) + ((y & BlockMask) << BlockShift)]; } U16 *getHeightAddress(U32 x, U32 y) { return &heightMap[(x & BlockMask) + ((y & BlockMask) << BlockShift)]; } void setBaseMaterial(U32 x, U32 y, U8 matGroup); S32 getTerrainMapIndex(Point3F& pt); U8 *getMaterialAlphaMap(U32 matIndex); U8* getBaseMaterialAddress(U32 x, U32 y); U8 getBaseMaterial(U32 x, U32 y); U16 *getFlagMapPtr(S32 x, S32 y); void getMaterialAlpha(Point2I pos, U8 alphas[MaterialGroups]); void setMaterialAlpha(Point2I pos, const U8 alphas[MaterialGroups]); // a more useful getHeight for the public... bool getHeight(const Point2F & pos, F32 * height); bool getNormal(const Point2F & pos, Point3F * normal, bool normalize = true); bool getNormalAndHeight(const Point2F & pos, Point3F * normal, F32 * height, bool normalize = true); // only the editor currently uses this method - should always be using a ray to collide with bool collideBox(const Point3F &start, const Point3F &end, RayInfo* info){return(castRay(start,end,info));} S32 getMaterialAlphaIndex(const char *materialName); private: S32 squareSize; public: void setFile(Resource file); bool save(const char* filename); static void flushCache(); void relight(const ColorF &lightColor, const ColorF &ambient, const Point3F &lightDir); S32 getSquareSize(); //-------------------------------------- // SceneGraph functions... protected: void setTransform (const MatrixF &mat); bool prepRenderImage ( SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState=false); void renderObject ( SceneState *state, SceneRenderImage *image); //-------------------------------------- // collision info private: BSPTree *mTree; S32 mHeightMin; S32 mHeightMax; public: void buildConvex(const Box3F& box,Convex* convex); bool buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere); BSPNode *buildCollisionBSP(BSPTree *tree, const Box3F &box, const SphereF &sphere); bool castRay(const Point3F &start, const Point3F &end, RayInfo* info); bool castRayI(const Point3F &start, const Point3F &end, RayInfo* info, bool emptyCollide); bool castRayBlock(const Point3F &pStart, const Point3F &pEnd, const Point2I &blockPos, U32 level, F32 invDeltaX, F32 invDeltaY, F32 startT, F32 endT, RayInfo *info, bool); private: BSPNode *buildSquareTree(S32 y, S32 x); BSPNode *buildXTree(S32 y, S32 xStart, S32 xEnd); public: bool buildMaterialMap(); void buildMipMap(); void setBaseMaterials(S32 argc, const char *argv[]); bool initMMXBlender(); // private helper private: bool mCollideEmpty; public: DECLARE_CONOBJECT(TerrainBlock); static void initPersistFields(); void inspectPostApply(); U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); void unpackUpdate(NetConnection *conn, BitStream *stream); }; //-------------------------------------- class TerrainFile : public ResourceInstance { public: enum Constants { FILE_VERSION = 3, MATERIAL_GROUP_MASK = 0x7 }; TerrainFile(); ~TerrainFile(); U16 mHeightMap[TerrainBlock::BlockSize * TerrainBlock::BlockSize]; U8 mBaseMaterialMap[TerrainBlock::BlockSize * TerrainBlock::BlockSize]; GridSquare mGridMapBase[TerrainBlock::GridMapSize]; GridSquare *mGridMap[TerrainBlock::BlockShift+1]; GridChunk mChunkMap[TerrainBlock::ChunkSquareWidth * TerrainBlock::ChunkSquareWidth]; U16 mFlagMap[TerrainBlock::FlagMapWidth * TerrainBlock::FlagMapWidth]; char *mTextureScript; char *mHeightfieldScript; TerrainBlock::Material mMaterialMap[TerrainBlock::BlockSquareWidth * TerrainBlock::BlockSquareWidth]; // DMMNOTE: This loads all the alpha maps, whether or not they are used. Possible to // restrict to only the used versions? StringTableEntry mMaterialFileName[TerrainBlock::MaterialGroups]; U8* mMaterialAlphaMap[TerrainBlock::MaterialGroups]; bool save(const char *filename); void buildChunkDeviance(S32 x, S32 y); void buildGridMap(); void heightDevLine(U32 p1x, U32 p1y, U32 p2x, U32 p2y, U32 pmx, U32 pmy, U16 *devPtr); inline GridSquare *findSquare(U32 level, Point2I pos) { return mGridMap[level] + (pos.x >> level) + ((pos.y>>level) << (TerrainBlock::BlockShift - level)); } inline U16 getHeight(U32 x, U32 y) { return mHeightMap[(x & TerrainBlock::BlockMask) + ((y & TerrainBlock::BlockMask) << TerrainBlock::BlockShift)]; } inline TerrainBlock::Material *getMaterial(U32 x, U32 y) { return &mMaterialMap[(x & TerrainBlock::BlockMask) + ((y & TerrainBlock::BlockMask) << TerrainBlock::BlockShift)]; } void setTextureScript(const char *script); void setHeightfieldScript(const char *script); const char *getTextureScript(); const char *getHeightfieldScript(); }; //-------------------------------------------------------------------------- inline U16 *TerrainBlock::getFlagMapPtr(S32 x, S32 y) { return flagMap + ((x >> 1) & TerrainBlock::FlagMapMask) + ((y >> 1) & TerrainBlock::FlagMapMask) * TerrainBlock::FlagMapWidth; } inline GridSquare *TerrainBlock::findSquare(U32 level, S32 x, S32 y) { return gridMap[level] + ((x & TerrainBlock::BlockMask) >> level) + (((y & TerrainBlock::BlockMask) >> level) << (TerrainBlock::BlockShift - level)); } inline GridSquare *TerrainBlock::findSquare(U32 level, Point2I pos) { return gridMap[level] + (pos.x >> level) + ((pos.y>>level) << (BlockShift - level)); } inline GridChunk *TerrainBlock::findChunk(Point2I pos) { return mChunkMap + (pos.x >> ChunkDownShift) + ((pos.y>>ChunkDownShift) << ChunkShift); } inline TerrainBlock::Material *TerrainBlock::getMaterial(U32 x, U32 y) { return materialMap + x + (y << BlockShift); } inline TextureHandle TerrainBlock::getDetailTextureHandle() { return mDetailTextureHandle; } inline S32 TerrainBlock::getSquareSize() { return squareSize; } inline U8 TerrainBlock::getBaseMaterial(U32 x, U32 y) { return mBaseMaterialMap[(x & BlockMask) + ((y & BlockMask) << BlockShift)]; } inline U8* TerrainBlock::getBaseMaterialAddress(U32 x, U32 y) { return &mBaseMaterialMap[(x & BlockMask) + ((y & BlockMask) << BlockShift)]; } inline U8* TerrainBlock::getMaterialAlphaMap(U32 matIndex) { if (mFile->mMaterialAlphaMap[matIndex] == NULL) { mFile->mMaterialAlphaMap[matIndex] = new U8[TerrainBlock::BlockSize * TerrainBlock::BlockSize]; dMemset(mFile->mMaterialAlphaMap[matIndex], 0, TerrainBlock::BlockSize * TerrainBlock::BlockSize); } return mFile->mMaterialAlphaMap[matIndex]; } // 11.5 fixed point - gives us a height range from 0->2048 in 1/32 inc inline F32 fixedToFloat(U16 val) { return F32(val) * 0.03125f; } inline U16 floatToFixed(F32 val) { return U16(val * 32.0); } extern ResourceInstance *constructTerrainFile(Stream &stream); #endif