terrData.cpp

Engine/source/terrain/terrData.cpp

More...

Public Typedefs

Public Variables

_getTerrainHeight2 ("@brief Gets the terrain height at the specified position\n\n" "@param x The X coordinate in world space\n" "@param y The Y coordinate in world space\n\n" "@return Returns the terrain height at the given point as an F32 value.\n\n" "@ingroup Terrain", NULL, "bool getTerrainHeight( F32 x, F32 y);")
_getTerrainHeightBelowPosition2 ("@brief Takes a world point and find the \"highest\" terrain underneath it\n\n" "@param x The X coordinate in world space\n" "@param y The Y coordinate in world space\n\n" "@return Returns the closest terrain height below the given point as an F32 value.\n\n" "@ingroup Terrain", NULL, "bool getTerrainHeightBelowPosition( F32 x, F32 y);")
_getTerrainUnderWorldPoint1 ("@brief Gets the terrain block that is located under the given world point\n\n" "@param position The world space coordinate you wish to query at. Formatted as (\"x y z\")\n\n" "@return Returns the ID of the requested terrain block (0 if not found).\n\n" "@ingroup Terrain", NULL, "bool getTerrainUnderWorldPoint( Point3F position );")
_getTerrainUnderWorldPoint2 ("@brief Takes a world point and find the \"highest\" terrain underneath it\n\n" "@param x The X coordinate in world space\n" "@param y The Y coordinate in world space\n\n" "@param z The Z coordinate in world space\n\n" "@return Returns the ID of the requested terrain block (0 if not found).\n\n" "@ingroup Terrain", NULL, "bool getTerrainUnderWorldPoint( F32 x, F32 y, F32 z);")

Public Functions

_getTerrainHeight1("@brief Gets the terrain height at the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position\n\n</a>" "@param position The world space point, minus the z(height) value. Formatted as(\"x y\")\n\n" "@return Returns the terrain height at the given point as an <a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value.\n\n</a>" "@ingroup Terrain" , NULL , "bool getTerrainHeight( <a href="/coding/class/classpoint2i/">Point2I</a> position );" )
_getTerrainHeightBelowPosition1("@brief Takes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> world point and find the \"highest\" terrain underneath <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it\n\n</a>" "@param position The world space point, minus the z(height) value. Formatted as(\"x y\")\n\n" "@return Returns the closest terrain height below the given point as an <a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value.\n\n</a>" "@ingroup Terrain" , NULL , "bool getTerrainHeightBelowPosition( <a href="/coding/class/classpoint2i/">Point2I</a> position );" )
ConsoleDocClass(TerrainBlock , "@brief Represent <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> terrain object in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Torque 3D <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">level\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classterrainblock/">TerrainBlock</a>(theTerrain)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   terrainFile = \"art/terrains/Deathball Desert_0.ter\";\n" "   squareSize = \"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   tile = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   baseTexSize = \"1024\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   screenError = \"16\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   position = \"-1024 -1024 179.978\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   rotation = \"1 0 0 0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   scale = \"1 1 1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   isRenderEnabled = \"true\";\n" "   canSaveDynamicFields = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TerrainMaterial\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Terrain\n</a>" )
DefineEngineFunction(getTerrainHeight , F32 , (const char *ptOrX, const char *y) , ("") , "(Point2 pos) - gets the terrain height at the specified position." "@param pos The world space point, minus the z(height) <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value\n</a> Can be formatted as either(\"x y\") or (x,y)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return Returns the terrain height at the given point as an <a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value.\n</a>" "@hide" )
DefineEngineFunction(getTerrainHeightBelowPosition , F32 , (const char *ptOrX, const char *y, const char *z) , ("", "") , "(Point3F pos) - gets the terrain height at the specified position." "@param pos The world space point. Can be formatted as either (\"x y z\") or (x,y,z)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@note This function is useful <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> you simply want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> grab the terrain height underneath an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return Returns the terrain height at the given point as an <a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value.\n</a>" "@hide" )
DefineEngineFunction(getTerrainUnderWorldPoint , S32 , (const char *ptOrX, const char *y, const char *z) , ("", "") , "(Point3F x/y/z) Gets the terrain block that is located under the given world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@param x/y/z The world coordinates (floating point values) you wish <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> query at. " "These can be formatted as either <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> string (\"x y z\") or separately as (x, y, z)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return Returns the ID of the requested terrain block (0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not found).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@hide" )
DefineEngineMethod(TerrainBlock , save , bool , (const char *fileName) , "@brief Saves the terrain block's terrain <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">name.\n\n</a>" "@param fileName Name and path of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save terrain data <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> save was successful, false otherwise" )
DefineEngineMethod(TerrainBlock , saveAsset , bool , () , "@brief Saves the terrain block's terrain <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">name.\n\n</a>" "@param fileName Name and path of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save terrain data <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> save was successful, false otherwise" )
DefineEngineMethod(TerrainBlock , setMaterialsDirty , void , () , "" )
ImplementEnumType(baseTexFormat , "Description\n" "@ingroup ?\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" )

Detailed Description

Public Typedefs

typedef TerrainBlock::BaseTexFormat baseTexFormat 

Public Variables

ConsoleDocFragment _getTerrainHeight2 ("@brief Gets the terrain height at the specified position\n\n" "@param x The X coordinate in world space\n" "@param y The Y coordinate in world space\n\n" "@return Returns the terrain height at the given point as an F32 value.\n\n" "@ingroup Terrain", NULL, "bool getTerrainHeight( F32 x, F32 y);")
ConsoleDocFragment _getTerrainHeightBelowPosition2 ("@brief Takes a world point and find the \"highest\" terrain underneath it\n\n" "@param x The X coordinate in world space\n" "@param y The Y coordinate in world space\n\n" "@return Returns the closest terrain height below the given point as an F32 value.\n\n" "@ingroup Terrain", NULL, "bool getTerrainHeightBelowPosition( F32 x, F32 y);")
ConsoleDocFragment _getTerrainUnderWorldPoint1 ("@brief Gets the terrain block that is located under the given world point\n\n" "@param position The world space coordinate you wish to query at. Formatted as (\"x y z\")\n\n" "@return Returns the ID of the requested terrain block (0 if not found).\n\n" "@ingroup Terrain", NULL, "bool getTerrainUnderWorldPoint( Point3F position );")
ConsoleDocFragment _getTerrainUnderWorldPoint2 ("@brief Takes a world point and find the \"highest\" terrain underneath it\n\n" "@param x The X coordinate in world space\n" "@param y The Y coordinate in world space\n\n" "@param z The Z coordinate in world space\n\n" "@return Returns the ID of the requested terrain block (0 if not found).\n\n" "@ingroup Terrain", NULL, "bool getTerrainUnderWorldPoint( F32 x, F32 y, F32 z);")
 EndImplementEnumType 
Convex sTerrainConvexList 

Public Functions

_getTerrainHeight1("@brief Gets the terrain height at the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position\n\n</a>" "@param position The world space point, minus the z(height) value. Formatted as(\"x y\")\n\n" "@return Returns the terrain height at the given point as an <a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value.\n\n</a>" "@ingroup Terrain" , NULL , "bool getTerrainHeight( <a href="/coding/class/classpoint2i/">Point2I</a> position );" )

_getTerrainHeightBelowPosition1("@brief Takes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> world point and find the \"highest\" terrain underneath <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">it\n\n</a>" "@param position The world space point, minus the z(height) value. Formatted as(\"x y\")\n\n" "@return Returns the closest terrain height below the given point as an <a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value.\n\n</a>" "@ingroup Terrain" , NULL , "bool getTerrainHeightBelowPosition( <a href="/coding/class/classpoint2i/">Point2I</a> position );" )

ConsoleDocClass(TerrainBlock , "@brief Represent <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> terrain object in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Torque 3D <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">level\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classterrainblock/">TerrainBlock</a>(theTerrain)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   terrainFile = \"art/terrains/Deathball Desert_0.ter\";\n" "   squareSize = \"2\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   tile = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   baseTexSize = \"1024\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   screenError = \"16\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   position = \"-1024 -1024 179.978\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   rotation = \"1 0 0 0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   scale = \"1 1 1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   isRenderEnabled = \"true\";\n" "   canSaveDynamicFields = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TerrainMaterial\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Terrain\n</a>" )

DefineEngineFunction(getTerrainHeight , F32 , (const char *ptOrX, const char *y) , ("") , "(Point2 pos) - gets the terrain height at the specified position." "@param pos The world space point, minus the z(height) <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value\n</a> Can be formatted as either(\"x y\") or (x,y)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return Returns the terrain height at the given point as an <a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value.\n</a>" "@hide" )

DefineEngineFunction(getTerrainHeightBelowPosition , F32 , (const char *ptOrX, const char *y, const char *z) , ("", "") , "(Point3F pos) - gets the terrain height at the specified position." "@param pos The world space point. Can be formatted as either (\"x y z\") or (x,y,z)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@note This function is useful <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> you simply want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> grab the terrain height underneath an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return Returns the terrain height at the given point as an <a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value.\n</a>" "@hide" )

DefineEngineFunction(getTerrainUnderWorldPoint , S32 , (const char *ptOrX, const char *y, const char *z) , ("", "") , "(Point3F x/y/z) Gets the terrain block that is located under the given world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">point.\n</a>" "@param x/y/z The world coordinates (floating point values) you wish <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> query at. " "These can be formatted as either <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> string (\"x y z\") or separately as (x, y, z)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return Returns the ID of the requested terrain block (0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not found).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@hide" )

DefineEngineMethod(TerrainBlock , save , bool , (const char *fileName) , "@brief Saves the terrain block's terrain <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">name.\n\n</a>" "@param fileName Name and path of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save terrain data <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> save was successful, false otherwise" )

DefineEngineMethod(TerrainBlock , saveAsset , bool , () , "@brief Saves the terrain block's terrain <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">name.\n\n</a>" "@param fileName Name and path of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save terrain data <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> save was successful, false otherwise" )

DefineEngineMethod(TerrainBlock , setMaterialsDirty , void , () , "" )

DefineEnumType(baseTexFormat )

getTerrainUnderWorldPoint(const Point3F & wPos)

IMPLEMENT_CO_NETOBJECT_V1(TerrainBlock )

ImplementEnumType(baseTexFormat , "Description\n" "@ingroup ?\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" )

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  26// Copyright (C) 2015 Faust Logic, Inc.
  27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  28
  29#include "platform/platform.h"
  30#include "terrain/terrData.h"
  31
  32#include "terrain/terrCollision.h"
  33#include "terrain/terrCell.h"
  34#include "terrain/terrRender.h"
  35#include "terrain/terrMaterial.h"
  36#include "terrain/terrCellMaterial.h"
  37#include "gui/worldEditor/terrainEditor.h"
  38#include "math/mathIO.h"
  39#include "core/stream/fileStream.h"
  40#include "core/stream/bitStream.h"
  41#include "console/consoleTypes.h"
  42#include "sim/netConnection.h"
  43#include "core/util/safeDelete.h"
  44#include "T3D/objectTypes.h"
  45#include "renderInstance/renderPassManager.h"
  46#include "scene/sceneRenderState.h"
  47#include "materials/materialManager.h"
  48#include "materials/baseMatInstance.h"
  49#include "gfx/gfxTextureManager.h"
  50#include "gfx/gfxCardProfile.h"
  51#include "gfx/gfxAPI.h"
  52#include "core/resourceManager.h"
  53#include "T3D/physics/physicsPlugin.h"
  54#include "T3D/physics/physicsBody.h"
  55#include "T3D/physics/physicsCollision.h"
  56#include "console/engineAPI.h"
  57#include "core/util/safeRelease.h"
  58
  59#include "T3D/assets/TerrainMaterialAsset.h"
  60using namespace Torque;
  61
  62IMPLEMENT_CO_NETOBJECT_V1(TerrainBlock);
  63
  64ConsoleDocClass( TerrainBlock,
  65   "@brief Represent a terrain object in a Torque 3D level\n\n"
  66
  67  "@tsexample\n"
  68   "new TerrainBlock(theTerrain)\n"
  69   "{\n"
  70   "   terrainFile = \"art/terrains/Deathball Desert_0.ter\";\n"
  71   "   squareSize = \"2\";\n"
  72   "   tile = \"0\";\n"
  73   "   baseTexSize = \"1024\";\n"
  74   "   screenError = \"16\";\n"
  75   "   position = \"-1024 -1024 179.978\";\n"
  76   "   rotation = \"1 0 0 0\";\n"
  77   "   scale = \"1 1 1\";\n"
  78   "   isRenderEnabled = \"true\";\n"
  79   "   canSaveDynamicFields = \"1\";\n"
  80   "};\n"
  81   "@endtsexample\n\n"
  82
  83   "@see TerrainMaterial\n\n"
  84
  85   "@ingroup Terrain\n"
  86);
  87
  88
  89Signal<void(U32,TerrainBlock*,const Point2I& ,const Point2I&)> TerrainBlock::smUpdateSignal;
  90
  91F32 TerrainBlock::smLODScale = 1.0f;
  92F32 TerrainBlock::smDetailScale = 1.0f;
  93
  94
  95//RBP - Global function declared in Terrdata.h
  96TerrainBlock* getTerrainUnderWorldPoint(const Point3F & wPos)
  97{
  98   // Cast a ray straight down from the world position and see which
  99   // Terrain is the closest to our starting point
 100   Point3F startPnt = wPos;
 101   Point3F endPnt = wPos + Point3F(0.0f, 0.0f, -10000.0f);
 102
 103   S32 blockIndex = -1;
 104   F32 nearT = 1.0f;
 105
 106   SimpleQueryList queryList;
 107   gServerContainer.findObjects( TerrainObjectType, SimpleQueryList::insertionCallback, &queryList);
 108
 109   for (U32 i = 0; i < queryList.mList.size(); i++)
 110   {
 111      Point3F tStartPnt, tEndPnt;
 112      TerrainBlock* terrBlock = dynamic_cast<TerrainBlock*>(queryList.mList[i]);
 113      terrBlock->getWorldTransform().mulP(startPnt, &tStartPnt);
 114      terrBlock->getWorldTransform().mulP(endPnt, &tEndPnt);
 115
 116      RayInfo ri;
 117      if (terrBlock->castRayI(tStartPnt, tEndPnt, &ri, true))
 118      {
 119         if (ri.t < nearT)
 120         {
 121            blockIndex = i;
 122            nearT = ri.t;
 123         }
 124      }
 125   }
 126
 127   if (blockIndex > -1)
 128      return (TerrainBlock*)(queryList.mList[blockIndex]);
 129
 130   return NULL;
 131}
 132
 133
 134ConsoleDocFragment _getTerrainUnderWorldPoint1(
 135   "@brief Gets the terrain block that is located under the given world point\n\n"
 136   "@param position The world space coordinate you wish to query at. Formatted as (\"x y z\")\n\n"
 137   "@return Returns the ID of the requested terrain block (0 if not found).\n\n"
 138   "@ingroup Terrain",
 139   NULL,
 140   "bool getTerrainUnderWorldPoint( Point3F position );"
 141);
 142ConsoleDocFragment _getTerrainUnderWorldPoint2(
 143   "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n"
 144   "@param x The X coordinate in world space\n"
 145   "@param y The Y coordinate in world space\n\n"
 146   "@param z The Z coordinate in world space\n\n"
 147   "@return Returns the ID of the requested terrain block (0 if not found).\n\n"
 148   "@ingroup Terrain",
 149   NULL,
 150   "bool getTerrainUnderWorldPoint( F32 x, F32 y, F32 z);"
 151);
 152
 153DefineEngineFunction( getTerrainUnderWorldPoint, S32, (const char* ptOrX, const char* y, const char* z), ("", ""),
 154                                                      "(Point3F x/y/z) Gets the terrain block that is located under the given world point.\n"
 155                                                      "@param x/y/z The world coordinates (floating point values) you wish to query at. " 
 156                                                      "These can be formatted as either a string (\"x y z\") or separately as (x, y, z)\n"
 157                                                      "@return Returns the ID of the requested terrain block (0 if not found).\n\n"
 158                                                      "@hide")
 159{
 160   Point3F pos;
 161   if(!String::isEmpty(ptOrX) && String::isEmpty(y) && String::isEmpty(z))
 162      dSscanf(ptOrX, "%f %f %f", &pos.x, &pos.y, &pos.z);
 163   else if(!String::isEmpty(ptOrX) && !String::isEmpty(y) && !String::isEmpty(z))
 164   {
 165      pos.x = dAtof(ptOrX);
 166      pos.y = dAtof(y);
 167      pos.z = dAtof(z);
 168   }
 169   TerrainBlock* terrain = getTerrainUnderWorldPoint(pos);
 170   if(terrain != NULL)
 171   {
 172      return terrain->getId();
 173   }
 174   return 0;
 175}
 176
 177
 178typedef TerrainBlock::BaseTexFormat baseTexFormat;
 179DefineEnumType(baseTexFormat);
 180
 181ImplementEnumType(baseTexFormat,
 182   "Description\n"
 183   "@ingroup ?\n\n")
 184{ TerrainBlock::NONE, "NONE", "No cached terrain.\n" },
 185{ TerrainBlock::DDS, "DDS", "Cache the terrain in a DDS format.\n" },
 186{ TerrainBlock::PNG, "PNG", "Cache the terrain in a PNG format.\n" },
 187EndImplementEnumType;
 188
 189TerrainBlock::TerrainBlock()
 190 : mLightMap( NULL ),
 191   mLightMapSize( 256 ),
 192   mCRC( 0 ),
 193   mMaxDetailDistance( 0.0f ),
 194   mBaseTexScaleConst( NULL ),
 195   mBaseTexIdConst( NULL ),
 196   mBaseLayerSizeConst(NULL),
 197   mDetailsDirty( false ),
 198   mLayerTexDirty( false ),
 199   mBaseTexSize( 1024 ),
 200   mBaseTexFormat( TerrainBlock::DDS ),
 201   mCell( NULL ),
 202   mBaseMaterial( NULL ),
 203   mDefaultMatInst( NULL ),
 204   mSquareSize( 1.0f ),
 205   mPhysicsRep( NULL ),
 206   mScreenError( 16 ),
 207   mCastShadows( true ),
 208   mZoningDirty( false ),
 209   mUpdateBasetex ( true ),
 210   mDetailTextureArray( NULL ),
 211   mMacroTextureArray( NULL ),
 212   mOrmTextureArray( NULL ),
 213   mNormalTextureArray( NULL )
 214{
 215   mTypeMask = TerrainObjectType | StaticObjectType | StaticShapeObjectType;
 216   mNetFlags.set(Ghostable | ScopeAlways);
 217   mIgnoreZodiacs = false;
 218   zode_primBuffer = 0;
 219
 220   mTerrainAsset = StringTable->EmptyString();
 221   mTerrainAssetId = StringTable->EmptyString();
 222}
 223
 224
 225extern Convex sTerrainConvexList;
 226
 227TerrainBlock::~TerrainBlock()
 228{
 229   // Kill collision
 230   sTerrainConvexList.nukeList();
 231
 232   SAFE_DELETE(mLightMap);
 233   mLightMapTex = NULL;
 234
 235#ifdef TORQUE_TOOLS
 236   TerrainEditor* editor = dynamic_cast<TerrainEditor*>(Sim::findObject("ETerrainEditor"));
 237   if (editor)
 238      editor->detachTerrain(this);
 239#endif
 240   deleteZodiacPrimitiveBuffer();
 241
 242   SAFE_RELEASE(mDetailTextureArray);
 243   SAFE_RELEASE(mMacroTextureArray);
 244   SAFE_RELEASE(mNormalTextureArray);
 245   SAFE_RELEASE(mOrmTextureArray);
 246}
 247
 248void TerrainBlock::_onTextureEvent( GFXTexCallbackCode code )
 249{
 250   if ( code == GFXZombify )
 251   {
 252      if (  mBaseTex.isValid() &&
 253            mBaseTex->isRenderTarget() )
 254         mBaseTex = NULL;
 255
 256      mLayerTex = NULL;
 257      mLightMapTex = NULL;
 258   }
 259}
 260
 261bool TerrainBlock::_setSquareSize( void *obj, const char *index, const char *data )
 262{
 263   TerrainBlock *terrain = static_cast<TerrainBlock*>( obj );
 264
 265   F32 newSqaureSize = dAtof( data );
 266   if ( !mIsEqual( terrain->mSquareSize, newSqaureSize ) )
 267   {
 268      terrain->mSquareSize = newSqaureSize;
 269
 270      if ( terrain->isServerObject() && terrain->isProperlyAdded() )
 271         terrain->_updateBounds();
 272
 273      terrain->setMaskBits( HeightMapChangeMask | SizeMask );
 274   }
 275
 276   return false;
 277}
 278
 279bool TerrainBlock::_setBaseTexSize( void *obj, const char *index, const char *data )
 280{
 281   TerrainBlock *terrain = static_cast<TerrainBlock*>( obj );
 282
 283   // NOTE: We're limiting the base texture size to 
 284   // 2048 as anything greater in size becomes too
 285   // large to generate for many cards.
 286   //
 287   // If you want to remove this limit feel free, but
 288   // prepare for problems if you don't ship the baked
 289   // base texture with your installer.
 290   //
 291
 292   S32 texSize = mClamp( dAtoi( data ), 0, 2048 );
 293   if ( terrain->mBaseTexSize != texSize )
 294   {
 295      terrain->mBaseTexSize = texSize;
 296      terrain->setMaskBits( MaterialMask );
 297   }
 298
 299   return false;
 300}
 301
 302bool TerrainBlock::_setBaseTexFormat(void *obj, const char *index, const char *data)
 303{
 304   TerrainBlock *terrain = static_cast<TerrainBlock*>(obj);
 305
 306   EngineEnumTable eTable = _baseTexFormat::_sEnumTable;
 307
 308   for (U8 i = 0; i < eTable.getNumValues(); i++)
 309   {
 310      if (strcasecmp(eTable[i].mName, data) == 0)
 311      {
 312         terrain->mBaseTexFormat = (BaseTexFormat)eTable[i].mInt;
 313         terrain->_updateMaterials();
 314
 315         if (terrain->isServerObject()) return false;
 316         terrain->_updateLayerTexture();
 317         // If the cached base texture is older that the terrain file or
 318         // it doesn't exist then generate and cache it.
 319         String baseCachePath = terrain->_getBaseTexCacheFileName();
 320         if (Platform::compareModifiedTimes(baseCachePath, terrain->mTerrainAsset->getTerrainFilePath()) < 0 && terrain->mUpdateBasetex)
 321            terrain->_updateBaseTexture(true);
 322         break;
 323      }
 324   }
 325
 326   return false;
 327}
 328
 329bool TerrainBlock::_setLightMapSize( void *obj, const char *index, const char *data )
 330{
 331   TerrainBlock *terrain = static_cast<TerrainBlock*>(obj);
 332
 333   // Handle inspector value decrements correctly
 334   U32 mapSize = dAtoi( data );
 335   if ( mapSize == terrain->mLightMapSize-1 )
 336      mapSize = terrain->mLightMapSize/2;
 337
 338   // Limit the lightmap size, and ensure it is a power of 2
 339   const U32 maxTextureSize = GFX->getCardProfiler()->queryProfile( "maxTextureSize", 1024 );
 340   mapSize = mClamp( getNextPow2( mapSize ), 0, maxTextureSize );
 341
 342   if ( terrain->mLightMapSize != mapSize )
 343   {
 344      terrain->mLightMapSize = mapSize;
 345      terrain->setMaskBits( MaterialMask );
 346   }
 347
 348   return false;
 349}
 350
 351bool TerrainBlock::setFile( const FileName &terrFileName )
 352{
 353   if ( mTerrainAsset && mTerrainAsset->getTerrainFilePath() == terrFileName )
 354      return mFile != NULL;
 355
 356   Resource<TerrainFile> file = ResourceManager::get().load( terrFileName );
 357   if( !file )
 358      return false;
 359      
 360   setFile( file );
 361   setMaskBits( FileMask | HeightMapChangeMask );
 362   
 363   return true;
 364}
 365
 366void TerrainBlock::setFile(const Resource<TerrainFile>& terr)
 367{
 368   if (mFile)
 369   {
 370      GFXTextureManager::removeEventDelegate(this, &TerrainBlock::_onTextureEvent);
 371      MATMGR->getFlushSignal().remove(this, &TerrainBlock::_onFlushMaterials);
 372   }
 373
 374   mFile = terr;
 375
 376   if (!mFile)
 377   {
 378      Con::errorf("TerrainBlock::setFile() - No valid terrain file!");
 379      return;
 380   }
 381
 382   if (terr->mNeedsResaving)
 383   {
 384      if (Platform::messageBox("Update Terrain File", "You appear to have a Terrain file in an older format. Do you want Torque to update it?", MBOkCancel, MIQuestion) == MROk)
 385      {
 386         mFile->save(terr->mFilePath.getFullPath());
 387         mFile->mNeedsResaving = false;
 388      }
 389   }
 390
 391   if (terr->mFileVersion != TerrainFile::FILE_VERSION || terr->mNeedsResaving)
 392   {
 393      Con::errorf(" *********************************************************");
 394      Con::errorf(" *********************************************************");
 395      Con::errorf(" *********************************************************");
 396      Con::errorf(" PLEASE RESAVE THE TERRAIN FILE FOR THIS MISSION!  THANKS!");
 397      Con::errorf(" *********************************************************");
 398      Con::errorf(" *********************************************************");
 399      Con::errorf(" *********************************************************");
 400   }
 401
 402
 403   _updateBounds();
 404
 405   resetWorldBox();
 406   setRenderTransform(mObjToWorld);
 407
 408   if (isClientObject())
 409   {
 410      if (mCRC != terr.getChecksum())
 411      {
 412         NetConnection::setLastError("Your terrain file doesn't match the version that is running on the server.");
 413         return;
 414      }
 415
 416      clearLightMap();
 417
 418      // Init the detail layer rendering helper.
 419      _updateMaterials();
 420      _updateLayerTexture();
 421
 422      // If the cached base texture is older that the terrain file or
 423      // it doesn't exist then generate and cache it.
 424      String baseCachePath = _getBaseTexCacheFileName();
 425      if (Platform::compareModifiedTimes(baseCachePath, mTerrainAsset->getTerrainFilePath()) < 0 && mUpdateBasetex)
 426         _updateBaseTexture(true);
 427
 428      // The base texture should have been cached by now... so load it.
 429      mBaseTex.set(baseCachePath, &GFXStaticTextureSRGBProfile, "TerrainBlock::mBaseTex");
 430
 431      GFXTextureManager::addEventDelegate(this, &TerrainBlock::_onTextureEvent);
 432      MATMGR->getFlushSignal().notify(this, &TerrainBlock::_onFlushMaterials);
 433
 434      // Build the terrain quadtree.
 435      _rebuildQuadtree();
 436
 437      // Preload all the materials.
 438      mCell->preloadMaterials();
 439
 440      mZoningDirty = true;
 441      SceneZoneSpaceManager::getZoningChangedSignal().notify(this, &TerrainBlock::_onZoningChanged);
 442   }
 443   else
 444      mCRC = terr.getChecksum();
 445}
 446
 447bool TerrainBlock::setTerrainAsset(const StringTableEntry terrainAssetId)
 448{
 449   if (TerrainAsset::getAssetById(terrainAssetId, &mTerrainAsset))
 450   {
 451      //Special exception case. If we've defaulted to the 'no shape' mesh, don't save it out, we'll retain the original ids/paths so it doesn't break
 452      //the TSStatic
 453      if (!mTerrainAsset.isNull())
 454      {
 455         mTerrFileName = StringTable->EmptyString();
 456      }
 457
 458      setFile(mTerrainAsset->getTerrainResource());
 459
 460      setMaskBits(-1);
 461
 462      return true;
 463   }
 464
 465   return false;
 466}
 467
 468bool TerrainBlock::save(const char *filename)
 469{
 470   return mFile->save(filename);
 471}
 472
 473bool TerrainBlock::saveAsset()
 474{
 475   if (!mTerrainAsset.isNull() && mTerrainAsset->isAssetValid())
 476   {
 477      mTerrainAsset->clearAssetDependencyFields("terrainMaterailAsset");
 478
 479      AssetQuery* pAssetQuery = new AssetQuery();
 480      pAssetQuery->registerObject();
 481
 482      AssetDatabase.findAssetType(pAssetQuery, "TerrainMaterialAsset");
 483
 484      TerrainBlock* clientTerr = static_cast<TerrainBlock*>(getClientObject());
 485
 486      for (U32 i = 0; i < pAssetQuery->mAssetList.size(); i++)
 487      {
 488         //Acquire it so we can check it for matches
 489         AssetPtr<TerrainMaterialAsset> terrMatAsset = pAssetQuery->mAssetList[i];
 490
 491         for (U32 m = 0; m < clientTerr->mFile->mMaterials.size(); m++)
 492         {
 493            StringTableEntry intMatName = clientTerr->mFile->mMaterials[m]->getInternalName();
 494
 495            StringTableEntry assetMatDefName = terrMatAsset->getMaterialDefinitionName();
 496            if (assetMatDefName == intMatName)
 497            {
 498               mTerrainAsset->addAssetDependencyField("terrainMaterailAsset", terrMatAsset.getAssetId());
 499            }
 500         }
 501
 502         terrMatAsset.clear();
 503      }
 504
 505      pAssetQuery->destroySelf();
 506
 507      bool saveAssetSuccess = mTerrainAsset->saveAsset();
 508
 509      if (!saveAssetSuccess)
 510         return false;
 511
 512      return mFile->save(mTerrainAsset->getTerrainFilePath());
 513   }
 514
 515   return false;
 516}
 517
 518bool TerrainBlock::_setTerrainFile( void *obj, const char *index, const char *data )
 519{
 520   //TerrainBlock* terrain = static_cast<TerrainBlock*>( obj )->setFile( FileName( data ) );
 521   TerrainBlock* terrain = static_cast<TerrainBlock*>(obj);
 522
 523   StringTableEntry file = StringTable->insert(data);
 524
 525   if (file != StringTable->EmptyString())
 526   {
 527      StringTableEntry assetId = TerrainAsset::getAssetIdByFilename(file);
 528      if (assetId != StringTable->EmptyString())
 529      {
 530         if (terrain->setTerrainAsset(assetId))
 531         {
 532            terrain->mTerrainAssetId = assetId;
 533            terrain->mTerrFileName = StringTable->EmptyString();
 534
 535            return false;
 536         }
 537      }
 538      else
 539      {
 540         terrain->mTerrainAsset = StringTable->EmptyString();
 541      }
 542   }
 543
 544   return true;
 545}
 546
 547bool TerrainBlock::_setTerrainAsset(void* obj, const char* index, const char* data)
 548{
 549   TerrainBlock* terr = static_cast<TerrainBlock*>(obj);// ->setFile(FileName(data));
 550
 551   terr->mTerrainAssetId = StringTable->insert(data);
 552
 553   return terr->setTerrainAsset(terr->mTerrainAssetId);
 554}
 555
 556void TerrainBlock::_updateBounds()
 557{
 558   if ( !mFile )
 559      return; // quick fix to stop crashing when deleting terrainblocks
 560
 561   // Setup our object space bounds.
 562   mBounds.minExtents.set( 0.0f, 0.0f, 0.0f );
 563   mBounds.maxExtents.set( getWorldBlockSize(), getWorldBlockSize(), 0.0f );
 564   getMinMaxHeight( &mBounds.minExtents.z, &mBounds.maxExtents.z );
 565
 566   // Set our mObjBox to be equal to mBounds
 567   if (  mObjBox.maxExtents != mBounds.maxExtents || 
 568         mObjBox.minExtents != mBounds.minExtents )
 569   {
 570      mObjBox = mBounds;
 571      resetWorldBox();
 572   }
 573}
 574
 575void TerrainBlock::_onZoningChanged( SceneZoneSpaceManager *zoneManager )
 576{
 577   const SceneManager* sm = getSceneManager();
 578
 579   if (mCell == NULL || (sm != NULL && sm->getZoneManager() != NULL && zoneManager != sm->getZoneManager()))
 580      return;
 581
 582   mZoningDirty = true;
 583}
 584
 585void TerrainBlock::setHeight( const Point2I &pos, F32 height )
 586{
 587   U16 ht = floatToFixed( height );
 588   mFile->setHeight( pos.x, pos.y, ht );
 589
 590   // Note: We do not update the grid here as this could
 591   // be called several times in a loop.  We depend on the
 592   // caller doing a grid update when he is done.
 593}
 594
 595F32 TerrainBlock::getHeight( const Point2I &pos )
 596{
 597   U16 ht = mFile->getHeight( pos.x, pos.y );
 598   return fixedToFloat( ht );
 599}
 600
 601void TerrainBlock::updateGridMaterials( const Point2I &minPt, const Point2I &maxPt )
 602{
 603   if ( mCell )
 604   {
 605      // Tell the terrain cell that something changed.
 606      const RectI gridRect( minPt, maxPt - minPt );
 607      mCell->updateGrid( gridRect, true );
 608   }
 609
 610   // We mark us as dirty... it will be updated
 611   // before the next time we render the terrain.
 612   mLayerTexDirty = true;
 613
 614   // Signal anyone that cares that the opacity was changed.
 615   smUpdateSignal.trigger( LayersUpdate, this, minPt, maxPt );
 616}
 617
 618
 619Point2I TerrainBlock::getGridPos( const Point3F &worldPos ) const
 620{
 621   Point3F terrainPos = worldPos;
 622   getWorldTransform().mulP( terrainPos );
 623
 624   F32 squareSize = ( F32 ) getSquareSize();
 625   F32 halfSquareSize = squareSize / 2.0;
 626
 627   F32 x = ( terrainPos.x + halfSquareSize ) / squareSize;
 628   F32 y = ( terrainPos.y + halfSquareSize ) / squareSize;
 629
 630   Point2I gridPos( ( S32 ) mFloor( x ), ( S32 ) mFloor( y ) );
 631   return gridPos;
 632}
 633
 634void TerrainBlock::updateGrid( const Point2I &minPt, const Point2I &maxPt, bool updateClient )
 635{
 636   // On the client we just signal everyone that the height
 637   // map has changed... the server does the actual changes.
 638   if ( isClientObject() )
 639   {
 640      PROFILE_SCOPE( TerrainBlock_updateGrid_Client );
 641
 642      // This depends on the client getting this call 'after' the server.
 643      // Which is currently the case.
 644      _updateBounds();
 645      mZoningDirty = true;
 646
 647      smUpdateSignal.trigger( HeightmapUpdate, this, minPt, maxPt );
 648
 649      // Tell the terrain cell that the height changed.
 650      const RectI gridRect( minPt, maxPt - minPt );
 651      mCell->updateGrid( gridRect );
 652
 653      // Rebuild the physics representation.
 654      if ( mPhysicsRep )
 655      {
 656         // Delay the update by a few milliseconds so 
 657         // that we're not rebuilding during an active
 658         // editing operation.
 659         mPhysicsRep->queueCallback( 500, Delegate<void()>( this, &TerrainBlock::_updatePhysics ) );
 660      }
 661
 662      return;
 663   }
 664
 665   // Now on the server we rebuild the 
 666   // affected area of the grid map.
 667   mFile->updateGrid( minPt, maxPt );
 668
 669   // Fix up the bounds.
 670   _updateBounds();
 671
 672   // Rebuild the physics representation.
 673   if ( mPhysicsRep )
 674   {
 675      // Delay the update by a few milliseconds so 
 676      // that we're not rebuilding during an active
 677      // editing operation.
 678      mPhysicsRep->queueCallback( 500, Delegate<void()>( this, &TerrainBlock::_updatePhysics ) );
 679   }
 680
 681   // Signal again here for any server side observers.
 682   smUpdateSignal.trigger( HeightmapUpdate, this, minPt, maxPt );
 683
 684   // If this is a server object and the client update
 685   // was requested then try to use the local connection
 686   // pointer to do it.
 687   if ( updateClient && getClientObject() )
 688      ((TerrainBlock*)getClientObject())->updateGrid( minPt, maxPt, false );
 689}
 690
 691bool TerrainBlock::getHeight( const Point2F &pos, F32 *height ) const
 692{
 693   PROFILE_SCOPE( TerrainBlock_getHeight );
 694
 695   F32 invSquareSize = 1.0f / mSquareSize;
 696   F32 xp = pos.x * invSquareSize;
 697   F32 yp = pos.y * invSquareSize;
 698   S32 x = S32(xp);
 699   S32 y = S32(yp);
 700   xp -= (F32)x;
 701   yp -= (F32)y;
 702
 703   const U32 blockMask = mFile->mSize - 1;
 704
 705   if ( x & ~blockMask || y & ~blockMask )
 706      return false;
 707   
 708   x &= blockMask;
 709   y &= blockMask;
 710   
 711   const TerrainSquare *sq = mFile->findSquare( 0, x, y );
 712   if ( sq->flags & TerrainSquare::Empty )
 713      return false;
 714
 715   F32 zBottomLeft = fixedToFloat( mFile->getHeight( x, y ) );
 716   F32 zBottomRight = fixedToFloat( mFile->getHeight( x + 1, y ) );
 717   F32 zTopLeft = fixedToFloat( mFile->getHeight( x, y + 1 ) );
 718   F32 zTopRight = fixedToFloat( mFile->getHeight( x + 1, y + 1 ) );
 719
 720   if ( sq->flags & TerrainSquare::Split45 )
 721   {
 722      if (xp>yp)
 723         // bottom half
 724         *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight);
 725      else
 726         // top half
 727         *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft);
 728   }
 729   else
 730   {
 731      if (1.0f-xp>yp)
 732         // bottom half
 733         *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft);
 734      else
 735         // top half
 736         *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight);
 737   }
 738
 739   return true;
 740}
 741
 742bool TerrainBlock::getNormal( const Point2F &pos, Point3F *normal, bool normalize, bool skipEmpty ) const
 743{
 744   PROFILE_SCOPE( TerrainBlock_getNormal );
 745
 746   F32 invSquareSize = 1.0f / mSquareSize;
 747   F32 xp = pos.x * invSquareSize;
 748   F32 yp = pos.y * invSquareSize;
 749   S32 x = S32(xp);
 750   S32 y = S32(yp);
 751   xp -= (F32)x;
 752   yp -= (F32)y;
 753   
 754   const U32 blockMask = mFile->mSize - 1;
 755
 756   if ( x & ~blockMask || y & ~blockMask )
 757      return false;
 758   
 759   x &= blockMask;
 760   y &= blockMask;
 761   
 762   const TerrainSquare *sq = mFile->findSquare( 0, x, y );
 763   if ( skipEmpty && sq->flags & TerrainSquare::Empty )
 764      return false;
 765
 766   F32 zBottomLeft = fixedToFloat( mFile->getHeight( x, y ) );
 767   F32 zBottomRight = fixedToFloat( mFile->getHeight( x + 1, y ) );
 768   F32 zTopLeft = fixedToFloat( mFile->getHeight( x, y + 1 ) );
 769   F32 zTopRight = fixedToFloat( mFile->getHeight( x + 1, y + 1 ) );
 770
 771   if ( sq->flags & TerrainSquare::Split45 )
 772   {
 773      if (xp>yp)
 774         // bottom half
 775         normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize);
 776      else
 777         // top half
 778         normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize);
 779   }
 780   else
 781   {
 782      if (1.0f-xp>yp)
 783         // bottom half
 784         normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize);
 785      else
 786         // top half
 787         normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize);
 788   }
 789
 790   if (normalize)
 791      normal->normalize();
 792
 793   return true;
 794}
 795
 796bool TerrainBlock::getSmoothNormal( const Point2F &pos, 
 797                                    Point3F *normal, 
 798                                    bool normalize,
 799                                    bool skipEmpty ) const
 800{
 801   PROFILE_SCOPE( TerrainBlock_getSmoothNormal );
 802
 803   F32 invSquareSize = 1.0f / mSquareSize;
 804   F32 xp = pos.x * invSquareSize;
 805   F32 yp = pos.y * invSquareSize;
 806   S32 x = S32(xp);
 807   S32 y = S32(yp);
 808   
 809   const U32 blockMask = mFile->mSize - 1;
 810
 811   if ( x & ~blockMask || y & ~blockMask )
 812      return false;
 813   
 814   x &= blockMask;
 815   y &= blockMask;
 816   
 817   const TerrainSquare *sq = mFile->findSquare( 0, x, y );
 818   if ( skipEmpty && sq->flags & TerrainSquare::Empty )
 819      return false;
 820
 821   F32 h1 = fixedToFloat( mFile->getHeight( x + 1, y ) );
 822   F32 h2 = fixedToFloat( mFile->getHeight( x, y + 1 ) );
 823   F32 h3 = fixedToFloat( mFile->getHeight( x - 1, y ) );
 824   F32 h4 = fixedToFloat( mFile->getHeight( x, y - 1 ) );
 825
 826   normal->set( h3 - h1, h4 - h2, mSquareSize * 2.0f );
 827
 828   if ( normalize )
 829      normal->normalize();
 830
 831   return true;
 832}
 833
 834bool TerrainBlock::getNormalAndHeight( const Point2F &pos, Point3F *normal, F32 *height, bool normalize ) const
 835{
 836   PROFILE_SCOPE( TerrainBlock_getNormalAndHeight );
 837
 838   F32 invSquareSize = 1.0f / mSquareSize;
 839   F32 xp = pos.x * invSquareSize;
 840   F32 yp = pos.y * invSquareSize;
 841   S32 x = S32(xp);
 842   S32 y = S32(yp);
 843   xp -= (F32)x;
 844   yp -= (F32)y;
 845
 846   const U32 blockMask = mFile->mSize - 1;
 847
 848   if ( x & ~blockMask || y & ~blockMask )
 849      return false;
 850   
 851   x &= blockMask;
 852   y &= blockMask;
 853   
 854   const TerrainSquare *sq = mFile->findSquare( 0, x, y );
 855   if ( sq->flags & TerrainSquare::Empty )
 856      return false;
 857
 858   F32 zBottomLeft  = fixedToFloat( mFile->getHeight(x, y) );
 859   F32 zBottomRight = fixedToFloat( mFile->getHeight(x + 1, y) );
 860   F32 zTopLeft     = fixedToFloat( mFile->getHeight(x, y + 1) );
 861   F32 zTopRight    = fixedToFloat( mFile->getHeight(x + 1, y + 1) );
 862
 863   if ( sq->flags & TerrainSquare::Split45 )
 864   {
 865      if (xp>yp)
 866      {
 867         // bottom half
 868         normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize);
 869         *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight);
 870      }
 871      else
 872      {
 873         // top half
 874         normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize);
 875         *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft);
 876      }
 877   }
 878   else
 879   {
 880      if (1.0f-xp>yp)
 881      {
 882         // bottom half
 883         normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize);
 884         *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft);
 885      }
 886      else
 887      {
 888         // top half
 889         normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize);
 890         *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight);
 891      }
 892   }
 893
 894   if (normalize)
 895      normal->normalize();
 896
 897   return true;
 898}
 899
 900
 901bool TerrainBlock::getNormalHeightMaterial(  const Point2F &pos, 
 902                                             Point3F *normal, 
 903                                             F32 *height, 
 904                                             StringTableEntry &matName ) const
 905{
 906   PROFILE_SCOPE( TerrainBlock_getNormalHeightMaterial );
 907
 908   F32 invSquareSize = 1.0f / mSquareSize;
 909   F32 xp = pos.x * invSquareSize;
 910   F32 yp = pos.y * invSquareSize;
 911   S32 x = S32(xp);
 912   S32 y = S32(yp);
 913   S32 xm = S32(mFloor( xp + 0.5f ));   
 914   S32 ym = S32(mFloor( yp + 0.5f ));   
 915   xp -= (F32)x;
 916   yp -= (F32)y;
 917
 918   const U32 blockMask = mFile->mSize - 1;
 919
 920   if ( x & ~blockMask || y & ~blockMask )
 921      return false;
 922   
 923   x &= blockMask;
 924   y &= blockMask;
 925   
 926   const TerrainSquare *sq = mFile->findSquare( 0, x, y );
 927   if ( sq->flags & TerrainSquare::Empty )
 928      return false;
 929
 930   F32 zBottomLeft  = fixedToFloat( mFile->getHeight(x, y) );
 931   F32 zBottomRight = fixedToFloat( mFile->getHeight(x + 1, y) );
 932   F32 zTopLeft     = fixedToFloat( mFile->getHeight(x, y + 1) );
 933   F32 zTopRight    = fixedToFloat( mFile->getHeight(x + 1, y + 1) );
 934
 935   matName = mFile->getMaterialName( xm, ym );
 936
 937   if ( sq->flags & TerrainSquare::Split45 )
 938   {
 939      if (xp>yp)
 940      {
 941         // bottom half
 942         normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize);
 943         *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight);
 944      }
 945      else
 946      {
 947         // top half
 948         normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize);
 949         *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft);
 950      }
 951   }
 952   else
 953   {
 954      if (1.0f-xp>yp)
 955      {
 956         // bottom half
 957         normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize);
 958         *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft);
 959      }
 960      else
 961      {
 962         // top half
 963         normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize);
 964         *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight);
 965      }
 966   }
 967
 968   normal->normalize();
 969
 970   return true;
 971}
 972
 973U32 TerrainBlock::getMaterialCount() const
 974{
 975   return mFile->mMaterials.size();
 976}
 977
 978void TerrainBlock::addMaterial( const String &name, U32 insertAt )
 979{
 980   TerrainMaterial *mat = TerrainMaterial::findOrCreate( name );
 981
 982   if ( insertAt == -1 )
 983   {
 984      mFile->mMaterials.push_back( mat );
 985      mFile->_initMaterialInstMapping();
 986
 987      bool isSrv = isServerObject();
 988
 989      //now we update our asset
 990      if (mTerrainAsset)
 991      {
 992         StringTableEntry terrMatName = StringTable->insert(name.c_str());
 993
 994         AssetQuery* aq = new AssetQuery();
 995         U32 foundCount = AssetDatabase.findAssetType(aq, "TerrainMaterialAsset");
 996
 997         for (U32 i = 0; i < foundCount; i++)
 998         {
 999            TerrainMaterialAsset* terrMatAsset = AssetDatabase.acquireAsset<TerrainMaterialAsset>(aq->mAssetList[i]);
1000            if (terrMatAsset && terrMatAsset->getMaterialDefinitionName() == terrMatName)
1001            {
1002               //Do iterative logic to find the next available slot and write to it with our new mat field
1003               mTerrainAsset->setDataField(StringTable->insert("terrainMaterialAsset"), nullptr, aq->mAssetList[i]);
1004            }
1005         }
1006      }
1007   }
1008   else
1009   {
1010
1011      // TODO: Insert and reindex!        
1012
1013   }
1014
1015   mDetailsDirty = true;
1016   mLayerTexDirty = true;
1017}
1018
1019void TerrainBlock::removeMaterial( U32 index )
1020{
1021   // Cannot delete if only one layer.
1022   if ( mFile->mMaterials.size() == 1 )
1023      return;
1024
1025   mFile->mMaterials.erase( index );
1026   mFile->_initMaterialInstMapping();
1027
1028   for ( S32 i = 0; i < mFile->mLayerMap.size(); i++ )
1029   {
1030      if ( mFile->mLayerMap[i] >= index &&
1031           mFile->mLayerMap[i] != 0 )
1032      {
1033            mFile->mLayerMap[i]--;
1034      }
1035   }
1036
1037   mDetailsDirty = true;
1038   mLayerTexDirty = true;     
1039}
1040
1041void TerrainBlock::updateMaterial( U32 index, const String &name )
1042{
1043   if ( index >= mFile->mMaterials.size() )
1044      return;
1045
1046   mFile->mMaterials[ index ] = TerrainMaterial::findOrCreate( name );
1047   mFile->_initMaterialInstMapping();
1048
1049   mDetailsDirty = true;
1050   mLayerTexDirty = true;
1051}
1052
1053TerrainMaterial* TerrainBlock::getMaterial( U32 index ) const
1054{
1055   if ( index >= mFile->mMaterials.size() )
1056      return NULL;
1057
1058   return mFile->mMaterials[ index ];
1059}
1060
1061void TerrainBlock::deleteAllMaterials()
1062{
1063   mFile->mMaterials.clear();
1064   mFile->mMaterialInstMapping.clearMatInstList();
1065}
1066
1067const char* TerrainBlock::getMaterialName( U32 index ) const
1068{
1069   if ( index < mFile->mMaterials.size() )
1070      return mFile->mMaterials[ index ]->getInternalName();
1071
1072   return NULL;
1073}
1074
1075void TerrainBlock::setLightMap( GBitmap *newLightMap )
1076{
1077   SAFE_DELETE( mLightMap );
1078   mLightMap = newLightMap;
1079   mLightMapTex = NULL;
1080}
1081
1082void TerrainBlock::clearLightMap()
1083{
1084   if ( !mLightMap )
1085      mLightMap = new GBitmap( mLightMapSize, mLightMapSize, 0, GFXFormatR8G8B8 );
1086
1087   mLightMap->fillWhite();
1088   mLightMapTex = NULL;
1089}
1090
1091GFXTextureObject* TerrainBlock::getLightMapTex()
1092{
1093   if ( mLightMapTex.isNull() && mLightMap )
1094   {
1095      mLightMapTex.set( mLightMap, 
1096                        &GFXStaticTextureProfile, 
1097                        false, 
1098                        "TerrainBlock::getLightMapTex()" );
1099   }
1100
1101   return mLightMapTex;
1102}
1103
1104void TerrainBlock::onEditorEnable()
1105{
1106}
1107
1108void TerrainBlock::onEditorDisable()
1109{
1110}
1111
1112bool TerrainBlock::onAdd()
1113{
1114   if(!Parent::onAdd())
1115      return false;
1116
1117   Resource<TerrainFile> terr;
1118
1119   if (!mTerrainAsset.isNull())
1120   {
1121      terr = mTerrainAsset->getTerrainResource();
1122
1123      if (terr == NULL)
1124      {
1125         if (isClientObject())
1126            NetConnection::setLastError("Unable to load terrain asset: %s", mTerrainAsset.getAssetId());
1127         return false;
1128      }
1129
1130      setFile(terr);
1131   }
1132 
1133   addToScene();
1134
1135   _updatePhysics();
1136
1137   return true;
1138}
1139
1140String TerrainBlock::_getBaseTexCacheFileName() const
1141{
1142   Torque::Path basePath( mTerrainAsset->getTerrainFilePath() );
1143   basePath.setFileName( basePath.getFileName() + "_basetex" );
1144   basePath.setExtension( formatToExtension(mBaseTexFormat) );
1145   return basePath.getFullPath();
1146}
1147
1148void TerrainBlock::_rebuildQuadtree()
1149{
1150   SAFE_DELETE( mCell );
1151
1152   // Recursively build the cells.
1153   mCell = TerrCell::init( this );
1154
1155   // Build the shared PrimitiveBuffer.
1156   mCell->createPrimBuffer( &mPrimBuffer );
1157   deleteZodiacPrimitiveBuffer();
1158}
1159
1160void TerrainBlock::_updatePhysics()
1161{
1162   if ( !PHYSICSMGR )
1163      return;
1164
1165   SAFE_DELETE( mPhysicsRep );
1166
1167   PhysicsCollision *colShape;
1168
1169   // If we can steal the collision shape from the local server
1170   // object then do so as it saves us alot of cpu time and memory.
1171   //
1172   // TODO: We should move this sharing down into TerrFile where
1173   // it probably belongs.
1174   //
1175   if ( getServerObject() )
1176   {
1177      TerrainBlock *serverTerrain = (TerrainBlock*)getServerObject();
1178      colShape = serverTerrain->mPhysicsRep->getColShape();
1179   }
1180   else
1181   {
1182      // Get empty state of each vert
1183      bool *holes = new bool[ getBlockSize() * getBlockSize() ];
1184      for ( U32 row = 0; row < getBlockSize(); row++ )
1185         for ( U32 column = 0; column < getBlockSize(); column++ )
1186            holes[ row + (column * getBlockSize()) ] = mFile->isEmptyAt( row, column );
1187
1188      colShape = PHYSICSMGR->createCollision();
1189      colShape->addHeightfield( mFile->getHeightMap().address(), holes, getBlockSize(), mSquareSize, MatrixF::Identity );
1190
1191      delete [] holes;
1192   }
1193
1194   PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
1195   mPhysicsRep = PHYSICSMGR->createBody();
1196   mPhysicsRep->init( colShape, 0, 0, this, world );
1197   mPhysicsRep->setTransform( getTransform() );
1198}
1199
1200void TerrainBlock::onRemove()
1201{
1202   removeFromScene();
1203   SceneZoneSpaceManager::getZoningChangedSignal().remove( this, &TerrainBlock::_onZoningChanged );
1204
1205   SAFE_DELETE( mPhysicsRep );
1206
1207   if ( isClientObject() )
1208   {
1209      mBaseTex = NULL;
1210      mLayerTex = NULL;
1211      SAFE_DELETE( mBaseMaterial );
1212      SAFE_DELETE( mDefaultMatInst );
1213      SAFE_DELETE( mCell );
1214      mPrimBuffer = NULL;
1215      mBaseShader = NULL;
1216      GFXTextureManager::removeEventDelegate( this, &TerrainBlock::_onTextureEvent );
1217      MATMGR->getFlushSignal().remove( this, &TerrainBlock::_onFlushMaterials );
1218   }
1219
1220   Parent::onRemove();
1221}
1222
1223void TerrainBlock::prepRenderImage( SceneRenderState* state )
1224{
1225   PROFILE_SCOPE(TerrainBlock_prepRenderImage);
1226   
1227   // If we need to update our cached 
1228   // zone state then do it now.
1229   if ( mZoningDirty )
1230   {
1231      mZoningDirty = false;
1232      mCell->updateZoning( getSceneManager()->getZoneManager() );
1233   }
1234
1235   _renderBlock( state );
1236}
1237
1238void TerrainBlock::setTransform(const MatrixF & mat)
1239{
1240   Parent::setTransform( mat );
1241
1242   // Update world-space OBBs.
1243   if( mCell )
1244   {
1245      mCell->updateOBBs();
1246      mZoningDirty = true;
1247   }
1248
1249   if ( mPhysicsRep )
1250      mPhysicsRep->setTransform( mat );
1251
1252   setRenderTransform( mat );
1253   setMaskBits( TransformMask );
1254
1255   if(isClientObject())
1256      smUpdateSignal.trigger( HeightmapUpdate, this, Point2I::Zero, Point2I::Max );
1257}
1258
1259void TerrainBlock::setScale( const VectorF &scale )
1260{
1261   // We disable scaling... we never scale!
1262   Parent::setScale( VectorF::One );   
1263}
1264
1265void TerrainBlock::initPersistFields()
1266{
1267   addGroup( "Media" );
1268
1269      addProtectedField("terrainAsset", TypeTerrainAssetId, Offset(mTerrainAssetId, TerrainBlock),
1270         &TerrainBlock::_setTerrainAsset, &defaultProtectedGetFn,
1271         "The source terrain data asset.");
1272
1273      addProtectedField( "terrainFile", TypeStringFilename, Offset( mTerrFileName, TerrainBlock ), 
1274         &TerrainBlock::_setTerrainFile, &defaultProtectedGetFn,
1275         "The source terrain data file." );
1276
1277   endGroup( "Media" );
1278
1279   addGroup( "Misc" );
1280
1281      addField( "castShadows", TypeBool, Offset( mCastShadows, TerrainBlock ),   
1282         "Allows the terrain to cast shadows onto itself and other objects."); 
1283   
1284      addProtectedField( "squareSize", TypeF32, Offset( mSquareSize, TerrainBlock ),
1285         &TerrainBlock::_setSquareSize, &defaultProtectedGetFn,
1286         "Indicates the spacing between points on the XY plane on the terrain." );
1287
1288      addProtectedField( "baseTexSize", TypeS32, Offset( mBaseTexSize, TerrainBlock ),
1289         &TerrainBlock::_setBaseTexSize, &defaultProtectedGetFn,
1290         "Size of base texture size per meter." );
1291
1292      addProtectedField("baseTexFormat", TYPEID<baseTexFormat>(), Offset(mBaseTexFormat, TerrainBlock),
1293         &TerrainBlock::_setBaseTexFormat, &defaultProtectedGetFn,
1294         "");
1295
1296      addProtectedField( "lightMapSize", TypeS32, Offset( mLightMapSize, TerrainBlock ),
1297         &TerrainBlock::_setLightMapSize, &defaultProtectedGetFn,
1298         "Light map dimensions in pixels." );
1299
1300      addField( "screenError", TypeS32, Offset( mScreenError, TerrainBlock ), "Not yet implemented." );
1301   
1302      addField( "updateBasetex", TypeBool, Offset( mUpdateBasetex, TerrainBlock ), "Whether or not to update the Base Texture" );
1303
1304   endGroup( "Misc" );
1305
1306   addGroup("AFX");
1307   addField("ignoreZodiacs",     TypeBool,      Offset(mIgnoreZodiacs,    TerrainBlock));
1308   endGroup("AFX");
1309   Parent::initPersistFields();
1310
1311   removeField( "scale" );
1312
1313   Con::addVariable( "$TerrainBlock::debugRender", TypeBool, &smDebugRender, "Triggers debug rendering of terrain cells\n\n"
1314      "@ingroup Terrain");
1315   
1316   Con::addVariable( "$pref::Terrain::lodScale", TypeF32, &smLODScale, "A global LOD scale used to tweak the default terrain screen error value.\n\n"
1317      "@ingroup Terrain");
1318
1319   Con::addVariable( "$pref::Terrain::detailScale", TypeF32, &smDetailScale, "A global detail scale used to tweak the material detail distances.\n\n" 
1320      "@ingroup Terrain");
1321}
1322
1323void TerrainBlock::inspectPostApply()
1324{
1325   Parent::inspectPostApply();
1326   setMaskBits( MiscMask );
1327}
1328
1329U32 TerrainBlock::packUpdate(NetConnection* con, U32 mask, BitStream *stream)
1330{
1331   U32 retMask = Parent::packUpdate( con, mask, stream );
1332   
1333   if ( stream->writeFlag( mask & TransformMask ) )
1334      mathWrite( *stream, getTransform() );
1335
1336   if ( stream->writeFlag( mask & FileMask ) )
1337   {
1338      S32 idasdasdf = getId();
1339      stream->write(mCRC);
1340      stream->writeString( mTerrainAsset.getAssetId() );
1341   }
1342
1343   if ( stream->writeFlag( mask & SizeMask ) )
1344      stream->write( mSquareSize );
1345
1346   stream->writeFlag( mCastShadows );
1347   
1348   if ( stream->writeFlag( mask & MaterialMask ) )
1349   {
1350      stream->write( mBaseTexSize );
1351      stream->write( mLightMapSize );
1352   }
1353
1354   stream->writeFlag( mask & HeightMapChangeMask );
1355
1356   if ( stream->writeFlag( mask & MiscMask ) )
1357      stream->write( mScreenError );
1358
1359   stream->writeInt(mBaseTexFormat, 32);
1360   
1361   stream->writeFlag(mUpdateBasetex);
1362   stream->writeFlag(mIgnoreZodiacs);
1363
1364   return retMask;
1365}
1366
1367void TerrainBlock::unpackUpdate(NetConnection* con, BitStream *stream)
1368{
1369   Parent::unpackUpdate( con, stream );
1370   
1371   if ( stream->readFlag() ) // TransformMask
1372   {
1373      MatrixF mat;
1374      mathRead( *stream, &mat );
1375      setTransform( mat );
1376   }
1377
1378   if ( stream->readFlag() ) // FileMask
1379   {
1380      stream->read(&mCRC);
1381
1382      char buffer[256];
1383      stream->readString(buffer);
1384      bool validAsset = setTerrainAsset(StringTable->insert(buffer));
1385   }
1386
1387   if ( stream->readFlag() ) // SizeMask
1388      stream->read( &mSquareSize );
1389
1390   mCastShadows = stream->readFlag();
1391
1392   if ( stream->readFlag() ) // MaterialMask
1393   {
1394      U32 baseTexSize;
1395      stream->read( &baseTexSize );
1396      if ( mBaseTexSize != baseTexSize )
1397      {
1398         mBaseTexSize = baseTexSize;
1399         if ( isProperlyAdded() )
1400            _updateBaseTexture( NONE );
1401      }
1402
1403      U32 lightMapSize;
1404      stream->read( &lightMapSize );
1405      if ( mLightMapSize != lightMapSize )
1406      {
1407         mLightMapSize = lightMapSize;
1408         if ( isProperlyAdded() )
1409         {
1410            SAFE_DELETE( mLightMap );
1411            clearLightMap();
1412         }
1413      }
1414   }
1415
1416   if ( stream->readFlag() && isProperlyAdded() ) // HeightMapChangeMask
1417   {
1418      _updateBounds();
1419      _rebuildQuadtree();
1420      _updatePhysics();
1421      mDetailsDirty = true;
1422      mLayerTexDirty = true;
1423   }
1424
1425   if ( stream->readFlag() ) // MiscMask
1426      stream->read( &mScreenError );
1427
1428   mBaseTexFormat = (BaseTexFormat)stream->readInt(32);
1429   
1430   mUpdateBasetex = stream->readFlag();
1431   mIgnoreZodiacs = stream->readFlag();
1432}
1433
1434void TerrainBlock::getMinMaxHeight( F32 *minHeight, F32 *maxHeight ) const 
1435{
1436   // We can get the bound height from the last grid level.
1437   const TerrainSquare *sq = mFile->findSquare( mFile->mGridLevels, 0, 0 );
1438   *minHeight = fixedToFloat( sq->minHeight );
1439   *maxHeight = fixedToFloat( sq->maxHeight );
1440}
1441
1442void TerrainBlock::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
1443{
1444   if (!mTerrainAsset.isNull())
1445      usedAssetsList->push_back_unique(mTerrainAsset->getAssetId());
1446}
1447//-----------------------------------------------------------------------------
1448// Console Methods
1449//-----------------------------------------------------------------------------
1450
1451DefineEngineMethod( TerrainBlock, save, bool, ( const char* fileName),,
1452               "@brief Saves the terrain block's terrain file to the specified file name.\n\n"
1453
1454               "@param fileName Name and path of file to save terrain data to.\n\n"
1455
1456               "@return True if file save was successful, false otherwise")
1457{
1458   char filename[256];
1459   dStrcpy(filename,fileName,256);
1460   char *ext = dStrrchr(filename, '.');
1461   if (!ext || dStricmp(ext, ".ter") != 0)
1462      dStrcat(filename, ".ter", 256);
1463   return static_cast<TerrainBlock*>(object)->save(filename);
1464}
1465
1466DefineEngineMethod(TerrainBlock, saveAsset, bool, (), ,
1467   "@brief Saves the terrain block's terrain file to the specified file name.\n\n"
1468
1469   "@param fileName Name and path of file to save terrain data to.\n\n"
1470
1471   "@return True if file save was successful, false otherwise")
1472{
1473   return static_cast<TerrainBlock*>(object)->saveAsset();
1474}
1475
1476DefineEngineMethod( TerrainBlock, setMaterialsDirty, void, (),, "")
1477{
1478   static_cast<TerrainBlock*>(object)->setMaterialsDirty();
1479}
1480
1481//ConsoleMethod(TerrainBlock, save, bool, 3, 3, "(string fileName) - saves the terrain block's terrain file to the specified file name.")
1482//{
1483//   char filename[256];
1484//   dStrcpy(filename,argv[2],256);
1485//   char *ext = dStrrchr(filename, '.');
1486//   if (!ext || dStricmp(ext, ".ter") != 0)
1487//      dStrcat(filename, ".ter", 256);
1488//   return static_cast<TerrainBlock*>(object)->save(filename);
1489//}
1490
1491ConsoleDocFragment _getTerrainHeight1(
1492   "@brief Gets the terrain height at the specified position\n\n"
1493   "@param position The world space point, minus the z (height) value. Formatted as (\"x y\")\n\n"
1494   "@return Returns the terrain height at the given point as an F32 value.\n\n"
1495   "@ingroup Terrain",
1496   NULL,
1497   "bool getTerrainHeight( Point2I position );"
1498);
1499ConsoleDocFragment _getTerrainHeight2(
1500   "@brief Gets the terrain height at the specified position\n\n"
1501   "@param x The X coordinate in world space\n"
1502   "@param y The Y coordinate in world space\n\n"
1503   "@return Returns the terrain height at the given point as an F32 value.\n\n"
1504   "@ingroup Terrain",
1505   NULL,
1506   "bool getTerrainHeight( F32 x, F32 y);"
1507);
1508
1509DefineEngineFunction( getTerrainHeight, F32, (const char* ptOrX, const char* y), (""), "(Point2 pos) - gets the terrain height at the specified position."
1510            "@param pos The world space point, minus the z (height) value\n Can be formatted as either (\"x y\") or (x,y)\n"
1511            "@return Returns the terrain height at the given point as an F32 value.\n"
1512            "@hide")
1513{
1514   F32 height = 0.0f;
1515
1516   Point2F pos;
1517   if(!String::isEmpty(ptOrX) && String::isEmpty(y))
1518      dSscanf(ptOrX, "%f %f", &pos.x, &pos.y);
1519   else if(!String::isEmpty(ptOrX) && !String::isEmpty(y))
1520   {
1521      pos.x = dAtof(ptOrX);
1522      pos.y = dAtof(y);
1523   }
1524
1525   TerrainBlock * terrain = getTerrainUnderWorldPoint(Point3F(pos.x, pos.y, 5000.0f));
1526   if(terrain && terrain->isServerObject())
1527   {
1528      Point3F offset;
1529      terrain->getTransform().getColumn(3, &offset);
1530      pos -= Point2F(offset.x, offset.y);
1531      terrain->getHeight(pos, &height);
1532   }
1533   return height;
1534}
1535
1536ConsoleDocFragment _getTerrainHeightBelowPosition1(
1537   "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n"
1538   "@param position The world space point, minus the z (height) value. Formatted as (\"x y\")\n\n"
1539   "@return Returns the closest terrain height below the given point as an F32 value.\n\n"
1540   "@ingroup Terrain",
1541   NULL,
1542   "bool getTerrainHeightBelowPosition( Point2I position );"
1543);
1544ConsoleDocFragment _getTerrainHeightBelowPosition2(
1545   "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n"
1546   "@param x The X coordinate in world space\n"
1547   "@param y The Y coordinate in world space\n\n"
1548   "@return Returns the closest terrain height below the given point as an F32 value.\n\n"
1549   "@ingroup Terrain",
1550   NULL,
1551   "bool getTerrainHeightBelowPosition( F32 x, F32 y);"
1552);
1553
1554DefineEngineFunction( getTerrainHeightBelowPosition, F32, (const char* ptOrX, const char* y, const char* z), ("", ""),
1555            "(Point3F pos) - gets the terrain height at the specified position."
1556            "@param pos The world space point. Can be formatted as either (\"x y z\") or (x,y,z)\n"
1557            "@note This function is useful if you simply want to grab the terrain height underneath an object.\n"
1558            "@return Returns the terrain height at the given point as an F32 value.\n"
1559            "@hide")
1560{
1561   F32 height = 0.0f;
1562
1563   Point3F pos;
1564   if(!String::isEmpty(ptOrX) && String::isEmpty(y) && String::isEmpty(z))
1565      dSscanf(ptOrX, "%f %f %f", &pos.x, &pos.y, &pos.z);
1566   else if(!String::isEmpty(ptOrX) && !String::isEmpty(y) && !String::isEmpty(z))
1567   {
1568      pos.x = dAtof(ptOrX);
1569      pos.y = dAtof(y);
1570      pos.z = dAtof(z);
1571   }
1572
1573   TerrainBlock * terrain = getTerrainUnderWorldPoint(pos);
1574   
1575   Point2F nohghtPos(pos.x, pos.y);
1576
1577   if(terrain)
1578   {
1579      if(terrain->isServerObject())
1580      {
1581         Point3F offset;
1582         terrain->getTransform().getColumn(3, &offset);
1583         nohghtPos -= Point2F(offset.x, offset.y);
1584         terrain->getHeight(nohghtPos, &height);
1585      }
1586   }
1587   
1588   return height;
1589}
1590const U16* TerrainBlock::getZodiacPrimitiveBuffer()
1591{ 
1592   if (!zode_primBuffer && !mIgnoreZodiacs)
1593      TerrCell::createZodiacPrimBuffer(&zode_primBuffer);
1594   return zode_primBuffer;
1595}
1596
1597void TerrainBlock::deleteZodiacPrimitiveBuffer()
1598{
1599   if (zode_primBuffer != 0)
1600   {
1601      delete [] zode_primBuffer;
1602      zode_primBuffer = 0;
1603   }
1604}
1605
1606