terrRender.cpp

Engine/source/terrain/terrRender.cpp

More...

Detailed Description

Public Functions

GFX_ImplementTextureProfile(TerrainLayerTexProfile , GFXTextureProfile::DiffuseMap , GFXTextureProfile::PreserveSize</a>|<a href="/coding/class/classgfxtextureprofile/#classgfxtextureprofile_1a09105f0bf717a1f2ae49ceda21b8e82da3dd353f9e8b45762fccd85bd4e7cf25a">GFXTextureProfile::Dynamic , GFXTextureProfile::NONE )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
 26// Copyright (C) 2015 Faust Logic, Inc.
 27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 28
 29#include "platform/platform.h"
 30#include "terrain/terrRender.h"
 31
 32#include "terrain/terrData.h"
 33#include "terrain/terrCell.h"
 34#include "terrain/terrMaterial.h"
 35#include "terrain/terrCellMaterial.h"
 36#include "materials/shaderData.h"
 37
 38#include "platform/profiler.h"
 39#include "scene/sceneRenderState.h"
 40#include "math/util/frustum.h"
 41#include "renderInstance/renderPassManager.h"
 42#include "renderInstance/renderTerrainMgr.h"
 43
 44#include "lighting/lightInfo.h"
 45#include "lighting/lightManager.h"
 46
 47#include "materials/matInstance.h"
 48#include "materials/materialManager.h"
 49#include "materials/matTextureTarget.h"
 50#include "shaderGen/conditionerFeature.h"
 51
 52#include "gfx/gfxDrawUtil.h"
 53
 54#ifdef TORQUE_AFX_ENABLED
 55#include "afx/arcaneFX.h"
 56#include "afx/ce/afxZodiacMgr.h"
 57#endif
 58
 59#include "gfx/gfxTransformSaver.h"
 60#include "gfx/bitmap/gBitmap.h"
 61#include "gfx/bitmap/ddsFile.h"
 62#include "gfx/bitmap/imageUtils.h"
 63#include "terrain/terrMaterial.h"
 64#include "gfx/gfxDebugEvent.h"
 65#include "gfx/gfxCardProfile.h"
 66#include "core/stream/fileStream.h"
 67
 68
 69bool TerrainBlock::smDebugRender = false;
 70
 71
 72GFX_ImplementTextureProfile( TerrainLayerTexProfile,
 73                            GFXTextureProfile::DiffuseMap, 
 74                            GFXTextureProfile::PreserveSize | 
 75                            GFXTextureProfile::Dynamic,
 76                            GFXTextureProfile::NONE );
 77
 78
 79void TerrainBlock::_onFlushMaterials()
 80{
 81   if ( mCell )
 82      mCell->deleteMaterials();
 83
 84   SAFE_DELETE( mBaseMaterial );
 85}
 86
 87void TerrainBlock::_updateMaterials()
 88{
 89   if (!mFile)
 90      return;
 91
 92   mBaseTextures.setSize( mFile->mMaterials.size() );
 93
 94   mMaxDetailDistance = 0.0f;
 95
 96   for ( U32 i=0; i < mFile->mMaterials.size(); i++ )
 97   {
 98      TerrainMaterial *mat = mFile->mMaterials[i];
 99
100      if (mat->getDiffuseMap().isNotEmpty())
101      {
102         mBaseTextures[i].set(mat->getDiffuseMap(), &GFXStaticTextureSRGBProfile,
103            "TerrainBlock::_updateMaterials() - DiffuseMap");
104      }
105      else
106         mBaseTextures[ i ] = GFXTexHandle();
107
108      // Find the maximum detail distance.
109      if (  mat->getDetailMap().isNotEmpty() &&
110            mat->getDetailDistance() > mMaxDetailDistance )
111         mMaxDetailDistance = mat->getDetailDistance();
112
113      if (  mat->getMacroMap().isNotEmpty() &&
114            mat->getMacroDistance() > mMaxDetailDistance )
115         mMaxDetailDistance = mat->getMacroDistance();
116   }
117
118   Vector<GFXTexHandle> detailTexArray;
119   detailTexArray.setSize(mFile->mMaterials.size());
120   Vector<GFXTexHandle> macroTexArray;
121   macroTexArray.setSize(mFile->mMaterials.size());
122   Vector<GFXTexHandle> normalTexArray;
123   normalTexArray.setSize(mFile->mMaterials.size());
124   Vector<GFXTexHandle> ormTexArray;
125   ormTexArray.setSize(mFile->mMaterials.size());
126
127   for (U32 i = 0; i < mFile->mMaterials.size(); i++)
128   {
129      TerrainMaterial* mat = mFile->mMaterials[i];
130      GFXTextureProfile* profile = &GFXStaticTextureProfile;
131      if (mat->getIsSRGB())
132         profile = &GFXStaticTextureSRGBProfile;
133
134      if (mat->getDetailMap().isNotEmpty())
135         detailTexArray[i] = TEXMGR->createTexture(mat->getDetailMap(), profile);
136      if (mat->getMacroMap().isNotEmpty())
137         macroTexArray[i] = TEXMGR->createTexture(mat->getMacroMap(), profile);
138      if (mat->getNormalMap().isNotEmpty())
139         normalTexArray[i] = TEXMGR->createTexture(mat->getNormalMap(), profile);
140      if (mat->getORMConfigMap().isNotEmpty())
141         ormTexArray[i] = TEXMGR->createTexture(mat->getORMConfigMap(), profile);
142   }
143
144   if (mDetailTextureArray.isNull())
145   {
146      mDetailTextureArray = GFX->createTextureArray();
147   }
148
149   if (mMacroTextureArray.isNull())
150   {
151      mMacroTextureArray = GFX->createTextureArray();
152   }
153
154   if (mNormalTextureArray.isNull())
155   {
156      mNormalTextureArray = GFX->createTextureArray();
157   }
158
159   if (mOrmTextureArray.isNull())
160   {
161      mOrmTextureArray = GFX->createTextureArray();
162   }
163
164   U32 detailTexArraySize = detailTexArray.size();
165   U32 macroTexArraySize = macroTexArray.size();
166   U32 normalTexArraySize = normalTexArray.size();
167   U32 ormTexArraySize = ormTexArray.size();
168#ifdef TORQUE_TOOLS
169   // For performance improvement when adding terrain layers, we always allocate at least 32 textures to the arrays in tool builds
170   detailTexArraySize = mMax(32, detailTexArraySize);
171   macroTexArraySize = mMax(32, macroTexArraySize);
172   normalTexArraySize = mMax(32, normalTexArraySize);
173   ormTexArraySize = mMax(32, ormTexArraySize);
174#endif
175
176   // Format has been explicitly set
177   const U32 detailTexSize = Con::getIntVariable("Terrain::DetailTextureSize");
178   const GFXFormat detailTexFormat = static_cast<GFXFormat>(Con::getIntVariable("Terrain::DetailTextureFormat"));
179   if (detailTexSize != 0)
180   {
181      GFXFormat format = GFXFormatR8G8B8A8;
182      if (detailTexFormat < GFXFormat_COUNT)
183      {
184         format = detailTexFormat;
185      }
186      mDetailTextureArray->set(detailTexSize, detailTexSize, detailTexArraySize, format);
187   }
188
189   const U32 macroTexSize = Con::getIntVariable("Terrain::MacroTextureSize");
190   const GFXFormat macroTexFormat = static_cast<GFXFormat>(Con::getIntVariable("Terrain::MacroTextureFormat"));
191   if (macroTexSize != 0)
192   {
193      GFXFormat format = GFXFormatR8G8B8A8;
194      if (macroTexFormat < GFXFormat_COUNT)
195      {
196         format = macroTexFormat;
197      }
198      mMacroTextureArray->set(macroTexSize, macroTexSize, macroTexArraySize, format);
199   }
200
201   const U32 normalTexSize = Con::getIntVariable("Terrain::NormalTextureSize");
202   const GFXFormat normalTexFormat = static_cast<GFXFormat>(Con::getIntVariable("Terrain::NormalTextureFormat"));
203   if (normalTexSize != 0)
204   {
205      GFXFormat format = GFXFormatR8G8B8A8;
206      if (normalTexFormat < GFXFormat_COUNT)
207      {
208         format = normalTexFormat;
209      }
210      mNormalTextureArray->set(normalTexSize, normalTexSize, normalTexArraySize, format);
211   }
212
213   const U32 ormTexSize = Con::getIntVariable("Terrain::OrmTextureSize");
214   const GFXFormat ormTexFormat = static_cast<GFXFormat>(Con::getIntVariable("Terrain::OrmTextureFormat"));
215   if (ormTexSize != 0)
216   {
217      GFXFormat format = GFXFormatR8G8B8A8;
218      if (ormTexFormat < GFXFormat_COUNT)
219      {
220         format = ormTexFormat;
221      }
222      mOrmTextureArray->set(ormTexSize, ormTexSize, ormTexArraySize, format);
223   }
224
225   if (!mDetailTextureArray->fromTextureArray(detailTexArray, detailTexArraySize))
226   {
227      Con::errorf("TerrainBlock::_updateMaterials - an issue with the diffuse terrain materials was detected. Please ensure they are all of the same size and format!");
228   }
229
230   if (!mMacroTextureArray->fromTextureArray(macroTexArray, macroTexArraySize))
231   {
232      Con::errorf("TerrainBlock::_updateMaterials - an issue with the detail terrain materials was detected. Please ensure they are all of the same size and format!");
233   }
234
235   if (!mNormalTextureArray->fromTextureArray(normalTexArray, normalTexArraySize))
236   {
237      Con::errorf("TerrainBlock::_updateMaterials - an issue with the normal terrain materials was detected. Please ensure they are all of the same size and format!");
238   }
239
240   if (!mOrmTextureArray->fromTextureArray(ormTexArray, ormTexArraySize))
241   {
242      Con::errorf("TerrainBlock::_updateMaterials - an issue with the orm terrain materials was detected. Please ensure they are all of the same size and format!");
243   }
244
245   if ( mCell )
246      mCell->deleteMaterials();
247}
248
249void TerrainBlock::_updateLayerTexture()
250{
251   const U32 layerSize = mFile->mSize;
252   const Vector<U8> &layerMap = mFile->mLayerMap;
253   const U32 pixelCount = layerMap.size();
254
255   if (  mLayerTex.isNull() ||
256         mLayerTex.getWidth() != layerSize ||
257         mLayerTex.getHeight() != layerSize )
258      mLayerTex.set( layerSize, layerSize, GFXFormatB8G8R8A8, &TerrainLayerTexProfile, "" );
259
260   AssertFatal(   mLayerTex.getWidth() == layerSize &&
261                  mLayerTex.getHeight() == layerSize,
262      "TerrainBlock::_updateLayerTexture - The texture size doesn't match the requested size!" );
263
264   // Update the layer texture.
265   GFXLockedRect *lock = mLayerTex.lock();
266
267   for ( U32 i=0; i < pixelCount; i++ )
268   {  
269      lock->bits[0] = layerMap[i];
270
271      if ( i + 1 >= pixelCount )
272         lock->bits[1] = lock->bits[0];
273      else
274         lock->bits[1] = layerMap[i+1];
275
276      if ( i + layerSize >= pixelCount )
277         lock->bits[2] = lock->bits[0];
278      else
279         lock->bits[2] = layerMap[i + layerSize];
280
281      if ( i + layerSize + 1 >= pixelCount )
282         lock->bits[3] = lock->bits[0];
283      else
284         lock->bits[3] = layerMap[i + layerSize + 1];
285
286      lock->bits += 4;
287   }
288
289   mLayerTex.unlock();
290   //mLayerTex->dumpToDisk( "png", "./layerTex.png" );
291}
292
293bool TerrainBlock::_initBaseShader()
294{
295   ShaderData *shaderData = NULL;
296   if ( !Sim::findObject( "TerrainBlendShader", shaderData ) || !shaderData )
297      return false;
298
299   mBaseShader = shaderData->getShader();
300
301   mBaseShaderConsts = mBaseShader->allocConstBuffer();
302   mBaseTexScaleConst = mBaseShader->getShaderConstHandle( "$texScale" );
303   mBaseTexIdConst = mBaseShader->getShaderConstHandle( "$texId" );
304   mBaseLayerSizeConst = mBaseShader->getShaderConstHandle( "$layerSize" );
305
306   mBaseTarget = GFX->allocRenderToTextureTarget();
307
308   GFXStateBlockDesc desc;
309   desc.samplersDefined = true;
310   desc.samplers[0] = GFXSamplerStateDesc::getClampPoint();   
311   desc.samplers[1] = GFXSamplerStateDesc::getWrapLinear();   
312   desc.zDefined = true;
313   desc.zWriteEnable = false;
314   desc.zEnable = false;
315   desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendOne  );
316   desc.cullDefined = true;
317   desc.cullMode = GFXCullNone;
318   desc.colorWriteAlpha = false;
319   mBaseShaderSB = GFX->createStateBlock( desc );
320
321   return true;
322}
323
324void TerrainBlock::_updateBaseTexture(bool writeToCache)
325{
326   if ( !mBaseShader && !_initBaseShader() )
327      return;
328
329   // This can sometimes occur outside a begin/end scene.
330   const bool sceneBegun = GFX->canCurrentlyRender();
331   if ( !sceneBegun )
332      GFX->beginScene();
333
334   GFXDEBUGEVENT_SCOPE( TerrainBlock_UpdateBaseTexture, ColorI::GREEN );
335
336   PROFILE_SCOPE( TerrainBlock_UpdateBaseTexture );
337
338   GFXTransformSaver saver;
339
340   const U32 maxTextureSize = GFX->getCardProfiler()->queryProfile( "maxTextureSize", 1024 );
341
342   U32 baseTexSize = getNextPow2( mBaseTexSize );
343   baseTexSize = getMin( maxTextureSize, baseTexSize );
344   Point2I destSize( baseTexSize, baseTexSize );
345
346   // Setup geometry
347   GFXVertexBufferHandle<GFXVertexPT> vb;
348   {
349      F32 copyOffsetX = 2.0f * GFX->getFillConventionOffset() / (F32)destSize.x;
350      F32 copyOffsetY = 2.0f * GFX->getFillConventionOffset() / (F32)destSize.y;
351
352      GFXVertexPT points[4];
353      points[0].point = Point3F(1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0);
354      points[0].texCoord = Point2F(1.0, 1.0f);
355      points[1].point = Point3F(1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0);
356      points[1].texCoord = Point2F(1.0, 0.0f);
357      points[2].point = Point3F(-1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0);
358      points[2].texCoord = Point2F(0.0, 1.0f);
359      points[3].point = Point3F(-1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0);
360      points[3].texCoord = Point2F(0.0, 0.0f);
361
362      vb.set( GFX, 4, GFXBufferTypeVolatile );
363      GFXVertexPT *ptr = vb.lock();
364      if(ptr)
365      {
366         dMemcpy( ptr, points, sizeof(GFXVertexPT) * 4 );
367         vb.unlock();
368      }
369   }
370
371   GFXTexHandle blendTex;
372
373   // If the base texture is already a valid render target then 
374   // use it to render to else we create one.
375   if (  mBaseTex.isValid() && 
376         mBaseTex->isRenderTarget() &&
377         mBaseTex->getFormat() == GFXFormatR8G8B8A8_SRGB &&
378         mBaseTex->getWidth() == destSize.x &&
379         mBaseTex->getHeight() == destSize.y )
380      blendTex = mBaseTex;
381   else
382      blendTex.set( destSize.x, destSize.y, GFXFormatR8G8B8A8_SRGB, &GFXRenderTargetSRGBProfile, "" );
383
384   GFX->pushActiveRenderTarget();   
385
386   // Set our shader stuff
387   GFX->setShader( mBaseShader );
388   GFX->setShaderConstBuffer( mBaseShaderConsts );
389   GFX->setStateBlock( mBaseShaderSB );
390   GFX->setVertexBuffer( vb );
391
392   mBaseTarget->attachTexture( GFXTextureTarget::Color0, blendTex );
393   GFX->setActiveRenderTarget( mBaseTarget );
394
395   GFX->clear( GFXClearTarget, ColorI(0,0,0,255), 1.0f, 0 );
396
397   GFX->setTexture( 0, mLayerTex );
398   mBaseShaderConsts->setSafe( mBaseLayerSizeConst, (F32)mLayerTex->getWidth() );      
399
400   for ( U32 i=0; i < mBaseTextures.size(); i++ )
401   {
402      GFXTextureObject *tex = mBaseTextures[i];
403      if ( !tex )
404         continue;
405
406      GFX->setTexture( 1, tex );
407
408      F32 baseSize = mFile->mMaterials[i]->getDiffuseSize();
409      F32 scale = 1.0f;
410      if ( !mIsZero( baseSize ) )
411         scale = getWorldBlockSize() / baseSize;
412      
413      // A mistake early in development means that texture
414      // coords are not flipped correctly.  To compensate
415      // we flip the y scale here.
416      mBaseShaderConsts->setSafe( mBaseTexScaleConst, Point2F( scale, -scale ) );
417      mBaseShaderConsts->setSafe( mBaseTexIdConst, (F32)i );
418
419      GFX->drawPrimitive( GFXTriangleStrip, 0, 2 );
420   }
421
422   mBaseTarget->resolve();
423   
424   GFX->setShader( NULL );
425   //GFX->setStateBlock( NULL ); // WHY NOT?
426   GFX->setShaderConstBuffer( NULL );
427   GFX->setVertexBuffer( NULL );
428
429   GFX->popActiveRenderTarget();
430
431   // End it if we begun it... Yeehaw!
432   if ( !sceneBegun )
433      GFX->endScene();
434
435   /// Do we cache this sucker?
436   if (mBaseTexFormat == NONE || !writeToCache)
437   {
438      // We didn't cache the result, so set the base texture
439      // to the render target we updated.  This should be good
440      // for realtime painting cases.
441      mBaseTex = blendTex;
442   }
443   else if (mBaseTexFormat == DDS)
444   {
445      String cachePath = _getBaseTexCacheFileName();
446
447      FileStream fs;
448      if ( fs.open( _getBaseTexCacheFileName(), Torque::FS::File::Write ) )
449      {
450         // Read back the render target, dxt compress it, and write it to disk.
451         GBitmap blendBmp( destSize.x, destSize.y, false, GFXFormatR8G8B8A8 );
452         blendTex.copyToBmp( &blendBmp );
453
454         /*
455         // Test code for dumping uncompressed bitmap to disk.
456         {
457         FileStream fs;
458         if ( fs.open( "./basetex.png", Torque::FS::File::Write ) )
459         {
460         blendBmp.writeBitmap( "png", fs );
461         fs.close();
462         }         
463         }
464         */
465
466         blendBmp.extrudeMipLevels();
467
468         DDSFile *blendDDS = DDSFile::createDDSFileFromGBitmap( &blendBmp );
469         ImageUtil::ddsCompress( blendDDS, GFXFormatBC1 );
470
471         // Write result to file stream
472         blendDDS->write( fs );
473         
474         delete blendDDS;
475      }
476      fs.close();
477   }
478   else
479   {
480      FileStream stream;
481      if (!stream.open(_getBaseTexCacheFileName(), Torque::FS::File::Write))
482      {
483         mBaseTex = blendTex;
484         return;
485      }
486
487      GBitmap bitmap(blendTex->getWidth(), blendTex->getHeight(), false, GFXFormatR8G8B8A8);
488      blendTex->copyToBmp(&bitmap);
489      bitmap.writeBitmap(formatToExtension(mBaseTexFormat), stream);
490   }
491}
492
493void TerrainBlock::_renderBlock( SceneRenderState *state )
494{
495   PROFILE_SCOPE( TerrainBlock_RenderBlock );
496
497   if (!mFile)
498      return;
499
500   // Prevent rendering shadows if feature is disabled
501   if ( !mCastShadows && state->isShadowPass() ) 
502      return;
503     
504   MatrixF worldViewXfm = state->getWorldViewMatrix();
505   worldViewXfm.mul( getRenderTransform() );
506
507   MatrixF worldViewProjXfm = state->getProjectionMatrix();
508   worldViewProjXfm.mul( worldViewXfm );
509
510   const MatrixF &objectXfm = getRenderWorldTransform();
511
512   Point3F objCamPos = state->getDiffuseCameraPosition();
513   objectXfm.mulP( objCamPos );
514
515   // Get the shadow material.
516   if ( !mDefaultMatInst )
517      mDefaultMatInst = TerrainCellMaterial::getShadowMat();
518
519   // Make sure we have a base material.
520   if ( !mBaseMaterial )
521   {
522      mBaseMaterial = new TerrainCellMaterial();
523      mBaseMaterial->init( this, 0, false, false, true );
524   }
525
526   // Did the detail layers change?
527   if ( mDetailsDirty )
528   {   
529      _updateMaterials();
530      mDetailsDirty = false;
531   }
532
533   // If the layer texture has been cleared or is 
534   // dirty then update it.
535   if ( mLayerTex.isNull() || mLayerTexDirty )
536      _updateLayerTexture();
537
538   // If the layer texture is dirty or we lost the base
539   // texture then regenerate it.
540   if ( mLayerTexDirty || mBaseTex.isNull() )
541   {
542      _updateBaseTexture( false );
543      mLayerTexDirty = false;
544   }   
545
546   static Vector<TerrCell*> renderCells;
547   renderCells.clear();
548
549   mCell->cullCells( state,
550                     objCamPos,
551                     &renderCells );
552
553   RenderPassManager *renderPass = state->getRenderPass();
554
555   MatrixF *riObjectToWorldXfm = renderPass->allocUniqueXform( getRenderTransform() );
556
557   const bool isColorDrawPass = state->isDiffusePass() || state->isReflectPass();
558
559   // This is here for shadows mostly... it allows the
560   // proper shadow material to be generated.
561   BaseMatInstance *defaultMatInst = state->getOverrideMaterial( mDefaultMatInst );
562
563   // Only pass and use the light manager if this is not a shadow pass.
564   LightManager *lm = NULL;
565   if ( isColorDrawPass )
566      lm = LIGHTMGR;
567
568#ifdef TORQUE_AFX_ENABLED
569   bool has_zodiacs = afxZodiacMgr::doesBlockContainZodiacs(state, this);
570#endif
571   for ( U32 i=0; i < renderCells.size(); i++ )
572   {
573      TerrCell *cell = renderCells[i];
574
575      // Ok this cell is fit to render.
576      TerrainRenderInst *inst = renderPass->allocInst<TerrainRenderInst>();
577
578      // Setup lights for this cell.
579      if ( lm )
580      {
581         SphereF bounds = cell->getSphereBounds();
582         getRenderTransform().mulP( bounds.center );
583
584         LightQuery query;
585         query.init( bounds );
586         query.getLights( inst->lights, 8 );
587      }
588
589      GFXVertexBufferHandleBase vertBuff;
590      GFXPrimitiveBufferHandle  primBuff;
591
592      cell->getRenderPrimitive( &inst->prim, &vertBuff, &primBuff );
593
594      inst->mat = defaultMatInst;
595      inst->vertBuff = vertBuff.getPointer();
596
597      if ( primBuff.isValid() )
598      {
599         // Use the cell's custom primitive buffer
600         inst->primBuff = primBuff.getPointer();
601      }
602      else
603      {
604         // Use the standard primitive buffer for this cell
605         inst->primBuff = mPrimBuffer.getPointer();
606      }
607
608      inst->objectToWorldXfm = riObjectToWorldXfm;
609
610      // If we're not drawing to the shadow map then we need
611      // to include the normal rendering materials. 
612      if ( isColorDrawPass )
613      {         
614         const SphereF &bounds = cell->getSphereBounds();         
615
616         F32 sqDist = ( bounds.center - objCamPos ).lenSquared();         
617
618         F32 radiusSq = mSquared( ( mMaxDetailDistance + bounds.radius ) * smDetailScale );
619
620         // If this cell is near enough to get detail textures then
621         // use the full detail mapping material.  Else we use the
622         // simple base only material.
623         if ( !state->isReflectPass() && sqDist < radiusSq )
624            inst->cellMat = cell->getMaterial();
625         else if ( state->isReflectPass() )
626            inst->cellMat = mBaseMaterial->getReflectMat();
627         else
628            inst->cellMat = mBaseMaterial;
629      }
630
631      inst->defaultKey = (U32)cell->getMaterials();
632#ifdef TORQUE_AFX_ENABLED
633      if (has_zodiacs)
634         afxZodiacMgr::renderTerrainZodiacs(state, this, cell);
635      // Submit it for rendering.
636#endif
637      renderPass->addInst( inst );
638   }
639
640   // Trigger the debug rendering.
641   if (  state->isDiffusePass() && 
642         !renderCells.empty() && 
643         smDebugRender )
644   {      
645      // Store the render cells for later.
646      mDebugCells = renderCells;
647
648      ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
649      ri->renderDelegate.bind( this, &TerrainBlock::_renderDebug );
650      ri->type = RenderPassManager::RIT_Editor;
651      state->getRenderPass()->addInst( ri );
652   }
653}
654
655void TerrainBlock::_renderDebug( ObjectRenderInst *ri, 
656                                 SceneRenderState *state, 
657                                 BaseMatInstance *overrideMat )
658{
659   GFXTransformSaver saver;
660   GFX->multWorld( getRenderTransform() );
661
662   for ( U32 i=0; i < mDebugCells.size(); i++ )
663      mDebugCells[i]->renderBounds();
664
665   mDebugCells.clear();
666}
667