Browse Source

Implementation of reflection and skylight probes.
Moves lighting math to the diffuse/specular two-channel logic.

Areloch 6 years ago
parent
commit
b19a4b22c8
100 changed files with 12216 additions and 1822 deletions
  1. 573 0
      Engine/source/T3D/lighting/IBLUtilities.cpp
  2. 30 0
      Engine/source/T3D/lighting/IBLUtilities.h
  3. 1285 0
      Engine/source/T3D/lighting/reflectionProbe.cpp
  4. 278 0
      Engine/source/T3D/lighting/reflectionProbe.h
  5. 899 0
      Engine/source/T3D/lighting/skylight.cpp
  6. 212 0
      Engine/source/T3D/lighting/skylight.h
  7. 0 1
      Engine/source/gfx/gfxEnums.h
  8. 23 2
      Engine/source/lighting/advanced/advancedLightBinManager.cpp
  9. 4 0
      Engine/source/lighting/advanced/advancedLightBinManager.h
  10. 2 0
      Engine/source/lighting/advanced/advancedLightingFeatures.cpp
  11. 3 3
      Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.cpp
  12. 2 2
      Engine/source/lighting/advanced/hlsl/advancedLightingFeaturesHLSL.cpp
  13. 2 0
      Engine/source/lighting/basic/basicLightManager.cpp
  14. 809 0
      Engine/source/lighting/probeManager.cpp
  15. 334 0
      Engine/source/lighting/probeManager.h
  16. 2 0
      Engine/source/materials/materialFeatureTypes.cpp
  17. 2 0
      Engine/source/materials/materialFeatureTypes.h
  18. 5 0
      Engine/source/materials/processedCustomMaterial.cpp
  19. 21 9
      Engine/source/materials/processedShaderMaterial.cpp
  20. 1 2
      Engine/source/renderInstance/renderBinManager.h
  21. 9 3
      Engine/source/renderInstance/renderDeferredMgr.cpp
  22. 3 0
      Engine/source/renderInstance/renderDeferredMgr.h
  23. 6 0
      Engine/source/renderInstance/renderPassManager.cpp
  24. 95 0
      Engine/source/renderInstance/renderPassManager.h
  25. 956 0
      Engine/source/renderInstance/renderProbeMgr.cpp
  26. 213 0
      Engine/source/renderInstance/renderProbeMgr.h
  27. 25 0
      Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h
  28. 222 0
      Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp
  29. 26 0
      Engine/source/shaderGen/HLSL/shaderFeatureHLSL.h
  30. 1 0
      Engine/source/shaderGen/HLSL/shaderGenHLSLInit.cpp
  31. 9 0
      Engine/source/shaderGen/shaderGenVars.cpp
  32. 9 0
      Engine/source/shaderGen/shaderGenVars.h
  33. 197 8
      Engine/source/terrain/glsl/terrFeatureGLSL.cpp
  34. 19 1
      Engine/source/terrain/glsl/terrFeatureGLSL.h
  35. 196 0
      Engine/source/terrain/hlsl/terrFeatureHLSL.cpp
  36. 17 0
      Engine/source/terrain/hlsl/terrFeatureHLSL.h
  37. 48 3
      Engine/source/terrain/terrCellMaterial.cpp
  38. 3 0
      Engine/source/terrain/terrCellMaterial.h
  39. 1 0
      Engine/source/terrain/terrFeatureTypes.cpp
  40. 1 0
      Engine/source/terrain/terrFeatureTypes.h
  41. 1 0
      Engine/source/terrain/terrMaterial.cpp
  42. 5 0
      Engine/source/terrain/terrMaterial.h
  43. 3 1
      Templates/Full/game/art/shapes/Cheetah/materials.cs
  44. 12 12
      Templates/Full/game/art/shapes/actors/Soldier/materials.cs
  45. 6 3
      Templates/Full/game/art/shapes/cube/materials.cs
  46. 17 6
      Templates/Full/game/art/shapes/station/materials.cs
  47. 1 0
      Templates/Full/game/art/terrains/materials.cs
  48. 8 2
      Templates/Full/game/core/art/grids/materials.cs
  49. 40 7
      Templates/Full/game/core/scripts/client/lighting/advanced/deferredShading.cs
  50. 5 5
      Templates/Full/game/core/scripts/client/lighting/advanced/lightViz.cs
  51. 124 7
      Templates/Full/game/core/scripts/client/lighting/advanced/shaders.cs
  52. 1 1
      Templates/Full/game/core/scripts/client/lighting/basic/init.cs
  53. 2 1
      Templates/Full/game/core/scripts/client/renderManager.cs
  54. 66 43
      Templates/Full/game/levels/AProbeTest.mis
  55. 1500 43
      Templates/Full/game/levels/Empty Room.mis
  56. 64 34
      Templates/Full/game/levels/Empty Terrain.mis
  57. 147 124
      Templates/Full/game/shaders/common/gl/lighting.glsl
  58. 16 0
      Templates/Full/game/shaders/common/gl/torque.glsl
  59. 38 1
      Templates/Full/game/shaders/common/lighting.hlsl
  60. 138 0
      Templates/Full/game/shaders/common/lighting/advanced/brdfLookupP.hlsl
  61. 37 0
      Templates/Full/game/shaders/common/lighting/advanced/cubemapV.hlsl
  62. 3 3
      Templates/Full/game/shaders/common/lighting/advanced/dbgLightMapVisualizeP.hlsl
  63. 15 13
      Templates/Full/game/shaders/common/lighting/advanced/deferredShadingP.hlsl
  64. 31 0
      Templates/Full/game/shaders/common/lighting/advanced/gl/cubemapV.glsl
  65. 3 3
      Templates/Full/game/shaders/common/lighting/advanced/gl/dbgLightMapVisualizeP.glsl
  66. 6 2
      Templates/Full/game/shaders/common/lighting/advanced/gl/deferredClearGBufferP.glsl
  67. 23 15
      Templates/Full/game/shaders/common/lighting/advanced/gl/deferredShadingP.glsl
  68. 60 0
      Templates/Full/game/shaders/common/lighting/advanced/gl/irradianceP.glsl
  69. 12 6
      Templates/Full/game/shaders/common/lighting/advanced/gl/pointLightP.glsl
  70. 59 0
      Templates/Full/game/shaders/common/lighting/advanced/gl/probeShadingP.glsl
  71. 229 0
      Templates/Full/game/shaders/common/lighting/advanced/gl/reflectionProbeP.glsl
  72. 32 0
      Templates/Full/game/shaders/common/lighting/advanced/gl/reflectionProbeV.glsl
  73. 16 8
      Templates/Full/game/shaders/common/lighting/advanced/gl/spotLightP.glsl
  74. 16 24
      Templates/Full/game/shaders/common/lighting/advanced/gl/vectorLightP.glsl
  75. 63 0
      Templates/Full/game/shaders/common/lighting/advanced/irradianceP.hlsl
  76. 103 97
      Templates/Full/game/shaders/common/lighting/advanced/pointLightP.hlsl
  77. 130 0
      Templates/Full/game/shaders/common/lighting/advanced/prefilterP.hlsl
  78. 52 0
      Templates/Full/game/shaders/common/lighting/advanced/probeShadingP.hlsl
  79. 217 0
      Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeP.hlsl
  80. 145 0
      Templates/Full/game/shaders/common/lighting/advanced/skylightP.hlsl
  81. 99 97
      Templates/Full/game/shaders/common/lighting/advanced/vectorLightP.hlsl
  82. 9 6
      Templates/Full/game/shaders/common/postFx/hdr/finalPassCombineP.hlsl
  83. 23 11
      Templates/Full/game/shaders/common/postFx/hdr/gl/finalPassCombineP.glsl
  84. 34 0
      Templates/Full/game/shaders/common/torque.hlsl
  85. 0 1
      Templates/Full/game/shaders/common/water/gl/waterBasicP.glsl
  86. 0 1
      Templates/Full/game/shaders/common/water/gl/waterP.glsl
  87. 1 0
      Templates/Full/game/shaders/procedural/.gitignore
  88. 61 0
      Templates/Full/game/tools/resources/ReflectProbeSphere.dae
  89. 8 0
      Templates/Full/game/tools/resources/materials.cs
  90. 49 0
      Templates/Full/game/tools/resources/reflectionProbePreviewP.hlsl
  91. 58 0
      Templates/Full/game/tools/resources/reflectionProbePreviewV.hlsl
  92. 1559 1206
      Templates/Full/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui
  93. 18 0
      Templates/Full/game/tools/worldEditor/gui/objectBuilderGui.ed.gui
  94. 192 0
      Templates/Full/game/tools/worldEditor/gui/probeBakeDlg.gui
  95. 6 3
      Templates/Full/game/tools/worldEditor/main.cs
  96. 4 0
      Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs
  97. 38 2
      Templates/Full/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs
  98. 18 0
      Templates/Full/game/tools/worldEditor/scripts/lighting.ed.cs
  99. 2 0
      Templates/Full/game/tools/worldEditor/scripts/menus.ed.cs
  100. 48 0
      Templates/Full/game/tools/worldEditor/scripts/probeBake.ed.cs

+ 573 - 0
Engine/source/T3D/lighting/IBLUtilities.cpp

@@ -0,0 +1,573 @@
+#include "console/engineAPI.h"
+#include "materials/shaderData.h"
+#include "gfx/gfxTextureManager.h"
+#include "gfx/gfxTransformSaver.h"
+#include "gfx/bitmap/cubemapSaver.h"
+
+namespace IBLUtilities
+{
+   void GenerateIrradianceMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, GFXCubemapHandle &cubemapOut)
+   {
+      GFXTransformSaver saver;
+
+      GFXStateBlockRef irrStateBlock;
+
+      ShaderData *irrShaderData;
+      GFXShaderRef irrShader = Sim::findObject("IrradianceShader", irrShaderData) ? irrShaderData->getShader() : NULL;
+      if (!irrShader)
+      {
+         Con::errorf("IBLUtilities::GenerateIrradianceMap() - could not find IrradianceShader");
+         return;
+      }
+
+      GFXShaderConstBufferRef irrConsts = irrShader->allocConstBuffer();
+      GFXShaderConstHandle* irrEnvMapSC = irrShader->getShaderConstHandle("$environmentMap");
+      GFXShaderConstHandle* irrFaceSC = irrShader->getShaderConstHandle("$face");
+
+      GFXStateBlockDesc desc;
+      desc.zEnable = false;
+      desc.samplersDefined = true;
+      desc.samplers[0].addressModeU = GFXAddressClamp;
+      desc.samplers[0].addressModeV = GFXAddressClamp;
+      desc.samplers[0].addressModeW = GFXAddressClamp;
+      desc.samplers[0].magFilter = GFXTextureFilterLinear;
+      desc.samplers[0].minFilter = GFXTextureFilterLinear;
+      desc.samplers[0].mipFilter = GFXTextureFilterLinear;
+
+      irrStateBlock = GFX->createStateBlock(desc);
+
+      GFX->pushActiveRenderTarget();
+      GFX->setShader(irrShader);
+      GFX->setShaderConstBuffer(irrConsts);
+      GFX->setStateBlock(irrStateBlock);
+      GFX->setVertexBuffer(NULL);
+      GFX->setCubeTexture(0, cubemap);
+
+      for (U32 i = 0; i < 6; i++)
+      {
+         renderTarget->attachTexture(GFXTextureTarget::Color0, cubemapOut, i);
+         irrConsts->setSafe(irrFaceSC, (S32)i);
+         GFX->setActiveRenderTarget(renderTarget);
+         GFX->clear(GFXClearTarget, LinearColorF::BLACK, 1.0f, 0);
+         GFX->drawPrimitive(GFXTriangleList, 0, 3);
+         renderTarget->resolve();
+      }
+
+      GFX->popActiveRenderTarget();
+   }
+
+   void GeneratePrefilterMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, U32 mipLevels, GFXCubemapHandle &cubemapOut)
+   {
+      GFXTransformSaver saver;
+
+      ShaderData *prefilterShaderData;
+      GFXShaderRef prefilterShader = Sim::findObject("PrefiterCubemapShader", prefilterShaderData) ? prefilterShaderData->getShader() : NULL;
+      if (!prefilterShader)
+      {
+         Con::errorf("IBLUtilities::GeneratePrefilterMap() - could not find PrefiterCubemapShader");
+         return;
+      }
+
+      GFXShaderConstBufferRef prefilterConsts = prefilterShader->allocConstBuffer();
+      GFXShaderConstHandle* prefilterEnvMapSC = prefilterShader->getShaderConstHandle("$environmentMap");
+      GFXShaderConstHandle* prefilterFaceSC = prefilterShader->getShaderConstHandle("$face");
+      GFXShaderConstHandle* prefilterRoughnessSC = prefilterShader->getShaderConstHandle("$roughness");
+      GFXShaderConstHandle* prefilterMipSizeSC = prefilterShader->getShaderConstHandle("$mipSize");
+	  GFXShaderConstHandle* prefilterResolutionSC = prefilterShader->getShaderConstHandle("$resolution");
+	  
+      GFX->pushActiveRenderTarget();
+      GFX->setShader(prefilterShader);
+      GFX->setShaderConstBuffer(prefilterConsts);
+      GFX->setCubeTexture(0, cubemap);
+
+      U32 prefilterSize = cubemapOut->getSize();
+
+      for (U32 face = 0; face < 6; face++)
+      {
+         prefilterConsts->setSafe(prefilterFaceSC, (S32)face);
+		 prefilterConsts->setSafe(prefilterResolutionSC, renderTarget->getSize().x);
+         for (U32 mip = 0; mip < mipLevels; mip++)
+         {
+            S32 mipSize = prefilterSize >> mip;
+            F32 roughness = (float)mip / (float)(mipLevels - 1);
+            prefilterConsts->setSafe(prefilterRoughnessSC, roughness);
+            prefilterConsts->setSafe(prefilterMipSizeSC, mipSize);
+            U32 size = prefilterSize * mPow(0.5f, mip);
+            renderTarget->attachTexture(GFXTextureTarget::Color0, cubemapOut, face, mip);
+            GFX->setActiveRenderTarget(renderTarget, false);//we set the viewport ourselves
+            GFX->setViewport(RectI(0, 0, size, size));
+            GFX->clear(GFXClearTarget, LinearColorF::BLACK, 1.0f, 0);
+            GFX->drawPrimitive(GFXTriangleList, 0, 3);
+            renderTarget->resolve();
+         }
+      }
+
+      GFX->popActiveRenderTarget();
+   }
+
+   void GenerateBRDFTexture(GFXTexHandle &textureOut)
+   {
+      GFXTransformSaver saver;
+
+      ShaderData *brdfShaderData;
+      GFXShaderRef brdfShader = Sim::findObject("BRDFLookupShader", brdfShaderData) ? brdfShaderData->getShader() : NULL;
+      if (!brdfShader)
+      {
+         Con::errorf("IBLUtilities::GenerateBRDFTexture() - could not find BRDFLookupShader");
+         return;
+      }
+
+      U32 textureSize = textureOut->getWidth();
+
+      GFXTextureTargetRef renderTarget = GFX->allocRenderToTextureTarget();
+      GFX->pushActiveRenderTarget();
+
+      GFX->setShader(brdfShader);
+      renderTarget->attachTexture(GFXTextureTarget::Color0, textureOut);
+      GFX->setActiveRenderTarget(renderTarget);//potential bug here with the viewport not updating with the new size
+      GFX->setViewport(RectI(0, 0, textureSize, textureSize));//see above comment
+      GFX->clear(GFXClearTarget, LinearColorF::BLUE, 1.0f, 0);
+      GFX->drawPrimitive(GFXTriangleList, 0, 3);
+      renderTarget->resolve();
+
+      GFX->popActiveRenderTarget();
+   }
+
+   void bakeReflection(String outputPath, S32 resolution)
+   {
+      //GFXDEBUGEVENT_SCOPE(ReflectionProbe_Bake, ColorI::WHITE);
+
+      /*PostEffect *preCapture = dynamic_cast<PostEffect*>(Sim::findObject("AL_PreCapture"));
+      PostEffect *deferredShading = dynamic_cast<PostEffect*>(Sim::findObject("AL_DeferredShading"));
+      if (preCapture)
+         preCapture->enable();
+      if (deferredShading)
+         deferredShading->disable();
+
+      //if (mReflectionModeType == StaticCubemap || mReflectionModeType == BakedCubemap || mReflectionModeType == SkyLight)
+      {
+         if (!mCubemap)
+         {
+            mCubemap = new CubemapData();
+            mCubemap->registerObject();
+         }
+      }
+
+      if (mReflectionModeType == DynamicCubemap && mDynamicCubemap.isNull())
+      {
+         //mCubemap->createMap();
+         mDynamicCubemap = GFX->createCubemap();
+         mDynamicCubemap->initDynamic(resolution, GFXFormatR8G8B8);
+      }
+      else if (mReflectionModeType != DynamicCubemap)
+      {
+         if (mReflectionPath.isEmpty() || !mPersistentId)
+         {
+            if (!mPersistentId)
+               mPersistentId = getOrCreatePersistentId();
+
+            mReflectionPath = outputPath.c_str();
+
+            mProbeUniqueID = std::to_string(mPersistentId->getUUID().getHash()).c_str();
+         }
+      }
+
+      bool validCubemap = true;
+
+      // Save the current transforms so we can restore
+      // it for child control rendering below.
+      GFXTransformSaver saver;
+
+      //bool saveEditingMission = gEditingMission;
+      //gEditingMission = false;
+
+      //Set this to true to use the prior method where it goes through the SPT_Reflect path for the bake
+      bool probeRenderState = ReflectionProbe::smRenderReflectionProbes;
+      ReflectionProbe::smRenderReflectionProbes = false;
+      for (U32 i = 0; i < 6; ++i)
+      {
+         GFXTexHandle blendTex;
+         blendTex.set(resolution, resolution, GFXFormatR8G8B8A8, &GFXRenderTargetProfile, "");
+
+         GFXTextureTargetRef mBaseTarget = GFX->allocRenderToTextureTarget();
+
+         GFX->clearTextureStateImmediate(0);
+         if (mReflectionModeType == DynamicCubemap)
+            mBaseTarget->attachTexture(GFXTextureTarget::Color0, mDynamicCubemap, i);
+         else
+            mBaseTarget->attachTexture(GFXTextureTarget::Color0, blendTex);
+
+         // Standard view that will be overridden below.
+         VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f);
+
+         switch (i)
+         {
+            case 0: // D3DCUBEMAP_FACE_POSITIVE_X:
+               vLookatPt = VectorF(1.0f, 0.0f, 0.0f);
+               vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+               break;
+            case 1: // D3DCUBEMAP_FACE_NEGATIVE_X:
+               vLookatPt = VectorF(-1.0f, 0.0f, 0.0f);
+               vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+               break;
+            case 2: // D3DCUBEMAP_FACE_POSITIVE_Y:
+               vLookatPt = VectorF(0.0f, 1.0f, 0.0f);
+               vUpVec = VectorF(0.0f, 0.0f, -1.0f);
+               break;
+            case 3: // D3DCUBEMAP_FACE_NEGATIVE_Y:
+               vLookatPt = VectorF(0.0f, -1.0f, 0.0f);
+               vUpVec = VectorF(0.0f, 0.0f, 1.0f);
+               break;
+            case 4: // D3DCUBEMAP_FACE_POSITIVE_Z:
+               vLookatPt = VectorF(0.0f, 0.0f, 1.0f);
+               vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+               break;
+            case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
+               vLookatPt = VectorF(0.0f, 0.0f, -1.0f);
+               vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+               break;
+         }
+
+         // create camera matrix
+         VectorF cross = mCross(vUpVec, vLookatPt);
+         cross.normalizeSafe();
+
+         MatrixF matView(true);
+         matView.setColumn(0, cross);
+         matView.setColumn(1, vLookatPt);
+         matView.setColumn(2, vUpVec);
+         matView.setPosition(getPosition());
+         matView.inverse();
+
+         // set projection to 90 degrees vertical and horizontal
+         F32 left, right, top, bottom;
+         F32 nearPlane = 0.01f;
+         F32 farDist = 1000.f;
+
+         MathUtils::makeFrustum(&left, &right, &top, &bottom, M_HALFPI_F, 1.0f, nearPlane);
+         Frustum frustum(false, left, right, top, bottom, nearPlane, farDist);
+
+         renderFrame(&mBaseTarget, matView, frustum, StaticObjectType | StaticShapeObjectType & EDITOR_RENDER_TYPEMASK, gCanvasClearColor);
+
+         mBaseTarget->resolve();
+
+         mCubemap->setCubeFaceTexture(i, blendTex);
+      }
+
+      if (mReflectionModeType != DynamicCubemap && validCubemap)
+      {
+         if (mCubemap->mCubemap)
+            mCubemap->updateFaces();
+         else
+            mCubemap->createMap();
+
+         char fileName[256];
+         dSprintf(fileName, 256, "%s%s.DDS", mReflectionPath.c_str(), mProbeUniqueID.c_str());
+
+         CubemapSaver::save(mCubemap->mCubemap, fileName);
+
+         if (!Platform::isFile(fileName))
+         {
+            validCubemap = false; //if we didn't save right, just 
+            Con::errorf("Failed to properly save out the skylight baked cubemap!");
+         }
+
+         mDirty = false;
+      }
+
+      //calculateSHTerms();
+
+      ReflectionProbe::smRenderReflectionProbes = probeRenderState;
+      setMaskBits(-1);
+
+      if (preCapture)
+         preCapture->disable();
+      if (deferredShading)
+         deferredShading->enable();*/
+   }
+
+   LinearColorF decodeSH(Point3F normal, const LinearColorF SHTerms[9], const F32 SHConstants[5])
+   {
+      float x = normal.x;
+      float y = normal.y;
+      float z = normal.z;
+
+      LinearColorF l00 = SHTerms[0];
+
+      LinearColorF l10 = SHTerms[1];
+      LinearColorF l11 = SHTerms[2];
+      LinearColorF l12 = SHTerms[3];
+
+      LinearColorF l20 = SHTerms[4];
+      LinearColorF l21 = SHTerms[5];
+      LinearColorF l22 = SHTerms[6];
+      LinearColorF l23 = SHTerms[7];
+      LinearColorF l24 = SHTerms[8];
+
+      LinearColorF result = (
+         l00 * SHConstants[0] +
+
+         l12 * SHConstants[1] * x +
+         l10 * SHConstants[1] * y +
+         l11 * SHConstants[1] * z +
+
+         l20 * SHConstants[2] * x*y +
+         l21 * SHConstants[2] * y*z +
+         l22 * SHConstants[3] * (3.0*z*z - 1.0) +
+         l23 * SHConstants[2] * x*z +
+         l24 * SHConstants[4] * (x*x - y * y)
+         );
+
+      return LinearColorF(mMax(result.red, 0), mMax(result.green, 0), mMax(result.blue, 0));
+   }
+
+   MatrixF getSideMatrix(U32 side)
+   {
+      // Standard view that will be overridden below.
+      VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f);
+
+      switch (side)
+      {
+         case 0: // D3DCUBEMAP_FACE_POSITIVE_X:
+            vLookatPt = VectorF(1.0f, 0.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+         case 1: // D3DCUBEMAP_FACE_NEGATIVE_X:
+            vLookatPt = VectorF(-1.0f, 0.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+         case 2: // D3DCUBEMAP_FACE_POSITIVE_Y:
+            vLookatPt = VectorF(0.0f, 1.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 0.0f, -1.0f);
+            break;
+         case 3: // D3DCUBEMAP_FACE_NEGATIVE_Y:
+            vLookatPt = VectorF(0.0f, -1.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 0.0f, 1.0f);
+            break;
+         case 4: // D3DCUBEMAP_FACE_POSITIVE_Z:
+            vLookatPt = VectorF(0.0f, 0.0f, 1.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+         case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
+            vLookatPt = VectorF(0.0f, 0.0f, -1.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+      }
+
+      // create camera matrix
+      VectorF cross = mCross(vUpVec, vLookatPt);
+      cross.normalizeSafe();
+
+      MatrixF rotMat(true);
+      rotMat.setColumn(0, cross);
+      rotMat.setColumn(1, vLookatPt);
+      rotMat.setColumn(2, vUpVec);
+      //rotMat.inverse();
+
+      return rotMat;
+   }
+
+   F32 harmonics(U32 termId, Point3F normal)
+   {
+      F32 x = normal.x;
+      F32 y = normal.y;
+      F32 z = normal.z;
+
+      switch (termId)
+      {
+         case 0:
+            return 1.0;
+         case 1:
+            return y;
+         case 2:
+            return z;
+         case 3:
+            return x;
+         case 4:
+            return x * y;
+         case 5:
+            return y * z;
+         case 6:
+            return 3.0*z*z - 1.0;
+         case 7:
+            return x * z;
+         default:
+            return x * x - y * y;
+      }
+   }
+
+   LinearColorF sampleSide(GBitmap* cubeFaceBitmaps[6], const U32& cubemapResolution, const U32& termindex, const U32& sideIndex)
+   {
+      MatrixF sideRot = getSideMatrix(sideIndex);
+
+      LinearColorF result = LinearColorF::ZERO;
+      F32 divider = 0;
+
+      for (int y = 0; y<cubemapResolution; y++)
+      {
+         for (int x = 0; x<cubemapResolution; x++)
+         {
+            Point2F sidecoord = ((Point2F(x, y) + Point2F(0.5, 0.5)) / Point2F(cubemapResolution, cubemapResolution))*2.0 - Point2F(1.0, 1.0);
+            Point3F normal = Point3F(sidecoord.x, sidecoord.y, -1.0);
+            normal.normalize();
+
+            F32 minBrightness = Con::getFloatVariable("$pref::GI::Cubemap_Sample_MinBrightness", 0.001f);
+
+            LinearColorF texel = cubeFaceBitmaps[sideIndex]->sampleTexel(y, x);
+            texel = LinearColorF(mMax(texel.red, minBrightness), mMax(texel.green, minBrightness), mMax(texel.blue, minBrightness)) * Con::getFloatVariable("$pref::GI::Cubemap_Gain", 1.5);
+
+            Point3F dir;
+            sideRot.mulP(normal, &dir);
+
+            result += texel * harmonics(termindex, dir) * -normal.z;
+            divider += -normal.z;
+         }
+      }
+
+      result /= divider;
+
+      return result;
+   }
+
+   //
+   //SH Calculations
+   // From http://sunandblackcat.com/tipFullView.php?l=eng&topicid=32&topic=Spherical-Harmonics-From-Cube-Texture
+   // With shader decode logic from https://github.com/nicknikolov/cubemap-sh
+   void calculateSHTerms(GFXCubemapHandle cubemap, LinearColorF SHTerms[9], F32 SHConstants[5])
+   {
+      if (!cubemap)
+         return;
+
+      const VectorF cubemapFaceNormals[6] =
+      {
+         // D3DCUBEMAP_FACE_POSITIVE_X:
+         VectorF(1.0f, 0.0f, 0.0f),
+         // D3DCUBEMAP_FACE_NEGATIVE_X:
+         VectorF(-1.0f, 0.0f, 0.0f),
+         // D3DCUBEMAP_FACE_POSITIVE_Y:
+         VectorF(0.0f, 1.0f, 0.0f),
+         // D3DCUBEMAP_FACE_NEGATIVE_Y:
+         VectorF(0.0f, -1.0f, 0.0f),
+         // D3DCUBEMAP_FACE_POSITIVE_Z:
+         VectorF(0.0f, 0.0f, 1.0f),
+         // D3DCUBEMAP_FACE_NEGATIVE_Z:
+         VectorF(0.0f, 0.0f, -1.0f),
+      };
+
+      U32 cubemapResolution = cubemap->getSize();
+
+      GBitmap* cubeFaceBitmaps[6];
+
+      for (U32 i = 0; i < 6; i++)
+      {
+         cubeFaceBitmaps[i] = new GBitmap(cubemapResolution, cubemapResolution, false, GFXFormatR16G16B16A16F);
+      }
+
+      //If we fail to parse the cubemap for whatever reason, we really can't continue
+      if (!CubemapSaver::getBitmaps(cubemap, GFXFormatR8G8B8, cubeFaceBitmaps))
+         return;
+
+      //Set up our constants
+      F32 L0 = Con::getFloatVariable("$pref::GI::SH_Term_L0", 1.0f);
+      F32 L1 = Con::getFloatVariable("$pref::GI::SH_Term_L1", 1.8f);
+      F32 L2 = Con::getFloatVariable("$pref::GI::SH_Term_L2", 0.83f);
+      F32 L2m2_L2m1_L21 = Con::getFloatVariable("$pref::GI::SH_Term_L2m2", 2.9f);
+      F32 L20 = Con::getFloatVariable("$pref::GI::SH_Term_L20", 0.58f);
+      F32 L22 = Con::getFloatVariable("$pref::GI::SH_Term_L22", 1.1f);
+
+      SHConstants[0] = L0;
+      SHConstants[1] = L1;
+      SHConstants[2] = L2 * L2m2_L2m1_L21;
+      SHConstants[3] = L2 * L20;
+      SHConstants[4] = L2 * L22;
+
+      for (U32 i = 0; i < 9; i++)
+      {
+         //Clear it, just to be sure
+         SHTerms[i] = LinearColorF(0.f, 0.f, 0.f);
+
+         //Now, encode for each side
+         SHTerms[i] = sampleSide(cubeFaceBitmaps, cubemapResolution, i, 0); //POS_X
+         SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 1); //NEG_X
+         SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 2); //POS_Y
+         SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 3); //NEG_Y
+         SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 4); //POS_Z
+         SHTerms[i] += sampleSide(cubeFaceBitmaps, cubemapResolution, i, 5); //NEG_Z
+
+                                                      //Average
+         SHTerms[i] /= 6;
+      }
+
+      for (U32 i = 0; i < 6; i++)
+         SAFE_DELETE(cubeFaceBitmaps[i]);
+
+      /*bool mExportSHTerms = false;
+      if (mExportSHTerms)
+      {
+         for (U32 f = 0; f < 6; f++)
+         {
+            char fileName[256];
+            dSprintf(fileName, 256, "%s%s_DecodedFaces_%d.png", mReflectionPath.c_str(),
+               mProbeUniqueID.c_str(), f);
+
+            LinearColorF color = decodeSH(cubemapFaceNormals[f]);
+
+            FileStream stream;
+            if (stream.open(fileName, Torque::FS::File::Write))
+            {
+               GBitmap bitmap(mCubemapResolution, mCubemapResolution, false, GFXFormatR8G8B8);
+
+               bitmap.fill(color.toColorI());
+
+               bitmap.writeBitmap("png", stream);
+            }
+         }
+
+         for (U32 f = 0; f < 9; f++)
+         {
+            char fileName[256];
+            dSprintf(fileName, 256, "%s%s_SHTerms_%d.png", mReflectionPath.c_str(),
+               mProbeUniqueID.c_str(), f);
+
+            LinearColorF color = mProbeInfo->SHTerms[f];
+
+            FileStream stream;
+            if (stream.open(fileName, Torque::FS::File::Write))
+            {
+               GBitmap bitmap(mCubemapResolution, mCubemapResolution, false, GFXFormatR8G8B8);
+
+               bitmap.fill(color.toColorI());
+
+               bitmap.writeBitmap("png", stream);
+            }
+         }
+      }*/
+   }
+
+   F32 areaElement(F32 x, F32 y)
+   {
+      return mAtan2(x * y, (F32)mSqrt(x * x + y * y + 1.0));
+   }
+
+   F32 texelSolidAngle(F32 aU, F32 aV, U32 width, U32 height)
+   {
+      // transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)]
+      // ( 0.5 is for texel center addressing)
+      const F32 U = (2.0 * (aU + 0.5) / width) - 1.0;
+      const F32 V = (2.0 * (aV + 0.5) / height) - 1.0;
+
+      // shift from a demi texel, mean 1.0 / size  with U and V in [-1..1]
+      const F32 invResolutionW = 1.0 / width;
+      const F32 invResolutionH = 1.0 / height;
+
+      // U and V are the -1..1 texture coordinate on the current face.
+      // get projected area for this texel
+      const F32 x0 = U - invResolutionW;
+      const F32 y0 = V - invResolutionH;
+      const F32 x1 = U + invResolutionW;
+      const F32 y1 = V + invResolutionH;
+      const F32 angle = areaElement(x0, y0) - areaElement(x0, y1) - areaElement(x1, y0) + areaElement(x1, y1);
+
+      return angle;
+   }
+};

+ 30 - 0
Engine/source/T3D/lighting/IBLUtilities.h

@@ -0,0 +1,30 @@
+#pragma once
+
+namespace IBLUtilities
+{
+   void GenerateIrradianceMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, GFXCubemapHandle &cubemapOut);
+
+   void GeneratePrefilterMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, U32 mipLevels, GFXCubemapHandle &cubemapOut);
+
+   void GenerateBRDFTexture(GFXTexHandle &textureOut);
+
+   void bakeReflection(String outputPath, S32 resolution);
+
+   LinearColorF decodeSH(Point3F normal, const LinearColorF SHTerms[9], const F32 SHConstants[5]);
+
+   MatrixF getSideMatrix(U32 side);
+
+   F32 harmonics(U32 termId, Point3F normal);
+
+   LinearColorF sampleSide(GBitmap* cubeFaceBitmaps[6], const U32& cubemapResolution, const U32& termindex, const U32& sideIndex);
+
+   //
+   //SH Calculations
+   // From http://sunandblackcat.com/tipFullView.php?l=eng&topicid=32&topic=Spherical-Harmonics-From-Cube-Texture
+   // With shader decode logic from https://github.com/nicknikolov/cubemap-sh
+   void calculateSHTerms(GFXCubemapHandle cubemap, LinearColorF SHTerms[9], F32 SHConstants[5]);
+
+   F32 texelSolidAngle(F32 aU, F32 aV, U32 width, U32 height);
+
+   F32 areaElement(F32 x, F32 y);
+};

+ 1285 - 0
Engine/source/T3D/lighting/reflectionProbe.cpp

@@ -0,0 +1,1285 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "T3D/lighting/reflectionProbe.h"
+#include "math/mathIO.h"
+#include "scene/sceneRenderState.h"
+#include "console/consoleTypes.h"
+#include "core/stream/bitStream.h"
+#include "materials/baseMatInstance.h"
+#include "console/engineAPI.h"
+#include "gfx/gfxDrawUtil.h"
+#include "gfx/gfxDebugEvent.h"
+#include "gfx/gfxTransformSaver.h"
+#include "math/mathUtils.h"
+#include "gfx/bitmap/gBitmap.h"
+#include "core/stream/fileStream.h"
+#include "core/fileObject.h"
+#include "core/resourceManager.h"
+#include "console/simPersistId.h"
+#include <string>
+#include "T3D/gameFunctions.h"
+#include "postFx/postEffect.h"
+#include "renderInstance/renderProbeMgr.h"
+#include "lighting/probeManager.h"
+
+#include "math/util/sphereMesh.h"
+#include "materials/materialManager.h"
+#include "math/util/matrixSet.h"
+#include "gfx/bitmap/cubemapSaver.h"
+
+#include "materials/materialFeatureTypes.h"
+
+#include "gfx/gfxTextureManager.h"
+#include "T3D/lighting/IBLUtilities.h"
+
+extern bool gEditingMission;
+extern ColorI gCanvasClearColor;
+bool ReflectionProbe::smRenderReflectionProbes = true;
+bool ReflectionProbe::smRenderPreviewProbes = true;
+
+IMPLEMENT_CO_NETOBJECT_V1(ReflectionProbe);
+
+ConsoleDocClass(ReflectionProbe,
+   "@brief An example scene object which renders a mesh.\n\n"
+   "This class implements a basic SceneObject that can exist in the world at a "
+   "3D position and render itself. There are several valid ways to render an "
+   "object in Torque. This class implements the preferred rendering method which "
+   "is to submit a MeshRenderInst along with a Material, vertex buffer, "
+   "primitive buffer, and transform and allow the RenderMeshMgr handle the "
+   "actual setup and rendering for you.\n\n"
+   "See the C++ code for implementation details.\n\n"
+   "@ingroup Examples\n");
+
+ImplementEnumType(ReflectProbeType,
+   "Type of mesh data available in a shape.\n"
+   "@ingroup gameObjects")
+{ ProbeInfo::Sphere, "Sphere", "Sphere shaped" },
+{ ProbeInfo::Box, "Box", "Box shape" }
+EndImplementEnumType;
+
+ImplementEnumType(IndrectLightingModeEnum,
+   "Type of mesh data available in a shape.\n"
+   "@ingroup gameObjects")
+{ ReflectionProbe::NoIndirect, "No Lighting", "This probe does not provide any local indirect lighting data" },
+{ ReflectionProbe::AmbientColor, "Ambient Color", "Adds a flat color to act as the local indirect lighting" },
+{ ReflectionProbe::SphericalHarmonics, "Spherical Harmonics", "Creates spherical harmonics data based off the reflection data" },
+   EndImplementEnumType;
+
+ImplementEnumType(ReflectionModeEnum,
+   "Type of mesh data available in a shape.\n"
+   "@ingroup gameObjects")
+{ ReflectionProbe::NoReflection, "No Reflections", "This probe does not provide any local reflection data"},
+{ ReflectionProbe::StaticCubemap, "Static Cubemap", "Uses a static CubemapData" },
+{ ReflectionProbe::BakedCubemap, "Baked Cubemap", "Uses a cubemap baked from the probe's current position" },
+//{ ReflectionProbe::DynamicCubemap, "Dynamic Cubemap", "Uses a cubemap baked from the probe's current position, updated at a set rate" },
+   EndImplementEnumType;
+
+//-----------------------------------------------------------------------------
+// Object setup and teardown
+//-----------------------------------------------------------------------------
+ReflectionProbe::ReflectionProbe()
+{
+   // Flag this object so that it will always
+   // be sent across the network to clients
+   mNetFlags.set(Ghostable | ScopeAlways);
+
+   mTypeMask = LightObjectType | MarkerObjectType;
+
+   mProbeShapeType = ProbeInfo::Sphere;
+
+   mIndrectLightingModeType = NoIndirect;
+   mAmbientColor = LinearColorF(1, 1, 1, 1);
+   mSphericalHarmonics = LinearColorF(0, 0, 0, 1);
+
+   mReflectionModeType = BakedCubemap;
+
+   mEnabled = true;
+   mBake = false;
+   mDirty = false;
+
+   mRadius = 10;
+
+   mUseCubemap = false;
+   mCubemap = NULL;
+   mReflectionPath = "";
+   mProbeUniqueID = "";
+
+   mEditorShapeInst = NULL;
+   mEditorShape = NULL;
+
+   mRefreshRateMS = 200;
+   mDynamicLastBakeMS = 0;
+
+   mMaxDrawDistance = 75;
+
+   mResourcesCreated = false;
+
+   mProbeInfo = new ProbeInfo();
+
+   mPrefilterSize = 64;
+   mPrefilterMipLevels = mLog2(F32(mPrefilterSize));
+}
+
+ReflectionProbe::~ReflectionProbe()
+{
+   if (mEditorShapeInst)
+      SAFE_DELETE(mEditorShapeInst);
+
+   if (mReflectionModeType != StaticCubemap && mCubemap)
+      mCubemap->deleteObject();
+}
+
+//-----------------------------------------------------------------------------
+// Object Editing
+//-----------------------------------------------------------------------------
+void ReflectionProbe::initPersistFields()
+{
+   addGroup("Rendering");
+      addProtectedField("enabled", TypeBool, Offset(mEnabled, ReflectionProbe),
+         &_setEnabled, &defaultProtectedGetFn, "Regenerate Voxel Grid");
+
+      addField("ProbeShape", TypeReflectProbeType, Offset(mProbeShapeType, ReflectionProbe),
+         "The type of mesh data to use for collision queries.");
+      addField("radius", TypeF32, Offset(mRadius, ReflectionProbe), "The name of the material used to render the mesh.");
+   endGroup("Rendering");
+
+   /*addGroup("IndirectLighting");
+      addField("IndirectLightMode", TypeIndrectLightingModeEnum, Offset(mIndrectLightingModeType, ReflectionProbe),
+         "The type of mesh data to use for collision queries.");
+
+      addField("IndirectLight", TypeColorF, Offset(mAmbientColor, ReflectionProbe), "Path of file to save and load results.");
+   endGroup("IndirectLighting");*/
+
+   addGroup("Reflection");
+      addField("ReflectionMode", TypeReflectionModeEnum, Offset(mReflectionModeType, ReflectionProbe),
+         "The type of mesh data to use for collision queries.");
+
+      addField("reflectionPath", TypeImageFilename, Offset(mReflectionPath, ReflectionProbe),
+         "The type of mesh data to use for collision queries.");
+
+      addField("StaticCubemap", TypeCubemapName, Offset(mCubemapName, ReflectionProbe), "Cubemap used instead of reflection texture if fullReflect is off.");
+
+      addProtectedField("Bake", TypeBool, Offset(mBake, ReflectionProbe),
+         &_doBake, &defaultProtectedGetFn, "Regenerate Voxel Grid", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
+   endGroup("Reflection");
+
+   Con::addVariable("$Light::renderReflectionProbes", TypeBool, &ReflectionProbe::smRenderReflectionProbes,
+      "Toggles rendering of light frustums when the light is selected in the editor.\n\n"
+      "@note Only works for shadow mapped lights.\n\n"
+      "@ingroup Lighting");
+
+   Con::addVariable("$Light::renderPreviewProbes", TypeBool, &ReflectionProbe::smRenderPreviewProbes,
+      "Toggles rendering of light frustums when the light is selected in the editor.\n\n"
+      "@note Only works for shadow mapped lights.\n\n"
+      "@ingroup Lighting");
+
+   /*addGroup("Internal");
+
+   addProtectedField("SHTerm", TypeRealString, NULL, &protectedSetSHTerms, &defaultProtectedGetFn,
+      "Do not modify, for internal use.", AbstractClassRep::FIELD_HideInInspectors);
+
+   addProtectedField("SHConsts", TypeRealString, NULL, &protectedSetSHConsts, &defaultProtectedGetFn,
+      "Do not modify, for internal use.", AbstractClassRep::FIELD_HideInInspectors);
+
+   endGroup("Internal");*/
+
+   // SceneObject already handles exposing the transform
+   Parent::initPersistFields();
+}
+
+bool ReflectionProbe::protectedSetSHTerms(void *object, const char *index, const char *data)
+{
+   ReflectionProbe *probe = static_cast< ReflectionProbe* >(object);
+
+   LinearColorF term;
+   U32 idx;
+
+   dSscanf(data, "%i %g %g %g", &idx, &term.red, &term.green, &term.blue);
+
+   probe->mProbeInfo->mSHTerms[idx] = term;
+
+   return false;
+}
+
+bool ReflectionProbe::protectedSetSHConsts(void *object, const char *index, const char *data)
+{
+   ReflectionProbe *probe = static_cast< ReflectionProbe* >(object);
+
+   dSscanf(data, "%g %g %g %g %g", &probe->mProbeInfo->mSHConstants[0],
+      &probe->mProbeInfo->mSHConstants[1], &probe->mProbeInfo->mSHConstants[2], &probe->mProbeInfo->mSHConstants[3], &probe->mProbeInfo->mSHConstants[4]);
+
+   return false;
+}
+
+void ReflectionProbe::writeFields(Stream &stream, U32 tabStop)
+{
+   Parent::writeFields(stream, tabStop);
+
+   if (mIndrectLightingModeType != SphericalHarmonics)
+      return;
+
+   // Now write all planes.
+
+   stream.write(2, "\r\n");
+
+   for (U32 i = 0; i < 9; i++)
+   {
+      const LinearColorF shTerm = mProbeInfo->mSHTerms[i];
+
+      stream.writeTabs(tabStop);
+
+      char buffer[1024];
+      dMemset(buffer, 0, 1024);
+
+      dSprintf(buffer, 1024, "SHTerm = \"%i %g %g %g\";", i, shTerm.red, shTerm.green, shTerm.blue);
+
+      stream.writeLine((const U8*)buffer);
+   }
+
+   stream.writeTabs(tabStop);
+
+   char buffer[1024];
+   dMemset(buffer, 0, 1024);
+
+   dSprintf(buffer, 1024, "SHConsts = \"%g %g %g %g %g\";", mProbeInfo->mSHConstants[0],
+      mProbeInfo->mSHConstants[1], mProbeInfo->mSHConstants[2], mProbeInfo->mSHConstants[3], mProbeInfo->mSHConstants[4]);
+
+   stream.writeLine((const U8*)buffer);
+}
+
+bool ReflectionProbe::writeField(StringTableEntry fieldname, const char *value)
+{
+   if (fieldname == StringTable->insert("SHTerm") || fieldname == StringTable->insert("SHConsts"))
+      return false;
+
+   return Parent::writeField(fieldname, value);
+}
+
+void ReflectionProbe::inspectPostApply()
+{
+   Parent::inspectPostApply();
+
+   mDirty = true;
+
+   // Flag the network mask to send the updates
+   // to the client object
+   setMaskBits(-1);
+}
+
+bool ReflectionProbe::_setEnabled(void *object, const char *index, const char *data)
+{
+   ReflectionProbe* probe = reinterpret_cast< ReflectionProbe* >(object);
+
+   probe->mEnabled = dAtob(data);
+   probe->setMaskBits(-1);
+
+   return true;
+}
+
+bool ReflectionProbe::_doBake(void *object, const char *index, const char *data)
+{
+   ReflectionProbe* probe = reinterpret_cast< ReflectionProbe* >(object);
+
+   if (probe->mDirty)
+      probe->bake(probe->mReflectionPath, 256);
+
+   return false;
+}
+
+bool ReflectionProbe::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   mObjBox.minExtents.set(-1, -1, -1);
+   mObjBox.maxExtents.set(1, 1, 1);
+   mObjScale.set(mRadius/2, mRadius/2, mRadius/2);
+
+   // Skip our transform... it just dirties mask bits.
+   Parent::setTransform(mObjToWorld);
+
+   resetWorldBox();
+
+   // Add this object to the scene
+   addToScene();
+
+   if (isServerObject())
+   {
+      if (!mPersistentId)
+         mPersistentId = getOrCreatePersistentId();
+
+      mProbeUniqueID = std::to_string(mPersistentId->getUUID().getHash()).c_str();
+   }
+
+   // Refresh this object's material (if any)
+   if (isClientObject())
+      updateMaterial();
+  
+   setMaskBits(-1);
+
+   return true;
+}
+
+void ReflectionProbe::onRemove()
+{
+   // Remove this object from the scene
+   removeFromScene();
+
+   Parent::onRemove();
+}
+
+void ReflectionProbe::setTransform(const MatrixF & mat)
+{
+   // Let SceneObject handle all of the matrix manipulation
+   Parent::setTransform(mat);
+
+   mDirty = true;
+
+   // Dirty our network mask so that the new transform gets
+   // transmitted to the client object
+   setMaskBits(TransformMask);
+}
+
+U32 ReflectionProbe::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
+{
+   // Allow the Parent to get a crack at writing its info
+   U32 retMask = Parent::packUpdate(conn, mask, stream);
+
+   // Write our transform information
+   if (stream->writeFlag(mask & TransformMask))
+   {
+      mathWrite(*stream, getTransform());
+      mathWrite(*stream, getScale());
+   }
+
+   if (stream->writeFlag(mask & ShapeTypeMask))
+   {
+      stream->write((U32)mProbeShapeType);
+   }
+
+   if (stream->writeFlag(mask & UpdateMask))
+   {
+      stream->write(mAmbientColor);
+      stream->write(mRadius);
+   }
+
+   if (stream->writeFlag(mask & BakeInfoMask))
+   {
+      stream->write(mReflectionPath);
+      stream->write(mProbeUniqueID);
+   }
+
+   if (stream->writeFlag(mask & EnabledMask))
+   {
+      stream->writeFlag(mEnabled);
+   }
+
+   if (stream->writeFlag(mask & ModeMask))
+   {
+      stream->write((U32)mIndrectLightingModeType);
+      stream->write((U32)mReflectionModeType);
+   }
+
+   if (stream->writeFlag(mask & CubemapMask))
+   {
+      stream->writeFlag(mUseCubemap);
+      stream->write(mCubemapName);
+   }
+
+   return retMask;
+}
+
+void ReflectionProbe::unpackUpdate(NetConnection *conn, BitStream *stream)
+{
+   // Let the Parent read any info it sent
+   Parent::unpackUpdate(conn, stream);
+
+   if (stream->readFlag())  // TransformMask
+   {
+      mathRead(*stream, &mObjToWorld);
+      mathRead(*stream, &mObjScale);
+
+      setTransform(mObjToWorld);
+   }
+
+   if (stream->readFlag())  // ShapeTypeMask
+   {
+      U32 shapeType = ProbeInfo::Sphere;
+      stream->read(&shapeType);
+
+      mProbeShapeType = (ProbeInfo::ProbeShapeType)shapeType;
+      createGeometry();
+   }
+
+   if (stream->readFlag())  // UpdateMask
+   {
+      stream->read(&mAmbientColor);
+      stream->read(&mRadius);
+   }
+
+   if (stream->readFlag())  // BakeInfoMask
+   {
+      stream->read(&mReflectionPath);
+      stream->read(&mProbeUniqueID);
+   }
+
+   if (stream->readFlag())  // EnabledMask
+   {
+      mEnabled = stream->readFlag();
+   }
+
+   bool isMaterialDirty = false;
+
+   if (stream->readFlag())  // ModeMask
+   {
+      U32 indirectModeType = AmbientColor;
+      stream->read(&indirectModeType);
+      mIndrectLightingModeType = (IndrectLightingModeType)indirectModeType;
+
+      U32 reflectModeType = BakedCubemap;
+      stream->read(&reflectModeType);
+      mReflectionModeType = (ReflectionModeType)reflectModeType;
+
+      isMaterialDirty = true;
+   }
+
+   if (stream->readFlag())  // CubemapMask
+   {
+      mUseCubemap = stream->readFlag();
+
+      stream->read(&mCubemapName);
+
+      isMaterialDirty = true;
+   }
+
+   updateProbeParams();
+
+   if(isMaterialDirty)
+      updateMaterial();
+}
+
+void ReflectionProbe::createGeometry()
+{
+   // Clean up our previous shape
+   if (mEditorShapeInst)
+      SAFE_DELETE(mEditorShapeInst);
+   
+   mEditorShape = NULL;
+   
+   String shapeFile = "tools/resources/ReflectProbeSphere.dae";
+   
+   // Attempt to get the resource from the ResourceManager
+   mEditorShape = ResourceManager::get().load(shapeFile);
+   if (mEditorShape)
+   {
+      mEditorShapeInst = new TSShapeInstance(mEditorShape, isClientObject());
+   }
+}
+
+//-----------------------------------------------------------------------------
+// Object Rendering
+//-----------------------------------------------------------------------------
+
+void ReflectionProbe::updateProbeParams()
+{
+   if (mProbeInfo == nullptr)
+      return;
+
+   if (mIndrectLightingModeType == AmbientColor)
+   {
+      mProbeInfo->mAmbient = mAmbientColor;
+   }
+   else
+   {
+      mProbeInfo->mAmbient = LinearColorF(0, 0, 0, 0);
+   }
+
+   mProbeInfo->mProbeShapeType = mProbeShapeType;
+
+   mProbeInfo->setPosition(getPosition());
+
+   //Update the bounds
+   mObjBox.minExtents.set(-1, -1, -1);
+   mObjBox.maxExtents.set(1, 1, 1);
+
+   mObjScale.set(mRadius / 2, mRadius / 2, mRadius / 2);
+
+   // Skip our transform... it just dirties mask bits.
+   Parent::setTransform(mObjToWorld);
+
+   resetWorldBox();
+
+   mProbeInfo->mBounds = mWorldBox;
+   mProbeInfo->mRadius = mRadius;
+
+   mProbeInfo->mIsSkylight = false;
+}
+
+void ReflectionProbe::updateMaterial()
+{
+   if (mReflectionModeType != DynamicCubemap)
+   {
+      if ((mReflectionModeType == BakedCubemap) && !mProbeUniqueID.isEmpty())
+      {
+         bool validCubemap = true;
+
+         char fileName[256];
+         dSprintf(fileName, 256, "%s%s.DDS", mReflectionPath.c_str(), mProbeUniqueID.c_str());
+
+         Vector<FileName> fileNames;
+
+         if (Platform::isFile(fileName))
+         {
+            if (!mCubemap)
+            {
+               mCubemap = new CubemapData();
+               mCubemap->registerObject();
+            }
+
+            mCubemap->setCubemapFile(FileName(fileName));
+         }
+         else
+         {
+            validCubemap = false;
+         }
+
+         if (validCubemap)
+         {
+            if (mCubemap->mCubemap)
+               mCubemap->updateFaces();
+            else
+               mCubemap->createMap();
+
+            mDirty = false;
+
+            mProbeInfo->mCubemap = &mCubemap->mCubemap;
+         }
+      }
+      else if (mReflectionModeType == StaticCubemap && !mCubemapName.isEmpty())
+      {
+         Sim::findObject(mCubemapName, mCubemap);
+
+         mProbeInfo->mCubemap = &mCubemap->mCubemap;
+      }
+   }
+   else if (mReflectionModeType == DynamicCubemap && !mDynamicCubemap.isNull())
+   {
+      mProbeInfo->mCubemap = &mDynamicCubemap;
+   }
+   
+   generateTextures();
+
+   if (mPrefilterMap.isValid())
+   {
+      mProbeInfo->mCubemap = &mPrefilterMap;
+      mProbeInfo->mIrradianceCubemap = &mIrridianceMap;
+      mProbeInfo->mBRDFTexture = &mBrdfTexture;
+   }
+   //calculateSHTerms();
+}
+
+bool ReflectionProbe::createClientResources()
+{
+   //irridiance resources
+   mIrridianceMap = GFX->createCubemap();
+   mIrridianceMap->initDynamic(128, GFXFormatR16G16B16A16F,1);
+
+   //prefilter resources - we share the irridiance stateblock
+   mPrefilterMap = GFX->createCubemap();
+   mPrefilterMap->initDynamic(mPrefilterSize, GFXFormatR16G16B16A16F, mPrefilterMipLevels);
+
+   //brdf lookup resources
+   //make the brdf lookup texture the same size as the prefilter texture
+   mBrdfTexture = TEXMGR->createTexture(mPrefilterSize, mPrefilterSize, GFXFormatR16G16B16A16F, &GFXRenderTargetProfile, 1, 0);
+
+   mResourcesCreated = true;
+
+   return true;
+}
+
+void ReflectionProbe::generateTextures()
+{
+   if (!mCubemap)
+      return;
+
+   if (!mResourcesCreated)
+   {
+      if (!createClientResources())
+      {
+         Con::errorf("SkyLight::createIrridianceMap: Failed to create resources");
+         return;
+      }
+   }
+
+   GFXTextureTargetRef renderTarget = GFX->allocRenderToTextureTarget(false);
+
+   //create irridiance cubemap
+   IBLUtilities::GenerateIrradianceMap(renderTarget, mCubemap->mCubemap, mIrridianceMap);
+
+   //create prefilter cubemap (radiance)
+   IBLUtilities::GeneratePrefilterMap(renderTarget, mCubemap->mCubemap, mPrefilterMipLevels, mPrefilterMap);
+
+   //create brdf lookup
+   IBLUtilities::GenerateBRDFTexture(mBrdfTexture);
+}
+
+void ReflectionProbe::prepRenderImage(SceneRenderState *state)
+{
+   if (!mEnabled || !ReflectionProbe::smRenderReflectionProbes)
+      return;
+
+   Point3F distVec = getRenderPosition() - state->getCameraPosition();
+   F32 dist = distVec.len();
+
+   //Culling distance. Can be adjusted for performance options considerations via the scalar
+   if (dist > mMaxDrawDistance * Con::getFloatVariable("$pref::GI::ProbeDrawDistScale", 1.0))
+      return;
+
+   if (mReflectionModeType == DynamicCubemap && mRefreshRateMS < (Platform::getRealMilliseconds() - mDynamicLastBakeMS))
+   {
+      bake("", 32);
+      mDynamicLastBakeMS = Platform::getRealMilliseconds();
+   }
+
+   //Submit our probe to actually do the probe action
+   // Get a handy pointer to our RenderPassmanager
+   //RenderPassManager *renderPass = state->getRenderPass();
+
+   //Update our score based on our radius, distance
+   mProbeInfo->mScore = mProbeInfo->mRadius/mMax(dist,1.0f);
+
+   Point3F vect = distVec;
+   vect.normalizeSafe();
+
+   mProbeInfo->mScore *= mMax(mAbs(mDot(vect, state->getCameraTransform().getForwardVector())),0.001f);
+
+   //Register
+   PROBEMGR->registerProbe(mProbeInfo, this);
+
+   if (ReflectionProbe::smRenderPreviewProbes && gEditingMission && mEditorShapeInst && mCubemap != nullptr)
+   {
+      GFXTransformSaver saver;
+
+      // Calculate the distance of this object from the camera
+      Point3F cameraOffset;
+      getRenderTransform().getColumn(3, &cameraOffset);
+      cameraOffset -= state->getDiffuseCameraPosition();
+      F32 dist = cameraOffset.len();
+      if (dist < 0.01f)
+         dist = 0.01f;
+
+      // Set up the LOD for the shape
+      F32 invScale = (1.0f / getMax(getMax(mObjScale.x, mObjScale.y), mObjScale.z));
+
+      mEditorShapeInst->setDetailFromDistance(state, dist * invScale);
+
+      // Make sure we have a valid level of detail
+      if (mEditorShapeInst->getCurrentDetail() < 0)
+         return;
+
+      BaseMatInstance* probePrevMat = mEditorShapeInst->getMaterialList()->getMaterialInst(0);
+
+      setPreviewMatParameters(state, probePrevMat);
+
+      // GFXTransformSaver is a handy helper class that restores
+      // the current GFX matrices to their original values when
+      // it goes out of scope at the end of the function
+
+      // Set up our TS render state      
+      TSRenderState rdata;
+      rdata.setSceneState(state);
+      rdata.setFadeOverride(1.0f);
+
+      if(mReflectionModeType != DynamicCubemap)
+         rdata.setCubemap(mCubemap->mCubemap);
+      else
+         rdata.setCubemap(mDynamicCubemap);
+
+      // We might have some forward lit materials
+      // so pass down a query to gather lights.
+      LightQuery query;
+      query.init(getWorldSphere());
+      rdata.setLightQuery(&query);
+
+      // Set the world matrix to the objects render transform
+      MatrixF mat = getRenderTransform();
+      mat.scale(Point3F(1, 1, 1));
+      GFX->setWorldMatrix(mat);
+
+      // Animate the the shape
+      mEditorShapeInst->animate();
+
+      // Allow the shape to submit the RenderInst(s) for itself
+      mEditorShapeInst->render(rdata);
+
+      saver.restore();
+   }
+
+   // If the light is selected or light visualization
+   // is enabled then register the callback.
+   const bool isSelectedInEditor = (gEditingMission && isSelected());
+   if (isSelectedInEditor)
+   {
+      ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
+      ri->renderDelegate.bind(this, &ReflectionProbe::_onRenderViz);
+      ri->type = RenderPassManager::RIT_Editor;
+      state->getRenderPass()->addInst(ri);
+   }
+}
+
+void ReflectionProbe::_onRenderViz(ObjectRenderInst *ri,
+   SceneRenderState *state,
+   BaseMatInstance *overrideMat)
+{
+   if (!ReflectionProbe::smRenderReflectionProbes)
+      return;
+
+   GFXDrawUtil *draw = GFX->getDrawUtil();
+
+   GFXStateBlockDesc desc;
+   desc.setZReadWrite(true, false);
+   desc.setCullMode(GFXCullNone);
+   desc.setBlend(true);
+
+   // Base the sphere color on the light color.
+   ColorI color = ColorI::WHITE;
+   color.alpha = 50;
+
+   if (mProbeShapeType == ProbeInfo::Sphere)
+   {
+      draw->drawSphere(desc, mRadius, getPosition(), color);
+   }
+   else
+   {
+      Box3F cube(mRadius);
+      cube.setCenter(getPosition());
+      draw->drawCube(desc, cube, color);
+   }
+}
+
+void ReflectionProbe::setPreviewMatParameters(SceneRenderState* renderState, BaseMatInstance* mat)
+{
+   if (!mat->getFeatures().hasFeature(MFT_isDeferred))
+      return;
+
+   //Set up the params
+   MaterialParameters *matParams = mat->getMaterialParameters();
+
+   //Get the deferred render target
+   NamedTexTarget* deferredTexTarget = NamedTexTarget::find("deferred");
+
+   GFXTextureObject *deferredTexObject = deferredTexTarget->getTexture();
+   if (!deferredTexObject) 
+      return;
+
+   GFX->setTexture(0, deferredTexObject);
+
+   //Set the cubemap
+   GFX->setCubeTexture(1, mCubemap->mCubemap);
+
+   //Set the invViewMat
+   MatrixSet &matrixSet = renderState->getRenderPass()->getMatrixSet();
+   const MatrixF &worldToCameraXfm = matrixSet.getWorldToCamera();
+
+   MaterialParameterHandle *invViewMat = mat->getMaterialParameterHandle("$invViewMat");
+
+   matParams->setSafe(invViewMat, worldToCameraXfm);
+}
+
+LinearColorF ReflectionProbe::decodeSH(Point3F normal)
+{
+   float x = normal.x;
+   float y = normal.y;
+   float z = normal.z;
+
+   LinearColorF l00 = mProbeInfo->mSHTerms[0];
+
+   LinearColorF l10 = mProbeInfo->mSHTerms[1];
+   LinearColorF l11 = mProbeInfo->mSHTerms[2];
+   LinearColorF l12 = mProbeInfo->mSHTerms[3];
+
+   LinearColorF l20 = mProbeInfo->mSHTerms[4];
+   LinearColorF l21 = mProbeInfo->mSHTerms[5];
+   LinearColorF l22 = mProbeInfo->mSHTerms[6];
+   LinearColorF l23 = mProbeInfo->mSHTerms[7];
+   LinearColorF l24 = mProbeInfo->mSHTerms[8];
+
+   LinearColorF result = (
+         l00 * mProbeInfo->mSHConstants[0] +
+
+         l12 * mProbeInfo->mSHConstants[1] * x +
+         l10 * mProbeInfo->mSHConstants[1] * y +
+         l11 * mProbeInfo->mSHConstants[1] * z +
+
+         l20 * mProbeInfo->mSHConstants[2] * x*y +
+         l21 * mProbeInfo->mSHConstants[2] * y*z +
+         l22 * mProbeInfo->mSHConstants[3] * (3.0*z*z - 1.0) +
+         l23 * mProbeInfo->mSHConstants[2] * x*z +
+         l24 * mProbeInfo->mSHConstants[4] * (x*x - y*y)
+      );
+
+   return LinearColorF(mMax(result.red, 0), mMax(result.green, 0), mMax(result.blue, 0));
+}
+
+MatrixF ReflectionProbe::getSideMatrix(U32 side)
+{
+   // Standard view that will be overridden below.
+   VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f);
+
+   switch (side)
+   {
+   case 0: // D3DCUBEMAP_FACE_POSITIVE_X:
+      vLookatPt = VectorF(1.0f, 0.0f, 0.0f);
+      vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+      break;
+   case 1: // D3DCUBEMAP_FACE_NEGATIVE_X:
+      vLookatPt = VectorF(-1.0f, 0.0f, 0.0f);
+      vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+      break;
+   case 2: // D3DCUBEMAP_FACE_POSITIVE_Y:
+      vLookatPt = VectorF(0.0f, 1.0f, 0.0f);
+      vUpVec = VectorF(0.0f, 0.0f, -1.0f);
+      break;
+   case 3: // D3DCUBEMAP_FACE_NEGATIVE_Y:
+      vLookatPt = VectorF(0.0f, -1.0f, 0.0f);
+      vUpVec = VectorF(0.0f, 0.0f, 1.0f);
+      break;
+   case 4: // D3DCUBEMAP_FACE_POSITIVE_Z:
+      vLookatPt = VectorF(0.0f, 0.0f, 1.0f);
+      vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+      break;
+   case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
+      vLookatPt = VectorF(0.0f, 0.0f, -1.0f);
+      vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+      break;
+   }
+
+   // create camera matrix
+   VectorF cross = mCross(vUpVec, vLookatPt);
+   cross.normalizeSafe();
+
+   MatrixF rotMat(true);
+   rotMat.setColumn(0, cross);
+   rotMat.setColumn(1, vLookatPt);
+   rotMat.setColumn(2, vUpVec);
+   //rotMat.inverse();
+
+   return rotMat;
+}
+
+F32 ReflectionProbe::harmonics(U32 termId, Point3F normal)
+{
+   F32 x = normal.x;
+   F32 y = normal.y;
+   F32 z = normal.z;
+
+   switch(termId)
+   {
+   case 0:
+      return 1.0;
+   case 1:
+      return y;
+   case 2:
+      return z;
+   case 3:
+      return x;
+   case 4:
+      return x*y;
+   case 5:
+      return y*z;
+   case 6:
+      return 3.0*z*z - 1.0;
+   case 7:
+      return x*z;
+   default:
+      return x*x - y*y;
+   }
+}
+
+LinearColorF ReflectionProbe::sampleSide(U32 termindex, U32 sideIndex)
+{
+   MatrixF sideRot = getSideMatrix(sideIndex);
+
+   LinearColorF result = LinearColorF::ZERO;
+   F32 divider = 0;
+
+   for (int y = 0; y<mCubemapResolution; y++)
+   {
+      for (int x = 0; x<mCubemapResolution; x++)
+      {
+         Point2F sidecoord = ((Point2F(x, y) + Point2F(0.5, 0.5)) / Point2F(mCubemapResolution, mCubemapResolution))*2.0 - Point2F(1.0, 1.0);
+         Point3F normal = Point3F(sidecoord.x, sidecoord.y, -1.0);
+         normal.normalize();
+
+         F32 minBrightness = Con::getFloatVariable("$pref::GI::Cubemap_Sample_MinBrightness", 0.001f);
+
+         LinearColorF texel = mCubeFaceBitmaps[sideIndex]->sampleTexel(y, x);
+         texel = LinearColorF(mMax(texel.red, minBrightness), mMax(texel.green, minBrightness), mMax(texel.blue, minBrightness)) * Con::getFloatVariable("$pref::GI::Cubemap_Gain", 1.5);
+
+         Point3F dir;
+         sideRot.mulP(normal, &dir);
+
+         result += texel * harmonics(termindex,dir) * -normal.z;
+         divider += -normal.z;
+      }
+   }
+
+   result /= divider;
+
+   return result;
+}
+
+//
+//SH Calculations
+// From http://sunandblackcat.com/tipFullView.php?l=eng&topicid=32&topic=Spherical-Harmonics-From-Cube-Texture
+// With shader decode logic from https://github.com/nicknikolov/cubemap-sh
+void ReflectionProbe::calculateSHTerms()
+{
+   if (!mCubemap || !mCubemap->mCubemap)
+      return;
+
+   const VectorF cubemapFaceNormals[6] =
+   {
+      // D3DCUBEMAP_FACE_POSITIVE_X:
+      VectorF(1.0f, 0.0f, 0.0f),
+      // D3DCUBEMAP_FACE_NEGATIVE_X:
+      VectorF(-1.0f, 0.0f, 0.0f),
+      // D3DCUBEMAP_FACE_POSITIVE_Y:
+      VectorF(0.0f, 1.0f, 0.0f),
+      // D3DCUBEMAP_FACE_NEGATIVE_Y:
+      VectorF(0.0f, -1.0f, 0.0f),
+      // D3DCUBEMAP_FACE_POSITIVE_Z:
+      VectorF(0.0f, 0.0f, 1.0f),
+      // D3DCUBEMAP_FACE_NEGATIVE_Z:
+      VectorF(0.0f, 0.0f, -1.0f),
+   };
+
+   mCubemapResolution = mCubemap->mCubemap->getSize();
+
+   for (U32 i = 0; i < 6; i++)
+   {
+      mCubeFaceBitmaps[i] = new GBitmap(mCubemapResolution, mCubemapResolution, false, GFXFormatR8G8B8);
+   }
+
+   //If we fail to parse the cubemap for whatever reason, we really can't continue
+   if (!CubemapSaver::getBitmaps(mCubemap->mCubemap, GFXFormatR8G8B8, mCubeFaceBitmaps))
+      return; 
+
+   //Set up our constants
+   F32 L0 = Con::getFloatVariable("$pref::GI::SH_Term_L0", 1.0f);
+   F32 L1 = Con::getFloatVariable("$pref::GI::SH_Term_L1", 1.8f);
+   F32 L2 = Con::getFloatVariable("$pref::GI::SH_Term_L2", 0.83f);
+   F32 L2m2_L2m1_L21 = Con::getFloatVariable("$pref::GI::SH_Term_L2m2", 2.9f);
+   F32 L20 = Con::getFloatVariable("$pref::GI::SH_Term_L20", 0.58f);
+   F32 L22 = Con::getFloatVariable("$pref::GI::SH_Term_L22", 1.1f);
+
+   mProbeInfo->mSHConstants[0] = L0;
+   mProbeInfo->mSHConstants[1] = L1;
+   mProbeInfo->mSHConstants[2] = L2 * L2m2_L2m1_L21;
+   mProbeInfo->mSHConstants[3] = L2 * L20;
+   mProbeInfo->mSHConstants[4] = L2 * L22;
+
+   for (U32 i = 0; i < 9; i++)
+   {
+      //Clear it, just to be sure
+      mProbeInfo->mSHTerms[i] = LinearColorF(0.f, 0.f, 0.f);
+
+      //Now, encode for each side
+      mProbeInfo->mSHTerms[i] = sampleSide(i, 0); //POS_X
+      mProbeInfo->mSHTerms[i] += sampleSide(i, 1); //NEG_X
+      mProbeInfo->mSHTerms[i] += sampleSide(i, 2); //POS_Y
+      mProbeInfo->mSHTerms[i] += sampleSide(i, 3); //NEG_Y
+      mProbeInfo->mSHTerms[i] += sampleSide(i, 4); //POS_Z
+      mProbeInfo->mSHTerms[i] += sampleSide(i, 5); //NEG_Z
+
+      //Average
+      mProbeInfo->mSHTerms[i] /= 6;
+   }
+
+   for (U32 i = 0; i < 6; i++)
+      SAFE_DELETE(mCubeFaceBitmaps[i]);
+
+   bool mExportSHTerms = false;
+   if (mExportSHTerms)
+   {
+      for (U32 f = 0; f < 6; f++)
+      {
+         char fileName[256];
+         dSprintf(fileName, 256, "%s%s_DecodedFaces_%d.png", mReflectionPath.c_str(),
+            mProbeUniqueID.c_str(), f);
+
+         LinearColorF color = decodeSH(cubemapFaceNormals[f]);
+
+         FileStream stream;
+         if (stream.open(fileName, Torque::FS::File::Write))
+         {
+            GBitmap bitmap(mCubemapResolution, mCubemapResolution, false, GFXFormatR8G8B8);
+
+            bitmap.fill(color.toColorI());
+
+            bitmap.writeBitmap("png", stream);
+         }
+      }
+
+      for (U32 f = 0; f < 9; f++)
+      {
+         char fileName[256];
+         dSprintf(fileName, 256, "%s%s_SHTerms_%d.png", mReflectionPath.c_str(),
+            mProbeUniqueID.c_str(), f);
+
+         LinearColorF color = mProbeInfo->mSHTerms[f];
+
+         FileStream stream;
+         if (stream.open(fileName, Torque::FS::File::Write))
+         {
+            GBitmap bitmap(mCubemapResolution, mCubemapResolution, false, GFXFormatR8G8B8);
+
+            bitmap.fill(color.toColorI());
+
+            bitmap.writeBitmap("png", stream);
+         }
+      }
+   }
+}
+
+F32 ReflectionProbe::texelSolidAngle(F32 aU, F32 aV, U32 width, U32 height)
+{
+   // transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)]
+   // ( 0.5 is for texel center addressing)
+   const F32 U = (2.0 * (aU + 0.5) / width) - 1.0;
+   const F32 V = (2.0 * (aV + 0.5) / height) - 1.0;
+
+   // shift from a demi texel, mean 1.0 / size  with U and V in [-1..1]
+   const F32 invResolutionW = 1.0 / width;
+   const F32 invResolutionH = 1.0 / height;
+
+   // U and V are the -1..1 texture coordinate on the current face.
+   // get projected area for this texel
+   const F32 x0 = U - invResolutionW;
+   const F32 y0 = V - invResolutionH;
+   const F32 x1 = U + invResolutionW;
+   const F32 y1 = V + invResolutionH;
+   const F32 angle = areaElement(x0, y0) - areaElement(x0, y1) - areaElement(x1, y0) + areaElement(x1, y1);
+
+   return angle;
+}
+
+F32 ReflectionProbe::areaElement(F32 x, F32 y) 
+{
+   return mAtan2(x * y, (F32)mSqrt(x * x + y * y + 1.0));
+}
+
+DefineEngineMethod(ReflectionProbe, postApply, void, (), ,
+   "A utility method for forcing a network update.\n")
+{
+   object->inspectPostApply();
+}
+
+void ReflectionProbe::bake(String outputPath, S32 resolution)
+{
+   GFXDEBUGEVENT_SCOPE(ReflectionProbe_Bake, ColorI::WHITE);
+
+   PostEffect *preCapture = dynamic_cast<PostEffect*>(Sim::findObject("AL_PreCapture"));
+   PostEffect *deferredShading = dynamic_cast<PostEffect*>(Sim::findObject("AL_DeferredShading"));
+   if (preCapture)
+      preCapture->enable();
+   if (deferredShading)
+      deferredShading->disable();
+
+   //if (mReflectionModeType == StaticCubemap || mReflectionModeType == BakedCubemap || mReflectionModeType == SkyLight)
+   {
+      if (!mCubemap)
+      {
+         mCubemap = new CubemapData();
+         mCubemap->registerObject();
+      }
+   }
+
+   if (mReflectionModeType == DynamicCubemap && mDynamicCubemap.isNull())
+   {
+      //mCubemap->createMap();
+      mDynamicCubemap = GFX->createCubemap();
+      mDynamicCubemap->initDynamic(resolution, GFXFormatR8G8B8);
+   }
+   else if (mReflectionModeType != DynamicCubemap)
+   {
+      if (mReflectionPath.isEmpty() || !mPersistentId)
+      {
+         if (!mPersistentId)
+            mPersistentId = getOrCreatePersistentId();
+
+         mReflectionPath = outputPath.c_str();
+
+         mProbeUniqueID = std::to_string(mPersistentId->getUUID().getHash()).c_str();
+      }
+   }
+
+   bool validCubemap = true;
+
+   // Save the current transforms so we can restore
+   // it for child control rendering below.
+   GFXTransformSaver saver;
+
+   //bool saveEditingMission = gEditingMission;
+   //gEditingMission = false;
+
+   //Set this to true to use the prior method where it goes through the SPT_Reflect path for the bake
+   bool probeRenderState = ReflectionProbe::smRenderReflectionProbes;
+   ReflectionProbe::smRenderReflectionProbes = false;
+   for (U32 i = 0; i < 6; ++i)
+   {
+      GFXTexHandle blendTex;
+      blendTex.set(resolution, resolution, GFXFormatR8G8B8A8, &GFXRenderTargetProfile, "");
+
+      GFXTextureTargetRef mBaseTarget = GFX->allocRenderToTextureTarget();
+
+      GFX->clearTextureStateImmediate(0);
+      if (mReflectionModeType == DynamicCubemap)
+         mBaseTarget->attachTexture(GFXTextureTarget::Color0, mDynamicCubemap, i);
+      else
+         mBaseTarget->attachTexture(GFXTextureTarget::Color0, blendTex);
+
+      // Standard view that will be overridden below.
+      VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f);
+
+      switch (i)
+      {
+         case 0: // D3DCUBEMAP_FACE_POSITIVE_X:
+            vLookatPt = VectorF(1.0f, 0.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+         case 1: // D3DCUBEMAP_FACE_NEGATIVE_X:
+            vLookatPt = VectorF(-1.0f, 0.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+         case 2: // D3DCUBEMAP_FACE_POSITIVE_Y:
+            vLookatPt = VectorF(0.0f, 1.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 0.0f, -1.0f);
+            break;
+         case 3: // D3DCUBEMAP_FACE_NEGATIVE_Y:
+            vLookatPt = VectorF(0.0f, -1.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 0.0f, 1.0f);
+            break;
+         case 4: // D3DCUBEMAP_FACE_POSITIVE_Z:
+            vLookatPt = VectorF(0.0f, 0.0f, 1.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+         case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
+            vLookatPt = VectorF(0.0f, 0.0f, -1.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+      }
+
+      // create camera matrix
+      VectorF cross = mCross(vUpVec, vLookatPt);
+      cross.normalizeSafe();
+
+      MatrixF matView(true);
+      matView.setColumn(0, cross);
+      matView.setColumn(1, vLookatPt);
+      matView.setColumn(2, vUpVec);
+      matView.setPosition(getPosition());
+      matView.inverse();
+
+      // set projection to 90 degrees vertical and horizontal
+      F32 left, right, top, bottom;
+      F32 nearPlane = 0.01f;
+      F32 farDist = 1000.f;
+
+      MathUtils::makeFrustum(&left, &right, &top, &bottom, M_HALFPI_F, 1.0f, nearPlane);
+      Frustum frustum(false, left, right, top, bottom, nearPlane, farDist);
+
+      renderFrame(&mBaseTarget, matView, frustum, StaticObjectType | StaticShapeObjectType & EDITOR_RENDER_TYPEMASK, gCanvasClearColor);
+
+      mBaseTarget->resolve();
+
+      mCubemap->setCubeFaceTexture(i, blendTex);
+   }
+
+      /*if (mReflectionModeType != DynamicCubemap)
+      {
+         char fileName[256];
+         dSprintf(fileName, 256, "%s%s_%i.png", mReflectionPath.c_str(),
+            mProbeUniqueID.c_str(), i);
+
+         FileStream stream;
+         if (!stream.open(fileName, Torque::FS::File::Write))
+         {
+            Con::errorf("ReflectionProbe::bake(): Couldn't open cubemap face file fo writing " + String(fileName));
+            if (preCapture)
+               preCapture->disable();
+            if (deferredShading)
+               deferredShading->enable();
+            return;
+         }
+
+         GBitmap bitmap(blendTex->getWidth(), blendTex->getHeight(), false, GFXFormatR8G8B8);
+         blendTex->copyToBmp(&bitmap);
+         bitmap.writeBitmap("png", stream);
+
+         if (Platform::isFile(fileName) && mCubemap)
+            mCubemap->setCubeFaceFile(i, FileName(fileName));
+         else
+            validCubemap = false;
+
+         bitmap.deleteImage();
+      }
+   }*/
+
+   if (mReflectionModeType != DynamicCubemap && validCubemap)
+   {
+      if (mCubemap->mCubemap)
+         mCubemap->updateFaces();
+      else
+         mCubemap->createMap();
+
+      char fileName[256];
+      dSprintf(fileName, 256, "%s%s.DDS", mReflectionPath.c_str(), mProbeUniqueID.c_str());
+
+      CubemapSaver::save(mCubemap->mCubemap, fileName);
+
+      if (!Platform::isFile(fileName))
+      {
+         validCubemap = false; //if we didn't save right, just 
+         Con::errorf("Failed to properly save out the skylight baked cubemap!");
+      }
+
+      mDirty = false;
+   }
+
+   //calculateSHTerms();
+
+   ReflectionProbe::smRenderReflectionProbes = probeRenderState;
+   setMaskBits(-1);
+
+   if (preCapture)
+      preCapture->disable();
+   if (deferredShading)
+      deferredShading->enable();
+}
+
+DefineEngineMethod(ReflectionProbe, Bake, void, (String outputPath, S32 resolution), ("", 256),
+   "@brief returns true if control object is inside the fog\n\n.")
+{
+   object->bake(outputPath, resolution);
+}

+ 278 - 0
Engine/source/T3D/lighting/reflectionProbe.h

@@ -0,0 +1,278 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#ifndef REFLECTIONPROBE_H
+#define REFLECTIONPROBE_H
+
+#ifndef _SCENEOBJECT_H_
+#include "scene/sceneObject.h"
+#endif
+#ifndef _GFXVERTEXBUFFER_H_
+#include "gfx/gfxVertexBuffer.h"
+#endif
+#ifndef _GFXPRIMITIVEBUFFER_H_
+#include "gfx/gfxPrimitiveBuffer.h"
+#endif
+#ifndef _TSSHAPEINSTANCE_H_
+#include "ts/tsShapeInstance.h"
+#endif
+#include "lighting/lightInfo.h"
+
+#ifndef _RENDERPASSMANAGER_H_
+#include "renderInstance/renderPassManager.h"
+#endif
+
+#ifndef PROBEMANAGER_H
+#include "lighting/probeManager.h"
+#endif
+
+class BaseMatInstance;
+
+
+//-----------------------------------------------------------------------------
+// This class implements a basic SceneObject that can exist in the world at a
+// 3D position and render itself. There are several valid ways to render an
+// object in Torque. This class implements the preferred rendering method which
+// is to submit a MeshRenderInst along with a Material, vertex buffer,
+// primitive buffer, and transform and allow the RenderMeshMgr handle the
+// actual setup and rendering for you.
+//-----------------------------------------------------------------------------
+
+class ReflectionProbe : public SceneObject
+{
+   typedef SceneObject Parent;
+
+public:
+
+   enum IndrectLightingModeType
+   {
+      NoIndirect = 0,            
+      AmbientColor = 1, 
+      SphericalHarmonics = 2
+   };
+
+   enum ReflectionModeType
+   {
+      NoReflection = 0,
+      StaticCubemap = 1,
+      BakedCubemap = 2,
+      DynamicCubemap = 5,
+   };
+
+private:
+
+   // Networking masks
+   // We need to implement a mask specifically to handle
+   // updating our transform from the server object to its
+   // client-side "ghost". We also need to implement a
+   // maks for handling editor updates to our properties
+   // (like material).
+   enum MaskBits
+   {
+      TransformMask = Parent::NextFreeMask << 0,
+      UpdateMask = Parent::NextFreeMask << 1,
+      EnabledMask = Parent::NextFreeMask << 2,
+      CubemapMask = Parent::NextFreeMask << 3,
+      ModeMask = Parent::NextFreeMask << 4,
+      RadiusMask = Parent::NextFreeMask << 5,
+      ShapeTypeMask = Parent::NextFreeMask << 6,
+      BakeInfoMask = Parent::NextFreeMask << 7,
+      NextFreeMask = Parent::NextFreeMask << 8
+   };
+
+   bool mBake;
+   bool mEnabled;
+   bool mDirty;
+
+   Resource<TSShape> mEditorShape;
+   TSShapeInstance* mEditorShapeInst;
+
+   //--------------------------------------------------------------------------
+   // Rendering variables
+   //--------------------------------------------------------------------------
+   ProbeInfo::ProbeShapeType mProbeShapeType;
+
+   ProbeInfo* mProbeInfo;
+
+   //Indirect Lighting Contribution stuff
+   IndrectLightingModeType mIndrectLightingModeType;
+   LinearColorF mAmbientColor;
+   LinearColorF mSphericalHarmonics;
+
+   //Reflection Contribution stuff
+   ReflectionModeType mReflectionModeType;
+
+   F32 mRadius;
+
+   String mCubemapName;
+   CubemapData *mCubemap;
+   GFXCubemapHandle  mDynamicCubemap;
+   bool mUseCubemap;
+
+   //irridiance resources
+   GFXCubemapHandle mIrridianceMap;
+
+   //prefilter resources
+   GFXCubemapHandle mPrefilterMap;
+   U32 mPrefilterMipLevels;
+   U32 mPrefilterSize;
+
+   //brdflookup resources - shares the texture target with the prefilter
+   GFXTexHandle mBrdfTexture;
+
+   String mReflectionPath;
+   String mProbeUniqueID;
+
+   // Define our vertex format here so we don't have to
+   // change it in multiple spots later
+   typedef GFXVertexPNTTB VertexType;
+
+   // The GFX vertex and primitive buffers
+   GFXVertexBufferHandle< VertexType > mVertexBuffer;
+   GFXPrimitiveBufferHandle            mPrimitiveBuffer;
+
+   U32 mSphereVertCount;
+   U32 mSpherePrimitiveCount;
+
+   //Debug rendering
+   static bool smRenderReflectionProbes;
+   static bool smRenderPreviewProbes;
+
+   U32 mDynamicLastBakeMS;
+   U32 mRefreshRateMS;
+
+   GBitmap* mCubeFaceBitmaps[6];
+   U32 mCubemapResolution;
+
+   F32 mMaxDrawDistance;
+
+   bool mResourcesCreated;
+
+public:
+   ReflectionProbe();
+   virtual ~ReflectionProbe();
+
+   // Declare this object as a ConsoleObject so that we can
+   // instantiate it into the world and network it
+   DECLARE_CONOBJECT(ReflectionProbe);
+
+   //--------------------------------------------------------------------------
+   // Object Editing
+   // Since there is always a server and a client object in Torque and we
+   // actually edit the server object we need to implement some basic
+   // networking functions
+   //--------------------------------------------------------------------------
+   // Set up any fields that we want to be editable (like position)
+   static void initPersistFields();
+
+   // Allows the object to update its editable settings
+   // from the server object to the client
+   virtual void inspectPostApply();
+
+   static bool _setEnabled(void *object, const char *index, const char *data);
+   static bool _doBake(void *object, const char *index, const char *data);
+
+   static bool protectedSetSHTerms(void *object, const char *index, const char *data);
+   static bool protectedSetSHConsts(void *object, const char *index, const char *data);
+
+   // Handle when we are added to the scene and removed from the scene
+   bool onAdd();
+   void onRemove();
+
+   virtual void writeFields(Stream &stream, U32 tabStop);
+   virtual bool writeField(StringTableEntry fieldname, const char *value);
+
+   // Override this so that we can dirty the network flag when it is called
+   void setTransform(const MatrixF &mat);
+
+   // This function handles sending the relevant data from the server
+   // object to the client object
+   U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
+   // This function handles receiving relevant data from the server
+   // object and applying it to the client object
+   void unpackUpdate(NetConnection *conn, BitStream *stream);
+
+   //--------------------------------------------------------------------------
+   // Object Rendering
+   // Torque utilizes a "batch" rendering system. This means that it builds a
+   // list of objects that need to render (via RenderInst's) and then renders
+   // them all in one batch. This allows it to optimized on things like
+   // minimizing texture, state, and shader switching by grouping objects that
+   // use the same Materials.
+   //--------------------------------------------------------------------------
+
+   // Create the geometry for rendering
+   void createGeometry();
+
+   // Get the Material instance
+   void updateMaterial();
+
+   void updateProbeParams();
+
+   bool createClientResources();
+   void generateTextures();
+
+   // This is the function that allows this object to submit itself for rendering
+   void prepRenderImage(SceneRenderState *state);
+
+   void _onRenderViz(ObjectRenderInst *ri,
+      SceneRenderState *state,
+      BaseMatInstance *overrideMat);
+
+   void setPreviewMatParameters(SceneRenderState* renderState, BaseMatInstance* mat);
+
+   //Spherical Harmonics
+   void calculateSHTerms();
+   F32 texelSolidAngle(F32 aU, F32 aV, U32 width, U32 height);
+   F32 areaElement(F32 x, F32 y);
+
+   //
+   MatrixF getSideMatrix(U32 side);
+   LinearColorF decodeSH(Point3F normal);
+
+   //
+   void calcDirectionVector(U32 face, U32 face_x, U32 face_y, F32& out_x, F32& out_y, F32& out_z) const;
+   F32 calcSolidAngle(U32 face, U32 x, U32 y) const;
+   LinearColorF sampleFace(U32 face, F32 s, F32 t);
+   LinearColorF readTexelClamped(U32 face, U32 x, U32 y);
+   void computeTexCoords(F32 x, F32 y, F32 z, U32& out_face, F32& out_s, F32& out_t);
+   LinearColorF readTexel(U32 face, U32 x, U32 y) const;
+
+   //
+   LinearColorF sampleSide(U32 termindex, U32 sideIndex);
+   F32 harmonics(U32 termId, Point3F normal);
+
+
+   //Baking
+   void bake(String outputPath, S32 resolution);
+};
+
+typedef ProbeInfo::ProbeShapeType ReflectProbeType;
+DefineEnumType(ReflectProbeType);
+
+typedef ReflectionProbe::IndrectLightingModeType IndrectLightingModeEnum;
+DefineEnumType(IndrectLightingModeEnum);
+
+typedef ReflectionProbe::ReflectionModeType ReflectionModeEnum;
+DefineEnumType(ReflectionModeEnum);
+
+#endif // _ReflectionProbe_H_

+ 899 - 0
Engine/source/T3D/lighting/skylight.cpp

@@ -0,0 +1,899 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "T3D/lighting/Skylight.h"
+#include "math/mathIO.h"
+#include "scene/sceneRenderState.h"
+#include "console/consoleTypes.h"
+#include "core/stream/bitStream.h"
+#include "materials/baseMatInstance.h"
+#include "console/engineAPI.h"
+#include "gfx/gfxDrawUtil.h"
+#include "gfx/gfxDebugEvent.h"
+#include "gfx/gfxTransformSaver.h"
+#include "math/mathUtils.h"
+#include "gfx/bitmap/gBitmap.h"
+#include "core/stream/fileStream.h"
+#include "core/fileObject.h"
+#include "core/resourceManager.h"
+#include "console/simPersistId.h"
+#include <string>
+#include "T3D/gameFunctions.h"
+#include "postFx/postEffect.h"
+#include "renderInstance/renderProbeMgr.h"
+#include "lighting/probeManager.h"
+
+#include "math/util/sphereMesh.h"
+#include "materials/materialManager.h"
+#include "math/util/matrixSet.h"
+#include "gfx/bitmap/cubemapSaver.h"
+
+#include "materials/materialFeatureTypes.h"
+
+#include "materials/shaderData.h"
+#include "gfx/gfxTextureManager.h"
+
+#include "gfx/bitmap/imageUtils.h"
+
+#include "T3D/lighting/IBLUtilities.h"
+
+extern bool gEditingMission;
+extern ColorI gCanvasClearColor;
+bool Skylight::smRenderSkylights = true;
+bool Skylight::smRenderPreviewProbes = true;
+
+IMPLEMENT_CO_NETOBJECT_V1(Skylight);
+
+ConsoleDocClass(Skylight,
+   "@brief An example scene object which renders a mesh.\n\n"
+   "This class implements a basic SceneObject that can exist in the world at a "
+   "3D position and render itself. There are several valid ways to render an "
+   "object in Torque. This class implements the preferred rendering method which "
+   "is to submit a MeshRenderInst along with a Material, vertex buffer, "
+   "primitive buffer, and transform and allow the RenderMeshMgr handle the "
+   "actual setup and rendering for you.\n\n"
+   "See the C++ code for implementation details.\n\n"
+   "@ingroup Examples\n");
+
+ImplementEnumType(SkylightReflectionModeEnum,
+   "Type of mesh data available in a shape.\n"
+   "@ingroup gameObjects")
+{ Skylight::StaticCubemap, "Static Cubemap", "Uses a static CubemapData" },
+{ Skylight::BakedCubemap, "Baked Cubemap", "Uses a cubemap baked from the probe's current position" },
+   EndImplementEnumType;
+
+//-----------------------------------------------------------------------------
+// Object setup and teardown
+//-----------------------------------------------------------------------------
+Skylight::Skylight()
+{
+   // Flag this object so that it will always
+   // be sent across the network to clients
+   mNetFlags.set(Ghostable | ScopeAlways);
+
+   mTypeMask = LightObjectType | MarkerObjectType;
+
+   mReflectionModeType = StaticCubemap;
+
+   mEnabled = true;
+   mBake = false;
+   mDirty = false;
+
+   mCubemap = NULL;
+   mReflectionPath = "";
+   mProbeUniqueID = "";
+
+   mEditorShapeInst = NULL;
+   mEditorShape = NULL;
+
+   mIrridianceMap = NULL;
+   mPrefilterMap = NULL;
+   mBrdfTexture = NULL;
+   mResourcesCreated = false;
+   mPrefilterSize = 512;
+   mPrefilterMipLevels = 6;
+
+   mProbeInfo = new ProbeInfo();
+}
+
+Skylight::~Skylight()
+{
+   if (mEditorShapeInst)
+      SAFE_DELETE(mEditorShapeInst);
+
+   if (mReflectionModeType != StaticCubemap && mCubemap)
+      mCubemap->deleteObject();
+}
+
+//-----------------------------------------------------------------------------
+// Object Editing
+//-----------------------------------------------------------------------------
+void Skylight::initPersistFields()
+{
+   addGroup("Rendering");
+      addProtectedField("enabled", TypeBool, Offset(mEnabled, Skylight),
+         &_setEnabled, &defaultProtectedGetFn, "Regenerate Voxel Grid");
+   endGroup("Rendering");
+
+   addGroup("Reflection");
+      //addField("ReflectionMode", TypeSkylightReflectionModeEnum, Offset(mReflectionModeType, Skylight),
+      //   "The type of mesh data to use for collision queries.");
+
+      //addField("reflectionPath", TypeImageFilename, Offset(mReflectionPath, Skylight),
+      //   "The type of mesh data to use for collision queries.");
+
+      addField("StaticCubemap", TypeCubemapName, Offset(mCubemapName, Skylight), "Cubemap used instead of reflection texture if fullReflect is off.");
+
+      //addProtectedField("Bake", TypeBool, Offset(mBake, Skylight),
+      //   &_doBake, &defaultProtectedGetFn, "Regenerate Voxel Grid", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
+   endGroup("Reflection");
+
+   Con::addVariable("$Light::renderSkylights", TypeBool, &Skylight::smRenderSkylights,
+      "Toggles rendering of light frustums when the light is selected in the editor.\n\n"
+      "@note Only works for shadow mapped lights.\n\n"
+      "@ingroup Lighting");
+
+   Con::addVariable("$Light::renderPreviewProbes", TypeBool, &Skylight::smRenderPreviewProbes,
+      "Toggles rendering of light frustums when the light is selected in the editor.\n\n"
+      "@note Only works for shadow mapped lights.\n\n"
+      "@ingroup Lighting");
+
+   // SceneObject already handles exposing the transform
+   Parent::initPersistFields();
+}
+
+void Skylight::inspectPostApply()
+{
+   Parent::inspectPostApply();
+
+   mDirty = true;
+
+   // Flag the network mask to send the updates
+   // to the client object
+   setMaskBits(-1);
+}
+
+bool Skylight::_setEnabled(void *object, const char *index, const char *data)
+{
+   Skylight* probe = reinterpret_cast< Skylight* >(object);
+
+   probe->mEnabled = dAtob(data);
+   probe->setMaskBits(-1);
+
+   return true;
+}
+
+bool Skylight::_doBake(void *object, const char *index, const char *data)
+{
+   Skylight* probe = reinterpret_cast< Skylight* >(object);
+
+   if (probe->mDirty)
+      probe->bake(probe->mReflectionPath, 256);
+
+   return false;
+}
+
+bool Skylight::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   mObjBox.minExtents.set(-1, -1, -1);
+   mObjBox.maxExtents.set(1, 1, 1);
+
+   // Skip our transform... it just dirties mask bits.
+   Parent::setTransform(mObjToWorld);
+
+   resetWorldBox();
+
+   // Add this object to the scene
+   addToScene();
+
+   if (isServerObject())
+   {
+      if (!mPersistentId)
+         mPersistentId = getOrCreatePersistentId();
+
+      mProbeUniqueID = std::to_string(mPersistentId->getUUID().getHash()).c_str();
+   }
+
+   // Refresh this object's material (if any)
+   if (isClientObject())
+      updateMaterial();
+  
+   setMaskBits(-1);
+
+   return true;
+}
+
+void Skylight::onRemove()
+{
+   // Remove this object from the scene
+   removeFromScene();
+
+   Parent::onRemove();
+}
+
+void Skylight::setTransform(const MatrixF & mat)
+{
+   // Let SceneObject handle all of the matrix manipulation
+   Parent::setTransform(mat);
+
+   mDirty = true;
+
+   // Dirty our network mask so that the new transform gets
+   // transmitted to the client object
+   setMaskBits(TransformMask);
+}
+
+U32 Skylight::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
+{
+   // Allow the Parent to get a crack at writing its info
+   U32 retMask = Parent::packUpdate(conn, mask, stream);
+
+   if (stream->writeFlag(mask & InitialUpdateMask))
+   {
+      //initial work, just in case?
+   }
+
+   // Write our transform information
+   if (stream->writeFlag(mask & TransformMask))
+   {
+      mathWrite(*stream, getTransform());
+      mathWrite(*stream, getScale());
+   }
+
+   /*if (stream->writeFlag(mask & BakeInfoMask))
+   {
+      stream->write(mReflectionPath);
+      stream->write(mProbeUniqueID);
+   }*/
+
+   if (stream->writeFlag(mask & EnabledMask))
+   {
+      stream->writeFlag(mEnabled);
+   }
+
+   /*if (stream->writeFlag(mask & ModeMask))
+   {
+      stream->write((U32)mReflectionModeType);
+   }*/
+
+   if (stream->writeFlag(mask & CubemapMask))
+   {
+      stream->write(mCubemapName);
+   }
+
+   return retMask;
+}
+
+void Skylight::unpackUpdate(NetConnection *conn, BitStream *stream)
+{
+   // Let the Parent read any info it sent
+   Parent::unpackUpdate(conn, stream);
+
+   if (stream->readFlag())
+   {
+      //some initial work?
+      createGeometry();
+   }
+
+   if (stream->readFlag())  // TransformMask
+   {
+      mathRead(*stream, &mObjToWorld);
+      mathRead(*stream, &mObjScale);
+
+      setTransform(mObjToWorld);
+   }
+
+   /*if (stream->readFlag())  // BakeInfoMask
+   {
+      stream->read(&mReflectionPath);
+      stream->read(&mProbeUniqueID);
+   }*/
+
+   if (stream->readFlag())  // EnabledMask
+   {
+      mEnabled = stream->readFlag();
+   }
+
+   bool isMaterialDirty = false;
+
+   /*if (stream->readFlag())  // ModeMask
+   {
+      U32 reflectModeType = StaticCubemap;
+      stream->read(&reflectModeType);
+      mReflectionModeType = (ReflectionModeType)reflectModeType;
+
+      isMaterialDirty = true;
+   }*/
+
+   if (stream->readFlag())  // CubemapMask
+   {
+      stream->read(&mCubemapName);
+
+      isMaterialDirty = true;
+   }
+
+   updateProbeParams();
+
+   if(isMaterialDirty)
+      updateMaterial();
+}
+
+void Skylight::createGeometry()
+{
+   // Clean up our previous shape
+   if (mEditorShapeInst)
+      SAFE_DELETE(mEditorShapeInst);
+   
+   mEditorShape = NULL;
+   
+   String shapeFile = "tools/resources/ReflectProbeSphere.dae";
+   
+   // Attempt to get the resource from the ResourceManager
+   mEditorShape = ResourceManager::get().load(shapeFile);
+   if (mEditorShape)
+   {
+      mEditorShapeInst = new TSShapeInstance(mEditorShape, isClientObject());
+   }
+}
+
+//-----------------------------------------------------------------------------
+// Object Rendering
+//-----------------------------------------------------------------------------
+
+void Skylight::updateProbeParams()
+{
+   if (mProbeInfo == nullptr)
+      return;
+
+   mProbeInfo->mIntensity = 1;
+
+   mProbeInfo->mAmbient = LinearColorF(0, 0, 0, 0);
+
+   mProbeInfo->mProbeShapeType = ProbeInfo::Sphere;
+
+   mProbeInfo->setPosition(getPosition());
+
+   //Update the bounds
+   mObjBox.minExtents.set(-1, -1, -1);
+   mObjBox.maxExtents.set(1, 1, 1);
+
+   // Skip our transform... it just dirties mask bits.
+   Parent::setTransform(mObjToWorld);
+
+   resetWorldBox();
+
+   F32 visDist = gClientSceneGraph->getVisibleDistance();
+   Box3F skylightBounds = Box3F(visDist * 2);
+
+   skylightBounds.setCenter(Point3F::Zero);
+
+   mProbeInfo->setPosition(Point3F::Zero);
+
+   mProbeInfo->mBounds = skylightBounds;
+
+   setGlobalBounds();
+
+   mProbeInfo->mIsSkylight = true; 
+   mProbeInfo->mScore = -1.0f; //sky comes first
+}
+
+bool Skylight::createClientResources()
+{
+   //irridiance resources
+   mIrridianceMap = GFX->createCubemap();
+   mIrridianceMap->initDynamic(128, GFXFormatR16G16B16A16F, 1);
+
+   //prefilter resources - we share the irridiance stateblock
+   mPrefilterMap = GFX->createCubemap();
+   mPrefilterMap->initDynamic(mPrefilterSize, GFXFormatR16G16B16A16F, mPrefilterMipLevels);
+
+   //brdf lookup resources
+   //make the brdf lookup texture the same size as the prefilter texture
+   mBrdfTexture = TEXMGR->createTexture(mPrefilterSize, mPrefilterSize, GFXFormatR16G16B16A16F, &GFXRenderTargetProfile, 1, 0);
+
+   mResourcesCreated = true;
+
+   return true;
+}
+
+void Skylight::updateMaterial()
+{
+   if ((mReflectionModeType == BakedCubemap) && !mProbeUniqueID.isEmpty())
+   {
+      bool validCubemap = true;
+
+      char fileName[256];
+      dSprintf(fileName, 256, "%s%s.DDS", mReflectionPath.c_str(), mProbeUniqueID.c_str());
+
+      Vector<FileName> fileNames;
+
+      if (Platform::isFile(fileName))
+      {
+         if (!mCubemap)
+         {
+            mCubemap = new CubemapData();
+            mCubemap->registerObject();
+         }
+
+         mCubemap->setCubemapFile(FileName(fileName));
+      }
+      else
+      {
+         validCubemap = false;
+      }
+
+      if (validCubemap)
+      {
+         if (mCubemap->mCubemap)
+            mCubemap->updateFaces();
+         else
+            mCubemap->createMap();
+
+         mDirty = false;
+
+         mProbeInfo->mCubemap = &mCubemap->mCubemap;
+      }
+
+      /*for (U32 i = 0; i < 6; ++i)
+      {
+         char faceFile[256];
+         dSprintf(faceFile, sizeof(faceFile), "%s%s_%i.png", mReflectionPath.c_str(),
+            mProbeUniqueID.c_str(), i);
+
+         if (Platform::isFile(faceFile))
+         {
+            fileNames.push_back(FileName(faceFile));
+         }
+         else
+         {
+            validCubemap = false;
+            break;
+         }
+      }
+
+      if (validCubemap)
+      {
+         if (!mCubemap)
+         {
+            mCubemap = new CubemapData();
+            mCubemap->registerObject();
+         }
+
+         for(U32 i=0; i < 6; i++)
+            mCubemap->setCubeFaceFile(i, fileNames[i]);
+
+         mCubemap->createMap();
+         mCubemap->updateFaces();
+
+         mProbeInfo->mCubemap = &mCubemap->mCubemap;
+      }*/
+   }
+   else if (mReflectionModeType == StaticCubemap && !mCubemapName.isEmpty())
+   {
+      Sim::findObject(mCubemapName, mCubemap);
+
+      if (!mCubemap)
+         return;
+
+      if (mCubemap->mCubemap)
+         mCubemap->updateFaces();
+      else
+         mCubemap->createMap();
+
+      mProbeInfo->mCubemap = &mCubemap->mCubemap;
+   }
+
+   //calculateSHTerms();
+
+   generateTextures();
+
+   //Now that the work is done, assign the relevent maps
+   if (mPrefilterMap.isValid())
+   {
+      mProbeInfo->mCubemap = &mPrefilterMap;
+      mProbeInfo->mIrradianceCubemap = &mIrridianceMap;
+      mProbeInfo->mBRDFTexture = &mBrdfTexture;
+   }
+}
+
+void Skylight::generateTextures()
+{
+   if (!mCubemap)
+      return;
+
+   if (!mResourcesCreated)
+   {
+      if (!createClientResources())
+      {
+         Con::errorf("SkyLight::createIrridianceMap: Failed to create resources");
+         return;
+      }
+   }
+
+   //GFXTransformSaver saver;
+
+   GFXTextureTargetRef renderTarget = GFX->allocRenderToTextureTarget(false);
+
+   IBLUtilities::GenerateIrradianceMap(renderTarget, mCubemap->mCubemap, mIrridianceMap);
+
+   //Write it out
+   char fileName[256];
+   dSprintf(fileName, 256, "levels/test/irradiance.DDS");
+
+   CubemapSaver::save(mIrridianceMap, fileName);
+
+   if (!Platform::isFile(fileName))
+   {
+      Con::errorf("Failed to properly save out the skylight baked irradiance!");
+   }
+
+   //create prefilter cubemap (radiance)
+   IBLUtilities::GeneratePrefilterMap(renderTarget, mCubemap->mCubemap, mPrefilterMipLevels, mPrefilterMap);
+
+   //Write it out
+   fileName[256];
+   dSprintf(fileName, 256, "levels/test/prefilter.DDS");
+
+   CubemapSaver::save(mPrefilterMap, fileName);
+
+   if (!Platform::isFile(fileName))
+   {
+      Con::errorf("Failed to properly save out the skylight baked irradiance!");
+   }
+
+   //create brdf lookup
+   IBLUtilities::GenerateBRDFTexture(mBrdfTexture);
+
+   /*FileStream fs;
+   if (fs.open("levels/test/brdf.DDS", Torque::FS::File::Write))
+   {
+      // Read back the render target, dxt compress it, and write it to disk.
+      GBitmap brdfBmp(mBrdfTexture.getHeight(), mBrdfTexture.getWidth(), false, GFXFormatR8G8B8A8);
+      mBrdfTexture.copyToBmp(&brdfBmp);
+
+      brdfBmp.extrudeMipLevels();
+
+      DDSFile *brdfDDS = DDSFile::createDDSFileFromGBitmap(&brdfBmp);
+      ImageUtil::ddsCompress(brdfDDS, GFXFormatBC1);
+
+      // Write result to file stream
+      brdfDDS->write(fs);
+
+      delete brdfDDS;
+   }
+   fs.close();*/
+}
+
+void Skylight::prepRenderImage(SceneRenderState *state)
+{
+   if (!mEnabled || !Skylight::smRenderSkylights)
+      return;
+
+   Point3F distVec = getPosition() - state->getCameraPosition();
+   F32 dist = distVec.len();
+
+   //special hook-in for skylights
+   Point3F camPos = state->getCameraPosition();
+   mProbeInfo->mBounds.setCenter(camPos);
+
+   mProbeInfo->setPosition(camPos);
+
+   //Submit our probe to actually do the probe action
+   // Get a handy pointer to our RenderPassmanager
+   //RenderPassManager *renderPass = state->getRenderPass();
+
+   PROBEMGR->registerSkylight(mProbeInfo, this);
+
+   if (Skylight::smRenderPreviewProbes && gEditingMission && mEditorShapeInst && mCubemap != nullptr)
+   {
+      GFXTransformSaver saver;
+
+      // Calculate the distance of this object from the camera
+      Point3F cameraOffset;
+      getRenderTransform().getColumn(3, &cameraOffset);
+      cameraOffset -= state->getDiffuseCameraPosition();
+      F32 dist = cameraOffset.len();
+      if (dist < 0.01f)
+         dist = 0.01f;
+
+      // Set up the LOD for the shape
+      F32 invScale = (1.0f / getMax(getMax(mObjScale.x, mObjScale.y), mObjScale.z));
+
+      mEditorShapeInst->setDetailFromDistance(state, dist * invScale);
+
+      // Make sure we have a valid level of detail
+      if (mEditorShapeInst->getCurrentDetail() < 0)
+         return;
+
+      BaseMatInstance* probePrevMat = mEditorShapeInst->getMaterialList()->getMaterialInst(0);
+
+      setPreviewMatParameters(state, probePrevMat);
+
+      // GFXTransformSaver is a handy helper class that restores
+      // the current GFX matrices to their original values when
+      // it goes out of scope at the end of the function
+
+      // Set up our TS render state      
+      TSRenderState rdata;
+      rdata.setSceneState(state);
+      rdata.setFadeOverride(1.0f);
+
+      // We might have some forward lit materials
+      // so pass down a query to gather lights.
+      LightQuery query;
+      query.init(getWorldSphere());
+      rdata.setLightQuery(&query);
+
+      // Set the world matrix to the objects render transform
+      MatrixF mat = getRenderTransform();
+      mat.scale(Point3F(1, 1, 1));
+      GFX->setWorldMatrix(mat);
+
+      // Animate the the shape
+      mEditorShapeInst->animate();
+
+      // Allow the shape to submit the RenderInst(s) for itself
+      mEditorShapeInst->render(rdata);
+
+      saver.restore();
+   }
+
+   // If the light is selected or light visualization
+   // is enabled then register the callback.
+   const bool isSelectedInEditor = (gEditingMission && isSelected());
+   if (isSelectedInEditor)
+   {
+   }
+}
+
+void Skylight::setPreviewMatParameters(SceneRenderState* renderState, BaseMatInstance* mat)
+{
+   if (!mat->getFeatures().hasFeature(MFT_isDeferred))
+      return;
+
+   //Set up the params
+   MaterialParameters *matParams = mat->getMaterialParameters();
+
+   //Get the deferred render target
+   NamedTexTarget* deferredTexTarget = NamedTexTarget::find("deferred");
+
+   GFXTextureObject *deferredTexObject = deferredTexTarget->getTexture();
+   if (!deferredTexObject) 
+      return;
+
+   GFX->setTexture(0, deferredTexObject);
+
+   //Set the cubemap
+   GFX->setCubeTexture(1, mCubemap->mCubemap);
+
+   //Set the invViewMat
+   MatrixSet &matrixSet = renderState->getRenderPass()->getMatrixSet();
+   const MatrixF &worldToCameraXfm = matrixSet.getWorldToCamera();
+
+   MaterialParameterHandle *invViewMat = mat->getMaterialParameterHandle("$invViewMat");
+
+   matParams->setSafe(invViewMat, worldToCameraXfm);
+}
+
+DefineEngineMethod(Skylight, postApply, void, (), ,
+   "A utility method for forcing a network update.\n")
+{
+   object->inspectPostApply();
+}
+
+void Skylight::bake(String outputPath, S32 resolution)
+{
+   GFXDEBUGEVENT_SCOPE(Skylight_Bake, ColorI::WHITE);
+
+   PostEffect *preCapture = dynamic_cast<PostEffect*>(Sim::findObject("AL_PreCapture"));
+   PostEffect *deferredShading = dynamic_cast<PostEffect*>(Sim::findObject("AL_DeferredShading"));
+   if (preCapture)
+      preCapture->enable();
+   if (deferredShading)
+      deferredShading->disable();
+
+   //if (mReflectionModeType == StaticCubemap || mReflectionModeType == BakedCubemap || mReflectionModeType == SkyLight)
+   {
+      if (!mCubemap)
+      {
+         mCubemap = new CubemapData();
+         mCubemap->registerObject();
+      }
+   }
+
+   if (mReflectionModeType == BakedCubemap)
+   {
+      if (mReflectionPath.isEmpty() || !mPersistentId)
+      {
+         if (!mPersistentId)
+            mPersistentId = getOrCreatePersistentId();
+
+         mReflectionPath = outputPath.c_str();
+
+         mProbeUniqueID = std::to_string(mPersistentId->getUUID().getHash()).c_str();
+      }
+   }
+
+   bool validCubemap = true;
+
+   // Save the current transforms so we can restore
+   // it for child control rendering below.
+   GFXTransformSaver saver;
+
+   //bool saveEditingMission = gEditingMission;
+   //gEditingMission = false;
+
+   //Set this to true to use the prior method where it goes through the SPT_Reflect path for the bake
+   bool probeRenderState = Skylight::smRenderSkylights;
+   Skylight::smRenderSkylights = false;
+   for (U32 i = 0; i < 6; ++i)
+   {
+      GFXTexHandle blendTex;
+      blendTex.set(resolution, resolution, GFXFormatR8G8B8A8, &GFXRenderTargetProfile, "");
+
+      GFXTextureTargetRef mBaseTarget = GFX->allocRenderToTextureTarget();
+
+      GFX->clearTextureStateImmediate(0);
+      mBaseTarget->attachTexture(GFXTextureTarget::Color0, blendTex);
+
+      // Standard view that will be overridden below.
+      VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f);
+
+      switch (i)
+      {
+         case 0: // D3DCUBEMAP_FACE_POSITIVE_X:
+            vLookatPt = VectorF(1.0f, 0.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+         case 1: // D3DCUBEMAP_FACE_NEGATIVE_X:
+            vLookatPt = VectorF(-1.0f, 0.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+         case 2: // D3DCUBEMAP_FACE_POSITIVE_Y:
+            vLookatPt = VectorF(0.0f, 1.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 0.0f, -1.0f);
+            break;
+         case 3: // D3DCUBEMAP_FACE_NEGATIVE_Y:
+            vLookatPt = VectorF(0.0f, -1.0f, 0.0f);
+            vUpVec = VectorF(0.0f, 0.0f, 1.0f);
+            break;
+         case 4: // D3DCUBEMAP_FACE_POSITIVE_Z:
+            vLookatPt = VectorF(0.0f, 0.0f, 1.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+         case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
+            vLookatPt = VectorF(0.0f, 0.0f, -1.0f);
+            vUpVec = VectorF(0.0f, 1.0f, 0.0f);
+            break;
+      }
+
+      // create camera matrix
+      VectorF cross = mCross(vUpVec, vLookatPt);
+      cross.normalizeSafe();
+
+      MatrixF matView(true);
+      matView.setColumn(0, cross);
+      matView.setColumn(1, vLookatPt);
+      matView.setColumn(2, vUpVec);
+      matView.setPosition(getPosition());
+      matView.inverse();
+
+      // set projection to 90 degrees vertical and horizontal
+      F32 left, right, top, bottom;
+      F32 nearPlane = 100.f;
+      F32 farDist = 10000.f;
+
+      MathUtils::makeFrustum(&left, &right, &top, &bottom, M_HALFPI_F, 1.0f, nearPlane);
+      Frustum frustum(false, left, right, top, bottom, nearPlane, farDist);
+
+      renderFrame(&mBaseTarget, matView, frustum, StaticObjectType | StaticShapeObjectType & EDITOR_RENDER_TYPEMASK, ColorI::RED);
+
+      mBaseTarget->resolve();
+
+      mCubemap->setCubeFaceTexture(i, blendTex);
+
+      char fileName[256];
+      dSprintf(fileName, 256, "%s%s_%i.png", mReflectionPath.c_str(),
+         mProbeUniqueID.c_str(), i);
+
+      FileStream stream;
+      if (!stream.open(fileName, Torque::FS::File::Write))
+      {
+         Con::errorf("ReflectionProbe::bake(): Couldn't open cubemap face file fo writing " + String(fileName));
+         if (preCapture)
+            preCapture->disable();
+         if (deferredShading)
+            deferredShading->enable();
+         return;
+      }
+
+      GBitmap bitmap(blendTex->getWidth(), blendTex->getHeight(), false, GFXFormatR8G8B8);
+      blendTex->copyToBmp(&bitmap);
+      bitmap.writeBitmap("png", stream);
+
+      if (Platform::isFile(fileName) && mCubemap)
+      {
+         mCubemap->setCubeFaceFile(i, FileName(fileName));
+      }
+      else
+      {
+         validCubemap = false;
+         break;
+      }
+
+      bitmap.deleteImage();
+   }
+
+   if (validCubemap)
+   {
+      if (mCubemap->mCubemap)
+         mCubemap->updateFaces();
+      else
+         mCubemap->createMap();
+
+      char fileName[256];
+      dSprintf(fileName, 256, "%s%s.DDS", mReflectionPath.c_str(), mProbeUniqueID.c_str());
+
+      CubemapSaver::save(mCubemap->mCubemap, fileName);
+
+      if (!Platform::isFile(fileName))
+      {
+         validCubemap = false; //if we didn't save right, just 
+         Con::errorf("Failed to properly save out the skylight baked cubemap!");
+      }
+   }
+
+   if (validCubemap)
+   {
+      mDirty = false;
+
+      //remove the temp files
+      for (U32 i = 0; i < 6; i++)
+      {
+         char fileName[256];
+         dSprintf(fileName, 256, "%s%s_%i.png", mReflectionPath.c_str(),
+            mProbeUniqueID.c_str(), i);
+
+         Platform::fileDelete(fileName);
+      }
+   }
+
+   //calculateSHTerms();
+
+   Skylight::smRenderSkylights = probeRenderState;
+   setMaskBits(-1);
+
+   if (preCapture)
+      preCapture->disable();
+
+   if (deferredShading)
+      deferredShading->enable();
+}
+
+DefineEngineMethod(Skylight, Bake, void, (String outputPath, S32 resolution), ("", 256),
+   "@brief returns true if control object is inside the fog\n\n.")
+{
+   object->bake(outputPath, resolution);
+}

+ 212 - 0
Engine/source/T3D/lighting/skylight.h

@@ -0,0 +1,212 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#ifndef SKYLIGHT_H
+#define SKYLIGHT_H
+
+#ifndef _SCENEOBJECT_H_
+#include "scene/sceneObject.h"
+#endif
+#ifndef _GFXVERTEXBUFFER_H_
+#include "gfx/gfxVertexBuffer.h"
+#endif
+#ifndef _GFXPRIMITIVEBUFFER_H_
+#include "gfx/gfxPrimitiveBuffer.h"
+#endif
+#ifndef _TSSHAPEINSTANCE_H_
+#include "ts/tsShapeInstance.h"
+#endif
+#include "lighting/lightInfo.h"
+
+#ifndef _RENDERPASSMANAGER_H_
+#include "renderInstance/renderPassManager.h"
+#endif
+
+#ifndef PROBEMANAGER_H
+#include "lighting/probeManager.h"
+#endif
+
+class BaseMatInstance;
+
+
+//-----------------------------------------------------------------------------
+// This class implements a basic SceneObject that can exist in the world at a
+// 3D position and render itself. There are several valid ways to render an
+// object in Torque. This class implements the preferred rendering method which
+// is to submit a MeshRenderInst along with a Material, vertex buffer,
+// primitive buffer, and transform and allow the RenderMeshMgr handle the
+// actual setup and rendering for you.
+//-----------------------------------------------------------------------------
+
+class Skylight : public SceneObject
+{
+   typedef SceneObject Parent;
+
+public:
+
+   enum IndrectLightingModeType
+   {
+      NoIndirect = 0,            
+      AmbientColor = 1, 
+      SphericalHarmonics = 2
+   };
+
+   enum ReflectionModeType
+   {
+      StaticCubemap = 1,
+      BakedCubemap = 2
+   };
+
+private:
+
+   // Networking masks
+   // We need to implement a mask specifically to handle
+   // updating our transform from the server object to its
+   // client-side "ghost". We also need to implement a
+   // maks for handling editor updates to our properties
+   // (like material).
+   enum MaskBits
+   {
+      TransformMask = Parent::NextFreeMask << 0,
+      UpdateMask = Parent::NextFreeMask << 1,
+      EnabledMask = Parent::NextFreeMask << 2,
+      CubemapMask = Parent::NextFreeMask << 3,
+      ModeMask = Parent::NextFreeMask << 4,
+      RadiusMask = Parent::NextFreeMask << 5,
+      ShapeTypeMask = Parent::NextFreeMask << 6,
+      BakeInfoMask = Parent::NextFreeMask << 7,
+      NextFreeMask = Parent::NextFreeMask << 8
+   };
+
+   bool mBake;
+   bool mEnabled;
+   bool mDirty;
+
+   Resource<TSShape> mEditorShape;
+   TSShapeInstance* mEditorShapeInst;
+
+   //--------------------------------------------------------------------------
+   // Rendering variables
+   //--------------------------------------------------------------------------
+   ProbeInfo* mProbeInfo;
+
+   //Reflection Contribution stuff
+   ReflectionModeType mReflectionModeType;
+
+   String mCubemapName;
+   CubemapData *mCubemap;
+
+   String mReflectionPath;
+   String mProbeUniqueID;
+
+   //Debug rendering
+   static bool smRenderSkylights;
+   static bool smRenderPreviewProbes;
+
+   //irridiance resources
+   GFXCubemapHandle mIrridianceMap;
+
+   //prefilter resources
+   GFXCubemapHandle mPrefilterMap;
+   U32 mPrefilterMipLevels;
+   U32 mPrefilterSize;
+
+   //brdflookup resources - shares the texture target with the prefilter
+   GFXTexHandle mBrdfTexture;
+
+   bool mResourcesCreated;
+
+public:
+   Skylight();
+   virtual ~Skylight();
+
+   // Declare this object as a ConsoleObject so that we can
+   // instantiate it into the world and network it
+   DECLARE_CONOBJECT(Skylight);
+
+   //--------------------------------------------------------------------------
+   // Object Editing
+   // Since there is always a server and a client object in Torque and we
+   // actually edit the server object we need to implement some basic
+   // networking functions
+   //--------------------------------------------------------------------------
+   // Set up any fields that we want to be editable (like position)
+   static void initPersistFields();
+
+   // Allows the object to update its editable settings
+   // from the server object to the client
+   virtual void inspectPostApply();
+
+   static bool _setEnabled(void *object, const char *index, const char *data);
+   static bool _doBake(void *object, const char *index, const char *data);
+
+   // Handle when we are added to the scene and removed from the scene
+   bool onAdd();
+   void onRemove();
+
+   // Override this so that we can dirty the network flag when it is called
+   void setTransform(const MatrixF &mat);
+
+   // This function handles sending the relevant data from the server
+   // object to the client object
+   U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
+   // This function handles receiving relevant data from the server
+   // object and applying it to the client object
+   void unpackUpdate(NetConnection *conn, BitStream *stream);
+
+   //--------------------------------------------------------------------------
+   // Object Rendering
+   // Torque utilizes a "batch" rendering system. This means that it builds a
+   // list of objects that need to render (via RenderInst's) and then renders
+   // them all in one batch. This allows it to optimized on things like
+   // minimizing texture, state, and shader switching by grouping objects that
+   // use the same Materials.
+   //--------------------------------------------------------------------------
+
+   // Create the geometry for rendering
+   void createGeometry();
+
+   bool createClientResources();
+
+   // Get the Material instance
+   void updateMaterial();
+
+   void generateTextures();
+
+   void updateProbeParams();
+
+   // This is the function that allows this object to submit itself for rendering
+   void prepRenderImage(SceneRenderState *state);
+
+   void setPreviewMatParameters(SceneRenderState* renderState, BaseMatInstance* mat);
+
+    //Baking
+   void bake(String outputPath, S32 resolution);
+};
+
+typedef Skylight::IndrectLightingModeType SkylightIndrectLightingModeEnum;
+DefineEnumType(SkylightIndrectLightingModeEnum);
+
+typedef Skylight::ReflectionModeType SkylightReflectionModeEnum;
+DefineEnumType(SkylightReflectionModeEnum);
+
+#endif // _Skylight_H_

+ 0 - 1
Engine/source/gfx/gfxEnums.h

@@ -497,7 +497,6 @@ enum GFXTextureTransformFlags
 // CodeReview: This number is used for the declaration of variables, but it
 // should *not* be used for any run-time purposes [7/2/2007 Pat]
 #define TEXTURE_STAGE_COUNT 32
-#define TEXTURE_STAGE_COUNT 16
 
 enum GFXSamplerState 
 {

+ 23 - 2
Engine/source/lighting/advanced/advancedLightBinManager.cpp

@@ -40,9 +40,10 @@
 #include "gfx/gfxDebugEvent.h"
 #include "math/util/matrixSet.h"
 #include "console/consoleTypes.h"
+#include "gfx/gfxTextureManager.h"
 
-const RenderInstType AdvancedLightBinManager::RIT_LightInfo( "directLighting" );
-const String AdvancedLightBinManager::smBufferName( "directLighting" );
+const RenderInstType AdvancedLightBinManager::RIT_LightInfo( "specularLighting" );
+const String AdvancedLightBinManager::smBufferName( "specularLighting" );
 
 ShadowFilterMode AdvancedLightBinManager::smShadowFilterMode = ShadowFilterMode_SoftShadowHighQuality;
 bool AdvancedLightBinManager::smPSSMDebugRender = false;
@@ -180,6 +181,26 @@ bool AdvancedLightBinManager::setTargetSize(const Point2I &newTargetSize)
    return ret;
 }
 
+bool AdvancedLightBinManager::_updateTargets()
+{
+   PROFILE_SCOPE(AdvancedLightBinManager_updateTargets);
+
+   bool ret = Parent::_updateTargets();
+
+   mDiffuseLightingTarget = NamedTexTarget::find("diffuseLighting");
+   if (mDiffuseLightingTarget.isValid())
+   {
+      mDiffuseLightingTex = mDiffuseLightingTarget->getTexture();
+
+      for (U32 i = 0; i < mTargetChainLength; i++)
+         mTargetChain[i]->attachTexture(GFXTextureTarget::Color1, mDiffuseLightingTex);
+   }
+
+   GFX->finalizeReset();
+
+   return ret;
+}
+
 void AdvancedLightBinManager::addLight( LightInfo *light )
 {
    // Get the light type.

+ 4 - 0
Engine/source/lighting/advanced/advancedLightBinManager.h

@@ -90,6 +90,9 @@ public:
    // registered buffer name
    static const String smBufferName;
 
+   NamedTexTargetRef mDiffuseLightingTarget;
+   GFXTexHandle      mDiffuseLightingTex;
+
    /// The shadow filter mode to use on shadowed light materials.
    static ShadowFilterMode smShadowFilterMode;
 
@@ -128,6 +131,7 @@ public:
    bool MRTLightmapsDuringDeferred() const { return mMRTLightmapsDuringDeferred; }
    void MRTLightmapsDuringDeferred(bool val);
 
+   bool _updateTargets();
 
    typedef Signal<void(SceneRenderState *, AdvancedLightBinManager *)> RenderSignal;
    static RenderSignal &getRenderSignal();

+ 2 - 0
Engine/source/lighting/advanced/advancedLightingFeatures.cpp

@@ -63,6 +63,7 @@ void AdvancedLightingFeatures::registerFeatures( const GFXFormat &deferredTarget
       FEATUREMGR->registerFeature(MFT_PixSpecular, new DeferredPixelSpecularGLSL());
       FEATUREMGR->registerFeature(MFT_MinnaertShading, new DeferredMinnaertGLSL());
       FEATUREMGR->registerFeature(MFT_SubSurface, new DeferredSubSurfaceGLSL());
+      FEATUREMGR->registerFeature(MFT_ReflectionProbes, new ReflectionProbeFeatGLSL);
 #endif
    }
    else
@@ -75,6 +76,7 @@ void AdvancedLightingFeatures::registerFeatures( const GFXFormat &deferredTarget
       FEATUREMGR->registerFeature(MFT_PixSpecular, new DeferredPixelSpecularHLSL());
       FEATUREMGR->registerFeature(MFT_MinnaertShading, new DeferredMinnaertHLSL());
       FEATUREMGR->registerFeature(MFT_SubSurface, new DeferredSubSurfaceHLSL());
+      FEATUREMGR->registerFeature(MFT_ReflectionProbes, new ReflectionProbeFeatHLSL);
 #endif
    }
 

+ 3 - 3
Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.cpp

@@ -98,7 +98,7 @@ void DeferredRTLightingFeatGLSL::processPix( Vector<ShaderComponent*> &component
    uvScene->setName( "uvScene" );
    LangElement *uvSceneDecl = new DecOp( uvScene );
 
-   String rtParamName = String::ToString( "rtParams%s", "directLightingBuffer" );
+   String rtParamName = String::ToString( "rtParams%s", "diffuseLightingBuffer" );
    Var *rtParams = (Var*) LangElement::find( rtParamName );
    if( !rtParams )
    {
@@ -121,7 +121,7 @@ void DeferredRTLightingFeatGLSL::processPix( Vector<ShaderComponent*> &component
    // create texture var
    Var *lightInfoBuffer = new Var;
    lightInfoBuffer->setType( "sampler2D" );
-   lightInfoBuffer->setName( "directLightingBuffer" );
+   lightInfoBuffer->setName( "diffuseLightingBuffer" );
    lightInfoBuffer->uniform = true;
    lightInfoBuffer->sampler = true;
    lightInfoBuffer->constNum = Var::getTexUnitNum();     // used as texture unit num here
@@ -207,7 +207,7 @@ void DeferredRTLightingFeatGLSL::setTexData( Material::StageData &stageDat,
       mLastTexIndex = texIndex;
 
       passData.mTexType[ texIndex ] = Material::TexTarget;      
-      passData.mSamplerNames[ texIndex ]= "directLightingBuffer";
+      passData.mSamplerNames[ texIndex ]= "diffuseLightingBuffer";
       passData.mTexSlot[ texIndex++ ].texTarget = texTarget;
    }
 }

+ 2 - 2
Engine/source/lighting/advanced/hlsl/advancedLightingFeaturesHLSL.cpp

@@ -99,7 +99,7 @@ void DeferredRTLightingFeatHLSL::processPix( Vector<ShaderComponent*> &component
    uvScene->setName("uvScene");
    LangElement *uvSceneDecl = new DecOp(uvScene);
 
-   String rtParamName = String::ToString("rtParams%s", "directLightingBuffer");
+   String rtParamName = String::ToString("rtParams%s", "diffuseLightingBuffer");
    Var *rtParams = (Var*)LangElement::find(rtParamName);
    if (!rtParams)
    {
@@ -215,7 +215,7 @@ void DeferredRTLightingFeatHLSL::setTexData( Material::StageData &stageDat,
       mLastTexIndex = texIndex;
 
       passData.mTexType[ texIndex ] = Material::TexTarget;
-      passData.mSamplerNames[ texIndex ]= "directLightingBuffer";
+      passData.mSamplerNames[ texIndex ]= "diffuseLightingBuffer";
       passData.mTexSlot[ texIndex++ ].texTarget = texTarget;
    }
 }

+ 2 - 0
Engine/source/lighting/basic/basicLightManager.cpp

@@ -168,6 +168,7 @@ void BasicLightManager::activate( SceneManager *sceneManager )
          FEATUREMGR->registerFeature( MFT_NormalMap, new BumpFeatGLSL );
          FEATUREMGR->registerFeature( MFT_RTLighting, new RTLightingFeatGLSL );
          FEATUREMGR->registerFeature( MFT_PixSpecular, new PixelSpecularGLSL );
+         FEATUREMGR->registerFeature(MFT_ReflectionProbes, new ReflectionProbeFeatGLSL);
       #endif
    }
    else
@@ -178,6 +179,7 @@ void BasicLightManager::activate( SceneManager *sceneManager )
          FEATUREMGR->registerFeature( MFT_NormalMap, new BumpFeatHLSL );
          FEATUREMGR->registerFeature( MFT_RTLighting, new RTLightingFeatHLSL );
          FEATUREMGR->registerFeature( MFT_PixSpecular, new PixelSpecularHLSL );
+         FEATUREMGR->registerFeature(MFT_ReflectionProbes, new ReflectionProbeFeatHLSL);
       #endif
    }
 

+ 809 - 0
Engine/source/lighting/probeManager.cpp

@@ -0,0 +1,809 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "lighting/probeManager.h"
+
+#include "console/console.h"
+#include "console/consoleTypes.h"
+#include "core/util/safeDelete.h"
+#include "console/sim.h"
+#include "console/simSet.h"
+#include "scene/sceneManager.h"
+#include "materials/materialManager.h"
+#include "materials/sceneData.h"
+#include "lighting/lightInfo.h"
+#include "lighting/lightingInterfaces.h"
+#include "T3D/gameBase/gameConnection.h"
+#include "gfx/gfxStringEnumTranslate.h"
+#include "console/engineAPI.h"
+#include "renderInstance/renderDeferredMgr.h"
+#include "shaderGen/shaderGenVars.h"
+
+Signal<void(const char*,bool)> ProbeManager::smActivateSignal;
+ProbeManager *ProbeManager::smProbeManager = NULL;
+
+
+//
+//
+ProbeInfo::ProbeInfo()
+   : mTransform(true),
+   mAmbient(0.0f, 0.0f, 0.0f, 1.0f),
+   mPriority(1.0f),
+   mScore(0.0f),
+   mDebugRender(false),
+   mCubemap(NULL),
+   mIrradianceCubemap(NULL),
+   mBRDFTexture(NULL),
+   mRadius(1.0f),
+   mIntensity(1.0f)
+{
+   for (U32 i = 0; i < 5; ++i)
+   {
+      mSHConstants[i] = 0;
+   }
+}
+
+ProbeInfo::~ProbeInfo()
+{
+   SAFE_DELETE(mCubemap);
+}
+
+void ProbeInfo::set(const ProbeInfo *probeInfo)
+{
+   mTransform = probeInfo->mTransform;
+   mAmbient = probeInfo->mAmbient;
+   mCubemap = probeInfo->mCubemap;
+   mIrradianceCubemap = probeInfo->mIrradianceCubemap;
+   mBRDFTexture = probeInfo->mBRDFTexture;
+   mRadius = probeInfo->mRadius;
+   mIntensity = probeInfo->mIntensity;
+   mProbeShapeType = probeInfo->mProbeShapeType;
+   numPrims = probeInfo->numPrims;
+   numVerts = probeInfo->numVerts;
+   numIndicesForPoly = probeInfo->numIndicesForPoly;
+   mBounds = probeInfo->mBounds;
+
+   for (U32 i = 0; i < 9; i++)
+   {
+      mSHTerms[i] = probeInfo->mSHTerms[i];
+   }
+
+   for (U32 i = 0; i < 5; i++)
+   {
+      mSHConstants[i] = probeInfo->mSHConstants[i];
+   }
+}
+
+void ProbeInfo::getWorldToLightProj(MatrixF *outMatrix) const
+{
+   *outMatrix = getTransform();
+   outMatrix->inverse();
+}
+
+
+void ProbeInfoList::registerProbe(ProbeInfo *light)
+{
+   if (!light)
+      return;
+   // just add the light, we'll try to scan for dupes later...
+   push_back(light);
+}
+
+void ProbeInfoList::unregisterProbe(ProbeInfo *light)
+{
+   // remove all of them...
+   ProbeInfoList &list = *this;
+   for (U32 i = 0; i<list.size(); i++)
+   {
+      if (list[i] != light)
+         continue;
+
+      // this moves last to i, which allows
+      // the search to continue forward...
+      list.erase_fast(i);
+      // want to check this location again...
+      i--;
+   }
+}
+
+ProbeShaderConstants::ProbeShaderConstants()
+   : mInit(false),
+   mShader(NULL),
+   mProbeParamsSC(NULL),
+   mProbePositionSC(NULL),
+   mProbeRadiusSC(NULL),
+   mProbeBoxMinSC(NULL),
+   mProbeBoxMaxSC(NULL),
+   mProbeIsSphereSC(NULL),
+   mProbeLocalPosSC(NULL),
+   mProbeCubemapSC(NULL)
+{
+}
+
+ProbeShaderConstants::~ProbeShaderConstants()
+{
+   if (mShader.isValid())
+   {
+      mShader->getReloadSignal().remove(this, &ProbeShaderConstants::_onShaderReload);
+      mShader = NULL;
+   }
+}
+
+void ProbeShaderConstants::init(GFXShader* shader)
+{
+   if (mShader.getPointer() != shader)
+   {
+      if (mShader.isValid())
+         mShader->getReloadSignal().remove(this, &ProbeShaderConstants::_onShaderReload);
+
+      mShader = shader;
+      mShader->getReloadSignal().notify(this, &ProbeShaderConstants::_onShaderReload);
+   }
+
+   mProbeParamsSC = shader->getShaderConstHandle("$probeParams");
+
+   //Reflection Probes
+   mProbePositionSC = shader->getShaderConstHandle(ShaderGenVars::probePosition);
+   mProbeRadiusSC = shader->getShaderConstHandle(ShaderGenVars::probeRadius);
+   mProbeBoxMinSC = shader->getShaderConstHandle(ShaderGenVars::probeBoxMin);
+   mProbeBoxMaxSC = shader->getShaderConstHandle(ShaderGenVars::probeBoxMax);
+   mProbeIsSphereSC = shader->getShaderConstHandle(ShaderGenVars::probeIsSphere);
+   mProbeLocalPosSC = shader->getShaderConstHandle(ShaderGenVars::probeLocalPos);
+   mProbeCubemapSC = shader->getShaderConstHandle(ShaderGenVars::probeCubemap);
+
+   mInit = true;
+}
+
+void ProbeShaderConstants::_onShaderReload()
+{
+   if (mShader.isValid())
+      init(mShader);
+}
+
+ProbeManager::ProbeManager()
+   :  mDefaultProbe( NULL ),
+      mSceneManager( NULL ),
+      mCullPos( Point3F::Zero )
+{ 
+   dMemset( &mSpecialProbes, 0, sizeof(mSpecialProbes) );
+
+   mLastShader = NULL;
+   mLastConstants = NULL;
+}
+
+ProbeManager::~ProbeManager() 
+{
+}
+
+ProbeInfo* ProbeManager::createProbeInfo(ProbeInfo* probe /* = NULL */)
+{
+   ProbeInfo *outProbe = (probe != NULL) ? probe : new ProbeInfo;
+
+   /*ProbeManagerMap &ProbeManagers = _getProbeManagers();
+   ProbeManagerMap::Iterator iter = ProbeManagers.begin();
+   for ( ; iter != ProbeManagers.end(); iter++ )
+   {
+      ProbeManager *lm = iter->value;
+      lm->_addLightInfoEx( outLight );
+   }*/
+
+   return outProbe;
+}
+
+/*void ProbeManager::initLightFields()
+{
+   ProbeManagerMap &ProbeManagers = _getProbeManagers();
+
+   ProbeManagerMap::Iterator iter = ProbeManagers.begin();
+   for ( ; iter != ProbeManagers.end(); iter++ )
+   {
+      ProbeManager *lm = iter->value;
+      lm->_initLightFields();
+   }
+}*/
+
+IMPLEMENT_GLOBAL_CALLBACK( onProbeManagerActivate, void, ( const char *name ), ( name ),
+   "A callback called by the engine when a light manager is activated.\n"
+   "@param name The name of the light manager being activated.\n"
+   "@ingroup Lighting\n" );
+
+void ProbeManager::activate( SceneManager *sceneManager )
+{
+   AssertFatal( sceneManager, "ProbeManager::activate() - Got null scene manager!" );
+   //AssertFatal( mIsActive == false, "ProbeManager::activate() - Already activated!" );
+   AssertFatal(smProbeManager == NULL, "ProbeManager::activate() - A previous ProbeManager is still active!" );
+
+   mSceneManager = sceneManager;
+   smProbeManager = this;
+}
+
+IMPLEMENT_GLOBAL_CALLBACK( onProbeManagerDeactivate, void, ( const char *name ), ( name ),
+   "A callback called by the engine when a light manager is deactivated.\n"
+   "@param name The name of the light manager being deactivated.\n"
+   "@ingroup Lighting\n" );
+
+void ProbeManager::deactivate()
+{
+   //AssertFatal( mIsActive == true, "ProbeManager::deactivate() - Already deactivated!" );
+   AssertFatal( smProbeManager == this, "ProbeManager::activate() - This isn't the active light manager!" );
+
+   //if( Sim::getRootGroup() ) // To protect against shutdown.
+   //   onProbeManagerDeactivate_callback( getName() );
+
+   //mIsActive = false;
+   mSceneManager = NULL;
+   smProbeManager = NULL;
+
+   // Just in case... make sure we're all clear.
+   unregisterAllProbes();
+}
+
+ProbeInfo* ProbeManager::getDefaultLight()
+{
+   // The sun is always our default light when
+   // when its registered.
+   if ( mSpecialProbes[ ProbeManager::SkylightProbeType ] )
+      return mSpecialProbes[ ProbeManager::SkylightProbeType ];
+
+   // Else return a dummy special light.
+   //if ( !mDefaultLight )
+   //   mDefaultLight = createLightInfo();
+   return NULL;
+}
+
+ProbeInfo* ProbeManager::getSpecialProbe( ProbeManager::SpecialProbeTypesEnum type, bool useDefault )
+{
+   //if ( mSpecialLights[type] )
+   //   return mSpecialLights[type];
+
+   if ( useDefault )
+      return getDefaultLight();
+
+   return NULL;
+}
+
+void ProbeManager::setSpecialProbe( ProbeManager::SpecialProbeTypesEnum type, ProbeInfo *probe )
+{
+   if (probe && type == SkylightProbeType )
+   {
+      // The sun must be specially positioned and ranged
+      // so that it can be processed like a point light 
+      // in the stock light shader used by Basic Lighting.
+      
+      probe->setPosition( mCullPos - (probe->getDirection() * 10000.0f ) );
+      probe->mRadius = 2000000.0f;
+   }
+
+   mSpecialProbes[type] = probe;
+   registerProbe(probe, NULL );
+}
+
+void ProbeManager::registerProbes( const Frustum *frustum, bool staticLighting )
+{
+   PROFILE_SCOPE( ProbeManager_RegisterProbes );
+
+   // TODO: We need to work this out...
+   //
+   // 1. Why do we register and unregister lights on every 
+   //    render when they don't often change... shouldn't we
+   //    just register once and keep them?
+   // 
+   // 2. If we do culling of lights should this happen as part
+   //    of registration or somewhere else?
+   //
+
+   // Grab the lights to process.
+   Vector<SceneObject*> activeLights;
+   const U32 lightMask = LightObjectType;
+   
+   if ( staticLighting || !frustum )
+   {
+      // We're processing static lighting or want all the lights
+      // in the container registerd...  so no culling.
+      getSceneManager()->getContainer()->findObjectList( lightMask, &activeLights );
+   }
+   else
+   {
+      // Cull the lights using the frustum.
+      getSceneManager()->getContainer()->findObjectList( *frustum, lightMask, &activeLights );
+      /* supress light culling filter until we can sort out why that's misbehaving with dynamic cube mapping
+      for (U32 i = 0; i < activeLights.size(); ++i)
+      {
+         if (!getSceneManager()->mRenderedObjectsList.contains(activeLights[i]))
+         {
+            activeLights.erase(i);
+            --i;
+         }
+      }
+      */
+      // Store the culling position for sun placement
+      // later... see setSpecialLight.
+      mCullPos = frustum->getPosition();
+
+      // HACK: Make sure the control object always gets 
+      // processed as lights mounted to it don't change
+      // the shape bounds and can often get culled.
+
+      GameConnection *conn = GameConnection::getConnectionToServer();
+      if ( conn->getControlObject() )
+      {
+         GameBase *conObject = conn->getControlObject();
+         activeLights.push_back_unique( conObject );
+      }
+   }
+
+   // Let the lights register themselves.
+   /*for ( U32 i = 0; i < activeLights.size(); i++ )
+   {
+      ISceneLight *lightInterface = dynamic_cast<ISceneLight*>( activeLights[i] );
+      if ( lightInterface )
+         lightInterface->submitLights( this, staticLighting );
+   }*/
+}
+
+void ProbeManager::registerSkylight(ProbeInfo *probe, SimObject *obj)
+{
+   mSkylight = probe;
+
+   if (String("Advanced Lighting").equal(LIGHTMGR->getName(), String::NoCase))
+   {
+      SceneRenderState* state = mSceneManager->getCurrentRenderState();
+
+      RenderPassManager *renderPass = state->getRenderPass();
+
+      // Allocate an MeshRenderInst so that we can submit it to the RenderPassManager
+      ProbeRenderInst *probeInst = renderPass->allocInst<ProbeRenderInst>();
+
+      probeInst->set(probe);
+
+      probeInst->type = RenderPassManager::RIT_Probes;
+
+      // Submit our RenderInst to the RenderPassManager
+      state->getRenderPass()->addInst(probeInst);
+   }
+}
+
+void ProbeManager::registerProbe(ProbeInfo *probe, SimObject *obj )
+{
+  // AssertFatal( !mRegisteredProbes.contains(probe),
+      //"ProbeManager::registerGlobalLight - This light is already registered!" );
+
+   if (!mRegisteredProbes.contains(probe))
+      mRegisteredProbes.push_back(probe);
+
+   if (String("Advanced Lighting").equal(LIGHTMGR->getName(), String::NoCase))
+   {
+      SceneRenderState* state = mSceneManager->getCurrentRenderState();
+
+      RenderPassManager *renderPass = state->getRenderPass();
+
+      // Allocate an MeshRenderInst so that we can submit it to the RenderPassManager
+      ProbeRenderInst *probeInst = renderPass->allocInst<ProbeRenderInst>();
+
+      probeInst->set(probe);
+
+      probeInst->type = RenderPassManager::RIT_Probes;
+
+      // Submit our RenderInst to the RenderPassManager
+      state->getRenderPass()->addInst(probeInst);
+   }
+}
+
+void ProbeManager::unregisterProbe(ProbeInfo *probe )
+{
+   mRegisteredProbes.unregisterProbe(probe);
+
+   // If this is the sun... clear the special light too.
+   if (probe == mSpecialProbes[SkylightProbeType] )
+      dMemset(mSpecialProbes, 0, sizeof(mSpecialProbes) );
+}
+
+void ProbeManager::unregisterAllProbes()
+{
+   //dMemset(mSpecialProbes, 0, sizeof(mSpecialProbes) );
+   mRegisteredProbes.clear();
+
+   mSkylight = nullptr;
+}
+
+void ProbeManager::getAllUnsortedProbes( Vector<ProbeInfo*> *list ) const
+{
+   list->merge( mRegisteredProbes );
+}
+
+ProbeShaderConstants* ProbeManager::getProbeShaderConstants(GFXShaderConstBuffer* buffer)
+{
+   if (!buffer)
+      return NULL;
+
+   PROFILE_SCOPE(ProbeManager_GetProbeShaderConstants);
+
+   GFXShader* shader = buffer->getShader();
+
+   // Check to see if this is the same shader, we'll get hit repeatedly by
+   // the same one due to the render bin loops.
+   if (mLastShader.getPointer() != shader)
+   {
+      ProbeConstantMap::Iterator iter = mConstantLookup.find(shader);
+      if (iter != mConstantLookup.end())
+      {
+         mLastConstants = iter->value;
+      }
+      else
+      {
+         ProbeShaderConstants* psc = new ProbeShaderConstants();
+         mConstantLookup[shader] = psc;
+
+         mLastConstants = psc;
+      }
+
+      // Set our new shader
+      mLastShader = shader;
+   }
+
+   mLastConstants = new ProbeShaderConstants();
+
+   // Make sure that our current lighting constants are initialized
+   if (!mLastConstants->mInit)
+      mLastConstants->init(shader);
+
+   return mLastConstants;
+}
+
+void ProbeManager::_update4ProbeConsts(   const SceneData &sgData,
+                                          MatrixSet &matSet,
+                                          GFXShaderConstHandle *probePositionSC,
+                                          GFXShaderConstHandle *probeRadiusSC,
+                                          GFXShaderConstHandle *probeBoxMinSC,
+                                          GFXShaderConstHandle *probeBoxMaxSC,
+                                          GFXShaderConstHandle *probeCubemapSC,
+                                          GFXShaderConstHandle *probeIsSphereSC,
+                                          GFXShaderConstHandle *probeLocalPosSC,
+                                          GFXShaderConstBuffer *shaderConsts )
+{
+   PROFILE_SCOPE( ProbeManager_Update4ProbeConsts );
+
+   // Skip over gathering lights if we don't have to!
+   if (probePositionSC->isValid() ||
+      probeRadiusSC->isValid() ||
+      probeBoxMinSC->isValid() ||
+      probeBoxMaxSC->isValid() ||
+      probeCubemapSC->isValid()  && (!mRegisteredProbes.empty() || mSkylight))
+   {
+      PROFILE_SCOPE(ProbeManager_Update4ProbeConsts_setProbes);
+
+      static AlignedArray<Point3F> probePositions(4, sizeof(Point3F));
+      static AlignedArray<F32> probeRadius(4, sizeof(F32));
+      static AlignedArray<Point3F> probeBoxMins(4, sizeof(Point3F));
+      static AlignedArray<Point3F> probeBoxMaxs(4, sizeof(Point3F));
+      static AlignedArray<Point3F> probeLocalPositions(4, sizeof(Point3F));
+      static AlignedArray<F32> probeIsSphere(4, sizeof(F32));
+      //static AlignedArray<CubemapData> probeCubemap(4, sizeof(CubemapData));
+      F32 range;
+
+      // Need to clear the buffers so that we don't leak
+      // lights from previous passes or have NaNs.
+      dMemset(probePositions.getBuffer(), 0, probePositions.getBufferSize());
+      dMemset(probeRadius.getBuffer(), 0, probeRadius.getBufferSize());
+      dMemset(probeBoxMins.getBuffer(), 0, probeBoxMins.getBufferSize());
+      dMemset(probeBoxMaxs.getBuffer(), 0, probeBoxMaxs.getBufferSize());
+      dMemset(probeLocalPositions.getBuffer(), 0, probeLocalPositions.getBufferSize());
+      dMemset(probeIsSphere.getBuffer(), 0, probeRadius.getBufferSize());
+      //dMemset(probeCubemap.getBuffer(), 0, probeCubemap.getBufferSize());
+
+      matSet.restoreSceneViewProjection();
+
+      const MatrixF &worldToCameraXfm = matSet.getWorldToCamera();
+
+      // Gather the data for the first 4 probes.
+      const ProbeInfo *probe;
+      for (U32 i = 0; i < 4; i++)
+      {
+         if (i >= mRegisteredProbes.size())
+            break;
+
+         if (i == 0 && mSkylight)
+         {
+            //quickly try and see if we have a skylight, and set that to always be probe 0
+            probe = mSkylight;
+         }
+         else
+         {
+            probe = mRegisteredProbes[i];
+         }
+
+         if (!probe)
+            continue;
+
+         // The light positions and spot directions are 
+         // in SoA order to make optimal use of the GPU.
+         const Point3F &probePos = probe->getPosition();
+         probePositions[i].x = probePos.x;
+         probePositions[i].y = probePos.y;
+         probePositions[i].z = probePos.z;
+
+         probeRadius[i] = probe->mRadius;
+
+         const Point3F &minExt = probe->mBounds.minExtents;
+         probeBoxMins[i].x = minExt.x;
+         probeBoxMins[i].y = minExt.y;
+         probeBoxMins[i].z = minExt.z;
+
+         const Point3F &maxExt = probe->mBounds.maxExtents;
+         probeBoxMaxs[i].x = maxExt.x;
+         probeBoxMaxs[i].y = maxExt.y;
+         probeBoxMaxs[i].z = maxExt.z;
+
+         probeIsSphere[i] = probe->mProbeShapeType == ProbeInfo::Sphere ? 1.0 : 0.0;
+
+         Point3F localProbePos;
+         worldToCameraXfm.mulP(probe->getPosition(), &localProbePos);
+
+         probeLocalPositions[i].x = localProbePos.x;
+         probeLocalPositions[i].y = localProbePos.y;
+         probeLocalPositions[i].z = localProbePos.z;
+
+         if (probe->mCubemap && !probe->mCubemap->isNull())
+         {
+            S32 samplerReg = probeCubemapSC->getSamplerRegister();
+
+            if(samplerReg != -1)
+               GFX->setCubeTexture(samplerReg + i, probe->mCubemap->getPointer());
+         }
+      }
+
+      shaderConsts->setSafe(probePositionSC, probePositions);
+      shaderConsts->setSafe(probeRadiusSC, probeRadius);
+      shaderConsts->setSafe(probeBoxMinSC, probeBoxMins);
+      shaderConsts->setSafe(probeBoxMaxSC, probeBoxMaxs);
+      shaderConsts->setSafe(probeLocalPosSC, probeLocalPositions);
+      shaderConsts->setSafe(probeIsSphereSC, probeIsSphere);
+
+      //
+      
+      //shaderConsts->setSafe(lightSpotAngleSC, lightSpotAngle);
+      //shaderConsts->setSafe(lightSpotFalloffSC, lightSpotFalloff);
+   }
+   else
+   {
+      /*if (probe->mCubemap && !probe->mCubemap->isNull())
+      {
+         GFX->setCubeTexture(1, probe->mCubemap->getPointer());
+      }*/
+      if (probeCubemapSC->isValid())
+      {
+         for(U32 i=0; i < 4; ++i)
+            GFX->setCubeTexture(probeCubemapSC->getSamplerRegister() + i, NULL);
+      }
+   }
+}
+
+void ProbeManager::setProbeInfo(ProcessedMaterial *pmat,
+   const Material *mat,
+   const SceneData &sgData,
+   const SceneRenderState *state,
+   U32 pass,
+   GFXShaderConstBuffer *shaderConsts)
+{
+   
+   // Skip this if we're rendering from the deferred bin.
+   if ( sgData.binType == SceneData::DeferredBin )
+      return;
+
+  // if (mRegisteredProbes.empty())
+  //    return;
+
+   PROFILE_SCOPE(ProbeManager_setProbeInfo);
+
+   ProbeShaderConstants *psc = getProbeShaderConstants(shaderConsts);
+
+   //ProbeInfo *probe;
+   //probe = mRegisteredProbes[0];
+
+   // NOTE: If you encounter a crash from this point forward
+   // while setting a shader constant its probably because the
+   // mConstantLookup has bad shaders/constants in it.
+   //
+   // This is a known crash bug that can occur if materials/shaders
+   // are reloaded and the light manager is not reset.
+   //
+   // We should look to fix this by clearing the table.
+
+   MatrixSet matSet = state->getRenderPass()->getMatrixSet();
+
+   // Update the forward shading light constants.
+   _update4ProbeConsts( sgData,
+      matSet,
+      psc->mProbePositionSC,
+      psc->mProbeRadiusSC,
+      psc->mProbeBoxMinSC,
+      psc->mProbeBoxMaxSC,
+      psc->mProbeCubemapSC,
+      psc->mProbeIsSphereSC,
+      psc->mProbeLocalPosSC,
+      shaderConsts );
+
+   // Static
+   /*if (lsm && light->getCastShadows())
+   {
+      if (psc->mWorldToLightProjSC->isValid())
+         shaderConsts->set(psc->mWorldToLightProjSC,
+            lsm->getWorldToLightProj(),
+            psc->mWorldToLightProjSC->getType());
+
+      if (psc->mViewToLightProjSC->isValid())
+      {
+         // TODO: Should probably cache these results and
+         // not do this mul here on every material that needs
+         // this transform.
+
+         shaderConsts->set(psc->mViewToLightProjSC,
+            lsm->getWorldToLightProj() * state->getCameraTransform(),
+            psc->mViewToLightProjSC->getType());
+      }
+
+      shaderConsts->setSafe(psc->mShadowMapSizeSC, 1.0f / (F32)lsm->getTexSize());
+
+      // Do this last so that overrides can properly override parameters previously set
+      lsm->setShaderParameters(shaderConsts, psc);
+   }
+   else
+   {
+      if (psc->mViewToLightProjSC->isValid())
+      {
+         // TODO: Should probably cache these results and
+         // not do this mul here on every material that needs
+         // this transform.
+         MatrixF proj;
+         light->getWorldToLightProj(&proj);
+
+         shaderConsts->set(psc->mViewToLightProjSC,
+            proj * state->getCameraTransform(),
+            psc->mViewToLightProjSC->getType());
+      }
+   }
+
+   // Dynamic
+   if (dynamicShadowMap)
+   {
+      if (psc->mDynamicWorldToLightProjSC->isValid())
+         shaderConsts->set(psc->mDynamicWorldToLightProjSC,
+            dynamicShadowMap->getWorldToLightProj(),
+            psc->mDynamicWorldToLightProjSC->getType());
+
+      if (psc->mDynamicViewToLightProjSC->isValid())
+      {
+         // TODO: Should probably cache these results and
+         // not do this mul here on every material that needs
+         // this transform.
+
+         shaderConsts->set(psc->mDynamicViewToLightProjSC,
+            dynamicShadowMap->getWorldToLightProj() * state->getCameraTransform(),
+            psc->mDynamicViewToLightProjSC->getType());
+      }
+
+      shaderConsts->setSafe(psc->mShadowMapSizeSC, 1.0f / (F32)dynamicShadowMap->getTexSize());
+
+      // Do this last so that overrides can properly override parameters previously set
+      dynamicShadowMap->setShaderParameters(shaderConsts, psc);
+   }
+   else
+   {
+      if (psc->mDynamicViewToLightProjSC->isValid())
+      {
+         // TODO: Should probably cache these results and
+         // not do this mul here on every material that needs
+         // this transform.
+         MatrixF proj;
+         light->getWorldToLightProj(&proj);
+
+         shaderConsts->set(psc->mDynamicViewToLightProjSC,
+            proj * state->getCameraTransform(),
+            psc->mDynamicViewToLightProjSC->getType());
+      }
+   }*/
+}
+
+/// Allows us to set textures during the Material::setTextureStage call, return true if we've done work.
+bool ProbeManager::setTextureStage(const SceneData &sgData,
+   const U32 currTexFlag,
+   const U32 textureSlot,
+   GFXShaderConstBuffer *shaderConsts,
+   ShaderConstHandles *handles)
+{
+   return false;
+}
+
+AvailableSLInterfaces* ProbeManager::getSceneLightingInterface()
+{
+   //if ( !mAvailableSLInterfaces )
+   //   mAvailableSLInterfaces = new AvailableSLInterfaces();
+
+   return NULL;
+}
+
+/*bool ProbeManager::lightScene( const char* callback, const char* param )
+{
+   BitSet32 flags = 0;
+
+   if ( param )
+   {
+      if ( !dStricmp( param, "forceAlways" ) )
+         flags.set( SceneLighting::ForceAlways );
+      else if ( !dStricmp(param, "forceWritable" ) )
+         flags.set( SceneLighting::ForceWritable );
+      else if ( !dStricmp(param, "loadOnly" ) )
+         flags.set( SceneLighting::LoadOnly );
+   }
+
+   // The SceneLighting object will delete itself 
+   // once the lighting process is complete.   
+   SceneLighting* sl = new SceneLighting( getSceneLightingInterface() );
+   return sl->lightScene( callback, flags );
+}*/
+
+/*RenderDeferredMgr* ProbeManager::_findDeferredRenderBin()
+{
+   RenderPassManager* rpm = getSceneManager()->getDefaultRenderPass();
+   for( U32 i = 0; i < rpm->getManagerCount(); i++ )
+   {
+      RenderBinManager *bin = rpm->getManager( i );
+      if( bin->getRenderInstType() == RenderDeferredMgr::RIT_Deferred )
+      {
+         return ( RenderDeferredMgr* ) bin;
+      }
+   }
+
+   return NULL;
+}*/
+
+DefineEngineFunction( CreateProbeManager, bool, (),,
+   "Finds and activates the named light manager.\n"
+   "@return Returns true if the light manager is found and activated.\n"
+   "@ingroup Lighting\n" )
+{
+   ProbeManager* probeManager = new ProbeManager();
+   
+   if (probeManager != nullptr && gClientSceneGraph != nullptr)
+   {
+      probeManager->activate(gClientSceneGraph);
+      return true;
+   }
+
+   return false;
+}
+
+DefineEngineFunction( resetProbeManager, void, (),,
+   "@brief Deactivates and then activates the currently active light manager."
+   "This causes most shaders to be regenerated and is often used when global "
+   "rendering changes have occured.\n"
+   "@ingroup Lighting\n" )
+{
+   ProbeManager *pm = PROBEMGR;
+   if ( !pm)
+      return;
+
+   /*SceneManager *sm = lm->getSceneManager();
+   lm->deactivate();
+   lm->activate( sm );*/
+}

+ 334 - 0
Engine/source/lighting/probeManager.h

@@ -0,0 +1,334 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#ifndef PROBEMANAGER_H
+#define PROBEMANAGER_H
+
+#ifndef _TORQUE_STRING_H_
+#include "core/util/str.h"
+#endif
+#ifndef _TSIGNAL_H_
+#include "core/util/tSignal.h"
+#endif
+#ifndef _LIGHTINFO_H_
+#include "lighting/lightInfo.h"
+#endif
+#ifndef _LIGHTQUERY_H_
+#include "lighting/lightQuery.h"
+#endif
+#ifndef _MATRIXSET_H_
+#include "math/util/matrixSet.h"
+#endif
+#ifndef _CUBEMAPDATA_H_
+#include "gfx/sim/cubemapData.h"
+#endif
+
+class SimObject;
+class ProbeManager;
+class Material;
+class ProcessedMaterial;
+class SceneManager;
+struct SceneData;
+class Point3F;
+class AvailableSLInterfaces;
+class SceneObject;
+class GFXShaderConstBuffer;
+class GFXShaderConstHandle;
+class ShaderConstHandles;
+class SceneRenderState;
+class RenderDeferredMgr;
+class Frustum;
+
+struct ProbeInfo
+{
+   LinearColorF mAmbient;
+
+   MatrixF mTransform;
+
+   F32 mRadius;
+   F32 mIntensity;
+
+   Box3F mBounds;
+
+   GFXCubemapHandle *mCubemap;
+
+   GFXCubemapHandle *mIrradianceCubemap;
+
+   GFXTexHandle *mBRDFTexture;
+
+   /// The priority of this light used for
+   /// light and shadow scoring.
+   F32 mPriority;
+
+   /// A temporary which holds the score used
+   /// when prioritizing lights for rendering.
+   F32 mScore;
+
+   bool mIsSkylight;
+
+   /// Whether to render debugging visualizations
+   /// for this light.
+   bool mDebugRender;
+
+   //GFXPrimitiveBufferHandle primBuffer;
+   //GFXVertexBufferHandle<GFXVertexPC> vertBuffer;
+   U32 numPrims;
+   U32 numVerts;
+   Vector< U32 > numIndicesForPoly;
+
+   enum ProbeShapeType
+   {
+      Sphere = 0,            ///< Sphere shaped
+      Box = 1,               ///< Box-based shape
+   };
+
+   ProbeShapeType mProbeShapeType;
+
+   //Spherical Harmonics data
+   LinearColorF mSHTerms[9];
+   F32 mSHConstants[5];
+
+public:
+
+   ProbeInfo();
+   ~ProbeInfo();
+
+   // Copies data passed in from another probe
+   void set(const ProbeInfo *probe);
+
+   // Accessors
+   const MatrixF& getTransform() const { return mTransform; }
+   void setTransform(const MatrixF &xfm) { mTransform = xfm; }
+
+   Point3F getPosition() const { return mTransform.getPosition(); }
+   void setPosition(const Point3F &pos) { mTransform.setPosition(pos); }
+
+   VectorF getDirection() const { return mTransform.getForwardVector(); }
+   void setDirection(const VectorF &val);
+
+   const LinearColorF& getAmbient() const { return mAmbient; }
+   void setAmbient(const LinearColorF &val) { mAmbient = val; }
+
+   void setPriority(F32 priority) { mPriority = priority; }
+   F32 getPriority() const { return mPriority; }
+
+   void setScore(F32 score) { mScore = score; }
+   F32 getScore() const { return mScore; }
+
+   bool isDebugRenderingEnabled() const { return mDebugRender; }
+   void enableDebugRendering(bool value) { mDebugRender = value; }
+
+   // Builds the world to light view projection used for
+   // shadow texture and cookie lookups.
+   void getWorldToLightProj(MatrixF *outMatrix) const;
+
+   void clear();
+};
+
+class ProbeInfoList : public Vector<ProbeInfo*>
+{
+public:
+   void registerProbe(ProbeInfo *probe);
+   void unregisterProbe(ProbeInfo *probe);
+};
+
+struct ProbeShaderConstants
+{
+   bool mInit;
+
+   GFXShaderRef mShader;
+
+   GFXShaderConstHandle* mProbeParamsSC;
+
+   //Reflection Probes
+   GFXShaderConstHandle *mProbePositionSC;
+   GFXShaderConstHandle *mProbeRadiusSC;
+   GFXShaderConstHandle *mProbeBoxMinSC;
+   GFXShaderConstHandle *mProbeBoxMaxSC;
+   GFXShaderConstHandle *mProbeIsSphereSC;
+   GFXShaderConstHandle *mProbeLocalPosSC;
+   GFXShaderConstHandle *mProbeCubemapSC;
+
+   ProbeShaderConstants();
+   ~ProbeShaderConstants();
+
+   void init(GFXShader* buffer);
+
+   void _onShaderReload();
+};
+
+typedef Map<GFXShader*, ProbeShaderConstants*> ProbeConstantMap;
+
+class ProbeManager
+{
+public:
+
+   enum SpecialProbeTypesEnum
+   {
+      SkylightProbeType,
+      SpecialProbeTypesCount
+   };
+
+   ProbeManager();
+
+   ~ProbeManager();
+
+   ///
+   static void initProbeFields();
+
+   /// 
+   static ProbeInfo* createProbeInfo(ProbeInfo* light = NULL);
+
+   /// The light manager activation signal.
+   static Signal<void(const char*,bool)> smActivateSignal;
+
+   /// Returns the active LM.
+   static inline ProbeManager* getProbeManager();
+
+   // Returns the scene manager passed at activation.
+   SceneManager* getSceneManager() { return mSceneManager; }
+
+   // Called when the lighting manager should become active
+   virtual void activate( SceneManager *sceneManager );
+
+   // Called when we don't want the light manager active (should clean up)
+   virtual void deactivate();
+
+   // Returns the active scene lighting interface for this light manager.  
+   virtual AvailableSLInterfaces* getSceneLightingInterface();
+
+   // Returns a "default" light info that callers should not free.  Used for instances where we don't actually care about 
+   // the light (for example, setting default data for SceneData)
+   virtual ProbeInfo* getDefaultLight();
+
+   /// Returns the special light or the default light if useDefault is true.
+   /// @see getDefaultLight
+   virtual ProbeInfo* getSpecialProbe(SpecialProbeTypesEnum type,
+                                       bool useDefault = true );
+
+   /// Set a special light type.
+   virtual void setSpecialProbe(SpecialProbeTypesEnum type, ProbeInfo *light );
+
+   void registerSkylight(ProbeInfo *probe, SimObject *obj);
+
+   // registered before scene traversal...
+   virtual void registerProbe(ProbeInfo *light, SimObject *obj );
+   virtual void unregisterProbe(ProbeInfo *light );
+
+   virtual void registerProbes( const Frustum *frustum, bool staticlighting );
+   virtual void unregisterAllProbes();
+
+   /// Returns all unsorted and un-scored lights (both global and local).
+   void getAllUnsortedProbes( Vector<ProbeInfo*> *list ) const;
+
+   /// Sets shader constants / textures for light infos
+   virtual void setProbeInfo( ProcessedMaterial *pmat, 
+                              const Material *mat, 
+                              const SceneData &sgData, 
+                              const SceneRenderState *state,
+                              U32 pass, 
+                              GFXShaderConstBuffer *shaderConsts );
+
+   /// Allows us to set textures during the Material::setTextureStage call, return true if we've done work.
+   virtual bool setTextureStage( const SceneData &sgData, 
+                                 const U32 currTexFlag, 
+                                 const U32 textureSlot, 
+                                 GFXShaderConstBuffer *shaderConsts, 
+                                 ShaderConstHandles *handles );
+protected:
+
+   /// The current active light manager.
+   static ProbeManager *smProbeManager;
+
+   /// Find the pre-pass render bin on the scene's default render pass.
+   RenderDeferredMgr* _findDeferredRenderBin();
+
+public:
+   ProbeShaderConstants* getProbeShaderConstants(GFXShaderConstBuffer* buffer);
+protected:
+   /// This helper function sets the shader constansts
+   /// for the stock 4 light forward lighting code.
+   void _update4ProbeConsts( const SceneData &sgData,
+                                    MatrixSet &matSet,
+                                    GFXShaderConstHandle *probePositionSC,
+                                    GFXShaderConstHandle *probeRadiusSC,
+                                    GFXShaderConstHandle *probeBoxMinSC,
+                                    GFXShaderConstHandle *probeBoxMaxSC,
+                                    GFXShaderConstHandle *probeCubemapSC,
+                                    GFXShaderConstHandle *probeIsSphereSC,
+                                    GFXShaderConstHandle *probeLocalPosSC,
+                                    GFXShaderConstBuffer *shaderConsts );
+
+   /// A dummy default light used when no lights
+   /// happen to be registered with the manager.
+   ProbeInfo *mDefaultProbe;
+  
+   /// The list of global registered lights which is
+   /// initialized before the scene is rendered.
+   ProbeInfoList mRegisteredProbes;
+
+   ProbeInfo* mSkylight;
+
+   /// The registered special light list.
+   ProbeInfo *mSpecialProbes[SpecialProbeTypesCount];
+
+   /// The root culling position used for
+   /// special sun light placement.
+   /// @see setSpecialLight
+   Point3F mCullPos;
+
+   ///
+   //virtual void _initLightFields();
+
+   /// The scene graph the light manager is associated with.
+   SceneManager *mSceneManager;
+
+   ProbeConstantMap mConstantLookup;
+
+   GFXShaderRef mLastShader;
+
+   ProbeShaderConstants* mLastConstants;
+};
+
+ProbeManager* ProbeManager::getProbeManager()
+{
+	if (smProbeManager == nullptr)
+	{
+		ProbeManager* probeManager = new ProbeManager();
+
+		if (gClientSceneGraph != nullptr)
+		{
+			probeManager->activate(gClientSceneGraph);
+		}
+		else
+		{
+			delete probeManager;
+		}
+	}
+
+	return smProbeManager;
+}
+
+/// Returns the current active light manager.
+#define PROBEMGR ProbeManager::getProbeManager()
+
+#endif // PROBEMANAGER_H

+ 2 - 0
Engine/source/materials/materialFeatureTypes.cpp

@@ -62,6 +62,8 @@ ImplementFeatureType( MFT_SubSurface, MFG_Lighting, 8.0f, true );
 ImplementFeatureType( MFT_VertLit, MFG_Lighting, 9.0f, true );
 ImplementFeatureType( MFT_MinnaertShading, MFG_Lighting, 10.0f, true );
 
+ImplementFeatureType(MFT_ReflectionProbes, MFG_Lighting, 11.0f, true);
+
 ImplementFeatureType( MFT_GlowMask, MFG_PostLighting, 1.0f, true );
 ImplementFeatureType( MFT_Visibility, MFG_PostLighting, 2.0f, true );
 ImplementFeatureType( MFT_Fog, MFG_PostProcess, 3.0f, true );

+ 2 - 0
Engine/source/materials/materialFeatureTypes.h

@@ -129,6 +129,8 @@ DeclareFeatureType( MFT_InvertSmoothness );
 DeclareFeatureType( MFT_SpecularMap );
 DeclareFeatureType( MFT_GlossMap );
 
+DeclareFeatureType( MFT_ReflectionProbes );
+
 /// This feature is only used to detect alpha transparency
 /// and does not have any code associtated with it. 
 DeclareFeatureType( MFT_IsTranslucent );

+ 5 - 0
Engine/source/materials/processedCustomMaterial.cpp

@@ -40,6 +40,7 @@
 #include "console/propertyParsing.h"
 #include "gfx/util/screenspace.h"
 #include "scene/reflectionManager.h"
+#include "lighting/probeManager.h"
 
 
 ProcessedCustomMaterial::ProcessedCustomMaterial(Material &mat)
@@ -322,6 +323,10 @@ bool ProcessedCustomMaterial::setupPass( SceneRenderState *state, const SceneDat
    if (lm)
       lm->setLightInfo(this, NULL, sgData, state, pass, shaderConsts);
 
+   ProbeManager* pm = state ? PROBEMGR : NULL;
+   if (pm)
+      pm->setProbeInfo(this, NULL, sgData, state, pass, shaderConsts);
+
    shaderConsts->setSafe(rpd->shaderHandles.mAccumTimeSC, MATMGR->getTotalTime());   
 
    return true;

+ 21 - 9
Engine/source/materials/processedShaderMaterial.cpp

@@ -41,6 +41,8 @@
 #include "gfx/util/screenspace.h"
 #include "math/util/matrixSet.h"
 
+#include "lighting/probeManager.h"
+
 // We need to include customMaterialDefinition for ShaderConstHandles::init
 #include "materials/customMaterialDefinition.h"
 
@@ -343,10 +345,16 @@ void ProcessedShaderMaterial::_determineFeatures(  U32 stageNum,
    if ( mMaterial->mAlphaTest )
       fd.features.addFeature( MFT_AlphaTest );
 
-   if ( mMaterial->mEmissive[stageNum] )
-      fd.features.addFeature( MFT_IsEmissive );
+   if (mMaterial->mEmissive[stageNum])
+   {
+      fd.features.addFeature(MFT_IsEmissive);
+   }
    else
-      fd.features.addFeature( MFT_RTLighting );
+   {
+      fd.features.addFeature(MFT_RTLighting);
+	  if (mMaterial->isTranslucent())
+		  fd.features.addFeature(MFT_ReflectionProbes);
+   }
 
    if ( mMaterial->mAnimFlags[stageNum] )
       fd.features.addFeature( MFT_TexAnim );  
@@ -357,7 +365,7 @@ void ProcessedShaderMaterial::_determineFeatures(  U32 stageNum,
    // cubemaps only available on stage 0 for now - bramage   
    if ( stageNum < 1 && mMaterial->isTranslucent() &&
          (  (  mMaterial->mCubemapData && mMaterial->mCubemapData->mCubemap ) ||
-               mMaterial->mDynamicCubemap ) )
+               mMaterial->mDynamicCubemap ) && !features.hasFeature(MFT_ReflectionProbes))
    {
        fd.features.addFeature( MFT_CubeMap );
    }
@@ -367,6 +375,8 @@ void ProcessedShaderMaterial::_determineFeatures(  U32 stageNum,
       fd.features.addFeature(MFT_StaticCubemap);
       fd.features.addFeature(MFT_CubeMap);
       fd.features.addFeature(MFT_SkyBox);
+
+      fd.features.removeFeature(MFT_ReflectionProbes);
    }
    fd.features.addFeature( MFT_Visibility );
 
@@ -1296,13 +1306,15 @@ void ProcessedShaderMaterial::setSceneInfo(SceneRenderState * state, const Scene
       shaderConsts->set(handles->mEyePosSC, eyepos);   
    }
 
-   shaderConsts->setSafe(handles->mEyeMatSC, state->getCameraTransform());   
+   shaderConsts->setSafe(handles->mEyeMatSC, state->getCameraTransform());
+
+   ShaderRenderPassData *rpd = _getRPD(pass);
+   for (U32 i = 0; i < rpd->featureShaderHandles.size(); i++)
+      rpd->featureShaderHandles[i]->setConsts(state, sgData, shaderConsts);
 
-   ShaderRenderPassData *rpd = _getRPD( pass );
-   for ( U32 i=0; i < rpd->featureShaderHandles.size(); i++ )
-      rpd->featureShaderHandles[i]->setConsts( state, sgData, shaderConsts );
+   LIGHTMGR->setLightInfo(this, mMaterial, sgData, state, pass, shaderConsts);
 
-   LIGHTMGR->setLightInfo( this, mMaterial, sgData, state, pass, shaderConsts );
+   PROBEMGR->setProbeInfo(this, mMaterial, sgData, state, pass, shaderConsts);
 }
 
 void ProcessedShaderMaterial::setBuffers( GFXVertexBufferHandleBase *vertBuffer, GFXPrimitiveBufferHandle *primBuffer )

+ 1 - 2
Engine/source/renderInstance/renderBinManager.h

@@ -86,8 +86,6 @@ public:
 
    MaterialOverrideDelegate& getMatOverrideDelegate() { return mMatOverrideDelegate; }
 
-protected:
-
    struct MainSortElem
    {
       RenderInst *inst;
@@ -95,6 +93,7 @@ protected:
       U32 key2;
    };
 
+protected:
    void setRenderPass( RenderPassManager *rpm );
 
    /// Called from derived bins to add additional

+ 9 - 3
Engine/source/renderInstance/renderDeferredMgr.cpp

@@ -56,7 +56,7 @@ const String RenderDeferredMgr::BufferName("deferred");
 const RenderInstType RenderDeferredMgr::RIT_Deferred("Deferred");
 const String RenderDeferredMgr::ColorBufferName("color");
 const String RenderDeferredMgr::MatInfoBufferName("matinfo");
-const String RenderDeferredMgr::LightMapBufferName("indirectLighting");
+const String RenderDeferredMgr::LightMapBufferName("diffuseLighting");
 
 IMPLEMENT_CONOBJECT(RenderDeferredMgr);
 
@@ -92,6 +92,7 @@ RenderDeferredMgr::RenderDeferredMgr( bool gatherDepth,
    notifyType( RenderPassManager::RIT_Mesh );
    notifyType( RenderPassManager::RIT_Terrain );
    notifyType( RenderPassManager::RIT_Object );
+   notifyType( RenderPassManager::RIT_Probes );
 
    // We want a full-resolution buffer
    mTargetSizeType = RenderTexTargetBinManager::WindowSize;
@@ -186,10 +187,10 @@ bool RenderDeferredMgr::_updateTargets()
          mTargetChain[i]->attachTexture(GFXTextureTarget::Color2, mMatInfoTarget.getTexture());
    }
 
-   if (mLightMapTex.getFormat() != mTargetFormat || mLightMapTex.getWidthHeight() != mTargetSize || GFX->recentlyReset())
+   if (mLightMapTex.getFormat() != GFXFormatR16G16B16A16F || mLightMapTex.getWidthHeight() != mTargetSize || GFX->recentlyReset())
    {
       mLightMapTarget.release();
-      mLightMapTex.set(mTargetSize.x, mTargetSize.y, mTargetFormat,
+      mLightMapTex.set(mTargetSize.x, mTargetSize.y, GFXFormatR16G16B16A16F,
          &GFXRenderTargetProfile, avar("%s() - (line %d)", __FUNCTION__, __LINE__),
          1, GFXTextureManager::AA_MATCH_BACKBUFFER);
       mLightMapTarget.setTexture(mLightMapTex);
@@ -238,6 +239,8 @@ void RenderDeferredMgr::addElement( RenderInst *inst )
 
    const bool isTerrainInst = inst->type == RenderPassManager::RIT_Terrain;
 
+   const bool isProbeInst = inst->type == RenderPassManager::RIT_Probes;
+
    // Get the material if its a mesh.
    BaseMatInstance* matInst = NULL;
    if ( isMeshInst || isDecalMeshInst )
@@ -262,6 +265,8 @@ void RenderDeferredMgr::addElement( RenderInst *inst )
       elementList = &mElementList;
    else if ( isTerrainInst )
       elementList = &mTerrainElementList;
+   else if (isProbeInst)
+      elementList = &mProbeElementList;
    else
       elementList = &mObjectElementList;
 
@@ -294,6 +299,7 @@ void RenderDeferredMgr::sort()
 void RenderDeferredMgr::clear()
 {
    Parent::clear();
+   mProbeElementList.clear();
    mTerrainElementList.clear();
    mObjectElementList.clear();
 }

+ 3 - 0
Engine/source/renderInstance/renderDeferredMgr.h

@@ -92,6 +92,9 @@ protected:
 
    DeferredMatInstance *mDeferredMatInstance;
 
+   ///
+   Vector< MainSortElem > mProbeElementList;
+
    virtual void _registerFeatures();
    virtual void _unregisterFeatures();
    virtual bool _updateTargets();

+ 6 - 0
Engine/source/renderInstance/renderPassManager.cpp

@@ -61,6 +61,7 @@ const RenderInstType RenderPassManager::RIT_Custom("Custom");
 const RenderInstType RenderPassManager::RIT_Particle("Particle");
 const RenderInstType RenderPassManager::RIT_Occluder("Occluder");
 const RenderInstType RenderPassManager::RIT_Editor("Editor");
+const RenderInstType RenderPassManager::RIT_Probes("Probes");
 
 
 //*****************************************************************************
@@ -102,6 +103,11 @@ void OccluderRenderInst::clear()
    dMemset( this, 0, sizeof(OccluderRenderInst) );
 }
 
+void ProbeRenderInst::clear()
+{
+   dMemset(this, 0, sizeof(ProbeRenderInst));
+   //mCubemap);
+}
 
 IMPLEMENT_CONOBJECT(RenderPassManager);
 

+ 95 - 0
Engine/source/renderInstance/renderPassManager.h

@@ -37,6 +37,18 @@
 #ifndef _SCENEMANAGER_H_
 #include "scene/sceneManager.h"
 #endif
+#ifndef _SCENEMANAGER_H_
+#include "scene/sceneManager.h"
+#endif
+#ifndef _CUBEMAPDATA_H_
+#include "gfx/sim/cubemapData.h"
+#endif
+#ifndef _GFXPRIMITIVEBUFFER_H_
+#include "gfx/gfxPrimitiveBuffer.h"
+#endif
+#ifndef PROBEMANAGER_H
+#include "lighting/probeManager.h"
+#endif
 
 class SceneRenderState;
 class ISceneObject;
@@ -48,6 +60,7 @@ class LightInfo;
 struct RenderInst;
 class MatrixSet;
 class GFXPrimitiveBufferHandle;
+class CubemapData;
 
 /// A RenderInstType hash value.
 typedef U32 RenderInstTypeHash;
@@ -118,6 +131,7 @@ public:
    static const RenderInstType RIT_Particle;
    static const RenderInstType RIT_Occluder;
    static const RenderInstType RIT_Editor;
+   static const RenderInstType RIT_Probes;
 
 public:
 
@@ -463,4 +477,85 @@ struct OccluderRenderInst : public RenderInst
    void clear();
 };
 
+struct ProbeRenderInst : public RenderInst
+{
+   LinearColorF mAmbient;
+
+   MatrixF mTransform;
+
+   F32 mRadius;
+   F32 mIntensity;
+
+   Box3F mBounds;
+
+   GFXCubemapHandle *mCubemap;
+
+   GFXCubemapHandle *mIrradianceCubemap;
+
+   GFXTexHandle *mBRDFTexture;
+
+   /// The priority of this light used for
+   /// light and shadow scoring.
+   F32 mPriority;
+
+   /// A temporary which holds the score used
+   /// when prioritizing lights for rendering.
+   F32 mScore;
+
+   bool mIsSkylight;
+
+   /// Whether to render debugging visualizations
+   /// for this light.
+   bool mDebugRender;
+
+   GFXPrimitiveBufferHandle primBuffer;
+   GFXVertexBufferHandle<GFXVertexPC> vertBuffer;
+   U32 numPrims;
+   U32 numVerts;
+   Vector< U32 > numIndicesForPoly;
+
+   ProbeInfo::ProbeShapeType mProbeShapeType;
+
+   //Spherical Harmonics data
+   LinearColorF mSHTerms[9];
+   F32 mSHConstants[5];
+
+public:
+
+   ProbeRenderInst();
+   ~ProbeRenderInst();
+
+   // Copies data passed in from light
+   void set(const ProbeRenderInst *probeInfo);
+   void set(const ProbeInfo *probeInfo);
+
+   // Accessors
+   const MatrixF& getTransform() const { return mTransform; }
+   void setTransform(const MatrixF &xfm) { mTransform = xfm; }
+
+   Point3F getPosition() const { return mTransform.getPosition(); }
+   void setPosition(const Point3F &pos) { mTransform.setPosition(pos); }
+
+   VectorF getDirection() const { return mTransform.getForwardVector(); }
+   void setDirection(const VectorF &val);
+
+   const LinearColorF& getAmbient() const { return mAmbient; }
+   void setAmbient(const LinearColorF &val) { mAmbient = val; }
+
+   void setPriority(F32 priority) { mPriority = priority; }
+   F32 getPriority() const { return mPriority; }
+
+   void setScore(F32 score) { mScore = score; }
+   F32 getScore() const { return mScore; }
+
+   bool isDebugRenderingEnabled() const { return mDebugRender; }
+   void enableDebugRendering(bool value) { mDebugRender = value; }
+
+   // Builds the world to light view projection used for
+   // shadow texture and cookie lookups.
+   void getWorldToLightProj(MatrixF *outMatrix) const;
+
+   void clear();
+};
+
 #endif // _RENDERPASSMANAGER_H_

+ 956 - 0
Engine/source/renderInstance/renderProbeMgr.cpp

@@ -0,0 +1,956 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+#include "renderProbeMgr.h"
+#include "console/consoleTypes.h"
+#include "scene/sceneObject.h"
+#include "materials/materialManager.h"
+#include "scene/sceneRenderState.h"
+#include "math/util/sphereMesh.h"
+#include "math/util/matrixSet.h"
+#include "materials/processedMaterial.h"
+#include "renderInstance/renderDeferredMgr.h"
+#include "math/mPolyhedron.impl.h"
+#include "gfx/gfxTransformSaver.h"
+
+IMPLEMENT_CONOBJECT(RenderProbeMgr);
+
+ConsoleDocClass( RenderProbeMgr, 
+   "@brief A render bin which uses object callbacks for rendering.\n\n"
+   "This render bin gathers object render instances and calls its delegate "
+   "method to perform rendering.  It is used infrequently for specialized "
+   "scene objects which perform custom rendering.\n\n"
+   "@ingroup RenderBin\n" );
+
+S32 QSORT_CALLBACK AscendingReflectProbeInfluence(const void* a, const void* b)
+{
+   // Debug Profiling.
+   PROFILE_SCOPE(AdvancedLightBinManager_AscendingReflectProbeInfluence);
+
+   // Fetch asset definitions.
+   const ProbeRenderInst* pReflectProbeA = static_cast<ProbeRenderInst*>(((RenderBinManager::MainSortElem*)(a))->inst);
+   const ProbeRenderInst* pReflectProbeB = static_cast<ProbeRenderInst*>(((RenderBinManager::MainSortElem*)(b))->inst);
+
+   // Sort.
+   //First, immediate check on if either is a skylight. Skylight always gets the highest priority
+   //if (pReflectProbeA->mIsSkylight)
+   //   return 1;
+   //else if (pReflectProbeB->mIsSkylight)
+   //   return -1;
+   //No? then sort by score
+   if (pReflectProbeA->mScore > pReflectProbeB->mScore)
+	   return 1;
+   else if (pReflectProbeA->mScore < pReflectProbeB->mScore)
+	   return -1;
+   return  0;
+}
+
+RenderProbeMgr::RenderProbeMgr()
+: RenderBinManager(RenderPassManager::RIT_Probes, 1.0f, 1.0f)
+{
+   mReflectProbeMaterial = nullptr;
+   mSkylightMaterial = nullptr;
+}
+
+RenderProbeMgr::RenderProbeMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder)
+ : RenderBinManager(riType, renderOrder, processAddOrder)
+{  
+   mReflectProbeMaterial = nullptr;
+   mSkylightMaterial = nullptr;
+}
+
+void RenderProbeMgr::initPersistFields()
+{
+   Parent::initPersistFields();
+}
+
+void RenderProbeMgr::addElement(RenderInst *inst)
+{
+   // If this instance is translucent handle it in RenderTranslucentMgr
+   if (inst->translucentSort)
+      return;
+
+   //AssertFatal(inst->defaultKey != 0, "RenderMeshMgr::addElement() - Got null sort key... did you forget to set it?");
+
+   internalAddElement(inst);
+
+   ProbeRenderInst* probeInst = static_cast<ProbeRenderInst*>(inst);
+
+   if (probeInst->mIsSkylight)
+   {
+      addSkylightProbe(probeInst);
+   }
+   else
+   {
+      if (probeInst->mProbeShapeType == ProbeInfo::Sphere)
+         addSphereReflectionProbe(probeInst);
+      else
+         addConvexReflectionProbe(probeInst);
+   }
+}
+
+//remove
+//Con::setIntVariable("lightMetrics::activeReflectionProbes", mReflectProbeBin.size());
+//Con::setIntVariable("lightMetrics::culledReflectProbes", 0/*mNumLightsCulled*/);
+//
+
+GFXVertexBufferHandle<GFXVertexPC> RenderProbeMgr::getSphereMesh(U32 &outNumPrimitives, GFXPrimitiveBufferHandle &outPrimitives)
+{
+   static SphereMesh sSphereMesh;
+
+   if (mSphereGeometry.isNull())
+   {
+      const SphereMesh::TriangleMesh * sphereMesh = sSphereMesh.getMesh(3);
+      S32 numPoly = sphereMesh->numPoly;
+      mSpherePrimitiveCount = 0;
+      mSphereGeometry.set(GFX, numPoly * 3, GFXBufferTypeStatic);
+      mSphereGeometry.lock();
+      S32 vertexIndex = 0;
+
+      for (S32 i = 0; i<numPoly; i++)
+      {
+         mSpherePrimitiveCount++;
+
+         mSphereGeometry[vertexIndex].point = sphereMesh->poly[i].pnt[0];
+         mSphereGeometry[vertexIndex].color = ColorI::WHITE;
+         vertexIndex++;
+
+         mSphereGeometry[vertexIndex].point = sphereMesh->poly[i].pnt[1];
+         mSphereGeometry[vertexIndex].color = ColorI::WHITE;
+         vertexIndex++;
+
+         mSphereGeometry[vertexIndex].point = sphereMesh->poly[i].pnt[2];
+         mSphereGeometry[vertexIndex].color = ColorI::WHITE;
+         vertexIndex++;
+      }
+      mSphereGeometry.unlock();
+   }
+
+   outNumPrimitives = mSpherePrimitiveCount;
+   outPrimitives = NULL; // For now
+   return mSphereGeometry;
+}
+
+void RenderProbeMgr::addSkylightProbe(ProbeRenderInst *probeInfo)
+{
+   probeInfo->vertBuffer = getSphereMesh(probeInfo->numPrims, probeInfo->primBuffer);
+
+   if (!mSkylightMaterial)
+      mSkylightMaterial = _getSkylightMaterial();
+}
+
+void RenderProbeMgr::addSphereReflectionProbe(ProbeRenderInst *probeInfo)
+{
+   probeInfo->vertBuffer = getSphereMesh(probeInfo->numPrims, probeInfo->primBuffer);
+
+   if (!mReflectProbeMaterial)
+      mReflectProbeMaterial = _getReflectProbeMaterial();
+}
+
+void RenderProbeMgr::addConvexReflectionProbe(ProbeRenderInst *probeInfo)
+{
+   static const Point3F cubePoints[8] =
+   {
+      Point3F(1, -1, -1), Point3F(1, -1,  1), Point3F(1,  1, -1), Point3F(1,  1,  1),
+      Point3F(-1, -1, -1), Point3F(-1,  1, -1), Point3F(-1, -1,  1), Point3F(-1,  1,  1)
+   };
+
+   /*static const Point3F cubeNormals[6] =
+   {
+      Point3F(1,  0,  0), Point3F(-1,  0,  0), Point3F(0,  1,  0),
+      Point3F(0, -1,  0), Point3F(0,  0,  1), Point3F(0,  0, -1)
+   };*/
+
+   /*static const Point2F cubeTexCoords[4] =
+   {
+      Point2F(0,  0), Point2F(0, -1),
+      Point2F(1,  0), Point2F(1, -1)
+   };*/
+
+   static const U32 cubeFaces[36][3] =
+   {
+      { 3, 0, 3 },{ 0, 0, 0 },{ 1, 0, 1 },
+      { 2, 0, 2 },{ 0, 0, 0 },{ 3, 0, 3 },
+      { 7, 1, 1 },{ 4, 1, 2 },{ 5, 1, 0 },
+      { 6, 1, 3 },{ 4, 1, 2 },{ 7, 1, 1 },
+      { 3, 2, 1 },{ 5, 2, 2 },{ 2, 2, 0 },
+      { 7, 2, 3 },{ 5, 2, 2 },{ 3, 2, 1 },
+      { 1, 3, 3 },{ 4, 3, 0 },{ 6, 3, 1 },
+      { 0, 3, 2 },{ 4, 3, 0 },{ 1, 3, 3 },
+      { 3, 4, 3 },{ 6, 4, 0 },{ 7, 4, 1 },
+      { 1, 4, 2 },{ 6, 4, 0 },{ 3, 4, 3 },
+      { 2, 5, 1 },{ 4, 5, 2 },{ 0, 5, 0 },
+      { 5, 5, 3 },{ 4, 5, 2 },{ 2, 5, 1 }
+   };
+
+   // Fill the vertex buffer
+   GFXVertexPC *pVert = NULL;
+
+   probeInfo->numVerts = 36;
+
+   probeInfo->vertBuffer.set(GFX, 36, GFXBufferTypeStatic);
+   pVert = probeInfo->vertBuffer.lock();
+
+   Point3F halfSize = Point3F(probeInfo->mRadius, probeInfo->mRadius, probeInfo->mRadius);
+
+   for (U32 i = 0; i < 36; i++)
+   {
+      const U32& vdx = cubeFaces[i][0];
+      pVert[i].point = cubePoints[vdx] * halfSize;
+   }
+
+   probeInfo->vertBuffer.unlock();
+
+   // Fill the primitive buffer
+   U16 *pIdx = NULL;
+
+   probeInfo->primBuffer.set(GFX, 36, 12, GFXBufferTypeStatic);
+
+   probeInfo->primBuffer.lock(&pIdx);
+
+   for (U16 i = 0; i < 36; i++)
+      pIdx[i] = i;
+
+   probeInfo->primBuffer.unlock();
+
+   probeInfo->numPrims = 12;
+
+   if (!mReflectProbeMaterial)
+      mReflectProbeMaterial = _getReflectProbeMaterial();
+   //
+
+  // mReflectProbeBin.push_back(pEntry);
+}
+
+void RenderProbeMgr::_setupPerFrameParameters(const SceneRenderState *state)
+{
+   PROFILE_SCOPE(RenderProbeMgr_SetupPerFrameParameters);
+   const Frustum &frustum = state->getCameraFrustum();
+
+   MatrixF invCam(frustum.getTransform());
+   invCam.inverse();
+
+   const Point3F *wsFrustumPoints = frustum.getPoints();
+   const Point3F& cameraPos = frustum.getPosition();
+
+   // Perform a camera offset.  We need to manually perform this offset on the sun (or vector) light's
+   // polygon, which is at the far plane.
+   Point3F cameraOffsetPos = cameraPos;
+
+   // Now build the quad for drawing full-screen vector light
+   // passes.... this is a volatile VB and updates every frame.
+   FarFrustumQuadVert verts[4];
+   {
+      verts[0].point.set(wsFrustumPoints[Frustum::FarTopLeft] - cameraPos);
+      invCam.mulP(wsFrustumPoints[Frustum::FarTopLeft], &verts[0].normal);
+      verts[0].texCoord.set(-1.0, 1.0);
+      verts[0].tangent.set(wsFrustumPoints[Frustum::FarTopLeft] - cameraOffsetPos);
+
+      verts[1].point.set(wsFrustumPoints[Frustum::FarTopRight] - cameraPos);
+      invCam.mulP(wsFrustumPoints[Frustum::FarTopRight], &verts[1].normal);
+      verts[1].texCoord.set(1.0, 1.0);
+      verts[1].tangent.set(wsFrustumPoints[Frustum::FarTopRight] - cameraOffsetPos);
+
+      verts[2].point.set(wsFrustumPoints[Frustum::FarBottomLeft] - cameraPos);
+      invCam.mulP(wsFrustumPoints[Frustum::FarBottomLeft], &verts[2].normal);
+      verts[2].texCoord.set(-1.0, -1.0);
+      verts[2].tangent.set(wsFrustumPoints[Frustum::FarBottomLeft] - cameraOffsetPos);
+
+      verts[3].point.set(wsFrustumPoints[Frustum::FarBottomRight] - cameraPos);
+      invCam.mulP(wsFrustumPoints[Frustum::FarBottomRight], &verts[3].normal);
+      verts[3].texCoord.set(1.0, -1.0);
+      verts[3].tangent.set(wsFrustumPoints[Frustum::FarBottomRight] - cameraOffsetPos);
+   }
+   mFarFrustumQuadVerts.set(GFX, 4);
+   dMemcpy(mFarFrustumQuadVerts.lock(), verts, sizeof(verts));
+   mFarFrustumQuadVerts.unlock();
+
+   PlaneF farPlane(wsFrustumPoints[Frustum::FarBottomLeft], wsFrustumPoints[Frustum::FarTopLeft], wsFrustumPoints[Frustum::FarTopRight]);
+   PlaneF vsFarPlane(verts[0].normal, verts[1].normal, verts[2].normal);
+
+   MatrixSet &matrixSet = getRenderPass()->getMatrixSet();
+   matrixSet.restoreSceneViewProjection();
+
+   const MatrixF &worldToCameraXfm = matrixSet.getWorldToCamera();
+
+   MatrixF inverseViewMatrix = worldToCameraXfm;
+   //inverseViewMatrix.fullInverse();
+   //inverseViewMatrix.transpose();
+
+   //inverseViewMatrix = MatrixF::Identity;
+
+   // Parameters calculated, assign them to the materials
+   if (mSkylightMaterial != nullptr && mSkylightMaterial->matInstance != nullptr)
+   {
+      mSkylightMaterial->setViewParameters(frustum.getNearDist(),
+         frustum.getFarDist(),
+         frustum.getPosition(),
+         farPlane,
+         vsFarPlane, inverseViewMatrix);
+   }
+
+   if (mReflectProbeMaterial != nullptr && mReflectProbeMaterial->matInstance != nullptr)
+   {
+      mReflectProbeMaterial->setViewParameters(frustum.getNearDist(),
+         frustum.getFarDist(),
+         frustum.getPosition(),
+         farPlane,
+         vsFarPlane, inverseViewMatrix);
+   }
+}
+
+//-----------------------------------------------------------------------------
+// render objects
+//-----------------------------------------------------------------------------
+void RenderProbeMgr::render( SceneRenderState *state )
+{
+   PROFILE_SCOPE(RenderProbeMgr_render);
+
+   // Early out if nothing to draw.
+   if(!mElementList.size())
+      return;
+
+   GFXTransformSaver saver;
+
+   NamedTexTargetRef diffuseLightingTarget = NamedTexTarget::find("diffuseLighting");
+
+   if (diffuseLightingTarget.isNull())
+      return;
+
+   NamedTexTargetRef specularLightingTarget = NamedTexTarget::find("specularLighting");
+
+   if (specularLightingTarget.isNull())
+      return;
+
+   GFXTextureTargetRef probeLightingTargetRef = GFX->allocRenderToTextureTarget();
+
+   if (probeLightingTargetRef.isNull())
+      return;
+
+   probeLightingTargetRef->attachTexture(GFXTextureTarget::Color0, diffuseLightingTarget->getTexture());
+   probeLightingTargetRef->attachTexture(GFXTextureTarget::Color1, specularLightingTarget->getTexture());
+
+   GFX->pushActiveRenderTarget();
+   GFX->setActiveRenderTarget(probeLightingTargetRef);
+
+   GFX->setViewport(diffuseLightingTarget->getViewport());
+   //GFX->setViewport(specularLightingTarget->getViewport());
+
+   // Restore transforms
+   MatrixSet &matrixSet = getRenderPass()->getMatrixSet();
+   matrixSet.restoreSceneViewProjection();
+
+   const MatrixF &worldToCameraXfm = matrixSet.getWorldToCamera();
+
+   // Set up the SG Data
+   SceneData sgData;
+   sgData.init(state);
+
+   // Initialize and set the per-frame parameters after getting
+   // the vector light material as we use lazy creation.
+   _setupPerFrameParameters(state);
+
+   //Order the probes by size, biggest to smallest
+   dQsort(mElementList.address(), mElementList.size(), sizeof(const MainSortElem), AscendingReflectProbeInfluence);
+
+   //Specular
+   PROFILE_START(RenderProbeManager_ReflectProbeRender);
+
+   for (U32 i = 0; i<mElementList.size(); i++)
+   {
+      ProbeRenderInst *curEntry = static_cast<ProbeRenderInst*>(mElementList[i].inst);
+
+      if (curEntry->numPrims == 0)
+         continue;
+
+      if (curEntry->mIsSkylight && (!mSkylightMaterial || !mSkylightMaterial->matInstance))
+         continue;
+
+      if (!curEntry->mIsSkylight && (!mReflectProbeMaterial || !mReflectProbeMaterial->matInstance))
+         break;
+
+      //Setup
+      MatrixF probeTrans = curEntry->getTransform();
+
+      if (!curEntry->mIsSkylight)
+      {
+         if (curEntry->mProbeShapeType == ProbeInfo::Sphere)
+            probeTrans.scale(curEntry->mRadius * 1.01f);
+      }
+      else
+      {
+         probeTrans.scale(10); //force it to be big enough to surround the camera
+      }
+
+      sgData.objTrans = &probeTrans;
+
+      if(curEntry->mIsSkylight)
+         mSkylightMaterial->setSkylightParameters(curEntry, state, worldToCameraXfm);
+      else
+         mReflectProbeMaterial->setProbeParameters(curEntry, state, worldToCameraXfm);
+
+      // Set geometry
+      GFX->setVertexBuffer(curEntry->vertBuffer);
+      GFX->setPrimitiveBuffer(curEntry->primBuffer);
+
+      if (curEntry->mIsSkylight)
+      {
+         while (mSkylightMaterial->matInstance->setupPass(state, sgData))
+         {
+            // Set transforms
+            matrixSet.setWorld(*sgData.objTrans);
+            mSkylightMaterial->matInstance->setTransforms(matrixSet, state);
+            mSkylightMaterial->matInstance->setSceneInfo(state, sgData);
+
+            GFX->drawPrimitive(GFXTriangleList, 0, curEntry->numPrims);
+         }
+      }
+      else
+      {
+         while (mReflectProbeMaterial->matInstance->setupPass(state, sgData))
+         {
+            // Set transforms
+            matrixSet.setWorld(*sgData.objTrans);
+            mReflectProbeMaterial->matInstance->setTransforms(matrixSet, state);
+            mReflectProbeMaterial->matInstance->setSceneInfo(state, sgData);
+
+            GFX->drawPrimitive(GFXTriangleList, 0, curEntry->numPrims);
+         }
+      }
+   }
+
+   probeLightingTargetRef->resolve();
+   GFX->popActiveRenderTarget();
+
+   PROBEMGR->unregisterAllProbes();
+   PROFILE_END();
+
+   GFX->setVertexBuffer(NULL);
+   GFX->setPrimitiveBuffer(NULL);
+
+   // Fire off a signal to let others know that light-bin rendering is ending now
+   //getRenderSignal().trigger(state, this);
+}
+
+//
+RenderProbeMgr::ReflectProbeMaterialInfo::ReflectProbeMaterialInfo(const String &matName,
+   const GFXVertexFormat *vertexFormat)
+   : matInstance(NULL),
+   zNearFarInvNearFar(NULL),
+   farPlane(NULL),
+   vsFarPlane(NULL),
+   negFarPlaneDotEye(NULL),
+   probeWSPos(NULL),
+   attenuation(NULL),
+   radius(NULL),
+   invViewMat(NULL)
+{
+   Material *mat = MATMGR->getMaterialDefinitionByName(matName);
+   if (!mat)
+      return;
+
+   matInstance = new ReflectProbeMatInstance(*mat);
+
+   const Vector<GFXShaderMacro> &macros = Vector<GFXShaderMacro>();
+
+   for (U32 i = 0; i < macros.size(); i++)
+      matInstance->addShaderMacro(macros[i].name, macros[i].value);
+
+   matInstance->init(MATMGR->getDefaultFeatures(), vertexFormat);
+
+   attenuation = matInstance->getMaterialParameterHandle("$attenuation");
+   radius = matInstance->getMaterialParameterHandle("$radius");
+   probeLSPos = matInstance->getMaterialParameterHandle("$probeLSPos");
+   probeWSPos = matInstance->getMaterialParameterHandle("$probeWSPos");
+   farPlane = matInstance->getMaterialParameterHandle("$farPlane");
+   vsFarPlane = matInstance->getMaterialParameterHandle("$vsFarPlane");
+   negFarPlaneDotEye = matInstance->getMaterialParameterHandle("$negFarPlaneDotEye");
+   zNearFarInvNearFar = matInstance->getMaterialParameterHandle("$zNearFarInvNearFar");
+
+   invViewMat = matInstance->getMaterialParameterHandle("$invViewMat");
+
+   useCubemap = matInstance->getMaterialParameterHandle("$useCubemap");
+
+   cubemap = matInstance->getMaterialParameterHandle("$cubeMap");
+
+   eyePosWorld = matInstance->getMaterialParameterHandle("$eyePosWorld");
+   bbMin = matInstance->getMaterialParameterHandle("$bbMin");
+   bbMax = matInstance->getMaterialParameterHandle("$bbMax");
+
+   useSphereMode = matInstance->getMaterialParameterHandle("$useSphereMode");
+
+   for(U32 i=0; i < 9; i++)
+      shTerms[i] = matInstance->getMaterialParameterHandle(String::ToString("$SHTerms%d",i));
+
+   for (U32 i = 0; i < 5; i++)
+      shConsts[i] = matInstance->getMaterialParameterHandle(String::ToString("$SHConsts%d", i));
+}
+
+RenderProbeMgr::ReflectProbeMaterialInfo::~ReflectProbeMaterialInfo()
+{
+   SAFE_DELETE(matInstance);
+}
+
+void RenderProbeMgr::ReflectProbeMaterialInfo::setViewParameters(const F32 _zNear,
+   const F32 _zFar,
+   const Point3F &_eyePos,
+   const PlaneF &_farPlane,
+   const PlaneF &_vsFarPlane, const MatrixF &_inverseViewMatrix)
+{
+   MaterialParameters *matParams = matInstance->getMaterialParameters();
+
+   matParams->setSafe(farPlane, *((const Point4F *)&_farPlane));
+
+   matParams->setSafe(vsFarPlane, *((const Point4F *)&_vsFarPlane));
+
+   if (negFarPlaneDotEye->isValid())
+   {
+      // -dot( farPlane, eyePos )
+      const F32 negFarPlaneDotEyeVal = -(mDot(*((const Point3F *)&_farPlane), _eyePos) + _farPlane.d);
+      matParams->set(negFarPlaneDotEye, negFarPlaneDotEyeVal);
+   }
+
+   matParams->setSafe(zNearFarInvNearFar, Point4F(_zNear, _zFar, 1.0f / _zNear, 1.0f / _zFar));
+
+   matParams->setSafe(invViewMat, _inverseViewMatrix);
+
+   Point4F frPlane = *((const Point4F *)&_farPlane);
+   Point4F vsFrPlane = *((const Point4F *)&_vsFarPlane);
+   Point4F nearFarInvNearFar = Point4F(_zNear, _zFar, 1.0f / _zNear, 1.0f / _zFar);
+   const F32 negFarPlaneDotEyeVal = -(mDot(*((const Point3F *)&_farPlane), _eyePos) + _farPlane.d);
+}
+
+void RenderProbeMgr::ReflectProbeMaterialInfo::setProbeParameters(const ProbeRenderInst *probeInfo, const SceneRenderState* renderState, const MatrixF &worldViewOnly)
+{
+   //Set up the params
+   MaterialParameters *matParams = matInstance->getMaterialParameters();
+
+   matParams->setSafe(radius, probeInfo->mRadius);
+
+   Point3F probePos = probeInfo->getPosition();
+   //worldViewOnly.mulP(probeInfo->getPosition(), &probePos);
+   matParams->setSafe(probeWSPos, probePos);
+
+   worldViewOnly.mulP(probeInfo->getPosition(), &probePos);
+   matParams->setSafe(probeLSPos, probePos);
+
+   // Get the attenuation falloff ratio and normalize it.
+   Point3F attenRatio = Point3F(0.0f, 1.0f, 1.0f);
+   F32 total = attenRatio.x + attenRatio.y + attenRatio.z;
+   if (total > 0.0f)
+      attenRatio /= total;
+
+   F32 radius = probeInfo->mRadius;
+
+   Point2F attenParams((1.0f / radius) * attenRatio.y,
+      (1.0f / (radius * radius)) * attenRatio.z);
+
+   matParams->setSafe(attenuation, attenParams);
+
+   NamedTexTarget* deferredTexTarget = NamedTexTarget::find("deferred");
+
+   GFXTextureObject *deferredTexObject = deferredTexTarget->getTexture();
+   if (!deferredTexObject) return;
+
+   GFX->setTexture(0, deferredTexObject);
+
+   NamedTexTarget* matInfoTexTarget = NamedTexTarget::find("matinfo");
+
+   GFXTextureObject *matInfoTexObject = matInfoTexTarget->getTexture();
+   if (!matInfoTexObject) return;
+
+   GFX->setTexture(1, matInfoTexObject);
+
+   if (probeInfo->mCubemap && !probeInfo->mCubemap->isNull())
+   {
+      GFX->setCubeTexture(2, probeInfo->mCubemap->getPointer());
+   }
+   else
+   {
+      GFX->setCubeTexture(2, NULL);
+   }
+
+   if (probeInfo->mIrradianceCubemap && !probeInfo->mIrradianceCubemap->isNull())
+   {
+      GFX->setCubeTexture(3, probeInfo->mIrradianceCubemap->getPointer());
+   }
+   else
+   {
+      GFX->setCubeTexture(3, NULL);
+   }
+
+   if (probeInfo->mBRDFTexture && !probeInfo->mBRDFTexture->isNull())
+   {
+      GFX->setTexture(4, probeInfo->mBRDFTexture->getPointer());
+   }
+   else
+   {
+      GFX->setTexture(4, NULL);
+   }
+
+
+   matParams->setSafe(eyePosWorld, renderState->getCameraPosition());
+   matParams->setSafe(bbMin, probeInfo->mBounds.minExtents);
+   matParams->setSafe(bbMax, probeInfo->mBounds.maxExtents);
+
+   matParams->setSafe(useSphereMode, probeInfo->mProbeShapeType == ProbeInfo::Sphere ? 1.0f : 0.0f);
+
+   //SH Terms
+   //static AlignedArray<Point3F> shTermsArray(9, sizeof(Point3F));
+   //dMemset(shTermsArray.getBuffer(), 0, shTermsArray.getBufferSize());
+
+   for (U32 i = 0; i < 9; i++)
+   {
+      matParams->setSafe(shTerms[i], probeInfo->mSHTerms[i]);
+   }
+
+   for (U32 i = 0; i < 5; i++)
+   {
+      matParams->setSafe(shConsts[i], probeInfo->mSHConstants[i]);
+   }
+}
+
+
+bool ReflectProbeMatInstance::init(const FeatureSet &features, const GFXVertexFormat *vertexFormat)
+{
+   bool success = Parent::init(features, vertexFormat);
+
+   // If the initialization failed don't continue.
+   if (!success || !mProcessedMaterial || mProcessedMaterial->getNumPasses() == 0)
+      return false;
+   return true;
+}
+
+bool ReflectProbeMatInstance::setupPass(SceneRenderState *state, const SceneData &sgData)
+{
+   // Go no further if the material failed to initialize properly.
+   if (!mProcessedMaterial ||
+      mProcessedMaterial->getNumPasses() == 0)
+      return false;
+   
+   bool bRetVal = Parent::setupPass(state, sgData);;
+
+   AssertFatal(mProcessedMaterial->getNumPasses() > 0, "No passes created! Ohnoes");
+   const RenderPassData *rpd = mProcessedMaterial->getPass(0);
+   AssertFatal(rpd, "No render pass data!");
+   AssertFatal(rpd->mRenderStates[0], "No render state 0!");
+   
+   if (!mProjectionState)
+   {
+	   GFXStateBlockDesc desc;
+	   desc.setZReadWrite(false);
+	   desc.zWriteEnable = false;
+	   desc.setCullMode(GFXCullNone);
+	   desc.setBlend(true, GFXBlendOne, GFXBlendOne);
+	   mProjectionState = GFX->createStateBlock(desc);
+   }
+   // Now override stateblock with our own
+   GFX->setStateBlock(mProjectionState);
+
+   return bRetVal;
+}
+
+RenderProbeMgr::ReflectProbeMaterialInfo* RenderProbeMgr::_getReflectProbeMaterial()
+{
+   PROFILE_SCOPE(AdvancedLightBinManager_getReflectProbeMaterial);
+
+   //ReflectProbeMaterialInfo *info = NULL;
+
+   if (!mReflectProbeMaterial)
+
+      // Now create the material info object.
+      mReflectProbeMaterial = new ReflectProbeMaterialInfo("ReflectionProbeMaterial",
+         getGFXVertexFormat<GFXVertexPC>());
+
+   return mReflectProbeMaterial;
+}
+
+//
+RenderProbeMgr::SkylightMaterialInfo::SkylightMaterialInfo(const String &matName,
+   const GFXVertexFormat *vertexFormat)
+   : matInstance(NULL),
+   zNearFarInvNearFar(NULL),
+   farPlane(NULL),
+   vsFarPlane(NULL),
+   negFarPlaneDotEye(NULL),
+   invViewMat(NULL)
+{
+   Material *mat = MATMGR->getMaterialDefinitionByName(matName);
+   if (!mat)
+      return;
+
+   matInstance = new SkylightMatInstance(*mat);
+
+   const Vector<GFXShaderMacro> &macros = Vector<GFXShaderMacro>();
+
+   for (U32 i = 0; i < macros.size(); i++)
+      matInstance->addShaderMacro(macros[i].name, macros[i].value);
+
+   matInstance->init(MATMGR->getDefaultFeatures(), vertexFormat);
+
+   farPlane = matInstance->getMaterialParameterHandle("$farPlane");
+   vsFarPlane = matInstance->getMaterialParameterHandle("$vsFarPlane");
+   negFarPlaneDotEye = matInstance->getMaterialParameterHandle("$negFarPlaneDotEye");
+   zNearFarInvNearFar = matInstance->getMaterialParameterHandle("$zNearFarInvNearFar");
+
+   invViewMat = matInstance->getMaterialParameterHandle("$invViewMat");
+
+   useCubemap = matInstance->getMaterialParameterHandle("$useCubemap");
+   cubemap = matInstance->getMaterialParameterHandle("$cubeMap");
+
+   eyePosWorld = matInstance->getMaterialParameterHandle("$eyePosWorld");
+
+   for (U32 i = 0; i < 9; i++)
+      shTerms[i] = matInstance->getMaterialParameterHandle(String::ToString("$SHTerms%d", i));
+
+   for (U32 i = 0; i < 5; i++)
+      shConsts[i] = matInstance->getMaterialParameterHandle(String::ToString("$SHConsts%d", i));
+}
+
+RenderProbeMgr::SkylightMaterialInfo::~SkylightMaterialInfo()
+{
+   SAFE_DELETE(matInstance);
+}
+
+void RenderProbeMgr::SkylightMaterialInfo::setViewParameters(const F32 _zNear,
+   const F32 _zFar,
+   const Point3F &_eyePos,
+   const PlaneF &_farPlane,
+   const PlaneF &_vsFarPlane, const MatrixF &_inverseViewMatrix)
+{
+   MaterialParameters *matParams = matInstance->getMaterialParameters();
+
+   matParams->setSafe(farPlane, *((const Point4F *)&_farPlane));
+
+   matParams->setSafe(vsFarPlane, *((const Point4F *)&_vsFarPlane));
+
+   if (negFarPlaneDotEye->isValid())
+   {
+      // -dot( farPlane, eyePos )
+      const F32 negFarPlaneDotEyeVal = -(mDot(*((const Point3F *)&_farPlane), _eyePos) + _farPlane.d);
+      matParams->set(negFarPlaneDotEye, negFarPlaneDotEyeVal);
+   }
+
+   matParams->setSafe(zNearFarInvNearFar, Point4F(_zNear, _zFar, 1.0f / _zNear, 1.0f / _zFar));
+
+   matParams->setSafe(invViewMat, _inverseViewMatrix);
+
+   Point4F frPlane = *((const Point4F *)&_farPlane);
+   Point4F vsFrPlane = *((const Point4F *)&_vsFarPlane);
+   Point4F nearFarInvNearFar = Point4F(_zNear, _zFar, 1.0f / _zNear, 1.0f / _zFar);
+   const F32 negFarPlaneDotEyeVal = -(mDot(*((const Point3F *)&_farPlane), _eyePos) + _farPlane.d);
+}
+
+void RenderProbeMgr::SkylightMaterialInfo::setSkylightParameters(const ProbeRenderInst *probeInfo, const SceneRenderState* renderState, const MatrixF &worldViewOnly)
+{
+   //Set up the params
+   MaterialParameters *matParams = matInstance->getMaterialParameters();
+
+   NamedTexTarget* deferredTexTarget = NamedTexTarget::find("deferred");
+
+   GFXTextureObject *deferredTexObject = deferredTexTarget->getTexture();
+   if (!deferredTexObject) return;
+
+   GFX->setTexture(0, deferredTexObject);
+
+   NamedTexTarget* matInfoTexTarget = NamedTexTarget::find("matinfo");
+
+   GFXTextureObject *matInfoTexObject = matInfoTexTarget->getTexture();
+   if (!matInfoTexObject) return;
+
+   GFX->setTexture(1, matInfoTexObject);
+
+   if (probeInfo->mCubemap && !probeInfo->mCubemap->isNull())
+   {
+      GFX->setCubeTexture(2, probeInfo->mCubemap->getPointer());
+   }
+   else
+   {
+      GFX->setCubeTexture(2, NULL);
+   }
+
+   if (probeInfo->mIrradianceCubemap && !probeInfo->mIrradianceCubemap->isNull())
+   {
+      GFX->setCubeTexture(3, probeInfo->mIrradianceCubemap->getPointer());
+   }
+   else
+   {
+      GFX->setCubeTexture(3, NULL);
+   }
+
+   if (probeInfo->mBRDFTexture && !probeInfo->mBRDFTexture->isNull())
+   {
+      GFX->setTexture(4, probeInfo->mBRDFTexture->getPointer());
+   }
+   else
+   {
+      GFX->setTexture(4, NULL);
+   }
+
+   matParams->setSafe(eyePosWorld, renderState->getCameraPosition());
+
+   for (U32 i = 0; i < 9; i++)
+   {
+      matParams->setSafe(shTerms[i], probeInfo->mSHTerms[i]);
+   }
+
+   for (U32 i = 0; i < 5; i++)
+   {
+      matParams->setSafe(shConsts[i], probeInfo->mSHConstants[i]);
+   }
+}
+
+
+bool SkylightMatInstance::init(const FeatureSet &features, const GFXVertexFormat *vertexFormat)
+{
+   bool success = Parent::init(features, vertexFormat);
+
+   // If the initialization failed don't continue.
+   if (!success || !mProcessedMaterial || mProcessedMaterial->getNumPasses() == 0)
+      return false;
+
+   return true;
+}
+
+bool SkylightMatInstance::setupPass(SceneRenderState *state, const SceneData &sgData)
+{
+   // Go no further if the material failed to initialize properly.
+   if (!mProcessedMaterial ||
+      mProcessedMaterial->getNumPasses() == 0)
+      return false;
+
+   bool bRetVal = Parent::setupPass(state, sgData);;
+
+   AssertFatal(mProcessedMaterial->getNumPasses() > 0, "No passes created! Ohnoes");
+   const RenderPassData *rpd = mProcessedMaterial->getPass(0);
+   AssertFatal(rpd, "No render pass data!");
+   AssertFatal(rpd->mRenderStates[0], "No render state 0!");
+
+   if (!mProjectionState)
+   {
+	  GFXStateBlockDesc desc;
+	  desc.setZReadWrite(false);
+	  desc.zWriteEnable = false;
+	  desc.setCullMode(GFXCullNone);
+	  desc.setBlend(true, GFXBlendOne, GFXBlendOne);
+      mProjectionState = GFX->createStateBlock(desc);
+   }
+   // Now override stateblock with our own
+   GFX->setStateBlock(mProjectionState);
+
+   return bRetVal;
+}
+
+RenderProbeMgr::SkylightMaterialInfo* RenderProbeMgr::_getSkylightMaterial()
+{
+   PROFILE_SCOPE(AdvancedLightBinManager_getSkylightMaterial);
+
+   //ReflectProbeMaterialInfo *info = NULL;
+
+   if (!mSkylightMaterial)
+
+      // Now create the material info object.
+      mSkylightMaterial = new SkylightMaterialInfo("SklyightMaterial",
+         getGFXVertexFormat<GFXVertexPC>());
+
+   return mSkylightMaterial;
+}
+
+//
+//
+ProbeRenderInst::ProbeRenderInst()
+   : mTransform(true),
+   mAmbient(0.0f, 0.0f, 0.0f, 1.0f),
+   mPriority(1.0f),
+   mScore(0.0f),
+   mDebugRender(false),
+   mCubemap(NULL),
+   mRadius(1.0f),
+   mIntensity(1.0f)
+{
+}
+
+ProbeRenderInst::~ProbeRenderInst()
+{
+   SAFE_DELETE(mCubemap);
+}
+
+void ProbeRenderInst::set(const ProbeRenderInst *probeInfo)
+{
+   mTransform = probeInfo->mTransform;
+   mAmbient = probeInfo->mAmbient;
+   mCubemap = probeInfo->mCubemap;
+   mIrradianceCubemap = probeInfo->mIrradianceCubemap;
+   mBRDFTexture = probeInfo->mBRDFTexture;
+   mRadius = probeInfo->mRadius;
+   mIntensity = probeInfo->mIntensity;
+   mProbeShapeType = probeInfo->mProbeShapeType;
+   numPrims = probeInfo->numPrims;
+   numVerts = probeInfo->numVerts;
+   numIndicesForPoly = probeInfo->numIndicesForPoly;
+   mBounds = probeInfo->mBounds;
+   mScore = probeInfo->mScore;
+   mIsSkylight = probeInfo->mIsSkylight;
+
+   for (U32 i = 0; i < 9; i++)
+   {
+      mSHTerms[i] = probeInfo->mSHTerms[i];
+   }
+
+   for (U32 i = 0; i < 5; i++)
+   {
+      mSHConstants[i] = probeInfo->mSHConstants[i];
+   }
+}
+
+void ProbeRenderInst::set(const ProbeInfo *probeInfo)
+{
+   mTransform = probeInfo->mTransform;
+   mAmbient = probeInfo->mAmbient;
+   mCubemap = probeInfo->mCubemap;
+   mIrradianceCubemap = probeInfo->mIrradianceCubemap;
+   mBRDFTexture = probeInfo->mBRDFTexture;
+   mRadius = probeInfo->mRadius;
+   mIntensity = probeInfo->mIntensity;
+   mProbeShapeType = probeInfo->mProbeShapeType;
+   numPrims = probeInfo->numPrims;
+   numVerts = probeInfo->numVerts;
+   numIndicesForPoly = probeInfo->numIndicesForPoly;
+   mBounds = probeInfo->mBounds;
+   mScore = probeInfo->mScore;
+   mIsSkylight = probeInfo->mIsSkylight;
+
+   for (U32 i = 0; i < 9; i++)
+   {
+      mSHTerms[i] = probeInfo->mSHTerms[i];
+   }
+
+   for (U32 i = 0; i < 5; i++)
+   {
+      mSHConstants[i] = probeInfo->mSHConstants[i];
+   }
+}
+
+void ProbeRenderInst::getWorldToLightProj(MatrixF *outMatrix) const
+{
+   *outMatrix = getTransform();
+   outMatrix->inverse();
+}

+ 213 - 0
Engine/source/renderInstance/renderProbeMgr.h

@@ -0,0 +1,213 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+#ifndef RENDER_PROBE_MGR_H
+#define RENDER_PROBE_MGR_H
+
+#ifndef _RENDERBINMANAGER_H_
+#include "renderInstance/renderBinManager.h"
+#endif
+#ifndef _MATINSTANCE_H_
+#include "materials/matInstance.h"
+#endif
+#ifndef _MATTEXTURETARGET_H_
+#include "materials/matTextureTarget.h"
+#endif
+#ifndef _GFXPRIMITIVEBUFFER_H_
+#include "gfx/gfxPrimitiveBuffer.h"
+#endif
+#ifndef _GFXVERTEXBUFFER_H_
+#include "gfx/gfxVertexBuffer.h"
+#endif
+
+class ReflectProbeMatInstance : public MatInstance
+{
+   typedef MatInstance Parent;
+protected:
+   MaterialParameterHandle *mProbeParamsSC;
+   bool mInternalPass;
+
+   GFXStateBlockRef mProjectionState;
+
+public:
+   ReflectProbeMatInstance(Material &mat) : Parent(mat), mProbeParamsSC(NULL), mInternalPass(false), mProjectionState(NULL){}
+
+   virtual bool init(const FeatureSet &features, const GFXVertexFormat *vertexFormat);
+   virtual bool setupPass(SceneRenderState *state, const SceneData &sgData);
+};
+
+class SkylightMatInstance : public MatInstance
+{
+   typedef MatInstance Parent;
+protected:
+   MaterialParameterHandle * mSkylightParamsSC;
+   bool mInternalPass;
+
+   GFXStateBlockRef mProjectionState;
+
+public:
+   SkylightMatInstance(Material &mat) : Parent(mat), mSkylightParamsSC(NULL), mInternalPass(false), mProjectionState(NULL) {}
+
+   virtual bool init(const FeatureSet &features, const GFXVertexFormat *vertexFormat);
+   virtual bool setupPass(SceneRenderState *state, const SceneData &sgData);
+};
+
+//**************************************************************************
+// RenderObjectMgr
+//**************************************************************************
+class RenderProbeMgr : public RenderBinManager
+{
+   typedef RenderBinManager Parent;
+public:
+   typedef GFXVertexPNTT FarFrustumQuadVert;
+
+protected:
+   struct ReflectProbeMaterialInfo
+   {
+      ReflectProbeMatInstance *matInstance;
+
+      // { zNear, zFar, 1/zNear, 1/zFar }
+      MaterialParameterHandle *zNearFarInvNearFar;
+
+      // Far frustum plane (World Space)
+      MaterialParameterHandle *farPlane;
+
+      // Far frustum plane (View Space)
+      MaterialParameterHandle *vsFarPlane;
+
+      // -dot( farPlane, eyePos )
+      MaterialParameterHandle *negFarPlaneDotEye;
+
+      // Inverse View matrix
+      MaterialParameterHandle *invViewMat;
+
+      // Light Parameters
+      MaterialParameterHandle *probeLSPos;
+      MaterialParameterHandle *probeWSPos;
+      MaterialParameterHandle *attenuation;
+      MaterialParameterHandle *radius;
+
+      MaterialParameterHandle *useCubemap;
+      MaterialParameterHandle *cubemap;
+
+      MaterialParameterHandle *eyePosWorld;
+      MaterialParameterHandle *bbMin;
+      MaterialParameterHandle *bbMax;
+
+      MaterialParameterHandle *useSphereMode;
+
+      MaterialParameterHandle *shTerms[9];
+      MaterialParameterHandle *shConsts[5];
+
+      ReflectProbeMaterialInfo(const String &matName, const GFXVertexFormat *vertexFormat);
+
+      virtual ~ReflectProbeMaterialInfo();
+
+
+      void setViewParameters(const F32 zNear,
+         const F32 zFar,
+         const Point3F &eyePos,
+         const PlaneF &farPlane,
+         const PlaneF &_vsFarPlane,
+         const MatrixF &_inverseViewMatrix);
+
+      void setProbeParameters(const ProbeRenderInst *probe, const SceneRenderState* renderState, const MatrixF &worldViewOnly);
+   };
+
+   struct SkylightMaterialInfo
+   {
+      SkylightMatInstance *matInstance;
+
+      // { zNear, zFar, 1/zNear, 1/zFar }
+      MaterialParameterHandle *zNearFarInvNearFar;
+
+      // Far frustum plane (World Space)
+      MaterialParameterHandle *farPlane;
+
+      // Far frustum plane (View Space)
+      MaterialParameterHandle *vsFarPlane;
+
+      // -dot( farPlane, eyePos )
+      MaterialParameterHandle *negFarPlaneDotEye;
+
+      // Inverse View matrix
+      MaterialParameterHandle *invViewMat;
+
+      MaterialParameterHandle *useCubemap;
+      MaterialParameterHandle *cubemap;
+
+      MaterialParameterHandle *eyePosWorld;
+
+      MaterialParameterHandle *shTerms[9];
+      MaterialParameterHandle *shConsts[5];
+
+      SkylightMaterialInfo(const String &matName, const GFXVertexFormat *vertexFormat);
+
+      virtual ~SkylightMaterialInfo();
+
+
+      void setViewParameters(const F32 zNear,
+         const F32 zFar,
+         const Point3F &eyePos,
+         const PlaneF &farPlane,
+         const PlaneF &_vsFarPlane,
+         const MatrixF &_inverseViewMatrix);
+
+      void setSkylightParameters(const ProbeRenderInst *probe, const SceneRenderState* renderState, const MatrixF &worldViewOnly);
+   };
+
+   GFXVertexBufferHandle<FarFrustumQuadVert> mFarFrustumQuadVerts;
+
+   GFXVertexBufferHandle<GFXVertexPC> getSphereMesh(U32 &outNumPrimitives, GFXPrimitiveBufferHandle &outPrimitives);
+
+   // Convex geometry for lights
+   GFXVertexBufferHandle<GFXVertexPC> mSphereGeometry;
+
+   GFXPrimitiveBufferHandle mSphereIndices;
+
+   U32 mSpherePrimitiveCount;
+
+public:
+   RenderProbeMgr();
+   RenderProbeMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder);
+
+   // RenderBinMgr
+   void _setupPerFrameParameters(const SceneRenderState *state);
+   virtual void addElement(RenderInst *inst);
+   virtual void render(SceneRenderState * state);
+
+   // ConsoleObject
+   static void initPersistFields();
+   DECLARE_CONOBJECT(RenderProbeMgr);
+
+   ReflectProbeMaterialInfo* mReflectProbeMaterial;
+   ReflectProbeMaterialInfo* _getReflectProbeMaterial();
+
+   SkylightMaterialInfo* mSkylightMaterial;
+   SkylightMaterialInfo* _getSkylightMaterial();
+
+   // Add a reflection probe to the bin
+   void addSkylightProbe(ProbeRenderInst *probeInfo);
+   void addSphereReflectionProbe(ProbeRenderInst *probeInfo);
+   void addConvexReflectionProbe(ProbeRenderInst *probeInfo);
+};
+
+#endif // RENDER_PROBE_MGR_H

+ 25 - 0
Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h

@@ -670,4 +670,29 @@ public:
    virtual String getName() { return "Hardware Skinning"; }
 };
 
+/// Reflection Probes
+class ReflectionProbeFeatGLSL : public ShaderFeatureGLSL
+{
+public:
+   virtual void processVert(Vector<ShaderComponent*> &componentList,
+      const MaterialFeatureData &fd) {}
+
+   virtual void processPix(Vector<ShaderComponent*> &componentList,
+      const MaterialFeatureData &fd) {}
+
+   virtual Resources getResources(const MaterialFeatureData &fd) {
+      return Resources();
+   }
+
+   // Sets textures and texture flags for current pass
+   virtual void setTexData(Material::StageData &stageDat,
+      const MaterialFeatureData &fd,
+      RenderPassData &passData,
+      U32 &texIndex) {}
+
+   virtual String getName()
+   {
+      return "Reflection Probes";
+   }
+};
 #endif // _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_

+ 222 - 0
Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp

@@ -2933,3 +2933,225 @@ void HardwareSkinningFeatureHLSL::processVert(   Vector<ShaderComponent*> &compo
 
    output = meta;
 }
+
+//****************************************************************************
+// ReflectionProbeFeatHLSL
+//****************************************************************************
+
+ReflectionProbeFeatHLSL::ReflectionProbeFeatHLSL()
+   : mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/lighting.hlsl"))
+{
+   addDependency(&mDep);
+}
+
+void ReflectionProbeFeatHLSL::processPix(Vector<ShaderComponent*> &componentList,
+   const MaterialFeatureData &fd)
+{
+   // Skip out on realtime lighting if we don't have a normal
+   // or we're doing some sort of baked lighting.
+   //
+   // TODO: We can totally detect for this in the material
+   // feature setup... we should move it out of here!
+   //
+   if (fd.features[MFT_LightMap] || fd.features[MFT_ToneMap] || fd.features[MFT_VertLit])
+      return;
+
+   ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>(componentList[C_CONNECTOR]);
+
+   MultiLine *meta = new MultiLine;
+
+   // Look for a wsNormal or grab it from the connector.
+   Var *wsNormal = (Var*)LangElement::find("wsNormal");
+   if (!wsNormal)
+   {
+      wsNormal = connectComp->getElement(RT_TEXCOORD);
+      wsNormal->setName("wsNormal");
+      wsNormal->setStructName("IN");
+      wsNormal->setType("float3");
+
+      // If we loaded the normal its our responsibility
+      // to normalize it... the interpolators won't.
+      //
+      // Note we cast to half here to get partial precision
+      // optimized code which is an acceptable loss of
+      // precision for normals and performs much better
+      // on older Geforce cards.
+      //
+      meta->addStatement(new GenOp("   @ = normalize( half3( @ ) );\r\n", wsNormal, wsNormal));
+   }
+
+   // Now the wsPosition and wsView.
+   Var *wsPosition = getInWsPosition(componentList);
+   Var *wsView = getWsView(wsPosition, meta);
+   
+   Var *metalness = (Var*)LangElement::find("metalness");
+   Var *smoothness = (Var*)LangElement::find("smoothness");
+   if (!fd.features[MFT_SpecularMap])
+   {
+      if (!metalness)
+      {
+         metalness = new Var("metalness", "float");
+         metalness->uniform = true;
+         metalness->constSortPos = cspPotentialPrimitive;
+      }
+	  if (!smoothness)
+	  {
+		  smoothness = new Var("smoothness", "float");
+		  smoothness->uniform = true;
+		  smoothness->constSortPos = cspPotentialPrimitive;
+	  }
+   }
+
+   Var *albedo = (Var*)LangElement::find(getOutputTargetVarName(ShaderFeature::DefaultTarget));
+
+   //Reflection Probe WIP
+   Var *inProbePos = new Var("inProbePos", "float3");
+   inProbePos->arraySize = 4;
+   inProbePos->uniform = true;
+   inProbePos->constSortPos = cspPotentialPrimitive;
+
+   Var *inProbeRadius = new Var("inProbeRadius", "float");
+   inProbeRadius->arraySize = 4;
+   inProbeRadius->uniform = true;
+   inProbeRadius->constSortPos = cspPotentialPrimitive;
+
+   Var *inProbeBoxMin = new Var("inProbeBoxMin", "float3");
+   inProbeBoxMin->arraySize = 4;
+   inProbeBoxMin->uniform = true;
+   inProbeBoxMin->constSortPos = cspPotentialPrimitive;
+
+   Var *inProbeBoxMax = new Var("inProbeBoxMax", "float3");
+   inProbeBoxMax->arraySize = 4;
+   inProbeBoxMax->uniform = true;
+   inProbeBoxMax->constSortPos = cspPotentialPrimitive;
+
+   Var *inProbeIsSphere = new Var("inProbeIsSphere", "float");
+   inProbeIsSphere->arraySize = 4;
+   inProbeIsSphere->uniform = true;
+   inProbeIsSphere->constSortPos = cspPotentialPrimitive;
+
+   Var *inProbeLocalPos = new Var("inProbeLocalPos", "float3");
+   inProbeLocalPos->arraySize = 4;
+   inProbeLocalPos->uniform = true;
+   inProbeLocalPos->constSortPos = cspPotentialPrimitive;
+
+   Var *inProbeCubemap = new Var("inProbeCubemap", "SamplerState");
+   //inProbeCubemap->arraySize = 4;
+   inProbeCubemap->uniform = true;
+   inProbeCubemap->sampler = true;
+   inProbeCubemap->constNum = Var::getTexUnitNum();     // used as texture unit num here
+
+   Var *inProbeCubemapTex = new Var("inProbeCubemapTex", "TextureCube");
+   //inProbeCubemapTex->arraySize = 4;
+   inProbeCubemapTex->uniform = true;
+   inProbeCubemapTex->texture = true;
+   inProbeCubemapTex->constNum = inProbeCubemap->constNum;
+
+   //Var *nDotL = new Var("nDotL", "float3");
+   //meta->addStatement(new GenOp("   @ = abs(dot(@,@);\r\n", new DecOp(nDotL), wsView, wsNormal));
+
+   Var *probeVec = new Var("probeVec", "float3");
+   meta->addStatement(new GenOp("   @ = @[0] - @;\r\n", new DecOp(probeVec), inProbePos, wsPosition));
+
+   Var *nDotL = new Var("nDotL", "float");
+   meta->addStatement(new GenOp("   @ = abs(dot(@, @));\r\n", new DecOp(nDotL), probeVec, wsNormal));
+
+   meta->addStatement(new GenOp("      \r\n"));
+
+   Var *reflectDir = new Var("reflectDir", "float3");
+   meta->addStatement(new GenOp("   @ = reflect(-float4(@,0),float4(@,@)).xyz;\r\n", new DecOp(reflectDir), wsView, wsNormal, nDotL));
+
+   meta->addStatement(new GenOp("      \r\n"));
+
+   Var *nrDir = new Var("nrDir", "float3");
+   meta->addStatement(new GenOp("   @ = normalize(@);\r\n", new DecOp(nrDir), reflectDir));
+
+   Var *rbmax = new Var("rbmax", "float3");
+   meta->addStatement(new GenOp("   @ = (@[0] - @) / @;\r\n", new DecOp(rbmax), inProbeBoxMax, wsPosition, nrDir));
+
+   Var *rbmin = new Var("rbmin", "float3");
+   meta->addStatement(new GenOp("   @ = (@[0] - @) / @;\r\n", new DecOp(rbmin), inProbeBoxMin, wsPosition, nrDir));
+
+   Var *rbMinMax = new Var("rbMinMax", "float3");
+   meta->addStatement(new GenOp("   @ = (@ > 0.0) ? @ : @;\r\n", new DecOp(rbMinMax), nrDir, rbmax, rbmin));
+
+   meta->addStatement(new GenOp("      \r\n"));
+
+   Var *fa = new Var("fa", "float3");
+   meta->addStatement(new GenOp("   @ = min(min(@.x,@.y),@.z);\r\n", new DecOp(fa), rbMinMax, rbMinMax, rbMinMax));
+   
+  
+   meta->addStatement(new GenOp("/*   if (dot( @, @ ) < 0.0f)\r\n", probeVec, wsNormal));
+   meta->addStatement(new GenOp("      clip(@);  */\r\n", fa));
+ 
+
+   meta->addStatement(new GenOp("      \r\n"));
+
+   Var *posOnBox = new Var("posOnBox", "float3");
+   meta->addStatement(new GenOp("   @ = @ + @ * @;\r\n", new DecOp(posOnBox), wsPosition, nrDir, fa));
+   meta->addStatement(new GenOp("   @ = @ - @[0];\r\n", reflectDir, posOnBox, inProbePos));
+
+   meta->addStatement(new GenOp("      \r\n"));
+
+   Var *probeColor = new Var("wipProbeColor", "float3");
+
+   Var *probeMip = new Var("probeMip", "float");
+   meta->addStatement(new GenOp("   @ = min((1.0 - @)*11.0 + 1.0, 8.0);\r\n", new DecOp(probeMip), smoothness));
+   meta->addStatement(new GenOp("   @ = @.SampleLevel(@, @, @).rgb;\r\n", new DecOp(probeColor), inProbeCubemapTex, inProbeCubemap, reflectDir, probeMip));
+   //meta->addStatement(new GenOp("   @ = @.rgb;\r\n", new DecOp(probeColor), inProbeTestColor));
+
+   Var *FRESNEL_BIAS = new Var("FRESNEL_BIAS", "float");
+   meta->addStatement(new GenOp("   @  = 0.1;\r\n", new DecOp(FRESNEL_BIAS)));
+
+   Var *FRESNEL_POWER = new Var("FRESNEL_POWER", "float");
+   meta->addStatement(new GenOp("   @  = 1;\r\n", new DecOp(FRESNEL_POWER)));
+
+   Var *angle = new Var("angle", "float");
+   meta->addStatement(new GenOp("   @  = saturate(dot(@, @));\r\n", new DecOp(angle), wsView, wsNormal));
+   meta->addStatement(new GenOp("\r\n"));
+
+   if (metalness)
+   {
+      Var *dColor = new Var("difColor", "float3");
+      Var *reflectColor = new Var("reflctColor", "float3");
+
+      meta->addStatement(new GenOp("   @ = @.rgb - (@.rgb * @);\r\n", new DecOp(dColor), albedo, albedo, metalness));
+      meta->addStatement(new GenOp("   @ = @; //@.rgb*(@).rgb*@;\r\n", new DecOp(reflectColor), probeColor, albedo, probeColor, metalness));
+
+      meta->addStatement(new GenOp("   @.rgb  = simpleFresnel(@, @, @, @, @, @);\r\n", albedo, dColor, reflectColor, metalness, angle, FRESNEL_BIAS, FRESNEL_POWER));
+   }
+   //else if (lerpVal)
+   //   meta->addStatement(new GenOp("   @ *= float4(@.rgb*@.a, @.a);\r\n", targ, texCube, lerpVal, targ));
+   else
+   {
+      meta->addStatement(new GenOp("   @.rgb  = simpleFresnel(@.rgb, @, 0, @, @, @));\r\n", albedo, albedo, probeColor, angle, FRESNEL_BIAS, FRESNEL_POWER));
+   }
+
+   output = meta;
+}
+
+ShaderFeature::Resources ReflectionProbeFeatHLSL::getResources(const MaterialFeatureData &fd)
+{
+   Resources res;
+
+   //res.numTex = 4;
+   //res.numTexReg = 4;
+
+   res.numTex = 4;
+   res.numTexReg = 4;
+
+   return res;
+}
+
+void ReflectionProbeFeatHLSL::setTexData(Material::StageData &stageDat,
+   const MaterialFeatureData &stageFeatures,
+   RenderPassData &passData,
+   U32 &texIndex)
+{
+   if (stageFeatures.features[MFT_ReflectionProbes])
+   {
+      // assuming here that it is a scenegraph cubemap
+      passData.mSamplerNames[texIndex] = "inProbeCubemap";
+      passData.mTexType[texIndex++] = Material::SGCube;
+   }
+}

+ 26 - 0
Engine/source/shaderGen/HLSL/shaderFeatureHLSL.h

@@ -672,4 +672,30 @@ public:
    virtual String getName() { return "Hardware Skinning"; }
 };
 
+/// Reflection Probes
+class ReflectionProbeFeatHLSL : public ShaderFeatureHLSL
+{
+protected:
+   ShaderIncludeDependency mDep;
+
+public:
+   ReflectionProbeFeatHLSL();
+
+   virtual void processPix(Vector<ShaderComponent*> &componentList,
+      const MaterialFeatureData &fd);
+
+   virtual Resources getResources(const MaterialFeatureData &fd);
+
+   // Sets textures and texture flags for current pass
+   virtual void setTexData(Material::StageData &stageDat,
+      const MaterialFeatureData &fd,
+      RenderPassData &passData,
+      U32 &texIndex);
+
+   virtual String getName()
+   {
+      return "Reflection Probes";
+   }
+};
+
 #endif // _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_

+ 1 - 0
Engine/source/shaderGen/HLSL/shaderGenHLSLInit.cpp

@@ -64,6 +64,7 @@ void _initShaderGenHLSL( ShaderGen *shaderGen )
    FEATUREMGR->registerFeature( MFT_DetailMap, new DetailFeatHLSL );
 	FEATUREMGR->registerFeature( MFT_StaticCubemap, new NamedFeatureHLSL( "Static Cubemap" ) );
    FEATUREMGR->registerFeature( MFT_CubeMap, new ReflectCubeFeatHLSL );
+   FEATUREMGR->registerFeature( MFT_ReflectionProbes, new ReflectionProbeFeatHLSL);
    FEATUREMGR->registerFeature( MFT_PixSpecular, new PixelSpecularHLSL );
    FEATUREMGR->registerFeature( MFT_InvertSmoothness, new NamedFeatureHLSL( "Roughest = 1.0" ) );
    FEATUREMGR->registerFeature( MFT_IsTranslucent, new NamedFeatureHLSL( "Translucent" ) );

+ 9 - 0
Engine/source/shaderGen/shaderGenVars.cpp

@@ -68,6 +68,15 @@ const String ShaderGenVars::specularColor("$specularColor");
 const String ShaderGenVars::smoothness("$smoothness");
 const String ShaderGenVars::metalness("$metalness");
 
+//Reflection Probes
+const String ShaderGenVars::probePosition("$inProbePos");
+const String ShaderGenVars::probeRadius("$inProbeRadius");
+const String ShaderGenVars::probeBoxMin("$inProbeBoxMin");
+const String ShaderGenVars::probeBoxMax("$inProbeBoxMax");
+const String ShaderGenVars::probeLocalPos("$inProbeLocalPos");
+const String ShaderGenVars::probeIsSphere("$inProbeIsSphere");
+const String ShaderGenVars::probeCubemap("$inProbeCubemap");
+
 // These are ignored by the D3D layers.
 const String ShaderGenVars::fogMap("$fogMap");
 const String ShaderGenVars::dlightMap("$dlightMap");

+ 9 - 0
Engine/source/shaderGen/shaderGenVars.h

@@ -80,6 +80,15 @@ struct ShaderGenVars
    const static String specularColor;
    const static String smoothness;
    const static String metalness;
+
+   //Reflection Probes
+   const static String probePosition;
+   const static String probeRadius;
+   const static String probeBoxMin;
+   const static String probeBoxMax;
+   const static String probeLocalPos;
+   const static String probeIsSphere;
+   const static String probeCubemap;
    
    // Textures
    const static String fogMap;

+ 197 - 8
Engine/source/terrain/glsl/terrFeatureGLSL.cpp

@@ -49,6 +49,7 @@ namespace
       FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatGLSL );
       FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureGLSL( "Terrain Side Projection" ) );
       FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatGLSL );
+      FEATUREMGR->registerFeature( MFT_TerrainCompositeMap, new TerrainCompositeMapFeatGLSL );
       FEATUREMGR->registerFeature( MFT_DeferredTerrainBlankInfoMap, new TerrainBlankInfoMapFeatGLSL );
    }
 
@@ -141,6 +142,24 @@ Var* TerrainFeatGLSL::_getNormalMapTex()
    return normalMap;
 }
 
+Var* TerrainFeatGLSL::_getCompositeMapTex()
+{
+	String name(String::ToString("compositeMap%d", getProcessIndex()));
+	Var *compositeMap = (Var*)LangElement::find(name);
+
+	if (!compositeMap)
+	{
+		compositeMap = new Var;
+		compositeMap->setType("sampler2D");
+		compositeMap->setName(name);
+		compositeMap->uniform = true;
+		compositeMap->sampler = true;
+		compositeMap->constNum = Var::getTexUnitNum();
+	}
+
+	return compositeMap;
+}
+
 Var* TerrainFeatGLSL::_getDetailIdStrengthParallax()
 {
    String name( String::ToString( "detailIdStrengthParallax%d", getProcessIndex() ) );
@@ -203,7 +222,7 @@ void TerrainBaseMapFeatGLSL::processVert( Vector<ShaderComponent*> &componentLis
       // So instead i fixed this by flipping the base and detail
       // coord y scale to compensate when rendering.
       //
-      meta->addStatement( new GenOp( "   @ = @.xyz * float3( @, @, -@ );\r\n", 
+      meta->addStatement( new GenOp( "   @ = @.xyz * vec3( @, @, -@ );\r\n", 
          new DecOp( inTex ), inPos, oneOverTerrainSize, oneOverTerrainSize, oneOverTerrainSize ) );
    }
 
@@ -223,7 +242,7 @@ void TerrainBaseMapFeatGLSL::processVert( Vector<ShaderComponent*> &componentLis
    {
       Var *inNormal = (Var*)LangElement::find( "normal" );
       meta->addStatement( 
-         new GenOp( "   @.z = pow( abs( dot( normalize( float3( @.x, @.y, 0 ) ), float3( 0, 1, 0 ) ) ), 10.0 );\r\n", 
+         new GenOp( "   @.z = pow( abs( dot( normalize( vec3( @.x, @.y, 0 ) ), vec3( 0, 1, 0 ) ) ), 10.0 );\r\n", 
             outTex, inNormal, inNormal ) );
    }
    else
@@ -239,8 +258,8 @@ void TerrainBaseMapFeatGLSL::processVert( Vector<ShaderComponent*> &componentLis
    Var *inTangentZ = getVertTexCoord( "tcTangentZ" );
    Var *inTanget = new Var( "T", "vec3" );
    Var *squareSize = _getUniformVar( "squareSize", "float", cspPass );
-   meta->addStatement( new GenOp( "   @ = normalize( float3( @, 0, @ ) );\r\n", 
-      new DecOp( inTanget ), squareSize, inTangentZ ) );  
+   meta->addStatement( new GenOp( "   @ = normalize( vec3( @, 0, @ ) );\r\n", 
+      new DecOp( inTanget ), squareSize, inTangentZ ) );
 }
 
 void TerrainBaseMapFeatGLSL::processPix(  Vector<ShaderComponent*> &componentList, 
@@ -334,7 +353,7 @@ void TerrainDetailMapFeatGLSL::processVert(  Vector<ShaderComponent*> &component
       outNegViewTS->setName( "outNegViewTS" );
       outNegViewTS->setStructName( "OUT" );
       outNegViewTS->setType( "vec3" );
-      meta->addStatement( new GenOp( "   @ = tMul( @, float3( @ - @.xyz ) );\r\n", 
+      meta->addStatement( new GenOp( "   @ = tMul( @, vec3( @ - @.xyz ) );\r\n", 
          outNegViewTS, objToTangentSpace, eyePos, inPos ) );
    }
 
@@ -490,7 +509,7 @@ void TerrainDetailMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
    {
 	   // create color var
 	   outColor = new Var;
-	   outColor->setType("float4");
+	   outColor->setType("vec4");
 	   outColor->setName("col");
        outColor->setStructName("OUT");
 	   meta->addStatement(new GenOp("   @;\r\n", outColor));
@@ -500,7 +519,7 @@ void TerrainDetailMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
    if (!detailColor)
    {
 	   detailColor = new Var;
-	   detailColor->setType("float4");
+	   detailColor->setType("vec4");
 	   detailColor->setName("detailColor");
 	   meta->addStatement(new GenOp("   @;\r\n", new DecOp(detailColor)));
    }
@@ -1106,6 +1125,176 @@ void TerrainAdditiveFeatGLSL::processPix( Vector<ShaderComponent*> &componentLis
 
 //standard matInfo map contains data of the form .r = bitflags, .g = (will contain AO), 
 //.b = specular strength, a= spec power. 
+
+
+void TerrainCompositeMapFeatGLSL::processVert(Vector<ShaderComponent*> &componentList,
+	const MaterialFeatureData &fd)
+{
+	const S32 detailIndex = getProcessIndex();
+
+	// Grab incoming texture coords... the base map feature
+	// made sure this was created.
+	Var *inTex = (Var*)LangElement::find("texCoord");
+	AssertFatal(inTex, "The texture coord is missing!");
+
+	// Grab the input position.
+	Var *inPos = (Var*)LangElement::find("inPosition");
+	if (!inPos)
+		inPos = (Var*)LangElement::find("position");
+
+	// Get the object space eye position.
+	Var *eyePos = _getUniformVar("eyePos", "vec3", cspPotentialPrimitive);
+
+	MultiLine *meta = new MultiLine;
+
+	// If we have parallax mapping then make sure we've sent
+	// the negative view vector to the pixel shader.
+	if (fd.features.hasFeature(MFT_TerrainParallaxMap) &&
+		!LangElement::find("outNegViewTS"))
+	{
+		// Get the object to tangent transform which
+		// will consume 3 output registers.
+		Var *objToTangentSpace = getOutObjToTangentSpace(componentList, meta, fd);
+
+		// Now use a single output register to send the negative
+		// view vector in tangent space to the pixel shader.
+		ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>(componentList[C_CONNECTOR]);
+		Var *outNegViewTS = connectComp->getElement(RT_TEXCOORD);
+		outNegViewTS->setName("outNegViewTS");
+		outNegViewTS->setStructName("OUT");
+		outNegViewTS->setType("vec3");
+		meta->addStatement(new GenOp("   @ =  @ * vec3( @ - @.xyz );\r\n",
+			outNegViewTS, objToTangentSpace, eyePos, inPos));
+	}
+
+	// Get the distance from the eye to this vertex.
+	Var *dist = (Var*)LangElement::find("dist");
+	if (!dist)
+	{
+		dist = new Var;
+		dist->setType("float");
+		dist->setName("dist");
+
+		meta->addStatement(new GenOp("   @ = distance( @.xyz, @ );\r\n",
+			new DecOp(dist), inPos, eyePos));
+	}
+
+	// grab connector texcoord register
+	ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>(componentList[C_CONNECTOR]);
+	Var *outTex = (Var*)LangElement::find(String::ToString("detCoord%d", detailIndex));
+	if (outTex == NULL)
+	{
+		outTex = connectComp->getElement(RT_TEXCOORD);
+		outTex->setName(String::ToString("detCoord%d", detailIndex));
+		outTex->setStructName("OUT");
+		outTex->setType("vec4");
+	}
+	// Get the detail scale and fade info.
+	Var *detScaleAndFade = (Var*)LangElement::find(String::ToString("detailScaleAndFade%d", detailIndex));
+	if (detScaleAndFade == NULL)
+	{
+		detScaleAndFade->setType("vec4");
+		detScaleAndFade->setName(String::ToString("detailScaleAndFade%d", detailIndex));
+		detScaleAndFade->uniform = true;
+		detScaleAndFade->constSortPos = cspPotentialPrimitive;
+	}
+
+	// Setup the detail coord.
+	//
+	// NOTE: You see here we scale the texture coord by 'xyx'
+	// to generate the detail coord.  This y is here because
+	// its scale is flipped to correct for the non negative y
+	// in texCoord.
+	//
+	// See TerrainBaseMapFeatGLSL::processVert().
+	//
+	meta->addStatement(new GenOp("   @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade));
+
+	// And sneak the detail fade thru the w detailCoord.
+	meta->addStatement(new GenOp("   @.w = clamp( ( @.z - @ ) * @.w, 0.0, 1.0 );\r\n",
+		outTex, detScaleAndFade, dist, detScaleAndFade));
+
+	output = meta;
+}
+
+U32 TerrainCompositeMapFeatGLSL::getOutputTargets(const MaterialFeatureData &fd) const
+{
+	return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget2 : ShaderFeature::RenderTarget1;
+}
+
+void TerrainCompositeMapFeatGLSL::processPix(Vector<ShaderComponent*> &componentList,
+	const MaterialFeatureData &fd)
+{
+	/// Get the texture coord.
+	Var *inDet = _getInDetailCoord(componentList);
+	Var *inTex = getVertTexCoord("texCoord");
+
+	const S32 compositeIndex = getProcessIndex();
+	Var *compositeMap = _getCompositeMapTex();
+	// Sample the normal map.
+	//
+	// We take two normal samples and lerp between them for
+	// side projection layers... else a single sample.
+	LangElement *texOp;
+	
+	if (fd.features.hasFeature(MFT_TerrainSideProject, compositeIndex))
+	{
+		texOp = new GenOp("lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z )",
+			compositeMap, inDet, compositeMap, inDet, inTex);
+	}
+	else
+		texOp = new GenOp("tex2D(@, @.xy)", compositeMap, inDet);
+
+	// search for material var
+	Var *material;
+	OutputTarget targ = RenderTarget1;
+	if (fd.features[MFT_isDeferred])
+	{
+		targ = RenderTarget2;
+	}
+	material = (Var*)LangElement::find(getOutputTargetVarName(targ));
+
+	MultiLine * meta = new MultiLine;
+	if (!material)
+	{
+		// create color var
+		material = new Var;
+		material->setType("fragout");
+		material->setName(getOutputTargetVarName(targ));
+		material->setStructName("OUT");
+	}
+
+	Var *detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", compositeIndex));
+	AssertFatal(detailBlend, "The detail blend is missing!");
+
+	String matinfoName(String::ToString("matinfoCol%d", compositeIndex));
+	Var *matinfoCol = new Var(matinfoName, "vec3");
+
+	Var *priorComp = (Var*)LangElement::find(String::ToString("matinfoCol%d", compositeIndex - 1));
+	if (priorComp)
+	{
+		meta->addStatement(new GenOp("   @ = @.grb*@;\r\n", new DecOp(matinfoCol), texOp, detailBlend));
+		meta->addStatement(new GenOp("   @.gba += @;\r\n", material, matinfoCol));
+	}
+	else
+	{
+		meta->addStatement(new GenOp("   @ = lerp(vec3(1,0,0),@.grb,@);\r\n", new DecOp(matinfoCol), texOp, detailBlend));
+		meta->addStatement(new GenOp("   @ = vec4(0.0,@);\r\n", material, matinfoCol));
+	}
+
+
+	output = meta;
+}
+
+ShaderFeature::Resources TerrainCompositeMapFeatGLSL::getResources(const MaterialFeatureData &fd)
+{
+	Resources res;
+	res.numTex = 1;
+	res.numTexReg += 1;
+	return res;
+}
+
+
 //here, it's merely a cutout for now, so that lightmapping (target3) doesn't get mangled.
 //we'll most likely revisit that later. possibly several ways...
 
@@ -1136,7 +1325,7 @@ void TerrainBlankInfoMapFeatGLSL::processPix(Vector<ShaderComponent*> &component
       material->setStructName("OUT");
    }
 
-   meta->addStatement(new GenOp("   @ = float4(0.0,0.0,0.0,0.0001);\r\n", material));
+   meta->addStatement(new GenOp("   @ = vec4(0.0,1.0,0.0,0.0001);\r\n", material));
 
    output = meta;
 }

+ 19 - 1
Engine/source/terrain/glsl/terrFeatureGLSL.h

@@ -45,7 +45,9 @@ public:
    Var* _getInMacroCoord(Vector<ShaderComponent*> &componentList );
 
    Var* _getNormalMapTex();
-   
+
+   Var* _getCompositeMapTex();
+
    static Var* _getUniformVar( const char *name, const char *type, ConstantSortPosition csp );
    
    Var* _getDetailIdStrengthParallax();
@@ -160,6 +162,22 @@ public:
    virtual String getName() { return "Terrain Additive"; }
 };
 
+class TerrainCompositeMapFeatGLSL : public TerrainFeatGLSL
+{
+public:
+
+	virtual void processVert(Vector<ShaderComponent*> &componentList,
+		const MaterialFeatureData &fd);
+
+	virtual void processPix(Vector<ShaderComponent*> &componentList,
+		const MaterialFeatureData &fd);
+
+	virtual Resources getResources(const MaterialFeatureData &fd);
+
+	virtual U32 getOutputTargets(const MaterialFeatureData &fd) const;
+	virtual String getName() { return "Composite Matinfo map"; }
+};
+
 class TerrainBlankInfoMapFeatGLSL : public ShaderFeatureGLSL
 {
 public:

+ 196 - 0
Engine/source/terrain/hlsl/terrFeatureHLSL.cpp

@@ -49,6 +49,7 @@ namespace
       FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatHLSL );
       FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureHLSL( "Terrain Side Projection" ) );
       FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatHLSL );  
+      FEATUREMGR->registerFeature( MFT_TerrainCompositeMap, new TerrainCompositeMapFeatHLSL );
       FEATUREMGR->registerFeature( MFT_DeferredTerrainBlankInfoMap, new TerrainBlankInfoMapFeatHLSL );
    }
 };
@@ -140,6 +141,24 @@ Var* TerrainFeatHLSL::_getNormalMapTex()
    return normalMap;
 }
 
+Var* TerrainFeatHLSL::_getCompositeMapTex()
+{
+   String name(String::ToString("compositeMap%d", getProcessIndex()));
+   Var *compositeMap = (Var*)LangElement::find(name);
+
+   if (!compositeMap)
+   {
+      compositeMap = new Var;
+      compositeMap->setType("SamplerState");
+      compositeMap->setName(name);
+      compositeMap->uniform = true;
+      compositeMap->sampler = true;
+      compositeMap->constNum = Var::getTexUnitNum();
+   }
+
+   return compositeMap;
+}
+
 Var* TerrainFeatHLSL::_getDetailIdStrengthParallax()
 {
    String name( String::ToString( "detailIdStrengthParallax%d", getProcessIndex() ) );
@@ -1116,6 +1135,183 @@ void TerrainAdditiveFeatHLSL::processPix( Vector<ShaderComponent*> &componentLis
 
 //standard matInfo map contains data of the form .r = bitflags, .g = (will contain AO), 
 //.b = specular strength, a= spec power. 
+
+void TerrainCompositeMapFeatHLSL::processVert(Vector<ShaderComponent*> &componentList,
+   const MaterialFeatureData &fd)
+{
+   const S32 detailIndex = getProcessIndex();
+
+   // Grab incoming texture coords... the base map feature
+   // made sure this was created.
+   Var *inTex = (Var*)LangElement::find("texCoord");
+   AssertFatal(inTex, "The texture coord is missing!");
+
+   // Grab the input position.
+   Var *inPos = (Var*)LangElement::find("inPosition");
+   if (!inPos)
+      inPos = (Var*)LangElement::find("position");
+
+   // Get the object space eye position.
+   Var *eyePos = _getUniformVar("eyePos", "float3", cspPotentialPrimitive);
+
+   MultiLine *meta = new MultiLine;
+
+   // If we have parallax mapping then make sure we've sent
+   // the negative view vector to the pixel shader.
+   if (fd.features.hasFeature(MFT_TerrainParallaxMap) &&
+      !LangElement::find("outNegViewTS"))
+   {
+      // Get the object to tangent transform which
+      // will consume 3 output registers.
+      Var *objToTangentSpace = getOutObjToTangentSpace(componentList, meta, fd);
+
+      // Now use a single output register to send the negative
+      // view vector in tangent space to the pixel shader.
+      ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>(componentList[C_CONNECTOR]);
+      Var *outNegViewTS = connectComp->getElement(RT_TEXCOORD);
+      outNegViewTS->setName("outNegViewTS");
+      outNegViewTS->setStructName("OUT");
+      outNegViewTS->setType("float3");
+      meta->addStatement(new GenOp("   @ = mul( @, float3( @ - @.xyz ) );\r\n",
+         outNegViewTS, objToTangentSpace, eyePos, inPos));
+   }
+
+   // Get the distance from the eye to this vertex.
+   Var *dist = (Var*)LangElement::find("dist");
+   if (!dist)
+   {
+      dist = new Var;
+      dist->setType("float");
+      dist->setName("dist");
+
+      meta->addStatement(new GenOp("   @ = distance( @.xyz, @ );\r\n",
+         new DecOp(dist), inPos, eyePos));
+   }
+
+   // grab connector texcoord register
+   ShaderConnector *connectComp = dynamic_cast<ShaderConnector *>(componentList[C_CONNECTOR]);
+   Var *outTex = (Var*)LangElement::find(String::ToString("detCoord%d", detailIndex));
+   if (outTex == NULL)
+   {
+      outTex = connectComp->getElement(RT_TEXCOORD);
+      outTex->setName(String::ToString("detCoord%d", detailIndex));
+      outTex->setStructName("OUT");
+      outTex->setType("float4");
+   }
+   // Get the detail scale and fade info.
+   Var *detScaleAndFade = (Var*)LangElement::find(String::ToString("detailScaleAndFade%d", detailIndex));
+   if (detScaleAndFade == NULL)
+   {
+      detScaleAndFade->setType("float4");
+      detScaleAndFade->setName(String::ToString("detailScaleAndFade%d", detailIndex));
+      detScaleAndFade->uniform = true;
+      detScaleAndFade->constSortPos = cspPotentialPrimitive;
+   }
+
+   // Setup the detail coord.
+   //
+   // NOTE: You see here we scale the texture coord by 'xyx'
+   // to generate the detail coord.  This y is here because
+   // its scale is flipped to correct for the non negative y
+   // in texCoord.
+   //
+   // See TerrainBaseMapFeatHLSL::processVert().
+   //
+   meta->addStatement(new GenOp("   @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade));
+
+   // And sneak the detail fade thru the w detailCoord.
+   meta->addStatement(new GenOp("   @.w = clamp( ( @.z - @ ) * @.w, 0.0, 1.0 );\r\n",
+      outTex, detScaleAndFade, dist, detScaleAndFade));
+
+   output = meta;
+}
+
+U32 TerrainCompositeMapFeatHLSL::getOutputTargets(const MaterialFeatureData &fd) const
+{
+   return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget2 : ShaderFeature::RenderTarget1;
+}
+
+void TerrainCompositeMapFeatHLSL::processPix(Vector<ShaderComponent*> &componentList,
+   const MaterialFeatureData &fd)
+{
+   /// Get the texture coord.
+   Var *inDet = _getInDetailCoord(componentList);
+   Var *inTex = getVertTexCoord("texCoord");
+
+   const S32 compositeIndex = getProcessIndex();
+   Var *compositeMap = _getCompositeMapTex();
+   // Sample the normal map.
+   //
+   // We take two normal samples and lerp between them for
+   // side projection layers... else a single sample.
+   LangElement *texOp;
+   String name(String::ToString("compositeMapTex%d", getProcessIndex()));
+   Var *compositeMapTex = (Var*)LangElement::find(name);
+   if (!compositeMapTex)
+   {
+      compositeMapTex = new Var;
+      compositeMapTex->setName(String::ToString("compositeMapTex%d", getProcessIndex()));
+      compositeMapTex->setType("Texture2D");
+      compositeMapTex->uniform = true;
+      compositeMapTex->texture = true;
+      compositeMapTex->constNum = compositeMap->constNum;
+   }
+   if (fd.features.hasFeature(MFT_TerrainSideProject, compositeIndex))
+   {
+      texOp = new GenOp("lerp( @.Sample( @, @.yz ), @.Sample( @, @.xz ), @.z )",
+         compositeMapTex, compositeMap, inDet, compositeMapTex, compositeMap, inDet, inTex);
+   }
+   else
+      texOp = new GenOp("@.Sample(@, @.xy)", compositeMapTex, compositeMap, inDet);
+
+   // search for material var
+   Var *material;
+   OutputTarget targ = RenderTarget1;
+   if (fd.features[MFT_isDeferred])
+   {
+      targ = RenderTarget2;
+   }
+   material = (Var*)LangElement::find(getOutputTargetVarName(targ));
+
+   MultiLine * meta = new MultiLine;
+   if (!material)
+   {
+      // create color var
+      material = new Var;
+      material->setType("fragout");
+      material->setName(getOutputTargetVarName(targ));
+      material->setStructName("OUT");
+   }
+
+   Var *detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", compositeIndex));
+   AssertFatal(detailBlend, "The detail blend is missing!");
+
+   String matinfoName(String::ToString("matinfoCol%d", compositeIndex));
+   Var *matinfoCol = new Var(matinfoName, "float3");
+   
+   Var *priorComp = (Var*)LangElement::find(String::ToString("matinfoCol%d", compositeIndex - 1));
+   if (priorComp)
+   {
+      meta->addStatement(new GenOp("   @ = @.grb*@;\r\n", new DecOp(matinfoCol), texOp, detailBlend));
+      meta->addStatement(new GenOp("   @.gba += @;\r\n", material, matinfoCol));
+   }
+   else
+   {
+      meta->addStatement(new GenOp("   @ = lerp(float3(1,0,0),@.grb,@);\r\n", new DecOp(matinfoCol), texOp, detailBlend));
+      meta->addStatement(new GenOp("   @ = float4(0.0,@);\r\n", material, matinfoCol));
+   }
+
+
+   output = meta;
+}
+
+ShaderFeature::Resources TerrainCompositeMapFeatHLSL::getResources(const MaterialFeatureData &fd)
+{
+   Resources res;
+   res.numTex = 1;
+   return res;
+}
+
 //here, it's merely a cutout for now, so that lightmapping (target3) doesn't get mangled.
 //we'll most likely revisit that later. possibly several ways...
 

+ 17 - 0
Engine/source/terrain/hlsl/terrFeatureHLSL.h

@@ -46,6 +46,7 @@ public:
    Var* _getInMacroCoord(Vector<ShaderComponent*> &componentList );
 
    Var* _getNormalMapTex();
+   Var* _getCompositeMapTex();
 
    static Var* _getUniformVar( const char *name, const char *type, ConstantSortPosition csp );
 
@@ -161,6 +162,22 @@ public:
    virtual String getName() { return "Terrain Additive"; }
 };
 
+class TerrainCompositeMapFeatHLSL : public TerrainFeatHLSL
+{
+public:
+
+   virtual void processVert(Vector<ShaderComponent*> &componentList,
+      const MaterialFeatureData &fd);
+
+   virtual void processPix(Vector<ShaderComponent*> &componentList,
+      const MaterialFeatureData &fd);
+
+   virtual Resources getResources(const MaterialFeatureData &fd);
+
+   virtual U32 getOutputTargets(const MaterialFeatureData &fd) const;
+   virtual String getName() { return "Composite Matinfo map"; }
+};
+
 class TerrainBlankInfoMapFeatHLSL : public TerrainFeatHLSL
 {
 public:

+ 48 - 3
Engine/source/terrain/terrCellMaterial.cpp

@@ -59,7 +59,8 @@ Vector<String> _initSamplerNames()
    {
       samplerNames.push_back(avar("$normalMap%d",i));
       samplerNames.push_back(avar("$detailMap%d",i));
-      samplerNames.push_back(avar("$macroMap%d",i));
+      samplerNames.push_back(avar("$macroMap%d", i));
+      samplerNames.push_back(avar("$compositeMap%d", i));
    }   
 
    return samplerNames;
@@ -149,6 +150,19 @@ void TerrainCellMaterial::_updateDefaultAnisotropy()
                   desc.samplers[sampler].minFilter = GFXTextureFilterLinear;
             }
 
+            if (matInfo->compositeTexConst->isValid())
+            {
+               const S32 sampler = matInfo->compositeTexConst->getSamplerRegister();
+
+               if (maxAnisotropy > 1)
+               {
+                  desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic;
+                  desc.samplers[sampler].maxAnisotropy = maxAnisotropy;
+               }
+               else
+                  desc.samplers[sampler].minFilter = GFXTextureFilterLinear;
+            }
+
          } // for ( U32 m=0; m < pass.materials.size(); m++ )
 
          // Set the updated stateblock.
@@ -424,6 +438,14 @@ bool TerrainCellMaterial::_createPass( Vector<MaterialInfo*> *materials,
             features.addFeature(MFT_isDeferred, featureIndex);
          features.addFeature( MFT_TerrainDetailMap, featureIndex );
 
+         if (!(mat->getCompositeMap().isEmpty()))
+         {
+            if (deferredMat)
+               features.addFeature(MFT_isDeferred, featureIndex);
+            features.addFeature(MFT_TerrainCompositeMap, featureIndex);
+            features.removeFeature(MFT_DeferredTerrainBlankInfoMap);
+         }
+
          pass->materials.push_back( (*materials)[i] );
          normalMaps.increment();
 
@@ -496,7 +518,7 @@ bool TerrainCellMaterial::_createPass( Vector<MaterialInfo*> *materials,
          // isn't fooled into thinking there is a real bug.  That is until
          // we get down to a single material.  If a single material case
          // fails it means it cannot generate any passes at all!
-         const bool logErrors = matCount == 1;
+         const bool logErrors = true;// matCount == 1;
          GFXShader::setLogging( logErrors, true );
 
          pass->shader = SHADERGEN->getShader( featureData, getGFXVertexFormat<TerrVertex>(), NULL, mSamplerNames );
@@ -613,6 +635,27 @@ bool TerrainCellMaterial::_createPass( Vector<MaterialInfo*> *materials,
             &GFXStaticTextureProfile, "TerrainCellMaterial::_createPass() - DetailMap" );
       }
 
+	  matInfo->compositeTexConst = pass->shader->getShaderConstHandle(avar("$compositeMap%d", i));
+	  if (matInfo->compositeTexConst->isValid())
+	  {
+		  matInfo->compositeTex.set(matInfo->mat->getCompositeMap(),
+			  &GFXStaticTextureProfile, "TerrainCellMaterial::_createPass() - CompositeMap");
+		  const S32 sampler = matInfo->compositeTexConst->getSamplerRegister();
+		  Con::errorf("sampler=%i", sampler);
+
+		  desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear();
+		  desc.samplers[sampler].magFilter = GFXTextureFilterLinear;
+		  desc.samplers[sampler].mipFilter = GFXTextureFilterLinear;
+
+		  if (maxAnisotropy > 1)
+		  {
+			  desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic;
+			  desc.samplers[sampler].maxAnisotropy = maxAnisotropy;
+		  }
+		  else
+			  desc.samplers[sampler].minFilter = GFXTextureFilterLinear;
+	  }
+
       matInfo->macroInfoVConst = pass->shader->getShaderConstHandle( avar( "$macroScaleAndFade%d", i ) );
       matInfo->macroInfoPConst = pass->shader->getShaderConstHandle( avar( "$macroIdStrengthParallax%d", i ) );
 
@@ -810,6 +853,8 @@ bool TerrainCellMaterial::setupPass(   const SceneRenderState *state,
          GFX->setTexture( matInfo->macroTexConst->getSamplerRegister(), matInfo->macroTex );
       if ( matInfo->normalTexConst->isValid() )
          GFX->setTexture( matInfo->normalTexConst->getSamplerRegister(), matInfo->normalTex );
+      if ( matInfo->compositeTexConst->isValid() )
+         GFX->setTexture( matInfo->compositeTexConst->getSamplerRegister(), matInfo->compositeTex );
    }
 
    pass.consts->setSafe( pass.layerSizeConst, (F32)mTerrain->mLayerTex.getWidth() );
@@ -837,7 +882,7 @@ bool TerrainCellMaterial::setupPass(   const SceneRenderState *state,
          pass.lightParamsConst->isValid() )
    {
       if ( !mLightInfoTarget )
-         mLightInfoTarget = NamedTexTarget::find( "directLighting" );
+         mLightInfoTarget = NamedTexTarget::find( "diffuseLighting" );
 
       GFXTextureObject *texObject = mLightInfoTarget->getTexture();
       

+ 3 - 0
Engine/source/terrain/terrCellMaterial.h

@@ -77,6 +77,9 @@ protected:
       GFXShaderConstHandle *normalTexConst;
       GFXTexHandle normalTex;
 
+      GFXShaderConstHandle *compositeTexConst;
+      GFXTexHandle compositeTex;
+
       GFXShaderConstHandle *detailInfoVConst;
       GFXShaderConstHandle *detailInfoPConst;
 

+ 1 - 0
Engine/source/terrain/terrFeatureTypes.cpp

@@ -36,3 +36,4 @@ ImplementFeatureType( MFT_TerrainSideProject, MFG_Texture, 106.0f, false );
 ImplementFeatureType( MFT_TerrainAdditive, MFG_PostProcess, 999.0f, false );
 //Deferred Shading
 ImplementFeatureType( MFT_DeferredTerrainBlankInfoMap, MFG_Texture, 104.1f, false);
+ImplementFeatureType( MFT_TerrainCompositeMap, MFG_Texture, 104.2f, false);

+ 1 - 0
Engine/source/terrain/terrFeatureTypes.h

@@ -36,6 +36,7 @@ DeclareFeatureType( MFT_TerrainLightMap );
 DeclareFeatureType( MFT_TerrainSideProject );
 DeclareFeatureType( MFT_TerrainAdditive );
 //Deferred Shading
+DeclareFeatureType( MFT_TerrainCompositeMap );
 DeclareFeatureType( MFT_DeferredTerrainBlankInfoMap );
 
 

+ 1 - 0
Engine/source/terrain/terrMaterial.cpp

@@ -97,6 +97,7 @@ void TerrainMaterial::initPersistFields()
    addField( "parallaxScale", TypeF32, Offset( mParallaxScale, TerrainMaterial ), "Used to scale the height from the normal map to give some self "
 	   "occlusion effect (aka parallax) to the terrain material" );
 
+   addField("compositeMap", TypeStringFilename, Offset(mCompositeMap, TerrainMaterial), "Composite map for the material");
    Parent::initPersistFields();
 
    // Gotta call this at least once or it won't get created!

+ 5 - 0
Engine/source/terrain/terrMaterial.h

@@ -49,6 +49,9 @@ protected:
    ///
    FileName mDetailMap;
 
+   ///
+   FileName mCompositeMap;
+   
    /// The size of the detail map in meters used
    /// to generate the texture coordinates for the
    /// detail and normal maps.
@@ -103,6 +106,8 @@ public:
 
    const String& getMacroMap() const { return mMacroMap; }
 
+   const String& getCompositeMap() const { return mCompositeMap; }
+
    F32 getDetailSize() const { return mDetailSize; }
 
    F32 getDetailStrength() const { return mDetailStrength; }

+ 3 - 1
Templates/Full/game/art/shapes/Cheetah/materials.cs

@@ -28,8 +28,10 @@ singleton Material(Cheetah_Main)
    specularPower[0] = "10";
    translucentBlendOp = "None";
    normalMap[0] = "art/shapes/Cheetah/Cheetah_N";
-   specularMap[0] = "art/shapes/Cheetah/Cheetah_S";
    castDynamicShadows = true;
+   diffuseColor[3] = "1 1 1 1";
+   specular0 = "0.9 0.9 0.9 1";
+   specularPower0 = "10";
 };
 
 singleton Material(Cheetah_TailLights)

+ 12 - 12
Templates/Full/game/art/shapes/actors/Soldier/materials.cs

@@ -24,19 +24,18 @@ singleton Material(Mat_Soldier_Main)
 {
    mapTo = "base_Soldier_Main";
 
-   diffuseMap[0] = "Soldier_Dif.dds";
-   normalMap[0] = "Soldier_N.dds";
-   specularMap[0] = "Soldier_Spec.dds";
-
-   diffuseColor[0] = "1 1 1 1";
-   specular[0] = "0.9 0.9 0.9 1";
-   specularPower[0] = 10;
-
-   doubleSided = false;
-   translucent = false;
-   showFootprints = "0";
+   diffuseMap[0] = "art/shapes/actors/Soldier/Soldier_Dif.dds";
+   normalMap[0] = "art/shapes/actors/Soldier/Soldier_N.dds";
+   specularMap[0] = "art/shapes/actors/Soldier/Soldier_c";
+   FlipRB[0] = "1";
    castDynamicShadows = true;
    materialTag0 = "Player";
+   isSRGb[0] = "1";
+   roughness0 = "1";
+   FlipRB0 = "1";
+   specularPower0 = "10";
+   pixelSpecular0 = "0";
+   specular0 = "0.9 0.9 0.9 1";
 };
 
 singleton Material(Mat_Soldier_Dazzle)
@@ -110,7 +109,8 @@ singleton Material(Mat_Orange_Soldier_Main : Mat_Soldier_Main)
 singleton Material(Mat_Red_Soldier_Main : Mat_Soldier_Main)
 {
    mapTo = "Red_Soldier_Main";
-   diffuseMap[0] = "Soldier_Red_Dif.dds";
+   diffuseMap[0] = "art/shapes/actors/Soldier/Soldier_Red_Dif.dds";
+   normalMap[0] = "art/shapes/actors/Soldier/Soldier_N.dds";
 };
 
 singleton Material(Mat_Teal_Soldier_Main : Mat_Soldier_Main)

+ 6 - 3
Templates/Full/game/art/shapes/cube/materials.cs

@@ -25,9 +25,6 @@ singleton Material(cube_GridMaterial)
 {
 	mapTo = "GridMaterial";
 
-	diffuseMap[0] = "grid";
-	normalMap[0] = "";
-	specularMap[0] = "";
 
 	diffuseColor[0] = "1 1 1 1";
 	specular[0] = "0.9 0.9 0.9 1";
@@ -38,6 +35,12 @@ singleton Material(cube_GridMaterial)
 	doubleSided = false;
 	translucent = false;
 	translucentBlendOp = "None";
+   materialTag0 = "Miscellaneous";
+   smoothness[0] = "1";
+   metalness[0] = "1";
+   specularPower0 = "0.415939";
+   pixelSpecular0 = "0";
+   specular0 = "0.9 0.9 0.9 1";
 };
 
 //--- cube.dae MATERIALS END ---

+ 17 - 6
Templates/Full/game/art/shapes/station/materials.cs

@@ -24,22 +24,33 @@ new Material(Structure_wall)
 {
    mapTo = "building01walls";
    diffuseMap[0] = "art/shapes/station/building01walls";
+   normalMap[0] = "art/shapes/station/building01walls_n.dds";
+   specularMap[0] = "art/shapes/station/building01walls_c.dds";
+   effectColor[0] = "InvisibleBlack";
+   materialTag0 = "Miscellaneous";
+   pixelSpecular0 = "0";
    //emissive[0] = true;
 };
 
 
-new Material(Structure_grid)
+new Material(Structure_plate)
 {
-   mapTo = "grid";
-   diffuseMap[0] = "art/shapes/station/grid";
+   mapTo = "plate";
+   diffuseMap[0] = "art/shapes/station/plate";
+   normalMap[0] = "art/shapes/station/plate_n.dds";
+   specularMap[0] = "art/shapes/station/plate_c.dds";
+   pixelSpecular0 = "0";
+   materialTag0 = "Miscellaneous";
    //emissive[0] = true;
 };
 
 
-new Material(Structure_plate)
+new Material(Structure_grid)
 {
-   mapTo = "plate";
-   diffuseMap[0] = "art/shapes/station/plate";
+   mapTo = "grid";
+   diffuseMap[0] = "art/shapes/station/grid";
+   normalMap[0] = "art/shapes/station/grid_n.dds";
+   specularMap[0] = "art/shapes/station/grid_c.dds";
    //emissive[0] = true;
 };
 

+ 1 - 0
Templates/Full/game/art/terrains/materials.cs

@@ -151,6 +151,7 @@ new TerrainMaterial()
    detailBrightness = "1";
    Enabled = "1";
    diffuseSize = "400";
+   compositeMap = "art/terrains/Example/snowtop";
 };
 
 // ----------------------------------------------------------------------------

+ 8 - 2
Templates/Full/game/core/art/grids/materials.cs

@@ -51,8 +51,12 @@ singleton Material( Grid512_ForestGreenLines_Mat )
 singleton Material( Grid512_Green_Mat )
 {
    mapTo = "Grid512_Green_Mat";
-   diffuseMap[0] = "512_green";
+   diffuseMap[0] = "core/art/grids/512_green";
    materialTag0 = "TestMaterial";
+   smoothness[0] = "1";
+   metalness[0] = "1";
+   translucent = "0";
+   translucentBlendOp = "Add";
 };
 
 singleton Material( Grid512_Grey_Mat )
@@ -65,8 +69,10 @@ singleton Material( Grid512_Grey_Mat )
 singleton Material( Grid512_GreyBase_Mat )
 {
    mapTo = "Grid512_GreyBase_Mat";
-   diffuseMap[0] = "512_grey_base";
+   diffuseMap[0] = "core/art/grids/512_grey_base";
    materialTag0 = "TestMaterial";
+   smoothness[0] = "1";
+   metalness[0] = "1";
 };
 
 singleton Material( Grid512_Orange_Mat )

+ 40 - 7
Templates/Full/game/core/scripts/client/lighting/advanced/deferredShading.cs

@@ -47,23 +47,56 @@ new ShaderData( AL_DeferredShader )
    OGLPixelShaderFile  = "shaders/common/lighting/advanced/gl/deferredShadingP.glsl";
 
    samplerNames[0] = "colorBufferTex";
-   samplerNames[1] = "directLightingBuffer";
+   samplerNames[1] = "diffuseLightingBuffer";
    samplerNames[2] = "matInfoTex";
-   samplerNames[3] = "indirectLightingBuffer";
+   samplerNames[3] = "specularLightingBuffer";
    samplerNames[4] = "deferredTex";
    pixVersion = 2.0;
 };
 
+new ShaderData( AL_ProbeShader )
+{
+   DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl";
+   DXPixelShaderFile  = "shaders/common/lighting/advanced/probeShadingP.hlsl";
+   
+   OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl";
+   OGLPixelShaderFile  = "shaders/common/lighting/advanced/gl/probeShadingP.glsl";
+
+   samplerNames[0] = "colorBufferTex";
+   samplerNames[1] = "diffuseLightingBuffer";
+   samplerNames[2] = "matInfoTex";
+   samplerNames[3] = "specularLightingBuffer";
+   samplerNames[4] = "deferredTex";
+   pixVersion = 2.0;
+};
+
+
+singleton PostEffect( AL_PreCapture )
+{
+   renderTime = "PFXBeforeBin";
+   renderBin = "ProbeBin";
+   shader = AL_ProbeShader;
+   stateBlock = AL_DeferredShadingState;
+   texture[0] = "#color";
+   texture[1] = "#diffuseLighting";
+   texture[2] = "#matinfo";
+   texture[3] = "#specularLighting";
+   texture[4] = "#deferred";
+   target = "$backBuffer";
+   renderPriority = 10000;
+   allowReflectPass = true;
+};
+
 singleton PostEffect( AL_DeferredShading )
 {
    renderTime = "PFXAfterBin";
-   renderBin = "SkyBin";
+   renderBin = "ProbeBin";
    shader = AL_DeferredShader;
    stateBlock = AL_DeferredShadingState;
    texture[0] = "#color";
-   texture[1] = "#directLighting";
+   texture[1] = "#diffuseLighting";
    texture[2] = "#matinfo";
-   texture[3] = "#indirectLighting";
+   texture[3] = "#specularLighting";
    texture[4] = "#deferred";
    
    target = "$backBuffer";
@@ -192,7 +225,7 @@ new ShaderData( AL_LightMapShader )
    OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl";
    OGLPixelShaderFile  = "shaders/common/lighting/advanced/gl/dbgLightMapVisualizeP.glsl";
 
-   samplerNames[0] = "indirectLightingBuffer";
+   samplerNames[0] = "specularLightingBuffer";
    pixVersion = 2.0;
 };
 
@@ -200,7 +233,7 @@ singleton PostEffect( AL_LightMapVisualize )
 {   
    shader = AL_LightMapShader;
    stateBlock = AL_DefaultVisualizeState;
-   texture[0] = "#indirectLighting";
+   texture[0] = "#specularLighting";
    target = "$backBuffer";
    renderPriority = 9999;
 };

+ 5 - 5
Templates/Full/game/core/scripts/client/lighting/advanced/lightViz.cs

@@ -149,7 +149,7 @@ new ShaderData( AL_LightColorVisualizeShader )
    OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl";
    OGLPixelShaderFile  = "shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl";
    
-   samplerNames[0] = "directLightingBuffer";
+   samplerNames[0] = "diffuseLightingBuffer";
    
    pixVersion = 2.0;
 };
@@ -158,7 +158,7 @@ singleton PostEffect( AL_LightColorVisualize )
 {   
    shader = AL_LightColorVisualizeShader;
    stateBlock = AL_DefaultVisualizeState;
-   texture[0] = "#directLighting";
+   texture[0] = "#diffuseLighting";
    target = "$backBuffer";
    renderPriority = 9999;
 };
@@ -184,16 +184,16 @@ new ShaderData( AL_LightSpecularVisualizeShader )
    OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl";
    OGLPixelShaderFile  = "shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl";
    
-   samplerNames[0] = "directLightingBuffer";
+   samplerNames[0] = "diffuseLightingBuffer";
    
    pixVersion = 2.0;
 };
 
 singleton PostEffect( AL_LightSpecularVisualize )
 {   
-   shader = AL_LightSpecularVisualizeShader;
+   shader = AL_LightColorVisualizeShader;
    stateBlock = AL_DefaultVisualizeState;
-   texture[0] = "#directLighting";
+   texture[0] = "#specularLighting";
    target = "$backBuffer";
    renderPriority = 9999;
 };

+ 124 - 7
Templates/Full/game/core/scripts/client/lighting/advanced/shaders.cs

@@ -86,11 +86,11 @@ new CustomMaterial( AL_VectorLightMaterial )
    sampler["shadowMap"] = "$dynamiclight";
    sampler["dynamicShadowMap"] = "$dynamicShadowMap";
    sampler["ssaoMask"] = "#ssaoMask";  
-   sampler["lightBuffer"] = "#indirectLighting";
+   sampler["lightBuffer"] = "#specularLighting";
    sampler["colorBuffer"] = "#color";
    sampler["matInfoBuffer"] = "#matinfo";
    
-   target = "directLighting";
+   target = "diffuseLighting";
    
    pixVersion = 3.0;
 };
@@ -163,11 +163,11 @@ new CustomMaterial( AL_PointLightMaterial )
    sampler["shadowMap"] = "$dynamiclight";
    sampler["dynamicShadowMap"] = "$dynamicShadowMap";
    sampler["cookieMap"] = "$dynamiclightmask";
-   sampler["lightBuffer"] = "#indirectLighting";
+   sampler["lightBuffer"] = "#specularLighting";
    sampler["colorBuffer"] = "#color";
    sampler["matInfoBuffer"] = "#matinfo";
    
-   target = "directLighting";
+   target = "diffuseLighting";
    
    pixVersion = 3.0;
 };
@@ -202,11 +202,11 @@ new CustomMaterial( AL_SpotLightMaterial )
    sampler["shadowMap"] = "$dynamiclight";
    sampler["dynamicShadowMap"] = "$dynamicShadowMap";
    sampler["cookieMap"] = "$dynamiclightmask";
-   sampler["lightBuffer"] = "#indirectLighting";
+   sampler["lightBuffer"] = "#specularLighting";
    sampler["colorBuffer"] = "#color";
    sampler["matInfoBuffer"] = "#matinfo";
    
-   target = "directLighting";
+   target = "diffuseLighting";
    
    pixVersion = 3.0;
 };
@@ -269,7 +269,124 @@ new CustomMaterial( AL_ParticlePointLightMaterial )
    stateBlock = AL_ConvexLightState;
    
    sampler["deferredBuffer"] = "#deferred";
-   target = "directLighting";
+   target = "diffuseLighting";
    
    pixVersion = 3.0;
 };
+
+//Reflection probe Specular
+new ShaderData( ReflectionProbeShader )
+{
+   DXVertexShaderFile = "shaders/common/lighting/advanced/convexGeometryV.hlsl";
+   DXPixelShaderFile  = "shaders/common/lighting/advanced/reflectionProbeP.hlsl";
+
+   OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/convexGeometryV.glsl";
+   OGLPixelShaderFile  = "shaders/common/lighting/advanced/gl/reflectionProbeP.glsl";
+
+   samplerNames[0] = "$deferredBuffer";
+   samplerNames[1] = "$matInfoBuffer";
+   
+   pixVersion = 3.0;
+};
+
+// Convex-geometry light states
+new GFXStateBlockData( AL_ProbeState )
+{
+   blendDefined = true;
+   blendEnable = true;
+   blendSrc = GFXBlendOne;
+   blendDest = GFXBlendOne;
+   blendOp = GFXBlendOpAdd;
+   
+   zDefined = true;
+   zEnable = true;
+   zWriteEnable = false;
+   zFunc = GFXCmpGreaterEqual;
+
+   samplersDefined = true;
+   samplerStates[0] = SamplerClampPoint;  // G-buffer
+   mSamplerNames[0] = "deferredBuffer";
+   samplerStates[1] = SamplerClampLinear;  // Shadow Map (Do not use linear, these are perspective projections)
+   mSamplerNames[1] = "matInfoBuffer";
+   
+   cullDefined = true;
+   cullMode = GFXCullCW;
+   
+   stencilDefined = true;
+   stencilEnable = true;
+   stencilFailOp = GFXStencilOpKeep;
+   stencilZFailOp = GFXStencilOpKeep;
+   stencilPassOp = GFXStencilOpKeep;
+   stencilFunc = GFXCmpLess;
+   stencilRef = 0;
+};
+
+new CustomMaterial( ReflectionProbeMaterial )
+{
+   shader = ReflectionProbeShader;
+   stateBlock = AL_ProbeState;
+   
+   sampler["deferredBuffer"] = "#deferred";
+   sampler["matInfoBuffer"] = "#matinfo";
+   
+   pixVersion = 3.0;
+};
+
+//Skylight
+new ShaderData( IrradianceShader )
+{
+   DXVertexShaderFile = "shaders/common/lighting/advanced/cubemapV.hlsl";
+   DXPixelShaderFile  = "shaders/common/lighting/advanced/irradianceP.hlsl";
+
+   OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/cubemapV.glsl";
+   OGLPixelShaderFile  = "shaders/common/lighting/advanced/gl/irradianceP.glsl";
+   
+   pixVersion = 3.0;
+};
+
+new ShaderData( PrefiterCubemapShader )
+{
+   DXVertexShaderFile = "shaders/common/lighting/advanced/cubemapV.hlsl";
+   DXPixelShaderFile  = "shaders/common/lighting/advanced/prefilterP.hlsl";
+
+   OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/cubemapV.glsl";
+   OGLPixelShaderFile  = "shaders/common/lighting/advanced/gl/prefilterP.glsl";
+   
+   pixVersion = 3.0;
+};
+
+new ShaderData( BRDFLookupShader )
+{
+   DXVertexShaderFile = "shaders/common/lighting/advanced/cubemapV.hlsl";
+   DXPixelShaderFile  = "shaders/common/lighting/advanced/brdfLookupP.hlsl";
+
+   OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/cubemapV.glsl";
+   OGLPixelShaderFile  = "shaders/common/lighting/advanced/gl/brdfLookupP.glsl";
+   
+   pixVersion = 3.0;
+};
+
+new ShaderData( SklyightShader )
+{
+   DXVertexShaderFile = "shaders/common/lighting/advanced/convexGeometryV.hlsl";
+   DXPixelShaderFile  = "shaders/common/lighting/advanced/skylightP.hlsl";
+
+   OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/convexGeometryV.glsl";
+   OGLPixelShaderFile  = "shaders/common/lighting/advanced/gl/skylightP.glsl";
+
+   samplerNames[0] = "$deferredBuffer";
+   samplerNames[1] = "$matInfoBuffer";
+   
+   pixVersion = 3.0;
+};
+
+new CustomMaterial( SklyightMaterial )
+{
+   shader = SklyightShader;
+   stateBlock = AL_ProbeState;
+   
+   sampler["deferredBuffer"] = "#deferred";
+   sampler["matInfoBuffer"] = "#matinfo";
+   
+   pixVersion = 3.0;
+};

+ 1 - 1
Templates/Full/game/core/scripts/client/lighting/basic/init.cs

@@ -64,7 +64,7 @@ singleton CustomMaterial( BL_ProjectedShadowMaterial )
 function onActivateBasicLM()
 {
    // If HDR is enabled... enable the special format token.
-   if ( $platform !$= "macos" && HDRPostFx.isEnabled )
+   if ( HDRPostFx.isEnabled )
       AL_FormatToken.enable();
       
    // Create render pass for projected shadow.

+ 2 - 1
Templates/Full/game/core/scripts/client/renderManager.cs

@@ -51,7 +51,8 @@ function initRenderManager()
    // meshes... but that causes issues in reflections.
    DiffuseRenderPassManager.addManager( new RenderObjectMgr(SkyBin) { bintype = "Sky"; renderOrder = 0.1; processAddOrder = 0.1; } );
    
-   //DiffuseRenderPassManager.addManager( new RenderVistaMgr()               { bintype = "Vista"; renderOrder = 0.15; processAddOrder = 0.15; } );
+   DiffuseRenderPassManager.addManager( new RenderProbeMgr(ProbeBin)       { bintype = "Probes"; renderOrder = 0.15; processAddOrder = 0.15; } );
+   //DiffuseRenderPassManager.addManager( new RenderVistaMgr()             { bintype = "Vista"; renderOrder = 0.15; processAddOrder = 0.15; } );
    
    DiffuseRenderPassManager.addManager( new RenderObjectMgr(BeginBin)      { bintype = "Begin"; renderOrder = 0.2; processAddOrder = 0.2; } );
    // Normal mesh rendering.

+ 66 - 43
Templates/Full/game/levels/AProbeTest.mis

@@ -28,7 +28,7 @@ new SimGroup(MissionGroup) {
          levelName = "A Probe Test";
    };
    new SkyBox(theSky) {
-      Material = "DesertSkyMat";
+      Material = "sky_day_hdr";
       drawBottom = "0";
       fogBandHeight = "0";
       position = "0 0 0";
@@ -37,48 +37,16 @@ new SimGroup(MissionGroup) {
       canSave = "1";
       canSaveDynamicFields = "1";
    };
-   new Sun(theSun) {
-      azimuth = "290";
-      elevation = "50";
-      color = "1 1 1 1";
-      ambient = "0.473532 0.473532 0.473532 1";
-      brightness = "1";
-      castShadows = "1";
-      staticRefreshFreq = "250";
-      dynamicRefreshFreq = "8";
-      coronaEnabled = "1";
-      coronaScale = "0.5";
-      coronaTint = "1 1 1 1";
-      coronaUseLightColor = "1";
-      flareScale = "1";
-      attenuationRatio = "0 1 1";
-      shadowType = "PSSM";
-      texSize = "1024";
-      overDarkFactor = "3000 1500 750 250";
-      shadowDistance = "100";
-      shadowSoftness = "0.25";
-      numSplits = "4";
-      logWeight = "0.9";
-      fadeStartDistance = "0";
-      lastSplitTerrainOnly = "0";
-      representedInLightmap = "0";
-      shadowDarkenColor = "0 0 0 -1";
-      includeLightmappedGeometryInShadow = "0";
+   new Skylight() {
+      enabled = "1";
+      StaticCubemap = "sky_day_hdr_cubemap";
       position = "0 0 0";
       rotation = "1 0 0 0";
       scale = "1 1 1";
       canSave = "1";
       canSaveDynamicFields = "1";
-         bias = "0.1";
-         Blur = "1";
-         enabled = "1";
-         height = "1024";
-         lightBleedFactor = "0.8";
-         minVariance = "0";
-         pointShadowType = "PointShadowType_Paraboloid";
-         shadowBox = "-100 -100 -100 100 100 100";
-         splitFadeDistances = "1 1 1 1";
-         width = "3072";
+      persistentId = "e4c73467-4089-11e8-b478-cd227cd60b8b";
+         reflectionPath = "levels/AProbeTest/probes/";
    };
    new SimGroup(PlayerDropPoints) {
       canSave = "1";
@@ -117,13 +85,46 @@ new SimGroup(MissionGroup) {
          rotation = "1 0 0 0";
          scale = "1 1 1";
    };
+   new Sun() {
+      azimuth = "200";
+      elevation = "20";
+      color = "0.8 0.8 0.8 1";
+      ambient = "0.2 0.2 0.2 1";
+      brightness = "1";
+      castShadows = "1";
+      staticRefreshFreq = "250";
+      dynamicRefreshFreq = "8";
+      coronaEnabled = "1";
+      coronaMaterial = "Corona_Mat";
+      coronaScale = "0.5";
+      coronaTint = "1 1 1 1";
+      coronaUseLightColor = "1";
+      flareType = "SunFlareExample";
+      flareScale = "1";
+      attenuationRatio = "0 1 1";
+      shadowType = "PSSM";
+      texSize = "512";
+      overDarkFactor = "2000 1000 500 100";
+      shadowDistance = "400";
+      shadowSoftness = "0.15";
+      numSplits = "4";
+      logWeight = "0.91";
+      fadeStartDistance = "0";
+      lastSplitTerrainOnly = "0";
+      representedInLightmap = "0";
+      shadowDarkenColor = "0 0 0 -1";
+      includeLightmappedGeometryInShadow = "0";
+      position = "35.7492 65.5507 -15.5377";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+         direction = "1 1 -1";
+   };
    new ReflectionProbe() {
-      enabled = "1";
+      enabled = "0";
       ProbeShape = "Sphere";
       radius = "10";
-      Intensity = "1";
-      IndirectLightMode = "Ambient Color";
-      IndirectLight = "1 1 1 1";
       ReflectionMode = "Baked Cubemap";
       reflectionPath = "levels/probeTest/probes/";
       Bake = "0";
@@ -134,6 +135,9 @@ new SimGroup(MissionGroup) {
       canSaveDynamicFields = "1";
       persistentId = "8072e1be-2846-11e7-9f56-abd46b190c60";
          GroundColor = "0.8 0.7 0.5 1";
+         IndirectLight = "1 1 1 1";
+         IndirectLightMode = "Spherical Harmonics";
+         Intensity = "1";
          SkyColor = "0.5 0.5 1 1";
    };
    new ConvexShape() {
@@ -243,7 +247,7 @@ new SimGroup(MissionGroup) {
    };
    new PointLight() {
       radius = "10";
-      isEnabled = "1";
+      isEnabled = "0";
       color = "0.887923 1 0.008023 1";
       brightness = "1";
       castShadows = "0";
@@ -272,5 +276,24 @@ new SimGroup(MissionGroup) {
       canSave = "1";
       canSaveDynamicFields = "1";
    };
+   new ReflectionProbe() {
+      enabled = "0";
+      ProbeShape = "Box";
+      radius = "10";
+      ReflectionMode = "Baked Cubemap";
+      reflectionPath = "levels/probeTest/probes/";
+      Bake = "0";
+      position = "15.4549 0.559989 4.53387";
+      rotation = "1 0 0 0";
+      scale = "5 5 5";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+      persistentId = "4ec88e62-4e8f-11e8-ae68-993c6bb8eb5b";
+         GroundColor = "0.8 0.7 0.5 1";
+         IndirectLight = "1 1 1 1";
+         IndirectLightMode = "Spherical Harmonics";
+         Intensity = "1";
+         SkyColor = "0.5 0.5 1 1";
+   };
 };
 //--- OBJECT WRITE END ---

+ 1500 - 43
Templates/Full/game/levels/Empty Room.mis

@@ -1,82 +1,1539 @@
 //--- OBJECT WRITE BEGIN ---
 new SimGroup(MissionGroup) {
+   canSave = "1";
    canSaveDynamicFields = "1";
-      Enabled = "1";
+      enabled = "1";
 
    new LevelInfo(theLevelInfo) {
+      nearClip = "0.1";
       visibleDistance = "1000";
+      visibleGhostDistance = "0";
+      decalBias = "0.0015";
       fogColor = "0.6 0.6 0.7 1";
       fogDensity = "0";
       fogDensityOffset = "700";
       fogAtmosphereHeight = "0";
       canvasClearColor = "0 0 0 255";
-         canSaveDynamicFields = "1";
-         levelName = "Empty Room";
-         desc0 = "An empty room ready to be populated with Torque objects.";
-         Enabled = "1";
-   };
-   new SkyBox(theSky) {
+      ambientLightBlendPhase = "1";
+      ambientLightBlendCurve = "0 0 -1 -1";
+      LevelEnvMap = "BlueSkyCubemap";
+      soundAmbience = "AudioAmbienceDefault";
+      soundDistanceModel = "Linear";
+      canSave = "1";
       canSaveDynamicFields = "1";
-      Position = "0 0 0";
-      rotation = "1 0 0 0";
-      scale = "1 1 1";
-      Material = "BlackSkyMat";
-      drawBottom = "0";
-      fogBandHeight = "0";
+         desc0 = "An empty room ready to be populated with Torque objects.";
+         enabled = "1";
+         levelName = "Empty Room";
    };
-   new Sun(theSun) {
-      canSaveDynamicFields = "1";
-      Position = "0 0 0";
-      rotation = "1 0 0 0";
-      scale = "1 1 1";
-      azimuth = "230.396";
-      elevation = "45";
-      color = "0.968628 0.901961 0.901961 1";
-      ambient = "0.078431 0.113725 0.156863 1";
+   new ScatterSky() {
+      skyBrightness = "25";
+      sunSize = "1";
+      colorizeAmount = "0";
+      colorize = "0 0 0 1";
+      rayleighScattering = "0.0035";
+      sunScale = "1 1 1 1";
+      ambientScale = "1 1 1 1";
+      fogScale = "1 1 1 1";
+      exposure = "1";
+      zOffset = "0";
+      azimuth = "0";
+      elevation = "90";
+      moonAzimuth = "0";
+      moonElevation = "45";
       castShadows = "1";
+      staticRefreshFreq = "8";
+      dynamicRefreshFreq = "8";
+      brightness = "1";
+      flareScale = "1";
+      nightColor = "0.0196078 0.0117647 0.109804 1";
+      nightFogColor = "0.0196078 0.0117647 0.109804 1";
+      moonEnabled = "1";
+      moonMat = "Moon_Glow_Mat";
+      moonScale = "0.2";
+      moonLightColor = "0.192157 0.192157 0.192157 1";
+      useNightCubemap = "1";
+      nightCubemap = "NightCubemap";
       attenuationRatio = "0 1 1";
       shadowType = "PSSM";
       texSize = "1024";
-      overDarkFactor = "3000 2000 1000 250";
-      shadowDistance = "400";
-      shadowSoftness = "0.25";
+      overDarkFactor = "2000 1000 500 100";
+      shadowDistance = "200";
+      shadowSoftness = "0.15";
       numSplits = "4";
-      logWeight = "0.96";
-      fadeStartDistance = "325";
+      logWeight = "0.91";
+      fadeStartDistance = "0";
       lastSplitTerrainOnly = "0";
+      representedInLightmap = "0";
+      shadowDarkenColor = "0 0 0 -1";
+      includeLightmappedGeometryInShadow = "0";
+      position = "171.065 195.399 -1.10657";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+         mieScattering = "0.0045";
    };
    new SimGroup(PlayerDropPoints) {
+      canSave = "1";
       canSaveDynamicFields = "1";
-         Enabled = "1";
+         enabled = "1";
 
       new SpawnSphere() {
-         canSaveDynamicFields = "1";
-         Position = "0 0 2";
-         rotation = "1 0 0 0";
-         scale = "1 1 1";
-         dataBlock = "SpawnSphereMarker";
-         radius = "5";
-         spawnClass = "";
-         spawnDatablock = "";
          autoSpawn = "0";
+         spawnTransform = "0";
+         radius = "5";
          sphereWeight = "1";
          indoorWeight = "1";
          outdoorWeight = "1";
-            Enabled = "1";
+         isAIControlled = "0";
+         dataBlock = "SpawnSphereMarker";
+         position = "0 0 2";
+         rotation = "1 0 0 0";
+         scale = "1 1 1";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+            enabled = "1";
             homingCount = "0";
             lockCount = "0";
       };
    };
-   new GroundPlane() {
+   new SimGroup(CameraBookmarks) {
+      canSave = "1";
       canSaveDynamicFields = "1";
-      Position = "0 0 0";
-      rotation = "1 0 0 0";
-      scale = "1 1 1";
+
+      new CameraBookmark() {
+         isAIControlled = "0";
+         dataBlock = "CameraBookmarkMarker";
+         position = "12.4869 4.10897 4.58326";
+         rotation = "-0.0713349 -0.106471 -0.991754 112.795";
+         scale = "1 1 1";
+         internalName = "CryEngine_Sponza_Compare";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+      };
+      new CameraBookmark() {
+         isAIControlled = "0";
+         dataBlock = "CameraBookmarkMarker";
+         position = "-17.8058 0.291567 6.37798";
+         rotation = "0.214026 0.125288 -0.96876 62.2862";
+         scale = "1 1 1";
+         internalName = "mirrorTest";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+      };
+      new CameraBookmark() {
+         isAIControlled = "0";
+         dataBlock = "CameraBookmarkMarker";
+         position = "15.8629 -8.35756 4.69165";
+         rotation = "0.0151395 -0.00460125 0.999875 33.8145";
+         scale = "1 1 1";
+         internalName = "NewCamera_0";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+      };
+   };
+   new GroundPlane() {
       squareSize = "128";
       scaleU = "12";
       scaleV = "12";
-      Material = "BlankWhite";
-         Enabled = "1";
+      Material = "Grid512_Grey_Mat";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+         enabled = "1";
+         position = "0 0 0";
+         rotation = "1 0 0 0";
+         scale = "1 1 1";
+   };
+   new TSStatic() {
+      shapeName = "art/shapes/sponza/sponza.dae";
+      playAmbient = "1";
+      meshCulling = "0";
+      originSort = "0";
+      CollisionType = "Collision Mesh";
+      DecalType = "Visible Mesh";
+      allowPlayerStep = "0";
+      alphaFadeEnable = "0";
+      alphaFadeStart = "100";
+      alphaFadeEnd = "150";
+      alphaFadeInverse = "0";
+      renderNormals = "0";
+      forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
+      position = "-8.91623 -0.551134 0.392056";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+         bricks = "bricks_mat";
+         flagpole = "flagpole_mat";
+   };
+   new TSStatic() {
+      shapeName = "art/shapes/sponza/sponza_extra_curtains.dae";
+      playAmbient = "1";
+      meshCulling = "0";
+      originSort = "0";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
+      allowPlayerStep = "0";
+      alphaFadeEnable = "0";
+      alphaFadeStart = "100";
+      alphaFadeEnd = "150";
+      alphaFadeInverse = "0";
+      renderNormals = "0";
+      forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
+      position = "-8.91623 -0.551134 0.392056";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      hidden = "1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new SimGroup(Probes) {
+      canSave = "1";
+      canSaveDynamicFields = "1";
+
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.618686 0.45908 0.147998 0.5";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         Bake = "0";
+         position = "-11.4607 0.712664 16.8264";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "c04383f9-2147-11e7-9298-9a7d9c6feabe";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "19.0392 11.1366 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6ab2beca-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "8.19861 11.1366 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6ab8d961-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-0.368001 11.1366 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6abfde5c-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-10.2042 11.1366 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6ac869fc-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-19.4857 11.1366 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6acef9c4-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-29.2419 11.1366 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6ad5145b-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-38.8665 11.1366 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6adb7d13-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "18.8673 -10.4486 16.5257";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6ae25afd-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "8.02669 -10.4486 16.5257";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6aea4a5b-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-0.53992 -10.4486 16.5257";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6af0da23-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-10.3761 -10.4486 16.5257";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6af769ec-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-19.6576 -10.4486 16.5257";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6afdf9b4-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-29.4138 -10.4486 16.5257";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6b05c202-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-39.0384 -10.4486 16.5257";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "6b0dd870-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "1 0.00455975 0.00455975 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "19.0392 11.1366 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "25d6efbe-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "1 0.00455975 0.00455975 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.201096 0.00775103 0.00775103 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "8.19861 11.1366 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "25de1bc8-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.0356144 0.0315514 0.00604059 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-0.368001 11.1366 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "25e4365f-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.0356144 0.0315514 0.00604059 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-10.2042 11.1366 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "25eaed38-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.532";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-19.4857 11.1366 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "25f17d01-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.201096 0.00775103 0.00775103 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-29.2419 11.1366 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "25fa7dd3-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "1 0.00455975 0.00455975 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-38.8665 11.1366 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "26021f0f-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "1 0.00455975 0.00455975 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "1 0.00455975 0.00455975 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "18.8673 -10.4486 5.00406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "ac3fe3f0-1ff5-11e7-b852-d5bab2d8fbcc";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "1 0.00455975 0.00455975 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.201096 0.00775103 0.00775103 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "8.02669 -10.4486 5.00406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "f41bf404-209f-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.0461488 0.113921 0.0915184 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-0.53992 -10.4486 5.00406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "fa446dc9-209f-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.499505 1 0.687031 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-10.3761 -10.4486 5.00406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "fe03f4d6-209f-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.0461488 0.113921 0.0915184 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-19.6576 -10.4486 5.00406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "02bbe946-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.201096 0.00775103 0.00775103 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-29.4138 -10.4486 5.00406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "072e6f42-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.119264 0.334458 0.554227 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "8.19861 0.0539651 26.7401";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "afa9693c-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.119264 0.334458 0.554227 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-0.368001 0.0539651 26.7401";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "afa1c7ff-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.119264 0.334458 0.554227 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-10.2042 0.0539651 26.7401";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "af993c5f-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.119264 0.334458 0.554227 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-19.4857 0.0539651 26.7401";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "af919b23-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.119264 0.334458 0.554227 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-29.2419 0.0539651 26.7401";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "af8a4807-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "19.0392 0.0539651 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1f5851f-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.618686 0.45908 0.147998 0.5";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "8.19861 0.0539651 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1eef556-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.618686 0.45908 0.147998 0.5";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-0.368001 0.0539651 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1e8658e-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "1 0.1 0.005 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-9.37523 0.0539651 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1e13983-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.618686 0.45908 0.147998 0.5";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-19.4857 0.0539651 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1d8ade3-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.618686 0.45908 0.147998 0.5";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-29.2419 0.0539651 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1d133b7-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.036 0.032 0.006 0.505";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-38.8665 0.0539651 16.5406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1cacaff-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.89 0.39 0.148 0.949";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "19.0392 0.0539651 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1c43b36-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.618686 0.45908 0.147998 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "8.19861 0.0539651 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1bdf98f-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "1 0.1 0.005 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-0.368001 0.0539651 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1b71ba5-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.618686 0.45908 0.147998 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-10.2042 0.0539651 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1afef9a-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.618686 0.45908 0.147998 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-19.4857 0.0539651 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1a9fc14-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.618686 0.45908 0.147998 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-29.2419 0.0539651 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a1a3e17d-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "0.89 0.39 0.148 0.949";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-38.8665 0.0539651 5.01901";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "a19dedf6-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "0.5 0.5 1 1";
+      };
+      new ReflectionProbe() {
+         enabled = "1";
+         ProbeShape = "Sphere";
+         radius = "10";
+         Intensity = "1";
+         IndirectLightMode = "Ambient Color";
+         IndirectLight = "1 0.00455975 0.00455975 1";
+         ReflectionMode = "Baked Cubemap";
+         reflectionPath = "levels/Empty Room/probes/";
+         StaticCubemap = "DesertSkyCubemap";
+         Bake = "0";
+         position = "-39.0384 -10.4486 5.00406";
+         rotation = "1 0 0 0";
+         scale = "5 5 5";
+         canSave = "1";
+         canSaveDynamicFields = "1";
+         persistentId = "0b06aecb-20a0-11e7-9c9c-8cefe314de25";
+            GroundColor = "0.8 0.7 0.5 1";
+            ProbeMode = "Baked Cubemap";
+            SkyColor = "1 0.00455975 0.00455975 1";
+      };
+   };
+   new ReflectionProbe(Skylight) {
+      enabled = "1";
+      ProbeShape = "Sphere";
+      radius = "200";
+      Intensity = "0.1";
+      IndirectLightMode = "Ambient Color";
+      IndirectLight = "1 1 1 1";
+      ReflectionMode = "Baked Cubemap";
+      reflectionPath = "levels/Empty Room/probes/";
+      StaticCubemap = "DesertSkyCubemap";
+      Bake = "0";
+      position = "0 0 50";
+      rotation = "1 0 0 0";
+      scale = "100 100 100";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+      persistentId = "0039b2a6-20c2-11e7-b558-91c869a431e1";
+         GroundColor = "0.8 0.7 0.5 1";
+         ProbeMode = "Baked Cubemap";
+         SkyColor = "0.5 0.5 1 1";
+   };
+   new TSStatic() {
+      shapeName = "art/shapes/textures/pbrGrid.dae";
+      playAmbient = "1";
+      meshCulling = "0";
+      originSort = "0";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
+      allowPlayerStep = "0";
+      alphaFadeEnable = "0";
+      alphaFadeStart = "100";
+      alphaFadeEnd = "150";
+      alphaFadeInverse = "0";
+      renderNormals = "0";
+      forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
+      position = "-26.8371 -4.49793 0.284616";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new TSStatic() {
+      shapeName = "art/shapes/textures/PBRTEST.dae";
+      playAmbient = "1";
+      meshCulling = "0";
+      originSort = "0";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
+      allowPlayerStep = "0";
+      alphaFadeEnable = "0";
+      alphaFadeStart = "100";
+      alphaFadeEnd = "150";
+      alphaFadeInverse = "0";
+      renderNormals = "0";
+      forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
+      position = "23.1539 0.397566 1.22924";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new PointLight() {
+      radius = "15";
+      isEnabled = "1";
+      color = "1 0.275833 0.00455975 1";
+      brightness = "1";
+      castShadows = "0";
+      staticRefreshFreq = "250";
+      dynamicRefreshFreq = "8";
+      priority = "1";
+      animate = "1";
+      animationPeriod = "1";
+      animationPhase = "1";
+      flareScale = "1";
+      attenuationRatio = "0 1 1";
+      shadowType = "DualParaboloidSinglePass";
+      texSize = "512";
+      overDarkFactor = "2000 1000 500 100";
+      shadowDistance = "400";
+      shadowSoftness = "0.15";
+      numSplits = "1";
+      logWeight = "0.91";
+      fadeStartDistance = "0";
+      lastSplitTerrainOnly = "0";
+      representedInLightmap = "0";
+      shadowDarkenColor = "0 0 0 -1";
+      includeLightmappedGeometryInShadow = "0";
+      position = "-38.6032 -10.4171 3.87317";
+      rotation = "1 0 0 0";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new PointLight() {
+      radius = "15";
+      isEnabled = "1";
+      color = "1 0.275833 0.00455975 1";
+      brightness = "1";
+      castShadows = "0";
+      staticRefreshFreq = "250";
+      dynamicRefreshFreq = "8";
+      priority = "1";
+      animate = "1";
+      animationPeriod = "1";
+      animationPhase = "1";
+      flareScale = "1";
+      attenuationRatio = "0 1 1";
+      shadowType = "DualParaboloidSinglePass";
+      texSize = "512";
+      overDarkFactor = "2000 1000 500 100";
+      shadowDistance = "400";
+      shadowSoftness = "0.15";
+      numSplits = "1";
+      logWeight = "0.91";
+      fadeStartDistance = "0";
+      lastSplitTerrainOnly = "0";
+      representedInLightmap = "0";
+      shadowDarkenColor = "0 0 0 -1";
+      includeLightmappedGeometryInShadow = "0";
+      position = "-38.8136 10.7957 3.87317";
+      rotation = "1 0 0 0";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new PointLight() {
+      radius = "15";
+      isEnabled = "1";
+      color = "1 0.275833 0.00455975 1";
+      brightness = "1";
+      castShadows = "0";
+      staticRefreshFreq = "250";
+      dynamicRefreshFreq = "8";
+      priority = "1";
+      animate = "1";
+      animationPeriod = "1";
+      animationPhase = "1";
+      flareScale = "1";
+      attenuationRatio = "0 1 1";
+      shadowType = "DualParaboloidSinglePass";
+      texSize = "512";
+      overDarkFactor = "2000 1000 500 100";
+      shadowDistance = "400";
+      shadowSoftness = "0.15";
+      numSplits = "1";
+      logWeight = "0.91";
+      fadeStartDistance = "0";
+      lastSplitTerrainOnly = "0";
+      representedInLightmap = "0";
+      shadowDarkenColor = "0 0 0 -1";
+      includeLightmappedGeometryInShadow = "0";
+      position = "19.1024 10.5955 3.87317";
+      rotation = "1 0 0 0";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new PointLight() {
+      radius = "15";
+      isEnabled = "1";
+      color = "1 0.275833 0.00455975 1";
+      brightness = "1";
+      castShadows = "0";
+      staticRefreshFreq = "250";
+      dynamicRefreshFreq = "8";
+      priority = "1";
+      animate = "1";
+      animationPeriod = "1";
+      animationPhase = "1";
+      flareScale = "1";
+      attenuationRatio = "0 1 1";
+      shadowType = "DualParaboloidSinglePass";
+      texSize = "512";
+      overDarkFactor = "2000 1000 500 100";
+      shadowDistance = "400";
+      shadowSoftness = "0.15";
+      numSplits = "1";
+      logWeight = "0.91";
+      fadeStartDistance = "0";
+      lastSplitTerrainOnly = "0";
+      representedInLightmap = "0";
+      shadowDarkenColor = "0 0 0 -1";
+      includeLightmappedGeometryInShadow = "0";
+      position = "19.2761 -10.4968 3.87317";
+      rotation = "1 0 0 0";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new PointLight() {
+      radius = "15";
+      isEnabled = "1";
+      color = "0.0802193 0.804559 0.7593 1";
+      brightness = "1";
+      castShadows = "0";
+      staticRefreshFreq = "250";
+      dynamicRefreshFreq = "8";
+      priority = "1";
+      animate = "1";
+      animationPeriod = "1";
+      animationPhase = "1";
+      flareScale = "1";
+      attenuationRatio = "0 1 1";
+      shadowType = "DualParaboloidSinglePass";
+      texSize = "512";
+      overDarkFactor = "2000 1000 500 100";
+      shadowDistance = "400";
+      shadowSoftness = "0.15";
+      numSplits = "1";
+      logWeight = "0.91";
+      fadeStartDistance = "0";
+      lastSplitTerrainOnly = "0";
+      representedInLightmap = "0";
+      shadowDarkenColor = "0 0 0 -1";
+      includeLightmappedGeometryInShadow = "0";
+      position = "-9.85153 -12.8646 7.06711";
+      rotation = "1 0 0 0";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new TSStatic() {
+      shapeName = "art/shapes/cube/cube.dae";
+      playAmbient = "1";
+      meshCulling = "0";
+      originSort = "0";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
+      allowPlayerStep = "0";
+      alphaFadeEnable = "0";
+      alphaFadeStart = "100";
+      alphaFadeEnd = "150";
+      alphaFadeInverse = "0";
+      renderNormals = "0";
+      forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
+      position = "-19.6187 2.45554 4.946";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new Prefab() {
+      fileName = "art/prefabs/vaseFlame.prefab";
+      position = "3.24204 4.91314 3.64629";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new Prefab() {
+      fileName = "art/prefabs/vaseFlame.prefab";
+      position = "3.27432 -4.14598 3.64629";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new Prefab() {
+      fileName = "art/prefabs/vaseFlame.prefab";
+      position = "-24.3409 -4.14598 3.64629";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new Prefab() {
+      fileName = "art/prefabs/vaseFlame.prefab";
+      position = "-24.378 4.92861 3.5101";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new TSStatic() {
+      shapeName = "art/shapes/cube/cube.dae";
+      playAmbient = "1";
+      meshCulling = "0";
+      originSort = "0";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
+      allowPlayerStep = "0";
+      alphaFadeEnable = "0";
+      alphaFadeStart = "100";
+      alphaFadeEnd = "150";
+      alphaFadeInverse = "0";
+      renderNormals = "0";
+      forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
+      position = "24.1149 10.5531 0.125195";
+      rotation = "1 0 0 0";
+      scale = "1 10 10";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+   };
+   new TSStatic() {
+      shapeName = "art/shapes/cube/cube.dae";
+      playAmbient = "1";
+      meshCulling = "0";
+      originSort = "0";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
+      allowPlayerStep = "0";
+      alphaFadeEnable = "0";
+      alphaFadeStart = "100";
+      alphaFadeEnd = "150";
+      alphaFadeInverse = "0";
+      renderNormals = "0";
+      forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
+      position = "12.7735 12.0238 0.464151";
+      rotation = "1 0 0 0";
+      scale = "1 10 10";
+      canSave = "1";
+      canSaveDynamicFields = "1";
    };
 };
 //--- OBJECT WRITE END ---

+ 64 - 34
Templates/Full/game/levels/Empty Terrain.mis

@@ -16,7 +16,6 @@ new SimGroup(MissionGroup) {
       canvasClearColor = "0 0 0 255";
       ambientLightBlendPhase = "1";
       ambientLightBlendCurve = "0 0 -1 -1";
-      LevelEnvMap = "DesertSkyCubemap";
       soundAmbience = "AudioAmbienceDefault";
       soundDistanceModel = "Linear";
       canSave = "1";
@@ -33,6 +32,7 @@ new SimGroup(MissionGroup) {
       baseTexFormat = "DDS";
       lightMapSize = "256";
       screenError = "16";
+      ignoreZodiacs = "0";
       position = "-1024 -1024 179.978";
       rotation = "1 0 0 0";
       canSave = "1";
@@ -67,8 +67,8 @@ new SimGroup(MissionGroup) {
       playAmbient = "1";
       meshCulling = "0";
       originSort = "0";
-      collisionType = "Collision Mesh";
-      decalType = "Collision Mesh";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
       allowPlayerStep = "1";
       alphaFadeEnable = "0";
       alphaFadeStart = "100";
@@ -76,6 +76,10 @@ new SimGroup(MissionGroup) {
       alphaFadeInverse = "0";
       renderNormals = "0";
       forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
       position = "9.42739 27.7428 241.767";
       rotation = "1 0 0 0";
       scale = "1 1 1";
@@ -133,8 +137,8 @@ new SimGroup(MissionGroup) {
       playAmbient = "1";
       meshCulling = "0";
       originSort = "0";
-      collisionType = "Collision Mesh";
-      decalType = "Collision Mesh";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
       allowPlayerStep = "1";
       alphaFadeEnable = "0";
       alphaFadeStart = "100";
@@ -142,6 +146,10 @@ new SimGroup(MissionGroup) {
       alphaFadeInverse = "0";
       renderNormals = "0";
       forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
       position = "17.8995 16.1596 241.136";
       rotation = "1 0 0 0";
       scale = "1 1 1";
@@ -158,27 +166,18 @@ new SimGroup(MissionGroup) {
 
       surface = "0 0 0 1 1.29457 -0.42643 0.5";
       surface = "0 1 0 0 1.29457 -0.42643 -0.5";
-      surface = "0.707107 0 0 0.707107 1.29457 5.75621 0";
-      surface = "0 0.707107 -0.707107 0 1.29457 -6.60907 -4.54747e-013";
-      surface = "0.5 0.5 -0.5 0.5 -3.35148 -0.42643 -1.28542e-008";
-      surface = "0.5 -0.5 0.5 0.5 5.94063 -0.42643 -1.28542e-008";
-   };
-   new EnvVolume() {
-      AreaEnvMap = "MipCubemap";
-      cubeReflectorDesc = "DefaultCubeDesc";
-      position = "8.21068 19.3464 241.855";
-      rotation = "1 0 0 0";
-      scale = "10 10 10";
-      canSave = "1";
-      canSaveDynamicFields = "1";
+      surface = "0.707107 0 0 0.707106 1.29457 5.75621 0";
+      surface = "0 0.707107 -0.707107 0 1.29457 -6.60907 -4.54747e-13";
+      surface = "0.5 0.5 -0.5 0.5 -3.35148 -0.42643 -1.28542e-08";
+      surface = "0.5 -0.5 0.5 0.5 5.94063 -0.42643 -1.28542e-08";
    };
    new TSStatic() {
       shapeName = "art/shapes/textures/PBRTEST2.dae";
       playAmbient = "1";
       meshCulling = "0";
       originSort = "0";
-      collisionType = "Collision Mesh";
-      decalType = "Collision Mesh";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
       allowPlayerStep = "1";
       alphaFadeEnable = "0";
       alphaFadeStart = "100";
@@ -186,6 +185,10 @@ new SimGroup(MissionGroup) {
       alphaFadeInverse = "0";
       renderNormals = "0";
       forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
       position = "5.60579 18.8689 241.462";
       rotation = "1 0 0 0";
       scale = "1 1 1";
@@ -198,8 +201,8 @@ new SimGroup(MissionGroup) {
       meshCulling = "0";
       originSort = "0";
       cubeReflectorDesc = "DefaultCubeDesc";
-      collisionType = "Collision Mesh";
-      decalType = "Collision Mesh";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
       allowPlayerStep = "1";
       alphaFadeEnable = "0";
       alphaFadeStart = "100";
@@ -207,6 +210,10 @@ new SimGroup(MissionGroup) {
       alphaFadeInverse = "0";
       renderNormals = "0";
       forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
       position = "22.1653 4.41543 241.364";
       rotation = "1 0 0 0";
       scale = "1 1 1";
@@ -218,8 +225,8 @@ new SimGroup(MissionGroup) {
       playAmbient = "1";
       meshCulling = "0";
       originSort = "0";
-      collisionType = "Collision Mesh";
-      decalType = "Collision Mesh";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
       allowPlayerStep = "1";
       alphaFadeEnable = "0";
       alphaFadeStart = "100";
@@ -227,6 +234,10 @@ new SimGroup(MissionGroup) {
       alphaFadeInverse = "0";
       renderNormals = "0";
       forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
       position = "15.72 5.9186 241.191";
       rotation = "1 0 0 0";
       scale = "1 1 1";
@@ -246,8 +257,8 @@ new SimGroup(MissionGroup) {
       playAmbient = "1";
       meshCulling = "0";
       originSort = "0";
-      collisionType = "Collision Mesh";
-      decalType = "Collision Mesh";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
       allowPlayerStep = "0";
       alphaFadeEnable = "0";
       alphaFadeStart = "100";
@@ -255,6 +266,10 @@ new SimGroup(MissionGroup) {
       alphaFadeInverse = "0";
       renderNormals = "0";
       forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
       position = "12.8895 -2.18239 238.765";
       rotation = "1 0 0 0";
       scale = "1 1 1";
@@ -266,8 +281,8 @@ new SimGroup(MissionGroup) {
       playAmbient = "1";
       meshCulling = "0";
       originSort = "0";
-      collisionType = "Collision Mesh";
-      decalType = "Collision Mesh";
+      CollisionType = "Collision Mesh";
+      DecalType = "Collision Mesh";
       allowPlayerStep = "0";
       alphaFadeEnable = "0";
       alphaFadeStart = "100";
@@ -275,6 +290,10 @@ new SimGroup(MissionGroup) {
       alphaFadeInverse = "0";
       renderNormals = "0";
       forceDetail = "-1";
+      ignoreZodiacs = "0";
+      useGradientRange = "0";
+      gradientRange = "0 180";
+      invertGradientRange = "0";
       position = "10.406 10.0939 240.551";
       rotation = "1 0 0 0";
       scale = "1 1 1";
@@ -300,9 +319,6 @@ new SimGroup(MissionGroup) {
       enabled = "1";
       ProbeShape = "Sphere";
       radius = "10";
-      Intensity = "1";
-      IndirectLightMode = "Ambient Color";
-      IndirectLight = "1 1 1 1";
       ReflectionMode = "Baked Cubemap";
       reflectionPath = "levels/empty terrain/";
       Bake = "0";
@@ -312,14 +328,14 @@ new SimGroup(MissionGroup) {
       canSave = "1";
       canSaveDynamicFields = "1";
       persistentId = "fc28816f-65a6-11e7-b9a1-b6f5317ad553";
+         IndirectLight = "1 1 1 1";
+         IndirectLightMode = "Ambient Color";
+         Intensity = "1";
    };
    new ReflectionProbe() {
       enabled = "1";
       ProbeShape = "Sphere";
       radius = "10";
-      Intensity = "1";
-      IndirectLightMode = "Ambient Color";
-      IndirectLight = "1 1 1 1";
       ReflectionMode = "Baked Cubemap";
       reflectionPath = "levels/empty terrain/";
       Bake = "0";
@@ -329,6 +345,20 @@ new SimGroup(MissionGroup) {
       canSave = "1";
       canSaveDynamicFields = "1";
       persistentId = "10bb5f41-65a7-11e7-b9a1-b6f5317ad553";
+         IndirectLight = "1 1 1 1";
+         IndirectLightMode = "Ambient Color";
+         Intensity = "1";
+   };
+   new Skylight() {
+      enabled = "1";
+      StaticCubemap = "HdrSkyCubemap";
+      position = "16.4688 10.5898 241.399";
+      rotation = "1 0 0 0";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+      persistentId = "de646cc8-4f49-11e8-8a25-ec48d38e6b49";
+         reflectionPath = "levels/Empty Terrain/probes/";
    };
 };
 //--- OBJECT WRITE END ---

+ 147 - 124
Templates/Full/game/shaders/common/gl/lighting.glsl

@@ -1,6 +1,6 @@
 //-----------------------------------------------------------------------------
 // Copyright (c) 2012 GarageGames, LLC
-//
+// Portions Copyright Zefiros
 // 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
@@ -38,12 +38,140 @@ uniform vec4 inLightColor[4];
 
 uniform vec4 ambient;
 #define ambientCameraFactor 0.3
-uniform float specularPower;
-uniform vec4 specularColor;
+uniform float smoothness;
+uniform float metalness;
+uniform vec4 albedo;
 
 #endif // !TORQUE_SHADERGEN
 
 
+vec3 F_schlick( in vec3 f0, in vec3 f90, in float u )
+{
+	//
+    //  F( v, h ) =  F0 + ( 1.0 - F0 ) *  pow( 1.0f - HdotV,  5.0f )
+    //
+    //
+    //  where 
+    //
+    //  F0 = BaseColor * nDotL
+    //
+    //  Dielectric materials always have a range of 0.02 < F0 < 0.05 , use a stock value of 0.04 ( roughly plastics )
+    //
+
+	return f0 + ( f90 - f0 ) * pow( 1.0f - u ,  5.0f );
+}
+
+float Fr_DisneyDiffuse ( float NdotV , float NdotL , float LdotH , float linearRoughness )
+{
+	float energyBias = mix (0 , 0.5 , linearRoughness );
+	float energyFactor = mix (1.0 , 1.0 / 1.51 , linearRoughness );
+	float fd90 = energyBias + 2.0 * LdotH * LdotH * linearRoughness ;
+	vec3 f0 = vec3 ( 1.0f , 1.0f , 1.0f );
+	float lightScatter = F_schlick( f0 , vec3(fd90), NdotL ).r;
+	float viewScatter = F_schlick(f0 , vec3(fd90), NdotV ).r;
+
+	return lightScatter * viewScatter * energyFactor ;
+}
+
+float SmithGGX( float NdotL, float NdotV, float alpha )
+{
+    //
+    // G( L, V, h ) = G( L ) G( V )
+    //
+    //                    nDotL
+    // G( L ) = _________________________
+    //             nDotL ( 1 - k ) + k
+    //
+    //         
+    //                     NdotV
+    // G( V ) = _________________________
+    //             NdotV ( 1 - k ) + k
+    //
+    //
+    //               pow( ( Roughness + 1 ), 2)
+    //  , Where  k = __________________________     ( unreal 4 )
+    //                          8
+    //
+	
+	float alphaSqr = alpha * alpha;
+
+	//float GGX_V = NdotL * sqrt ( ( - NdotV * alphaSqr + NdotV ) * NdotV + alphaSqr );
+	//float GGX_L = NdotV * sqrt ( ( - NdotL * alphaSqr + NdotL ) * NdotL + alphaSqr );
+	
+	float GGX_V = NdotL + sqrt ( ( - NdotV * alphaSqr + NdotV ) * NdotV + alphaSqr );
+	float GGX_L = NdotV + sqrt ( ( - NdotL * alphaSqr + NdotL ) * NdotL + alphaSqr );
+	
+	return 1.0/( GGX_V + GGX_L ); 
+	//return 0.5f / ( GGX_V + GGX_L ); 
+}
+
+float D_GGX( float NdotH , float alpha )
+{
+    //
+    // or GGX ( disney / unreal 4 )
+    //
+    //  alpha = pow( roughness, 2 );
+    //
+    //                                    pow( alpha, 2 )
+    //  D( h ) = ________________________________________________________________      
+    //           PI pow( pow( NdotH , 2 ) ( pow( alpha, 2 ) - 1 ) + 1 ), 2 )
+    //
+
+	float alphaSqr = alpha*alpha;
+	float f = ( NdotH * alphaSqr - NdotH ) * NdotH + 1;
+	return alphaSqr / ( M_PI_F * (f * f) );
+}
+
+vec4 EvalBDRF( vec3 baseColor, vec3 lightColor, vec3 toLight, vec3 position, vec3 normal,  float roughness, float metallic )
+{
+	//
+    //  Microfacet Specular Cook-Torrance
+    //
+    //                D( h ) F( v, h ) G( l, v, h )
+    //    f( l, v ) = ____________________________
+    //                 4 ( dot( n, l ) dot( n, v )
+    //                 
+    //
+
+	vec3 L = normalize( toLight );
+	vec3 V = normalize( -position );
+	vec3 H = normalize( L + V );
+	vec3 N = normal;
+	
+	float NdotV = abs( dot( N, V ) ) + 1e-5f;
+	float NdotH = saturate( dot( N, H ) );
+	float NdotL = saturate( dot( N, L ) );
+	float LdotH = saturate( dot( L, H ) );
+	
+	float VdotH = saturate( dot( V, H ) );
+
+	if ( NdotL == 0 ) 
+		return vec4( 0.0f, 0.0f, 0.0f, 0.0f ); 
+	
+	float alpha = roughness;
+	float visLinAlpha = alpha * alpha;
+	
+	vec3 f0 = baseColor;
+	float  metal = metallic;
+	
+	vec3 F_conductor= F_schlick( f0, vec3( 1.0, 1.0, 1.0 ), VdotH );
+	vec3 F_dielec   = F_schlick( vec3( 0.04, 0.04, 0.04 ), vec3( 1.0, 1.0, 1.0 ), VdotH );
+	float  Vis        = SmithGGX( NdotL, NdotV, visLinAlpha );
+	float  D          = D_GGX( NdotH, visLinAlpha );
+	
+	vec3 Fr_dielec    = D * F_dielec * Vis; 
+	vec3 Fr_conductor = D * F_conductor * Vis; 
+	
+	vec3 Fd = vec3(Fr_DisneyDiffuse( NdotV , NdotL , LdotH , visLinAlpha ) / M_PI_F);
+    vec3 specular = ( 1.0f - metal ) * Fr_dielec + metal * Fr_conductor;
+	vec3 diffuse  = ( 1.0f - metal ) * Fd * f0;
+   
+    vec3 ret = ( diffuse + specular + lightColor) * vec3(NdotL);
+	
+	float FR = saturate(length(specular));
+	return vec4(ret,FR);
+}
+
 void compute4Lights( vec3 wsView, 
                      vec3 wsPosition, 
                      vec3 wsNormal,
@@ -57,8 +185,9 @@ void compute4Lights( vec3 wsView,
                         vec4 inLightSpotDir[3],
                         vec4 inLightSpotAngle,
                         vec4 inLightSpotFalloff,
-                        float specularPower,
-                        vec4 specularColor,
+                        float smoothness,
+                        float metalness,
+                        vec4 albedo,
 
                      #endif // TORQUE_SHADERGEN
                      
@@ -81,9 +210,6 @@ void compute4Lights( vec3 wsView,
    for ( i = 0; i < 3; i++ )
       lightVectors[i] = wsPosition[i] - inLightPos[i];
 
-   vec4 squareDists = vec4(0);
-   for ( i = 0; i < 3; i++ )
-      squareDists += lightVectors[i] * lightVectors[i];
 
    // Accumulate the dot product between the light 
    // vector and the normal.
@@ -99,40 +225,12 @@ void compute4Lights( vec3 wsView,
    vec4 nDotL = vec4(0);
    for ( i = 0; i < 3; i++ )
       nDotL += lightVectors[i] * -wsNormal[i];
-
-   vec4 rDotL = vec4(0);
-   #ifndef TORQUE_BL_NOSPECULAR
-
-      // We're using the Phong specular reflection model
-      // here where traditionally Torque has used Blinn-Phong
-      // which has proven to be more accurate to real materials.
-      //
-      // We do so because its cheaper as do not need to 
-      // calculate the half angle for all 4 lights.
-      //   
-      // Advanced Lighting still uses Blinn-Phong, but the
-      // specular reconstruction it does looks fairly similar
-      // to this.
-      //
-      vec3 R = reflect( wsView, -wsNormal );
-
-      for ( i = 0; i < 3; i++ )
-         rDotL += lightVectors[i] * R[i];
-
-   #endif
  
-   // Normalize the dots.
-   //
-   // Notice we're using the half type here to get a
-   // much faster sqrt via the rsq_pp instruction at 
-   // the loss of some precision.
-   //
-   // Unless we have some extremely large point lights
-   // i don't believe the precision loss will matter.
-   //
+   vec4 squareDists = vec4(0);
+   for ( i = 0; i < 3; i++ )
+      squareDists += lightVectors[i] * lightVectors[i];
    half4 correction = half4(inversesqrt( squareDists ));
    nDotL = saturate( nDotL * correction );
-   rDotL = clamp( rDotL * correction, 0.00001, 1.0 );
 
    // First calculate a simple point light linear 
    // attenuation factor.
@@ -157,93 +255,18 @@ void compute4Lights( vec3 wsView,
 
    #endif
 
-   // Finally apply the shadow masking on the attenuation.
-   atten *= shadowMask;
-
    // Get the final light intensity.
    vec4 intensity = nDotL * atten;
-
-   // Combine the light colors for output.
-   outDiffuse = vec4(0);
-   for ( i = 0; i < 4; i++ )
-      outDiffuse += intensity[i] * inLightColor[i];
-
-   // Output the specular power.
-   vec4 specularIntensity = pow( rDotL, vec4(specularPower) ) * atten;
    
-   // Apply the per-light specular attenuation.
-   vec4 specular = vec4(0,0,0,1);
+   // Combine the light colors for output.
+   vec4 lightColor = vec4(0);
    for ( i = 0; i < 4; i++ )
-      specular += vec4( inLightColor[i].rgb * inLightColor[i].a * specularIntensity[i], 1 );
-
-   // Add the final specular intensity values together
-   // using a single dot product operation then get the
-   // final specular lighting color.
-   outSpecular = specularColor * specular;
-}
-
-
-// This value is used in AL as a constant power to raise specular values
-// to, before storing them into the light info buffer. The per-material 
-// specular value is then computer by using the integer identity of 
-// exponentiation: 
-//
-//    (a^m)^n = a^(m*n)
-//
-//       or
-//
-//    (specular^constSpecular)^(matSpecular/constSpecular) = specular^(matSpecular*constSpecular)   
-//
-#define AL_ConstantSpecularPower 12.0f
-
-/// The specular calculation used in Advanced Lighting.
-///
-///   @param toLight    Normalized vector representing direction from the pixel 
-///                     being lit, to the light source, in world space.
-///
-///   @param normal  Normalized surface normal.
-///   
-///   @param toEye   The normalized vector representing direction from the pixel 
-///                  being lit to the camera.
-///
-float AL_CalcSpecular( vec3 toLight, vec3 normal, vec3 toEye )
-{
-   // (R.V)^c
-   float specVal = dot( normalize( -reflect( toLight, normal ) ), toEye );
-
-   // Return the specular factor.
-   return pow( max( specVal, 0.00001f ), AL_ConstantSpecularPower );
-}
-
-/// The output for Deferred Lighting
-///
-///   @param toLight    Normalized vector representing direction from the pixel 
-///                     being lit, to the light source, in world space.
-///
-///   @param normal  Normalized surface normal.
-///   
-///   @param toEye   The normalized vector representing direction from the pixel 
-///                  being lit to the camera.
-///
-vec4 AL_DeferredOutput(
-      vec3   lightColor,
-      vec3   diffuseColor,
-      vec4   matInfo,
-      vec4   ambient,
-      float specular,
-      float shadowAttenuation)
-{
-   vec3 specularColor = vec3(specular);
-   bool metalness = getFlag(matInfo.r, 3);
-   if ( metalness )
-   {
-       specularColor = 0.04 * (1 - specular) + diffuseColor * specular;
-   }
-
-   //specular = color * map * spec^gloss
-   float specularOut = (specularColor * matInfo.b * min(pow(max(specular,1.0f), max((matInfo.a / AL_ConstantSpecularPower),1.0f)),matInfo.a)).r;
+      lightColor += intensity[i] * inLightColor[i];
       
-   lightColor *= vec3(shadowAttenuation);
-   lightColor += ambient.rgb;
-   return vec4(lightColor.rgb, specularOut); 
-}
+   vec3 toLight = vec3(0);
+   for ( i = 0; i < 3; i++ )
+      toLight += lightVectors[i].rgb;
+      
+   outDiffuse = vec4(albedo.rgb*(1.0-metalness),albedo.a);
+   outSpecular = EvalBDRF( vec3( 1.0, 1.0, 1.0 ), lightColor.rgb, toLight, wsPosition, wsNormal, smoothness, metalness );
+}

+ 16 - 0
Templates/Full/game/shaders/common/gl/torque.glsl

@@ -336,4 +336,20 @@ vec3 toGamma(vec3 tex)
 }
 #endif //
 
+vec3 PBRFresnel(vec3 albedo, vec3 indirect, float metalness, float fresnel)
+{
+   vec3 diffuseColor = albedo - (albedo * metalness);
+   vec3 reflectColor = mix(indirect*albedo, indirect, fresnel);
+
+   return diffuseColor + reflectColor;
+}
+
+vec3 simpleFresnel(vec3 diffuseColor, vec3 reflectColor, float metalness, float angle, float bias, float power)
+{
+   float fresnelTerm = bias + (1.0 - bias) * pow(abs(1.0 - max(angle, 0)), power);
+
+   fresnelTerm *= metalness;
+
+   return mix(diffuseColor, reflectColor, fresnelTerm);
+}
 #endif // _TORQUE_GLSL_

+ 38 - 1
Templates/Full/game/shaders/common/lighting.hlsl

@@ -70,7 +70,7 @@ float Fr_DisneyDiffuse ( float NdotV , float NdotL , float LdotH , float linearR
 	float lightScatter = F_schlick( f0 , fd90 , NdotL ).r;
 	float viewScatter = F_schlick(f0 , fd90 , NdotV ).r;
 
-	return lightScatter * viewScatter * energyFactor ;
+	return lightScatter * viewScatter * energyFactor;
 }
 
 float SmithGGX( float NdotL, float NdotV, float alpha )
@@ -268,4 +268,41 @@ void compute4Lights( float3 wsView,
 
    outDiffuse = float4(albedo.rgb*(1.0-metalness),albedo.a);
    outSpecular = EvalBDRF( float3( 1.0, 1.0, 1.0 ), lightColor.rgb, toLight, wsPosition, wsNormal, smoothness, metalness );
+}
+
+float G1V(float dotNV, float k)
+{
+	return 1.0f/(dotNV*(1.0f-k)+k);
+}
+
+float3 directSpecular(float3 N, float3 V, float3 L, float roughness, float F0)
+{
+	float alpha = roughness*roughness;
+
+	//TODO don't need to calculate all this again timmy!!!!!!
+    float3 H = normalize(V + L);
+    float dotNL = clamp(dot(N,L), 0.0, 1.0);
+    float dotNV = clamp(dot(N,V), 0.0, 1.0);
+    float dotNH = clamp(dot(N,H), 0.0, 1.0);
+	float dotHV = clamp(dot(H,V), 0.0, 1.0);
+	float dotLH = clamp(dot(L,H), 0.0, 1.0);
+
+	float F, D, vis;
+
+	// D
+	float alphaSqr = alpha*alpha;
+	float pi = 3.14159f;
+	float denom = dotNH * dotNH *(alphaSqr-1.0) + 1.0f;
+	D = alphaSqr/(pi * denom * denom);
+
+	// F
+	float dotLH5 = pow(1.0f-dotLH,5);
+	F = F0 + (1.0-F0)*(dotLH5);
+
+	// V
+	float k = alpha/2.0f;
+	vis = G1V(dotNL,k)*G1V(dotNV,k);
+
+	float specular = dotNL * D * F * vis;
+	return float3(specular,specular,specular);
 }

+ 138 - 0
Templates/Full/game/shaders/common/lighting/advanced/brdfLookupP.hlsl

@@ -0,0 +1,138 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "../../torque.hlsl"
+
+struct ConnectData
+{
+    float4 hpos    : TORQUE_POSITION;
+    float2 uv      : TEXCOORD;
+};
+
+// ----------------------------------------------------------------------------
+// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
+// efficient VanDerCorpus calculation.
+float RadicalInverse_VdC(uint bits) 
+{
+     bits = (bits << 16u) | (bits >> 16u);
+     bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+     bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+     bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+     bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+     return float(bits) * 2.3283064365386963e-10; // / 0x100000000
+}
+// ----------------------------------------------------------------------------
+float2 Hammersley(uint i, uint N)
+{
+	return float2(float(i)/float(N), RadicalInverse_VdC(i));
+}
+// ----------------------------------------------------------------------------
+float3 ImportanceSampleGGX(float2 Xi, float3 N, float roughness)
+{
+	float a = roughness*roughness;
+	
+	float phi = 2.0 * M_PI_F * Xi.x;
+	float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
+	float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
+	
+	// from spherical coordinates to cartesian coordinates - halfway vector
+	float3 H;
+	H.x = cos(phi) * sinTheta;
+	H.y = sin(phi) * sinTheta;
+	H.z = cosTheta;
+	
+	// from tangent-space H vector to world-space sample vector
+	float3 up          = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
+	float3 tangent   = normalize(cross(up, N));
+	float3 bitangent = cross(N, tangent);
+	
+	float3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
+	return normalize(sampleVec);
+}
+// ----------------------------------------------------------------------------
+float GeometrySchlickGGX(float NdotV, float roughness)
+{
+    // note that we use a different k for IBL
+    float a = roughness;
+    float k = (a * a) / 2.0;
+
+    float nom   = NdotV;
+    float denom = NdotV * (1.0 - k) + k;
+
+    return nom / denom;
+}
+// ----------------------------------------------------------------------------
+float GeometrySmith(float3 N, float3 V, float3 L, float roughness)
+{
+    float NdotV = max(dot(N, V), 0.0);
+    float NdotL = max(dot(N, L), 0.0);
+    float ggx2 = GeometrySchlickGGX(NdotV, roughness);
+    float ggx1 = GeometrySchlickGGX(NdotL, roughness);
+
+    return ggx1 * ggx2;
+}
+// ----------------------------------------------------------------------------
+float2 IntegrateBRDF(float NdotV, float roughness)
+{
+    float3 V;
+    V.x = sqrt(1.0 - NdotV*NdotV);
+    V.y = 0.0;
+    V.z = NdotV;
+
+    float A = 0.0;
+    float B = 0.0; 
+
+    float3 N = float3(0.0, 0.0, 1.0);
+    
+    const uint SAMPLE_COUNT = 1024u;
+    for(uint i = 0u; i < SAMPLE_COUNT; ++i)
+    {
+        // generates a sample vector that's biased towards the
+        // preferred alignment direction (importance sampling).
+        float2 Xi = Hammersley(i, SAMPLE_COUNT);
+        float3 H = ImportanceSampleGGX(Xi, N, roughness);
+        float3 L = normalize(2.0 * dot(V, H) * H - V);
+
+        float NdotL = max(L.z, 0.0);
+        float NdotH = max(H.z, 0.0);
+        float VdotH = max(dot(V, H), 0.0);
+
+        if(NdotL > 0.0)
+        {
+            float G = GeometrySmith(N, V, L, roughness);
+            float G_Vis = (G * VdotH) / (NdotH * NdotV);
+            float Fc = pow(1.0 - VdotH, 5.0);
+
+            A += (1.0 - Fc) * G_Vis;
+            B += Fc * G_Vis;
+        }
+    }
+    A /= float(SAMPLE_COUNT);
+    B /= float(SAMPLE_COUNT);
+    return float2(A, B);
+}
+
+float4 main(ConnectData IN) : TORQUE_TARGET0
+{
+    return float4(IntegrateBRDF(IN.uv.x, IN.uv.y).rg,0,1);
+    //return float2(1,1);
+}

+ 37 - 0
Templates/Full/game/shaders/common/lighting/advanced/cubemapV.hlsl

@@ -0,0 +1,37 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "../../shaderModel.hlsl"
+
+struct ConnectData
+{
+    float4 hpos    : TORQUE_POSITION;
+    float2 uv      : TEXCOORD;
+};
+
+ConnectData main( uint vertexID : SV_VertexID )
+{
+    ConnectData result;
+    result.uv = float2((vertexID << 1) & 2, vertexID & 2);
+    result.hpos = float4(result.uv * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
+    return result;
+}

+ 3 - 3
Templates/Full/game/shaders/common/lighting/advanced/dbgLightMapVisualizeP.hlsl

@@ -23,11 +23,11 @@
 #include "shadergen:/autogenConditioners.h"
 #include "../../postfx/postFx.hlsl"
 
-TORQUE_UNIFORM_SAMPLER2D(indirectLightingBuffer,0);
+TORQUE_UNIFORM_SAMPLER2D(specularLightingBuffer,0);
 
 
 float4 main( PFXVertToPix IN ) : TORQUE_TARGET0
 {
-	float4 directlighting = float4(TORQUE_TEX2D( indirectLightingBuffer, IN.uv0 ).rgb,1.0);
-   return directlighting;
+	float4 diffuseLighting = float4(TORQUE_TEX2D( specularLightingBuffer, IN.uv0 ).rgb,1.0);
+   return diffuseLighting;
 }

+ 15 - 13
Templates/Full/game/shaders/common/lighting/advanced/deferredShadingP.hlsl

@@ -25,9 +25,9 @@
 #include "shaders/common/torque.hlsl"
 
 TORQUE_UNIFORM_SAMPLER2D(colorBufferTex,0);
-TORQUE_UNIFORM_SAMPLER2D(directLightingBuffer,1);
+TORQUE_UNIFORM_SAMPLER2D(diffuseLightingBuffer,1);
 TORQUE_UNIFORM_SAMPLER2D(matInfoTex,2);
-TORQUE_UNIFORM_SAMPLER2D(indirectLightingBuffer,3);
+TORQUE_UNIFORM_SAMPLER2D(specularLightingBuffer,3);
 TORQUE_UNIFORM_SAMPLER2D(deferredTex,4);
 
 float4 main( PFXVertToPix IN ) : TORQUE_TARGET0
@@ -37,25 +37,27 @@ float4 main( PFXVertToPix IN ) : TORQUE_TARGET0
    if (depth>0.9999)
       return float4(0,0,0,0);
 
-   float3 colorBuffer = TORQUE_TEX2D( colorBufferTex, IN.uv0 ).rgb; //albedo
+   float3 albedo = TORQUE_TEX2D( colorBufferTex, IN.uv0 ).rgb; //albedo
    float4 matInfo = TORQUE_TEX2D(matInfoTex, IN.uv0); //flags|smoothness|ao|metallic
 
    bool emissive = getFlag(matInfo.r, 0);
    if (emissive)
    {
-      return float4(colorBuffer, 1.0);
+      return float4(albedo, 1.0);
    }
 	  
-   float4 directLighting = TORQUE_TEX2D( directLightingBuffer, IN.uv0 ); //shadowmap*specular
-   float3 indirectLighting = TORQUE_TEX2D( indirectLightingBuffer, IN.uv0 ).rgb; //environment mapping*lightmaps
+   float4 diffuse = TORQUE_TEX2D( diffuseLightingBuffer, IN.uv0 ); //shadowmap*specular
+   float4 specular = TORQUE_TEX2D( specularLightingBuffer, IN.uv0 ); //environment mapping*lightmaps
+   
    float metalness = matInfo.a;
-	  
-   float frez = directLighting.a;
    
-   float3 diffuseColor = colorBuffer - (colorBuffer * metalness);
-   float3 reflectColor = indirectLighting*colorBuffer;
-   colorBuffer = diffuseColor+lerp(reflectColor,indirectLighting,frez);
-   colorBuffer *= max(directLighting.rgb,float3(0,0,0));
+   float3 diffuseColor = albedo - (albedo * metalness);
+   float3 specularColor = lerp(float3(0.04,0.04,0.04), albedo, metalness);
+
+   float3 light = (diffuseColor * diffuse.rgb) + (specularColor * specular.rgb);
+
+   //albedo = diffuseColor+lerp(reflectColor,indiffuseLighting,frez);
+   //albedo *= max(diffuseLighting.rgb,float3(0,0,0));
    
-   return hdrEncode( float4(colorBuffer.rgb, 1.0) );
+   return float4(light.rgb, 1.0);
 }

+ 31 - 0
Templates/Full/game/shaders/common/lighting/advanced/gl/cubemapV.glsl

@@ -0,0 +1,31 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "../../../gl/hlslCompat.glsl"
+out vec2 uv;
+
+void main()
+{
+    uv = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2);
+    gl_Position = vec4(uv * vec2(2.0f, -2.0f) + vec2(-1.0f, 1.0f), 0.0f, 1.0f);
+    correctSSP(gl_Position);
+}

+ 3 - 3
Templates/Full/game/shaders/common/lighting/advanced/gl/dbgLightMapVisualizeP.glsl

@@ -24,12 +24,12 @@
 #include "shadergen:/autogenConditioners.h"
 
 in vec2 uv0;
-uniform sampler2D directLightingBuffer;
+uniform sampler2D diffuseLightingBuffer;
 
 out vec4 OUT_col;
 
 void main()
 {
-   vec4 directLighting = vec4(texture( directLightingBuffer, uv0 ).rgb,1.0);
-   OUT_col = directLighting;
+   vec4 diffuseLighting = vec4(texture( diffuseLightingBuffer, uv0 ).rgb,1.0);
+   OUT_col = diffuseLighting;
 }

+ 6 - 2
Templates/Full/game/shaders/common/lighting/advanced/gl/deferredClearGBufferP.glsl

@@ -23,6 +23,7 @@
 out vec4 OUT_col;
 out vec4 OUT_col1;
 out vec4 OUT_col2;
+out vec4 OUT_col3;
 
 //-----------------------------------------------------------------------------
 // Main                                                                        
@@ -33,8 +34,11 @@ void main()
    OUT_col =  vec4(1.0, 1.0, 1.0, 1.0);
 
    // Clear Color Buffer.
-   OUT_col1 = vec4(0.0, 0.0, 0.0, 1.0);
+   OUT_col1 = vec4(0.0, 0.0, 0.0, 0.0001);
 
    // Clear Material Info Buffer.
-   OUT_col2 = vec4(0.0, 0.0, 0.0, 1.0);
+   OUT_col2 = vec4(0.0, 0.0, 0.0, 0.0);
+   
+   // Clear Light Info Buffer.
+   OUT_col3 = vec4(0.0, 0.0, 0.0, 0.0);
 }

+ 23 - 15
Templates/Full/game/shaders/common/lighting/advanced/gl/deferredShadingP.glsl

@@ -26,8 +26,9 @@
 #include "../../../gl/torque.glsl"
 
 uniform sampler2D colorBufferTex;
-uniform sampler2D lightDeferredTex;
+uniform sampler2D diffuseLightingBuffer;
 uniform sampler2D matInfoTex;
+uniform sampler2D specularLightingBuffer;
 uniform sampler2D deferredTex;
 
 out vec4 OUT_col;
@@ -40,20 +41,27 @@ void main()
       OUT_col = vec4(0.0);
       return;
    }
-   vec4 lightBuffer = texture( lightDeferredTex, uv0 );
-   vec4 colorBuffer = texture( colorBufferTex, uv0 );
-   vec4 matInfo = texture( matInfoTex, uv0 );
-   float specular = clamp(lightBuffer.a,0.0,1.0);
-
-   // Diffuse Color Altered by Metalness
-   bool metalness = getFlag(matInfo.r, 3);
-   if ( metalness )
+   
+   vec3 colorBuffer = texture( colorBufferTex, uv0 ).rgb; //albedo
+   vec4 matInfo = texture( matInfoTex, uv0 ); //flags|smoothness|ao|metallic
+   bool emissive = getFlag(matInfo.r, 0);
+   if (emissive)
    {
-      colorBuffer *= (1.0 - colorBuffer.a);
+      OUT_col = float4(colorBuffer, 1.0);
+	  return;
    }
-
-   colorBuffer += vec4(specular, specular, specular, 1.0);
-   colorBuffer *= vec4(lightBuffer.rgb, 1.0);
-
-   OUT_col = hdrEncode( vec4(colorBuffer.rgb, 1.0) );
+   
+   vec4 diffuseLighting = texture( diffuseLightingBuffer, uv0 ); //shadowmap*specular
+   
+   vec3 specularLighting = texture( specularLightingBuffer, uv0 ).rgb; //environment mapping*lightmaps
+   float metalness = matInfo.a;
+      
+   float frez = diffuseLighting.a;
+   
+   vec3 diffuseColor = colorBuffer - (colorBuffer * metalness);
+   vec3 reflectColor = specularLighting*colorBuffer;
+   colorBuffer = diffuseColor+lerp(reflectColor,specularLighting,frez);
+   colorBuffer *= max(diffuseLighting.rgb,vec3(0,0,0)); 
+   
+   OUT_col =  hdrEncode(vec4(colorBuffer,1.0));
 }

+ 60 - 0
Templates/Full/game/shaders/common/lighting/advanced/gl/irradianceP.glsl

@@ -0,0 +1,60 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "../../../gl/torque.glsl"
+
+in vec2 uv;
+uniform int face;
+
+uniform samplerCube environmentMap;
+
+out vec4 outColor;
+
+void main()
+{
+    vec3 N = getCubeDir(face, uv);
+    vec3 irradiance = vec3(0.0);
+    
+    // tangent space calculation from origin point
+    vec3 up    = vec3(0.0, 0.0, 1.0);
+    vec3 right = cross(up, N);
+    up         = cross(N, right);
+       
+    float sampleDelta = 0.025;
+    int nrSamples = 0;
+    for(float phi = 0.0; phi < M_2PI_F; phi += sampleDelta)
+    {
+        for(float theta = 0.0; theta < M_HALFPI_F; theta += sampleDelta)
+        {
+            // spherical to cartesian (in tangent space)
+            vec3 tangentSample = vec3(sin(theta) * cos(phi),  sin(theta) * sin(phi), cos(theta));
+            // tangent space to world
+            vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; 
+
+            irradiance += texture(environmentMap, sampleVec).rgb * cos(theta) * sin(theta);
+            nrSamples++;
+        }
+    }
+    irradiance = M_PI_F * irradiance * (1.0 / float(nrSamples));
+    
+    outColor =  vec4(irradiance, 1.0);
+}

+ 12 - 6
Templates/Full/game/shaders/common/lighting/advanced/gl/pointLightP.glsl

@@ -138,8 +138,9 @@ void main()
    vec3 ssPos = ssPos.xyz / ssPos.w;
    vec2 uvScene = getUVFromSSPos( ssPos, rtParams0 );
    
-   // Emissive.
+   // Matinfo flags
    vec4 matInfo = texture( matInfoBuffer, uvScene );   
+   //early out if emissive
    bool emissive = getFlag( matInfo.r, 0 );
    if ( emissive )
    {
@@ -245,12 +246,17 @@ void main()
    // cause the hardware occlusion query to disable the shadow.
 
    // Specular term
-   float specular = AL_CalcSpecular(   lightVec, 
-                                       normal, 
-                                       normalize( -eyeRay ) ) * lightBrightness * atten * shadowed;
+   float specular = 0;
+   vec4 real_specular = EvalBDRF( colorSample.rgb,
+                                    lightcol,
+                                    lightVec,
+                                    viewSpacePos,
+                                    normal,
+                                    1.05-matInfo.b*0.9, //slightly compress roughness to allow for non-baked lighting
+                                    matInfo.a );
+   vec3 lightColorOut = real_specular.rgb * lightBrightness * shadowed* atten;
 
    float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness;
-   vec3 lightColorOut = lightMapParams.rgb * lightcol;
    vec4 addToResult = vec4(0.0);
     
    // TODO: This needs to be removed when lightmapping is disabled
@@ -269,5 +275,5 @@ void main()
       addToResult = ( 1.0 - shadowed ) * abs(lightMapParams);
    }
 
-   OUT_col = AL_DeferredOutput(lightColorOut+subsurface*(1.0-Sat_NL_Att), colorSample.rgb, matInfo, addToResult, specular, Sat_NL_Att);
+   OUT_col = vec4((lightColorOut*Sat_NL_Att+subsurface*(1.0-Sat_NL_Att)+addToResult.rgb),real_specular.a);
 }

+ 59 - 0
Templates/Full/game/shaders/common/lighting/advanced/gl/probeShadingP.glsl

@@ -0,0 +1,59 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "../../../gl/hlslCompat.glsl"
+#include "shadergen:/autogenConditioners.h"
+#include "../../../postFx/gl/postFX.glsl"
+#include "../../../gl/torque.glsl"
+
+uniform sampler2D colorBufferTex;
+uniform sampler2D diffuseLightingBuffer;
+uniform sampler2D matInfoTex;
+uniform sampler2D specularLightingBuffer;
+uniform sampler2D deferredTex;
+
+out vec4 OUT_col;
+
+void main()
+{
+   float depth = deferredUncondition( deferredTex, uv0 ).w;
+   if (depth>0.9999)
+   {
+      OUT_col = vec4(0.0);
+      return;
+   }
+   
+   vec3 colorBuffer = texture( colorBufferTex, uv0 ).rgb; //albedo
+   vec4 matInfo = texture( matInfoTex, uv0 ); //flags|smoothness|ao|metallic
+   bool emissive = getFlag(matInfo.r, 0);
+   if (emissive)
+   {
+      OUT_col = float4(colorBuffer, 1.0);
+	  return;
+   }
+   
+   vec4 diffuseLighting = texture( diffuseLightingBuffer, uv0 ); //shadowmap*specular
+   
+   colorBuffer *= max(diffuseLighting.rgb,vec3(0,0,0)); 
+   
+   OUT_col =  hdrEncode(vec4(colorBuffer,1.0));
+}

+ 229 - 0
Templates/Full/game/shaders/common/lighting/advanced/gl/reflectionProbeP.glsl

@@ -0,0 +1,229 @@
+#include "../../../gl/hlslCompat.glsl"
+#include "shadergen:/autogenConditioners.h"
+
+#include "farFrustumQuad.glsl"
+#include "lightingUtils.glsl"
+#include "../../../gl/lighting.glsl"
+#include "../../../gl/torque.glsl"
+#line 8
+
+in vec4 pos;
+in vec4 wsEyeDir;
+in vec4 ssPos;
+in vec4 vsEyeDir;
+
+uniform sampler2D deferredBuffer;
+uniform sampler2D matInfoBuffer;
+uniform samplerCube cubeMap;
+
+uniform vec4 rtParams0;
+
+uniform vec3 probeWSPos;
+uniform vec3 probeLSPos;
+uniform vec4 vsFarPlane;
+
+uniform float  lightRange;
+uniform vec2 lightAttenuation;
+
+uniform mat4 invViewMat;
+
+uniform vec3 eyePosWorld;
+uniform vec3 bbMin;
+uniform vec3 bbMax;
+
+uniform float Intensity;
+
+//SHTerms
+uniform vec4 SHTerms0;
+uniform vec4 SHTerms1;
+uniform vec4 SHTerms2;
+uniform vec4 SHTerms3;
+uniform vec4 SHTerms4;
+uniform vec4 SHTerms5;
+uniform vec4 SHTerms6;
+uniform vec4 SHTerms7;
+uniform vec4 SHTerms8;
+
+uniform float SHConsts0;
+uniform float SHConsts1;
+uniform float SHConsts2;
+uniform float SHConsts3;
+uniform float SHConsts4;
+
+uniform float useSphereMode;
+
+vec4 decodeSH(vec3 normal)
+{
+   float x = normal.x;
+   float y = normal.y;
+   float z = normal.z;
+
+   vec3 l00 = SHTerms0.rgb;
+
+   vec3 l10 = SHTerms1.rgb;
+   vec3 l11 = SHTerms2.rgb;
+   vec3 l12 = SHTerms3.rgb;
+
+   vec3 l20 = SHTerms4.rgb;
+   vec3 l21 = SHTerms5.rgb;
+   vec3 l22 = SHTerms6.rgb;
+   vec3 l23 = SHTerms7.rgb;
+   vec3 l24 = SHTerms8.rgb;
+
+   vec3 result = (
+         l00 * SHConsts0 +
+
+         l12 * SHConsts1 * x +
+         l10 * SHConsts1 * y +
+         l11 * SHConsts1 * z +
+
+         l20 * SHConsts2 * x*y +
+         l21 * SHConsts2 * y*z +
+         l22 * SHConsts3 * (3.0*z*z - 1.0) +
+         l23 * SHConsts2 * x*z +
+         l24 * SHConsts4 * (x*x - y*y)
+      );
+
+    return vec4(result,1);
+}
+
+out vec4 OUT_col;
+void main()
+{ 
+    // Compute scene UV
+    vec3 ssPos = ssPos.xyz / ssPos.w; 
+
+    //vec4 hardCodedRTParams0 = vec4(0,0.0277777780,1,0.972222209);
+    vec2 uvScene = getUVFromSSPos( ssPos, rtParams0 );
+
+    // Matinfo flags
+    vec4 matInfo = texture( matInfoBuffer, uvScene ); 
+
+    // Sample/unpack the normal/z data
+    vec4 deferredSample = deferredUncondition( deferredBuffer, uvScene );
+    vec3 normal = deferredSample.rgb;
+    float depth = deferredSample.a;
+    if (depth>0.9999)
+        OUT_col = vec4(0,0,0,0); 
+
+    // Need world-space normal.
+    vec3 wsNormal = tMul(vec4(normal, 1), invViewMat).rgb;
+
+    vec4 color = vec4(1, 1, 1, 1);
+    vec4 ref = vec4(0,0,0,0);
+    float alpha = 0;
+
+    vec3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, vsEyeDir.xyz, vsFarPlane );
+    vec3 viewSpacePos = eyeRay * depth;
+
+    vec3 wsEyeRay = tMul(vec4(eyeRay, 1), invViewMat).rgb;
+
+    // Use eye ray to get ws pos
+    vec3 worldPos = vec3(eyePosWorld + wsEyeRay * depth);
+    float smoothness = min((1.0 - matInfo.b)*11.0 + 1.0, 8.0);//bump up to 8 for finalization
+
+    if(useSphereMode>0.0)
+    {
+        // Eye ray - Eye -> Pixel
+        
+            
+        // Build light vec, get length, clip pixel if needed
+        vec3 lightVec = probeLSPos - viewSpacePos;
+        float lenLightV = length( lightVec );
+        clip( lightRange - lenLightV );
+
+        // Get the attenuated falloff.
+        float atten = attenuate( vec4(1,1,1,1), lightAttenuation, lenLightV );
+        clip( atten - 1e-6 );
+
+        // Normalize lightVec
+        lightVec /= lenLightV;
+
+        // If we can do dynamic branching then avoid wasting
+        // fillrate on pixels that are backfacing to the light.
+        float nDotL = abs(dot( lightVec, normal ));
+
+        float Sat_NL_Att = saturate( nDotL * atten );
+
+        vec3 reflectionVec = reflect(wsEyeDir, vec4(wsNormal,nDotL)).xyz;
+
+        vec3 nrdir = normalize(reflectionVec);
+        vec3 rbmax = (bbMax - worldPos.xyz) / nrdir;
+        vec3 rbmin = (bbMin - worldPos.xyz) / nrdir;
+
+       vec3 rbminmax = rbmin;
+	   if (nrdir.x > 0.0)
+          rbminmax.x = rbmax.x;
+	   if (nrdir.y > 0.0)
+          rbminmax.y = rbmax.y;
+	   if (nrdir.z > 0.0)
+          rbminmax.z = rbmax.z;
+		  
+        float fa = min(min(rbminmax.x,rbminmax.y),rbminmax.z);
+		  if (dot( lightVec, normal )<0.0f)
+           clip(fa);
+
+        vec3 posOnBox = worldPos.xyz + nrdir * fa;
+        reflectionVec = posOnBox - probeWSPos;
+
+        //reflectionVec = tMul(probeWSPos,reflectionVec);
+
+        ref = vec4(reflectionVec, smoothness);
+
+        alpha = Sat_NL_Att;
+    }
+    else
+    {
+       // Build light vec, get length, clip pixel if needed
+       vec3 lightVec = probeLSPos - viewSpacePos;
+       float lenLightV = length(lightVec);
+       //clip(lightRange - lenLightV);
+
+       // Normalize lightVec
+       lightVec /= lenLightV;
+
+       // If we can do dynamic branching then avoid wasting
+       // fillrate on pixels that are backfacing to the light.
+       float nDotL = abs(dot(lightVec, normal));
+
+       vec3 reflectionVec = reflect(wsEyeDir, vec4(wsNormal, nDotL)).xyz;
+
+       vec3 nrdir = normalize(reflectionVec);
+       vec3 rbmax = (bbMax - worldPos.xyz) / nrdir;
+       vec3 rbmin = (bbMin - worldPos.xyz) / nrdir;
+
+       vec3 rbminmax = rbmin;
+	   if (nrdir.x > 0.0)
+          rbminmax.x = rbmax.x;
+	   if (nrdir.y > 0.0)
+          rbminmax.y = rbmax.y;
+	   if (nrdir.z > 0.0)
+          rbminmax.z = rbmax.z;
+		  
+       float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
+       if (dot(lightVec, normal)<0.0f)
+          clip(fa);
+
+       //Try to clip anything that falls outside our box as well
+       //TODO: Make it support rotated boxes as well
+       if(worldPos.x > bbMax.x || worldPos.y > bbMax.y || worldPos.z > bbMax.z ||
+          worldPos.x < bbMin.x || worldPos.y < bbMin.y || worldPos.z < bbMin.z)
+          clip(-1);
+
+       vec3 posOnBox = worldPos.xyz + nrdir * fa;
+       reflectionVec = posOnBox - probeWSPos;
+
+       ref = vec4(reflectionVec, smoothness);
+
+        alpha = 1;
+    }
+
+    color = textureLod(cubeMap, vec3(ref.xyz), float(ref.w));
+
+    vec4 specularColor = (color);
+    vec4 indirectColor = (decodeSH(wsNormal));
+
+    color.rgb = lerp(indirectColor.rgb * 1.5, specularColor.rgb * 1.5, matInfo.b);
+
+    OUT_col = vec4(color.rgb, alpha);
+}

+ 32 - 0
Templates/Full/game/shaders/common/lighting/advanced/gl/reflectionProbeV.glsl

@@ -0,0 +1,32 @@
+#include "shadergen:/autogenConditioners.h"
+#include "../../torque.hlsl"
+
+// This is the shader input
+struct Vert
+{
+   float4 position : POSITION;
+   float2 uv0      : TEXCOORD0;
+   float3 wsEyeRay : TEXCOORD1;
+};
+
+// This is the shader output data.
+struct Conn
+{
+   float4 position : POSITION;
+   float2 uv0      : TEXCOORD0;
+   float3 wsEyeRay : TEXCOORD1;
+};
+
+// Render Target Paramaters
+float4 rtParams0;
+
+Conn main(Vert IN,
+          uniform float4x4 modelView : register(C0))	         
+{
+   Conn OUT;   
+   OUT.position = IN.position;
+   OUT.uv0 = viewportCoordToRenderTarget( IN.uv0, rtParams0 );
+   OUT.wsEyeRay = IN.wsEyeRay;
+   return OUT;
+}
+

+ 16 - 8
Templates/Full/game/shaders/common/lighting/advanced/gl/spotLightP.glsl

@@ -80,12 +80,13 @@ void main()
    vec3 ssPos = IN_ssPos.xyz / IN_ssPos.w;
    vec2 uvScene = getUVFromSSPos( ssPos, rtParams0 );
 
-   // Emissive.
+   // Matinfo flags
    vec4 matInfo = texture( matInfoBuffer, uvScene );   
+   //early out if emissive
    bool emissive = getFlag( matInfo.r, 0 );
    if ( emissive )
    {
-       OUT_col = vec4(0.0, 0.0, 0.0, 0.0);
+      OUT_col = vec4(0.0, 0.0, 0.0, 0.0);
 	   return;
    }
    
@@ -182,12 +183,19 @@ void main()
    // cause the hardware occlusion query to disable the shadow.
 
    // Specular term
-   float specular = AL_CalcSpecular(   -lightToPxlVec, 
-                                       normal, 
-                                       normalize( -eyeRay ) ) * lightBrightness * atten * shadowed;
-
+   float specular = 0;
+
+   vec3 lightVec = lightPosition - viewSpacePos;
+   vec4 real_specular = EvalBDRF( colorSample.rgb,
+                                    lightcol,
+                                    lightVec,
+                                    viewSpacePos,
+                                    normal,
+                                    1.05-matInfo.b*0.9, //slightly compress roughness to allow for non-baked lighting
+                                    matInfo.a );
+   vec3 lightColorOut = real_specular.rgb * lightBrightness * shadowed* atten;
+   
    float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness;
-   vec3 lightColorOut = lightMapParams.rgb * lightcol;
    vec4 addToResult = vec4(0.0);
 
    // TODO: This needs to be removed when lightmapping is disabled
@@ -206,5 +214,5 @@ void main()
       addToResult = ( 1.0 - shadowed ) * abs(lightMapParams);
    }
 
-   OUT_col = AL_DeferredOutput(lightColorOut+subsurface*(1.0-Sat_NL_Att), colorSample.rgb, matInfo, addToResult, specular, Sat_NL_Att);
+   OUT_col = vec4((lightColorOut*Sat_NL_Att+subsurface*(1.0-Sat_NL_Att)+addToResult.rgb),real_specular.a);
 }

+ 16 - 24
Templates/Full/game/shaders/common/lighting/advanced/gl/vectorLightP.glsl

@@ -193,12 +193,13 @@ vec4 AL_VectorLightShadowCast( sampler2D _sourceshadowMap,
 out vec4 OUT_col;
 void main()             
 {
-   // Emissive.
+   // Matinfo flags
    float4 matInfo = texture( matInfoBuffer, uv0 );   
+   //early out if emissive
    bool emissive = getFlag( matInfo.r, 0 );
    if ( emissive )
    {
-       OUT_col = vec4(1.0, 1.0, 1.0, 0.0);
+      OUT_col = vec4(0.0, 0.0, 0.0, 0.0);
        return;
    }
    
@@ -289,29 +290,20 @@ void main()
 
    #endif // !NO_SHADOW
 
-   // Specular term
-   float specular = AL_CalcSpecular(   -lightDirection, 
-                                       normal, 
-                                       normalize(-vsEyeRay) ) * lightBrightness * shadowed;
+   // Specular term   
+   vec3 viewSpacePos = vsEyeRay * depth;
+   vec4 real_specular = EvalBDRF( colorSample.rgb,
+                                    lightColor.rgb,
+                                    normalize( -lightDirection ),
+                                    viewSpacePos,
+                                    normal,
+                                    1.0-matInfo.b,
+                                    matInfo.a );
+   vec3 lightColorOut = real_specular.rgb * lightBrightness * shadowed;
    
    float Sat_NL_Att = saturate( dotNL * shadowed ) * lightBrightness;
-   vec3 lightColorOut = lightMapParams.rgb * lightColor.rgb;
-   vec4 addToResult = (lightAmbient * (1 - ambientCameraFactor)) + ( lightAmbient * ambientCameraFactor * saturate(dot(normalize(-vsEyeRay), normal)) );
-
-   // TODO: This needs to be removed when lightmapping is disabled
-   // as its extra work per-pixel on dynamic lit scenes.
-   //
-   // Special lightmapping pass.
-   if ( lightMapParams.a < 0.0 )
-   {
-      // This disables shadows on the backsides of objects.
-      shadowed = dotNL < 0.0f ? 1.0f : shadowed;
-
-      Sat_NL_Att = 1.0f;
-      lightColorOut = vec3(shadowed);
-      specular *= lightBrightness;
-      addToResult = ( 1.0 - shadowed ) * abs(lightMapParams);
-   }
+   float Sat_NdotV = saturate(dot(normalize(-vsEyeRay), normal));   
+   vec4 addToResult = ( lightAmbient * (1 - ambientCameraFactor)) + ( lightAmbient * ambientCameraFactor * Sat_NdotV );
 
    // Sample the AO texture.      
    #ifdef USE_SSAO_MASK
@@ -323,5 +315,5 @@ void main()
       lightColorOut = debugColor;
    #endif
 
-   OUT_col = AL_DeferredOutput(lightColorOut+subsurface*(1.0-Sat_NL_Att), colorSample.rgb, matInfo, addToResult, specular, Sat_NL_Att); 
+   OUT_col = vec4(matInfo.g*(lightColorOut*Sat_NL_Att+subsurface*(1.0-Sat_NL_Att)+addToResult.rgb),real_specular.a);
 }

+ 63 - 0
Templates/Full/game/shaders/common/lighting/advanced/irradianceP.hlsl

@@ -0,0 +1,63 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "../../torque.hlsl"
+
+struct ConnectData
+{
+    float4 hpos    : TORQUE_POSITION;
+    float2 uv      : TEXCOORD;
+};
+
+uniform int face;
+
+TORQUE_UNIFORM_SAMPLERCUBE(environmentMap, 0);
+
+float4 main(ConnectData IN) : TORQUE_TARGET0
+{
+    float3 N = getCubeDir(face,IN.uv);
+    float3 irradiance = 0;
+    
+    // tangent space calculation from origin point
+    float3 up    = float3(0.0, 0.0, 1.0);
+    float3 right = cross(up, N);
+    up           = cross(N, right);
+       
+    float sampleDelta = 0.025;
+    int nrSamples = 0;
+    for(float phi = 0.0; phi < M_2PI_F; phi += sampleDelta)
+    {
+        for(float theta = 0.0; theta < M_HALFPI_F; theta += sampleDelta)
+        {
+            // spherical to cartesian (in tangent space)
+            float3 tangentSample = float3(sin(theta) * cos(phi),  sin(theta) * sin(phi), cos(theta));
+            // tangent space to world
+            float3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; 
+
+            irradiance += TORQUE_TEXCUBE(environmentMap, sampleVec).rgb * cos(theta) * sin(theta);
+            nrSamples++;
+        }
+    }
+    irradiance = M_PI_F * irradiance * (1.0 / float(nrSamples));
+    
+    return float4(irradiance, 1.0);
+}

+ 103 - 97
Templates/Full/game/shaders/common/lighting/advanced/pointLightP.hlsl

@@ -136,48 +136,58 @@ uniform float2 lightAttenuation;
 uniform float3x3 viewToLightProj;
 uniform float3x3 dynamicViewToLightProj;
 
-float4 main( ConvexConnectP IN ) : TORQUE_TARGET0
-{   
+struct PS_OUTPUT
+{
+   float4 diffuse: TORQUE_TARGET0;
+   float4 spec: TORQUE_TARGET1;
+};
+
+PS_OUTPUT main(ConvexConnectP IN)
+{
+   PS_OUTPUT Output = (PS_OUTPUT)0;
+
    // Compute scene UV
    float3 ssPos = IN.ssPos.xyz / IN.ssPos.w;
-   float2 uvScene = getUVFromSSPos( ssPos, rtParams0 );
-   
+   float2 uvScene = getUVFromSSPos(ssPos, rtParams0);
+
    // Matinfo flags
-   float4 matInfo = TORQUE_TEX2D( matInfoBuffer, uvScene ); 
+   float4 matInfo = TORQUE_TEX2D(matInfoBuffer, uvScene);
    //early out if emissive
    bool emissive = getFlag(matInfo.r, 0);
    if (emissive)
    {
-       return float4(0.0, 0.0, 0.0, 0.0);
+      //return float4(0.0, 0.0, 0.0, 0.0);
+      return Output;
    }
-   float4 colorSample = TORQUE_TEX2D( colorBuffer, uvScene );
-   float3 subsurface = float3(0.0,0.0,0.0); 
-   if (getFlag( matInfo.r, 1 ))
+
+   float4 colorSample = TORQUE_TEX2D(colorBuffer, uvScene);
+   float3 subsurface = float3(0.0, 0.0, 0.0);
+   if (getFlag(matInfo.r, 1))
    {
       subsurface = colorSample.rgb;
-      if (colorSample.r>colorSample.g)
+      if (colorSample.r > colorSample.g)
          subsurface = float3(0.772549, 0.337255, 0.262745);
 	  else
          subsurface = float3(0.337255, 0.772549, 0.262745);
 	}
    
    // Sample/unpack the normal/z data
-   float4 deferredSample = TORQUE_DEFERRED_UNCONDITION( deferredBuffer, uvScene );
+   float4 deferredSample = TORQUE_DEFERRED_UNCONDITION(deferredBuffer, uvScene);
    float3 normal = deferredSample.rgb;
    float depth = deferredSample.a;
    
    // Eye ray - Eye -> Pixel
-   float3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, IN.vsEyeDir.xyz, vsFarPlane );
+   float3 eyeRay = getDistanceVectorToPlane(-vsFarPlane.w, IN.vsEyeDir.xyz, vsFarPlane);
    float3 viewSpacePos = eyeRay * depth;
       
    // Build light vec, get length, clip pixel if needed
    float3 lightVec = lightPosition - viewSpacePos;
-   float lenLightV = length( lightVec );
-   clip( lightRange - lenLightV );
+   float lenLightV = length(lightVec);
+   clip(lightRange - lenLightV);
 
    // Get the attenuated falloff.
-   float atten = attenuate( lightColor, lightAttenuation, lenLightV );
-   clip( atten - 1e-6 );
+   float atten = attenuate(lightColor, lightAttenuation, lenLightV);
+   clip(atten - 1e-6);
 
    // Normalize lightVec
    lightVec /= lenLightV;
@@ -187,97 +197,93 @@ float4 main( ConvexConnectP IN ) : TORQUE_TARGET0
    float nDotL = dot( lightVec, normal );
    //DB_CLIP( nDotL < 0 );
 
-   #ifdef NO_SHADOW
-   
-      float shadowed = 1.0;
-      	
-   #else
-
-      // Get a linear depth from the light source.
-      float distToLight = lenLightV / lightRange;      
-
-      #ifdef SHADOW_CUBE
-              
-         // TODO: We need to fix shadow cube to handle soft shadows!
-         float occ = TORQUE_TEXCUBE( shadowMap, mul( viewToLightProj, -lightVec ) ).r;
-         float shadowed = saturate( exp( lightParams.y * ( occ - distToLight ) ) );
-         
-      #else
-
-         // Static
-         float2 shadowCoord = decodeShadowCoord( mul( viewToLightProj, -lightVec ) ).xy;
-         float static_shadowed = softShadow_filter( TORQUE_SAMPLER2D_MAKEARG(shadowMap),
-                                             ssPos.xy,
-                                             shadowCoord,
-                                             shadowSoftness,
-                                             distToLight,
-                                             nDotL,
-                                             lightParams.y );
-
-         // Dynamic
-         float2 dynamicShadowCoord = decodeShadowCoord( mul( dynamicViewToLightProj, -lightVec ) ).xy;
-         float dynamic_shadowed = softShadow_filter( TORQUE_SAMPLER2D_MAKEARG(dynamicShadowMap),
-                                             ssPos.xy,
-                                             dynamicShadowCoord,
-                                             shadowSoftness,
-                                             distToLight,
-                                             nDotL,
-                                             lightParams.y );
-
-         float shadowed = min(static_shadowed, dynamic_shadowed);
+#ifdef NO_SHADOW
 
-      #endif
+   float shadowed = 1.0;
+
+#else
+
+   // Get a linear depth from the light source.
+   float distToLight = lenLightV / lightRange;
+
+#ifdef SHADOW_CUBE
+
+   // TODO: We need to fix shadow cube to handle soft shadows!
+   float occ = TORQUE_TEXCUBE(shadowMap, mul(viewToLightProj, -lightVec)).r;
+   float shadowed = saturate(exp(lightParams.y * (occ - distToLight)));
+
+#else
+
+   // Static
+   float2 shadowCoord = decodeShadowCoord(mul(viewToLightProj, -lightVec)).xy;
+   float static_shadowed = softShadow_filter(TORQUE_SAMPLER2D_MAKEARG(shadowMap),
+      ssPos.xy,
+      shadowCoord,
+      shadowSoftness,
+      distToLight,
+      nDotL,
+      lightParams.y);
+
+   // Dynamic
+   float2 dynamicShadowCoord = decodeShadowCoord(mul(dynamicViewToLightProj, -lightVec)).xy;
+   float dynamic_shadowed = softShadow_filter(TORQUE_SAMPLER2D_MAKEARG(dynamicShadowMap),
+      ssPos.xy,
+      dynamicShadowCoord,
+      shadowSoftness,
+      distToLight,
+      nDotL,
+      lightParams.y);
+
+   float shadowed = min(static_shadowed, dynamic_shadowed);
+
+#endif
+
+#endif // !NO_SHADOW
 
-   #endif // !NO_SHADOW
-   
    float3 lightcol = lightColor.rgb;
-   #ifdef USE_COOKIE_TEX
+#ifdef USE_COOKIE_TEX
 
-      // Lookup the cookie sample.
-      float4 cookie = TORQUE_TEXCUBE( cookieMap, mul( viewToLightProj, -lightVec ) );
+   // Lookup the cookie sample.
+   float4 cookie = TORQUE_TEXCUBE(cookieMap, mul(viewToLightProj, -lightVec));
 
-      // Multiply the light with the cookie tex.
-      lightcol *= cookie.rgb;
+   // Multiply the light with the cookie tex.
+   lightcol *= cookie.rgb;
 
-      // Use a maximum channel luminance to attenuate 
-      // the lighting else we get specular in the dark
-      // regions of the cookie texture.
-      atten *= max( cookie.r, max( cookie.g, cookie.b ) );
+   // Use a maximum channel luminance to attenuate 
+   // the lighting else we get specular in the dark
+   // regions of the cookie texture.
+   atten *= max(cookie.r, max(cookie.g, cookie.b));
 
-   #endif
+#endif
 
    // NOTE: Do not clip on fully shadowed pixels as it would
    // cause the hardware occlusion query to disable the shadow.
 
-   // Specular term
-   float specular = 0;
+   float3 l = lightVec;// normalize(-lightDirection);
+   float3 v = eyeRay;// normalize(eyePosWorld - worldPos.xyz);
+
+   float3 h = normalize(v + l);
+   float dotNLa = clamp(dot(normal, l), 0.0, 1.0);
+   float dotNVa = clamp(dot(normal, v), 0.0, 1.0);
+   float dotNHa = clamp(dot(normal, h), 0.0, 1.0);
+   float dotHVa = clamp(dot(normal, v), 0.0, 1.0);
+   float dotLHa = clamp(dot(l, h), 0.0, 1.0);
+
+   float roughness = matInfo.g;
+   float metalness = matInfo.b;
+
+   //diffuse
+   float disDiff = Fr_DisneyDiffuse(dotNVa, dotNLa, dotLHa, roughness);
+   float3 diffuse = float3(disDiff, disDiff, disDiff) / M_PI_F;// alternative: (lightColor * dotNL) / Pi;
+                                                               //specular
+   float3 specular = directSpecular(normal, v, l, roughness, 1.0) * lightColor.rgb;
+
    
-   float4 real_specular = EvalBDRF( float3( 1.0, 1.0, 1.0 ),
-                                    lightcol,
-                                    lightVec,
-                                    viewSpacePos,
-                                    normal,
-                                    1.0-matInfo.b,
-                                    matInfo.a );
-   float3 lightColorOut = real_specular.rgb * lightBrightness * shadowed* atten;
-   //lightColorOut /= colorSample.rgb;
-   float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness;
-   float4 addToResult = 0.0;
-    
-   // TODO: This needs to be removed when lightmapping is disabled
-   // as its extra work per-pixel on dynamic lit scenes.
-   //
-   // Special lightmapping pass.
-   if ( lightMapParams.a < 0.0 )
-   {
-      // This disables shadows on the backsides of objects.
-      shadowed = nDotL < 0.0f ? 1.0f : shadowed;
-
-      Sat_NL_Att = 1.0f;
-      shadowed = lerp( 1.0f, shadowed, atten );
-      lightColorOut = shadowed;
-      specular *= lightBrightness;
-      addToResult = ( 1.0 - shadowed ) * abs(lightMapParams);
-   }     
-   return float4((lightColorOut*Sat_NL_Att+subsurface*(1.0-Sat_NL_Att)+addToResult.rgb),real_specular.a);
+   if (nDotL<0) shadowed = 0;
+   float Sat_NL_Att = saturate( nDotL * shadowed ) * lightBrightness;
+   //output
+   Output.diffuse = float4(diffuse * lightBrightness*shadowed, Sat_NL_Att);
+   Output.spec = float4(specular * lightBrightness*shadowed, Sat_NL_Att);
+
+   return Output;
 }

+ 130 - 0
Templates/Full/game/shaders/common/lighting/advanced/prefilterP.hlsl

@@ -0,0 +1,130 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "../../torque.hlsl"
+
+struct ConnectData
+{
+    float4 hpos     : SV_Position;
+    float2 uv      : TEXCOORD;
+};
+
+TORQUE_UNIFORM_SAMPLERCUBE(environmentMap, 0);
+
+uniform float roughness;
+uniform int face;
+uniform int mipSize;
+uniform int resolution;
+float RadicalInverse_VdC(uint bits)
+{
+	bits = (bits << 16u) | (bits >> 16u);
+	bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+	bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+	bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+	bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+	return float(bits) * 2.3283064365386963e-10; // / 0x100000000
+}
+
+float2 Hammersley(uint i, uint N)
+{
+	return float2(float(i) / float(N), RadicalInverse_VdC(i));
+}
+
+float DistributionGGX(float3 N, float3 H, float roughness)
+{
+	float a = roughness * roughness;
+	float a2 = a * a;
+	float NdotH = max(dot(N, H), 0.0);
+	float NdotH2 = NdotH * NdotH;
+
+	float nom = a2;
+	float denom = (NdotH2 * (a2 - 1.0) + 1.0);
+	denom = M_PI_F * denom * denom;
+
+	return nom / denom;
+}
+
+float3 ImportanceSampleGGX(float2 Xi, float3 N)
+{
+	float a = roughness * roughness;
+
+	float phi = 2.0 * M_PI_F * Xi.x;
+	float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
+	float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
+
+	// from spherical coordinates to cartesian coordinates
+	float3 H;
+	H.x = cos(phi) * sinTheta;
+	H.y = sin(phi) * sinTheta;
+	H.z = cosTheta;
+
+	// from tangent-space vector to world-space sample vector
+	float3 up = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
+	float3 tangent = normalize(cross(up, N));
+	float3 bitangent = cross(N, tangent);
+
+	float3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
+	return normalize(sampleVec);
+}
+
+float3 prefilterEnvMap(float3 R)
+{
+    int sampleCount = resolution*2;
+	float3 N = R;
+	float3 V = R;
+	float totalWeight = 0.0;
+	float3 prefilteredColor = float3(0.0, 0.0, 0.0);
+
+	for (int i = 0; i < sampleCount; ++i)
+	{
+			float2 Xi = Hammersley(i, sampleCount);
+			float3 H = ImportanceSampleGGX(Xi, N);
+			float3 L = normalize(2.0 * dot(V, H) * H - V);
+
+			float NdotL = max(dot(N, L), 0.0);
+			if (NdotL > 0.0)
+			{
+				// sample from the environment's mip level based on roughness/pdf
+				float D = DistributionGGX(N, H, roughness);
+				float NdotH = max(dot(N, H), 0.0);
+				float HdotV = max(dot(H, V), 0.0);
+				float pdf = D * NdotH / (4.0 * HdotV) + 0.0001;
+
+				float saTexel = 4.0 * M_PI_F / (6.0 * sampleCount * sampleCount);
+				float saSample = 1.0 / (float(sampleCount) * pdf + 0.0001);
+
+				float mipLevel = roughness == 0.0 ? 0.0 : 0.5 * log2(saSample / saTexel);
+
+				prefilteredColor += TORQUE_TEXCUBELOD(environmentMap, float4(L, mipLevel)).rgb * NdotL;				
+
+				totalWeight += NdotL;
+			}
+	}
+
+	return (prefilteredColor / totalWeight);
+}
+
+float4 main(ConnectData IN) : TORQUE_TARGET0
+{
+	float3 N = getCubeDir(face, IN.uv);
+	return float4(prefilterEnvMap(N), 1.0);
+}

+ 52 - 0
Templates/Full/game/shaders/common/lighting/advanced/probeShadingP.hlsl

@@ -0,0 +1,52 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "../../shaderModelAutoGen.hlsl"
+#include "../../postfx/postFx.hlsl"
+#include "shaders/common/torque.hlsl"
+
+TORQUE_UNIFORM_SAMPLER2D(colorBufferTex,0);
+TORQUE_UNIFORM_SAMPLER2D(diffuseLightingBuffer,1);
+TORQUE_UNIFORM_SAMPLER2D(matInfoTex,2);
+TORQUE_UNIFORM_SAMPLER2D(specularLightingBuffer,3);
+TORQUE_UNIFORM_SAMPLER2D(deferredTex,4);
+
+float4 main( PFXVertToPix IN) : TORQUE_TARGET0
+{        
+   float depth = TORQUE_DEFERRED_UNCONDITION( deferredTex, IN.uv0 ).w;
+   if (depth>0.9999)
+      return float4(0,0,0,0);
+
+   float3 colorBuffer = TORQUE_TEX2D( colorBufferTex, IN.uv0 ).rgb; //albedo
+   float4 matInfo = TORQUE_TEX2D(matInfoTex, IN.uv0); //flags|smoothness|ao|metallic
+
+   bool emissive = getFlag(matInfo.r, 0);
+   if (emissive)
+   {
+      return float4(colorBuffer, 1.0);
+   }
+	  
+   float4 diffuseLighting = TORQUE_TEX2D( diffuseLightingBuffer, IN.uv0 ); //shadowmap*specular
+   colorBuffer *= diffuseLighting.rgb;
+   
+   return hdrEncode( float4(colorBuffer.rgb, 1.0) );
+}

+ 217 - 0
Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeP.hlsl

@@ -0,0 +1,217 @@
+#include "../../shaderModelAutoGen.hlsl"
+
+#include "farFrustumQuad.hlsl"
+#include "lightingUtils.hlsl"
+#include "../../lighting.hlsl"
+#include "../../torque.hlsl"
+
+struct ConvexConnectP
+{
+   float4 pos : TORQUE_POSITION;
+   float4 wsEyeDir : TEXCOORD0;
+   float4 ssPos : TEXCOORD1;
+   float4 vsEyeDir : TEXCOORD2;
+};
+
+TORQUE_UNIFORM_SAMPLER2D(deferredBuffer, 0);
+TORQUE_UNIFORM_SAMPLER2D(matInfoBuffer, 1);
+TORQUE_UNIFORM_SAMPLERCUBE(cubeMap, 2);
+TORQUE_UNIFORM_SAMPLERCUBE(irradianceCubemap, 3);
+TORQUE_UNIFORM_SAMPLER2D(BRDFTexture, 4);
+
+uniform float4 rtParams0;
+
+uniform float3 probeWSPos;
+uniform float3 probeLSPos;
+uniform float4 vsFarPlane;
+
+uniform float  radius;
+uniform float2 attenuation;
+
+uniform float4x4 invViewMat;
+
+uniform float3 eyePosWorld;
+uniform float3 bbMin;
+uniform float3 bbMax;
+
+uniform float useSphereMode;
+
+float3 iblSpecular(float3 v, float3 n, float roughness)
+{
+   float3 R = reflect(v, n);
+   const float MAX_REFLECTION_LOD = 6.0;
+   float3 prefilteredColor = TORQUE_TEXCUBELOD(cubeMap, float4(R, roughness * MAX_REFLECTION_LOD)).rgb;
+   float2 envBRDF = TORQUE_TEX2D(BRDFTexture, float2(max(dot(n, v), 0.0), roughness)).rg;
+   //return prefilteredColor * (envBRDF.x + envBRDF.y);
+   return prefilteredColor;
+}
+
+// Box Projected IBL Lighting
+// Based on: http://www.gamedev.net/topic/568829-box-projected-cubemap-environment-mapping/
+
+float3 boxProject(float3 wsPosition, float3 reflectDir, float3 boxWSPos, float3 boxMin, float3 boxMax)
+{ 
+    float3 nrdir = normalize(reflectDir);
+    float3 rbmax = (boxMax - wsPosition) / nrdir;
+    float3 rbmin = (boxMin - wsPosition) / nrdir;
+
+    float3 rbminmax;
+    rbminmax.x = (nrdir.x > 0.0) ? rbmax.x : rbmin.x;
+    rbminmax.y = (nrdir.y > 0.0) ? rbmax.y : rbmin.y;
+    rbminmax.z = (nrdir.z > 0.0) ? rbmax.z : rbmin.z; 
+
+    float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
+    float3 posonbox = wsPosition + nrdir * fa;
+
+    return posonbox - boxWSPos;
+}
+
+float3 iblBoxDiffuse(float3 normal,
+					float3 wsPos, 
+                    TORQUE_SAMPLERCUBE(irradianceCube), 
+                    float3 boxPos,
+                    float3 boxMin,
+                    float3 boxMax)
+{
+    // Irradiance (Diffuse)
+    float3 cubeN = normalize(normal);
+    float3 irradiance = TORQUE_TEXCUBE(irradianceCube, cubeN).xyz;
+
+    return irradiance;
+}
+
+float3 iblBoxSpecular(float3 normal,
+					float3 wsPos, 
+					float roughness,
+                    float3 viewDir, 
+                    TORQUE_SAMPLER2D(brdfTexture), 
+                    TORQUE_SAMPLERCUBE(radianceCube),
+                    float3 boxPos,
+                    float3 boxMin,
+                    float3 boxMax)
+{
+    float3 v = viewDir;
+    float3 n = normalize(normal);
+    float ndotv = clamp(dot(n, v), 0.0, 1.0);
+
+    // BRDF
+    float2 brdf = TORQUE_TEX2D(brdfTexture, float2(roughness, ndotv)).xy;
+
+    // Radiance (Specular)
+    float lod = roughness * 6.0;
+    float3 r = 2.0 * ndotv * n - v; // reflect(v, n);
+    float3 cubeR = normalize(r);
+    cubeR = boxProject(wsPos, cubeR, boxPos, boxMin, boxMax);
+	
+    float3 radiance = TORQUE_TEXCUBELOD(radianceCube, float4(cubeR, lod)).xyz * (brdf.x + brdf.y);
+    
+    return radiance;
+}
+
+struct PS_OUTPUT
+{
+    float4 diffuse: TORQUE_TARGET0;
+    float4 spec: TORQUE_TARGET1;
+};
+
+PS_OUTPUT main( ConvexConnectP IN )
+{ 
+    PS_OUTPUT Output = (PS_OUTPUT)0;
+
+    // Compute scene UV
+    float3 ssPos = IN.ssPos.xyz / IN.ssPos.w; 
+
+    //float4 hardCodedRTParams0 = float4(0,0.0277777780,1,0.972222209);
+    float2 uvScene = getUVFromSSPos( ssPos, rtParams0 );
+
+    // Matinfo flags
+    float4 matInfo = TORQUE_TEX2D( matInfoBuffer, uvScene ); 
+
+    // Sample/unpack the normal/z data
+    float4 deferredSample = TORQUE_DEFERRED_UNCONDITION( deferredBuffer, uvScene );
+    float3 normal = deferredSample.rgb;
+    float depth = deferredSample.a;
+    if (depth>0.9999)
+          clip(-1);
+
+    // Need world-space normal.
+    float3 wsNormal = mul(float4(normal, 1), invViewMat).rgb;
+
+    float4 color = float4(1, 1, 1, 1);
+    float4 ref = float4(0,0,0,0);
+    float alpha = 1;
+
+    float3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, IN.vsEyeDir.xyz, vsFarPlane );
+    float3 viewSpacePos = eyeRay * depth;
+
+    float3 wsEyeRay = mul(float4(eyeRay, 1), invViewMat).rgb;
+
+    // Use eye ray to get ws pos
+    float3 worldPos = float3(eyePosWorld + wsEyeRay * depth);
+    float smoothness = min((1.0 - matInfo.b)*11.0 + 1.0, 8.0);//bump up to 8 for finalization
+
+    if(useSphereMode)
+    {
+        // Build light vec, get length, clip pixel if needed
+        float3 lightVec = probeLSPos - viewSpacePos;
+        float lenLightV = length( lightVec );
+        clip( radius - lenLightV );
+
+        // Get the attenuated falloff.
+        float atten = attenuate( float4(1,1,1,1), attenuation, lenLightV );
+        clip( atten - 1e-6 );
+
+        // Normalize lightVec
+        lightVec = normalize(lightVec);
+
+        // If we can do dynamic branching then avoid wasting
+        // fillrate on pixels that are backfacing to the light.
+        float nDotL = abs(dot( lightVec, normal ));
+
+        float Sat_NL_Att = saturate( nDotL * atten );
+
+        float3 reflectionVec = reflect(IN.wsEyeDir, float4(wsNormal,nDotL)).xyz;
+
+        float3 nrdir = normalize(reflectionVec);
+        float3 rbmax = (bbMax - worldPos.xyz) / nrdir;
+        float3 rbmin = (bbMin - worldPos.xyz) / nrdir;
+
+        float3 rbminmax = (nrdir > 0.0) ? rbmax : rbmin;
+        float fa = min(min(rbminmax.x,rbminmax.y),rbminmax.z);
+		  if (dot( lightVec, normal )<0.0f)
+           clip(fa);
+
+        float3 posOnBox = worldPos.xyz + nrdir * fa;
+        reflectionVec = posOnBox - probeWSPos;
+
+        reflectionVec = mul(probeWSPos,reflectionVec);
+
+        ref = float4(reflectionVec, smoothness);
+
+        alpha = Sat_NL_Att;
+		float roughness = 1 - matInfo.b;
+
+		float3 irradiance = TORQUE_TEXCUBELOD(irradianceCubemap, ref).rgb;
+
+		float3 specular = TORQUE_TEXCUBELOD(cubeMap, ref).rgb;// iblSpecular(wsEyeRay, wsNormal, roughness);
+
+		Output.diffuse = float4(irradiance.rgb, alpha);
+		Output.spec = float4(specular.rgb, alpha);
+
+		return Output;
+    }
+    else
+    {
+       //Try to clip anything that falls outside our box as well
+       //TODO: Make it support rotated boxes as well
+       if(worldPos.x > bbMax.x || worldPos.y > bbMax.y || worldPos.z > bbMax.z ||
+          worldPos.x < bbMin.x || worldPos.y < bbMin.y || worldPos.z < bbMin.z)
+          clip(-1);
+		  
+       float blendVal = 1.0;
+	   float3 pixDir = normalize(eyePosWorld.xyz - worldPos.xyz);
+       Output.diffuse = float4(iblBoxDiffuse(wsNormal, worldPos, TORQUE_SAMPLERCUBE_MAKEARG(irradianceCubemap), probeWSPos, bbMin, bbMax), blendVal);
+	   Output.spec = float4(iblBoxSpecular(wsNormal, worldPos, 1.0 - matInfo.b, pixDir, TORQUE_SAMPLER2D_MAKEARG(BRDFTexture), TORQUE_SAMPLERCUBE_MAKEARG(cubeMap), probeWSPos, bbMin, bbMax), blendVal);
+       return Output;	   
+    }
+}

+ 145 - 0
Templates/Full/game/shaders/common/lighting/advanced/skylightP.hlsl

@@ -0,0 +1,145 @@
+#include "../../shaderModelAutoGen.hlsl"
+
+#include "farFrustumQuad.hlsl"
+#include "lightingUtils.hlsl"
+#include "../../lighting.hlsl"
+#include "../../torque.hlsl"
+
+struct ConvexConnectP
+{
+   float4 pos : TORQUE_POSITION;
+   float4 wsEyeDir : TEXCOORD0;
+   float4 ssPos : TEXCOORD1;
+   float4 vsEyeDir : TEXCOORD2;
+};
+
+TORQUE_UNIFORM_SAMPLER2D(deferredBuffer, 0);
+TORQUE_UNIFORM_SAMPLER2D(matInfoBuffer, 1);
+TORQUE_UNIFORM_SAMPLERCUBE(cubeMap, 2);
+TORQUE_UNIFORM_SAMPLERCUBE(irradianceCubemap, 3);
+TORQUE_UNIFORM_SAMPLER2D(BRDFTexture, 4);
+
+
+uniform float4 rtParams0;
+
+uniform float4 vsFarPlane;
+
+uniform float4x4 invViewMat;
+
+uniform float3 eyePosWorld;
+
+//SHTerms
+/*uniform float4 SHTerms0;
+uniform float4 SHTerms1;
+uniform float4 SHTerms2;
+uniform float4 SHTerms3;
+uniform float4 SHTerms4;
+uniform float4 SHTerms5;
+uniform float4 SHTerms6;
+uniform float4 SHTerms7;
+uniform float4 SHTerms8;
+
+uniform float SHConsts0;
+uniform float SHConsts1;
+uniform float SHConsts2;
+uniform float SHConsts3;
+uniform float SHConsts4;
+
+float4 decodeSH(float3 normal)
+{
+   float x = normal.x;
+   float y = normal.y;
+   float z = normal.z;
+
+   float3 l00 = SHTerms0.rgb;
+
+   float3 l10 = SHTerms1.rgb;
+   float3 l11 = SHTerms2.rgb;
+   float3 l12 = SHTerms3.rgb;
+
+   float3 l20 = SHTerms4.rgb;
+   float3 l21 = SHTerms5.rgb;
+   float3 l22 = SHTerms6.rgb;
+   float3 l23 = SHTerms7.rgb;
+   float3 l24 = SHTerms8.rgb;
+
+   float3 result = (
+         l00 * SHConsts0 +
+
+         l12 * SHConsts1 * x +
+         l10 * SHConsts1 * y +
+         l11 * SHConsts1 * z +
+
+         l20 * SHConsts2 * x*y +
+         l21 * SHConsts2 * y*z +
+         l22 * SHConsts3 * (3.0*z*z - 1.0) +
+         l23 * SHConsts2 * x*z +
+         l24 * SHConsts4 * (x*x - y*y)
+      );
+
+    return float4(result,1);
+}*/
+
+float3 iblSpecular(float3 v, float3 n, float roughness)
+{
+	float3 R = reflect(v, n); 
+	const float MAX_REFLECTION_LOD = 4.0;
+	float3 prefilteredColor = TORQUE_TEXCUBELOD(cubeMap, float4(R, roughness * MAX_REFLECTION_LOD)).rgb;
+	float2 envBRDF  = TORQUE_TEX2D(BRDFTexture, float2(max(dot(n, v), 0.0), roughness)).rg;
+	return prefilteredColor * (envBRDF.x + envBRDF.y);
+   //return prefilteredColor;
+ }
+
+struct PS_OUTPUT
+{
+    float4 diffuse: TORQUE_TARGET0;
+    float4 spec: TORQUE_TARGET1;
+};
+
+PS_OUTPUT main( ConvexConnectP IN )
+{ 
+    PS_OUTPUT Output = (PS_OUTPUT)0;
+
+    // Compute scene UV
+    float3 ssPos = IN.ssPos.xyz / IN.ssPos.w; 
+
+    //float4 hardCodedRTParams0 = float4(0,0.0277777780,1,0.972222209);
+    float2 uvScene = getUVFromSSPos( ssPos, rtParams0 );
+
+    // Matinfo flags
+    float4 matInfo = TORQUE_TEX2D( matInfoBuffer, uvScene ); 
+
+    // Sample/unpack the normal/z data
+    float4 deferredSample = TORQUE_DEFERRED_UNCONDITION( deferredBuffer, uvScene );
+    float3 normal = deferredSample.rgb;
+    float depth = deferredSample.a;
+    if (depth>0.9999)
+        return Output; 
+
+    // Need world-space normal.
+    float3 wsNormal = mul(float4(normal, 1), invViewMat).rgb;
+
+    float3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, IN.vsEyeDir.xyz, vsFarPlane );
+
+    float3 wsEyeRay = mul(float4(eyeRay, 1), invViewMat).rgb;
+
+    // Use eye ray to get ws pos
+    float3 worldPos = float3(eyePosWorld + wsEyeRay * depth);
+
+    float3 reflectionVec = reflect(IN.wsEyeDir, float4(wsNormal,1)).xyz;
+
+    float roughness = 1 - matInfo.b;
+
+    float3 v = normalize(eyePosWorld - worldPos);
+
+    float3 irradiance = TORQUE_TEXCUBE(irradianceCubemap, wsNormal).rgb;
+
+    float3 specular = iblSpecular(wsEyeRay, wsNormal, roughness);
+
+
+    Output.diffuse = float4(irradiance.rgb, 1);
+    Output.spec = float4(specular.rgb, 1);
+
+    return Output;
+
+}

+ 99 - 97
Templates/Full/game/shaders/common/lighting/advanced/vectorLightP.hlsl

@@ -192,23 +192,24 @@ float4 AL_VectorLightShadowCast( TORQUE_SAMPLER2D(sourceShadowMap),
                                  dot( finalMask, overDarkPSSM ) ) );
 };
 
-float4 main( FarFrustumQuadConnectP IN ) : TORQUE_TARGET0
+struct PS_OUTPUT
 {
+    float4 spec: TORQUE_TARGET0;
+    float4 diffuse: TORQUE_TARGET1;
+};
+
+PS_OUTPUT main(FarFrustumQuadConnectP IN)
+{
+   PS_OUTPUT Output = (PS_OUTPUT)0;
    // Matinfo flags
-   float4 matInfo = TORQUE_TEX2D( matInfoBuffer, IN.uv0 );
-   //early out if emissive
-   bool emissive = getFlag(matInfo.r, 0);
-   if (emissive)
-   {
-      return float4(0.0, 0.0, 0.0, 0.0);
-   }
-   
-   float4 colorSample = TORQUE_TEX2D( colorBuffer, IN.uv0 );
-   float3 subsurface = float3(0.0,0.0,0.0); 
-   if (getFlag( matInfo.r, 1 ))
+   float4 matInfo = TORQUE_TEX2D(matInfoBuffer, IN.uv0);
+
+   float4 colorSample = TORQUE_TEX2D(colorBuffer, IN.uv0);
+   float3 subsurface = float3(0.0, 0.0, 0.0);
+   if (getFlag(matInfo.r, 1))
    {
       subsurface = colorSample.rgb;
-      if (colorSample.r>colorSample.g)
+      if (colorSample.r > colorSample.g)
          subsurface = float3(0.772549, 0.337255, 0.262745);
 	  else
          subsurface = float3(0.337255, 0.772549, 0.262745);
@@ -224,96 +225,97 @@ float4 main( FarFrustumQuadConnectP IN ) : TORQUE_TARGET0
    // Get the light attenuation.
    float dotNL = dot(-lightDirection, normal);
 
-   #ifdef PSSM_DEBUG_RENDER
-      float3 debugColor = float3(0,0,0);
-   #endif
-   
-   #ifdef NO_SHADOW
+#ifdef PSSM_DEBUG_RENDER
+   float3 debugColor = float3(0, 0, 0);
+#endif
 
-      // Fully unshadowed.
-      float shadowed = 1.0;
+#ifdef NO_SHADOW
 
-      #ifdef PSSM_DEBUG_RENDER
-         debugColor = float3(1.0,1.0,1.0);
-      #endif
+   // Fully unshadowed.
+   float shadowed = 1.0;
 
-   #else
-      
-      float4 static_shadowed_colors = AL_VectorLightShadowCast( TORQUE_SAMPLER2D_MAKEARG(shadowMap),
-                                                        IN.uv0.xy,
-                                                        worldToLightProj,
-                                                        worldPos,
-                                                        scaleX, scaleY,
-                                                        offsetX, offsetY,
-                                                        farPlaneScalePSSM,
-                                                        atlasXOffset, atlasYOffset,
-                                                        atlasScale,
-                                                        shadowSoftness, 
-                                                        dotNL,
-                                                        overDarkPSSM);
-      float4 dynamic_shadowed_colors = AL_VectorLightShadowCast( TORQUE_SAMPLER2D_MAKEARG(dynamicShadowMap),
-                                                        IN.uv0.xy,
-                                                        dynamicWorldToLightProj,
-                                                        worldPos,
-                                                        dynamicScaleX, dynamicScaleY,
-                                                        dynamicOffsetX, dynamicOffsetY,
-                                                        dynamicFarPlaneScalePSSM,
-                                                        atlasXOffset, atlasYOffset,
-                                                        atlasScale,
-                                                        shadowSoftness, 
-                                                        dotNL,
-                                                        overDarkPSSM);
-      
-      float static_shadowed = static_shadowed_colors.a;
-      float dynamic_shadowed = dynamic_shadowed_colors.a;
-	  
-      #ifdef PSSM_DEBUG_RENDER
-	     debugColor = static_shadowed_colors.rgb*0.5+dynamic_shadowed_colors.rgb*0.5;
-      #endif
-  
-      // Fade out the shadow at the end of the range.
-      float4 zDist = (zNearFarInvNearFar.x + zNearFarInvNearFar.y * depth);
-      float fadeOutAmt = ( zDist.x - fadeStartLength.x ) * fadeStartLength.y;
+#ifdef PSSM_DEBUG_RENDER
+   debugColor = float3(1.0, 1.0, 1.0);
+#endif
 
-      static_shadowed = lerp( static_shadowed, 1.0, saturate( fadeOutAmt ) );
-      dynamic_shadowed = lerp( dynamic_shadowed, 1.0, saturate( fadeOutAmt ) );
+#else
+
+   float4 static_shadowed_colors = AL_VectorLightShadowCast(TORQUE_SAMPLER2D_MAKEARG(shadowMap),
+      IN.uv0.xy,
+      worldToLightProj,
+      worldPos,
+      scaleX, scaleY,
+      offsetX, offsetY,
+      farPlaneScalePSSM,
+      atlasXOffset, atlasYOffset,
+      atlasScale,
+      shadowSoftness,
+      dotNL,
+      overDarkPSSM);
+   float4 dynamic_shadowed_colors = AL_VectorLightShadowCast(TORQUE_SAMPLER2D_MAKEARG(dynamicShadowMap),
+      IN.uv0.xy,
+      dynamicWorldToLightProj,
+      worldPos,
+      dynamicScaleX, dynamicScaleY,
+      dynamicOffsetX, dynamicOffsetY,
+      dynamicFarPlaneScalePSSM,
+      atlasXOffset, atlasYOffset,
+      atlasScale,
+      shadowSoftness,
+      dotNL,
+      overDarkPSSM);
+
+   float static_shadowed = static_shadowed_colors.a;
+   float dynamic_shadowed = dynamic_shadowed_colors.a;
+
+#ifdef PSSM_DEBUG_RENDER
+   debugColor = static_shadowed_colors.rgb*0.5 + dynamic_shadowed_colors.rgb*0.5;
+#endif
 
-      // temp for debugging. uncomment one or the other.
-      //float shadowed = static_shadowed;
-      //float shadowed = dynamic_shadowed;
-      float shadowed = min(static_shadowed, dynamic_shadowed);
+   // Fade out the shadow at the end of the range.
+   float4 zDist = (zNearFarInvNearFar.x + zNearFarInvNearFar.y * depth);
+   float fadeOutAmt = (zDist.x - fadeStartLength.x) * fadeStartLength.y;
 
-      #ifdef PSSM_DEBUG_RENDER
-         if ( fadeOutAmt > 1.0 )
-            debugColor = 1.0;
-      #endif
+   static_shadowed = lerp(static_shadowed, 1.0, saturate(fadeOutAmt));
+   dynamic_shadowed = lerp(dynamic_shadowed, 1.0, saturate(fadeOutAmt));
 
-   #endif // !NO_SHADOW
-
-   // Specular term   
-   float3 viewSpacePos = IN.vsEyeRay * depth;
-   float4 real_specular = EvalBDRF( float3( 1.0, 1.0, 1.0 ),
-                                    lightColor.rgb,
-                                    normalize( -lightDirection ),
-                                    viewSpacePos,
-                                    normal,
-                                    1.0-matInfo.b,
-                                    matInfo.a );
-   float3 lightColorOut = real_specular.rgb * lightBrightness * shadowed;
-   
-   float Sat_NL_Att = saturate( dotNL * shadowed ) * lightBrightness;
-   float Sat_NdotV = saturate(dot(normalize(-IN.vsEyeRay), normal));
-   float4 addToResult = ( lightAmbient * (1 - ambientCameraFactor)) + ( lightAmbient * ambientCameraFactor * Sat_NdotV );
-
-   // Sample the AO texture.      
-   #ifdef USE_SSAO_MASK
-      float ao = 1.0 - TORQUE_TEX2D( ssaoMask, viewportCoordToRenderTarget( IN.uv0.xy, rtParams3 ) ).r;
-      addToResult *= ao;
-   #endif
-
-   #ifdef PSSM_DEBUG_RENDER
-      lightColorOut = debugColor;
-   #endif
+   // temp for debugging. uncomment one or the other.
+   //float shadowed = static_shadowed;
+   //float shadowed = dynamic_shadowed;
+   float shadowed = min(static_shadowed, dynamic_shadowed);
+
+#ifdef PSSM_DEBUG_RENDER
+   if (fadeOutAmt > 1.0)
+      debugColor = 1.0;
+#endif
+
+#endif // !NO_SHADOW
+
+   float3 l = normalize(-lightDirection);
+   float3 v = normalize(eyePosWorld - worldPos.xyz);
+
+   float3 h = normalize(v + l);
+   float dotNLa = clamp(dot(normal, l), 0.0, 1.0);
+   float dotNVa = clamp(dot(normal, v), 0.0, 1.0);
+   float dotNHa = clamp(dot(normal, h), 0.0, 1.0);
+   float dotHVa = clamp(dot(normal, v), 0.0, 1.0);
+   float dotLHa = clamp(dot(l, h), 0.0, 1.0);
+
+   float roughness = matInfo.g;
+   float metalness = matInfo.b;
+
+   //diffuse
+   //float dotNL = clamp(dot(normal,l), 0.0, 1.0);
+   float disDiff = Fr_DisneyDiffuse(dotNVa, dotNLa, dotLHa, roughness);
+   float3 diffuse = float3(disDiff, disDiff, disDiff) / M_PI_F;// alternative: (lightColor * dotNL) / Pi;
+   //specular
+   float3 specular = directSpecular(normal, v, l, roughness, 1.0) * lightColor.rgb;
    
-   return float4(matInfo.g*(lightColorOut*Sat_NL_Att+subsurface*(1.0-Sat_NL_Att)+addToResult.rgb),real_specular.a);
+   float finalShadowed = shadowed;
+
+//output
+   Output.diffuse = float4(diffuse * (lightBrightness*shadowed), dotNLa);
+   Output.spec = float4(specular * (lightBrightness*shadowed), dotNLa);
+
+   return Output;
 }

+ 9 - 6
Templates/Full/game/shaders/common/postFx/hdr/finalPassCombineP.hlsl

@@ -55,15 +55,16 @@ float3 Uncharted2Tonemap(const float3 x)
    return ((x*(A*x + C*B) + D*E) / (x*(A*x + B) + D*F)) - E / F;
 }
 
-float3 tonemap(float3 c)
+float3 tonemap(float3 color)
 {
    const float W = 11.2;
    float ExposureBias = 2.0f;
-   float ExposureAdjust = 1.5f;
-   c *= ExposureAdjust;
-   float3 curr = Uncharted2Tonemap(ExposureBias*c);
-   float3 whiteScale = 1.0f / Uncharted2Tonemap(W);
-   return curr*whiteScale;
+   //float ExposureAdjust = 1.5f;
+   //c *= ExposureAdjust;
+   color = Uncharted2Tonemap(ExposureBias*color);
+   color = color * (1.0f / Uncharted2Tonemap(W));
+
+   return color;
 }
 
 float4 main( PFXVertToPix IN ) : TORQUE_TARGET0
@@ -100,5 +101,7 @@ float4 main( PFXVertToPix IN ) : TORQUE_TARGET0
    sample.g = TORQUE_TEX1D( colorCorrectionTex, sample.g ).g;
    sample.b = TORQUE_TEX1D( colorCorrectionTex, sample.b ).b;
 
+   sample = float4(tonemap(sample.rgb),1);
+
    return sample;
 }

+ 23 - 11
Templates/Full/game/shaders/common/postFx/hdr/gl/finalPassCombineP.glsl

@@ -48,6 +48,29 @@ uniform float Contrast;
 
 out vec4 OUT_col;
 
+// uncharted 2 tonemapper see: http://filmicgames.com/archives/75
+vec3 Uncharted2Tonemap(vec3 x)
+{
+   const float A = 0.15;
+   const float B = 0.50;
+   const float C = 0.10;
+   const float D = 0.20;
+   const float E = 0.02;
+   const float F = 0.30;
+   return ((x*(A*x + C*B) + D*E) / (x*(A*x + B) + D*F)) - E / F;
+}
+
+vec3 tonemap(vec3 c)
+{
+   const float W = 11.2;
+   float ExposureBias = 2.0f;
+   float ExposureAdjust = 1.5f;
+   c *= ExposureAdjust;
+   vec3 curr = Uncharted2Tonemap(ExposureBias*c);
+   vec3 whiteScale = 1.0f / Uncharted2Tonemap(vec3(W,W,W));
+   return curr*whiteScale;
+}
+
 void main()
 {
    vec4 _sample = hdrDecode( texture( sceneTex, IN_uv0 ) );
@@ -76,17 +99,6 @@ void main()
 
    // Add the bloom effect.
    _sample += g_fBloomScale * bloom;
-   
-   // Map the high range of color values into a range appropriate for
-   // display, taking into account the user's adaptation level, 
-   // white point, and selected value for for middle gray.
-   if ( g_fEnableToneMapping > 0.0f )
-   {
-      float Lp = (g_fMiddleGray / (adaptedLum + 0.0001)) * hdrLuminance( _sample.rgb );
-      //float toneScalar = ( Lp * ( 1.0 + ( Lp / ( g_fWhiteCutoff ) ) ) ) / ( 1.0 + Lp );
-	  float toneScalar = Lp;
-      _sample.rgb = mix( _sample.rgb, _sample.rgb * toneScalar, g_fEnableToneMapping );
-   }
 
    // Apply the color correction.
    _sample.r = texture( colorCorrectionTex, _sample.r ).r;

+ 34 - 0
Templates/Full/game/shaders/common/torque.hlsl

@@ -310,4 +310,38 @@ float3 simpleFresnel(float3 diffuseColor, float3 reflectColor, float metalness,
 
    return lerp(diffuseColor, reflectColor, fresnelTerm);
 }
+
+//hlsl version of the glsl funcion mod - note hlsl fmod is different
+#define mod(x,y) (x-y*floor(x/y))
+
+//get direction for a cube face
+float3 getCubeDir(int face, float2 uv)
+{
+	float2 debiased = uv * 2.0f - 1.0f;
+
+	float3 dir = 0;
+
+	switch (face)
+	{
+		case 0: dir = float3(1, -debiased.y, -debiased.x); 
+			break;
+
+		case 1: dir = float3(-1, -debiased.y, debiased.x); 
+			break;
+
+		case 2: dir = float3(debiased.x, 1, debiased.y); 
+			break;
+
+		case 3: dir = float3(debiased.x, -1, -debiased.y); 
+			break;
+
+		case 4: dir = float3(debiased.x, -debiased.y, 1); 
+			break;
+
+		case 5: dir = float3(-debiased.x, -debiased.y, -1); 
+			break;
+	};
+
+	return normalize(dir);
+}
 #endif // _TORQUE_HLSL_

+ 0 - 1
Templates/Full/game/shaders/common/water/gl/waterBasicP.glsl

@@ -120,7 +120,6 @@ void main()
 { 
    // Modulate baseColor by the ambientColor.
    vec4 waterBaseColor = baseColor * vec4( ambientColor.rgb, 1 );
-   waterBaseColor = waterBaseColor;
    
    // Get the bumpNorm...
    vec3 bumpNorm = ( texture( bumpMap, IN_rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x;

+ 0 - 1
Templates/Full/game/shaders/common/water/gl/waterP.glsl

@@ -324,7 +324,6 @@ void main()
    
    // Calculate the water "base" color based on depth.
    vec4 waterBaseColor = baseColor * texture( depthGradMap, saturate( delta / depthGradMax ) );
-   waterBaseColor = waterBaseColor;
       
    // Modulate baseColor by the ambientColor.
    waterBaseColor *= vec4( ambientColor.rgb, 1 );     

+ 1 - 0
Templates/Full/game/shaders/procedural/.gitignore

@@ -0,0 +1 @@
+/procedural/

File diff suppressed because it is too large
+ 61 - 0
Templates/Full/game/tools/resources/ReflectProbeSphere.dae


+ 8 - 0
Templates/Full/game/tools/resources/materials.cs

@@ -0,0 +1,8 @@
+singleton Material(ReflectProbePreviewMat)
+{
+   mapTo = "ReflectProbePreviewMat";
+   diffuseColor[0] = "1 1 1 1";
+   smoothness[0] = "1";
+   metalness[0] = "1";
+   translucentBlendOp = "None";
+};

+ 49 - 0
Templates/Full/game/tools/resources/reflectionProbePreviewP.hlsl

@@ -0,0 +1,49 @@
+#include "shaders/common/shaderModelAutoGen.hlsl"
+
+#include "shaders/common/lighting/advanced/farFrustumQuad.hlsl"
+#include "shaders/common/lighting/advanced/lightingUtils.hlsl"
+#include "shaders/common/lighting.hlsl"
+#include "shaders/common/torque.hlsl"
+
+struct ConvexConnectP
+{
+   float4 pos : TORQUE_POSITION;
+   float4 wsEyeDir : TEXCOORD0;
+   float4 ssPos : TEXCOORD1;
+   float4 vsEyeDir : TEXCOORD2;
+};
+
+TORQUE_UNIFORM_SAMPLER2D(deferredBuffer, 0);
+TORQUE_UNIFORM_SAMPLERCUBE(cubeMap, 1);
+
+uniform float4 rtParams0;
+
+uniform float4x4 invViewMat;
+
+float4 main( ConvexConnectP IN ) : TORQUE_TARGET0
+{ 
+    // Compute scene UV
+    float3 ssPos = IN.ssPos.xyz / IN.ssPos.w;
+    float2 uvScene = getUVFromSSPos( ssPos, rtParams0 );
+
+    //float3 eyeRay = IN.vsEyeDir.xyz;
+
+    // Sample/unpack the normal/z data
+    float4 deferredSample = TORQUE_DEFERRED_UNCONDITION( deferredBuffer, uvScene );
+    float3 normal = deferredSample.rgb;
+    float depth = deferredSample.a;
+    if (depth>0.9999)
+        return float4(0,0,0,0);
+
+    // Need world-space normal.
+    float3 wsNormal = mul(float4(normal, 1), invViewMat).rgb;
+
+    float3 reflectionVec = reflect(IN.wsEyeDir, float4(normalize(wsNormal),1)).rgb;
+
+    float4 color = TORQUE_TEXCUBE(cubeMap, reflectionVec);
+
+    //simple visibility testing
+    //float4 color = float4(1,0,0,1);
+
+    return float4(color.rgb, 1);
+}

+ 58 - 0
Templates/Full/game/tools/resources/reflectionProbePreviewV.hlsl

@@ -0,0 +1,58 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "shaders/common/hlslStructs.hlsl"
+#include "shaders/common/shaderModel.hlsl"
+
+struct VertData
+{
+   float3 pos        : POSITION;
+   float tangentW        : TEXCOORD3;
+   float3 normal          : NORMAL;
+   float3 T               : TANGENT;
+   float2 texCoord        : TEXCOORD0;
+};
+
+struct ConvexConnectV
+{
+   float4 hpos : TORQUE_POSITION;
+   float4 wsEyeDir : TEXCOORD0;
+   float4 ssPos : TEXCOORD1;
+   float4 vsEyeDir : TEXCOORD2;
+};
+
+uniform float4x4 modelview;
+uniform float4x4 objTrans;
+uniform float4x4 worldViewOnly;
+uniform float3 eyePosWorld;
+
+ConvexConnectV main( VertData IN )
+{
+   ConvexConnectV OUT;
+
+   OUT.hpos = mul( modelview, float4(IN.pos,1.0) );
+   OUT.wsEyeDir = mul(objTrans, float4(IN.pos, 1.0)) - float4(eyePosWorld, 0.0);
+   OUT.vsEyeDir = mul(worldViewOnly, float4(IN.pos, 1.0));
+   OUT.ssPos = OUT.hpos;
+
+   return OUT;
+}

+ 1559 - 1206
Templates/Full/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui

@@ -1,637 +1,239 @@
 //--- OBJECT WRITE BEGIN ---
-%guiContent = new GuiControl(TerrainMaterialDlg, EditorGuiGroup) {
-   canSaveDynamicFields = "0";
-   isContainer = "1";
-   Profile = "ToolsGuiDefaultProfile";
-   HorizSizing = "right";
-   VertSizing = "bottom";
+%guiContent = new GuiControl(TerrainMaterialDlg,EditorGuiGroup) {
    position = "0 0";
-   Extent = "800 768";
-   MinExtent = "8 2";
-   canSave = "1";
-   Visible = "1";
-   tooltipprofile = "ToolsGuiToolTipProfile";
+   extent = "1024 768";
+   minExtent = "8 2";
+   horizSizing = "right";
+   vertSizing = "bottom";
+   profile = "ToolsGuiDefaultProfile";
+   visible = "1";
+   active = "1";
+   tooltipProfile = "ToolsGuiToolTipProfile";
    hovertime = "1000";
+   isContainer = "1";
+   canSave = "1";
+   canSaveDynamicFields = "1";
+      activeMat = "17411";
+      matIndex = "0";
+      onApplyCallback = "EPainter_TerrainMaterialUpdateCallback";
 
    new GuiWindowCtrl() {
-      canSaveDynamicFields = "0";
-      isContainer = "1";
-      Profile = "ToolsGuiWindowProfile";
-      HorizSizing = "center";
-      VertSizing = "center";
-      position = "221 151";
-      Extent = "394 432";
-      MinExtent = "358 432";
-      canSave = "1";
-      Visible = "1";
-      tooltipprofile = "ToolsGuiToolTipProfile";
-      hovertime = "1000";
-      Docking = "None";
-      Margin = "4 4 4 4";
-      Padding = "0 0 0 0";
-      AnchorTop = "0";
-      AnchorBottom = "0";
-      AnchorLeft = "0";
-      AnchorRight = "0";
+      text = "Terrain Materials Editor";
       resizeWidth = "1";
       resizeHeight = "1";
       canMove = "1";
       canClose = "1";
       canMinimize = "0";
       canMaximize = "0";
-      minSize = "50 50";
+      canCollapse = "0";
       closeCommand = "TerrainMaterialDlg.dialogCancel();";
-      EdgeSnap = "0";
-      text = "Terrain Materials Editor";
-      new GuiContainer(){ //Node Properties
+      edgeSnap = "0";
+      docking = "None";
+      margin = "4 4 4 4";
+      padding = "0 0 0 0";
+      anchorTop = "0";
+      anchorBottom = "0";
+      anchorLeft = "0";
+      anchorRight = "0";
+      position = "315 168";
+      extent = "394 432";
+      minExtent = "358 432";
+      horizSizing = "center";
+      vertSizing = "center";
+      profile = "ToolsGuiWindowProfile";
+      visible = "1";
+      active = "1";
+      tooltipProfile = "ToolsGuiToolTipProfile";
+      hovertime = "1000";
+      isContainer = "1";
+      canSave = "1";
+      canSaveDynamicFields = "0";
+
+      new GuiContainer() {
+         margin = "0 0 0 0";
+         padding = "0 0 0 0";
+         anchorTop = "1";
+         anchorBottom = "0";
+         anchorLeft = "1";
+         anchorRight = "0";
+         position = "6 25";
+         extent = "189 64";
+         minExtent = "8 2";
+         horizSizing = "width";
+         vertSizing = "bottom";
+         profile = "inspectorStyleRolloutDarkProfile";
+         visible = "1";
+         active = "1";
+         tooltipProfile = "GuiToolTipProfile";
+         hovertime = "1000";
          isContainer = "1";
-         Profile = "inspectorStyleRolloutDarkProfile";
-         HorizSizing = "width";
-         VertSizing = "bottom";
-         Position = "6 25";
-         Extent = "189 64";
-         
-         new GuiTextCtrl(){
-            Profile = "ToolsGuiDefaultProfile";
-            HorizSizing = "right";
-            VertSizing = "bottom";
-            Position = "5 0";
-            Extent = "91 18";
+         canSave = "1";
+         canSaveDynamicFields = "0";
+
+         new GuiTextCtrl() {
             text = "Terrain Materials";
+            maxLength = "1024";
+            margin = "0 0 0 0";
+            padding = "0 0 0 0";
+            anchorTop = "1";
+            anchorBottom = "0";
+            anchorLeft = "1";
+            anchorRight = "0";
+            position = "5 0";
+            extent = "91 18";
+            minExtent = "8 2";
+            horizSizing = "right";
+            vertSizing = "bottom";
+            profile = "ToolsGuiDefaultProfile";
+            visible = "1";
+            active = "1";
+            tooltipProfile = "GuiToolTipProfile";
+            hovertime = "1000";
+            isContainer = "1";
+            canSave = "1";
+            canSaveDynamicFields = "0";
          };
          new GuiBitmapButtonCtrl() {
-            canSaveDynamicFields = "0";
-            isContainer = "0";
-            Profile = "ToolsGuiButtonProfile";
-            HorizSizing = "left";
-            VertSizing = "top";
-            position = "160 2";
-            Extent = "15 15";
-            MinExtent = "8 2";
-            canSave = "1";
-            Visible = "1";
-            Command = "TerrainMaterialDlg.newMat();";
-            tooltipprofile = "ToolsGuiToolTipProfile";
-            hovertime = "1000";
+            bitmap = "tools/gui/images/new";
+            bitmapMode = "Stretched";
+            autoFitExtents = "0";
+            useModifiers = "0";
+            useStates = "1";
+            masked = "0";
             groupNum = "-1";
             buttonType = "PushButton";
             useMouseEvents = "0";
-            bitmap = "tools/gui/images/new";
-         };
-         new GuiBitmapButtonCtrl() {
-            canSaveDynamicFields = "0";
+            position = "160 2";
+            extent = "15 15";
+            minExtent = "8 2";
+            horizSizing = "left";
+            vertSizing = "top";
+            profile = "ToolsGuiButtonProfile";
+            visible = "1";
+            active = "1";
+            command = "TerrainMaterialDlg.newMat();";
+            tooltipProfile = "ToolsGuiToolTipProfile";
+            hovertime = "1000";
             isContainer = "0";
-            Profile = "ToolsGuiButtonProfile";
-            HorizSizing = "left";
-            VertSizing = "top";
-            position = "173 2";
-            Extent = "15 15";
-            MinExtent = "8 2";
             canSave = "1";
-            Visible = "1";
-            Command = "TerrainMaterialDlg.deleteMat();";
-            tooltipprofile = "ToolsGuiToolTipProfile";
-            hovertime = "1000";
+            canSaveDynamicFields = "0";
+         };
+         new GuiBitmapButtonCtrl() {
+            bitmap = "tools/gui/images/delete";
+            bitmapMode = "Stretched";
+            autoFitExtents = "0";
+            useModifiers = "0";
+            useStates = "1";
+            masked = "0";
             groupNum = "-1";
             buttonType = "PushButton";
             useMouseEvents = "0";
-            bitmap = "tools/gui/images/delete";
+            position = "173 2";
+            extent = "15 15";
+            minExtent = "8 2";
+            horizSizing = "left";
+            vertSizing = "top";
+            profile = "ToolsGuiButtonProfile";
+            visible = "1";
+            active = "1";
+            command = "TerrainMaterialDlg.deleteMat();";
+            tooltipProfile = "ToolsGuiToolTipProfile";
+            hovertime = "1000";
+            isContainer = "0";
+            canSave = "1";
+            canSaveDynamicFields = "0";
          };
       };
       new GuiContainer() {
-         canSaveDynamicFields = "0";
-         internalName = "matSettingsParent";
-         isContainer = "1";
-         Profile = "inspectorStyleRolloutProfile";
-         HorizSizing = "left";
-         VertSizing = "height";
+         margin = "0 0 0 0";
+         padding = "0 0 0 0";
+         anchorTop = "1";
+         anchorBottom = "0";
+         anchorLeft = "1";
+         anchorRight = "0";
          position = "202 26";
-         Extent = "185 363";
-         MinExtent = "8 2";
-         canSave = "1";
-         Visible = "1";
-         tooltipprofile = "ToolsGuiToolTipProfile";
+         extent = "185 363";
+         minExtent = "8 2";
+         horizSizing = "left";
+         vertSizing = "height";
+         profile = "inspectorStyleRolloutProfile";
+         visible = "1";
+         active = "1";
+         tooltipProfile = "ToolsGuiToolTipProfile";
          hovertime = "1000";
-         Margin = "0 0 0 0";
-         Padding = "0 0 0 0";
-         AnchorTop = "1";
-         AnchorBottom = "0";
-         AnchorLeft = "1";
-         AnchorRight = "0";
+         isContainer = "1";
+         internalName = "matSettingsParent";
+         canSave = "1";
+         canSaveDynamicFields = "0";
 
-         new GuiBitmapCtrl() {
-            canSaveDynamicFields = "0";
-            isContainer = "0";
-            Profile = "ToolsGuiDefaultProfile";
-            HorizSizing = "width";
-            VertSizing = "bottom";
-            position = "1 0";
-            Extent = "183 2";
-            MinExtent = "8 2";
-            canSave = "1";
-            Visible = "1";
-            tooltipprofile = "ToolsGuiToolTipProfile";
-            hovertime = "1000";
-            bitmap = "core/art/gui/images/separator-v";
-            wrap = "0";
-         };
          new GuiTextCtrl() {
-            canSaveDynamicFields = "0";
-            isContainer = "0";
-            Profile = "ToolsGuiTextProfile";
-            HorizSizing = "right";
-            VertSizing = "bottom";
-            position = "8 22";
-            Extent = "44 17";
-            MinExtent = "8 2";
-            canSave = "1";
-            Visible = "1";
-            tooltipprofile = "ToolsGuiDefaultProfile";
-            hovertime = "1000";
-            Margin = "0 0 0 0";
-            Padding = "0 0 0 0";
-            AnchorTop = "1";
-            AnchorBottom = "0";
-            AnchorLeft = "1";
-            AnchorRight = "0";
             text = "Name";
             maxLength = "1024";
-         };
-         new GuiTextEditCtrl() {
-            internalName = "matNameCtrl";
-            canSaveDynamicFields = "0";
+            margin = "0 0 0 0";
+            padding = "0 0 0 0";
+            anchorTop = "1";
+            anchorBottom = "0";
+            anchorLeft = "1";
+            anchorRight = "0";
+            position = "8 22";
+            extent = "44 17";
+            minExtent = "8 2";
+            horizSizing = "right";
+            vertSizing = "bottom";
+            profile = "ToolsGuiTextProfile";
+            visible = "1";
+            active = "1";
+            tooltipProfile = "ToolsGuiDefaultProfile";
+            hovertime = "1000";
             isContainer = "0";
-            Profile = "ToolsGuiTextEditProfile";
-            HorizSizing = "right";
-            VertSizing = "bottom";
-            position = "39 21";
-            Extent = "143 18";
-            MinExtent = "8 2";
             canSave = "1";
-            Visible = "1";
-            tooltipprofile = "ToolsGuiToolTipProfile";
-            hovertime = "1000";
-            Margin = "0 0 0 0";
-            Padding = "0 0 0 0";
-            AnchorTop = "1";
-            AnchorBottom = "0";
-            AnchorLeft = "1";
-            AnchorRight = "0";
-            maxLength = "1024";
+            canSaveDynamicFields = "0";
+         };
+         new GuiTextEditCtrl() {
             historySize = "0";
-            password = "0";
             tabComplete = "0";
             sinkAllKeyEvents = "0";
+            password = "0";
             passwordMask = "*";
-            altCommand = "TerrainMaterialDlg.setMaterialName( $ThisControl.getText() );";
-         };
-         new GuiTextCtrl() {
-            canSaveDynamicFields = "0";
-            isContainer = "0";
-            Profile = "ToolsGuiInspectorTitleTextProfile";
-            HorizSizing = "right";
-            VertSizing = "bottom";
-            position = "8 0";
-            Extent = "117 16";
-            MinExtent = "8 2";
-            canSave = "1";
-            Visible = "1";
-            tooltipprofile = "ToolsGuiToolTipProfile";
-            hovertime = "1000";
-            Margin = "0 0 0 0";
-            Padding = "0 0 0 0";
-            AnchorTop = "1";
-            AnchorBottom = "0";
-            AnchorLeft = "1";
-            AnchorRight = "0";
-            text = "Material Properties";
+            text = "sand";
             maxLength = "1024";
-         };
-         new GuiContainer() {
-            canSaveDynamicFields = "0";
-            isContainer = "1";
-            Profile = "ToolsGuiDefaultProfile";
-            HorizSizing = "width";
-            VertSizing = "bottom";
-            position = "6 43";
-            Extent = "185 75";
-            MinExtent = "8 2";
-            canSave = "1";
-            Visible = "1";
-            tooltipprofile = "ToolsGuiToolTipProfile";
+            margin = "0 0 0 0";
+            padding = "0 0 0 0";
+            anchorTop = "1";
+            anchorBottom = "0";
+            anchorLeft = "1";
+            anchorRight = "0";
+            position = "39 21";
+            extent = "143 18";
+            minExtent = "8 2";
+            horizSizing = "right";
+            vertSizing = "bottom";
+            profile = "ToolsGuiTextEditProfile";
+            visible = "1";
+            active = "1";
+            altCommand = "TerrainMaterialDlg.setMaterialName( $ThisControl.getText() );";
+            tooltipProfile = "ToolsGuiToolTipProfile";
             hovertime = "1000";
-            Margin = "0 0 0 0";
-            Padding = "0 0 0 0";
-            AnchorTop = "1";
-            AnchorBottom = "0";
-            AnchorLeft = "1";
-            AnchorRight = "0";
-
-            new GuiCheckBoxCtrl() {
-               internalName = "sideProjectionCtrl";
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiCheckBoxProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "55 54";
-               Extent = "119 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               text = " Use Side Projection";
-               groupNum = "-1";
-               buttonType = "ToggleButton";
-               useMouseEvents = "0";
-               useInactiveState = "0";
-            };
-            new GuiBitmapCtrl() {
-               internalName = "baseTexCtrl";
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiDefaultProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "1 1";
-               Extent = "47 47";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               bitmap = "tools/materialEditor/gui/unknownImage";
-               wrap = "0";
-            };
-            new GuiBitmapButtonCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiDefaultProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "1 1";
-               Extent = "48 48";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               Command = "TerrainMaterialDlg.changeBase();";
-               tooltipprofile = "ToolsGuiDefaultProfile";
-               ToolTip = "Change the Active Diffuse Map for this layer";
-               hovertime = "1000";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-               bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
-            };
-            new GuiTextCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "EditorTextProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "56 -3";
-               Extent = "39 18";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "1";
-               AnchorBottom = "0";
-               AnchorLeft = "1";
-               AnchorRight = "0";
-               text = "Diffuse";
-               maxLength = "1024";
-            };
-            new GuiTextCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextProfile";
-               HorizSizing = "width";
-               VertSizing = "bottom";
-               position = "56 16";
-               Extent = "116 17";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "1";
-               AnchorBottom = "0";
-               AnchorLeft = "1";
-               AnchorRight = "0";
-               text = "None";
-               maxLength = "1024";
-            };
-            new GuiButtonCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiButtonProfile";
-               HorizSizing = "left";
-               VertSizing = "bottom";
-               position = "116 0";
-               Extent = "40 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               Command = "TerrainMaterialDlg.changeBase();";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               text = "Edit";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-            };
-            new GuiBitmapButtonCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiDefaultProfile";
-               HorizSizing = "left";
-               VertSizing = "bottom";
-               position = "159 0";
-               Extent = "16 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               Command = "TerrainMaterialDlg-->baseTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-               bitmap = "tools/gui/images/delete";
-            };
-            new GuiTextCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "132 35";
-               Extent = "39 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "0";
-               AnchorBottom = "0";
-               AnchorLeft = "0";
-               AnchorRight = "0";
-               text = "Size";
-               maxLength = "1024";
-            };
-            new GuiTextEditCtrl() {
-               internalName = "baseSizeCtrl";
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextEditProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "94 34";
-               Extent = "34 18";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "0";
-               AnchorBottom = "0";
-               AnchorLeft = "0";
-               AnchorRight = "0";
-               maxLength = "1024";
-               historySize = "0";
-               password = "0";
-               tabComplete = "0";
-               sinkAllKeyEvents = "0";
-               passwordMask = "*";
-            };
-         };
-         new GuiBitmapCtrl() {
-            canSaveDynamicFields = "0";
             isContainer = "0";
-            Profile = "ToolsGuiDefaultProfile";
-            HorizSizing = "width";
-            VertSizing = "bottom";
-            position = "6 116";
-            Extent = "175 2";
-            MinExtent = "8 2";
+            internalName = "matNameCtrl";
             canSave = "1";
-            Visible = "1";
-            tooltipprofile = "ToolsGuiToolTipProfile";
-            hovertime = "1000";
-            bitmap = "tools/gui/images/separator-v";
-            wrap = "0";
-         };
-         new GuiContainer() {
             canSaveDynamicFields = "0";
-            isContainer = "1";
-            Profile = "ToolsGuiDefaultProfile";
-            HorizSizing = "width";
-            VertSizing = "bottom";
-            position = "6 295";
-            Extent = "185 50";
-            MinExtent = "8 2";
-            canSave = "1";
-            Visible = "1";
-            tooltipprofile = "ToolsGuiToolTipProfile";
-            hovertime = "1000";
-            Margin = "0 0 0 0";
-            Padding = "0 0 0 0";
-            AnchorTop = "1";
-            AnchorBottom = "0";
-            AnchorLeft = "1";
-            AnchorRight = "0";
-
-            new GuiBitmapCtrl() {
-               internalName = "normTexCtrl";
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiDefaultProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "1 1";
-               Extent = "47 47";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               bitmap = "tools/materialEditor/gui/unknownImage";
-               wrap = "0";
-            };
-            new GuiTextCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "EditorTextProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "56 -3";
-               Extent = "39 18";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "1";
-               AnchorBottom = "0";
-               AnchorLeft = "1";
-               AnchorRight = "0";
-               text = "Normal";
-               maxLength = "1024";
-            };
-            new GuiBitmapButtonCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiDefaultProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "1 1";
-               Extent = "48 48";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               Command = "TerrainMaterialDlg.changeNormal();";
-               tooltipprofile = "ToolsGuiDefaultProfile";
-               ToolTip = "Change the active Normal Map for this layer.";
-               hovertime = "1000";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-               bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
-            };
-            new GuiTextCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextProfile";
-               HorizSizing = "width";
-               VertSizing = "bottom";
-               position = "56 15";
-               Extent = "116 17";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "1";
-               AnchorBottom = "0";
-               AnchorLeft = "1";
-               AnchorRight = "0";
-               text = "None";
-               maxLength = "1024";
-            };
-            new GuiButtonCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiButtonProfile";
-               HorizSizing = "left";
-               VertSizing = "bottom";
-               position = "116 0";
-               Extent = "40 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               Command = "TerrainMaterialDlg.changeNormal();";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               text = "Edit";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-            };
-            new GuiBitmapButtonCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiDefaultProfile";
-               HorizSizing = "left";
-               VertSizing = "bottom";
-               position = "159 0";
-               Extent = "16 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               Command = "TerrainMaterialDlg-->normTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-               bitmap = "tools/gui/images/delete";
-            };
-            new GuiTextCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "92 34";
-               Extent = "77 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "1";
-               AnchorBottom = "0";
-               AnchorLeft = "1";
-               AnchorRight = "0";
-               text = "Parallax Scale";
-               maxLength = "1024";
-            };
-            new GuiTextEditCtrl() {
-               internalName = "parallaxScaleCtrl";
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextEditProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "55 33";
-               Extent = "34 18";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "0";
-               AnchorBottom = "0";
-               AnchorLeft = "0";
-               AnchorRight = "0";
-               text = "0.00";
-               maxLength = "1024";
-               historySize = "0";
-               password = "0";
-               tabComplete = "0";
-               sinkAllKeyEvents = "0";
-               passwordMask = "*";
-            };
          };
-
-         new GuiBitmapCtrl() {
-            bitmap = "tools/gui/images/separator-v";
-            wrap = "0";
-            position = "6 288";
-            extent = "175 2";
+         new GuiTextCtrl() {
+            text = "Material Properties";
+            maxLength = "1024";
+            margin = "0 0 0 0";
+            padding = "0 0 0 0";
+            anchorTop = "1";
+            anchorBottom = "0";
+            anchorLeft = "1";
+            anchorRight = "0";
+            position = "8 0";
+            extent = "117 16";
             minExtent = "8 2";
-            horizSizing = "width";
+            horizSizing = "right";
             vertSizing = "bottom";
-            profile = "ToolsGuiDefaultProfile";
+            profile = "ToolsGuiInspectorTitleTextProfile";
             visible = "1";
             active = "1";
             tooltipProfile = "ToolsGuiToolTipProfile";
@@ -640,19 +242,27 @@
             canSave = "1";
             canSaveDynamicFields = "0";
          };
-         new GuiContainer() {
+         new GuiScrollCtrl() {
+            willFirstRespond = "1";
+            hScrollBar = "dynamic";
+            vScrollBar = "dynamic";
+            lockHorizScroll = "1";
+            lockVertScroll = "0";
+            constantThumbHeight = "0";
+            childMargin = "0 0";
+            mouseWheelScrollSpeed = "-1";
             margin = "0 0 0 0";
             padding = "0 0 0 0";
             anchorTop = "1";
             anchorBottom = "0";
             anchorLeft = "1";
             anchorRight = "0";
-            position = "6 122";
-            extent = "185 72";
+            position = "0 45";
+            extent = "189 329";
             minExtent = "8 2";
             horizSizing = "width";
-            vertSizing = "bottom";
-            profile = "ToolsGuiDefaultProfile";
+            vertSizing = "height";
+            profile = "ToolsGuiScrollProfile";
             visible = "1";
             active = "1";
             tooltipProfile = "ToolsGuiToolTipProfile";
@@ -661,730 +271,1473 @@
             canSave = "1";
             canSaveDynamicFields = "0";
 
-            new GuiBitmapCtrl() {
-               bitmap = "tools/materialEditor/gui/unknownImage";
-               wrap = "0";
-               position = "1 1";
-               extent = "47 47";
-               minExtent = "8 2";
-               horizSizing = "right";
-               vertSizing = "bottom";
-               profile = "ToolsGuiDefaultProfile";
-               visible = "1";
-               active = "1";
-               tooltipProfile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               isContainer = "0";
-               internalName = "macroTexCtrl";
-               canSave = "1";
-               canSaveDynamicFields = "0";
-            };
-            new GuiBitmapButtonCtrl() {
-               bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
-               bitmapMode = "Stretched";
-               autoFitExtents = "0";
-               useModifiers = "0";
-               useStates = "1";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-               position = "1 1";
-               extent = "48 48";
-               minExtent = "8 2";
-               horizSizing = "right";
-               vertSizing = "bottom";
-               profile = "ToolsGuiDefaultProfile";
-               visible = "1";
-               active = "1";
-               command = "TerrainMaterialDlg.changeMacro();";
-               tooltipProfile = "ToolsGuiDefaultProfile";
-               tooltip = "Change the active Macro Map for this layer.";
-               hovertime = "1000";
-               isContainer = "0";
-               canSave = "1";
-               canSaveDynamicFields = "0";
-            };
-            new GuiTextCtrl() {
-               text = "Macro";
-               maxLength = "1024";
+            new GuiContainer() {
                margin = "0 0 0 0";
                padding = "0 0 0 0";
                anchorTop = "1";
                anchorBottom = "0";
                anchorLeft = "1";
                anchorRight = "0";
-               position = "56 -3";
-               extent = "34 18";
+               position = "1 1";
+               extent = "174 73";
                minExtent = "8 2";
-               horizSizing = "right";
+               horizSizing = "width";
                vertSizing = "bottom";
-               profile = "EditorTextProfile";
+               profile = "ToolsGuiDefaultProfile";
                visible = "1";
                active = "1";
                tooltipProfile = "ToolsGuiToolTipProfile";
                hovertime = "1000";
-               isContainer = "0";
+               isContainer = "1";
                canSave = "1";
                canSaveDynamicFields = "0";
+
+               new GuiCheckBoxCtrl() {
+                  text = " Use Side Projection";
+                  groupNum = "-1";
+                  buttonType = "ToggleButton";
+                  useMouseEvents = "0";
+                  position = "55 54";
+                  extent = "119 16";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiCheckBoxProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "sideProjectionCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapCtrl() {
+                  bitmap = "art/terrains/Example/sand";
+                  color = "255 255 255 255";
+                  wrap = "0";
+                  position = "1 1";
+                  extent = "47 47";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "baseTexCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapButtonCtrl() {
+                  bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
+                  bitmapMode = "Stretched";
+                  autoFitExtents = "0";
+                  useModifiers = "0";
+                  useStates = "1";
+                  masked = "0";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "1 1";
+                  extent = "48 48";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg.changeBase();";
+                  tooltipProfile = "ToolsGuiDefaultProfile";
+                  tooltip = "Change the Active Diffuse Map for this layer";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Diffuse";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "1";
+                  anchorBottom = "0";
+                  anchorLeft = "1";
+                  anchorRight = "0";
+                  position = "56 -3";
+                  extent = "39 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "EditorTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "None";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "1";
+                  anchorBottom = "0";
+                  anchorLeft = "1";
+                  anchorRight = "0";
+                  position = "56 16";
+                  extent = "79 17";
+                  minExtent = "8 2";
+                  horizSizing = "width";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiButtonCtrl() {
+                  text = "Edit";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "110 0";
+                  extent = "40 16";
+                  minExtent = "8 2";
+                  horizSizing = "left";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiButtonProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg.changeBase();";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapButtonCtrl() {
+                  bitmap = "tools/gui/images/delete";
+                  bitmapMode = "Stretched";
+                  autoFitExtents = "0";
+                  useModifiers = "0";
+                  useStates = "1";
+                  masked = "0";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "153 0";
+                  extent = "16 16";
+                  minExtent = "8 2";
+                  horizSizing = "left";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg-->baseTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Size";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "132 35";
+                  extent = "39 16";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextEditCtrl() {
+                  historySize = "0";
+                  tabComplete = "0";
+                  sinkAllKeyEvents = "0";
+                  password = "0";
+                  passwordMask = "*";
+                  text = "200";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "94 34";
+                  extent = "34 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextEditProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "baseSizeCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapCtrl() {
+                  bitmap = "tools/gui/images/separator-v";
+                  color = "255 255 255 255";
+                  wrap = "0";
+                  position = "1 71";
+                  extent = "148 2";
+                  minExtent = "8 2";
+                  horizSizing = "width";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
             };
-            new GuiTextCtrl() {
-               text = "None";
-               maxLength = "1024";
+            new GuiContainer() {
                margin = "0 0 0 0";
                padding = "0 0 0 0";
                anchorTop = "1";
                anchorBottom = "0";
                anchorLeft = "1";
                anchorRight = "0";
-               position = "56 17";
-               extent = "116 17";
+               position = "4 78";
+               extent = "174 76";
                minExtent = "8 2";
                horizSizing = "width";
                vertSizing = "bottom";
-               profile = "ToolsGuiTextProfile";
-               visible = "1";
-               active = "1";
-               tooltipProfile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               isContainer = "0";
-               canSave = "1";
-               canSaveDynamicFields = "0";
-            };
-            new GuiButtonCtrl() {
-               text = "Edit";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-               position = "116 0";
-               extent = "40 16";
-               minExtent = "8 2";
-               horizSizing = "left";
-               vertSizing = "bottom";
-               profile = "ToolsGuiButtonProfile";
-               visible = "1";
-               active = "1";
-               command = "TerrainMaterialDlg.changeMacro();";
-               tooltipProfile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               isContainer = "0";
-               canSave = "1";
-               canSaveDynamicFields = "0";
-            };
-            new GuiBitmapButtonCtrl() {
-               bitmap = "tools/gui/images/delete";
-               bitmapMode = "Stretched";
-               autoFitExtents = "0";
-               useModifiers = "0";
-               useStates = "1";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-               position = "159 0";
-               extent = "16 16";
-               minExtent = "8 2";
-               horizSizing = "left";
-               vertSizing = "bottom";
                profile = "ToolsGuiDefaultProfile";
                visible = "1";
                active = "1";
-               command = "TerrainMaterialDlg-->macroTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
-               tooltipProfile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               isContainer = "0";
-               canSave = "1";
-               canSaveDynamicFields = "0";
-            };
-            new GuiTextCtrl() {
-               text = "Size";
-               maxLength = "1024";
-               margin = "0 0 0 0";
-               padding = "0 0 0 0";
-               anchorTop = "0";
-               anchorBottom = "0";
-               anchorLeft = "0";
-               anchorRight = "0";
-               position = "132 33";
-               extent = "39 16";
-               minExtent = "8 2";
-               horizSizing = "right";
-               vertSizing = "bottom";
-               profile = "ToolsGuiTextProfile";
-               visible = "1";
-               active = "1";
-               tooltipProfile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               isContainer = "0";
-               canSave = "1";
-               canSaveDynamicFields = "0";
-            };
-            new GuiTextEditCtrl() {
-               historySize = "0";
-               tabComplete = "0";
-               sinkAllKeyEvents = "0";
-               password = "0";
-               passwordMask = "*";
-               maxLength = "1024";
-               margin = "0 0 0 0";
-               padding = "0 0 0 0";
-               anchorTop = "0";
-               anchorBottom = "0";
-               anchorLeft = "0";
-               anchorRight = "0";
-               position = "94 32";
-               extent = "34 18";
-               minExtent = "8 2";
-               horizSizing = "right";
-               vertSizing = "bottom";
-               profile = "ToolsGuiTextEditProfile";
-               visible = "1";
-               active = "1";
                tooltipProfile = "ToolsGuiToolTipProfile";
                hovertime = "1000";
-               isContainer = "0";
-               internalName = "macroSizeCtrl";
-               canSave = "1";
-               canSaveDynamicFields = "0";
-            };
-            new GuiTextCtrl() {
-               text = "Strength";
-               maxLength = "1024";
-               margin = "0 0 0 0";
-               padding = "0 0 0 0";
-               anchorTop = "0";
-               anchorBottom = "0";
-               anchorLeft = "0";
-               anchorRight = "0";
-               position = "39 54";
-               extent = "46 16";
-               minExtent = "8 2";
-               horizSizing = "right";
-               vertSizing = "bottom";
-               profile = "ToolsGuiTextProfile";
-               visible = "1";
-               active = "1";
-               tooltipProfile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               isContainer = "0";
+               isContainer = "1";
                canSave = "1";
                canSaveDynamicFields = "0";
+
+               new GuiBitmapCtrl() {
+                  bitmap = "art/terrains/Example/sand_d";
+                  color = "255 255 255 255";
+                  wrap = "0";
+                  position = "1 1";
+                  extent = "47 47";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "detailTexCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapButtonCtrl() {
+                  bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
+                  bitmapMode = "Stretched";
+                  autoFitExtents = "0";
+                  useModifiers = "0";
+                  useStates = "1";
+                  masked = "0";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "1 1";
+                  extent = "48 48";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg.changeDetail();";
+                  tooltipProfile = "ToolsGuiDefaultProfile";
+                  tooltip = "Change the active Detail Map for this layer.";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Detail";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "1";
+                  anchorBottom = "0";
+                  anchorLeft = "1";
+                  anchorRight = "0";
+                  position = "56 -3";
+                  extent = "30 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "EditorTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "None";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "1";
+                  anchorBottom = "0";
+                  anchorLeft = "1";
+                  anchorRight = "0";
+                  position = "56 16";
+                  extent = "79 17";
+                  minExtent = "8 2";
+                  horizSizing = "width";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiButtonCtrl() {
+                  text = "Edit";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "111 0";
+                  extent = "40 16";
+                  minExtent = "8 2";
+                  horizSizing = "left";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiButtonProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg.changeDetail();";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapButtonCtrl() {
+                  bitmap = "tools/gui/images/delete";
+                  bitmapMode = "Stretched";
+                  autoFitExtents = "0";
+                  useModifiers = "0";
+                  useStates = "1";
+                  masked = "0";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "154 0";
+                  extent = "16 16";
+                  minExtent = "8 2";
+                  horizSizing = "left";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg-->detailTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Size";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "132 33";
+                  extent = "39 16";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextEditCtrl() {
+                  historySize = "0";
+                  tabComplete = "0";
+                  sinkAllKeyEvents = "0";
+                  password = "0";
+                  passwordMask = "*";
+                  text = "10";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "94 32";
+                  extent = "34 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextEditProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "detSizeCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Strength";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "39 54";
+                  extent = "46 16";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextEditCtrl() {
+                  historySize = "0";
+                  tabComplete = "0";
+                  sinkAllKeyEvents = "0";
+                  password = "0";
+                  passwordMask = "*";
+                  text = "1";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "1 53";
+                  extent = "34 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextEditProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "detStrengthCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Distance";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "132 54";
+                  extent = "45 16";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextEditCtrl() {
+                  historySize = "0";
+                  tabComplete = "0";
+                  sinkAllKeyEvents = "0";
+                  password = "0";
+                  passwordMask = "*";
+                  text = "100";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "94 53";
+                  extent = "34 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextEditProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "detDistanceCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapCtrl() {
+                  bitmap = "core/art/gui/images/separator-v";
+                  color = "255 255 255 255";
+                  wrap = "0";
+                  position = "1 73";
+                  extent = "148 2";
+                  minExtent = "8 2";
+                  horizSizing = "width";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
             };
-            new GuiTextEditCtrl() {
-               historySize = "0";
-               tabComplete = "0";
-               sinkAllKeyEvents = "0";
-               password = "0";
-               passwordMask = "*";
-               maxLength = "1024";
+            new GuiContainer() {
                margin = "0 0 0 0";
                padding = "0 0 0 0";
-               anchorTop = "0";
+               anchorTop = "1";
                anchorBottom = "0";
-               anchorLeft = "0";
+               anchorLeft = "1";
                anchorRight = "0";
-               position = "1 53";
-               extent = "34 18";
+               position = "6 158";
+               extent = "174 54";
                minExtent = "8 2";
-               horizSizing = "right";
+               horizSizing = "width";
                vertSizing = "bottom";
-               profile = "ToolsGuiTextEditProfile";
+               profile = "ToolsGuiDefaultProfile";
                visible = "1";
                active = "1";
                tooltipProfile = "ToolsGuiToolTipProfile";
                hovertime = "1000";
-               isContainer = "0";
-               internalName = "macroStrengthCtrl";
+               isContainer = "1";
                canSave = "1";
                canSaveDynamicFields = "0";
+
+               new GuiBitmapCtrl() {
+                  bitmap = "tools/materialEditor/gui/unknownImage";
+                  color = "255 255 255 255";
+                  wrap = "0";
+                  position = "1 1";
+                  extent = "47 47";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "normTexCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Normal";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "1";
+                  anchorBottom = "0";
+                  anchorLeft = "1";
+                  anchorRight = "0";
+                  position = "56 -3";
+                  extent = "39 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "EditorTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapButtonCtrl() {
+                  bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
+                  bitmapMode = "Stretched";
+                  autoFitExtents = "0";
+                  useModifiers = "0";
+                  useStates = "1";
+                  masked = "0";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "1 1";
+                  extent = "48 48";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg.changeNormal();";
+                  tooltipProfile = "ToolsGuiDefaultProfile";
+                  tooltip = "Change the active Normal Map for this layer.";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "None";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "1";
+                  anchorBottom = "0";
+                  anchorLeft = "1";
+                  anchorRight = "0";
+                  position = "56 15";
+                  extent = "79 17";
+                  minExtent = "8 2";
+                  horizSizing = "width";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiButtonCtrl() {
+                  text = "Edit";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "111 0";
+                  extent = "40 16";
+                  minExtent = "8 2";
+                  horizSizing = "left";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiButtonProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg.changeNormal();";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapButtonCtrl() {
+                  bitmap = "tools/gui/images/delete";
+                  bitmapMode = "Stretched";
+                  autoFitExtents = "0";
+                  useModifiers = "0";
+                  useStates = "1";
+                  masked = "0";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "154 0";
+                  extent = "16 16";
+                  minExtent = "8 2";
+                  horizSizing = "left";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg-->normTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Parallax Scale";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "1";
+                  anchorBottom = "0";
+                  anchorLeft = "1";
+                  anchorRight = "0";
+                  position = "92 34";
+                  extent = "77 16";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextEditCtrl() {
+                  historySize = "0";
+                  tabComplete = "0";
+                  sinkAllKeyEvents = "0";
+                  password = "0";
+                  passwordMask = "*";
+                  text = "0";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "55 33";
+                  extent = "34 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextEditProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "parallaxScaleCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapCtrl() {
+                  bitmap = "tools/gui/images/separator-v";
+                  color = "255 255 255 255";
+                  wrap = "0";
+                  position = "-10 53";
+                  extent = "148 2";
+                  minExtent = "8 2";
+                  horizSizing = "width";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
             };
-            new GuiTextCtrl() {
-               text = "Distance";
-               maxLength = "1024";
+            new GuiContainer() {
                margin = "0 0 0 0";
                padding = "0 0 0 0";
-               anchorTop = "0";
+               anchorTop = "1";
                anchorBottom = "0";
-               anchorLeft = "0";
+               anchorLeft = "1";
                anchorRight = "0";
-               position = "132 54";
-               extent = "45 16";
+               position = "6 216";
+               extent = "174 53";
                minExtent = "8 2";
-               horizSizing = "right";
+               horizSizing = "width";
                vertSizing = "bottom";
-               profile = "ToolsGuiTextProfile";
+               profile = "ToolsGuiDefaultProfile";
                visible = "1";
                active = "1";
                tooltipProfile = "ToolsGuiToolTipProfile";
                hovertime = "1000";
-               isContainer = "0";
+               isContainer = "1";
                canSave = "1";
                canSaveDynamicFields = "0";
+
+               new GuiBitmapCtrl() {
+                  bitmap = "tools/materialEditor/gui/unknownImage";
+                  color = "255 255 255 255";
+                  wrap = "0";
+                  position = "1 1";
+                  extent = "47 47";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "compositeTexCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapButtonCtrl() {
+                  bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
+                  bitmapMode = "Stretched";
+                  autoFitExtents = "0";
+                  useModifiers = "0";
+                  useStates = "1";
+                  masked = "0";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "1 1";
+                  extent = "48 48";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg.changecomposite();";
+                  tooltipProfile = "ToolsGuiDefaultProfile";
+                  tooltip = "Change the active composite Map for this layer.";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Composite";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "1";
+                  anchorBottom = "0";
+                  anchorLeft = "1";
+                  anchorRight = "0";
+                  position = "56 -3";
+                  extent = "61 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "EditorTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "None";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "1";
+                  anchorBottom = "0";
+                  anchorLeft = "1";
+                  anchorRight = "0";
+                  position = "56 17";
+                  extent = "79 17";
+                  minExtent = "8 2";
+                  horizSizing = "width";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiButtonCtrl() {
+                  text = "Edit";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "111 0";
+                  extent = "40 16";
+                  minExtent = "8 2";
+                  horizSizing = "left";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiButtonProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg.changecomposite();";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapButtonCtrl() {
+                  bitmap = "tools/gui/images/delete";
+                  bitmapMode = "Stretched";
+                  autoFitExtents = "0";
+                  useModifiers = "0";
+                  useStates = "1";
+                  masked = "0";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "154 0";
+                  extent = "16 16";
+                  minExtent = "8 2";
+                  horizSizing = "left";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg-->compositeTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapCtrl() {
+                  bitmap = "tools/gui/images/separator-v";
+                  color = "255 255 255 255";
+                  wrap = "0";
+                  position = "4 51";
+                  extent = "148 2";
+                  minExtent = "8 2";
+                  horizSizing = "width";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
             };
-            new GuiTextEditCtrl() {
-               historySize = "0";
-               tabComplete = "0";
-               sinkAllKeyEvents = "0";
-               password = "0";
-               passwordMask = "*";
-               maxLength = "1024";
+            new GuiContainer() {
                margin = "0 0 0 0";
                padding = "0 0 0 0";
-               anchorTop = "0";
+               anchorTop = "1";
                anchorBottom = "0";
-               anchorLeft = "0";
+               anchorLeft = "1";
                anchorRight = "0";
-               position = "94 53";
-               extent = "34 18";
+               position = "6 272";
+               extent = "174 71";
                minExtent = "8 2";
-               horizSizing = "right";
+               horizSizing = "width";
                vertSizing = "bottom";
-               profile = "ToolsGuiTextEditProfile";
+               profile = "ToolsGuiDefaultProfile";
                visible = "1";
                active = "1";
                tooltipProfile = "ToolsGuiToolTipProfile";
                hovertime = "1000";
-               isContainer = "0";
-               internalName = "macroDistanceCtrl";
+               isContainer = "1";
                canSave = "1";
                canSaveDynamicFields = "0";
-            };
-         };
-         
-         new GuiBitmapCtrl() {
-            canSaveDynamicFields = "0";
-            isContainer = "0";
-            Profile = "ToolsGuiDefaultProfile";
-            HorizSizing = "width";
-            VertSizing = "bottom";
-            position = "6 200";
-            Extent = "175 2";
-            MinExtent = "8 2";
-            canSave = "1";
-            Visible = "1";
-            tooltipprofile = "ToolsGuiToolTipProfile";
-            hovertime = "1000";
-            bitmap = "tools/gui/images/separator-v";
-            wrap = "0";
-         };
-         new GuiContainer() {
-            canSaveDynamicFields = "0";
-            isContainer = "1";
-            Profile = "ToolsGuiDefaultProfile";
-            HorizSizing = "width";
-            VertSizing = "bottom";
-            position = "6 206";
-            Extent = "185 72";
-            MinExtent = "8 2";
-            canSave = "1";
-            Visible = "1";
-            tooltipprofile = "ToolsGuiToolTipProfile";
-            hovertime = "1000";
-            Margin = "0 0 0 0";
-            Padding = "0 0 0 0";
-            AnchorTop = "1";
-            AnchorBottom = "0";
-            AnchorLeft = "1";
-            AnchorRight = "0";
 
-            new GuiBitmapCtrl() {
-               internalName = "detailTexCtrl";
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiDefaultProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "1 1";
-               Extent = "47 47";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               bitmap = "tools/materialEditor/gui/unknownImage";
-               wrap = "0";
-            };
-            new GuiBitmapButtonCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiDefaultProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "1 1";
-               Extent = "48 48";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               Command = "TerrainMaterialDlg.changeDetail();";
-               tooltipprofile = "ToolsGuiDefaultProfile";
-               ToolTip = "Change the active Detail Map for this layer.";
-               hovertime = "1000";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-               bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
-            };
-            new GuiTextCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "EditorTextProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "56 -3";
-               Extent = "30 18";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "1";
-               AnchorBottom = "0";
-               AnchorLeft = "1";
-               AnchorRight = "0";
-               text = "Detail";
-               maxLength = "1024";
-            };
-            new GuiTextCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextProfile";
-               HorizSizing = "width";
-               VertSizing = "bottom";
-               position = "56 16";
-               Extent = "116 17";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "1";
-               AnchorBottom = "0";
-               AnchorLeft = "1";
-               AnchorRight = "0";
-               text = "None";
-               maxLength = "1024";
-            };
-            new GuiButtonCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiButtonProfile";
-               HorizSizing = "left";
-               VertSizing = "bottom";
-               position = "116 0";
-               Extent = "40 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               Command = "TerrainMaterialDlg.changeDetail();";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               text = "Edit";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-            };
-            new GuiBitmapButtonCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiDefaultProfile";
-               HorizSizing = "left";
-               VertSizing = "bottom";
-               position = "159 0";
-               Extent = "16 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               Command = "TerrainMaterialDlg-->detailTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               groupNum = "-1";
-               buttonType = "PushButton";
-               useMouseEvents = "0";
-               bitmap = "tools/gui/images/delete";
-            };
-            new GuiTextCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "132 33";
-               Extent = "39 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "0";
-               AnchorBottom = "0";
-               AnchorLeft = "0";
-               AnchorRight = "0";
-               text = "Size";
-               maxLength = "1024";
-            };
-            new GuiTextEditCtrl() {
-               internalName = "detSizeCtrl";
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextEditProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "94 32";
-               Extent = "34 18";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "0";
-               AnchorBottom = "0";
-               AnchorLeft = "0";
-               AnchorRight = "0";
-               maxLength = "1024";
-               historySize = "0";
-               password = "0";
-               tabComplete = "0";
-               sinkAllKeyEvents = "0";
-               passwordMask = "*";
-            };
-            new GuiTextCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "39 54";
-               Extent = "46 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "0";
-               AnchorBottom = "0";
-               AnchorLeft = "0";
-               AnchorRight = "0";
-               text = "Strength";
-               maxLength = "1024";
-            };
-            new GuiTextEditCtrl() {
-               internalName = "detStrengthCtrl";
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextEditProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "1 53";
-               Extent = "34 18";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "0";
-               AnchorBottom = "0";
-               AnchorLeft = "0";
-               AnchorRight = "0";
-               maxLength = "1024";
-               historySize = "0";
-               password = "0";
-               tabComplete = "0";
-               sinkAllKeyEvents = "0";
-               passwordMask = "*";
-            };
-            new GuiTextCtrl() {
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "132 54";
-               Extent = "45 16";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "0";
-               AnchorBottom = "0";
-               AnchorLeft = "0";
-               AnchorRight = "0";
-               text = "Distance";
-               maxLength = "1024";
-            };
-            new GuiTextEditCtrl() {
-               internalName = "detDistanceCtrl";
-               canSaveDynamicFields = "0";
-               isContainer = "0";
-               Profile = "ToolsGuiTextEditProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "94 53";
-               Extent = "34 18";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
-               Margin = "0 0 0 0";
-               Padding = "0 0 0 0";
-               AnchorTop = "0";
-               AnchorBottom = "0";
-               AnchorLeft = "0";
-               AnchorRight = "0";
-               maxLength = "1024";
-               historySize = "0";
-               password = "0";
-               tabComplete = "0";
-               sinkAllKeyEvents = "0";
-               passwordMask = "*";
+               new GuiBitmapCtrl() {
+                  bitmap = "tools/materialEditor/gui/unknownImage";
+                  color = "255 255 255 255";
+                  wrap = "0";
+                  position = "1 1";
+                  extent = "47 47";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "macroTexCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapButtonCtrl() {
+                  bitmap = "tools/materialEditor/gui/cubemapBtnBorder";
+                  bitmapMode = "Stretched";
+                  autoFitExtents = "0";
+                  useModifiers = "0";
+                  useStates = "1";
+                  masked = "0";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "1 1";
+                  extent = "48 48";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg.changeMacro();";
+                  tooltipProfile = "ToolsGuiDefaultProfile";
+                  tooltip = "Change the active Macro Map for this layer.";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Macro";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "1";
+                  anchorBottom = "0";
+                  anchorLeft = "1";
+                  anchorRight = "0";
+                  position = "56 -3";
+                  extent = "34 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "EditorTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "None";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "1";
+                  anchorBottom = "0";
+                  anchorLeft = "1";
+                  anchorRight = "0";
+                  position = "56 17";
+                  extent = "111 17";
+                  minExtent = "8 2";
+                  horizSizing = "width";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiButtonCtrl() {
+                  text = "Edit";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "111 0";
+                  extent = "40 16";
+                  minExtent = "8 2";
+                  horizSizing = "left";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiButtonProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg.changeMacro();";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiBitmapButtonCtrl() {
+                  bitmap = "tools/gui/images/delete";
+                  bitmapMode = "Stretched";
+                  autoFitExtents = "0";
+                  useModifiers = "0";
+                  useStates = "1";
+                  masked = "0";
+                  groupNum = "-1";
+                  buttonType = "PushButton";
+                  useMouseEvents = "0";
+                  position = "154 0";
+                  extent = "16 16";
+                  minExtent = "8 2";
+                  horizSizing = "left";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiDefaultProfile";
+                  visible = "1";
+                  active = "1";
+                  command = "TerrainMaterialDlg-->macroTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Size";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "132 33";
+                  extent = "39 16";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextEditCtrl() {
+                  historySize = "0";
+                  tabComplete = "0";
+                  sinkAllKeyEvents = "0";
+                  password = "0";
+                  passwordMask = "*";
+                  text = "200";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "94 32";
+                  extent = "34 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextEditProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "macroSizeCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Strength";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "39 54";
+                  extent = "46 16";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextEditCtrl() {
+                  historySize = "0";
+                  tabComplete = "0";
+                  sinkAllKeyEvents = "0";
+                  password = "0";
+                  passwordMask = "*";
+                  text = "0.7";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "1 53";
+                  extent = "34 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextEditProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "macroStrengthCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextCtrl() {
+                  text = "Distance";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "132 54";
+                  extent = "45 16";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
+               new GuiTextEditCtrl() {
+                  historySize = "0";
+                  tabComplete = "0";
+                  sinkAllKeyEvents = "0";
+                  password = "0";
+                  passwordMask = "*";
+                  text = "500";
+                  maxLength = "1024";
+                  margin = "0 0 0 0";
+                  padding = "0 0 0 0";
+                  anchorTop = "0";
+                  anchorBottom = "0";
+                  anchorLeft = "0";
+                  anchorRight = "0";
+                  position = "94 53";
+                  extent = "34 18";
+                  minExtent = "8 2";
+                  horizSizing = "right";
+                  vertSizing = "bottom";
+                  profile = "ToolsGuiTextEditProfile";
+                  visible = "1";
+                  active = "1";
+                  tooltipProfile = "ToolsGuiToolTipProfile";
+                  hovertime = "1000";
+                  isContainer = "0";
+                  internalName = "macroDistanceCtrl";
+                  canSave = "1";
+                  canSaveDynamicFields = "0";
+               };
             };
          };
       };
       new GuiControl() {
-         canSaveDynamicFields = "0";
-         isContainer = "1";
-         Profile = "ToolsGuiDefaultProfile";
-         HorizSizing = "width";
-         VertSizing = "height";
          position = "6 42";
-         Extent = "189 373";
-         MinExtent = "8 2";
-         canSave = "1";
-         Visible = "1";
-         tooltipprofile = "ToolsGuiToolTipProfile";
+         extent = "189 373";
+         minExtent = "8 2";
+         horizSizing = "width";
+         vertSizing = "height";
+         profile = "ToolsGuiDefaultProfile";
+         visible = "1";
+         active = "1";
+         tooltipProfile = "ToolsGuiToolTipProfile";
          hovertime = "1000";
+         isContainer = "1";
+         canSave = "1";
+         canSaveDynamicFields = "0";
 
          new GuiScrollCtrl() {
-            canSaveDynamicFields = "0";
-            isContainer = "1";
-            Profile = "ToolsGuiScrollProfile";
-            HorizSizing = "width";
-            VertSizing = "height";
-            position = "0 0";
-            Extent = "189 374";
-            MinExtent = "8 2";
-            canSave = "1";
-            Visible = "1";
-            tooltipprofile = "ToolsGuiToolTipProfile";
-            hovertime = "1000";
-            Margin = "0 0 0 0";
-            Padding = "0 0 0 0";
-            AnchorTop = "1";
-            AnchorBottom = "0";
-            AnchorLeft = "1";
-            AnchorRight = "0";
             willFirstRespond = "1";
             hScrollBar = "dynamic";
             vScrollBar = "dynamic";
-            lockHorizScroll = "false";
-            lockVertScroll = "false";
+            lockHorizScroll = "0";
+            lockVertScroll = "0";
             constantThumbHeight = "0";
             childMargin = "0 0";
             mouseWheelScrollSpeed = "-1";
+            margin = "0 0 0 0";
+            padding = "0 0 0 0";
+            anchorTop = "1";
+            anchorBottom = "0";
+            anchorLeft = "1";
+            anchorRight = "0";
+            position = "0 -1";
+            extent = "189 374";
+            minExtent = "8 2";
+            horizSizing = "width";
+            vertSizing = "height";
+            profile = "ToolsGuiScrollProfile";
+            visible = "1";
+            active = "1";
+            tooltipProfile = "ToolsGuiToolTipProfile";
+            hovertime = "1000";
+            isContainer = "1";
+            canSave = "1";
+            canSaveDynamicFields = "0";
 
             new GuiTreeViewCtrl() {
-               internalName = "matLibTree";
-               canSaveDynamicFields = "0";
-               class = "TerrainMaterialTreeCtrl";
-               className = "TerrainMaterialTreeCtrl";
-               isContainer = "1";
-               Profile = "ToolsGuiTreeViewProfile";
-               HorizSizing = "right";
-               VertSizing = "bottom";
-               position = "1 1";
-               Extent = "125 84";
-               MinExtent = "8 2";
-               canSave = "1";
-               Visible = "1";
-               tooltipprofile = "ToolsGuiToolTipProfile";
-               hovertime = "1000";
                tabSize = "16";
                textOffset = "2";
                fullRowSelect = "0";
                itemHeight = "21";
                destroyTreeOnSleep = "1";
-               MouseDragging = "0";
-               MultipleSelections = "0";
-               DeleteObjectAllowed = "0";
-               DragToItemAllowed = "0";
-               ClearAllOnSingleSelection = "1";
+               mouseDragging = "0";
+               multipleSelections = "0";
+               deleteObjectAllowed = "0";
+               dragToItemAllowed = "0";
+               clearAllOnSingleSelection = "1";
                showRoot = "0";
-               internalNamesOnly = "1";
-               objectNamesOnly = "0";
+               useInspectorTooltips = "0";
+               tooltipOnWidthOnly = "0";
+               showObjectIds = "0";
+               showClassNames = "0";
+               showObjectNames = "0";
+               showInternalNames = "1";
+               showClassNameForUnnamedObjects = "0";
+               compareToObjectID = "1";
+               canRenameObjects = "1";
+               renameInternal = "0";
+               position = "1 1";
+               extent = "109 147";
+               minExtent = "8 2";
+               horizSizing = "right";
+               vertSizing = "bottom";
+               profile = "ToolsGuiTreeViewProfile";
+               visible = "1";
+               active = "1";
+               tooltipProfile = "ToolsGuiToolTipProfile";
+               hovertime = "1000";
+               isContainer = "1";
+               internalName = "matLibTree";
+               class = "TerrainMaterialTreeCtrl";
+               canSave = "1";
+               canSaveDynamicFields = "0";
             };
          };
       };
       new GuiButtonCtrl() {
-         canSaveDynamicFields = "0";
-         isContainer = "0";
-         Profile = "ToolsGuiButtonProfile";
-         HorizSizing = "left";
-         VertSizing = "top";
-         position = "202 394";
-         Extent = "98 22";
-         MinExtent = "8 2";
-         canSave = "1";
-         Visible = "1";
-         Command = "TerrainMaterialDlg.dialogApply();";
-         tooltipprofile = "ToolsGuiToolTipProfile";
-         hovertime = "1000";
          text = "Apply&Select";
          groupNum = "-1";
          buttonType = "PushButton";
          useMouseEvents = "0";
-      };
-      new GuiButtonCtrl() {
-         canSaveDynamicFields = "0";
+         position = "202 394";
+         extent = "98 22";
+         minExtent = "8 2";
+         horizSizing = "left";
+         vertSizing = "top";
+         profile = "ToolsGuiButtonProfile";
+         visible = "1";
+         active = "1";
+         command = "TerrainMaterialDlg.dialogApply();";
+         tooltipProfile = "ToolsGuiToolTipProfile";
+         hovertime = "1000";
          isContainer = "0";
-         Profile = "ToolsGuiButtonProfile";
-         HorizSizing = "left";
-         VertSizing = "top";
-         position = "307 394";
-         Extent = "80 22";
-         MinExtent = "8 2";
          canSave = "1";
-         Visible = "1";
-         Command = "TerrainMaterialDlg.dialogCancel();";
-         tooltipprofile = "ToolsGuiToolTipProfile";
-         hovertime = "1000";
+         canSaveDynamicFields = "0";
+      };
+      new GuiButtonCtrl() {
          text = "Cancel";
          groupNum = "-1";
          buttonType = "PushButton";
          useMouseEvents = "0";
+         position = "307 394";
+         extent = "80 22";
+         minExtent = "8 2";
+         horizSizing = "left";
+         vertSizing = "top";
+         profile = "ToolsGuiButtonProfile";
+         visible = "1";
+         active = "1";
+         command = "TerrainMaterialDlg.dialogCancel();";
+         tooltipProfile = "ToolsGuiToolTipProfile";
+         hovertime = "1000";
+         isContainer = "0";
+         canSave = "1";
+         canSaveDynamicFields = "0";
       };
-      
-      new GuiBitmapCtrl() { // inactive overlay
-         internalName = "inactiveOverlay";
-         Profile = "ToolsGuiDefaultProfile";
-         HorizSizing = "left";
-         VertSizing = "height";
-         position = "199 23";
-         Extent = "190 267";
-         isContainer = true;
-         Visible = false;
+      new GuiBitmapCtrl() {
          bitmap = "tools/gui/images/inactive-overlay";
-         
-         new GuiTextCtrl(){
-            internalName = "inactiveOverlayDlg";
-            Profile = "ToolsGuiTextCenterProfile";
-            HorizSizing = "width";
-            VertSizing = "center";
-            position = "0 104";
-            Extent = "190 64";
+         color = "255 255 255 255";
+         wrap = "0";
+         position = "199 23";
+         extent = "190 267";
+         minExtent = "8 2";
+         horizSizing = "left";
+         vertSizing = "height";
+         profile = "ToolsGuiDefaultProfile";
+         visible = "0";
+         active = "1";
+         tooltipProfile = "GuiToolTipProfile";
+         hovertime = "1000";
+         isContainer = "1";
+         internalName = "inactiveOverlay";
+         hidden = "1";
+         canSave = "1";
+         canSaveDynamicFields = "0";
+
+         new GuiTextCtrl() {
             text = "Inactive";
+            maxLength = "1024";
+            margin = "0 0 0 0";
+            padding = "0 0 0 0";
+            anchorTop = "1";
+            anchorBottom = "0";
+            anchorLeft = "1";
+            anchorRight = "0";
+            position = "0 104";
+            extent = "190 64";
+            minExtent = "8 2";
+            horizSizing = "width";
+            vertSizing = "center";
+            profile = "ToolsGuiTextCenterProfile";
+            visible = "1";
+            active = "1";
+            tooltipProfile = "GuiToolTipProfile";
+            hovertime = "1000";
+            isContainer = "1";
+            internalName = "inactiveOverlayDlg";
+            canSave = "1";
+            canSaveDynamicFields = "0";
          };
       };
    };

+ 18 - 0
Templates/Full/game/tools/worldEditor/gui/objectBuilderGui.ed.gui

@@ -877,6 +877,24 @@ function ObjectBuilderGui::buildParticleSimulation(%this)
 	%this.process();
 }
 
+function ObjectBuilderGui::buildReflectionProbe(%this)
+{
+   %this.objectClassName = "ReflectionProbe";
+   %this.process();
+   
+   %defaultPath = filePath($Server::MissionFile) @ "/" @ fileBase($Server::MissionFile) @ "/probes/";   
+   %this.addField("reflectionPath", "TypeFilepath", "reflectionPath", %defaultPath);
+}
+
+function ObjectBuilderGui::buildSkylight(%this)
+{
+   %this.objectClassName = "Skylight";
+   %this.process();
+   
+   %defaultPath = filePath($Server::MissionFile) @ "/" @ fileBase($Server::MissionFile) @ "/probes/";   
+   %this.addField("reflectionPath", "TypeFilepath", "reflectionPath", %defaultPath);
+}
+
 //------------------------------------------------------------------------------
 // Mission
 //------------------------------------------------------------------------------

+ 192 - 0
Templates/Full/game/tools/worldEditor/gui/probeBakeDlg.gui

@@ -0,0 +1,192 @@
+//--- OBJECT WRITE BEGIN ---
+%guiContent = new GuiControl(ProbeBakeDlg) {
+   position = "0 0";
+   extent = "1024 768";
+   minExtent = "8 2";
+   horizSizing = "right";
+   vertSizing = "bottom";
+   profile = "GuiDefaultProfile";
+   visible = "1";
+   active = "1";
+   tooltipProfile = "GuiToolTipProfile";
+   hovertime = "1000";
+   isContainer = "1";
+   canSave = "1";
+   canSaveDynamicFields = "1";
+
+   new GuiWindowCtrl() {
+      text = "Update Environment Probes";
+      resizeWidth = "0";
+      resizeHeight = "0";
+      canMove = "1";
+      canClose = "1";
+      canMinimize = "0";
+      canMaximize = "0";
+      canCollapse = "0";
+      closeCommand = "Canvas.popDialog(ProbeBakeDlg);";
+      edgeSnap = "0";
+      margin = "0 0 0 0";
+      padding = "0 0 0 0";
+      anchorTop = "1";
+      anchorBottom = "0";
+      anchorLeft = "1";
+      anchorRight = "0";
+      position = "392 314";
+      extent = "270 164";
+      minExtent = "8 2";
+      horizSizing = "right";
+      vertSizing = "bottom";
+      profile = "GuiWindowProfile";
+      visible = "1";
+      active = "1";
+      tooltipProfile = "GuiToolTipProfile";
+      hovertime = "1000";
+      isContainer = "1";
+      canSave = "1";
+      canSaveDynamicFields = "0";
+
+      new GuiTextCtrl() {
+         text = "Probe Resolution";
+         maxLength = "1024";
+         margin = "0 0 0 0";
+         padding = "0 0 0 0";
+         anchorTop = "1";
+         anchorBottom = "0";
+         anchorLeft = "1";
+         anchorRight = "0";
+         position = "11 32";
+         extent = "91 13";
+         minExtent = "8 2";
+         horizSizing = "right";
+         vertSizing = "bottom";
+         profile = "GuiTextProfile";
+         visible = "1";
+         active = "1";
+         tooltipProfile = "GuiToolTipProfile";
+         hovertime = "1000";
+         isContainer = "1";
+         canSave = "1";
+         canSaveDynamicFields = "0";
+      };
+      new GuiPopUpMenuCtrl(ProbeBakeDlg_ProbeResList) {
+         maxPopupHeight = "200";
+         sbUsesNAColor = "0";
+         reverseTextList = "0";
+         bitmapBounds = "16 16";
+         text = "64";
+         maxLength = "1024";
+         margin = "0 0 0 0";
+         padding = "0 0 0 0";
+         anchorTop = "1";
+         anchorBottom = "0";
+         anchorLeft = "1";
+         anchorRight = "0";
+         position = "103 29";
+         extent = "157 19";
+         minExtent = "8 2";
+         horizSizing = "right";
+         vertSizing = "bottom";
+         profile = "GuiPopUpMenuProfile";
+         visible = "1";
+         active = "1";
+         tooltipProfile = "GuiToolTipProfile";
+         hovertime = "1000";
+         isContainer = "1";
+         canSave = "1";
+         canSaveDynamicFields = "0";
+      };
+      new GuiTextCtrl() {
+         text = "Number of bake iterations";
+         maxLength = "1024";
+         margin = "0 0 0 0";
+         padding = "0 0 0 0";
+         anchorTop = "1";
+         anchorBottom = "0";
+         anchorLeft = "1";
+         anchorRight = "0";
+         position = "11 56";
+         extent = "129 13";
+         minExtent = "8 2";
+         horizSizing = "right";
+         vertSizing = "bottom";
+         profile = "GuiTextProfile";
+         visible = "1";
+         active = "1";
+         tooltipProfile = "GuiToolTipProfile";
+         hovertime = "1000";
+         isContainer = "1";
+         canSave = "1";
+         canSaveDynamicFields = "0";
+      };
+      new GuiTextEditCtrl(ProbeBakeDlg_NumIterTxt) {
+         historySize = "0";
+         tabComplete = "0";
+         sinkAllKeyEvents = "0";
+         password = "0";
+         passwordMask = "*";
+         text = "1";
+         maxLength = "1024";
+         margin = "0 0 0 0";
+         padding = "0 0 0 0";
+         anchorTop = "1";
+         anchorBottom = "0";
+         anchorLeft = "1";
+         anchorRight = "0";
+         position = "150 53";
+         extent = "108 18";
+         minExtent = "8 2";
+         horizSizing = "right";
+         vertSizing = "bottom";
+         profile = "GuiTextEditProfile";
+         visible = "1";
+         active = "1";
+         tooltipProfile = "GuiToolTipProfile";
+         hovertime = "1000";
+         isContainer = "1";
+         canSave = "1";
+         canSaveDynamicFields = "0";
+      };
+      new GuiButtonCtrl(ProbeBakeDlg_RunBake) {
+         text = "Update Probes";
+         groupNum = "-1";
+         buttonType = "PushButton";
+         useMouseEvents = "0";
+         position = "68 120";
+         extent = "140 30";
+         minExtent = "8 2";
+         horizSizing = "right";
+         vertSizing = "bottom";
+         profile = "GuiButtonProfile";
+         visible = "1";
+         active = "1";
+         tooltipProfile = "GuiToolTipProfile";
+         hovertime = "1000";
+         isContainer = "0";
+         canSave = "1";
+         canSaveDynamicFields = "0";
+      };
+      new GuiProgressCtrl(ProbeBakeDlg_Progress) {
+         maxLength = "1024";
+         margin = "0 0 0 0";
+         padding = "0 0 0 0";
+         anchorTop = "1";
+         anchorBottom = "0";
+         anchorLeft = "1";
+         anchorRight = "0";
+         position = "8 80";
+         extent = "251 25";
+         minExtent = "8 2";
+         horizSizing = "right";
+         vertSizing = "bottom";
+         profile = "GuiProgressProfile";
+         visible = "1";
+         active = "1";
+         tooltipProfile = "GuiToolTipProfile";
+         hovertime = "1000";
+         isContainer = "1";
+         canSave = "1";
+         canSaveDynamicFields = "0";
+      };
+   };
+};
+//--- OBJECT WRITE END ---

+ 6 - 3
Templates/Full/game/tools/worldEditor/main.cs

@@ -43,6 +43,7 @@ function initializeWorldEditor()
    exec("./gui/AddFMODProjectDlg.ed.gui");
    exec("./gui/SelectObjectsWindow.ed.gui");
    exec("./gui/ProceduralTerrainPainterGui.gui" );
+   exec("./gui/probeBakeDlg.gui" );
    
    // Load Scripts.
    exec("./scripts/menus.ed.cs");
@@ -61,6 +62,7 @@ function initializeWorldEditor()
    exec("./scripts/ManageSFXParametersWindow.ed.cs");
    exec("./scripts/AddFMODProjectDlg.ed.cs");
    exec("./scripts/SelectObjectsWindow.ed.cs");
+   exec("./scripts/probeBake.ed.cs");
 
    // Load Custom Editors
    loadDirectory(expandFilename("./scripts/editors"));
@@ -115,10 +117,11 @@ function initializeWorldEditor()
    EVisibility.addOption( "Debug Render: Light Frustums", "$Light::renderLightFrustums", "" );
    EVisibility.addOption( "Debug Render: Bounding Boxes", "$Scene::renderBoundingBoxes", "" );
    EVisibility.addOption( "Debug Render: Physics World", "$PhysicsWorld::render", "togglePhysicsDebugViz" );
+   EVisibility.addOption( "Debug Render: Reflection Probes", "$Light::renderReflectionProbes", "" );
+   EVisibility.addOption( "Debug Render: Probe Previews", "$Light::renderPreviewProbes", "" );
    EVisibility.addOption( "AL: Disable Shadows", "$Shadows::disable", "" );   
-   EVisibility.addOption( "AL: Light Color Viz", "$AL_LightColorVisualizeVar", "toggleLightColorViz" );
-   EVisibility.addOption( "AL: Environment Light", "$AL_LightMapShaderVar", "toggleLightMapViz" );
-   EVisibility.addOption( "AL: Light Specular Viz", "$AL_LightSpecularVisualizeVar", "toggleLightSpecularViz" );
+   EVisibility.addOption( "AL: Diffuse Lighting Viz", "$AL_LightColorVisualizeVar", "toggleLightColorViz" );
+   EVisibility.addOption( "AL: Specular Lighting Viz", "$AL_LightSpecularVisualizeVar", "toggleLightSpecularViz" );
    EVisibility.addOption( "AL: Normals Viz", "$AL_NormalsVisualizeVar", "toggleNormalsViz" );
    EVisibility.addOption( "AL: Depth Viz", "$AL_DepthVisualizeVar", "toggleDepthViz" );
    EVisibility.addOption( "AL: Color Buffer", "$AL_ColorBufferShaderVar", "toggleColorBufferViz" );

+ 4 - 0
Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs

@@ -55,6 +55,10 @@ function EWCreatorWindow::init( %this )
       
       %this.registerMissionObject( "PointLight",          "Point Light" );
       %this.registerMissionObject( "SpotLight",           "Spot Light" );
+      
+      %this.registerMissionObject( "ReflectionProbe",       "Reflection Probe" );
+      %this.registerMissionObject( "Skylight",       "Skylight" );
+      
       %this.registerMissionObject( "GroundCover",         "Ground Cover" );
       %this.registerMissionObject( "TerrainBlock",        "Terrain Block" );
       %this.registerMissionObject( "GroundPlane",         "Ground Plane" );

+ 38 - 2
Templates/Full/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs

@@ -294,6 +294,28 @@ function TerrainMaterialDlg::changeNormal( %this )
 
 //-----------------------------------------------------------------------------
 
+function TerrainMaterialDlg::changecomposite( %this )
+{
+   %ctrl = %this-->compositeTexCtrl;
+   %file = %ctrl.bitmap;
+   if( getSubStr( %file, 0 , 6 ) $= "tools/" )
+      %file = "";
+
+   %file = TerrainMaterialDlg._selectTextureFileDialog( %file );  
+   if( %file $= "" )
+   {
+      if( %ctrl.bitmap !$= "" )
+         %file = %ctrl.bitmap;
+      else
+         %file = "tools/materialEditor/gui/unknownImage";
+   }
+   
+   %file = makeRelativePath( %file, getMainDotCsDir() );
+   %ctrl.setBitmap( %file );  
+}
+
+//-----------------------------------------------------------------------------
+
 function TerrainMaterialDlg::newMat( %this )
 {
    // Create a unique material name.
@@ -394,7 +416,12 @@ function TerrainMaterialDlg::setActiveMaterial( %this, %mat )
          %this-->normTexCtrl.setBitmap( "tools/materialEditor/gui/unknownImage" );
       }else{
          %this-->normTexCtrl.setBitmap( %mat.normalMap ); 
-      }
+      }	  
+      if (%mat.compositeMap $= ""){
+         %this-->compositeTexCtrl.setBitmap( "tools/materialEditor/gui/unknownImage" );
+      }else{
+         %this-->compositeTexCtrl.setBitmap( %mat.compositeMap );
+      }	  
       %this-->detSizeCtrl.setText( %mat.detailSize );
       %this-->baseSizeCtrl.setText( %mat.diffuseSize );
       %this-->detStrengthCtrl.setText( %mat.detailStrength );
@@ -448,6 +475,11 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
    }else{
       %newMacro = %this-->macroTexCtrl.bitmap;  
    }
+   if (%this-->compositeTexCtrl.bitmap $= "tools/materialEditor/gui/unknownImage"){
+      %newComposite = "";
+   }else{
+      %newComposite = %this-->compositeTexCtrl.bitmap;  
+   }
    %detailSize = %this-->detSizeCtrl.getText();      
    %diffuseSize = %this-->baseSizeCtrl.getText();     
    %detailStrength = %this-->detStrengthCtrl.getText();
@@ -466,6 +498,7 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
          %mat.diffuseMap $= %newDiffuse &&
          %mat.normalMap $= %newNormal &&
          %mat.detailMap $= %newDetail &&
+         %mat.compositeMap $= %newComposite &&
          %mat.macroMap $= %newMacro &&
          %mat.detailSize == %detailSize &&
          %mat.diffuseSize == %diffuseSize &&
@@ -497,7 +530,8 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
    }
    
    %mat.diffuseMap = %newDiffuse;    
-   %mat.normalMap = %newNormal;    
+   %mat.normalMap = %newNormal;
+   %mat.compositeMap = %newComposite;
    %mat.detailMap = %newDetail;    
    %mat.macroMap = %newMacro;
    %mat.detailSize = %detailSize;  
@@ -544,6 +578,7 @@ function TerrainMaterialDlg::snapshotMaterials( %this )
          diffuseMap = %mat.diffuseMap;
          normalMap = %mat.normalMap;
          detailMap = %mat.detailMap;
+         compositeMap = %mat.compositeMap;
          macroMap = %mat.macroMap;
          detailSize = %mat.detailSize;
          diffuseSize = %mat.diffuseSize;
@@ -578,6 +613,7 @@ function TerrainMaterialDlg::restoreMaterials( %this )
       %mat.diffuseMap = %obj.diffuseMap;
       %mat.normalMap = %obj.normalMap;
       %mat.detailMap = %obj.detailMap;
+      %mat.compositeMap = %obj.compositeMap;
       %mat.macroMap = %obj.macroMap;
       %mat.detailSize = %obj.detailSize;
       %mat.diffuseSize = %obj.diffuseSize;

+ 18 - 0
Templates/Full/game/tools/worldEditor/scripts/lighting.ed.cs

@@ -61,3 +61,21 @@ function EditorLightingMenu::onMenuSelect( %this )
    //%selSize = EWorldEditor.getSelectionSize();
    %this.enableItem( 1, true /*%selSize == 1*/ );   
 }
+
+function updateReflectionProbes()
+{
+   /*%probeIds = parseMissionGroupForIds("ReflectionProbe", "");
+   %probeCount = getWordCount(%probeIds);
+   
+   for(%i=0; %i < %probeCount; %i++)
+   {
+      %probe = getWord(%probeIds, %i);
+      
+      %path = filePath($Server::MissionFile) @ "/" @ fileBase($Server::MissionFile) @ "/probes/";
+      %probe.bake(%path, 64);
+   }
+   
+   EWorldEditor.isDirty = true;*/
+   
+   Canvas.pushDialog(ProbeBakeDlg);
+}

+ 2 - 0
Templates/Full/game/tools/worldEditor/scripts/menus.ed.cs

@@ -263,6 +263,8 @@ function EditorGui::buildMenus(%this)
       item[0] = "Full Relight" TAB "Alt L" TAB "Editor.lightScene(\"\", forceAlways);";
       item[1] = "Toggle ShadowViz" TAB "" TAB "toggleShadowViz();";
       item[2] = "-";
+      item[3] = "Update Reflection Probes" TAB "" TAB "updateReflectionProbes();";
+      item[4] = "-";
          
          // NOTE: The light managers will be inserted as the
          // last menu items in EditorLightingMenu::onAdd().

+ 48 - 0
Templates/Full/game/tools/worldEditor/scripts/probeBake.ed.cs

@@ -0,0 +1,48 @@
+function ProbeBakeDlg::onWake(%this)
+{
+   //set up
+   ProbeBakeDlg_ProbeResList.add( "32" );  
+   ProbeBakeDlg_ProbeResList.add( "64" );
+   ProbeBakeDlg_ProbeResList.add( "128" );
+   ProbeBakeDlg_ProbeResList.add( "256" );
+   ProbeBakeDlg_ProbeResList.add( "512" );
+   ProbeBakeDlg_ProbeResList.add( "1024" );
+   ProbeBakeDlg_ProbeResList.add( "2048" );
+   
+   ProbeBakeDlg_ProbeResList.setSelected( 1, false );
+   
+   ProbeBakeDlg_NumIterTxt.setText("1");
+}
+
+function ProbeBakeDlg_RunBake::onClick(%this)
+{
+   %probeIds = parseMissionGroupForIds("ReflectionProbe", "");
+   %skylightIds = parseMissionGroupForIds("Skylight", "");
+   
+   %probeIds = rtrim(ltrim(%probeIds SPC %skylightIds));
+   %probeCount = getWordCount(%probeIds);
+   
+   %numIter = ProbeBakeDlg_NumIterTxt.getText();
+   %resolution = ProbeBakeDlg_ProbeResList.getText();
+   %progressStep = 100 / (%numIter * %probeCount);
+   %currentProgressValue = 0;
+   
+   ProbeBakeDlg_Progress.setValue(%currentProgressValue);
+   
+   for(%iter=0; %iter < %numIter; %iter++)
+   {
+      for(%i=0; %i < %probeCount; %i++)
+      {
+         %probe = getWord(%probeIds, %i);
+         
+         %path = filePath($Server::MissionFile) @ "/" @ fileBase($Server::MissionFile) @ "/probes/";
+         %probe.bake(%path, %resolution);
+         
+         %currentProgressValue += %progressStep;
+         ProbeBakeDlg_Progress.setValue(%currentProgressValue);
+         Canvas.repaint();
+      }
+   }
+   
+   EWorldEditor.isDirty = true;
+}

Some files were not shown because too many files changed in this diff