Torque3D Documentation / _generateds / blTerrainSystem.cpp

blTerrainSystem.cpp

Engine/source/lighting/basic/blTerrainSystem.cpp

More...

Classes:

Public Variables

Detailed Description

Public Variables

SceneLighting * gLighting 
  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 "lighting/basic/blTerrainSystem.h"
 26
 27#include "core/bitVector.h"
 28#include "lighting/common/shadowVolumeBSP.h"
 29#include "lighting/lightingInterfaces.h"
 30#include "terrain/terrData.h"
 31#include "lighting/basic/basicLightManager.h"
 32#include "lighting/common/sceneLighting.h"
 33#include "gfx/bitmap/gBitmap.h"
 34#include "collision/collision.h"
 35
 36extern SceneLighting* gLighting;
 37
 38
 39struct blTerrainChunk : public PersistInfo::PersistChunk
 40{
 41   typedef PersistInfo::PersistChunk Parent;
 42
 43   blTerrainChunk();
 44   ~blTerrainChunk();
 45
 46   GBitmap *mLightmap;
 47
 48   bool read(Stream &);
 49   bool write(Stream &);
 50};
 51
 52//------------------------------------------------------------------------------
 53// Class SceneLighting::TerrainChunk
 54//------------------------------------------------------------------------------
 55blTerrainChunk::blTerrainChunk()
 56{
 57   mChunkType = PersistChunk::TerrainChunkType;
 58   mLightmap = NULL;
 59}
 60
 61blTerrainChunk::~blTerrainChunk()
 62{
 63   if(mLightmap)
 64      delete mLightmap;
 65}
 66
 67//------------------------------------------------------------------------------
 68
 69bool blTerrainChunk::read(Stream & stream)
 70{
 71   if(!Parent::read(stream))
 72      return(false);
 73
 74   mLightmap = new GBitmap();
 75   return mLightmap->readBitmap("png",stream);
 76}
 77
 78bool blTerrainChunk::write(Stream & stream)
 79{
 80   if(!Parent::write(stream))
 81      return(false);
 82
 83   if(!mLightmap)
 84      return(false);
 85
 86   if(!mLightmap->writeBitmap("png",stream))
 87      return(false);
 88
 89   return(true);
 90}
 91
 92class blTerrainProxy : public SceneLighting::ObjectProxy
 93{
 94protected:
 95
 96   typedef ObjectProxy Parent;
 97
 98   BitVector               mShadowMask;
 99   ShadowVolumeBSP *       mShadowVolume;
100   LinearColorF *                mLightmap;
101
102   /// The dimension of the lightmap in pixels.
103   const U32 mLightMapSize;
104
105   /// The dimension of the terrain height map sample array.
106   const U32 mTerrainBlockSize;
107
108   LinearColorF *sgBakedLightmap;
109   Vector<LightInfo*> sgLights;
110   bool sgMarkStaticShadow(void *terrainproxy, SceneObject *sceneobject, LightInfo *light);
111   //void postLight(bool lastLight);
112
113   void lightVector(LightInfo *);
114
115   struct SquareStackNode
116   {
117      U8          mLevel;
118      U16         mClipFlags;
119      Point2I     mPos;
120   };
121
122   S32 testSquare(const Point3F &, const Point3F &, S32, F32, const Vector<PlaneF> &);
123   bool markObjectShadow(ObjectProxy *);
124   bool sgIsCorrectStaticObjectType(SceneObject *obj);
125
126   inline LinearColorF _getValue( S32 row, S32 column );
127
128public:
129
130   blTerrainProxy(SceneObject * obj);
131   ~blTerrainProxy();
132   TerrainBlock * operator->() {return(static_cast<TerrainBlock*>(static_cast<SceneObject*>(mObj)));}
133   TerrainBlock * getObject() {return(static_cast<TerrainBlock*>(static_cast<SceneObject*>(mObj)));}
134
135   bool getShadowedSquares(const Vector<PlaneF> &, Vector<U16> &);
136
137   // lighting
138   void init();
139   bool preLight(LightInfo *);
140   void light(LightInfo *);
141
142   // persist
143   U32 getResourceCRC();
144   bool setPersistInfo(PersistInfo::PersistChunk *);
145   bool getPersistInfo(PersistInfo::PersistChunk *);
146
147   virtual bool supportsShadowVolume();
148   virtual void getClipPlanes(Vector<PlaneF>& planes);
149   virtual void addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level);
150
151   // events
152   //virtual void processTGELightProcessEvent(U32 curr, U32 max, LightInfo* currlight); 
153   //virtual void processSGObjectProcessEvent(LightInfo* currLight);
154};
155
156//-------------------------------------------------------------------------------
157// Class SceneLighting::TerrainProxy:
158//-------------------------------------------------------------------------------
159blTerrainProxy::blTerrainProxy( SceneObject *obj ) :
160   Parent( obj ),
161   mLightMapSize( getObject()->getLightMapSize() ),
162   mShadowVolume( NULL ),
163   mTerrainBlockSize( getObject()->getBlockSize() ),
164   mLightmap( NULL ),
165   sgBakedLightmap( NULL )
166{
167}
168
169blTerrainProxy::~blTerrainProxy()
170{
171   delete [] mLightmap;
172}
173
174//-------------------------------------------------------------------------------
175void blTerrainProxy::init()
176{
177   mLightmap = new LinearColorF[ mLightMapSize * mLightMapSize ];
178   dMemset(mLightmap, 0, mLightMapSize * mLightMapSize * sizeof(LinearColorF));
179   mShadowMask.setSize( mTerrainBlockSize * mTerrainBlockSize );
180}
181
182bool blTerrainProxy::preLight(LightInfo * light)
183{
184   if(!bool(mObj))
185      return(false);
186
187   if(light->getType() != LightInfo::Vector)
188      return(false);
189
190   mShadowMask.clear();
191   return(true);
192}
193
194inline LinearColorF blTerrainProxy::_getValue( S32 row, S32 column )
195{
196   while( row < 0 )
197      row += mLightMapSize;
198   row = row % mLightMapSize;
199
200   while( column < 0 )
201      column += mLightMapSize;
202   column = column % mLightMapSize;
203
204   U32 offset = row * mLightMapSize + column;
205
206   return mLightmap[offset];
207}
208
209bool blTerrainProxy::markObjectShadow(ObjectProxy * proxy)
210{
211   if (!proxy->supportsShadowVolume())
212      return false;
213
214   // setup the clip planes
215   Vector<PlaneF> clipPlanes;
216   proxy->getClipPlanes(clipPlanes);
217
218   Vector<U16> shadowList;
219   if(!getShadowedSquares(clipPlanes, shadowList))
220      return(false);
221
222   // set the correct bit
223   for(U32 i = 0; i < shadowList.size(); i++)
224      mShadowMask.set(shadowList[i]);
225
226   return(true);
227}
228
229void blTerrainProxy::light(LightInfo * light)
230{
231   // If we don't have terrain or its not a directional
232   // light then skip processing.
233   TerrainBlock * terrain = getObject();
234   if ( !terrain || light->getType() != LightInfo::Vector )
235      return;
236
237   S32 time = Platform::getRealMilliseconds();
238
239   // reset
240   mShadowVolume = new ShadowVolumeBSP;
241
242   // build interior shadow volume
243   for(ObjectProxy ** itr = gLighting->mLitObjects.begin(); itr != gLighting->mLitObjects.end(); itr++)
244   {
245      ObjectProxy* objproxy = *itr;
246      if (markObjectShadow(objproxy))
247         objproxy->addToShadowVolume(mShadowVolume, light, SceneLighting::SHADOW_DETAIL);
248   }
249
250   lightVector(light);
251
252   // set the lightmap...
253   terrain->clearLightMap();
254
255   // Blur...
256   F32 kernel[3][3] = { {1, 2, 1},
257                        {2, 3, 2},
258                        {1, 2, 1} };
259
260   F32 modifier = 1;
261   F32 divisor = 0;
262
263
264   for( U32 i=0; i<3; i++ )
265   {
266      for( U32 j=0; j<3; j++ )
267      {
268         if( i==1 && j==1 )
269         {
270            kernel[i][j] = 1 + kernel[i][j] * modifier;
271         }
272         else
273         {
274            kernel[i][j] = kernel[i][j] * modifier;
275         }
276
277         divisor += kernel[i][j];
278      }
279   }
280
281   for( U32 i=0; i < mLightMapSize; i++ )
282   {
283      for( U32 j=0; j < mLightMapSize; j++ )
284      {
285
286         LinearColorF val;
287         val  = _getValue( i-1, j-1  ) * kernel[0][0];
288         val += _getValue( i-1, j    ) * kernel[0][1];
289         val += _getValue( i-1, j+1  ) * kernel[0][2];
290         val += _getValue(   i, j-1  ) * kernel[1][0];
291         val += _getValue(   i, j    ) * kernel[1][1];
292         val += _getValue(   i, j+1  ) * kernel[1][2];
293         val += _getValue( i+1, j-1  ) * kernel[2][0];
294         val += _getValue( i+1, j    ) * kernel[2][1];
295         val += _getValue( i+1, j+1  ) * kernel[2][2];
296
297         U32 edge = 0;
298
299         if( j == 0 || j == mLightMapSize - 1 )
300            edge++;
301
302         if( i == 0 || i == mLightMapSize - 1 )
303            edge++;
304
305         if( !edge )
306            val = val / divisor;
307         else
308            val = mLightmap[ i * mLightMapSize + j ];
309
310         // clamp values
311         mLightmap[ i * mLightMapSize + j ]= val;
312      }
313   }
314
315   // And stuff it into the texture...
316   GBitmap *terrLightMap = terrain->getLightMap();
317   for(U32 y = 0; y < mLightMapSize; y++)
318   {
319      for(U32 x = 0; x < mLightMapSize; x++)
320      {
321         ColorI color(255, 255, 255, 255);
322         
323         color.red   = mLightmap[x + y * mLightMapSize].red   * 255;
324         color.green = mLightmap[x + y * mLightMapSize].green * 255;
325         color.blue  = mLightmap[x + y * mLightMapSize].blue  * 255;
326
327         terrLightMap->setColor(x, y, color);
328      }
329   }
330
331   /*
332   // This handles matching up the outer edges of the terrain
333   // lightmap when it has neighbors
334   if (!terrain->isTiling())
335   {
336      for (S32 y = 0; y < terrLightMap->getHeight(); y++)
337      {
338         ColorI c;
339         if (terrain->getFile()->mEdgeTerrainFiles[0])
340         {
341            terrLightMap->getColor(terrLightMap->getWidth()-1,y,c);
342            terrLightMap->setColor(0,y,c);
343            terrLightMap->setColor(1,y,c);
344         }
345         else
346         {
347            terrLightMap->getColor(0,y,c);
348            terrLightMap->setColor(terrLightMap->getWidth()-1,y,c);
349            terrLightMap->setColor(terrLightMap->getWidth()-2,y,c);
350         }
351      }
352
353      for (S32 x = 0; x < terrLightMap->getHeight(); x++)
354      {
355         ColorI c;
356         if (terrain->getFile()->mEdgeTerrainFiles[1])
357         {
358            terrLightMap->getColor(x,terrLightMap->getHeight()-1,c);
359            terrLightMap->setColor(x,0,c);
360            terrLightMap->setColor(x,1,c);
361         }
362         else
363         {
364            terrLightMap->getColor(x,0,c);
365            terrLightMap->setColor(x,terrLightMap->getHeight()-1,c);
366            terrLightMap->setColor(x,terrLightMap->getHeight()-2,c);
367         }
368      }
369   }
370   */
371
372   delete mShadowVolume;
373
374   Con::printf("    = terrain lit in %3.3f seconds", (Platform::getRealMilliseconds()-time)/1000.f);
375}
376
377//------------------------------------------------------------------------------
378S32 blTerrainProxy::testSquare(const Point3F & min, const Point3F & max, S32 mask, F32 expand, const Vector<PlaneF> & clipPlanes)
379{
380   expand = 0;
381   S32 retMask = 0;
382   Point3F minPoint, maxPoint;
383   for(S32 i = 0; i < clipPlanes.size(); i++)
384   {
385      if(mask & (1 << i))
386      {
387         if(clipPlanes[i].x > 0)
388         {
389            maxPoint.x = max.x;
390            minPoint.x = min.x;
391         }
392         else
393         {
394            maxPoint.x = min.x;
395            minPoint.x = max.x;
396         }
397         if(clipPlanes[i].y > 0)
398         {
399            maxPoint.y = max.y;
400            minPoint.y = min.y;
401         }
402         else
403         {
404            maxPoint.y = min.y;
405            minPoint.y = max.y;
406         }
407         if(clipPlanes[i].z > 0)
408         {
409            maxPoint.z = max.z;
410            minPoint.z = min.z;
411         }
412         else
413         {
414            maxPoint.z = min.z;
415            minPoint.z = max.z;
416         }
417         F32 maxDot = mDot(maxPoint, clipPlanes[i]);
418         F32 minDot = mDot(minPoint, clipPlanes[i]);
419         F32 planeD = clipPlanes[i].d;
420         if(maxDot <= -(planeD + expand))
421            return(U16(-1));
422         if(minDot <= -planeD)
423            retMask |= (1 << i);
424      }
425   }
426   return(retMask);
427}
428
429bool blTerrainProxy::getShadowedSquares(const Vector<PlaneF> & clipPlanes, Vector<U16> & shadowList)
430{
431   TerrainBlock *terrain = getObject();
432   if ( !terrain )
433      return false;
434
435   // TODO: Fix me for variable terrain sizes!
436   return true;
437
438   /*
439   SquareStackNode stack[TerrainBlock::BlockShift * 4];
440
441   stack[0].mLevel = TerrainBlock::BlockShift;
442   stack[0].mClipFlags = 0xff;
443   stack[0].mPos.set(0,0);
444
445   U32 stackSize = 1;
446
447   Point3F blockPos;
448   terrain->getTransform().getColumn(3, &blockPos);
449   S32 squareSize = terrain->getSquareSize();
450   F32 floatSquareSize = (F32)squareSize;
451
452   bool marked = false;
453
454   // push through all the levels of the quadtree
455   while(stackSize)
456   {
457      SquareStackNode * node = &stack[stackSize - 1];
458
459      S32 clipFlags = node->mClipFlags;
460      Point2I pos = node->mPos;
461      GridSquare * sq = terrain->findSquare(node->mLevel, pos);
462
463      Point3F minPoint, maxPoint;
464      minPoint.set(squareSize * pos.x + blockPos.x,
465         squareSize * pos.y + blockPos.y,
466         fixedToFloat(sq->minHeight));
467      maxPoint.set(minPoint.x + (squareSize << node->mLevel),
468         minPoint.y + (squareSize << node->mLevel),
469         fixedToFloat(sq->maxHeight));
470
471      // test the square against the current level
472      if(clipFlags)
473      {
474         clipFlags = testSquare(minPoint, maxPoint, clipFlags, floatSquareSize, clipPlanes);
475         if(clipFlags == U16(-1))
476         {
477            stackSize--;
478            continue;
479         }
480      }
481
482      // shadowed?
483      if(node->mLevel == 0)
484      {
485         marked = true;
486         shadowList.push_back(pos.x + (pos.y << TerrainBlock::BlockShift));
487         stackSize--;
488         continue;
489      }
490
491      // setup the next level of squares
492      U8 nextLevel = node->mLevel - 1;
493      S32 squareHalfSize = 1 << nextLevel;
494
495      for(U32 i = 0; i < 4; i++)
496      {
497         node[i].mLevel = nextLevel;
498         node[i].mClipFlags = clipFlags;
499      }
500
501      node[3].mPos = pos;
502      node[2].mPos.set(pos.x + squareHalfSize, pos.y);
503      node[1].mPos.set(pos.x, pos.y + squareHalfSize);
504      node[0].mPos.set(pos.x + squareHalfSize, pos.y + squareHalfSize);
505
506      stackSize += 3;
507   }
508
509   return marked;
510   */
511}
512
513void blTerrainProxy::lightVector(LightInfo * light)
514{
515   // Grab our terrain object
516   TerrainBlock* terrain = getObject();
517   if (!terrain)
518      return;
519
520   // Get the direction to the light (the inverse of the direction
521   // the light is pointing)
522   Point3F lightDir = -light->getDirection();
523   lightDir.normalize();
524
525   // Get the ratio between the light map pixel and world space (used below)   
526   F32 lmTerrRatio = (F32)mTerrainBlockSize / (F32) mLightMapSize;
527   lmTerrRatio *= terrain->getSquareSize();
528
529   U32 i = 0;
530   for (U32 y = 0; y < mLightMapSize; y++)
531   {
532      for (U32 x = 0; x < mLightMapSize; x++)
533      {
534         // Get the relative pixel position and scale it
535         // by the ratio between lightmap and world space
536         Point2F pixelPos(x, y);
537         pixelPos *= lmTerrRatio;         
538         
539         // Start with a default normal of straight up
540         Point3F normal(0.0f, 0.0f, 1.0f);
541         
542         // Try to get the actual normal from the terrain.
543         // Note: this won't change the default normal if
544         // it can't find a normal.
545         terrain->getNormal(pixelPos, &normal);
546
547         // The terrain lightmap only contains shadows.
548         F32 shadowed = 0.0f;
549
550         // Get the height at the lightmap pixel's position
551         F32 height = 0.0f;
552         terrain->getHeight(pixelPos, &height);
553
554         // Calculate the 3D position of the pixel
555         Point3F pixelPos3F(pixelPos.x, pixelPos.y, height);
556
557         // Translate that position by the terrain's transform
558         terrain->getTransform().mulP(pixelPos3F);
559
560         // Offset slighting along the normal so that we don't
561         // raycast into ourself
562         pixelPos3F += (normal * 0.1f);
563
564         // Calculate the light's position.
565         // If it is a vector light like the sun (no position
566         // just direction) then translate along that direction
567         // a reasonable distance to get a point sufficiently
568         // far away
569         Point3F lightPos = light->getPosition();
570         if(light->getType() == LightInfo::Vector)
571         {
572            lightPos = 1000.f * lightDir;            
573            lightPos = pixelPos3F + lightPos;
574         }
575
576         // Cast a ray from the world space position of the lightmap pixel to the light source.
577         // If we hit something then we are in shadow. This allows us to be shadowed by anything
578         // that supports a castRay operation.
579         RayInfo info;
580         if(terrain->getContainer()->castRay(pixelPos3F, lightPos, STATIC_COLLISION_TYPEMASK, &info))
581         {
582            // Shadow the pixel.
583            shadowed = 1.0f;
584         }
585
586         // Set the final lightmap color.
587         mLightmap[i++] += LinearColorF::WHITE * mClampF( 1.0f - shadowed, 0.0f, 1.0f );
588      }
589   }
590}
591
592//--------------------------------------------------------------------------
593U32 blTerrainProxy::getResourceCRC()
594{
595   TerrainBlock * terrain = getObject();
596   if(!terrain)
597      return(0);
598   return(terrain->getCRC());
599}
600
601//--------------------------------------------------------------------------
602bool blTerrainProxy::setPersistInfo(PersistInfo::PersistChunk * info)
603{
604   if(!Parent::setPersistInfo(info))
605      return(false);
606
607   blTerrainChunk * chunk = dynamic_cast<blTerrainChunk*>(info);
608   AssertFatal(chunk, "blTerrainProxy::setPersistInfo: invalid info chunk!");
609
610   TerrainBlock * terrain = getObject();
611   if(!terrain || !terrain->getLightMap())
612      return(false);
613
614   terrain->setLightMap( new GBitmap( *chunk->mLightmap) );
615
616   return(true);
617}
618
619bool blTerrainProxy::getPersistInfo(PersistInfo::PersistChunk * info)
620{
621   if(!Parent::getPersistInfo(info))
622      return(false);
623
624   blTerrainChunk * chunk = dynamic_cast<blTerrainChunk*>(info);
625   AssertFatal(chunk, "blTerrainProxy::getPersistInfo: invalid info chunk!");
626
627   TerrainBlock * terrain = getObject();
628   if(!terrain || !terrain->getLightMap())
629      return(false);
630
631   if(chunk->mLightmap) delete chunk->mLightmap;
632
633   chunk->mLightmap = new GBitmap(*terrain->getLightMap());
634
635   return(true);
636}
637
638bool blTerrainProxy::supportsShadowVolume()
639{
640   return false;
641}
642
643void blTerrainProxy::getClipPlanes(Vector<PlaneF>& planes)
644{
645
646}
647
648void blTerrainProxy::addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level)
649{
650
651}
652
653
654
655void blTerrainSystem::init()
656{
657}
658
659U32 blTerrainSystem::addObjectType()
660{
661   return TerrainObjectType;
662}
663
664SceneLighting::ObjectProxy* blTerrainSystem::createObjectProxy(SceneObject* obj, SceneLighting::ObjectProxyList* sceneObjects)
665{
666   if ((obj->getTypeMask() & TerrainObjectType) != 0)
667      return new blTerrainProxy(obj);
668   else
669      return NULL;
670}
671
672PersistInfo::PersistChunk* blTerrainSystem::createPersistChunk(const U32 chunkType)
673{
674   if (chunkType == PersistInfo::PersistChunk::TerrainChunkType)
675      return new blTerrainChunk();
676   else
677      return NULL;
678}
679
680bool blTerrainSystem::createPersistChunkFromProxy(SceneLighting::ObjectProxy* objproxy, PersistInfo::PersistChunk **ret)
681{
682   if (dynamic_cast<blTerrainProxy*>(objproxy) != NULL)
683   {
684      *ret = new blTerrainChunk();
685      return true;
686   } else {
687      return NULL;
688   }
689}
690
691// Given a ray, this will return the color from the lightmap of this object, return true if handled
692bool blTerrainSystem::getColorFromRayInfo(const RayInfo & collision, LinearColorF& result) const
693{
694   TerrainBlock *terrain = dynamic_cast<TerrainBlock *>(collision.object);
695   if (!terrain)
696      return false;
697
698   Point2F uv;
699   F32 terrainlength = (F32)terrain->getBlockSize();
700   Point3F pos = terrain->getPosition();
701   uv.x = (collision.point.x - pos.x) / terrainlength;
702   uv.y = (collision.point.y - pos.y) / terrainlength;
703
704   // similar to x = x & width...
705   uv.x = uv.x - F32(U32(uv.x));
706   uv.y = uv.y - F32(U32(uv.y));
707   const GBitmap* lightmap = terrain->getLightMap();
708   if (!lightmap)
709      return false;
710
711   result = lightmap->sampleTexel(uv.x, uv.y);
712   // terrain lighting is dim - look into this (same thing done in shaders)...
713   result *= 2.0f;
714   return true;
715}
716