Sfoglia il codice sorgente

WIP of timmy's changes merged in. Not properly initializing the probes/array slots just yet.

Areloch 6 anni fa
parent
commit
26471aaa77

+ 6 - 3
Engine/source/T3D/lighting/reflectionProbe.cpp

@@ -631,7 +631,7 @@ void ReflectionProbe::processStaticCubemap()
       IBLUtilities::SaveCubeMap(getPrefilterMapPath(), mPrefilterMap->mCubemap);
    }
 
-   mProbeInfo->mCubemap = mPrefilterMap->mCubemap;
+   mProbeInfo->mPrefilterCubemap = mPrefilterMap->mCubemap;
    mProbeInfo->mIrradianceCubemap = mIrridianceMap->mCubemap;
 }
 
@@ -647,7 +647,7 @@ void ReflectionProbe::updateMaterial()
       {
          if (mPrefilterMap != nullptr && mPrefilterMap->mCubemap.isValid())
          {
-            mProbeInfo->mCubemap = mPrefilterMap->mCubemap;
+            mProbeInfo->mPrefilterCubemap = mPrefilterMap->mCubemap;
          }
          else
          {
@@ -667,7 +667,7 @@ void ReflectionProbe::updateMaterial()
    {
       if (mReflectionModeType == DynamicCubemap && !mDynamicCubemap.isNull())
       {
-         mProbeInfo->mCubemap = mDynamicCubemap;
+         mProbeInfo->mPrefilterCubemap = mDynamicCubemap;
 
          mProbeInfo->mCubeReflector.registerReflector(this, reflectorDesc); //need to decide how we wanna do the reflectorDesc. static name or a field
       }
@@ -684,6 +684,9 @@ void ReflectionProbe::updateMaterial()
       mProbeInfo->mIsEnabled = false;
 
    PROBEMGR->updateProbes();
+
+   if (mProbeInfo->mPrefilterCubemap->isInitialized() && mProbeInfo->mIrradianceCubemap->isInitialized())
+      PROBEMGR->updateProbeTexture(mProbeInfo);
 }
 
 bool ReflectionProbe::createClientResources()

+ 104 - 9
Engine/source/gfx/D3D11/gfxD3D11Cubemap.cpp

@@ -141,7 +141,7 @@ void GFXD3D11Cubemap::initStatic(GFXTexHandle *faces)
       mSRView->GetDesc(&viewDesc);
       mMipMapLevels = viewDesc.TextureCube.MipLevels;
    }
-   mInitialized = true;
+
 }
 
 void GFXD3D11Cubemap::initStatic(DDSFile *dds)
@@ -208,7 +208,6 @@ void GFXD3D11Cubemap::initStatic(DDSFile *dds)
 	{
 		AssertFatal(false, "GFXD3D11Cubemap::initStatic(DDSFile *dds) - CreateTexture2D call failure");
 	}
-   mInitialized = true;
 }
 
 void GFXD3D11Cubemap::initDynamic(U32 texSize, GFXFormat faceFormat, U32 mipLevels)
@@ -331,7 +330,7 @@ void GFXD3D11Cubemap::initDynamic(U32 texSize, GFXFormat faceFormat, U32 mipLeve
 	}
 
    SAFE_RELEASE(depthTex);
-   mInitialized = true;
+
 }
 
 //-----------------------------------------------------------------------------
@@ -392,10 +391,11 @@ GFXD3D11CubemapArray::~GFXD3D11CubemapArray()
    SAFE_RELEASE(mTexture);
 }
 
-void GFXD3D11CubemapArray::initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount)
+//TODO: really need a common private 'init' function to avoid code double up with these init* functions
+void GFXD3D11CubemapArray::init(GFXCubemapHandle *cubemaps, const U32 cubemapCount)
 {
-   AssertFatal(cubemaps, "GFXD3D11CubemapArray - Got null GFXCubemapHandle!");
-   AssertFatal(*cubemaps, "GFXD3D11CubemapArray - Got empty cubemap!");
+   AssertFatal(cubemaps, "GFXD3D11CubemapArray::initStatic - Got null GFXCubemapHandle!");
+   AssertFatal(*cubemaps, "GFXD3D11CubemapArray::initStatic - Got empty cubemap!");
 
    //all cubemaps must be the same size,format and number of mipmaps. Grab the details from the first cubemap
    mSize = cubemaps[0]->getSize();
@@ -424,7 +424,7 @@ void GFXD3D11CubemapArray::initStatic(GFXCubemapHandle *cubemaps, const U32 cube
    HRESULT hr = D3D11DEVICE->CreateTexture2D(&desc, NULL, &mTexture);
 
    if (FAILED(hr))
-      AssertFatal(false, "GFXD3D11CubemapArray:initStatic(GFXCubemap *cubemaps,const U32 cubemapCount) - CreateTexture2D failure");
+      AssertFatal(false, "GFXD3D11CubemapArray::initStatic - CreateTexture2D failure");
 
    for (U32 i = 0; i < cubemapCount; i++)
    {
@@ -434,7 +434,7 @@ void GFXD3D11CubemapArray::initStatic(GFXCubemapHandle *cubemaps, const U32 cube
       {
          Con::printf("Trying to add an invalid Cubemap to a CubemapArray");
          //destroy array here first
-         AssertFatal(false, "GFXD3D11CubemapArray:initStatic(GFXCubemap *cubemaps,const U32 cubemapCount) - invalid cubemap");
+         AssertFatal(false, "GFXD3D11CubemapArray::initStatic - invalid cubemap");
       }
 
       for (U32 face = 0; face < CubeFaces; face++)
@@ -460,7 +460,102 @@ void GFXD3D11CubemapArray::initStatic(GFXCubemapHandle *cubemaps, const U32 cube
 
    hr = D3D11DEVICE->CreateShaderResourceView(mTexture, &SMViewDesc, &mSRView);
    if (FAILED(hr))
-      AssertFatal(false, "GFXD3D11CubemapArray:initStatic(GFXCubemap *cubemaps,const U32 cubemapCount) - shader resource view  creation failure");
+      AssertFatal(false, "GFXD3D11CubemapArray::initStatic - shader resource view  creation failure");
+
+}
+
+//Just allocate the cubemap array but we don't upload any data
+void GFXD3D11CubemapArray::init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format)
+{
+   mSize = cubemapFaceSize;
+   mMipMapLevels = ImageUtil::getMaxMipCount(cubemapFaceSize, cubemapFaceSize);
+   mNumCubemaps = cubemapCount;
+   mFormat = format;
+
+   //create texture object
+   UINT bindFlags = D3D11_BIND_SHADER_RESOURCE;
+   UINT miscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
+
+   D3D11_TEXTURE2D_DESC desc;
+   ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
+   desc.Width = mSize;
+   desc.Height = mSize;
+   desc.MipLevels = mMipMapLevels;
+   desc.ArraySize = CubeFaces * cubemapCount;
+   desc.Format = GFXD3D11TextureFormat[mFormat];
+   desc.SampleDesc.Count = 1;
+   desc.SampleDesc.Quality = 0;
+   desc.Usage = D3D11_USAGE_DEFAULT;
+   desc.BindFlags = bindFlags;
+   desc.MiscFlags = miscFlags;
+   desc.CPUAccessFlags = 0;
+
+   HRESULT hr = D3D11DEVICE->CreateTexture2D(&desc, NULL, &mTexture);
+
+   if (FAILED(hr))
+      AssertFatal(false, "GFXD3D11CubemapArray::initStatic - CreateTexture2D failure");
+
+   //create shader resource view
+   D3D11_SHADER_RESOURCE_VIEW_DESC SMViewDesc;
+   SMViewDesc.Format = GFXD3D11TextureFormat[mFormat];
+   SMViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY;
+   SMViewDesc.TextureCubeArray.MipLevels = mMipMapLevels;
+   SMViewDesc.TextureCubeArray.MostDetailedMip = 0;
+   SMViewDesc.TextureCubeArray.NumCubes = mNumCubemaps;
+   SMViewDesc.TextureCubeArray.First2DArrayFace = 0;
+
+   hr = D3D11DEVICE->CreateShaderResourceView(mTexture, &SMViewDesc, &mSRView);
+   if (FAILED(hr))
+      AssertFatal(false, "GFXD3D11CubemapArray::initStatic - shader resource view  creation failure");
+
+}
+
+void GFXD3D11CubemapArray::updateTexture(const GFXCubemapHandle &cubemap, const U32 slot)
+{
+   AssertFatal(slot <= mNumCubemaps, "GFXD3D11CubemapArray::updateTexture - trying to update a cubemap texture that is out of bounds!");
+
+   GFXD3D11Cubemap *pCubeObj = static_cast<GFXD3D11Cubemap*>((GFXCubemap*)cubemap);
+   ID3D11Resource *pDstRes = pCubeObj->get2DTex();
+   for (U32 face = 0; face < CubeFaces; face++)
+   {
+      const U32 arraySlice = face + CubeFaces * slot;
+      for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++)
+      {
+         const U32 srcSubResource = D3D11CalcSubresource(currentMip, face, mMipMapLevels);
+         const U32 dstSubResource = D3D11CalcSubresource(currentMip, arraySlice, mMipMapLevels);
+         D3D11DEVICECONTEXT->CopySubresourceRegion(mTexture, dstSubResource, 0, 0, 0, pDstRes, srcSubResource, NULL);
+      }
+   }
+}
+
+void GFXD3D11CubemapArray::copyTo(GFXCubemapArray *pDstCubemap)
+{
+   AssertFatal(pDstCubemap, "GFXD3D11CubemapArray::copyTo - Got null GFXCubemapArray");
+
+   const U32 dstCount = pDstCubemap->getNumCubemaps();
+   const GFXFormat dstFmt = pDstCubemap->getFormat();
+   const U32 dstSize = pDstCubemap->getSize();
+   const U32 dstMips = pDstCubemap->getMipMapLevels();
+
+   AssertFatal(dstCount > mNumCubemaps, "GFXD3D11CubemapArray::copyTo - Destination too small");
+   AssertFatal(dstFmt == mFormat, "GFXD3D11CubemapArray::copyTo - Destination format doesn't match");
+   AssertFatal(dstSize == mSize, "GFXD3D11CubemapArray::copyTo - Destination size doesn't match");
+   AssertFatal(dstMips == mMipMapLevels, "GFXD3D11CubemapArray::copyTo - Destination mip levels doesn't match");
+
+   GFXD3D11CubemapArray *pDstCube = static_cast<GFXD3D11CubemapArray*>(pDstCubemap);
+   ID3D11Resource *pDstRes = pDstCube->get2DTex();
+   for (U32 cubeMap = 0; cubeMap < mNumCubemaps; cubeMap++)
+   {
+      for (U32 face = 0; face < CubeFaces; face++)
+      {
+         const U32 arraySlice = face + CubeFaces * cubeMap;
+         for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++)
+         {
+            const U32 subResource = D3D11CalcSubresource(currentMip, arraySlice, mMipMapLevels);
+            D3D11DEVICECONTEXT->CopySubresourceRegion(pDstRes, subResource, 0, 0, 0, mTexture, subResource, NULL);
+         }
+      }
+   }
 
 }
 

+ 6 - 1
Engine/source/gfx/D3D11/gfxD3D11Cubemap.h

@@ -48,6 +48,8 @@ public:
    virtual void zombify();
    virtual void resurrect();
 
+   virtual bool isInitialized() { return mTexture ? true : false; }
+
    // Get functions
    ID3D11ShaderResourceView* getSRView();
    ID3D11RenderTargetView* getRTView(U32 faceIdx, U32 mipIndex=0);
@@ -81,7 +83,10 @@ class GFXD3D11CubemapArray : public GFXCubemapArray
 public:
    GFXD3D11CubemapArray();
    virtual ~GFXD3D11CubemapArray();
-   virtual void initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount);
+   virtual void init(GFXCubemapHandle *cubemaps, const U32 cubemapCount);
+   virtual void init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format);
+   virtual void updateTexture(const GFXCubemapHandle &cubemap, const U32 slot);
+   virtual void copyTo(GFXCubemapArray *pDstCubemap);
    virtual void setToTexUnit(U32 tuNum);
 
    ID3D11ShaderResourceView* getSRView() { return mSRView; }

+ 4 - 2
Engine/source/gfx/Null/gfxNullDevice.cpp

@@ -169,8 +169,10 @@ private:
    virtual void setToTexUnit(U32 tuNum) { };
 
 public:
-   virtual void initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount) { };
-
+   virtual void init(GFXCubemapHandle *cubemaps, const U32 cubemapCount) { };
+   virtual void init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format) { };
+   virtual void updateTexture(const GFXCubemapHandle &cubemap, const U32 slot) { };
+   virtual void copyTo(GFXCubemapArray *pDstCubemap) { }
    virtual ~GFXNullCubemapArray() {};
    virtual void zombify() {}
    virtual void resurrect() {}

+ 5 - 0
Engine/source/gfx/bitmap/imageUtils.cpp

@@ -301,4 +301,9 @@ namespace ImageUtil
          return format;
       };
    }
+
+   U32 getMaxMipCount(const U32 width, const U32 height)
+   {
+      return mFloor(mLog2(mMax(width, height))) + 1;
+   }
 }

+ 5 - 0
Engine/source/gfx/bitmap/imageUtils.h

@@ -29,6 +29,9 @@
 #ifndef _GFXENUMS_H_
 #include "gfx/gfxEnums.h"
 #endif
+#ifndef _MMATHFN_H_
+#include "math/mMathFn.h"
+#endif
 
 struct DDSFile;
 
@@ -57,6 +60,8 @@ namespace ImageUtil
 
    //convert to sRGB format
    GFXFormat toSRGBFormat(const GFXFormat format);
+
+   U32 getMaxMipCount(const U32 width, const U32 height);
 };
 
 #endif

+ 20 - 5
Engine/source/gfx/gfxCubemap.h

@@ -49,6 +49,7 @@ protected:
 
 
    U32 mMipMapLevels;
+
    bool mInitialized;
 public:
 
@@ -62,6 +63,7 @@ public:
    virtual void initDynamic( U32 texSize, GFXFormat faceFormat = GFXFormatR8G8B8A8, U32 mipLevels = 0 ) = 0;
 
    void initNormalize(U32 size);
+      
    GFXCubemap();
    virtual ~GFXCubemap();
 
@@ -71,6 +73,9 @@ public:
    /// Returns the face texture format.
    virtual GFXFormat getFormat() const = 0;
 
+   /// Returns if this cubemap has been initialized
+   virtual bool isInitialized() { return false; }
+
    /// Returns the cubemap file path set at creation.
    const String& getPath() const { return mPath; }
 
@@ -83,7 +88,6 @@ public:
 
    /// Get Z up face index of the cubemap. DDS files will be stored Y up
    static U32 zUpFaceIndex(const U32 index);
-   bool isInitialised() { return mInitialized; }
 };
 
 
@@ -102,32 +106,43 @@ public:
    void free() { StrongObjectRef::set( NULL ); }
 };
 
-/// Cubemap array
+/// Cubemap array - data lives on the GPU only with this class, but the data is not immutable so it can be updated
 class GFXCubemapArray : public StrongRefBase, public GFXResource
 {
    friend class GFXDevice;
    friend class GFXTextureManager;
 
 protected:
-   // should only be called by GFXDevice
+   /// should only be called by GFXDevice
    virtual void setToTexUnit( U32 tuNum ) = 0;
    /// number of cubemaps in the array
    U32 mNumCubemaps;
+   /// cubemap face size
    U32 mSize;
+   /// number of mip levels
    U32 mMipMapLevels;
+   /// format
    GFXFormat mFormat;
 
 public:
 
    virtual ~GFXCubemapArray() {};
-
-   virtual void initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount) = 0;
+   /// Initialize from an array of cubemaps
+   virtual void init(GFXCubemapHandle *cubemaps, const U32 cubemapCount) = 0;
+   /// Initialize cubemapCount number of blank cubemaps in the array
+   virtual void init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format) = 0;
+   /// Update cubemap in the array
+   virtual void updateTexture(const GFXCubemapHandle &cubemap, const U32 slot) = 0;
+   /// Copy this cubemap to another - destination must be same format, same face size & at-least the same size(or larger)
+   virtual void copyTo(GFXCubemapArray *pDstCubemap) = 0;
    /// Return number of textures in the array
    const U32 getNumCubemaps() const { return mNumCubemaps; }
    /// Get the number of mip maps
    const U32 getMipMapLevels() const { return mMipMapLevels; }
    /// Returns the size of the faces.
    const U32 getSize() const { return mSize; }
+   /// Returns the format
+   const GFXFormat getFormat() const { return mFormat; }
 
    virtual const String describeSelf() const;
 };

+ 143 - 25
Engine/source/renderInstance/renderProbeMgr.cpp

@@ -78,20 +78,21 @@ ProbeRenderInst::ProbeRenderInst() : SystemInterface(),
    mDirty(false),
    mPriority(1.0f),
    mScore(0.0f),
-   mCubemap(NULL),
+   mPrefilterCubemap(NULL),
    mIrradianceCubemap(NULL),
    mRadius(1.0f),
    mProbeRefOffset(0, 0, 0),
    mProbeRefScale(1,1,1),
-   mAtten(0.0)
+   mAtten(0.0),
+   mCubemapIndex(0)
 {
 }
 
 ProbeRenderInst::~ProbeRenderInst()
 {
-   if (mCubemap && mCubemap.isValid())
+   if (mPrefilterCubemap && mPrefilterCubemap.isValid())
    {
-      mCubemap.free();
+      mPrefilterCubemap.free();
    }
    if (mIrradianceCubemap && mIrradianceCubemap.isValid())
    {
@@ -102,7 +103,7 @@ ProbeRenderInst::~ProbeRenderInst()
 void ProbeRenderInst::set(const ProbeRenderInst *probeInfo)
 {
    mTransform = probeInfo->mTransform;
-   mCubemap = probeInfo->mCubemap;
+   mPrefilterCubemap = probeInfo->mPrefilterCubemap;
    mIrradianceCubemap = probeInfo->mIrradianceCubemap;
    mRadius = probeInfo->mRadius;
    mProbeShapeType = probeInfo->mProbeShapeType;
@@ -183,6 +184,13 @@ RenderProbeMgr::RenderProbeMgr()
    mProbeArrayEffect = nullptr;
 
    smProbeManager = this;
+
+   mCubeMapCount = 0;
+
+   for (U32 i = 0; i < PROBE_MAX_COUNT; i++)
+   {
+      mCubeMapSlots[i] = false;
+   }
 }
 
 RenderProbeMgr::RenderProbeMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder)
@@ -203,6 +211,37 @@ RenderProbeMgr::~RenderProbeMgr()
    mConstantLookup.clear();
 }
 
+bool RenderProbeMgr::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   mIrradianceArray = GFXCubemapArrayHandle(GFX->createCubemapArray());
+   mPrefilterArray = GFXCubemapArrayHandle(GFX->createCubemapArray());
+
+   //pre-allocate a few slots
+   mIrradianceArray->init(PROBE_ARRAY_SLOT_BUFFER_SIZE, PROBE_IRRAD_SIZE, PROBE_FORMAT);
+   mPrefilterArray->init(PROBE_ARRAY_SLOT_BUFFER_SIZE, PROBE_PREFILTER_SIZE, PROBE_FORMAT);
+   mCubeSlotCount = PROBE_ARRAY_SLOT_BUFFER_SIZE;
+
+   //create our own default default skylight
+   mDefaultSkyLight = new ProbeRenderInst;
+   mDefaultSkyLight->mProbeShapeType = ProbeRenderInst::Skylight;
+   if (!mDefaultSkyLight->mIrradianceCubemap.set("core/art/pbr/default_irradiance.dds"))
+   {
+      Con::errorf("RenderProbeMgr::onAdd: Failed to load default irradiance cubemap");
+      return false;
+   }
+
+   if (!mDefaultSkyLight->mPrefilterCubemap.set("core/art/pbr/default_prefilter.dds"))
+   {
+      Con::errorf("RenderProbeMgr::onAdd: Failed to load default prefilter cubemap");
+      return false;
+   }
+
+   return true;
+}
+
 void RenderProbeMgr::onRemove()
 {
    Parent::onRemove();
@@ -246,6 +285,38 @@ void RenderProbeMgr::registerProbe(U32 probeIdx)
 
    mRegisteredProbes.push_back_unique(probeIdx);
 
+   const U32 cubeIndex = _findNextEmptyCubeSlot();
+   if (cubeIndex == INVALID_CUBE_SLOT)
+   {
+      Con::warnf("RenderProbeMgr::addProbe: Invalid cubemap slot.");
+      return;
+   }
+
+   //check if we need to resize the cubemap array
+   if (cubeIndex >= mCubeSlotCount)
+   {
+      //alloc temp array handles
+      GFXCubemapArrayHandle irr = GFXCubemapArrayHandle(GFX->createCubemapArray());
+      GFXCubemapArrayHandle prefilter = GFXCubemapArrayHandle(GFX->createCubemapArray());
+
+      irr->init(mCubeSlotCount + PROBE_ARRAY_SLOT_BUFFER_SIZE, PROBE_IRRAD_SIZE, PROBE_FORMAT);
+      prefilter->init(mCubeSlotCount + PROBE_ARRAY_SLOT_BUFFER_SIZE, PROBE_PREFILTER_SIZE, PROBE_FORMAT);
+
+      mIrradianceArray->copyTo(irr);
+      mPrefilterArray->copyTo(prefilter);
+
+      //assign the temp handles to the new ones, this will destroy the old ones as well
+      mIrradianceArray = irr;
+      mPrefilterArray = prefilter;
+
+      mCubeSlotCount += PROBE_ARRAY_SLOT_BUFFER_SIZE;
+   }
+
+   ProbeRenderInst::all[probeIdx]->mCubemapIndex = cubeIndex;
+   //mark cubemap slot as taken
+   mCubeMapSlots[cubeIndex] = true;
+   mCubeMapCount++;
+
    //rebuild our probe data
    _setupStaticParameters();
 }
@@ -258,6 +329,13 @@ void RenderProbeMgr::unregisterProbe(U32 probeIdx)
 
    mRegisteredProbes.remove(probeIdx);
 
+   if (ProbeRenderInst::all[probeIdx]->mCubemapIndex == INVALID_CUBE_SLOT)
+      return;
+
+   //mark cubemap slot as available now
+   mCubeMapSlots[ProbeRenderInst::all[probeIdx]->mCubemapIndex] = false;
+   mCubeMapCount--;
+
    //rebuild our probe data
    _setupStaticParameters();
 }
@@ -314,6 +392,16 @@ void RenderProbeMgr::_setupStaticParameters()
    cubeMaps.clear();
    irradMaps.clear();
 
+   if (probeCount != 0 && ProbeRenderInst::all[0]->mPrefilterCubemap != nullptr)
+   {
+      //Get our mipCount
+      mMipCount = ProbeRenderInst::all[0]->mPrefilterCubemap.getPointer()->getMipMapLevels();
+   }
+   else
+   {
+      mMipCount = 1;
+   }
+
    for (U32 i = 0; i < probeCount; i++)
    {
       if (mEffectiveProbeCount >= MAXPROBECOUNT)
@@ -323,19 +411,23 @@ void RenderProbeMgr::_setupStaticParameters()
       if (!curEntry.mIsEnabled)
          continue;
 
-      if (curEntry.mCubemap.isNull() || curEntry.mIrradianceCubemap.isNull())
-         continue;
-
-      if (!curEntry.mCubemap->isInitialised())
+      if (curEntry.mIsSkylight)
+      {
+         skylightPos = curEntry.getPosition();
+         skylightPrefilterMap = curEntry.mPrefilterCubemap;
+         skylightIrradMap = curEntry.mIrradianceCubemap;
+         hasSkylight = true;
          continue;
+      }
 
-      if (!curEntry.mIrradianceCubemap->isInitialised())
-         continue;
+      //if (curEntry.mCubemap.isNull() || curEntry.mIrradianceCubemap.isNull())
+      //   continue;
 
-      //if (curEntry.mIsSkylight)
+      //if (!curEntry.mCubemap->isInitialized())
       //   continue;
 
-	   mMipCount = curEntry.mCubemap.getPointer()->getMipMapLevels();
+      //if (!curEntry.mIrradianceCubemap->isInitialized())
+      //   continue;
 
       //Setup
       Point3F probePos = curEntry.getPosition();
@@ -354,22 +446,40 @@ void RenderProbeMgr::_setupStaticParameters()
          curEntry.mAtten,
          1);
 
-      cubeMaps.push_back(curEntry.mCubemap);
-      irradMaps.push_back(curEntry.mIrradianceCubemap);
+      //cubeMaps.push_back(curEntry.mCubemap);
+      //irradMaps.push_back(curEntry.mIrradianceCubemap);
 
       mEffectiveProbeCount++;
    }
 
    if (mEffectiveProbeCount != 0)
    {
-      mCubemapArray = GFXCubemapArrayHandle(GFX->createCubemapArray());
-      mIrradArray = GFXCubemapArrayHandle(GFX->createCubemapArray());
+      //mPrefilterArray = GFXCubemapArrayHandle(GFX->createCubemapArray());
+      //mIrradianceArray = GFXCubemapArrayHandle(GFX->createCubemapArray());
 
-      mCubemapArray->initStatic(cubeMaps.address(), cubeMaps.size());
-      mIrradArray->initStatic(irradMaps.address(), irradMaps.size());
+      //mPrefilterArray->initStatic(cubeMaps.address(), cubeMaps.size());
+      //mIrradianceArray->initStatic(irradMaps.address(), irradMaps.size());
    }
 }
 
+void RenderProbeMgr::updateProbeTexture(ProbeRenderInst* probe)
+{
+   S32 probeIdx = ProbeRenderInst::all.find_next(probe);
+
+   if (probeIdx != -1) //i mean, the opposite shouldn't even be possible
+      updateProbeTexture(probeIdx);
+}
+
+void RenderProbeMgr::updateProbeTexture(U32 probeIdx)
+{
+   if (probeIdx >= ProbeRenderInst::all.size())
+      return;
+
+   const U32 cubeIndex = ProbeRenderInst::all[probeIdx]->mCubemapIndex;
+   mIrradianceArray->updateTexture(ProbeRenderInst::all[probeIdx]->mIrradianceCubemap, cubeIndex);
+   mPrefilterArray->updateTexture(ProbeRenderInst::all[probeIdx]->mPrefilterCubemap, cubeIndex);
+}
+
 void RenderProbeMgr::_setupPerFrameParameters(const SceneRenderState *state)
 {
    PROFILE_SCOPE(RenderProbeMgr_SetupPerFrameParameters);
@@ -607,11 +717,11 @@ void RenderProbeMgr::render( SceneRenderState *state )
    //updateProbes();
 
    // Early out if nothing to draw.
-   if (!ProbeRenderInst::all.size() || !RenderProbeMgr::smRenderReflectionProbes || mEffectiveProbeCount == 0
-	   || !state->isDiffusePass() || cubeMaps.empty() || irradMaps.empty())
+   if (!ProbeRenderInst::all.size() || !RenderProbeMgr::smRenderReflectionProbes || !state->isDiffusePass() || (mEffectiveProbeCount == 0
+      || mCubeMapCount != 0 && !hasSkylight))
    {
-	   getProbeArrayEffect()->setSkip(true);
-	   return;
+      getProbeArrayEffect()->setSkip(true);
+      return;
    }
 
    GFXTransformSaver saver;
@@ -625,10 +735,18 @@ void RenderProbeMgr::render( SceneRenderState *state )
    //Array rendering
    //U32 probeCount = ProbeRenderInst::all.size();
 
+   mProbeArrayEffect->setShaderConst("$hasSkylight", (float)hasSkylight);
+   if (hasSkylight)
+   {
+      mProbeArrayEffect->setCubemapTexture(6, skylightPrefilterMap);
+      mProbeArrayEffect->setCubemapTexture(7, skylightIrradMap);
+
+   }
+
    if (mEffectiveProbeCount != 0)
    {
-      mProbeArrayEffect->setCubemapArrayTexture(4, mCubemapArray);
-      mProbeArrayEffect->setCubemapArrayTexture(5, mIrradArray);
+      mProbeArrayEffect->setCubemapArrayTexture(4, mPrefilterArray);
+      mProbeArrayEffect->setCubemapArrayTexture(5, mIrradianceArray);
 
       String useDebugAtten = Con::getVariable("$Probes::showAttenuation", "0");
       mProbeArrayEffect->setShaderMacro("DEBUGVIZ_ATTENUATION", useDebugAtten);

+ 45 - 3
Engine/source/renderInstance/renderProbeMgr.h

@@ -72,7 +72,7 @@ struct ProbeRenderInst : public SystemInterface<ProbeRenderInst>
    Point3F mProbeRefScale;
    F32 mAtten;
 
-   GFXCubemapHandle mCubemap;
+   GFXCubemapHandle mPrefilterCubemap;
    GFXCubemapHandle mIrradianceCubemap;
 
    //Utilized in dynamic reflections
@@ -97,6 +97,8 @@ struct ProbeRenderInst : public SystemInterface<ProbeRenderInst>
 
    ProbeShapeType mProbeShapeType;
 
+   U32 mCubemapIndex;
+
 public:
 
    ProbeRenderInst();
@@ -161,6 +163,18 @@ class RenderProbeMgr : public RenderBinManager
 
    Vector<U32> mRegisteredProbes;
 
+   //maximum number of allowed probes
+   static const U32 PROBE_MAX_COUNT = 250;
+   //maximum number of rendered probes per frame adjust as needed
+   static const U32 PROBE_MAX_FRAME = 8;
+   //number of slots to allocate at once in the cubemap array
+   static const U32 PROBE_ARRAY_SLOT_BUFFER_SIZE = 10;
+
+   static const U32 PROBE_IRRAD_SIZE = 32;
+   static const U32 PROBE_PREFILTER_SIZE = 128;
+   static const GFXFormat PROBE_FORMAT = GFXFormatR8G8B8A8;// when hdr fixed GFXFormatR16G16B16A16F; look into bc6h compression
+   static const U32 INVALID_CUBE_SLOT = U32_MAX;
+
    //Array rendering
    U32 mEffectiveProbeCount;
    S32 mMipCount;
@@ -173,6 +187,11 @@ class RenderProbeMgr : public RenderBinManager
    Vector<GFXCubemapHandle> cubeMaps;
    Vector<GFXCubemapHandle> irradMaps;
 
+   bool hasSkylight;
+   GFXCubemapHandle skylightIrradMap;
+   GFXCubemapHandle skylightPrefilterMap;
+   Point4F          skylightPos;
+
    AlignedArray<Point4F> mProbePositions;
    AlignedArray<Point4F> mProbeBBMin;
    AlignedArray<Point4F> mProbeBBMax;
@@ -180,8 +199,15 @@ class RenderProbeMgr : public RenderBinManager
    AlignedArray<float> mProbeRadius;
    AlignedArray<float> mProbeAttenuation;
 
-   GFXCubemapArrayHandle mCubemapArray;
-   GFXCubemapArrayHandle mIrradArray;
+   //number of cubemaps
+   U32 mCubeMapCount;
+   //number of cubemap slots allocated
+   U32 mCubeSlotCount;
+   //array of cubemap slots, due to the editor these may be mixed around as probes are added and deleted
+   bool mCubeMapSlots[PROBE_MAX_COUNT];
+
+   GFXCubemapArrayHandle mPrefilterArray;
+   GFXCubemapArrayHandle mIrradianceArray;
 
    //Utilized in forward rendering
    ProbeConstantMap mConstantLookup;
@@ -191,10 +217,14 @@ class RenderProbeMgr : public RenderBinManager
    //
    SimObjectPtr<PostEffect> mProbeArrayEffect;
 
+   //Default skylight, used for shape editors, etc
+   ProbeRenderInst* mDefaultSkyLight;
+
 public:
    RenderProbeMgr();
    RenderProbeMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder);
    virtual ~RenderProbeMgr();
+   virtual bool onAdd();
    virtual void onRemove();
 
    // ConsoleObject
@@ -219,6 +249,7 @@ protected:
       GFXShaderConstBuffer *shaderConsts);
 
    void _setupStaticParameters();
+   void updateProbeTexture(U32 probeIdx);
    void _setupPerFrameParameters(const SceneRenderState *state);
    virtual void addElement(RenderInst *inst);
    virtual void render(SceneRenderState * state);
@@ -230,6 +261,7 @@ protected:
 public:
    // RenderBinMgr
    void updateProbes();
+   void updateProbeTexture(ProbeRenderInst* probe);
 
    /// Returns the active LM.
    static inline RenderProbeMgr* getProbeManager();
@@ -249,6 +281,16 @@ public:
 
    void bakeProbe(ReflectionProbe *probeInfo);
    void bakeProbes();
+
+   U32 _findNextEmptyCubeSlot()
+   {
+      for (U32 i = 0; i < PROBE_MAX_COUNT; i++)
+      {
+         if (!mCubeMapSlots[i])
+            return i;
+      }
+      return INVALID_CUBE_SLOT;
+   }
 };
 
 RenderProbeMgr* RenderProbeMgr::getProbeManager()

+ 137 - 101
Templates/Full/game/shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl

@@ -29,9 +29,13 @@ uniform float4    bbMaxArray[MAX_PROBES];
 uniform float4    probeConfigData[MAX_PROBES];   //r,g,b/mode,radius,atten
 
 #if DEBUGVIZ_CONTRIB
-uniform float4    probeContribColors[MAX_PROBES]; 
+uniform float4    probeContribColors[MAX_PROBES];
 #endif
 
+TORQUE_UNIFORM_SAMPLERCUBE(skylightPrefilterMap, 6);
+TORQUE_UNIFORM_SAMPLERCUBE(skylightIrradMap, 7);
+uniform float hasSkylight;
+
 //Probe IBL stuff
 struct ProbeData
 {
@@ -64,17 +68,18 @@ float defineSphereSpaceInfluence(Surface surface, ProbeData probe, float3 wsEyeR
 
 float getDistBoxToPoint(float3 pt, float3 extents)
 {
-      float3 d = max(max(-extents - pt, 0), pt - extents);
-      return max(max(d.x,d.y),d.z);
+   float3 d = max(max(-extents - pt, 0), pt - extents);
+   return max(max(d.x, d.y), d.z);
 }
 
 float defineBoxSpaceInfluence(Surface surface, ProbeData probe, float3 wsEyeRay)
 {
    float3 surfPosLS = mul(probe.worldToLocal, float4(surface.P, 1.0)).xyz;
-   float atten = probe.attenuation;
+   float probeattenuationvalue = 0.5; // feed meh
+   float atten = 1.0 - probeattenuationvalue;
    float baseVal = 0.25;
-   float dist = getDistBoxToPoint(surfPosLS,float3(baseVal,baseVal,baseVal));
-   return saturate(smoothstep(baseVal+0.0001,atten*baseVal,dist));
+   float dist = getDistBoxToPoint(surfPosLS, float3(baseVal, baseVal, baseVal));
+   return saturate(smoothstep(baseVal + 0.0001, atten*baseVal, dist));
 }
 
 // Box Projected IBL Lighting
@@ -82,12 +87,12 @@ float defineBoxSpaceInfluence(Surface surface, ProbeData probe, float3 wsEyeRay)
 // and https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
 float3 boxProject(Surface surface, ProbeData probe)
 {
-   float3 RayLS = mul(probe.worldToLocal, float4(surface.R,0.0)).xyz;
-   float3 PositionLS = mul( probe.worldToLocal,  float4(surface.P,1.0)).xyz;
-   
-   float3 unit = probe.boxMax-probe.boxMin;
-   float3 plane1vec  = (unit/2 - PositionLS) / RayLS;
-   float3 plane2vec = (-unit/2 - PositionLS) / RayLS;
+   float3 RayLS = mul(probe.worldToLocal, float4(surface.R, 0.0)).xyz;
+   float3 PositionLS = mul(probe.worldToLocal, float4(surface.P, 1.0)).xyz;
+
+   float3 unit = probe.boxMax - probe.boxMin;
+   float3 plane1vec = (unit / 2 - PositionLS) / RayLS;
+   float3 plane2vec = (-unit / 2 - PositionLS) / RayLS;
    float3 furthestPlane = max(plane1vec, plane2vec);
    float dist = min(min(furthestPlane.x, furthestPlane.y), furthestPlane.z);
    float3 posonbox = surface.P + surface.R * dist;
@@ -99,8 +104,7 @@ float3 iblBoxDiffuse(Surface surface, ProbeData probe)
 {
    float3 dir = boxProject(surface, probe);
 
-   float lod = surface.roughness*cubeMips;
-   float3 color = TORQUE_TEXCUBEARRAYLOD(irradianceCubemapAR, dir, probe.probeIdx, lod).xyz;
+   float3 color = TORQUE_TEXCUBEARRAYLOD(irradianceCubemapAR, dir, probe.probeIdx, 0).xyz;
    if (probe.contribution>0)
       return color*probe.contribution;
    else
@@ -154,129 +158,163 @@ float3 iblSkylightSpecular(Surface surface, ProbeData probe)
    return color;
 }
 
-float4 main( PFXVertToPix IN ) : SV_TARGET
+float4 main(PFXVertToPix IN) : SV_TARGET
 {
    //unpack normal and linear depth 
    float4 normDepth = TORQUE_DEFERRED_UNCONDITION(deferredBuffer, IN.uv0.xy);
 
    //create surface
-   Surface surface = createSurface( normDepth, TORQUE_SAMPLER2D_MAKEARG(colorBuffer),TORQUE_SAMPLER2D_MAKEARG(matInfoBuffer),
-                                    IN.uv0.xy, eyePosWorld, IN.wsEyeRay, cameraToWorld);
+   Surface surface = createSurface(normDepth, TORQUE_SAMPLER2D_MAKEARG(colorBuffer),TORQUE_SAMPLER2D_MAKEARG(matInfoBuffer),
+      IN.uv0.xy, eyePosWorld, IN.wsEyeRay, cameraToWorld);
 
    //early out if emissive
    if (getFlag(surface.matFlag, 0))
-   {   
+   {
       discard;
-   }     
+   }
+
+   float alpha = 1;
 
    int i = 0;
    float blendFactor[MAX_PROBES];
    float blendSum = 0;
    float blendFacSum = 0;
    float invBlendSum = 0;
-   int skyID = 0;
    float probehits = 0;
    //Set up our struct data
    ProbeData probes[MAX_PROBES];
 
-   //Process prooooobes
-   for (i = 0; i < numProbes; ++i)
+   if (alpha > 0)
    {
-      probes[i].wsPosition = inProbePosArray[i].xyz;
-      probes[i].radius = probeConfigData[i].g;
-      probes[i].boxMin = bbMinArray[i].xyz;
-      probes[i].boxMax = bbMaxArray[i].xyz;
-      probes[i].refPosition = inRefPosArray[i].xyz;
-      probes[i].attenuation = probeConfigData[i].b;
-      probes[i].worldToLocal = worldToObjArray[i];
-      probes[i].probeIdx = i;
-      probes[i].type = probeConfigData[i].r;
-      probes[i].contribution = 0; 
-
-      if (probes[i].type == 0) //box
-      {
-         probes[i].contribution = defineBoxSpaceInfluence(surface, probes[i], IN.wsEyeRay);
-         probehits++;
-      }
-      else if (probes[i].type == 1) //sphere
-      {
-         probes[i].contribution = defineSphereSpaceInfluence(surface, probes[i], IN.wsEyeRay);
-         probehits++;
-      }
-      else //skylight
+      //Process prooooobes
+      for (i = 0; i < numProbes; ++i)
       {
-         //
-         //probes[i].contribution = defineSkylightInfluence(surface, probes[i], IN.wsEyeRay);
-         skyID = i;
-      }
-
-      if (probes[i].contribution>1 || probes[i].contribution<0)
+         probes[i].wsPosition = inProbePosArray[i].xyz;
+         probes[i].radius = probeConfigData[i].g;
+         probes[i].boxMin = bbMinArray[i].xyz;
+         probes[i].boxMax = bbMaxArray[i].xyz;
+         probes[i].refPosition = inRefPosArray[i].xyz;
+         probes[i].attenuation = probeConfigData[i].b;
+         probes[i].worldToLocal = worldToObjArray[i];
+         probes[i].probeIdx = i;
+         probes[i].type = probeConfigData[i].r;
          probes[i].contribution = 0;
 
-      blendSum += probes[i].contribution;
-      invBlendSum += (1.0f - probes[i].contribution);
-   }
+         if (probes[i].type == 0) //box
+         {
+            probes[i].contribution = defineBoxSpaceInfluence(surface, probes[i], IN.wsEyeRay);
+            probehits++;
+         }
+         else if (probes[i].type == 1) //sphere
+         {
+            probes[i].contribution = defineSphereSpaceInfluence(surface, probes[i], IN.wsEyeRay);
+            probehits++;
+         }
 
-   // Weight0 = normalized NDF, inverted to have 1 at center, 0 at boundary.
-   // And as we invert, we need to divide by Num-1 to stay normalized (else sum is > 1). 
-   // respect constraint B.
-   // Weight1 = normalized inverted NDF, so we have 1 at center, 0 at boundary
-   // and respect constraint A.
-   for (i = 0; i < numProbes; i++)
-   {
-      if (probehits>1.0)
-      {
-         blendFactor[i] = ((probes[i].contribution / blendSum)) / (probehits - 1);
-         blendFactor[i] *= ((probes[i].contribution) / invBlendSum);
-         blendFacSum += blendFactor[i];
+         if (probes[i].contribution>1 || probes[i].contribution<0)
+            probes[i].contribution = 0;
+
+         blendSum += probes[i].contribution;
+         invBlendSum += (1.0f - probes[i].contribution);
+
+         alpha -= probes[i].contribution;
       }
-      else
+
+      // Weight0 = normalized NDF, inverted to have 1 at center, 0 at boundary.
+      // And as we invert, we need to divide by Num-1 to stay normalized (else sum is > 1). 
+      // respect constraint B.
+      // Weight1 = normalized inverted NDF, so we have 1 at center, 0 at boundary
+      // and respect constraint A.
+      for (i = 0; i < numProbes; i++)
       {
-         blendFactor[i] = probes[i].contribution;
-         blendFacSum = probes[i].contribution;
+         if (probehits>1.0)
+         {
+            blendFactor[i] = ((probes[i].contribution / blendSum)) / (probehits - 1);
+            blendFactor[i] *= ((probes[i].contribution) / invBlendSum);
+            blendFacSum += blendFactor[i];
+         }
+         else
+         {
+            blendFactor[i] = probes[i].contribution;
+            blendFacSum = probes[i].contribution;
+         }
       }
-   }
 
-   // Normalize blendVal
+
+      // Normalize blendVal
 #if DEBUGVIZ_ATTENUATION == 0 //this can likely be removed when we fix the above normalization behavior
-   if (blendFacSum == 0.0f) // Possible with custom weight
-   {
-      blendFacSum = 1.0f;
-   }
+      if (blendFacSum == 0.0f) // Possible with custom weight
+      {
+         blendFacSum = 1.0f;
+      }
 #endif
-    //use probehits for sharp cuts when singular, 
-    //blendSum when wanting blend on all edging
-   if (blendSum>1.0)
-   {
-      float invBlendSumWeighted = 1.0f / blendFacSum;
-      for (i = 0; i < numProbes; ++i)
+
+      //use probehits for sharp cuts when singular, 
+      //blendSum when wanting blend on all edging
+      if (blendSum>1.0)
       {
-         blendFactor[i] *= invBlendSumWeighted;
-         probes[i].contribution = blendFactor[i];
+         float invBlendSumWeighted = 1.0f / blendFacSum;
+         for (i = 0; i < numProbes; ++i)
+         {
+            blendFactor[i] *= invBlendSumWeighted;
+            probes[i].contribution = blendFactor[i];
+
+            alpha -= probes[i].contribution;
+         }
       }
-   }
+
 #if DEBUGVIZ_ATTENUATION == 1
-   float attenVis = 0;
-   for (i = 0; i < numProbes; ++i)
-   {
+      /*float attenVis = 0;
+      for (i = 0; i < numProbes; ++i)
+      {
       attenVis += probes[i].contribution;
-   }
-   return float4(attenVis, attenVis, attenVis, 1);
+      }
+      return float4(attenVis, attenVis, attenVis, 1);*/
+      return float4(alpha, alpha, alpha, 1);
 #endif
 
 #if DEBUGVIZ_CONTRIB == 1
 
-   float3 finalContribColor = float3(0, 0, 0);
-   for (i = 0; i < numProbes; ++i)
-   {
-      if (probes[i].contribution == 0)
-         continue;
+      float3 finalContribColor = float3(0, 0, 0);
+      for (i = 0; i < numProbes; ++i)
+      {
+         if (probes[i].contribution == 0)
+            continue;
 
-      finalContribColor += probes[i].contribution * probeContribColors[i].rgb;
+         finalContribColor += probes[i].contribution * probeContribColors[i].rgb;
+      }
+
+      return float4(finalContribColor, 1);
+#endif
    }
 
-   return float4(finalContribColor, 1);
+   if (hasSkylight && alpha == 1)
+   {
+      float2 brdf = TORQUE_TEX2DLOD(BRDFTexture, float4(surface.roughness, surface.NdotV, 0.0, 0.0)).xy;
+      float lod = surface.roughness*cubeMips;
+#if DEBUGVIZ_SPECCUBEMAP == 0 && DEBUGVIZ_DIFFCUBEMAP == 0
+      float3 specular = TORQUE_TEXCUBELOD(skylightPrefilterMap, float4(surface.R, lod)).xyz * (brdf.x + brdf.y);
+      float3 irradiance = TORQUE_TEXCUBELOD(skylightIrradMap, float4(surface.R, 0)).xyz;
+
+      float3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness);
+
+      //energy conservation
+      float3 kD = 1.0.xxx - F;
+      kD *= 1.0 - surface.metalness;
+
+      float3 diffuse = kD * irradiance * surface.baseColor.rgb;
+      float4 finalColor = float4(diffuse + specular * surface.ao, alpha);
+      return finalColor;
+#elif DEBUGVIZ_SPECCUBEMAP == 1 && DEBUGVIZ_DIFFCUBEMAP == 0
+      float3 specular = TORQUE_TEXCUBELOD(skylightPrefilterMap, float4(surface.R, lod)).xyz;
+      float4 finalColor = float4(specular, 1);
+      return finalColor;
+#elif DEBUGVIZ_DIFFCUBEMAP == 1
+      float3 irradiance = TORQUE_TEXCUBELOD(skylightIrradMap, float4(surface.R, 0)).xyz;
+      float4 finalColor = float4(irradiance, 1);
+      return finalColor;
 #endif
+   }
 
 #if DEBUGVIZ_SPECCUBEMAP == 0 && DEBUGVIZ_DIFFCUBEMAP == 0
 
@@ -292,17 +330,15 @@ float4 main( PFXVertToPix IN ) : SV_TARGET
    {
       if (probes[i].contribution == 0)
          continue;
-                  
+
       if (probes[i].type == 2) //skip skylight
          continue;
-         
+
       irradiance += iblBoxDiffuse(surface, probes[i]);
       specular += F*iblBoxSpecular(surface, probes[i]);
-      contrib +=probes[i].contribution;
+      contrib += probes[i].contribution;
    }
    contrib = saturate(contrib);
-   irradiance = lerp(iblSkylightDiffuse(surface, probes[skyID]),irradiance,contrib);
-   specular = lerp(F*iblSkylightSpecular(surface, probes[skyID]),specular,contrib);
 
    //final diffuse color
    float3 diffuse = kD * irradiance * surface.baseColor.rgb;
@@ -328,7 +364,7 @@ float4 main( PFXVertToPix IN ) : SV_TARGET
    return float4(cubeColor, 1);
 
 #elif DEBUGVIZ_DIFFCUBEMAP == 1
-   
+
    float3 cubeColor = float3(0, 0, 0);
    for (i = 0; i < numProbes; ++i)
    {