groundPlane.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  23. // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  24. // Copyright (C) 2015 Faust Logic, Inc.
  25. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  26. #include "platform/platform.h"
  27. #include "T3D/groundPlane.h"
  28. #include "renderInstance/renderPassManager.h"
  29. #include "scene/sceneRenderState.h"
  30. #include "materials/sceneData.h"
  31. #include "materials/materialDefinition.h"
  32. #include "materials/materialManager.h"
  33. #include "materials/baseMatInstance.h"
  34. #include "math/util/frustum.h"
  35. #include "math/mPlane.h"
  36. #include "console/consoleTypes.h"
  37. #include "console/engineAPI.h"
  38. #include "core/stream/bitStream.h"
  39. #include "collision/boxConvex.h"
  40. #include "collision/abstractPolyList.h"
  41. #include "T3D/physics/physicsPlugin.h"
  42. #include "T3D/physics/physicsBody.h"
  43. #include "T3D/physics/physicsCollision.h"
  44. #ifdef TORQUE_AFX_ENABLED
  45. #include "afx/ce/afxZodiacMgr.h"
  46. #endif
  47. /// Minimum square size allowed. This is a cheap way to limit the amount
  48. /// of geometry possibly generated by the GroundPlane (vertex buffers have a
  49. /// limit, too). Dynamically clipping extents into range is a problem since the
  50. /// location of the horizon depends on the camera orientation. Just shifting
  51. /// squareSize as needed also doesn't work as that causes different geometry to
  52. /// be generated depending on the viewpoint and orientation which affects the
  53. /// texturing.
  54. static const F32 sMIN_SQUARE_SIZE = 16;
  55. IMPLEMENT_CO_NETOBJECT_V1( GroundPlane );
  56. ConsoleDocClass( GroundPlane,
  57. "@brief An infinite plane extending in all direction.\n\n"
  58. "%GroundPlane is useful for setting up simple testing scenes, or it can be "
  59. "placed under an existing scene to keep objects from falling into 'nothing'.\n\n"
  60. "%GroundPlane may not be moved or rotated, it is always at the world origin.\n\n"
  61. "@ingroup Terrain"
  62. );
  63. GroundPlane::GroundPlane()
  64. : mSquareSize( 128.0f ),
  65. mScaleU( 1.0f ),
  66. mScaleV( 1.0f ),
  67. mMaterial( NULL ),
  68. mPhysicsRep( NULL ),
  69. mMin( 0.0f, 0.0f ),
  70. mMax( 0.0f, 0.0f )
  71. {
  72. mTypeMask |= StaticObjectType | StaticShapeObjectType;
  73. mNetFlags.set( Ghostable | ScopeAlways );
  74. mConvexList = new Convex;
  75. mTypeMask |= TerrainLikeObjectType;
  76. mMaterialAsset = StringTable->EmptyString();
  77. mMaterialAssetId = StringTable->EmptyString();
  78. }
  79. GroundPlane::~GroundPlane()
  80. {
  81. if( mMaterial )
  82. SAFE_DELETE( mMaterial );
  83. mConvexList->nukeList();
  84. SAFE_DELETE( mConvexList );
  85. }
  86. void GroundPlane::initPersistFields()
  87. {
  88. addGroup( "Plane" );
  89. addField( "squareSize", TypeF32, Offset( mSquareSize, GroundPlane ), "Square size in meters to which %GroundPlane subdivides its geometry." );
  90. addField( "scaleU", TypeF32, Offset( mScaleU, GroundPlane ), "Scale of texture repeat in the U direction." );
  91. addField( "scaleV", TypeF32, Offset( mScaleV, GroundPlane ), "Scale of texture repeat in the V direction." );
  92. addProtectedField("materialAsset", TypeMaterialAssetId, Offset(mMaterialAssetId, GroundPlane),
  93. &GroundPlane::_setMaterialAsset, &defaultProtectedGetFn,
  94. "The material asset.");
  95. addProtectedField("material", TypeMaterialName, Offset(mMaterialName, GroundPlane),
  96. &GroundPlane::_setMaterialName, &defaultProtectedGetFn,
  97. "The material name.");
  98. endGroup( "Plane" );
  99. Parent::initPersistFields();
  100. removeField( "scale" );
  101. removeField( "position" );
  102. removeField( "rotation" );
  103. }
  104. bool GroundPlane::_setMaterialAsset(void* obj, const char* index, const char* data)
  105. {
  106. GroundPlane* gp = static_cast<GroundPlane*>(obj);// ->setFile(FileName(data));
  107. gp->mMaterialAssetId = StringTable->insert(data);
  108. return gp->setMaterialAsset(gp->mMaterialAssetId);
  109. }
  110. bool GroundPlane::_setMaterialName(void* obj, const char* index, const char* data)
  111. {
  112. GroundPlane* gp = static_cast<GroundPlane*>(obj);// ->setFile(FileName(data));
  113. StringTableEntry assetId = MaterialAsset::getAssetIdByMaterialName(StringTable->insert(data));
  114. if (assetId != StringTable->EmptyString())
  115. {
  116. //Special exception case. If we've defaulted to the 'no shape' mesh, don't save it out, we'll retain the original ids/paths so it doesn't break
  117. //the TSStatic
  118. if (gp->setMaterialAsset(assetId))
  119. {
  120. if (assetId == StringTable->insert("Core_Rendering:NoMaterial"))
  121. {
  122. gp->mMaterialName = data;
  123. gp->mMaterialAssetId = StringTable->EmptyString();
  124. return true;
  125. }
  126. else
  127. {
  128. gp->mMaterialAssetId = assetId;
  129. gp->mMaterialName = StringTable->EmptyString();
  130. return false;
  131. }
  132. }
  133. }
  134. else
  135. {
  136. gp->mMaterialAsset = StringTable->EmptyString();
  137. gp->mMaterialName = data;
  138. }
  139. return true;
  140. }
  141. bool GroundPlane::setMaterialAsset(const StringTableEntry materialAssetId)
  142. {
  143. if (MaterialAsset::getAssetById(materialAssetId, &mMaterialAsset))
  144. {
  145. //Special exception case. If we've defaulted to the 'no shape' mesh, don't save it out, we'll retain the original ids/paths so it doesn't break
  146. //the TSStatic
  147. if (mMaterialAsset.getAssetId() != StringTable->insert("Core_Rendering:noMaterial"))
  148. {
  149. mMaterialName = StringTable->EmptyString();
  150. }
  151. _updateMaterial();
  152. setMaskBits(-1);
  153. return true;
  154. }
  155. return false;
  156. }
  157. bool GroundPlane::onAdd()
  158. {
  159. if( !Parent::onAdd() )
  160. return false;
  161. if( isClientObject() )
  162. _updateMaterial();
  163. if( mSquareSize < sMIN_SQUARE_SIZE )
  164. {
  165. Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE );
  166. mSquareSize = sMIN_SQUARE_SIZE;
  167. }
  168. Parent::setScale( VectorF( 1.0f, 1.0f, 1.0f ) );
  169. Parent::setTransform( MatrixF::Identity );
  170. setGlobalBounds();
  171. resetWorldBox();
  172. addToScene();
  173. if ( PHYSICSMGR )
  174. {
  175. PhysicsCollision *colShape = PHYSICSMGR->createCollision();
  176. colShape->addPlane( PlaneF( Point3F::Zero, Point3F( 0, 0, 1 ) ) );
  177. PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
  178. mPhysicsRep = PHYSICSMGR->createBody();
  179. mPhysicsRep->init( colShape, 0, 0, this, world );
  180. }
  181. return true;
  182. }
  183. void GroundPlane::onRemove()
  184. {
  185. SAFE_DELETE( mPhysicsRep );
  186. removeFromScene();
  187. Parent::onRemove();
  188. }
  189. void GroundPlane::inspectPostApply()
  190. {
  191. Parent::inspectPostApply();
  192. setMaskBits( U32( -1 ) );
  193. if( mSquareSize < sMIN_SQUARE_SIZE )
  194. {
  195. Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE );
  196. mSquareSize = sMIN_SQUARE_SIZE;
  197. }
  198. setScale( VectorF( 1.0f, 1.0f, 1.0f ) );
  199. }
  200. void GroundPlane::setTransform( const MatrixF &mat )
  201. {
  202. // Ignore.
  203. }
  204. void GroundPlane::setScale( const Point3F& scale )
  205. {
  206. // Ignore.
  207. }
  208. U32 GroundPlane::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
  209. {
  210. U32 retMask = Parent::packUpdate( connection, mask, stream );
  211. stream->write( mSquareSize );
  212. stream->write( mScaleU );
  213. stream->write( mScaleV );
  214. stream->writeString( mMaterialAsset.getAssetId() );
  215. stream->write( mMaterialName );
  216. return retMask;
  217. }
  218. void GroundPlane::unpackUpdate( NetConnection* connection, BitStream* stream )
  219. {
  220. Parent::unpackUpdate( connection, stream );
  221. stream->read( &mSquareSize );
  222. stream->read( &mScaleU );
  223. stream->read( &mScaleV );
  224. char buffer[256];
  225. stream->readString(buffer);
  226. setMaterialAsset(StringTable->insert(buffer));
  227. stream->read( &mMaterialName );
  228. // If we're added then something possibly changed in
  229. // the editor... do an update of the material and the
  230. // geometry.
  231. if ( isProperlyAdded() )
  232. {
  233. _updateMaterial();
  234. mVertexBuffer = NULL;
  235. }
  236. }
  237. void GroundPlane::_updateMaterial()
  238. {
  239. if (!mMaterialAsset.isNull())
  240. {
  241. String matName = mMaterialAsset->getMaterialDefinitionName();
  242. mMaterial = MATMGR->createMatInstance(matName, getGFXVertexFormat< VertexType >());
  243. if (!mMaterial)
  244. Con::errorf("GroundPlane::_updateMaterial - no material called '%s'", matName.c_str());
  245. }
  246. }
  247. bool GroundPlane::castRay( const Point3F& start, const Point3F& end, RayInfo* info )
  248. {
  249. PlaneF plane( Point3F( 0.0f, 0.0f, 0.0f ), Point3F( 0.0f, 0.0f, 1.0f ) );
  250. F32 t = plane.intersect( start, end );
  251. if( t >= 0.0 && t <= 1.0 )
  252. {
  253. info->t = t;
  254. info->setContactPoint( start, end );
  255. info->normal.set( 0, 0, 1 );
  256. info->material = mMaterial;
  257. info->object = this;
  258. info->distance = 0;
  259. info->faceDot = 0;
  260. info->texCoord.set( 0, 0 );
  261. return true;
  262. }
  263. return false;
  264. }
  265. void GroundPlane::buildConvex( const Box3F& box, Convex* convex )
  266. {
  267. mConvexList->collectGarbage();
  268. Box3F planeBox = getPlaneBox();
  269. if ( !box.isOverlapped( planeBox ) )
  270. return;
  271. // See if we already have a convex in the working set.
  272. BoxConvex *boxConvex = NULL;
  273. CollisionWorkingList &wl = convex->getWorkingList();
  274. CollisionWorkingList *itr = wl.wLink.mNext;
  275. for ( ; itr != &wl; itr = itr->wLink.mNext )
  276. {
  277. if ( itr->mConvex->getType() == BoxConvexType &&
  278. itr->mConvex->getObject() == this )
  279. {
  280. boxConvex = (BoxConvex*)itr->mConvex;
  281. break;
  282. }
  283. }
  284. if ( !boxConvex )
  285. {
  286. boxConvex = new BoxConvex;
  287. mConvexList->registerObject( boxConvex );
  288. boxConvex->init( this );
  289. convex->addToWorkingList( boxConvex );
  290. }
  291. // Update our convex to best match the queried box
  292. if ( boxConvex )
  293. {
  294. Point3F queryCenter = box.getCenter();
  295. boxConvex->mCenter = Point3F( queryCenter.x, queryCenter.y, -GROUND_PLANE_BOX_HEIGHT_HALF );
  296. boxConvex->mSize = Point3F( box.getExtents().x,
  297. box.getExtents().y,
  298. GROUND_PLANE_BOX_HEIGHT_HALF );
  299. }
  300. }
  301. bool GroundPlane::buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& )
  302. {
  303. polyList->setObject( this );
  304. polyList->setTransform( &MatrixF::Identity, Point3F( 1.0f, 1.0f, 1.0f ) );
  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. // Add vertices to poly list.
  314. U32 v0 = polyList->addPoint(p0);
  315. polyList->addPoint(p1);
  316. polyList->addPoint(p2);
  317. polyList->addPoint(p3);
  318. // Add plane between first three vertices.
  319. polyList->begin(0, 0);
  320. polyList->vertex(v0);
  321. polyList->vertex(v0+1);
  322. polyList->vertex(v0+2);
  323. polyList->plane(v0, v0+1, v0+2);
  324. polyList->end();
  325. // Add plane between last three vertices.
  326. polyList->begin(0, 1);
  327. polyList->vertex(v0+2);
  328. polyList->vertex(v0+3);
  329. polyList->vertex(v0);
  330. polyList->plane(v0+2, v0+3, v0);
  331. polyList->end();
  332. return true;
  333. }
  334. Box3F planeBox = getPlaneBox();
  335. polyList->addBox( planeBox, mMaterial );
  336. return true;
  337. }
  338. void GroundPlane::prepRenderImage( SceneRenderState* state )
  339. {
  340. PROFILE_SCOPE( GroundPlane_prepRenderImage );
  341. // TODO: Should we skip rendering the ground plane into
  342. // the shadows? Its not like you can ever get under it.
  343. if ( !mMaterial )
  344. return;
  345. // If we don't have a material instance after the override then
  346. // we can skip rendering all together.
  347. BaseMatInstance *matInst = state->getOverrideMaterial( mMaterial );
  348. if ( !matInst )
  349. return;
  350. PROFILE_SCOPE( GroundPlane_prepRender );
  351. // Update the geometry.
  352. createGeometry( state->getCullingFrustum() );
  353. if( mVertexBuffer.isNull() )
  354. return;
  355. #ifdef TORQUE_AFX_ENABLED
  356. afxZodiacMgr::renderGroundPlaneZodiacs(state, this);
  357. #endif
  358. // Add a render instance.
  359. RenderPassManager* pass = state->getRenderPass();
  360. MeshRenderInst* ri = pass->allocInst< MeshRenderInst >();
  361. ri->type = RenderPassManager::RIT_Mesh;
  362. ri->vertBuff = &mVertexBuffer;
  363. ri->primBuff = &mPrimitiveBuffer;
  364. ri->prim = &mPrimitive;
  365. ri->matInst = matInst;
  366. ri->objectToWorld = pass->allocUniqueXform( MatrixF::Identity );
  367. ri->worldToCamera = pass->allocSharedXform( RenderPassManager::View );
  368. ri->projection = pass->allocSharedXform( RenderPassManager::Projection );
  369. ri->visibility = 1.0f;
  370. ri->translucentSort = matInst->getMaterial()->isTranslucent();
  371. ri->defaultKey = matInst->getStateHint();
  372. if( ri->translucentSort )
  373. ri->type = RenderPassManager::RIT_Translucent;
  374. // If we need lights then set them up.
  375. if ( matInst->isForwardLit() )
  376. {
  377. LightQuery query;
  378. query.init( getWorldSphere() );
  379. query.getLights( ri->lights, 8 );
  380. }
  381. pass->addInst( ri );
  382. }
  383. /// Generate a subset of the ground plane matching the given frustum.
  384. void GroundPlane::createGeometry( const Frustum& frustum )
  385. {
  386. PROFILE_SCOPE( GroundPlane_createGeometry );
  387. enum { MAX_WIDTH = 256, MAX_HEIGHT = 256 };
  388. // Project the frustum onto the XY grid.
  389. Point2F min;
  390. Point2F max;
  391. projectFrustum( frustum, mSquareSize, min, max );
  392. // Early out if the grid projection hasn't changed.
  393. if( mVertexBuffer.isValid() &&
  394. min == mMin &&
  395. max == mMax )
  396. return;
  397. mMin = min;
  398. mMax = max;
  399. // Determine the grid extents and allocate the buffers.
  400. // Adjust square size permanently if with the given frustum,
  401. // we end up producing more than a certain limit of geometry.
  402. // This is to prevent this code from causing trouble with
  403. // long viewing distances.
  404. // This only affects the client object, of course, and thus
  405. // has no permanent effect.
  406. U32 width = mCeil( ( max.x - min.x ) / mSquareSize );
  407. if( width > MAX_WIDTH )
  408. {
  409. mSquareSize = mCeil( ( max.x - min.x ) / MAX_WIDTH );
  410. width = MAX_WIDTH;
  411. }
  412. else if( !width )
  413. width = 1;
  414. U32 height = mCeil( ( max.y - min.y ) / mSquareSize );
  415. if( height > MAX_HEIGHT )
  416. {
  417. mSquareSize = mCeil( ( max.y - min.y ) / MAX_HEIGHT );
  418. height = MAX_HEIGHT;
  419. }
  420. else if( !height )
  421. height = 1;
  422. const U32 numVertices = ( width + 1 ) * ( height + 1 );
  423. const U32 numTriangles = width * height * 2;
  424. // Only reallocate if the buffers are too small.
  425. if ( mVertexBuffer.isNull() || numVertices > mVertexBuffer->mNumVerts )
  426. {
  427. mVertexBuffer.set( GFX, numVertices, GFXBufferTypeDynamic );
  428. }
  429. if ( mPrimitiveBuffer.isNull() || numTriangles > mPrimitiveBuffer->mPrimitiveCount )
  430. {
  431. mPrimitiveBuffer.set( GFX, numTriangles*3, numTriangles, GFXBufferTypeDynamic );
  432. }
  433. // Generate the grid.
  434. generateGrid( width, height, mSquareSize, min, max, mVertexBuffer, mPrimitiveBuffer );
  435. // Set up GFX primitive.
  436. mPrimitive.type = GFXTriangleList;
  437. mPrimitive.numPrimitives = numTriangles;
  438. mPrimitive.numVertices = numVertices;
  439. }
  440. /// Project the given frustum onto the ground plane and return the XY bounds in world space.
  441. void GroundPlane::projectFrustum( const Frustum& frustum, F32 squareSize, Point2F& outMin, Point2F& outMax )
  442. {
  443. // Get the frustum's min and max XY coordinates.
  444. const Box3F bounds = frustum.getBounds();
  445. Point2F minPt( bounds.minExtents.x, bounds.minExtents.y );
  446. Point2F maxPt( bounds.maxExtents.x, bounds.maxExtents.y );
  447. // Round the min and max coordinates so they align on the grid.
  448. minPt.x -= mFmod( minPt.x, squareSize );
  449. minPt.y -= mFmod( minPt.y, squareSize );
  450. F32 maxDeltaX = mFmod( maxPt.x, squareSize );
  451. F32 maxDeltaY = mFmod( maxPt.y, squareSize );
  452. if( maxDeltaX != 0.0f )
  453. maxPt.x += ( squareSize - maxDeltaX );
  454. if( maxDeltaY != 0.0f )
  455. maxPt.y += ( squareSize - maxDeltaY );
  456. // Add a safezone, so we don't touch the clipping planes.
  457. minPt.x -= squareSize; minPt.y -= squareSize;
  458. maxPt.x += squareSize; maxPt.y += squareSize;
  459. outMin = minPt;
  460. outMax = maxPt;
  461. }
  462. /// Generate a triangulated grid spanning the given bounds into the given buffers.
  463. void GroundPlane::generateGrid( U32 width, U32 height, F32 squareSize,
  464. const Point2F& min, const Point2F& max,
  465. GFXVertexBufferHandle< VertexType >& outVertices,
  466. GFXPrimitiveBufferHandle& outPrimitives )
  467. {
  468. // Generate the vertices.
  469. VertexType* vertices = outVertices.lock();
  470. for( F32 y = min.y; y <= max.y; y += squareSize )
  471. for( F32 x = min.x; x <= max.x; x += squareSize )
  472. {
  473. vertices->point.x = x;
  474. vertices->point.y = y;
  475. vertices->point.z = 0.0;
  476. vertices->texCoord.x = ( x / squareSize ) * mScaleU;
  477. vertices->texCoord.y = ( y / squareSize ) * -mScaleV;
  478. vertices->normal.x = 0.0f;
  479. vertices->normal.y = 0.0f;
  480. vertices->normal.z = 1.0f;
  481. vertices->tangent.x = 1.0f;
  482. vertices->tangent.y = 0.0f;
  483. vertices->tangent.z = 0.0f;
  484. vertices->binormal.x = 0.0f;
  485. vertices->binormal.y = 1.0f;
  486. vertices->binormal.z = 0.0f;
  487. vertices++;
  488. }
  489. outVertices.unlock();
  490. // Generate the indices.
  491. U16* indices;
  492. outPrimitives.lock( &indices );
  493. U16 corner1 = 0;
  494. U16 corner2 = 1;
  495. U16 corner3 = width + 1;
  496. U16 corner4 = width + 2;
  497. for( U32 y = 0; y < height; ++ y )
  498. {
  499. for( U32 x = 0; x < width; ++ x )
  500. {
  501. indices[ 0 ] = corner3;
  502. indices[ 1 ] = corner2;
  503. indices[ 2 ] = corner1;
  504. indices += 3;
  505. indices[ 0 ] = corner3;
  506. indices[ 1 ] = corner4;
  507. indices[ 2 ] = corner2;
  508. indices += 3;
  509. corner1 ++;
  510. corner2 ++;
  511. corner3 ++;
  512. corner4 ++;
  513. }
  514. corner1 ++;
  515. corner2 ++;
  516. corner3 ++;
  517. corner4 ++;
  518. }
  519. outPrimitives.unlock();
  520. }
  521. void GroundPlane::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
  522. {
  523. if (!mMaterialAsset.isNull() && mMaterialAsset->getAssetId() != StringTable->insert("Core_Rendering:noMaterial"))
  524. usedAssetsList->push_back_unique(mMaterialAsset->getAssetId());
  525. }
  526. DefineEngineMethod( GroundPlane, postApply, void, (),,
  527. "Intended as a helper to developers and editor scripts.\n"
  528. "Force trigger an inspectPostApply. This will transmit "
  529. "material and other fields to client objects."
  530. )
  531. {
  532. object->inspectPostApply();
  533. }