groundPlane.cpp
Engine/source/T3D/groundPlane.cpp
Public Variables
Minimum square size allowed.
Public Functions
ConsoleDocClass(GroundPlane , "@brief An infinite plane extending in all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">direction.\n\n</a>" "%<a href="/coding/class/classgroundplane/">GroundPlane</a> is useful <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> setting up simple testing scenes, or it can be " "placed under an existing scene <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> keep objects from falling into 'nothing'.\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "%<a href="/coding/class/classgroundplane/">GroundPlane</a> may not be moved or rotated, it is always at the world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">origin.\n\n</a>" " @ingroup Terrain" )
DefineEngineMethod(GroundPlane , postApply , void , () , "Intended as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> helper <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> developers and editor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts.\n</a>" "Force trigger an inspectPostApply. This will transmit " "material and other fields <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> client objects." )
Detailed Description
Public Variables
const F32 sMIN_SQUARE_SIZE
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.
Public Functions
ConsoleDocClass(GroundPlane , "@brief An infinite plane extending in all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">direction.\n\n</a>" "%<a href="/coding/class/classgroundplane/">GroundPlane</a> is useful <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> setting up simple testing scenes, or it can be " "placed under an existing scene <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> keep objects from falling into 'nothing'.\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "%<a href="/coding/class/classgroundplane/">GroundPlane</a> may not be moved or rotated, it is always at the world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">origin.\n\n</a>" " @ingroup Terrain" )
DefineEngineMethod(GroundPlane , postApply , void , () , "Intended as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> helper <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> developers and editor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts.\n</a>" "Force trigger an inspectPostApply. This will transmit " "material and other fields <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> client objects." )
IMPLEMENT_CO_NETOBJECT_V1(GroundPlane )
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 "T3D/groundPlane.h" 31 32#include "renderInstance/renderPassManager.h" 33#include "scene/sceneRenderState.h" 34#include "materials/sceneData.h" 35#include "materials/materialDefinition.h" 36#include "materials/materialManager.h" 37#include "materials/baseMatInstance.h" 38#include "math/util/frustum.h" 39#include "math/mPlane.h" 40#include "console/consoleTypes.h" 41#include "console/engineAPI.h" 42#include "core/stream/bitStream.h" 43#include "collision/boxConvex.h" 44#include "collision/abstractPolyList.h" 45#include "T3D/physics/physicsPlugin.h" 46#include "T3D/physics/physicsBody.h" 47#include "T3D/physics/physicsCollision.h" 48#ifdef TORQUE_AFX_ENABLED 49#include "afx/ce/afxZodiacMgr.h" 50#endif 51 52/// Minimum square size allowed. This is a cheap way to limit the amount 53/// of geometry possibly generated by the GroundPlane (vertex buffers have a 54/// limit, too). Dynamically clipping extents into range is a problem since the 55/// location of the horizon depends on the camera orientation. Just shifting 56/// squareSize as needed also doesn't work as that causes different geometry to 57/// be generated depending on the viewpoint and orientation which affects the 58/// texturing. 59static const F32 sMIN_SQUARE_SIZE = 16; 60 61 62IMPLEMENT_CO_NETOBJECT_V1( GroundPlane ); 63 64ConsoleDocClass( GroundPlane, 65 "@brief An infinite plane extending in all direction.\n\n" 66 67 "%GroundPlane is useful for setting up simple testing scenes, or it can be " 68 "placed under an existing scene to keep objects from falling into 'nothing'.\n\n" 69 70 "%GroundPlane may not be moved or rotated, it is always at the world origin.\n\n" 71 72 "@ingroup Terrain" 73); 74 75GroundPlane::GroundPlane() 76 : mSquareSize( 128.0f ), 77 mScaleU( 1.0f ), 78 mScaleV( 1.0f ), 79 mMaterial( NULL ), 80 mPhysicsRep( NULL ), 81 mMin( 0.0f, 0.0f ), 82 mMax( 0.0f, 0.0f ) 83{ 84 mTypeMask |= StaticObjectType | StaticShapeObjectType; 85 mNetFlags.set( Ghostable | ScopeAlways ); 86 87 mConvexList = new Convex; 88 mTypeMask |= TerrainLikeObjectType; 89 90 initMaterialAsset(Material); 91} 92 93GroundPlane::~GroundPlane() 94{ 95 if( mMaterial ) 96 SAFE_DELETE( mMaterial ); 97 98 mConvexList->nukeList(); 99 SAFE_DELETE( mConvexList ); 100} 101 102void GroundPlane::initPersistFields() 103{ 104 addGroup( "Plane" ); 105 106 addField( "squareSize", TypeF32, Offset( mSquareSize, GroundPlane ), "Square size in meters to which %GroundPlane subdivides its geometry." ); 107 addField( "scaleU", TypeF32, Offset( mScaleU, GroundPlane ), "Scale of texture repeat in the U direction." ); 108 addField( "scaleV", TypeF32, Offset( mScaleV, GroundPlane ), "Scale of texture repeat in the V direction." ); 109 110 scriptBindMaterialAsset(Material, GroundPlane, "The material used to render the ground plane."); 111 112 endGroup( "Plane" ); 113 114 Parent::initPersistFields(); 115 116 removeField( "scale" ); 117 removeField( "position" ); 118 removeField( "rotation" ); 119} 120 121bool GroundPlane::onAdd() 122{ 123 if( !Parent::onAdd() ) 124 return false; 125 126 if( isClientObject() ) 127 _updateMaterial(); 128 129 if( mSquareSize < sMIN_SQUARE_SIZE ) 130 { 131 Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE ); 132 mSquareSize = sMIN_SQUARE_SIZE; 133 } 134 135 Parent::setScale( VectorF( 1.0f, 1.0f, 1.0f ) ); 136 Parent::setTransform( MatrixF::Identity ); 137 setGlobalBounds(); 138 resetWorldBox(); 139 140 addToScene(); 141 142 if ( PHYSICSMGR ) 143 { 144 PhysicsCollision *colShape = PHYSICSMGR->createCollision(); 145 colShape->addPlane( PlaneF( Point3F::Zero, Point3F( 0, 0, 1 ) ) ); 146 147 PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); 148 mPhysicsRep = PHYSICSMGR->createBody(); 149 mPhysicsRep->init( colShape, 0, 0, this, world ); 150 } 151 152 return true; 153} 154 155void GroundPlane::onRemove() 156{ 157 SAFE_DELETE( mPhysicsRep ); 158 159 removeFromScene(); 160 Parent::onRemove(); 161} 162 163void GroundPlane::inspectPostApply() 164{ 165 Parent::inspectPostApply(); 166 setMaskBits( U32( -1 ) ); 167 168 if( mSquareSize < sMIN_SQUARE_SIZE ) 169 { 170 Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE ); 171 mSquareSize = sMIN_SQUARE_SIZE; 172 } 173 174 setScale( VectorF( 1.0f, 1.0f, 1.0f ) ); 175} 176 177void GroundPlane::setTransform( const MatrixF &mat ) 178{ 179 // Ignore. 180} 181 182void GroundPlane::setScale( const Point3F& scale ) 183{ 184 // Ignore. 185} 186 187U32 GroundPlane::packUpdate( NetConnection* connection, U32 mask, BitStream* stream ) 188{ 189 U32 retMask = Parent::packUpdate( connection, mask, stream ); 190 191 stream->write( mSquareSize ); 192 stream->write( mScaleU ); 193 stream->write( mScaleV ); 194 195 packMaterialAsset(connection, Material); 196 197 return retMask; 198} 199 200void GroundPlane::unpackUpdate( NetConnection* connection, BitStream* stream ) 201{ 202 Parent::unpackUpdate( connection, stream ); 203 204 stream->read( &mSquareSize ); 205 stream->read( &mScaleU ); 206 stream->read( &mScaleV ); 207 208 unpackMaterialAsset(connection, Material); 209 210 // If we're added then something possibly changed in 211 // the editor... do an update of the material and the 212 // geometry. 213 if ( isProperlyAdded() ) 214 { 215 _updateMaterial(); 216 mVertexBuffer = NULL; 217 } 218} 219 220void GroundPlane::_updateMaterial() 221{ 222 if (mMaterialAsset.notNull()) 223 { 224 if (mMaterial && String(mMaterialAsset->getMaterialDefinitionName()).equal(mMaterial->getMaterial()->getName(), String::NoCase)) 225 return; 226 227 SAFE_DELETE(mMaterial); 228 229 mMaterial = MATMGR->createMatInstance(mMaterialAsset->getMaterialDefinitionName(), getGFXVertexFormat< VertexType >()); 230 231 if (!mMaterial) 232 Con::errorf("GroundPlane::_updateMaterial - no Material called '%s'", mMaterialAsset->getMaterialDefinitionName()); 233 } 234} 235 236bool GroundPlane::castRay( const Point3F& start, const Point3F& end, RayInfo* info ) 237{ 238 PlaneF plane( Point3F( 0.0f, 0.0f, 0.0f ), Point3F( 0.0f, 0.0f, 1.0f ) ); 239 240 F32 t = plane.intersect( start, end ); 241 if( t >= 0.0 && t <= 1.0 ) 242 { 243 info->t = t; 244 info->setContactPoint( start, end ); 245 info->normal.set( 0, 0, 1 ); 246 info->material = mMaterial; 247 info->object = this; 248 info->distance = 0; 249 info->faceDot = 0; 250 info->texCoord.set( 0, 0 ); 251 return true; 252 } 253 254 return false; 255} 256 257void GroundPlane::buildConvex( const Box3F& box, Convex* convex ) 258{ 259 mConvexList->collectGarbage(); 260 261 Box3F planeBox = getPlaneBox(); 262 if ( !box.isOverlapped( planeBox ) ) 263 return; 264 265 // See if we already have a convex in the working set. 266 BoxConvex *boxConvex = NULL; 267 CollisionWorkingList &wl = convex->getWorkingList(); 268 CollisionWorkingList *itr = wl.wLink.mNext; 269 for ( ; itr != &wl; itr = itr->wLink.mNext ) 270 { 271 if ( itr->mConvex->getType() == BoxConvexType && 272 itr->mConvex->getObject() == this ) 273 { 274 boxConvex = (BoxConvex*)itr->mConvex; 275 break; 276 } 277 } 278 279 if ( !boxConvex ) 280 { 281 boxConvex = new BoxConvex; 282 mConvexList->registerObject( boxConvex ); 283 boxConvex->init( this ); 284 285 convex->addToWorkingList( boxConvex ); 286 } 287 288 // Update our convex to best match the queried box 289 if ( boxConvex ) 290 { 291 Point3F queryCenter = box.getCenter(); 292 293 boxConvex->mCenter = Point3F( queryCenter.x, queryCenter.y, -GROUND_PLANE_BOX_HEIGHT_HALF ); 294 boxConvex->mSize = Point3F( box.getExtents().x, 295 box.getExtents().y, 296 GROUND_PLANE_BOX_HEIGHT_HALF ); 297 } 298} 299 300bool GroundPlane::buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& ) 301{ 302 polyList->setObject( this ); 303 polyList->setTransform( &MatrixF::Identity, Point3F( 1.0f, 1.0f, 1.0f ) ); 304 305 if(context == PLC_Navigation) 306 { 307 F32 z = getPosition().z; 308 Point3F 309 p0(box.minExtents.x, box.maxExtents.y, z), 310 p1(box.maxExtents.x, box.maxExtents.y, z), 311 p2(box.maxExtents.x, box.minExtents.y, z), 312 p3(box.minExtents.x, box.minExtents.y, z); 313 314 // Add vertices to poly list. 315 U32 v0 = polyList->addPoint(p0); 316 polyList->addPoint(p1); 317 polyList->addPoint(p2); 318 polyList->addPoint(p3); 319 320 // Add plane between first three vertices. 321 polyList->begin(0, 0); 322 polyList->vertex(v0); 323 polyList->vertex(v0+1); 324 polyList->vertex(v0+2); 325 polyList->plane(v0, v0+1, v0+2); 326 polyList->end(); 327 328 // Add plane between last three vertices. 329 polyList->begin(0, 1); 330 polyList->vertex(v0+2); 331 polyList->vertex(v0+3); 332 polyList->vertex(v0); 333 polyList->plane(v0+2, v0+3, v0); 334 polyList->end(); 335 336 return true; 337 } 338 339 Box3F planeBox = getPlaneBox(); 340 polyList->addBox( planeBox, mMaterial ); 341 342 return true; 343} 344 345void GroundPlane::prepRenderImage( SceneRenderState* state ) 346{ 347 PROFILE_SCOPE( GroundPlane_prepRenderImage ); 348 349 // TODO: Should we skip rendering the ground plane into 350 // the shadows? Its not like you can ever get under it. 351 352 if ( !mMaterial ) 353 return; 354 355 // If we don't have a material instance after the override then 356 // we can skip rendering all together. 357 BaseMatInstance *matInst = state->getOverrideMaterial( mMaterial ); 358 if ( !matInst ) 359 return; 360 361 PROFILE_SCOPE( GroundPlane_prepRender ); 362 363 // Update the geometry. 364 createGeometry( state->getCullingFrustum() ); 365 if( mVertexBuffer.isNull() ) 366 return; 367#ifdef TORQUE_AFX_ENABLED 368 afxZodiacMgr::renderGroundPlaneZodiacs(state, this); 369#endif 370 // Add a render instance. 371 372 RenderPassManager* pass = state->getRenderPass(); 373 MeshRenderInst* ri = pass->allocInst< MeshRenderInst >(); 374 375 ri->type = RenderPassManager::RIT_Mesh; 376 ri->vertBuff = &mVertexBuffer; 377 ri->primBuff = &mPrimitiveBuffer; 378 ri->prim = &mPrimitive; 379 ri->matInst = matInst; 380 ri->objectToWorld = pass->allocUniqueXform( MatrixF::Identity ); 381 ri->worldToCamera = pass->allocSharedXform( RenderPassManager::View ); 382 ri->projection = pass->allocSharedXform( RenderPassManager::Projection ); 383 ri->visibility = 1.0f; 384 ri->translucentSort = matInst->getMaterial()->isTranslucent(); 385 ri->defaultKey = matInst->getStateHint(); 386 387 if( ri->translucentSort ) 388 ri->type = RenderPassManager::RIT_Translucent; 389 390 // If we need lights then set them up. 391 if ( matInst->isForwardLit() ) 392 { 393 LightQuery query; 394 query.init( getWorldSphere() ); 395 query.getLights( ri->lights, 8 ); 396 } 397 398 pass->addInst( ri ); 399} 400 401/// Generate a subset of the ground plane matching the given frustum. 402 403void GroundPlane::createGeometry( const Frustum& frustum ) 404{ 405 PROFILE_SCOPE( GroundPlane_createGeometry ); 406 407 enum { MAX_WIDTH = 256, MAX_HEIGHT = 256 }; 408 409 // Project the frustum onto the XY grid. 410 411 Point2F min; 412 Point2F max; 413 414 projectFrustum( frustum, mSquareSize, min, max ); 415 416 // Early out if the grid projection hasn't changed. 417 418 if( mVertexBuffer.isValid() && 419 min == mMin && 420 max == mMax ) 421 return; 422 423 mMin = min; 424 mMax = max; 425 426 // Determine the grid extents and allocate the buffers. 427 // Adjust square size permanently if with the given frustum, 428 // we end up producing more than a certain limit of geometry. 429 // This is to prevent this code from causing trouble with 430 // long viewing distances. 431 // This only affects the client object, of course, and thus 432 // has no permanent effect. 433 434 U32 width = mCeil( ( max.x - min.x ) / mSquareSize ); 435 if( width > MAX_WIDTH ) 436 { 437 mSquareSize = mCeil( ( max.x - min.x ) / MAX_WIDTH ); 438 width = MAX_WIDTH; 439 } 440 else if( !width ) 441 width = 1; 442 443 U32 height = mCeil( ( max.y - min.y ) / mSquareSize ); 444 if( height > MAX_HEIGHT ) 445 { 446 mSquareSize = mCeil( ( max.y - min.y ) / MAX_HEIGHT ); 447 height = MAX_HEIGHT; 448 } 449 else if( !height ) 450 height = 1; 451 452 const U32 numVertices = ( width + 1 ) * ( height + 1 ); 453 const U32 numTriangles = width * height * 2; 454 455 // Only reallocate if the buffers are too small. 456 if ( mVertexBuffer.isNull() || numVertices > mVertexBuffer->mNumVerts ) 457 { 458 mVertexBuffer.set( GFX, numVertices, GFXBufferTypeDynamic ); 459 } 460 if ( mPrimitiveBuffer.isNull() || numTriangles > mPrimitiveBuffer->mPrimitiveCount ) 461 { 462 mPrimitiveBuffer.set( GFX, numTriangles*3, numTriangles, GFXBufferTypeDynamic ); 463 } 464 465 // Generate the grid. 466 467 generateGrid( width, height, mSquareSize, min, max, mVertexBuffer, mPrimitiveBuffer ); 468 469 // Set up GFX primitive. 470 471 mPrimitive.type = GFXTriangleList; 472 mPrimitive.numPrimitives = numTriangles; 473 mPrimitive.numVertices = numVertices; 474} 475 476/// Project the given frustum onto the ground plane and return the XY bounds in world space. 477 478void GroundPlane::projectFrustum( const Frustum& frustum, F32 squareSize, Point2F& outMin, Point2F& outMax ) 479{ 480 // Get the frustum's min and max XY coordinates. 481 482 const Box3F bounds = frustum.getBounds(); 483 484 Point2F minPt( bounds.minExtents.x, bounds.minExtents.y ); 485 Point2F maxPt( bounds.maxExtents.x, bounds.maxExtents.y ); 486 487 // Round the min and max coordinates so they align on the grid. 488 489 minPt.x -= mFmod( minPt.x, squareSize ); 490 minPt.y -= mFmod( minPt.y, squareSize ); 491 492 F32 maxDeltaX = mFmod( maxPt.x, squareSize ); 493 F32 maxDeltaY = mFmod( maxPt.y, squareSize ); 494 495 if( maxDeltaX != 0.0f ) 496 maxPt.x += ( squareSize - maxDeltaX ); 497 if( maxDeltaY != 0.0f ) 498 maxPt.y += ( squareSize - maxDeltaY ); 499 500 // Add a safezone, so we don't touch the clipping planes. 501 502 minPt.x -= squareSize; minPt.y -= squareSize; 503 maxPt.x += squareSize; maxPt.y += squareSize; 504 505 outMin = minPt; 506 outMax = maxPt; 507} 508 509/// Generate a triangulated grid spanning the given bounds into the given buffers. 510 511void GroundPlane::generateGrid( U32 width, U32 height, F32 squareSize, 512 const Point2F& min, const Point2F& max, 513 GFXVertexBufferHandle< VertexType>& outVertices, 514 GFXPrimitiveBufferHandle& outPrimitives ) 515{ 516 // Generate the vertices. 517 518 VertexType* vertices = outVertices.lock(); 519 for( F32 y = min.y; y <= max.y; y += squareSize ) 520 for( F32 x = min.x; x <= max.x; x += squareSize ) 521 { 522 vertices->point.x = x; 523 vertices->point.y = y; 524 vertices->point.z = 0.0; 525 526 vertices->texCoord.x = ( x / squareSize ) * mScaleU; 527 vertices->texCoord.y = ( y / squareSize ) * -mScaleV; 528 529 vertices->normal.x = 0.0f; 530 vertices->normal.y = 0.0f; 531 vertices->normal.z = 1.0f; 532 533 vertices->tangent.x = 1.0f; 534 vertices->tangent.y = 0.0f; 535 vertices->tangent.z = 0.0f; 536 537 vertices->binormal.x = 0.0f; 538 vertices->binormal.y = 1.0f; 539 vertices->binormal.z = 0.0f; 540 541 vertices++; 542 } 543 outVertices.unlock(); 544 545 // Generate the indices. 546 547 U16* indices; 548 outPrimitives.lock( &indices ); 549 550 U16 corner1 = 0; 551 U16 corner2 = 1; 552 U16 corner3 = width + 1; 553 U16 corner4 = width + 2; 554 555 for( U32 y = 0; y < height; ++ y ) 556 { 557 for( U32 x = 0; x < width; ++ x ) 558 { 559 indices[ 0 ] = corner3; 560 indices[ 1 ] = corner2; 561 indices[ 2 ] = corner1; 562 563 indices += 3; 564 565 indices[ 0 ] = corner3; 566 indices[ 1 ] = corner4; 567 indices[ 2 ] = corner2; 568 569 indices += 3; 570 571 corner1 ++; 572 corner2 ++; 573 corner3 ++; 574 corner4 ++; 575 } 576 577 corner1 ++; 578 corner2 ++; 579 corner3 ++; 580 corner4 ++; 581 } 582 583 outPrimitives.unlock(); 584} 585 586void GroundPlane::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList) 587{ 588 if (!mMaterialAsset.isNull() && mMaterialAsset->getAssetId() != StringTable->insert("Core_Rendering:noMaterial")) 589 usedAssetsList->push_back_unique(mMaterialAsset->getAssetId()); 590 591} 592 593DefineEngineMethod( GroundPlane, postApply, void, (),, 594 "Intended as a helper to developers and editor scripts.\n" 595 "Force trigger an inspectPostApply. This will transmit " 596 "material and other fields to client objects." 597 ) 598{ 599 object->inspectPostApply(); 600} 601