2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
2017-07-26 21:05:04 +00:00
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
2012-09-19 15:15:01 +00:00
# include "platform/platform.h"
# include "T3D/groundPlane.h"
# include "renderInstance/renderPassManager.h"
# include "scene/sceneRenderState.h"
# include "materials/sceneData.h"
# include "materials/materialDefinition.h"
# include "materials/materialManager.h"
# include "materials/baseMatInstance.h"
# include "math/util/frustum.h"
# include "math/mPlane.h"
# include "console/consoleTypes.h"
# include "console/engineAPI.h"
# include "core/stream/bitStream.h"
# include "collision/boxConvex.h"
# include "collision/abstractPolyList.h"
# include "T3D/physics/physicsPlugin.h"
# include "T3D/physics/physicsBody.h"
# include "T3D/physics/physicsCollision.h"
2018-01-23 22:03:18 +00:00
# ifdef TORQUE_AFX_ENABLED
2017-07-26 21:05:04 +00:00
# include "afx/ce/afxZodiacMgr.h"
2018-01-23 22:03:18 +00:00
# endif
2012-09-19 15:15:01 +00:00
/// Minimum square size allowed. This is a cheap way to limit the amount
/// of geometry possibly generated by the GroundPlane (vertex buffers have a
/// limit, too). Dynamically clipping extents into range is a problem since the
/// location of the horizon depends on the camera orientation. Just shifting
/// squareSize as needed also doesn't work as that causes different geometry to
/// be generated depending on the viewpoint and orientation which affects the
/// texturing.
static const F32 sMIN_SQUARE_SIZE = 16 ;
IMPLEMENT_CO_NETOBJECT_V1 ( GroundPlane ) ;
ConsoleDocClass ( GroundPlane ,
" @brief An infinite plane extending in all direction. \n \n "
" %GroundPlane is useful for setting up simple testing scenes, or it can be "
" placed under an existing scene to keep objects from falling into 'nothing'. \n \n "
" %GroundPlane may not be moved or rotated, it is always at the world origin. \n \n "
" @ingroup Terrain "
) ;
GroundPlane : : GroundPlane ( )
: mSquareSize ( 128.0f ) ,
mScaleU ( 1.0f ) ,
mScaleV ( 1.0f ) ,
mMaterial ( NULL ) ,
2021-07-19 06:07:08 +00:00
mMaterialInst ( NULL ) ,
2016-10-14 23:16:55 +00:00
mPhysicsRep ( NULL ) ,
2012-09-19 15:15:01 +00:00
mMin ( 0.0f , 0.0f ) ,
2016-10-14 23:16:55 +00:00
mMax ( 0.0f , 0.0f )
2012-09-19 15:15:01 +00:00
{
mTypeMask | = StaticObjectType | StaticShapeObjectType ;
mNetFlags . set ( Ghostable | ScopeAlways ) ;
mConvexList = new Convex ;
2017-07-26 21:05:04 +00:00
mTypeMask | = TerrainLikeObjectType ;
2020-11-02 05:32:34 +00:00
2021-10-03 07:56:26 +00:00
INIT_ASSET ( Material ) ;
2012-09-19 15:15:01 +00:00
}
GroundPlane : : ~ GroundPlane ( )
{
2025-12-29 23:45:09 +00:00
mMaterial = NULL ;
2021-07-19 06:07:08 +00:00
if ( mMaterialInst )
SAFE_DELETE ( mMaterialInst ) ;
2012-09-19 15:15:01 +00:00
mConvexList - > nukeList ( ) ;
SAFE_DELETE ( mConvexList ) ;
}
2025-03-09 16:53:23 +00:00
FRangeValidator squareSizeRange ( sMIN_SQUARE_SIZE , FLT_MAX ) ;
2012-09-19 15:15:01 +00:00
void GroundPlane : : initPersistFields ( )
{
2023-01-27 07:13:15 +00:00
docsURL ;
2012-09-19 15:15:01 +00:00
addGroup ( " Plane " ) ;
2025-03-09 16:53:23 +00:00
addFieldV ( " squareSize " , TypeRangedF32 , Offset ( mSquareSize , GroundPlane ) , & squareSizeRange , " Square size in meters to which %GroundPlane subdivides its geometry. " ) ;
addFieldV ( " scaleU " , TypeRangedF32 , Offset ( mScaleU , GroundPlane ) , & CommonValidators : : PositiveFloat , " Scale of texture repeat in the U direction. " ) ;
addFieldV ( " scaleV " , TypeRangedF32 , Offset ( mScaleV , GroundPlane ) , & CommonValidators : : PositiveFloat , " Scale of texture repeat in the V direction. " ) ;
2020-11-02 05:32:34 +00:00
2021-07-19 06:07:08 +00:00
INITPERSISTFIELD_MATERIALASSET ( Material , GroundPlane , " The material used to render the ground plane. " ) ;
2012-09-19 15:15:01 +00:00
endGroup ( " Plane " ) ;
Parent : : initPersistFields ( ) ;
removeField ( " scale " ) ;
removeField ( " position " ) ;
removeField ( " rotation " ) ;
}
bool GroundPlane : : onAdd ( )
{
if ( ! Parent : : onAdd ( ) )
return false ;
if ( isClientObject ( ) )
_updateMaterial ( ) ;
if ( mSquareSize < sMIN_SQUARE_SIZE )
{
Con : : errorf ( " GroundPlane - squareSize below threshold; re-setting to %.02f " , sMIN_SQUARE_SIZE ) ;
mSquareSize = sMIN_SQUARE_SIZE ;
}
Parent : : setScale ( VectorF ( 1.0f , 1.0f , 1.0f ) ) ;
Parent : : setTransform ( MatrixF : : Identity ) ;
setGlobalBounds ( ) ;
resetWorldBox ( ) ;
addToScene ( ) ;
if ( PHYSICSMGR )
{
PhysicsCollision * colShape = PHYSICSMGR - > createCollision ( ) ;
colShape - > addPlane ( PlaneF ( Point3F : : Zero , Point3F ( 0 , 0 , 1 ) ) ) ;
PhysicsWorld * world = PHYSICSMGR - > getWorld ( isServerObject ( ) ? " server " : " client " ) ;
mPhysicsRep = PHYSICSMGR - > createBody ( ) ;
mPhysicsRep - > init ( colShape , 0 , 0 , this , world ) ;
}
return true ;
}
void GroundPlane : : onRemove ( )
{
2023-10-08 18:19:43 +00:00
U32 assetStatus = MaterialAsset : : getAssetErrCode ( mMaterialAsset ) ;
if ( assetStatus = = AssetBase : : Ok )
2021-07-19 06:07:08 +00:00
AssetDatabase . releaseAsset ( mMaterialAsset . getAssetId ( ) ) ;
//SAFE_DELETE(mMaterialInst);
2012-09-19 15:15:01 +00:00
SAFE_DELETE ( mPhysicsRep ) ;
removeFromScene ( ) ;
Parent : : onRemove ( ) ;
}
void GroundPlane : : inspectPostApply ( )
{
Parent : : inspectPostApply ( ) ;
setMaskBits ( U32 ( - 1 ) ) ;
if ( mSquareSize < sMIN_SQUARE_SIZE )
{
Con : : errorf ( " GroundPlane - squareSize below threshold; re-setting to %.02f " , sMIN_SQUARE_SIZE ) ;
mSquareSize = sMIN_SQUARE_SIZE ;
}
setScale ( VectorF ( 1.0f , 1.0f , 1.0f ) ) ;
}
void GroundPlane : : setTransform ( const MatrixF & mat )
{
// Ignore.
}
void GroundPlane : : setScale ( const Point3F & scale )
{
// Ignore.
}
U32 GroundPlane : : packUpdate ( NetConnection * connection , U32 mask , BitStream * stream )
{
U32 retMask = Parent : : packUpdate ( connection , mask , stream ) ;
stream - > write ( mSquareSize ) ;
stream - > write ( mScaleU ) ;
stream - > write ( mScaleV ) ;
2020-12-28 05:24:29 +00:00
2021-10-03 07:56:26 +00:00
PACK_ASSET ( connection , Material ) ;
2012-09-19 15:15:01 +00:00
return retMask ;
}
void GroundPlane : : unpackUpdate ( NetConnection * connection , BitStream * stream )
{
Parent : : unpackUpdate ( connection , stream ) ;
stream - > read ( & mSquareSize ) ;
stream - > read ( & mScaleU ) ;
stream - > read ( & mScaleV ) ;
2020-11-02 05:32:34 +00:00
2021-10-03 07:56:26 +00:00
UNPACK_ASSET ( connection , Material ) ;
2012-09-19 15:15:01 +00:00
// If we're added then something possibly changed in
// the editor... do an update of the material and the
// geometry.
if ( isProperlyAdded ( ) )
{
_updateMaterial ( ) ;
mVertexBuffer = NULL ;
}
}
void GroundPlane : : _updateMaterial ( )
{
2020-12-28 05:24:29 +00:00
if ( mMaterialAsset . notNull ( ) )
2012-09-19 15:15:01 +00:00
{
2021-07-19 06:07:08 +00:00
if ( mMaterialInst & & String ( mMaterialAsset - > getMaterialDefinitionName ( ) ) . equal ( mMaterialInst - > getMaterial ( ) - > getName ( ) , String : : NoCase ) )
2020-12-28 05:24:29 +00:00
return ;
2021-07-19 06:07:08 +00:00
SAFE_DELETE ( mMaterialInst ) ;
2020-12-28 05:24:29 +00:00
2021-07-19 06:07:08 +00:00
mMaterialInst = MATMGR - > createMatInstance ( mMaterialAsset - > getMaterialDefinitionName ( ) , getGFXVertexFormat < VertexType > ( ) ) ;
2012-09-19 15:15:01 +00:00
2021-07-19 06:07:08 +00:00
if ( ! mMaterialInst )
2020-12-28 05:24:29 +00:00
Con : : errorf ( " GroundPlane::_updateMaterial - no Material called '%s' " , mMaterialAsset - > getMaterialDefinitionName ( ) ) ;
2020-11-02 05:32:34 +00:00
}
2012-09-19 15:15:01 +00:00
}
bool GroundPlane : : castRay ( const Point3F & start , const Point3F & end , RayInfo * info )
{
PlaneF plane ( Point3F ( 0.0f , 0.0f , 0.0f ) , Point3F ( 0.0f , 0.0f , 1.0f ) ) ;
F32 t = plane . intersect ( start , end ) ;
if ( t > = 0.0 & & t < = 1.0 )
{
info - > t = t ;
info - > setContactPoint ( start , end ) ;
info - > normal . set ( 0 , 0 , 1 ) ;
2021-07-19 06:07:08 +00:00
info - > material = mMaterialInst ;
2012-09-19 15:15:01 +00:00
info - > object = this ;
info - > distance = 0 ;
info - > faceDot = 0 ;
info - > texCoord . set ( 0 , 0 ) ;
return true ;
}
return false ;
}
void GroundPlane : : buildConvex ( const Box3F & box , Convex * convex )
{
mConvexList - > collectGarbage ( ) ;
Box3F planeBox = getPlaneBox ( ) ;
if ( ! box . isOverlapped ( planeBox ) )
return ;
// See if we already have a convex in the working set.
2024-05-19 00:18:50 +00:00
PlaneConvex * planeConvex = NULL ;
2012-09-19 15:15:01 +00:00
CollisionWorkingList & wl = convex - > getWorkingList ( ) ;
CollisionWorkingList * itr = wl . wLink . mNext ;
for ( ; itr ! = & wl ; itr = itr - > wLink . mNext )
{
2024-05-19 00:18:50 +00:00
if ( itr - > mConvex - > getType ( ) = = PlaneConvexType & &
2012-09-19 15:15:01 +00:00
itr - > mConvex - > getObject ( ) = = this )
{
2024-05-19 00:18:50 +00:00
planeConvex = ( PlaneConvex * ) itr - > mConvex ;
2012-09-19 15:15:01 +00:00
break ;
}
}
2024-05-19 00:18:50 +00:00
if ( ! planeConvex )
2012-09-19 15:15:01 +00:00
{
2024-05-19 00:18:50 +00:00
planeConvex = new PlaneConvex ;
mConvexList - > registerObject ( planeConvex ) ;
planeConvex - > init ( this ) ;
2012-09-19 15:15:01 +00:00
2024-05-19 00:18:50 +00:00
convex - > addToWorkingList ( planeConvex ) ;
2012-09-19 15:15:01 +00:00
}
// Update our convex to best match the queried box
2024-05-19 00:18:50 +00:00
if ( planeConvex )
2012-09-19 15:15:01 +00:00
{
Point3F queryCenter = box . getCenter ( ) ;
2024-05-20 11:21:37 +00:00
planeConvex - > mCenter = Point3F ( queryCenter . x , queryCenter . y , 0 ) ;
planeConvex - > mSize = Point3F ( box . getExtents ( ) . x , box . getExtents ( ) . y , 0 ) ;
2012-09-19 15:15:01 +00:00
}
}
2013-07-04 07:23:23 +00:00
bool GroundPlane : : buildPolyList ( PolyListContext context , AbstractPolyList * polyList , const Box3F & box , const SphereF & )
2012-09-19 15:15:01 +00:00
{
polyList - > setObject ( this ) ;
polyList - > setTransform ( & MatrixF : : Identity , Point3F ( 1.0f , 1.0f , 1.0f ) ) ;
2013-07-04 07:23:23 +00:00
if ( context = = PLC_Navigation )
{
F32 z = getPosition ( ) . z ;
Point3F
p0 ( box . minExtents . x , box . maxExtents . y , z ) ,
p1 ( box . maxExtents . x , box . maxExtents . y , z ) ,
p2 ( box . maxExtents . x , box . minExtents . y , z ) ,
p3 ( box . minExtents . x , box . minExtents . y , z ) ;
// Add vertices to poly list.
U32 v0 = polyList - > addPoint ( p0 ) ;
polyList - > addPoint ( p1 ) ;
polyList - > addPoint ( p2 ) ;
polyList - > addPoint ( p3 ) ;
// Add plane between first three vertices.
polyList - > begin ( 0 , 0 ) ;
polyList - > vertex ( v0 ) ;
polyList - > vertex ( v0 + 1 ) ;
polyList - > vertex ( v0 + 2 ) ;
polyList - > plane ( v0 , v0 + 1 , v0 + 2 ) ;
polyList - > end ( ) ;
// Add plane between last three vertices.
polyList - > begin ( 0 , 1 ) ;
polyList - > vertex ( v0 + 2 ) ;
polyList - > vertex ( v0 + 3 ) ;
polyList - > vertex ( v0 ) ;
polyList - > plane ( v0 + 2 , v0 + 3 , v0 ) ;
polyList - > end ( ) ;
return true ;
}
2012-09-19 15:15:01 +00:00
Box3F planeBox = getPlaneBox ( ) ;
2021-07-19 06:07:08 +00:00
polyList - > addBox ( planeBox , mMaterialInst ) ;
2012-09-19 15:15:01 +00:00
return true ;
}
void GroundPlane : : prepRenderImage ( SceneRenderState * state )
{
PROFILE_SCOPE ( GroundPlane_prepRenderImage ) ;
// TODO: Should we skip rendering the ground plane into
// the shadows? Its not like you can ever get under it.
if ( ! mMaterial )
return ;
// If we don't have a material instance after the override then
// we can skip rendering all together.
2021-07-19 06:07:08 +00:00
BaseMatInstance * matInst = state - > getOverrideMaterial ( mMaterialInst ) ;
2012-09-19 15:15:01 +00:00
if ( ! matInst )
return ;
PROFILE_SCOPE ( GroundPlane_prepRender ) ;
// Update the geometry.
2013-11-07 20:07:16 +00:00
createGeometry ( state - > getCullingFrustum ( ) ) ;
2012-09-19 15:15:01 +00:00
if ( mVertexBuffer . isNull ( ) )
return ;
2018-01-23 22:03:18 +00:00
# ifdef TORQUE_AFX_ENABLED
2017-07-26 21:05:04 +00:00
afxZodiacMgr : : renderGroundPlaneZodiacs ( state , this ) ;
2018-01-23 22:03:18 +00:00
# endif
2012-09-19 15:15:01 +00:00
// Add a render instance.
RenderPassManager * pass = state - > getRenderPass ( ) ;
MeshRenderInst * ri = pass - > allocInst < MeshRenderInst > ( ) ;
ri - > type = RenderPassManager : : RIT_Mesh ;
ri - > vertBuff = & mVertexBuffer ;
ri - > primBuff = & mPrimitiveBuffer ;
ri - > prim = & mPrimitive ;
ri - > matInst = matInst ;
ri - > objectToWorld = pass - > allocUniqueXform ( MatrixF : : Identity ) ;
ri - > worldToCamera = pass - > allocSharedXform ( RenderPassManager : : View ) ;
ri - > projection = pass - > allocSharedXform ( RenderPassManager : : Projection ) ;
ri - > visibility = 1.0f ;
ri - > translucentSort = matInst - > getMaterial ( ) - > isTranslucent ( ) ;
ri - > defaultKey = matInst - > getStateHint ( ) ;
if ( ri - > translucentSort )
ri - > type = RenderPassManager : : RIT_Translucent ;
// If we need lights then set them up.
if ( matInst - > isForwardLit ( ) )
{
LightQuery query ;
query . init ( getWorldSphere ( ) ) ;
query . getLights ( ri - > lights , 8 ) ;
}
pass - > addInst ( ri ) ;
}
/// Generate a subset of the ground plane matching the given frustum.
void GroundPlane : : createGeometry ( const Frustum & frustum )
{
PROFILE_SCOPE ( GroundPlane_createGeometry ) ;
enum { MAX_WIDTH = 256 , MAX_HEIGHT = 256 } ;
// Project the frustum onto the XY grid.
Point2F min ;
Point2F max ;
projectFrustum ( frustum , mSquareSize , min , max ) ;
// Early out if the grid projection hasn't changed.
if ( mVertexBuffer . isValid ( ) & &
min = = mMin & &
max = = mMax )
return ;
mMin = min ;
mMax = max ;
// Determine the grid extents and allocate the buffers.
// Adjust square size permanently if with the given frustum,
// we end up producing more than a certain limit of geometry.
// This is to prevent this code from causing trouble with
// long viewing distances.
// This only affects the client object, of course, and thus
// has no permanent effect.
U32 width = mCeil ( ( max . x - min . x ) / mSquareSize ) ;
if ( width > MAX_WIDTH )
{
2023-04-27 03:27:35 +00:00
mSquareSize = mCeil ( ( max . x - min . x ) / ( F32 ) MAX_WIDTH ) ;
2012-09-19 15:15:01 +00:00
width = MAX_WIDTH ;
}
else if ( ! width )
width = 1 ;
U32 height = mCeil ( ( max . y - min . y ) / mSquareSize ) ;
if ( height > MAX_HEIGHT )
{
2023-04-27 03:27:35 +00:00
mSquareSize = mCeil ( ( max . y - min . y ) / ( F32 ) MAX_HEIGHT ) ;
2012-09-19 15:15:01 +00:00
height = MAX_HEIGHT ;
}
else if ( ! height )
height = 1 ;
const U32 numVertices = ( width + 1 ) * ( height + 1 ) ;
const U32 numTriangles = width * height * 2 ;
// Only reallocate if the buffers are too small.
if ( mVertexBuffer . isNull ( ) | | numVertices > mVertexBuffer - > mNumVerts )
{
mVertexBuffer . set ( GFX , numVertices , GFXBufferTypeDynamic ) ;
}
if ( mPrimitiveBuffer . isNull ( ) | | numTriangles > mPrimitiveBuffer - > mPrimitiveCount )
{
mPrimitiveBuffer . set ( GFX , numTriangles * 3 , numTriangles , GFXBufferTypeDynamic ) ;
}
// Generate the grid.
generateGrid ( width , height , mSquareSize , min , max , mVertexBuffer , mPrimitiveBuffer ) ;
// Set up GFX primitive.
mPrimitive . type = GFXTriangleList ;
mPrimitive . numPrimitives = numTriangles ;
mPrimitive . numVertices = numVertices ;
}
/// Project the given frustum onto the ground plane and return the XY bounds in world space.
void GroundPlane : : projectFrustum ( const Frustum & frustum , F32 squareSize , Point2F & outMin , Point2F & outMax )
{
// Get the frustum's min and max XY coordinates.
const Box3F bounds = frustum . getBounds ( ) ;
Point2F minPt ( bounds . minExtents . x , bounds . minExtents . y ) ;
Point2F maxPt ( bounds . maxExtents . x , bounds . maxExtents . y ) ;
// Round the min and max coordinates so they align on the grid.
minPt . x - = mFmod ( minPt . x , squareSize ) ;
minPt . y - = mFmod ( minPt . y , squareSize ) ;
F32 maxDeltaX = mFmod ( maxPt . x , squareSize ) ;
F32 maxDeltaY = mFmod ( maxPt . y , squareSize ) ;
if ( maxDeltaX ! = 0.0f )
maxPt . x + = ( squareSize - maxDeltaX ) ;
if ( maxDeltaY ! = 0.0f )
maxPt . y + = ( squareSize - maxDeltaY ) ;
// Add a safezone, so we don't touch the clipping planes.
minPt . x - = squareSize ; minPt . y - = squareSize ;
maxPt . x + = squareSize ; maxPt . y + = squareSize ;
outMin = minPt ;
outMax = maxPt ;
}
/// Generate a triangulated grid spanning the given bounds into the given buffers.
void GroundPlane : : generateGrid ( U32 width , U32 height , F32 squareSize ,
const Point2F & min , const Point2F & max ,
GFXVertexBufferHandle < VertexType > & outVertices ,
GFXPrimitiveBufferHandle & outPrimitives )
{
// Generate the vertices.
VertexType * vertices = outVertices . lock ( ) ;
for ( F32 y = min . y ; y < = max . y ; y + = squareSize )
for ( F32 x = min . x ; x < = max . x ; x + = squareSize )
{
vertices - > point . x = x ;
vertices - > point . y = y ;
vertices - > point . z = 0.0 ;
vertices - > texCoord . x = ( x / squareSize ) * mScaleU ;
vertices - > texCoord . y = ( y / squareSize ) * - mScaleV ;
vertices - > normal . x = 0.0f ;
vertices - > normal . y = 0.0f ;
vertices - > normal . z = 1.0f ;
vertices - > tangent . x = 1.0f ;
vertices - > tangent . y = 0.0f ;
vertices - > tangent . z = 0.0f ;
vertices - > binormal . x = 0.0f ;
vertices - > binormal . y = 1.0f ;
vertices - > binormal . z = 0.0f ;
vertices + + ;
}
outVertices . unlock ( ) ;
// Generate the indices.
U16 * indices ;
outPrimitives . lock ( & indices ) ;
U16 corner1 = 0 ;
U16 corner2 = 1 ;
U16 corner3 = width + 1 ;
U16 corner4 = width + 2 ;
for ( U32 y = 0 ; y < height ; + + y )
{
for ( U32 x = 0 ; x < width ; + + x )
{
indices [ 0 ] = corner3 ;
indices [ 1 ] = corner2 ;
indices [ 2 ] = corner1 ;
indices + = 3 ;
indices [ 0 ] = corner3 ;
indices [ 1 ] = corner4 ;
indices [ 2 ] = corner2 ;
indices + = 3 ;
corner1 + + ;
corner2 + + ;
corner3 + + ;
corner4 + + ;
}
corner1 + + ;
corner2 + + ;
corner3 + + ;
corner4 + + ;
}
outPrimitives . unlock ( ) ;
}
2020-11-02 05:32:34 +00:00
void GroundPlane : : getUtilizedAssets ( Vector < StringTableEntry > * usedAssetsList )
{
2023-10-08 18:19:43 +00:00
U32 assetStatus = MaterialAsset : : getAssetErrCode ( mMaterialAsset ) ;
if ( assetStatus = = AssetBase : : Ok )
{
2020-11-02 05:32:34 +00:00
usedAssetsList - > push_back_unique ( mMaterialAsset - > getAssetId ( ) ) ;
2023-10-08 18:19:43 +00:00
}
2020-11-02 05:32:34 +00:00
}
2012-09-19 15:15:01 +00:00
DefineEngineMethod ( GroundPlane , postApply , void , ( ) , ,
" Intended as a helper to developers and editor scripts. \n "
" Force trigger an inspectPostApply. This will transmit "
" material and other fields to client objects. "
)
{
object - > inspectPostApply ( ) ;
2020-11-02 05:32:34 +00:00
}