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