Browse Source

Update GFXTextureManager and GBitmap

GBitmap Changes:
Added all other formats to gbitmap that we support
gbitmap now supports cubemaps
added converters for all these other formats
added stb_image_resize for extrudemips so we can extrude mipmaps for all other formats

GFXTextureManager
Can now directly make cubemaps and texture arrays based on the GFXTextureProfile
API implementations for all functions that cubemaps and arrays needed
marauder2k7 2 weeks ago
parent
commit
3aef90a6bc
66 changed files with 3209 additions and 1833 deletions
  1. 12 12
      Engine/source/T3D/lighting/IBLUtilities.cpp
  2. 6 6
      Engine/source/T3D/lighting/IBLUtilities.h
  3. 28 13
      Engine/source/T3D/lighting/reflectionProbe.cpp
  4. 3 3
      Engine/source/T3D/lighting/reflectionProbe.h
  5. 15 0
      Engine/source/assets/assetManager.cpp
  6. 1 1
      Engine/source/environment/scatterSky.cpp
  7. 2 2
      Engine/source/environment/waterObject.cpp
  8. 67 45
      Engine/source/gfx/D3D11/gfxD3D11Target.cpp
  9. 3 1
      Engine/source/gfx/D3D11/gfxD3D11Target.h
  10. 166 92
      Engine/source/gfx/D3D11/gfxD3D11TextureManager.cpp
  11. 2 1
      Engine/source/gfx/D3D11/gfxD3D11TextureManager.h
  12. 237 159
      Engine/source/gfx/D3D11/gfxD3D11TextureObject.cpp
  13. 49 27
      Engine/source/gfx/D3D11/gfxD3D11TextureObject.h
  14. 9 6
      Engine/source/gfx/Null/gfxNullDevice.cpp
  15. 400 112
      Engine/source/gfx/bitmap/bitmapUtils.cpp
  16. 132 5
      Engine/source/gfx/bitmap/bitmapUtils.h
  17. 13 36
      Engine/source/gfx/bitmap/cubemapSaver.cpp
  18. 3 3
      Engine/source/gfx/bitmap/cubemapSaver.h
  19. 74 32
      Engine/source/gfx/bitmap/ddsFile.cpp
  20. 2 0
      Engine/source/gfx/bitmap/ddsFile.h
  21. 513 278
      Engine/source/gfx/bitmap/gBitmap.cpp
  22. 34 19
      Engine/source/gfx/bitmap/gBitmap.h
  23. 96 149
      Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp
  24. 179 178
      Engine/source/gfx/bitmap/loaders/stb/stb_image.h
  25. 308 224
      Engine/source/gfx/bitmap/loaders/stb/stb_image_resize2.h
  26. 34 0
      Engine/source/gfx/gfxAPI.cpp
  27. 2 0
      Engine/source/gfx/gfxAPI.h
  28. 6 2
      Engine/source/gfx/gfxShader.cpp
  29. 3 1
      Engine/source/gfx/gfxShader.h
  30. 1 1
      Engine/source/gfx/gfxTarget.h
  31. 6 6
      Engine/source/gfx/gfxTextureHandle.cpp
  32. 4 3
      Engine/source/gfx/gfxTextureHandle.h
  33. 7 6
      Engine/source/gfx/gfxTextureManager.cpp
  34. 6 3
      Engine/source/gfx/gfxTextureManager.h
  35. 2 0
      Engine/source/gfx/gfxTextureObject.cpp
  36. 12 2
      Engine/source/gfx/gfxTextureObject.h
  37. 15 0
      Engine/source/gfx/gfxTextureProfile.cpp
  38. 16 6
      Engine/source/gfx/gfxTextureProfile.h
  39. 280 101
      Engine/source/gfx/gl/gfxGLTextureManager.cpp
  40. 3 2
      Engine/source/gfx/gl/gfxGLTextureManager.h
  41. 215 22
      Engine/source/gfx/gl/gfxGLTextureObject.cpp
  42. 8 4
      Engine/source/gfx/gl/gfxGLTextureObject.h
  43. 98 135
      Engine/source/gfx/gl/gfxGLTextureTarget.cpp
  44. 11 11
      Engine/source/gfx/gl/gfxGLTextureTarget.h
  45. 22 3
      Engine/source/gfx/gl/sdl/gfxGLDevice.sdl.cpp
  46. 23 9
      Engine/source/gfx/sim/cubemapData.cpp
  47. 1 1
      Engine/source/gfx/sim/cubemapData.h
  48. 10 36
      Engine/source/lighting/shadowMap/cubeLightShadowMap.cpp
  49. 0 9
      Engine/source/lighting/shadowMap/cubeLightShadowMap.h
  50. 7 0
      Engine/source/lighting/shadowMap/lightShadowMap.cpp
  51. 1 0
      Engine/source/lighting/shadowMap/lightShadowMap.h
  52. 3 3
      Engine/source/materials/materialDefinition.h
  53. 2 2
      Engine/source/materials/processedCustomMaterial.cpp
  54. 1 1
      Engine/source/materials/processedMaterial.h
  55. 3 3
      Engine/source/materials/processedShaderMaterial.cpp
  56. 1 1
      Engine/source/materials/sceneData.h
  57. 1 1
      Engine/source/platformWin32/winAsmBlit.cpp
  58. 1 1
      Engine/source/renderInstance/renderDeferredMgr.cpp
  59. 1 1
      Engine/source/renderInstance/renderMeshMgr.cpp
  60. 1 1
      Engine/source/renderInstance/renderPassManager.h
  61. 26 23
      Engine/source/renderInstance/renderProbeMgr.cpp
  62. 2 8
      Engine/source/renderInstance/renderProbeMgr.h
  63. 1 1
      Engine/source/renderInstance/renderTranslucentMgr.cpp
  64. 13 14
      Engine/source/scene/reflector.cpp
  65. 3 3
      Engine/source/scene/reflector.h
  66. 3 3
      Engine/source/ts/tsRenderState.h

+ 12 - 12
Engine/source/T3D/lighting/IBLUtilities.cpp

@@ -31,7 +31,7 @@
 
 namespace IBLUtilities
 {
-   void GenerateIrradianceMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, GFXCubemapHandle &cubemapOut)
+   void GenerateIrradianceMap(GFXTextureTargetRef renderTarget, GFXTexHandle cubemap, GFXTexHandle &cubemapOut)
    {
       GFXTransformSaver saver;
 
@@ -65,11 +65,11 @@ namespace IBLUtilities
       GFX->setShaderConstBuffer(irrConsts);
       GFX->setStateBlock(irrStateBlock);
       GFX->setVertexBuffer(NULL);
-      GFX->setCubeTexture(0, cubemap);
+      GFX->setTexture(0, cubemap);
 
       for (U32 i = 0; i < 6; i++)
       {
-         renderTarget->attachTexture(GFXTextureTarget::Color0, cubemapOut, i);
+         renderTarget->attachTexture(GFXTextureTarget::Color0, cubemapOut, 0,0, i);
          irrConsts->setSafe(irrFaceSC, (S32)i);
          GFX->setActiveRenderTarget(renderTarget);
          GFX->clear(GFXClearTarget, LinearColorF::BLACK, 1.0f, 0);
@@ -80,7 +80,7 @@ namespace IBLUtilities
       GFX->popActiveRenderTarget();
    }
 
-   void GenerateAndSaveIrradianceMap(String outputPath, S32 resolution, GFXCubemapHandle cubemap, GFXCubemapHandle &cubemapOut)
+   void GenerateAndSaveIrradianceMap(String outputPath, S32 resolution, GFXTexHandle cubemap, GFXTexHandle &cubemapOut)
    {
       if (outputPath.isEmpty())
       {
@@ -101,7 +101,7 @@ namespace IBLUtilities
       }
    }
 
-   void SaveCubeMap(String outputPath, GFXCubemapHandle &cubemap)
+   void SaveCubeMap(String outputPath, GFXTexHandle &cubemap)
    {
       if (outputPath.isEmpty())
       {
@@ -118,7 +118,7 @@ namespace IBLUtilities
       }
    }
 
-   void GeneratePrefilterMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, U32 mipLevels, GFXCubemapHandle &cubemapOut)
+   void GeneratePrefilterMap(GFXTextureTargetRef renderTarget, GFXTexHandle cubemap, U32 mipLevels, GFXTexHandle &cubemapOut)
    {
       GFXTransformSaver saver;
 
@@ -153,9 +153,9 @@ namespace IBLUtilities
       GFX->pushActiveRenderTarget();
       GFX->setShader(prefilterShader);
       GFX->setShaderConstBuffer(prefilterConsts);
-      GFX->setCubeTexture(0, cubemap);
+      GFX->setTexture(0, cubemap);
 
-      U32 prefilterSize = cubemapOut->getSize();
+      U32 prefilterSize = cubemapOut->getWidth();
 
       U32 resolutionSize = prefilterSize;
 
@@ -171,7 +171,7 @@ namespace IBLUtilities
             prefilterConsts->setSafe(prefilterRoughnessSC, roughness);
             prefilterConsts->setSafe(prefilterMipSizeSC, mipSize);
             U32 size = prefilterSize * mPow(0.5f, mip);
-            renderTarget->attachTexture(GFXTextureTarget::Color0, cubemapOut, face);
+            renderTarget->attachTexture(GFXTextureTarget::Color0, cubemapOut, 0,0 ,face);
             GFX->setActiveRenderTarget(renderTarget, false);//we set the viewport ourselves
             GFX->setViewport(RectI(0, 0, size, size));
             GFX->clear(GFXClearTarget, LinearColorF::BLACK, 1.0f, 0);
@@ -183,7 +183,7 @@ namespace IBLUtilities
       GFX->popActiveRenderTarget();
    }
 
-   void GenerateAndSavePrefilterMap(String outputPath, S32 resolution, GFXCubemapHandle cubemap, U32 mipLevels, GFXCubemapHandle &cubemapOut)
+   void GenerateAndSavePrefilterMap(String outputPath, S32 resolution, GFXTexHandle cubemap, U32 mipLevels, GFXTexHandle &cubemapOut)
    {
       if (outputPath.isEmpty())
       {
@@ -504,7 +504,7 @@ namespace IBLUtilities
    //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])
+   void calculateSHTerms(GFXTexHandle cubemap, LinearColorF SHTerms[9], F32 SHConstants[5])
    {
       if (!cubemap)
          return;
@@ -525,7 +525,7 @@ namespace IBLUtilities
          VectorF(0.0f, 0.0f, -1.0f),
       };
 
-      U32 cubemapResolution = cubemap->getSize();
+      U32 cubemapResolution = cubemap->getWidth();
 
       GBitmap* cubeFaceBitmaps[6];
 

+ 6 - 6
Engine/source/T3D/lighting/IBLUtilities.h

@@ -38,13 +38,13 @@
 
 namespace IBLUtilities
 {
-   void GenerateIrradianceMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, GFXCubemapHandle &cubemapOut);
-   void GenerateAndSaveIrradianceMap(String outputPath, S32 resolution, GFXCubemapHandle cubemap, GFXCubemapHandle &cubemapOut);
+   void GenerateIrradianceMap(GFXTextureTargetRef renderTarget, GFXTexHandle cubemap, GFXTexHandle& cubemapOut);
+   void GenerateAndSaveIrradianceMap(String outputPath, S32 resolution, GFXTexHandle cubemap, GFXTexHandle& cubemapOut);
 
-   void GeneratePrefilterMap(GFXTextureTargetRef renderTarget, GFXCubemapHandle cubemap, U32 mipLevels, GFXCubemapHandle &cubemapOut);
-   void GenerateAndSavePrefilterMap(String outputPath, S32 resolution, GFXCubemapHandle cubemap, U32 mipLevels, GFXCubemapHandle &cubemapOut);
+   void GeneratePrefilterMap(GFXTextureTargetRef renderTarget, GFXTexHandle cubemap, U32 mipLevels, GFXTexHandle &cubemapOut);
+   void GenerateAndSavePrefilterMap(String outputPath, S32 resolution, GFXTexHandle cubemap, U32 mipLevels, GFXTexHandle &cubemapOut);
 
-   void SaveCubeMap(String outputPath, GFXCubemapHandle &cubemap);
+   void SaveCubeMap(String outputPath, GFXTexHandle &cubemap);
 
    void bakeReflection(String outputPath, S32 resolution);
 
@@ -60,7 +60,7 @@ namespace IBLUtilities
    //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]);
+   void calculateSHTerms(GFXTexHandle cubemap, LinearColorF SHTerms[9], F32 SHConstants[5]);
 
    F32 texelSolidAngle(F32 aU, F32 aV, U32 width, U32 height);
 

+ 28 - 13
Engine/source/T3D/lighting/reflectionProbe.cpp

@@ -86,6 +86,13 @@ ImplementEnumType(ReflectionModeEnum,
 //{ ReflectionProbe::DynamicCubemap, "Dynamic Cubemap", "Uses a cubemap baked from the probe's current position, updated at a set rate" },
    EndImplementEnumType;
 
+void ReflectionProbe::ProbeInfo::clear()
+{
+   mPrefilterCubemap.free();
+   mIrradianceCubemap.free();
+}
+
+
 //-----------------------------------------------------------------------------
 // Object setup and teardown
 //-----------------------------------------------------------------------------
@@ -146,11 +153,18 @@ ReflectionProbe::~ReflectionProbe()
    if (mReflectionModeType == StaticCubemap && mStaticCubemap)
       mStaticCubemap->deleteObject();
 
-   if (mIrridianceMap)
-      mIrridianceMap->deleteObject();
+   mProbeInfo.clear();
+
+   if (mIrridianceMap) {
+      if (mIrridianceMap->isProperlyAdded() && !mIrridianceMap->isRemoved())
+         mIrridianceMap->deleteObject();
+   }
 
    if (mPrefilterMap)
-      mPrefilterMap->deleteObject();
+   {
+      if (mPrefilterMap->isProperlyAdded() && !mPrefilterMap->isRemoved())
+         mPrefilterMap->deleteObject();
+   }
 }
 
 //-----------------------------------------------------------------------------
@@ -603,7 +617,7 @@ void ReflectionProbe::processBakedCubemap()
       return;
 
    String irrPath = getIrradianceMapPath();
-   if (Platform::isFile(irrPath))
+   if ((mIrridianceMap == nullptr || mIrridianceMap->mCubemap.isNull()) && Platform::isFile(irrPath))
    {
       mIrridianceMap->setCubemapFile(FileName(irrPath));
       mIrridianceMap->updateFaces();
@@ -616,7 +630,7 @@ void ReflectionProbe::processBakedCubemap()
    }
 
    String prefilPath = getPrefilterMapPath();
-   if (Platform::isFile(prefilPath))
+   if ((mPrefilterMap == nullptr || mPrefilterMap->mCubemap.isNull()) && Platform::isFile(prefilPath))
    {
       mPrefilterMap->setCubemapFile(FileName(prefilPath));
       mPrefilterMap->updateFaces();
@@ -631,7 +645,7 @@ void ReflectionProbe::processBakedCubemap()
    mProbeInfo.mPrefilterCubemap = mPrefilterMap->mCubemap;
    mProbeInfo.mIrradianceCubemap = mIrridianceMap->mCubemap;
 
-   if (mEnabled && mProbeInfo.mPrefilterCubemap->isInitialized() && mProbeInfo.mIrradianceCubemap->isInitialized())
+   if (mEnabled && !mProbeInfo.mPrefilterCubemap.isNull() && !mProbeInfo.mIrradianceCubemap.isNull())
    {
       //mProbeInfo.mIsEnabled = true;
 
@@ -698,7 +712,7 @@ void ReflectionProbe::processStaticCubemap()
          return;
       }
 
-      if (mStaticCubemap->mCubemap == nullptr)
+      if (mStaticCubemap->mCubemap.isNull())
       {
          mStaticCubemap->createMap();
          mStaticCubemap->updateFaces();
@@ -706,13 +720,13 @@ void ReflectionProbe::processStaticCubemap()
 
       if (mUseHDRCaptures)
       {
-         mIrridianceMap->mCubemap->initDynamic(mPrefilterSize, GFXFormatR16G16B16A16F);
-         mPrefilterMap->mCubemap->initDynamic(mPrefilterSize, GFXFormatR16G16B16A16F);
+         mIrridianceMap->mCubemap.set(mPrefilterSize, mPrefilterSize, GFXFormatR16G16B16A16F, &GFXCubemapRenderTargetProfile, "ReflectionProbe::mIrridianceMap_HDR");
+         mPrefilterMap->mCubemap.set(mPrefilterSize, mPrefilterSize, GFXFormatR16G16B16A16F, &GFXCubemapRenderTargetProfile, "ReflectionProbe::mPrefilterMap_HDR");
       }
       else
       {
-         mIrridianceMap->mCubemap->initDynamic(mPrefilterSize, GFXFormatR8G8B8A8);
-         mPrefilterMap->mCubemap->initDynamic(mPrefilterSize, GFXFormatR8G8B8A8);
+         mIrridianceMap->mCubemap.set(mPrefilterSize, mPrefilterSize, GFXFormatR8G8B8A8, &GFXCubemapRenderTargetProfile, "ReflectionProbe::mIrridianceMap");
+         mPrefilterMap->mCubemap.set(mPrefilterSize, mPrefilterSize, GFXFormatR8G8B8A8, &GFXCubemapRenderTargetProfile, "ReflectionProbe::mPrefilterMap");
       }
 
       GFXTextureTargetRef renderTarget = GFX->allocRenderToTextureTarget(false);
@@ -730,7 +744,7 @@ void ReflectionProbe::processStaticCubemap()
       mProbeInfo.mIrradianceCubemap = mIrridianceMap->mCubemap;
    }
 
-   if (mEnabled && mProbeInfo.mPrefilterCubemap->isInitialized() && mProbeInfo.mIrradianceCubemap->isInitialized())
+   if (mEnabled && mProbeInfo.mPrefilterCubemap.isValid() && mProbeInfo.mIrradianceCubemap.isValid())
    {
       mProbeInfo.mIsEnabled = true;
 
@@ -1009,7 +1023,7 @@ void ReflectionProbe::setPreviewMatParameters(SceneRenderState* renderState, Bas
    GFX->setTexture(0, deferredTexObject);
 
    //Set the cubemap
-   GFX->setCubeTexture(1, mPrefilterMap->mCubemap);
+   GFX->setTexture(1, mPrefilterMap->mCubemap);
 
    //Set the invViewMat
    MatrixSet &matrixSet = renderState->getRenderPass()->getMatrixSet();
@@ -1036,3 +1050,4 @@ DefineEngineMethod(ReflectionProbe, Bake, void, (), ,
       clientProbe->bake();
    }
 }
+

+ 3 - 3
Engine/source/T3D/lighting/reflectionProbe.h

@@ -94,8 +94,8 @@ public:
 
       F32 mScore;
 
-      GFXCubemapHandle mPrefilterCubemap;
-      GFXCubemapHandle mIrradianceCubemap;
+      GFXTexHandle mPrefilterCubemap;
+      GFXTexHandle mIrradianceCubemap;
 
       /// The priority of this light used for
       /// light and shadow scoring.
@@ -233,7 +233,7 @@ protected:
    /// </summary>
    StringTableEntry mCubemapName;
    CubemapData *mStaticCubemap;
-   GFXCubemapHandle  mDynamicCubemap;
+   GFXTexHandle  mDynamicCubemap;
 
    //String cubeDescName;
    //U32 cubeDescId;

+ 15 - 0
Engine/source/assets/assetManager.cpp

@@ -112,6 +112,21 @@ bool AssetManager::onAdd()
 
 void AssetManager::onRemove()
 {
+    // Remove all private assets explicitly before purge.
+    Vector<AssetDefinition*> assetDefinitions;
+    
+    // at this point all module assets should have been unloaded.
+    for (typeDeclaredAssetsHash::iterator assetItr = mDeclaredAssets.begin(); assetItr != mDeclaredAssets.end(); ++assetItr)
+    {
+       assetDefinitions.push_back(assetItr->value);
+    }
+    
+    for (Vector<AssetDefinition*>::iterator assetItr = assetDefinitions.begin(); assetItr != assetDefinitions.end(); ++assetItr)
+    {
+       AssetDefinition* pAssetDefinition = *assetItr;
+       unloadAsset(pAssetDefinition);
+    }
+
     // Do we have an asset tags manifest?
     if ( !mAssetTagsManifest.isNull() )
     {

+ 1 - 1
Engine/source/environment/scatterSky.cpp

@@ -1090,7 +1090,7 @@ void ScatterSky::_render( ObjectRenderInst *ri, SceneRenderState *state, BaseMat
       if ( !mNightCubemap->mCubemap )
          mNightCubemap->createMap();
 
-      GFX->setCubeTexture( 0, mNightCubemap->mCubemap );
+      GFX->setTexture( 0, mNightCubemap->mCubemap );
    }
    else
    {

+ 2 - 2
Engine/source/environment/waterObject.cpp

@@ -776,9 +776,9 @@ void WaterObject::setCustomTextures( S32 matIdx, U32 pass, const WaterMatParams
    }
 
    if ( ( matIdx == WaterMat || matIdx == BasicWaterMat ) && mCubemap )   
-      GFX->setCubeTexture( paramHandles.mCubemapSamplerSC->getSamplerRegister(pass), mCubemap->mCubemap );
+      GFX->setTexture( paramHandles.mCubemapSamplerSC->getSamplerRegister(pass), mCubemap->mCubemap );
    else if(paramHandles.mCubemapSamplerSC->getSamplerRegister(pass) != -1 )
-      GFX->setCubeTexture( paramHandles.mCubemapSamplerSC->getSamplerRegister(pass), NULL );
+      GFX->setTexture( paramHandles.mCubemapSamplerSC->getSamplerRegister(pass), NULL );
 }
 
 void WaterObject::drawUnderwaterFilter( SceneRenderState *state )

+ 67 - 45
Engine/source/gfx/D3D11/gfxD3D11Target.cpp

@@ -38,6 +38,7 @@ GFXD3D11TextureTarget::GFXD3D11TextureTarget(bool genMips)
       mResolveTargets[i] = NULL;
       mTargetViews[i] = NULL;
       mTargetSRViews[i] = NULL;
+      mTargetArrayIdx[i] = 0;
    }
 
    mGenMips = genMips;
@@ -57,9 +58,9 @@ GFXD3D11TextureTarget::~GFXD3D11TextureTarget()
    zombify();
 }
 
-void GFXD3D11TextureTarget::attachTexture( RenderSlot slot, GFXTextureObject *tex, U32 mipLevel/*=0*/, U32 zOffset /*= 0*/ )
+void GFXD3D11TextureTarget::attachTexture(RenderSlot slot, GFXTextureObject* tex, U32 mipLevel /*= 0*/, U32 zOffset /*= 0*/, U32 faceIndex /*= 0*/)
 {
-   GFXDEBUGEVENT_SCOPE( GFXPCD3D11TextureTarget_attachTexture, ColorI::RED );
+   GFXDEBUGEVENT_SCOPE(GFXPCD3D11TextureTarget_attachTexture, ColorI::RED);
 
    AssertFatal(slot < MaxRenderSlotId, "GFXD3D11TextureTarget::attachTexture - out of range slot.");
 
@@ -76,17 +77,17 @@ void GFXD3D11TextureTarget::attachTexture( RenderSlot slot, GFXTextureObject *te
    SAFE_RELEASE(mTargetViews[slot]);
    SAFE_RELEASE(mTargets[slot]);
    SAFE_RELEASE(mTargetSRViews[slot]);
-   
    mResolveTargets[slot] = NULL;
+   mTargetArrayIdx[slot] = 0;
 
-   if(slot == Color0)
+   if (slot == Color0)
    {
       mTargetSize = Point2I::Zero;
       mTargetFormat = GFXFormatR8G8B8A8;
    }
 
    // Are we clearing?
-   if(!tex)
+   if (!tex)
    {
       // Yup - just exit, it'll stay NULL.      
       return;
@@ -96,7 +97,7 @@ void GFXD3D11TextureTarget::attachTexture( RenderSlot slot, GFXTextureObject *te
    mTargetSRViews[slot] = NULL;
 
    // Take care of default targets
-   if( tex == GFXTextureTarget::sDefaultDepthStencil )
+   if (tex == GFXTextureTarget::sDefaultDepthStencil)
    {
       mTargets[slot] = D3D11->mDeviceDepthStencil;
       mTargetViews[slot] = D3D11->mDeviceDepthStencilView;
@@ -108,81 +109,100 @@ void GFXD3D11TextureTarget::attachTexture( RenderSlot slot, GFXTextureObject *te
       // Cast the texture object to D3D...
       AssertFatal(dynamic_cast<GFXD3D11TextureObject*>(tex), "GFXD3D11TextureTarget::attachTexture - invalid texture object.");
 
-      GFXD3D11TextureObject *d3dto = dynamic_cast<GFXD3D11TextureObject*>(tex);
+      GFXD3D11TextureObject* d3dto = dynamic_cast<GFXD3D11TextureObject*>(tex);
+      bool isCube = d3dto->isCubeMap();
 
       // Grab the surface level.
-      if( slot == DepthStencil )
-      {       
+      if (slot == DepthStencil)
+      {
          mTargets[slot] = d3dto->getSurface();
-         if ( mTargets[slot] )
+         if (mTargets[slot])
             mTargets[slot]->AddRef();
 
          mTargetViews[slot] = d3dto->getDSView();
-         if( mTargetViews[slot])
-            mTargetViews[slot]->AddRef();         
+         if (mTargetViews[slot])
+            mTargetViews[slot]->AddRef();
 
       }
       else
-      {         
-         // getSurface will almost always return NULL. It will only return non-NULL
-         // if the surface that it needs to render to is different than the mip level
-         // in the actual texture. This will happen with MSAA.
-         if( d3dto->getSurface() == NULL )
+      {
+         if (!isCube)
          {
-            
-            mTargets[slot] = d3dto->get2DTex();
-            mTargets[slot]->AddRef();
-            mTargetViews[slot] = d3dto->getRTView();
-            mTargetViews[slot]->AddRef();
-         } 
-         else 
+            if (d3dto->getSurface() == NULL)
+            {
+               mTargets[slot] = d3dto->get2DTex();
+               mTargets[slot]->AddRef();
+               mTargetViews[slot] = d3dto->getRTView();
+               mTargetViews[slot]->AddRef();
+            }
+            else
+            {
+               mTargets[slot] = d3dto->getSurface();
+               mTargets[slot]->AddRef();
+               mTargetViews[slot] = d3dto->getRTView();
+               mTargetViews[slot]->AddRef();
+               mResolveTargets[slot] = d3dto;
+            }
+         }
+         else
          {
-            mTargets[slot] = d3dto->getSurface();
-            mTargets[slot]->AddRef();
-            mTargetViews[slot]->AddRef();
-            mResolveTargets[slot] = d3dto;
+            // Cubemap render target face
+            mGenMips = false;
+            AssertFatal(faceIndex < 6, "Invalid cubemap face index!");
+            ID3D11RenderTargetView* faceRTV = d3dto->getCubeFaceRTView(faceIndex);
+            AssertFatal(faceRTV, "Cubemap face RTV is null!");
 
-            if ( tex && slot == Color0 )
+            mTargetArrayIdx[slot] = faceIndex;
+
+            if (d3dto->getSurface() == NULL)
+            {
+               mTargets[slot] = d3dto->get2DTex();
+               mTargets[slot]->AddRef();
+               mTargetViews[slot] = faceRTV;
+               mTargetViews[slot]->AddRef();
+            }
+            else
             {
-               mTargetSize.set( tex->getSize().x, tex->getSize().y );
-               mTargetFormat = tex->getFormat();
+               mTargets[slot] = d3dto->getSurface();
+               mTargets[slot]->AddRef();
+               mTargetViews[slot] = faceRTV;
+               mTargetViews[slot]->AddRef();
+               mResolveTargets[slot] = d3dto;
             }
          }
 
+         // For mip generation
          if (mGenMips)
          {
             mTargetSRViews[slot] = d3dto->getSRView();
-            mTargetSRViews[slot]->AddRef();
+            if (mTargetSRViews[slot])
+               mTargetSRViews[slot]->AddRef();
          }
       }
 
-      // Update surface size
-      if(slot == Color0)
+      // Update color target info
+      if (slot == Color0)
       {
-         ID3D11Texture2D *surface = mTargets[Color0];
-         if ( surface )
+         ID3D11Texture2D* surface = mTargets[Color0];
+         if (surface)
          {
             D3D11_TEXTURE2D_DESC sd;
             surface->GetDesc(&sd);
             mTargetSize = Point2I(sd.Width, sd.Height);
 
             S32 format = sd.Format;
-
             if (format == DXGI_FORMAT_R8G8B8A8_TYPELESS || format == DXGI_FORMAT_B8G8R8A8_TYPELESS)
-            {
                mTargetFormat = GFXFormatR8G8B8A8;
-               return;
+            else
+            {
+               GFXREVERSE_LOOKUP(GFXD3D11TextureFormat, GFXFormat, format);
+               mTargetFormat = (GFXFormat)format;
             }
-
-            GFXREVERSE_LOOKUP( GFXD3D11TextureFormat, GFXFormat, format );
-            mTargetFormat = (GFXFormat)format;
          }
       }
    }
-
 }
 
-
 void GFXD3D11TextureTarget::attachTexture( RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel/*=0*/ )
 {
    GFXDEBUGEVENT_SCOPE( GFXPCD3D11TextureTarget_attachTexture_Cubemap, ColorI::RED );
@@ -316,7 +336,9 @@ void GFXD3D11TextureTarget::resolveTo( GFXTextureObject *tex )
 
    D3D11_TEXTURE2D_DESC desc;
    mTargets[Color0]->GetDesc(&desc);
-   D3D11DEVICECONTEXT->CopySubresourceRegion(((GFXD3D11TextureObject*)(tex))->get2DTex(), 0, 0, 0, 0, mTargets[Color0], 0, NULL);
+   UINT mipLevels = desc.MipLevels ? desc.MipLevels : 1;
+   UINT subResource = D3D11CalcSubresource(0, mTargetArrayIdx[Color0], mipLevels);
+   D3D11DEVICECONTEXT->CopySubresourceRegion(((GFXD3D11TextureObject*)(tex))->get2DTex(), 0, 0, 0, 0, mTargets[Color0], subResource, NULL);
       
 }
 

+ 3 - 1
Engine/source/gfx/D3D11/gfxD3D11Target.h

@@ -49,6 +49,8 @@ class GFXD3D11TextureTarget : public GFXTextureTarget
 
    GFXFormat mTargetFormat;
 
+   U32 mTargetArrayIdx[MaxRenderSlotId];
+
 public:
 
    GFXD3D11TextureTarget(bool genMips);
@@ -57,7 +59,7 @@ public:
    // Public interface.
    const Point2I getSize() override { return mTargetSize; }
    GFXFormat getFormat() override { return mTargetFormat; }
-   void attachTexture(RenderSlot slot, GFXTextureObject *tex, U32 mipLevel=0, U32 zOffset = 0) override;
+   void attachTexture(RenderSlot slot, GFXTextureObject* tex, U32 mipLevel = 0, U32 zOffset = 0, U32 faceIndex = 0) override;
    void attachTexture(RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel=0) override;
    void resolve() override;
 

+ 166 - 92
Engine/source/gfx/D3D11/gfxD3D11TextureManager.cpp

@@ -52,7 +52,8 @@ void GFXD3D11TextureManager::_innerCreateTexture( GFXD3D11TextureObject *retTex,
                                                GFXTextureProfile *profile, 
                                                U32 numMipLevels,
                                                bool forceMips,
-                                               S32 antialiasLevel)
+                                               S32 antialiasLevel,
+															  U32 arraySize)
 {
    U32 usage = 0;
    U32 bindFlags = 0;
@@ -67,6 +68,9 @@ void GFXD3D11TextureManager::_innerCreateTexture( GFXD3D11TextureObject *retTex,
    retTex->isManaged = false;
    DXGI_FORMAT d3dTextureFormat = GFXD3D11TextureFormat[format];
 
+   if (retTex->isCubeMap())
+      miscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
+
    if( retTex->mProfile->isDynamic() )
    {
       usage = D3D11_USAGE_DYNAMIC;
@@ -199,7 +203,7 @@ void GFXD3D11TextureManager::_innerCreateTexture( GFXD3D11TextureObject *retTex,
 			D3D11_TEXTURE2D_DESC desc;
 		  
 			ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
-			desc.ArraySize = 1;
+			desc.ArraySize = arraySize * (retTex->isCubeMap() ? 6 : 1);
 			desc.BindFlags = bindFlags;
 			desc.CPUAccessFlags = cpuFlags;
 			desc.Format = d3dTextureFormat;
@@ -219,6 +223,7 @@ void GFXD3D11TextureManager::_innerCreateTexture( GFXD3D11TextureObject *retTex,
 
 			retTex->get2DTex()->GetDesc(&desc);
 			retTex->mMipLevels = desc.MipLevels;
+			retTex->mArraySize = arraySize;
 		}
 
 		// start creating the resource views...
@@ -267,6 +272,7 @@ GFXTextureObject *GFXD3D11TextureManager::_createTextureObject( U32 height,
                                                                U32 numMipLevels,
                                                                bool forceMips, 
                                                                S32 antialiasLevel,
+                                                               U32 arraySize,
                                                                GFXTextureObject *inTex )
 {
    GFXD3D11TextureObject *retTex;
@@ -278,11 +284,11 @@ GFXTextureObject *GFXD3D11TextureManager::_createTextureObject( U32 height,
    }      
    else
    {
-      retTex = new GFXD3D11TextureObject(GFX, profile);
+      retTex = new GFXD3D11TextureObject(GFX, profile, arraySize);
       retTex->registerResourceWithDevice(GFX);
    }
 
-   _innerCreateTexture(retTex, height, width, depth, format, profile, numMipLevels, forceMips, antialiasLevel);
+   _innerCreateTexture(retTex, height, width, depth, format, profile, numMipLevels, forceMips, antialiasLevel, arraySize);
 
    return retTex;
 }
@@ -295,7 +301,9 @@ bool GFXD3D11TextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *p
 
    // Check with profiler to see if we can do automatic mipmap generation.
    const bool supportsAutoMips = GFX->getCardProfiler()->queryProfile("autoMipMapLevel", true);
-
+   
+   const bool isCube = texture->isCubeMap() && pDL->getNumFaces() > 1;
+   const U32 numFaces = isCube ? 6 : 1;
    // Helper bool
    const bool isCompressedTexFmt = ImageUtil::isCompressedFormat(aTexture->mFormat);
 
@@ -312,98 +320,101 @@ bool GFXD3D11TextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *p
 
    bool isDynamic = texture->mProfile->isDynamic();
    // Fill the texture...
-   for( U32 i = 0; i < maxDownloadMip; i++ )
+   for (U32 face = 0; face < numFaces; ++face)
    {
-	   U32 subResource = D3D11CalcSubresource(i, 0, aTexture->mMipLevels);
+      for (U32 i = 0; i < maxDownloadMip; i++)
+      {
+         U32 subResource = D3D11CalcSubresource(i, face, aTexture->mMipLevels);
 
-	   if(!isDynamic)
-	   {
-		   U8* copyBuffer = NULL;
+         if (!isDynamic)
+         {
+            U8* copyBuffer = NULL;
 
-		   switch(texture->mFormat)
-			{
+            switch (texture->mFormat)
+            {
             case GFXFormatR8G8B8:
             case GFXFormatR8G8B8_SRGB:
-				{
-					PROFILE_SCOPE(Swizzle24_Upload);
-
-					U8* Bits = new U8[pDL->getWidth(i) * pDL->getHeight(i) * 4];
-					dMemcpy(Bits, pDL->getBits(i), pDL->getWidth(i) * pDL->getHeight(i) * 3);
-					bitmapConvertRGB_to_RGBX(&Bits, pDL->getWidth(i) * pDL->getHeight(i));
-					copyBuffer = new U8[pDL->getWidth(i) * pDL->getHeight(i) * 4];
-					
-					dev->getDeviceSwizzle32()->ToBuffer(copyBuffer, Bits, pDL->getWidth(i) * pDL->getHeight(i) * 4);
-					dev->getDeviceContext()->UpdateSubresource(texture->get2DTex(), subResource, NULL, copyBuffer, pDL->getWidth() * 4, pDL->getHeight() *4);
+            {
+               PROFILE_SCOPE(Swizzle24_Upload);
+
+               U8* Bits = new U8[pDL->getWidth(i) * pDL->getHeight(i) * 4];
+               dMemcpy(Bits, pDL->getBits(i, face), pDL->getWidth(i) * pDL->getHeight(i) * 3);
+               bitmapConvertRGB_to_RGBX(&Bits, pDL->getWidth(i) * pDL->getHeight(i));
+               copyBuffer = new U8[pDL->getWidth(i) * pDL->getHeight(i) * 4];
+
+               dev->getDeviceSwizzle32()->ToBuffer(copyBuffer, Bits, pDL->getWidth(i) * pDL->getHeight(i) * 4);
+               dev->getDeviceContext()->UpdateSubresource(texture->get2DTex(), subResource, NULL, copyBuffer, pDL->getWidth() * 4, pDL->getHeight() * 4);
                SAFE_DELETE_ARRAY(Bits);
-					break;
-				}
+               break;
+            }
 
-				case GFXFormatR8G8B8A8:
-				case GFXFormatR8G8B8X8:
+            case GFXFormatR8G8B8A8:
+            case GFXFormatR8G8B8X8:
             case GFXFormatR8G8B8A8_SRGB:
-				{
+            {
                PROFILE_SCOPE(Swizzle32_Upload);
                copyBuffer = new U8[pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel()];
-               dev->getDeviceSwizzle32()->ToBuffer(copyBuffer, pDL->getBits(i), pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel());
-               dev->getDeviceContext()->UpdateSubresource(texture->get2DTex(), subResource, NULL, copyBuffer, pDL->getWidth() * pDL->getBytesPerPixel(), pDL->getHeight() *pDL->getBytesPerPixel());
-					break;
-				}
+               dev->getDeviceSwizzle32()->ToBuffer(copyBuffer, pDL->getBits(i, face), pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel());
+               dev->getDeviceContext()->UpdateSubresource(texture->get2DTex(), subResource, NULL, copyBuffer, pDL->getWidth() * pDL->getBytesPerPixel(), pDL->getHeight() * pDL->getBytesPerPixel());
+               break;
+            }
 
-				default:
-				{
+            default:
+            {
                // Just copy the bits in no swizzle or padding
                PROFILE_SCOPE(SwizzleNull_Upload);
-               AssertFatal( pDL->getFormat() == texture->mFormat, "Format mismatch");
-               dev->getDeviceContext()->UpdateSubresource(texture->get2DTex(), subResource, NULL, pDL->getBits(i), pDL->getWidth() *pDL->getBytesPerPixel(), pDL->getHeight() *pDL->getBytesPerPixel());
-				}
-			}
+               AssertFatal(pDL->getFormat() == texture->mFormat, "Format mismatch");
+               dev->getDeviceContext()->UpdateSubresource(texture->get2DTex(), subResource, NULL, pDL->getBits(i, face), pDL->getWidth() * pDL->getBytesPerPixel(), pDL->getHeight() * pDL->getBytesPerPixel());
+            }
+            }
 
-         SAFE_DELETE_ARRAY(copyBuffer);
-	    }
-	  
-	   else
-	   {
-			D3D11_MAPPED_SUBRESOURCE mapping;
-			HRESULT res =  dev->getDeviceContext()->Map(texture->get2DTex(), subResource, D3D11_MAP_WRITE, 0, &mapping);
+            SAFE_DELETE_ARRAY(copyBuffer);
+         }
 
-			AssertFatal(res, "tex2d map call failure");
+         else
+         {
+            D3D11_MAPPED_SUBRESOURCE mapping;
+            HRESULT res = dev->getDeviceContext()->Map(texture->get2DTex(), subResource, D3D11_MAP_WRITE, 0, &mapping);
 
-			switch( texture->mFormat )
-			{
-				case GFXFormatR8G8B8:
+            AssertFatal(res, "tex2d map call failure");
+
+            switch (texture->mFormat)
+            {
+            case GFXFormatR8G8B8:
             case GFXFormatR8G8B8_SRGB:
-				{
-					PROFILE_SCOPE(Swizzle24_Upload);
+            {
+               PROFILE_SCOPE(Swizzle24_Upload);
 
-					U8* Bits = new U8[pDL->getWidth(i) * pDL->getHeight(i) * 4];
-					dMemcpy(Bits, pDL->getBits(i), pDL->getWidth(i) * pDL->getHeight(i) * 3);
-					bitmapConvertRGB_to_RGBX(&Bits, pDL->getWidth(i) * pDL->getHeight(i));					
+               U8* Bits = new U8[pDL->getWidth(i) * pDL->getHeight(i) * 4];
+               dMemcpy(Bits, pDL->getBits(i, face), pDL->getWidth(i) * pDL->getHeight(i) * 3);
+               bitmapConvertRGB_to_RGBX(&Bits, pDL->getWidth(i) * pDL->getHeight(i));
 
-					dev->getDeviceSwizzle32()->ToBuffer(mapping.pData, Bits, pDL->getWidth(i) * pDL->getHeight(i) * 4);
+               dev->getDeviceSwizzle32()->ToBuffer(mapping.pData, Bits, pDL->getWidth(i) * pDL->getHeight(i) * 4);
                SAFE_DELETE_ARRAY(Bits);
-				}
-				break;
+            }
+            break;
 
             case GFXFormatR8G8B8A8:
             case GFXFormatR8G8B8X8:
             case GFXFormatR8G8B8A8_SRGB:
             {
                PROFILE_SCOPE(Swizzle32_Upload);
-               dev->getDeviceSwizzle32()->ToBuffer(mapping.pData, pDL->getBits(i), pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel());
+               dev->getDeviceSwizzle32()->ToBuffer(mapping.pData, pDL->getBits(i, face), pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel());
             }
-				break;
+            break;
 
-				default:
-				{
+            default:
+            {
                // Just copy the bits in no swizzle or padding
                PROFILE_SCOPE(SwizzleNull_Upload);
-               AssertFatal( pDL->getFormat() == texture->mFormat, "Format mismatch");
-               dMemcpy(mapping.pData, pDL->getBits(i), pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel());
-				}
-			}
+               AssertFatal(pDL->getFormat() == texture->mFormat, "Format mismatch");
+               dMemcpy(mapping.pData, pDL->getBits(i, face), pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel());
+            }
+            }
 
-			dev->getDeviceContext()->Unmap(texture->get2DTex(), subResource);
-	   }
+            dev->getDeviceContext()->Unmap(texture->get2DTex(), subResource);
+         }
+      }
    }
 
    D3D11_TEXTURE2D_DESC desc;
@@ -487,7 +498,7 @@ bool GFXD3D11TextureManager::_refreshTexture(GFXTextureObject *texture)
    if(texture->mProfile->isRenderTarget() || texture->mProfile->isDynamic() || texture->mProfile->isZTarget())
    {
       realTex->release();
-      _innerCreateTexture(realTex, texture->getHeight(), texture->getWidth(), texture->getDepth(), texture->mFormat, texture->mProfile, texture->mMipLevels, false, texture->mAntialiasLevel);
+      _innerCreateTexture(realTex, texture->getHeight(), texture->getWidth(), texture->getDepth(), texture->mFormat, texture->mProfile, texture->mMipLevels, false, texture->mAntialiasLevel, texture->mArraySize);
       usedStrategies++;
    }
 
@@ -519,14 +530,31 @@ bool GFXD3D11TextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *d
    GFXD3D11TextureObject *texture = static_cast<GFXD3D11TextureObject*>(aTexture);
    GFXD3D11Device* dev = static_cast<GFXD3D11Device *>(GFX);
    // Fill the texture...
-   for( U32 i = 0; i < aTexture->mMipLevels; i++ )
-   {
-      PROFILE_SCOPE(GFXD3DTexMan_loadSurface);
-
-		AssertFatal( dds->mSurfaces.size() > 0, "Assumption failed. DDSFile has no surfaces." );
+   const bool isCube = texture->isCubeMap() && dds->isCubemap();
+   const U32 numFaces = isCube ? 6 : 1;
 
-		U32 subresource = D3D11CalcSubresource(i, 0, aTexture->mMipLevels);
-		dev->getDeviceContext()->UpdateSubresource(texture->get2DTex(), subresource, 0, dds->mSurfaces[0]->mMips[i], dds->getSurfacePitch(i), 0);
+   // Loop over faces and mips
+   for (U32 face = 0; face < numFaces; ++face)
+   {
+      for (U32 mip = 0; mip < aTexture->mMipLevels; ++mip)
+      {
+         PROFILE_SCOPE(GFXD3DTexMan_loadSurface);
+
+         // DDSFile must have data for each face
+         AssertFatal(dds->mSurfaces.size() > face, "DDSFile missing cubemap face data.");
+         AssertFatal(dds->mSurfaces[face]->mMips.size() > mip, "DDSFile missing mip level.");
+
+         const U32 subresource = D3D11CalcSubresource(mip, face, aTexture->mMipLevels);
+
+         dev->getDeviceContext()->UpdateSubresource(
+            texture->get2DTex(),         // resource
+            subresource,                 // subresource index
+            nullptr,                     // box (nullptr for full subresource)
+            dds->mSurfaces[face]->mMips[mip], // source data pointer
+            dds->getSurfacePitch(mip),  // row pitch
+            0                            // depth pitch
+         );
+      }
    }
 
    D3D11_TEXTURE2D_DESC desc;
@@ -541,14 +569,14 @@ bool GFXD3D11TextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *d
 void GFXD3D11TextureManager::createResourceView(U32 height, U32 width, U32 depth, DXGI_FORMAT format, U32 numMipLevels,U32 usageFlags, GFXTextureObject *inTex)
 {
 	GFXD3D11TextureObject *tex = static_cast<GFXD3D11TextureObject*>(inTex);
-	ID3D11Resource* resource = NULL;
-	
-	if(tex->get2DTex())
-		resource = tex->get2DTex();
-	else if(tex->getSurface())
-		resource = tex->getSurface();
-	else
-		resource = tex->get3DTex();
+   ID3D11Resource* resource;
+
+   if (tex->get2DTex())
+      resource = tex->get2DTex();
+   else if (tex->getSurface())
+      resource = tex->getSurface();
+   else
+      resource = tex->get3DTex();
 
 	HRESULT hr;
 	//TODO: add MSAA support later.
@@ -567,11 +595,40 @@ void GFXD3D11TextureManager::createResourceView(U32 height, U32 width, U32 depth
 			desc.Texture3D.MipLevels = -1;
 			desc.Texture3D.MostDetailedMip = 0;
 		}
-		else
+      else if (tex->isCubeMap())
+      {
+         if (tex->getArraySize() == 1)
+         {
+            desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
+            desc.TextureCube.MipLevels = -1;
+            desc.TextureCube.MostDetailedMip = 0;
+         }
+         else
+         {
+				desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY;
+				desc.TextureCubeArray.MostDetailedMip = 0;
+				desc.TextureCubeArray.MipLevels = -1;
+				desc.TextureCubeArray.First2DArrayFace = 0;
+            desc.TextureCubeArray.NumCubes = tex->getArraySize();
+         }
+         
+      }
+      else
 		{
-			desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
-			desc.Texture2D.MipLevels = -1;
-			desc.Texture2D.MostDetailedMip = 0;
+         if (tex->getArraySize() == 1)
+         {
+            desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+				desc.Texture2D.MipLevels = -1;
+            desc.Texture2D.MostDetailedMip = 0;
+         }
+         else
+         {
+            desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
+            desc.Texture2DArray.MipLevels = -1;
+            desc.Texture2DArray.MostDetailedMip = 0;
+            desc.Texture2DArray.FirstArraySlice = 0;
+            desc.Texture2DArray.ArraySize = tex->getArraySize();
+         }
 		}
 		
 		hr = D3D11DEVICE->CreateShaderResourceView(resource,&desc, tex->getSRViewPtr());
@@ -580,12 +637,29 @@ void GFXD3D11TextureManager::createResourceView(U32 height, U32 width, U32 depth
 
 	if(usageFlags & D3D11_BIND_RENDER_TARGET)
 	{
-		D3D11_RENDER_TARGET_VIEW_DESC desc;
-		desc.Format = format;
-		desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
-		desc.Texture2D.MipSlice = 0;
-		hr = D3D11DEVICE->CreateRenderTargetView(resource, &desc, tex->getRTViewPtr());
-		AssertFatal(SUCCEEDED(hr), "CreateRenderTargetView:: failed to create view!");
+      if (tex->isCubeMap())
+      {
+         for (U32 face = 0; face < 6; face++)
+         {
+            D3D11_RENDER_TARGET_VIEW_DESC desc;
+            desc.Format = format;
+            desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+            desc.Texture2DArray.ArraySize = 1;
+            desc.Texture2DArray.FirstArraySlice = face;
+            desc.Texture2DArray.MipSlice = 0;
+            hr = D3D11DEVICE->CreateRenderTargetView(resource, &desc, tex->getCubeFaceRTViewPtr(face));
+            AssertFatal(SUCCEEDED(hr), "CreateRenderTargetView:: failed to create view!");
+         }
+      }
+      else
+      {
+         D3D11_RENDER_TARGET_VIEW_DESC desc;
+         desc.Format = format;
+         desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+         desc.Texture2D.MipSlice = 0;
+         hr = D3D11DEVICE->CreateRenderTargetView(resource, &desc, tex->getRTViewPtr());
+         AssertFatal(SUCCEEDED(hr), "CreateRenderTargetView:: failed to create view!");
+      }
 	}
 
 	if(usageFlags & D3D11_BIND_DEPTH_STENCIL)

+ 2 - 1
Engine/source/gfx/D3D11/gfxD3D11TextureManager.h

@@ -45,6 +45,7 @@ protected:
                                              U32 numMipLevels,
                                              bool forceMips = false,
                                              S32 antialiasLevel = 0,
+                                             U32 arraySize = 1,
                                              GFXTextureObject *inTex = NULL ) override;
    
    bool _loadTexture(GFXTextureObject *texture, DDSFile *dds) override;
@@ -56,7 +57,7 @@ protected:
 private:
    U32 mCurTexSet[GFX_TEXTURE_STAGE_COUNT];
 
-   void _innerCreateTexture(GFXD3D11TextureObject *obj, U32 height, U32 width, U32 depth, GFXFormat format, GFXTextureProfile *profile, U32 numMipLevels, bool forceMips = false, S32 antialiasLevel = 0);
+   void _innerCreateTexture(GFXD3D11TextureObject *obj, U32 height, U32 width, U32 depth, GFXFormat format, GFXTextureProfile *profile, U32 numMipLevels, bool forceMips = false, S32 antialiasLevel = 0, U32 arraySize = 1);
 };
 
 #endif

+ 237 - 159
Engine/source/gfx/D3D11/gfxD3D11TextureObject.cpp

@@ -33,26 +33,16 @@ U32 GFXD3D11TextureObject::mTexCount = 0;
 //	GFXFormatR8G8B8 has now the same behaviour as GFXFormatR8G8B8X8. 
 //	This is because 24 bit format are now deprecated by microsoft, for data alignment reason there's no changes beetween 24 and 32 bit formats.
 //	DirectX 10-11 both have 24 bit format no longer.
-
-
-GFXD3D11TextureObject::GFXD3D11TextureObject( GFXDevice * d, GFXTextureProfile *profile) : GFXTextureObject( d, profile )
+GFXD3D11TextureObject::GFXD3D11TextureObject( GFXDevice * d, GFXTextureProfile *profile, const U32 arraySize) : GFXTextureObject( d, profile )
 {
 #ifdef D3D11_DEBUG_SPEW
    mTexCount++;
    Con::printf("+ texMake %d %x", mTexCount, this);
 #endif
-
-   mD3DTexture = NULL;
-   mLocked = false;
-
-   mD3DSurface = NULL;
+   isManaged = false;
    dMemset(&mLockRect, 0, sizeof(mLockRect));
    dMemset(&mLockBox, 0, sizeof(mLockBox));
-   mLockedSubresource = 0;
-   mDSView = NULL;
-   mRTView = NULL;
-   mSRView = NULL;
-   isManaged = false;
+   mArraySize = arraySize;
 }
 
 GFXD3D11TextureObject::~GFXD3D11TextureObject()
@@ -64,53 +54,76 @@ GFXD3D11TextureObject::~GFXD3D11TextureObject()
 #endif
 }
 
-GFXLockedRect *GFXD3D11TextureObject::lock(U32 mipLevel /*= 0*/, RectI *inRect /*= NULL*/)
+ID3D11Texture2D* GFXD3D11TextureObject::get2DTex() const
+{
+   ComPtr<ID3D11Texture2D> tex2D;
+   if (mD3DTexture) mD3DTexture.As(&tex2D);
+   return tex2D.Get();
+}
+
+ID3D11Texture3D* GFXD3D11TextureObject::get3DTex() const
+{
+   ComPtr<ID3D11Texture3D> tex3D;
+   if (mD3DTexture) mD3DTexture.As(&tex3D);
+   return tex3D.Get();
+}
+
+ID3D11Texture2D** GFXD3D11TextureObject::get2DTexPtr()
+{
+   return reinterpret_cast<ID3D11Texture2D**>(mD3DTexture.GetAddressOf());
+}
+
+ID3D11Texture3D** GFXD3D11TextureObject::get3DTexPtr()
+{
+   return reinterpret_cast<ID3D11Texture3D**>(mD3DTexture.GetAddressOf());
+}
+
+ID3D11RenderTargetView** GFXD3D11TextureObject::getCubeFaceRTViewPtr(U32 face)
+{
+   AssertFatal(isCubeMap(), "Not a cubemap texture!");
+   AssertFatal(face < 6, "Invalid cubemap face index!");
+   return mCubeRTV[face].GetAddressOf();
+}
+
+GFXLockedRect* GFXD3D11TextureObject::lock(U32 mipLevel /*= 0*/, RectI* inRect /*= NULL*/, U32 faceIndex /*= 0*/)
 {
-   AssertFatal( !mLocked, "GFXD3D11TextureObject::lock - The texture is already locked!" );
+   AssertFatal(!mLocked, "GFXD3D11TextureObject::lock - Texture is already locked!");
+   AssertFatal(faceIndex < 6 || !isCubeMap(), "Invalid cubemap face index!");
 
-   if( !mStagingTex ||
+   // Ensure staging texture exists and matches size
+   if (!mStagingTex.isValid() ||
       mStagingTex->getWidth() != getWidth() ||
       mStagingTex->getHeight() != getHeight() ||
       mStagingTex->getDepth() != getDepth())
    {
-      if (getDepth() != 0)
-      {
-         mStagingTex.set(getWidth(), getHeight(), getDepth(), mFormat, &GFXSystemMemTextureProfile, avar("%s() - mLockTex (line %d)", __FUNCTION__, __LINE__, 0));
-      }
-      else
-      {
-         mStagingTex.set(getWidth(), getHeight(), mFormat, &GFXSystemMemTextureProfile, avar("%s() - mLockTex (line %d)", __FUNCTION__, __LINE__));
-      }
+      mStagingTex.set(getWidth(), getHeight(), mFormat, &GFXSystemMemTextureProfile,
+         avar("%s() - stagingTex", __FUNCTION__));
    }
 
-   ID3D11DeviceContext* pContext = D3D11DEVICECONTEXT;
    D3D11_MAPPED_SUBRESOURCE mapInfo;
-   U32 offset = 0;
-   mLockedSubresource = D3D11CalcSubresource(mipLevel, 0, getMipLevels());
-   GFXD3D11TextureObject* pD3DStagingTex = (GFXD3D11TextureObject*)&(*mStagingTex);
 
-   //map staging texture
-   HRESULT hr = pContext->Map(pD3DStagingTex->getResource(), mLockedSubresource, D3D11_MAP_WRITE, 0, &mapInfo);      
+   mLockedSubresource = D3D11CalcSubresource(mipLevel, faceIndex, getMipLevels());
+   GFXD3D11TextureObject* staging = (GFXD3D11TextureObject*)&(*mStagingTex);
 
+   HRESULT hr = D3D11DEVICECONTEXT->Map(staging->getResource(), mLockedSubresource, D3D11_MAP_WRITE, 0, &mapInfo);
    if (FAILED(hr))
       AssertFatal(false, "GFXD3D11TextureObject:lock - failed to map render target resource!");
 
-
    const bool is3D = mStagingTex->getDepth() != 0;
    const U32 width = mTextureSize.x >> mipLevel;
    const U32 height = mTextureSize.y >> mipLevel;
    const U32 depth = is3D ? mTextureSize.z >> mipLevel : 1;
+   U32 offset = 0;
 
-   //calculate locked box region and offset
    if (inRect)
    {
-      if ((inRect->point.x + inRect->extent.x > width) || (inRect->point.y + inRect->extent.y > height))
-         AssertFatal(false, "GFXD3D11TextureObject::lock - Rectangle too big!");
+      AssertFatal(inRect->point.x + inRect->extent.x <= width, "GFXD3D11TextureObject::lock - Invalid lock rect width!");
+      AssertFatal(inRect->point.y + inRect->extent.y <= height, "GFXD3D11TextureObject::lock - Invalid lock rect height!");
 
       mLockBox.top = inRect->point.y;
       mLockBox.left = inRect->point.x;
-      mLockBox.bottom = inRect->point.y + inRect->extent.y;
       mLockBox.right = inRect->point.x + inRect->extent.x;
+      mLockBox.bottom = inRect->point.y + inRect->extent.y;
       mLockBox.back = depth;
       mLockBox.front = 0;
 
@@ -121,49 +134,57 @@ GFXLockedRect *GFXD3D11TextureObject::lock(U32 mipLevel /*= 0*/, RectI *inRect /
    {
       mLockBox.top = 0;
       mLockBox.left = 0;
-      mLockBox.bottom = height;
       mLockBox.right = width;
+      mLockBox.bottom = height;
       mLockBox.back = depth;
       mLockBox.front = 0;
+
    }
 
    mLocked = true;
    mLockRect.pBits = static_cast<U8*>(mapInfo.pData) + offset;
    mLockRect.Pitch = mapInfo.RowPitch;
 
-   return (GFXLockedRect*)&mLockRect;
+   return reinterpret_cast<GFXLockedRect*>(&mLockRect);
 }
 
-void GFXD3D11TextureObject::unlock(U32 mipLevel)
+void GFXD3D11TextureObject::unlock(U32 mipLevel /*= 0*/, U32 faceIndex /*= 0*/)
 {
-   AssertFatal( mLocked, "GFXD3D11TextureObject::unlock - Attempting to unlock a surface that has not been locked" );
+   AssertFatal(mLocked, "GFXD3D11TextureObject::unlock - Texture is not locked!");
+   AssertFatal(faceIndex < 6 || !isCubeMap(), "Invalid cubemap face index!");
 
-   //profile in the unlock function because all the heavy lifting is done here
-   PROFILE_START(GFXD3D11TextureObject_lockRT);
+   PROFILE_START(GFXD3D11TextureObject_unlock);
 
-   ID3D11DeviceContext* pContext = D3D11DEVICECONTEXT;
-   GFXD3D11TextureObject* pD3DStagingTex = (GFXD3D11TextureObject*)&(*mStagingTex);
-   ID3D11Resource* pStagingResource = pD3DStagingTex->getResource();
-   const bool is3D = mStagingTex->getDepth() != 0;
+   GFXD3D11TextureObject* staging = (GFXD3D11TextureObject*)&(*mStagingTex);
 
-   //unmap staging texture
-   pContext->Unmap(pStagingResource, mLockedSubresource);
-   //copy lock box region from the staging texture to our regular texture
-   pContext->CopySubresourceRegion(mD3DTexture, mLockedSubresource, mLockBox.left, mLockBox.top, is3D ? mLockBox.back : 0, pStagingResource, mLockedSubresource, &mLockBox);
+   D3D11DEVICECONTEXT->Unmap(staging->getResource(), mLockedSubresource);
 
-   PROFILE_END();
+   // Copy from staging back to GPU texture
+   D3D11DEVICECONTEXT->CopySubresourceRegion(
+      mD3DTexture.Get(),
+      mLockedSubresource,
+      0, 0, 0,
+      staging->getResource(),
+      mLockedSubresource,
+      &mLockBox
+   );
 
    mLockedSubresource = 0;
    mLocked = false;
+   
+   PROFILE_END();
 }
 
 void GFXD3D11TextureObject::release()
 {
-   SAFE_RELEASE(mSRView);
-   SAFE_RELEASE(mRTView);
-   SAFE_RELEASE(mDSView);
-   SAFE_RELEASE(mD3DTexture);
-   SAFE_RELEASE(mD3DSurface);
+   mSRView.Reset();
+   mRTView.Reset();
+   mDSView.Reset();
+   mD3DTexture.Reset();
+   mD3DSurface.Reset();
+
+   for (auto& faceRTV : mCubeRTV)
+      faceRTV.Reset();
 }
 
 void GFXD3D11TextureObject::zombify()
@@ -189,149 +210,206 @@ bool GFXD3D11TextureObject::copyToBmp(GBitmap* bmp)
    if (!bmp)
       return false;
 
-   // check format limitations
-   // at the moment we only support RGBA for the source (other 4 byte formats should
-   // be easy to add though)
-   AssertFatal(mFormat == GFXFormatR16G16B16A16F || mFormat == GFXFormatR8G8B8A8 || mFormat == GFXFormatR8G8B8A8_LINEAR_FORCE || mFormat == GFXFormatR8G8B8A8_SRGB || mFormat == GFXFormatR8G8B8, "copyToBmp: invalid format");
-   if (mFormat != GFXFormatR16G16B16A16F && mFormat != GFXFormatR8G8B8A8 && mFormat != GFXFormatR8G8B8A8_LINEAR_FORCE && mFormat != GFXFormatR8G8B8A8_SRGB && mFormat != GFXFormatR8G8B8)
-      return false;
+   AssertFatal(mFormat == GFXFormatR16G16B16A16F || mFormat == GFXFormatR8G8B8A8 ||
+      mFormat == GFXFormatR8G8B8A8_LINEAR_FORCE || mFormat == GFXFormatR8G8B8A8_SRGB ||
+      mFormat == GFXFormatR8G8B8,
+      "GFXD3D11TextureObject::copyToBmp - Unsupported source format.");
 
    PROFILE_START(GFXD3D11TextureObject_copyToBmp);
 
-   AssertFatal(bmp->getWidth() == getWidth(), avar("GFXGLTextureObject::copyToBmp - Width mismatch: %i vs %i", bmp->getWidth(), getWidth()));
-   AssertFatal(bmp->getHeight() == getHeight(), avar("GFXGLTextureObject::copyToBmp - Height mismatch: %i vs %i", bmp->getHeight(), getHeight()));
-   const U32 mipLevels = getMipLevels();
+   AssertFatal(bmp->getWidth() == getWidth(), "Width mismatch between texture and bitmap.");
+   AssertFatal(bmp->getHeight() == getHeight(), "Height mismatch between texture and bitmap.");
 
+   const U32 mipLevels = getMipLevels();
    bmp->setHasTransparency(mHasTransparency);
 
-   // set some constants
-   U32 sourceBytesPerPixel = 4;
-   U32 destBytesPerPixel = 0;
+   // Figure out bytes per pixel
+   const bool isFP16 = (bmp->getFormat() == GFXFormatR16G16B16A16F);
+   const U32 destBpp = (bmp->getFormat() == GFXFormatR8G8B8 ? 3 :
+      bmp->getFormat() == GFXFormatR16G16B16A16F ? 8 : 4);
+   const U32 srcBpp = (mFormat == GFXFormatR16G16B16A16F ? 8 : 4);
 
-   const GFXFormat fmt = bmp->getFormat();
-   bool fp16 = false;//is rgba16f format?
-   if (fmt == GFXFormatR16G16B16A16F)
-   {
-      destBytesPerPixel = 8;
-      sourceBytesPerPixel = 8;
-      fp16 = true;
-   }
-   else if (fmt == GFXFormatR8G8B8A8 || fmt == GFXFormatR8G8B8A8_LINEAR_FORCE || fmt == GFXFormatR8G8B8A8_SRGB)
-      destBytesPerPixel = 4;
-   else if(bmp->getFormat() == GFXFormatR8G8B8)
-      destBytesPerPixel = 3;
-   else
-      // unsupported
-      AssertFatal(false, "GFXD3D11TextureObject::copyToBmp - unsupported bitmap format");
-   
-   //create temp staging texture
-   D3D11_TEXTURE2D_DESC desc;
-   static_cast<ID3D11Texture2D*>(mD3DTexture)->GetDesc(&desc);
+   // --- Create staging texture ---
+   D3D11_TEXTURE2D_DESC desc = {};
+   reinterpret_cast<ID3D11Texture2D*>(mD3DTexture.Get())->GetDesc(&desc);
    desc.BindFlags = 0;
-   desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
+   desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    desc.Usage = D3D11_USAGE_STAGING;
    desc.MiscFlags = 0;
 
-   ID3D11Texture2D* pStagingTexture = NULL;
-   HRESULT hr = D3D11DEVICE->CreateTexture2D(&desc, NULL, &pStagingTexture);
+   ComPtr<ID3D11Texture2D> stagingTex;
+   HRESULT hr = D3D11DEVICE->CreateTexture2D(&desc, nullptr, stagingTex.GetAddressOf());
    if (FAILED(hr))
    {
-      Con::errorf("GFXD3D11TextureObject::copyToBmp - Failed to create staging texture"); 
+      Con::errorf("GFXD3D11TextureObject::copyToBmp - Failed to create staging texture (0x%X)", hr);
       return false;
    }
 
-   //copy the classes texture to the staging texture
-   D3D11DEVICECONTEXT->CopyResource(pStagingTexture, mD3DTexture);
+   // --- Copy texture (handle cubemap or 2D) ---
+   const U32 faceCount = isCubeMap() && bmp->getNumFaces() == 6 ? 6 : 1;
 
-   for (U32 mip = 0; mip < mipLevels; mip++)
+   for (U32 face = 0; face < faceCount; ++face)
    {
-      const U32 width = bmp->getWidth(mip);
-      const U32 height = bmp->getHeight(mip);
-      //map the staging resource
-      D3D11_MAPPED_SUBRESOURCE mappedRes;
-      const U32 subResource = D3D11CalcSubresource(mip, 0, mipLevels);
-      hr = D3D11DEVICECONTEXT->Map(pStagingTexture, subResource, D3D11_MAP_READ, 0, &mappedRes);
-      if (FAILED(hr))
+      for (U32 mip = 0; mip < mipLevels; ++mip)
       {
-         //cleanup
-         SAFE_RELEASE(pStagingTexture);
-         Con::errorf("GFXD3D11TextureObject::copyToBmp - Failed to map staging texture");
-         return false;
-      }
+         const U32 srcSubRes = D3D11CalcSubresource(mip, face, mipLevels);
+         // Always map mip-level 0..mipLevels-1 on *slice 0* of the staging texture
+         const U32 dstSubRes = D3D11CalcSubresource(mip, face, mipLevels);
+
+         D3D11DEVICECONTEXT->CopySubresourceRegion(
+            stagingTex.Get(), dstSubRes, 0, 0, 0,
+            mD3DTexture.Get(), srcSubRes, nullptr);
+
+         D3D11_MAPPED_SUBRESOURCE mapped = {};
+         hr = D3D11DEVICECONTEXT->Map(stagingTex.Get(), dstSubRes, D3D11_MAP_READ, 0, &mapped);
+         if (FAILED(hr))
+         {
+            Con::errorf("GFXD3D11TextureObject::copyToBmp - Failed to map staging texture (0x%X)", hr);
+            return false;
+         }
 
-      // set pointers
-      const U8* srcPtr = (U8*)mappedRes.pData;
-      U8* destPtr = bmp->getWritableBits(mip);
+         const U8* src = static_cast<const U8*>(mapped.pData);
+         U8* dst = bmp->getWritableBits(mip, face);
 
-      // we will want to skip over any D3D cache data in the source texture
-      const S32 sourceCacheSize = mappedRes.RowPitch - width * sourceBytesPerPixel;
-      AssertFatal(sourceCacheSize >= 0, "GFXD3D11TextureObject::copyToBmp - cache size is less than zero?");
+         const U32 width = bmp->getWidth(mip);
+         const U32 height = bmp->getHeight(mip);
 
-      // copy data into bitmap
-      for (U32 row = 0; row < height; ++row)
-      {
-         for (U32 col = 0; col < width; ++col)
+         for (U32 y = 0; y < height; ++y)
          {
-            //we can just copy data straight in with RGBA16F format
-            if (fp16)
+            const U8* srcRow = src;
+            U8* dstRow = dst;
+
+            for (U32 x = 0; x < width; ++x)
             {
-               dMemcpy(destPtr, srcPtr, sizeof(U16) * 4);
-            }
-            else
-            {            
-               destPtr[0] = srcPtr[2]; // red
-               destPtr[1] = srcPtr[1]; // green
-               destPtr[2] = srcPtr[0]; // blue 
-               if (destBytesPerPixel == 4)
-                  destPtr[3] = srcPtr[3]; // alpha
+               if (isFP16)
+               {
+                  dMemcpy(dstRow, srcRow, sizeof(U16) * 4);
+               }
+               else
+               {
+                  // Convert BGRA → RGB(A)
+                  dstRow[0] = srcRow[2];
+                  dstRow[1] = srcRow[1];
+                  dstRow[2] = srcRow[0];
+                  if (destBpp == 4)
+                     dstRow[3] = srcRow[3];
+               }
+               srcRow += srcBpp;
+               dstRow += destBpp;
             }
 
-            // go to next pixel in src
-            srcPtr += sourceBytesPerPixel;
-
-            // go to next pixel in dest
-            destPtr += destBytesPerPixel;
+            src += mapped.RowPitch;
+            dst += width * destBpp;
          }
-         // skip past the cache data for this row (if any)
-         srcPtr += sourceCacheSize;
-      }
-
-      // assert if we stomped or underran memory
-      AssertFatal(U32(destPtr - bmp->getWritableBits(mip)) == width * height * destBytesPerPixel, "GFXD3D11TextureObject::copyToBmp - memory error");
-      AssertFatal(U32(srcPtr - (U8*)mappedRes.pData) == height * mappedRes.RowPitch, "GFXD3D11TextureObject::copyToBmp - memory error");
 
-      D3D11DEVICECONTEXT->Unmap(pStagingTexture, subResource);
+         D3D11DEVICECONTEXT->Unmap(stagingTex.Get(), dstSubRes);
+      }
    }
 
-   SAFE_RELEASE(pStagingTexture);
    PROFILE_END();
-
    return true;
 }
 
-ID3D11ShaderResourceView* GFXD3D11TextureObject::getSRView()
-{
-	return mSRView;
-}
-ID3D11RenderTargetView* GFXD3D11TextureObject::getRTView()
+void GFXD3D11TextureObject::generateMipMaps()
 {
-	return mRTView;
-}
-ID3D11DepthStencilView* GFXD3D11TextureObject::getDSView()
-{
-	return mDSView;
+   //Generate mips
+   D3D11DEVICECONTEXT->GenerateMips(mSRView.Get());
+   //get mip level count
+   D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;
+   mSRView->GetDesc(&viewDesc);
+   mMipLevels = viewDesc.TextureCube.MipLevels;
 }
 
-ID3D11ShaderResourceView** GFXD3D11TextureObject::getSRViewPtr()
+void GFXD3D11TextureObject::updateTextureSlot(const GFXTexHandle& texHandle, const U32 slot, const S32 faceIdx /*=-1*/)
 {
-	return &mSRView;
-}
-ID3D11RenderTargetView** GFXD3D11TextureObject::getRTViewPtr()
-{
-	return &mRTView;
+   AssertFatal(slot < getArraySize(), "updateTextureSlot - destination slot out of bounds");
+   AssertFatal(mFormat == texHandle->getFormat(), "updateTextureSlot - format mismatch");
+   AssertFatal(getMipLevels() == texHandle->getMipLevels(), "updateTextureSlot - mip level mismatch");
+
+   GFXD3D11TextureObject* srcTex = static_cast<GFXD3D11TextureObject*>(texHandle.getPointer());
+
+   ID3D11Resource* dstRes = get2DTex();
+   ID3D11Resource* srcRes = srcTex->get2DTex();
+
+   const UINT mipLevels = getMipLevels();
+
+   const bool dstIsCube = isCubeMap();
+   const bool srcIsCube = srcTex->isCubeMap();
+
+   const UINT dstArraySize = getArraySize();
+   const UINT srcArraySize = srcTex->getArraySize();
+
+   // Determine number of faces to copy
+   const UINT faceCount = srcIsCube ? 6 : 1;
+   const UINT startFace = (faceIdx >= 0) ? faceIdx : 0;
+   const UINT endFace = (faceIdx >= 0) ? faceIdx + 1 : faceCount;
+
+   for (UINT face = startFace; face < endFace; ++face)
+   {
+      // Compute source slice
+      const UINT srcSlice = srcIsCube
+         ? (srcArraySize > 1 ? face + slot * 6 : face)   // only add slot*6 if it's a cubemap array
+         : (srcArraySize > 1 ? face + slot : 0);        // otherwise, single 2D texture or 2D array
+
+      const UINT dstSlice = dstIsCube
+         ? (dstArraySize > 1 ? face + slot * 6 : face)  // only add slot*6 if it's a cubemap array
+         : (dstArraySize > 1 ? face + slot : 0);        // otherwise, single 2D texture or 2D array
+
+      for (UINT mip = 0; mip < mipLevels; ++mip)
+      {
+         const UINT srcSubresource = D3D11CalcSubresource(mip, srcSlice, mipLevels);
+         const UINT dstSubresource = D3D11CalcSubresource(mip, dstSlice, mipLevels);
+
+         D3D11DEVICECONTEXT->CopySubresourceRegion(dstRes, dstSubresource, 0, 0, 0, srcRes, srcSubresource, nullptr);
+      }
+   }
 }
 
-ID3D11DepthStencilView** GFXD3D11TextureObject::getDSViewPtr()
+void GFXD3D11TextureObject::copyTo(GFXTextureObject* dstTex)
 {
-	return &mDSView;
+   AssertFatal(dstTex, "GFXD3D11TextureObject::copyTo - destination is null");
+
+   GFXD3D11TextureObject* pDstTex = static_cast<GFXD3D11TextureObject*>(dstTex);
+
+   ID3D11Texture2D* srcTex = (ID3D11Texture2D*)mD3DTexture.Get();
+   ID3D11Texture2D* dstTex2D = pDstTex->get2DTex();
+
+   D3D11_TEXTURE2D_DESC srcDesc, dstDesc;
+   srcTex->GetDesc(&srcDesc);
+   dstTex2D->GetDesc(&dstDesc);
+
+   // Sanity check – sizes and formats must match for a full copy.
+   AssertFatal(srcDesc.Width == dstDesc.Width && srcDesc.Height == dstDesc.Height,
+      "GFXD3D11TextureObject::copyTo - Mismatched texture dimensions");
+   AssertFatal(srcDesc.Format == dstDesc.Format,
+      "GFXD3D11TextureObject::copyTo - Mismatched formats");
+
+   UINT srcMipLevels = srcDesc.MipLevels ? srcDesc.MipLevels : 1;
+   UINT dstMipLevels = dstDesc.MipLevels ? dstDesc.MipLevels : 1;
+   UINT mipLevels = getMin(srcMipLevels, dstMipLevels);
+
+   UINT srcArraySize = srcDesc.ArraySize;
+   UINT dstArraySize = dstDesc.ArraySize;
+   UINT arraySize = getMin(srcArraySize, dstArraySize);
+
+   // Handle cube maps and cube map arrays
+   bool isCubeSrc = (srcDesc.MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE) != 0;
+
+   // In cubemaps, ArraySize is always 6 * numCubes
+   if (isCubeSrc) arraySize = srcArraySize; // 6 or 6*nCubes
+
+   for (UINT arraySlice = 0; arraySlice < arraySize; ++arraySlice)
+   {
+      for (UINT mip = 0; mip < mipLevels; ++mip)
+      {
+         UINT srcSubresource = D3D11CalcSubresource(mip, arraySlice, srcMipLevels);
+         UINT dstSubresource = D3D11CalcSubresource(mip, arraySlice, dstMipLevels);
+
+         D3D11DEVICECONTEXT->CopySubresourceRegion(
+            dstTex2D, dstSubresource,
+            0, 0, 0,
+            srcTex, srcSubresource,
+            nullptr);
+      }
+   }
 }

+ 49 - 27
Engine/source/gfx/D3D11/gfxD3D11TextureObject.h

@@ -27,55 +27,77 @@
 #include "gfx/gfxTextureHandle.h"
 #include "gfx/gfxTextureManager.h"
 
+#include <wrl/client.h>
+using Microsoft::WRL::ComPtr;
+
 class GFXD3D11TextureObject : public GFXTextureObject
 {
 protected:
    static U32 mTexCount;
+
    GFXTexHandle mStagingTex;
    DXGI_MAPPED_RECT mLockRect;
    D3D11_BOX mLockBox;
-   bool mLocked;
+   bool mLocked = false;
 
-   U32 mLockedSubresource;
-   ID3D11Resource *mD3DTexture;
+   U32 mLockedSubresource = 0;
 
-   // used for z buffers...
-   ID3D11Texture2D *mD3DSurface;
+   // Main GPU texture resource (2D / 3D / Cubemap)
+   ComPtr<ID3D11Resource> mD3DTexture;
 
-   ID3D11ShaderResourceView* mSRView; // for shader resource input
-   ID3D11RenderTargetView* mRTView; // for render targets
-   ID3D11DepthStencilView* mDSView; //render target view for depth stencil
+   // Used for Z-targets
+   ComPtr<ID3D11Texture2D> mD3DSurface;
 
+   // Views
+   ComPtr<ID3D11ShaderResourceView> mSRView; // Shader resource
+   ComPtr<ID3D11RenderTargetView>   mRTView; // Render target
+   ComPtr<ID3D11DepthStencilView>   mDSView; // Depth stencil
+
+   // Cubemap face render target views (optional)
+   ComPtr<ID3D11RenderTargetView>   mCubeRTV[6];
 public:
 
-   GFXD3D11TextureObject( GFXDevice * d, GFXTextureProfile *profile);
+   GFXD3D11TextureObject( GFXDevice * d, GFXTextureProfile *profile, const U32 arraySize = 1);
    ~GFXD3D11TextureObject();
 
-   ID3D11Resource*      getResource(){ return mD3DTexture; }
-   ID3D11Texture2D*     get2DTex(){ return (ID3D11Texture2D*) mD3DTexture; }
-   ID3D11Texture2D**    get2DTexPtr(){ return (ID3D11Texture2D**) &mD3DTexture; }
-   ID3D11Texture3D*		get3DTex(){ return (ID3D11Texture3D*) mD3DTexture; }
-   ID3D11Texture3D**	get3DTexPtr(){ return (ID3D11Texture3D**) &mD3DTexture; }
-   
-   ID3D11ShaderResourceView* getSRView();
-   ID3D11RenderTargetView* getRTView();
-   ID3D11DepthStencilView* getDSView();
-
-   ID3D11ShaderResourceView** getSRViewPtr();
-   ID3D11RenderTargetView** getRTViewPtr();
-   ID3D11DepthStencilView** getDSViewPtr();
-  
+   // Accessors
+   ID3D11Resource* getResource() const { return mD3DTexture.Get(); }
+   ID3D11Texture2D* get2DTex() const;
+   ID3D11Texture3D* get3DTex() const;
+   ID3D11Texture2D** get2DTexPtr();
+   ID3D11Texture3D** get3DTexPtr();
+
+   ID3D11ShaderResourceView* getSRView() const { return mSRView.Get(); }
+   ID3D11RenderTargetView* getRTView() const { return mRTView.Get(); }
+   ID3D11DepthStencilView* getDSView() const { return mDSView.Get(); }
+
+   ID3D11ShaderResourceView** getSRViewPtr() { return mSRView.GetAddressOf(); }
+   ID3D11RenderTargetView** getRTViewPtr() { return mRTView.GetAddressOf(); }
+   ID3D11DepthStencilView** getDSViewPtr() { return mDSView.GetAddressOf(); }
+
+   // Cubemap face RTV access (for render-to-cubemap)
+   ID3D11RenderTargetView* getCubeFaceRTView(U32 face) const
+   {
+      AssertFatal(isCubeMap(), "Not a cubemap texture!");
+      AssertFatal(face < 6, "Invalid cubemap face index!");
+      return mCubeRTV[face].Get();
+   }
+
+   ID3D11RenderTargetView** getCubeFaceRTViewPtr(U32 face);
 
    void release();
 
    bool isManaged; //setting to true tells this texture not to be released from being zombify
 
-   GFXLockedRect * lock(U32 mipLevel = 0, RectI *inRect = NULL) override;
-   void unlock(U32 mipLevel = 0 ) override;
+   GFXLockedRect* lock(U32 mipLevel = 0, RectI* inRect = NULL, U32 faceIndex = 0) override;
+   void unlock(U32 mipLevel = 0, U32 faceIndex = 0) override;
 
    bool			copyToBmp(GBitmap* bmp) override;
-   ID3D11Texture2D*		getSurface() {return mD3DSurface;}
-   ID3D11Texture2D**	getSurfacePtr() {return &mD3DSurface;}
+   void generateMipMaps() override;
+   void updateTextureSlot(const GFXTexHandle& texHandle, const U32 slot, const S32 face = -1) override;
+   void copyTo(GFXTextureObject* dstTex) override;
+   ID3D11Texture2D*		getSurface() {return mD3DSurface.Get();}
+   ID3D11Texture2D**	getSurfacePtr() {return mD3DSurface.GetAddressOf();}
 
    // GFXResource
    void zombify() override;

+ 9 - 6
Engine/source/gfx/Null/gfxNullDevice.cpp

@@ -72,10 +72,12 @@ public:
    void pureVirtualCrash() override {}
 #endif
 
-   GFXLockedRect * lock( U32 mipLevel = 0, RectI *inRect = NULL ) override { return NULL; };
-   void unlock( U32 mipLevel = 0) override {};
+   GFXLockedRect * lock( U32 mipLevel = 0, RectI *inRect = NULL, U32 faceIndex = 0)override { return NULL; };
+   void unlock( U32 mipLevel = 0, U32 faceIndex = 0)override {};
    bool copyToBmp(GBitmap *) override { return false; };
-
+   void updateTextureSlot(const GFXTexHandle& texHandle, const U32 slot, const S32 face = -1) override {};
+   void copyTo(GFXTextureObject* dstTex) override {};
+   void generateMipMaps() override {};
    void zombify() override {}
    void resurrect() override {}
 };
@@ -94,8 +96,8 @@ public:
    GFXTextureObject* createTexture(DDSFile* dds, GFXTextureProfile* profile, bool deleteDDS) override { return nullptr; }
    GFXTextureObject* createTexture(const Torque::Path& path, GFXTextureProfile* profile) override { return nullptr; }
    GFXTextureObject* createTexture(U32 width, U32 height, void* pixels, GFXFormat format, GFXTextureProfile* profile) override { return nullptr; }
-   GFXTextureObject* createTexture(U32 width, U32 height, U32 depth, GFXFormat format, GFXTextureProfile* profile, U32 numMipLevels = 1) override { return nullptr; }
-   GFXTextureObject* createTexture(U32 width, U32 height, GFXFormat format, GFXTextureProfile* profile, U32 numMipLevels, S32 antialiasLevel) override { return nullptr; }
+   GFXTextureObject* createTexture(U32 width, U32 height, U32 depth, GFXFormat format, GFXTextureProfile* profile, U32 numMipLevels = 1, U32 arraySize = 1) override { return nullptr; }
+   GFXTextureObject* createTexture(U32 width, U32 height, GFXFormat format, GFXTextureProfile* profile, U32 numMipLevels, S32 antialiasLevel, U32 arraySize = 1) override { return nullptr; }
    GFXTextureObject* createCompositeTexture(GBitmap* bmp[4], U32 inputKey[4], const String& resourceName, GFXTextureProfile* profile, bool deleteBmp) override { return nullptr; }
 protected:
       GFXTextureObject *_createTextureObject( U32 height, 
@@ -105,7 +107,8 @@ protected:
                                                       GFXTextureProfile *profile, 
                                                       U32 numMipLevels, 
                                                       bool forceMips = false, 
-                                                      S32 antialiasLevel = 0, 
+                                                      S32 antialiasLevel = 0,
+                                                      U32 arraySize = 1,
                                                       GFXTextureObject *inTex = NULL ) override
       { 
          GFXNullTextureObject *retTex;

+ 400 - 112
Engine/source/gfx/bitmap/bitmapUtils.cpp

@@ -24,6 +24,11 @@
 
 #include "platform/platform.h"
 
+#ifndef STB_IMAGE_RESIZE2_IMPLEMENTATION
+#define STB_IMAGE_RESIZE2_IMPLEMENTATION
+#define STBIR_PROFILE
+#include "gfx/bitmap/loaders/stb/stb_image_resize2.h"
+#endif // !STB_IMAGE_RESIZE2_IMPLEMENTATION
 
 void bitmapExtrude5551_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
 {
@@ -67,7 +72,7 @@ void bitmapExtrude5551_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWi
       {
          U32 a = src[0];
          U32 c = src[stride];
-#if defined(TORQUE_OS_MAC)
+#if defined(TORQUE_BIG_ENDIAN)
             dst[y] = ((( (a >> 10) + (c >> 10)) >> 1) << 10) |
                      ((( ((a >> 5) & 0x1F) + ((c >> 5) & 0x1f)) >> 1) << 5) |
                      ((( ((a >> 0) & 0x1F) + ((c >> 0) & 0x1f)) >> 1) << 0);
@@ -81,151 +86,434 @@ void bitmapExtrude5551_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWi
    }
 }
 
-
 //--------------------------------------------------------------------------
-void bitmapExtrudeRGB_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
-{
-   const U8 *src = (const U8 *) srcMip;
-   U8 *dst = (U8 *) mip;
-   U32 stride = srcHeight != 1 ? (srcWidth) * 3 : 0;
 
-   U32 width  = srcWidth  >> 1;
-   U32 height = srcHeight >> 1;
-   if (width  == 0) width  = 1;
-   if (height == 0) height = 1;
+template <typename T>
+void bitmapExtrudeGeneric(
+   const T* src, T* dst,
+   U32 srcWidth, U32 srcHeight,
+   U32 channels, U32 bpp)
+{
+   U32 srcRowStride = srcHeight != 1 ? (srcWidth * bpp) / sizeof(T) : 0;
+   U32 dstWidth = srcWidth > 1 ? srcWidth / 2 : 1;
+   U32 dstHeight = srcHeight > 1 ? srcHeight / 2 : 1;
+   U32 dstRowStride = dstHeight != 1 ? (dstWidth * bpp) / sizeof(T) : 0;
 
-   if (srcWidth != 1)
+   for (U32 y = 0; y < dstHeight; ++y)
    {
-      for(U32 y = 0; y < height; y++)
+      for (U32 x = 0; x < dstWidth; ++x)
       {
-         for(U32 x = 0; x < width; x++)
+         for (U32 c = 0; c < channels; ++c)
          {
-            *dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2;
-            src++;
-            *dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2;
-            src++;
-            *dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2;
-            src += 4;
+            U32 x0 = x * 2;
+            U32 y0 = y * 2;
+            U32 x1 = (x0 + 1 < srcWidth) ? x0 + 1 : x0;
+            U32 y1 = (y0 + 1 < srcHeight) ? y0 + 1 : y0;
+
+            if constexpr (std::is_floating_point_v<T>)
+            {
+               T sum = 0;
+               sum += src[y0 * srcRowStride + x0 * channels + c];
+               sum += src[y0 * srcRowStride + x1 * channels + c];
+               sum += src[y1 * srcRowStride + x0 * channels + c];
+               sum += src[y1 * srcRowStride + x1 * channels + c];
+
+               dst[y * dstRowStride + x * channels + c] = sum * 0.25f;
+            }
+            else
+            {
+               U32 sum = 0;
+               sum += src[y0 * srcRowStride + x0 * channels + c];
+               sum += src[y0 * srcRowStride + x1 * channels + c];
+               sum += src[y1 * srcRowStride + x0 * channels + c];
+               sum += src[y1 * srcRowStride + x1 * channels + c];
+               dst[y * dstRowStride + x * channels + c] = T((sum + 2) >> 2);
+            }
          }
-         src += stride;   // skip
       }
    }
-   else
+}
+
+// 8-bit RGBA
+auto bitmapExtrudeU8_RGBA = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) {
+   bitmapExtrudeGeneric((const U8*)src, (U8*)dst, w, h, 4, bpp);
+};
+
+// 16-bit RGBA (U16 / F32 stored as U16)
+auto bitmapExtrudeU16_RGBA = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) {
+   bitmapExtrudeGeneric((const U16*)src, (U16*)dst, w, h, 4, bpp);
+};
+
+// 32-bit float RGBA
+auto bitmapExtrudeF32_RGBA = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) {
+   bitmapExtrudeGeneric((const F32*)src, (F32*)dst, w, h, 4, bpp);
+};
+
+// RGB U8
+auto bitmapExtrudeU8_RGB = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) {
+   bitmapExtrudeGeneric((const U8*)src, (U8*)dst, w, h, 3, bpp);
+};
+
+void (*bitmapExtrude5551)(const void* srcMip, void* mip, U32 height, U32 width) = bitmapExtrude5551_c;
+void (*bitmapExtrudeRGB)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU8_RGB;
+void (*bitmapExtrudeRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU8_RGBA;
+void (*bitmapExtrude16BitRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU16_RGBA;
+void (*bitmapExtrudeFPRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU16_RGBA;
+void (*bitmapExtrudeF32RGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeF32_RGBA;
+
+struct StbResizeDesc
+{
+   stbir_datatype datatype;
+   stbir_pixel_layout layout;
+   U32 bytesPerPixel;
+};
+
+inline bool getStbResizeDesc(GFXFormat fmt, StbResizeDesc& out)
+{
+   switch (fmt)
    {
-      for(U32 y = 0; y < height; y++)
-      {
-         *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
-         src++;
-         *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
-         src++;
-         *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
-         src += 4;
-
-         src += stride;   // skip
-      }
+      // ---- 1 channel ----
+   case GFXFormatA8:
+   case GFXFormatL8:
+      out = { STBIR_TYPE_UINT8, STBIR_1CHANNEL, 1 };
+      return true;
+
+   case GFXFormatL16:
+      out = { STBIR_TYPE_UINT16, STBIR_1CHANNEL, 2 };
+      return true;
+
+   case GFXFormatR16F:
+      out = { STBIR_TYPE_HALF_FLOAT, STBIR_1CHANNEL, 2 };
+      return true;
+
+   case GFXFormatR32F:
+      out = { STBIR_TYPE_FLOAT, STBIR_1CHANNEL, 4 };
+      return true;
+
+      // ---- 2 channel ----
+   case GFXFormatA8L8:
+      out = { STBIR_TYPE_UINT8, STBIR_2CHANNEL, 2 };
+      return true;
+
+   case GFXFormatR16G16:
+      out = { STBIR_TYPE_UINT16, STBIR_2CHANNEL, 4 };
+      return true;
+
+   case GFXFormatR16G16F:
+      out = { STBIR_TYPE_HALF_FLOAT, STBIR_2CHANNEL, 4 };
+      return true;
+
+      // ---- RGB ----
+   case GFXFormatR8G8B8:
+      out = { STBIR_TYPE_UINT8, STBIR_RGB, 3 };
+      return true;
+
+   case GFXFormatR8G8B8_SRGB:
+      out = { STBIR_TYPE_UINT8_SRGB, STBIR_RGB, 3 };
+      return true;
+
+      // ---- RGBA / RGBX ----
+   case GFXFormatR8G8B8A8:
+   case GFXFormatR8G8B8X8:
+      out = { STBIR_TYPE_UINT8, STBIR_RGBA, 4 };
+      return true;
+
+   case GFXFormatR8G8B8A8_SRGB:
+      out = { STBIR_TYPE_UINT8_SRGB_ALPHA, STBIR_RGBA, 4 };
+      return true;
+
+   case GFXFormatB8G8R8A8:
+      out = { STBIR_TYPE_UINT8, STBIR_BGRA, 4 };
+      return true;
+
+      // ---- 16-bit RGBA ----
+   case GFXFormatR16G16B16A16:
+      out = { STBIR_TYPE_UINT16, STBIR_RGBA, 8 };
+      return true;
+
+   case GFXFormatR16G16B16A16F:
+      out = { STBIR_TYPE_HALF_FLOAT, STBIR_RGBA, 8 };
+      return true;
+
+      // ---- 32-bit RGBA ----
+   case GFXFormatR32G32B32A32F:
+      out = { STBIR_TYPE_FLOAT, STBIR_RGBA, 16 };
+      return true;
+
+   default:
+      return false;
    }
 }
 
-//--------------------------------------------------------------------------
-void bitmapExtrudeRGBA_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
+void bitmapStbResizeToOutput(const void* src, U32 srcHeight, U32 srcWidth, void* out, U32 outHeight, U32 outWidth, U32 bpp, GFXFormat format)
 {
-   const U8 *src = (const U8 *) srcMip;
-   U8 *dst = (U8 *) mip;
-   U32 stride = srcHeight != 1 ? (srcWidth) * 4 : 0;
+   StbResizeDesc desc;
+   if (!getStbResizeDesc(format, desc))
+   {
+      return;
+   }
 
-   U32 width  = srcWidth  >> 1;
-   U32 height = srcHeight >> 1;
-   if (width  == 0) width  = 1;
-   if (height == 0) height = 1;
+   const int srcStride = srcWidth * bpp;
+   const int dstStride = outWidth * bpp;
+
+   stbir_resize(
+      src,
+      srcWidth,
+      srcHeight,
+      srcStride,
+      out,
+      outWidth,
+      outHeight,
+      dstStride,
+      desc.layout,
+      desc.datatype,
+      STBIR_EDGE_CLAMP,
+      STBIR_FILTER_MITCHELL);
+}
 
-   if (srcWidth != 1)
+void(*bitmapResizeToOutput)(const void* src, U32 srcHeight, U32 srcWidth, void* out, U32 outHeight, U32 outWidth, U32 bpp, GFXFormat format) = bitmapStbResizeToOutput;
+
+//--------------------------------------------------------------------------------
+// Format description
+
+//--------------------------------------------------------------------------------
+// Channel semantics
+enum ChannelSemantic : U8
+{
+   CH_NONE,
+   CH_L,
+   CH_A,
+   CH_R,
+   CH_G,
+   CH_B
+};
+
+//--------------------------------------------------------------------------------
+// Bitmap format descriptor
+struct GBitmapFormatDesc
+{
+   U8 channels;
+   ChannelSemantic semantic[4]; // per-channel meaning
+   stbir_datatype datatype;
+   bool srgb;
+   bool premultiplied;
+   bool isFloat;
+   U8 bytesPerChannel;
+
+   bool is8()  const { return !isFloat && bytesPerChannel == 1; }
+   bool is16() const { return !isFloat && bytesPerChannel == 2; }
+};
+
+//--------------------------------------------------------------------------------
+// Table mapping GFXFormat -> descriptor
+GBitmapFormatDesc getFormatDesc(GFXFormat fmt)
+{
+   switch (fmt)
    {
-      for(U32 y = 0; y < height; y++)
+      // 8-bit formats
+   case GFXFormatA8:
+      return { 1, {CH_A, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
+   case GFXFormatL8:
+      return { 1, {CH_L, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
+   case GFXFormatA4L4:
+      return { 2, {CH_L, CH_A, CH_NONE, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
+
+      // 16-bit formats
+   case GFXFormatR5G6B5:
+      return { 3, {CH_R, CH_G, CH_B, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
+   case GFXFormatR5G5B5A1:
+      return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_UINT8, false, false, false, 1 };
+   case GFXFormatR5G5B5X1:
+      return { 4, {CH_R, CH_G, CH_B, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
+   case GFXFormatA8L8:
+      return { 2, {CH_L, CH_A, CH_NONE, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
+   case GFXFormatL16:
+      return { 1, {CH_L, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_UINT16, false, false, false, 2 };
+   case GFXFormatR16F:
+      return { 1, {CH_R, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_HALF_FLOAT, false, false, true, 2 };
+   case GFXFormatD16:
+      return { 1, {CH_L, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_UINT16, false, false, false, 2 };
+
+      // 24-bit formats
+   case GFXFormatR8G8B8:
+      return { 3, {CH_R, CH_G, CH_B, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
+   case GFXFormatR8G8B8_SRGB:
+      return { 3, {CH_R, CH_G, CH_B, CH_NONE}, STBIR_TYPE_UINT8_SRGB, true, false, false, 1 };
+
+      // 32-bit formats
+   case GFXFormatR8G8B8A8:
+   case GFXFormatR8G8B8X8:
+      return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_UINT8, false, false, false, 1 };
+   case GFXFormatB8G8R8A8:
+      return { 4, {CH_B, CH_G, CH_R, CH_A}, STBIR_TYPE_UINT8, false, false, false, 1 };
+   case GFXFormatR8G8B8A8_SRGB:
+      return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_UINT8_SRGB_ALPHA, true, false, false, 1 };
+   case GFXFormatR16G16:
+      return { 2, {CH_R, CH_G, CH_NONE, CH_NONE}, STBIR_TYPE_UINT16, false, false, false, 2 };
+   case GFXFormatR16G16F:
+      return { 2, {CH_R, CH_G, CH_NONE, CH_NONE}, STBIR_TYPE_HALF_FLOAT, false, false, true, 2 };
+
+      // 64-bit formats
+   case GFXFormatR16G16B16A16:
+      return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_UINT16, false, false, false, 2 };
+   case GFXFormatR16G16B16A16F:
+      return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_HALF_FLOAT, false, false, true, 2 };
+
+      // 128-bit formats
+   case GFXFormatR32G32B32A32F:
+      return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_FLOAT, false, false, true, 4 };
+
+   default: // fallback
+      return { 1, {CH_L, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
+   }
+}
+
+//--------------------------------------------------------------------------------
+// Conversion plan
+struct ConversionPlan
+{
+   bool bitDepthChange;
+   bool channelRepack;
+   bool colorspaceChange;
+};
+
+ConversionPlan decideConversion(const GBitmapFormatDesc& src, const GBitmapFormatDesc& dst)
+{
+   ConversionPlan plan = {};
+   plan.bitDepthChange = src.bytesPerChannel != dst.bytesPerChannel || src.isFloat != dst.isFloat;
+   plan.channelRepack = src.channels != dst.channels || dMemcmp(src.semantic, dst.semantic, sizeof(src.semantic)) != 0;
+   plan.colorspaceChange = src.srgb != dst.srgb;
+   return plan;
+}
+
+//--------------------------------------------------------------------------------
+// Linear representation
+struct LinearPixel
+{
+   float r = 0.f, g = 0.f, b = 0.f, a = 1.f;
+};
+
+inline float srgbToLinear(float c)
+{
+   return (c <= 0.04045f) ? c / 12.92f : powf((c + 0.055f) / 1.055f, 2.4f);
+}
+
+inline float linearToSrgb(float c)
+{
+   return (c <= 0.0031308f) ? c * 12.92f : 1.055f * powf(c, 1.f / 2.4f) - 0.055f;
+}
+
+//--------------------------------------------------------------------------------
+// Load a pixel from src format into LinearPixel
+static inline LinearPixel loadPixel(const void* src, const GBitmapFormatDesc& fmt, U32 index)
+{
+   LinearPixel p;
+   const U8* base = (const U8*)src + index * fmt.channels * fmt.bytesPerChannel;
+
+   for (U32 c = 0; c < fmt.channels; ++c)
+   {
+      float v = 0.f;
+      if (fmt.is8())
+         v = float(base[c]) / 255.f;
+      else if (fmt.is16())
+         v = float(convert16To8(*(const U16*)(base + c * 2))) / 255.f;
+      else if (fmt.isFloat)
       {
-         for(U32 x = 0; x < width; x++)
-         {
-            *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
-            src++;
-            *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
-            src++;
-            *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
-            src++;
-            *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
-            src += 5;
-         }
-         src += stride;   // skip
+         if (fmt.bytesPerChannel == 2) // half float
+            v = convertHalfToFloat(*(const U16*)(base + c * 2));
+         else // full float
+            v = *(const float*)(base + c * 4);
+      }
+
+      if (fmt.srgb && fmt.semantic[c] != CH_A)
+         v = srgbToLinear(v);
+
+      switch (fmt.semantic[c])
+      {
+      case CH_R: p.r = v; break;
+      case CH_G: p.g = v; break;
+      case CH_B: p.b = v; break;
+      case CH_A: p.a = v; break;
+      case CH_L: p.r = p.g = p.b = v; break;
+      default: break;
       }
    }
-   else
+   return p;
+}
+
+//--------------------------------------------------------------------------------
+// Store a LinearPixel into dst format
+static inline void storePixel(void* dst, const GBitmapFormatDesc& fmt, U32 index, const LinearPixel& p)
+{
+   U8* base = (U8*)dst + index * fmt.channels * fmt.bytesPerChannel;
+   for (U32 c = 0; c < fmt.channels; ++c)
    {
-      for(U32 y = 0; y < height; y++)
+      float v = 0.f;
+      switch (fmt.semantic[c])
       {
-         *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
-         src++;
-         *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
-         src++;
-         *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
-         src++;
-         *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
-         src += 5;
-
-         src += stride;   // skip
+      case CH_R: v = p.r; break;
+      case CH_G: v = p.g; break;
+      case CH_B: v = p.b; break;
+      case CH_A: v = p.a; break;
+      case CH_L: v = (p.r + p.g + p.b) / 3.f; break;
+      default: break;
+      }
+
+      if (fmt.srgb && fmt.semantic[c] != CH_A)
+         v = linearToSrgb(v);
+
+      if (fmt.is8())
+         base[c] = uint8_t(mClamp(v * 255.f, 0.f, 255.f));
+      else if (fmt.is16())
+         *(U16*)(base + c * 2) = convert8To16(uint8_t(mClamp(v * 255.f, 0.f, 255.f)));
+      else if (fmt.isFloat)
+      {
+         if (fmt.bytesPerChannel == 2) // half float
+            *(U16*)(base + c * 2) = convertFloatToHalf(v);
+         else
+            *(float*)(base + c * 4) = v;
       }
    }
 }
 
-void bitmapExtrudeFPRGBA_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
+//--------------------------------------------------------------------------------
+// Main generalized converter
+bool bitmapConvertFormat(U8** srcBuffer, U32 pixels, const GBitmapFormatDesc& srcFmt, const GBitmapFormatDesc& dstFmt)
 {
-   const U16 *src = (const U16 *)srcMip;
-   U16 *dst = (U16 *)mip;
-   U32 stride = srcHeight != 1 ? (srcWidth) * 8 : 0;
+   ConversionPlan plan = decideConversion(srcFmt, dstFmt);
+   if (!plan.bitDepthChange && !plan.channelRepack && !plan.colorspaceChange)
+      return true; // nothing to do
 
-   U32 width = srcWidth >> 1;
-   U32 height = srcHeight >> 1;
-   if (width == 0) width = 1;
-   if (height == 0) height = 1;
+   void* dstBuffer = *srcBuffer;
 
-   if (srcWidth != 1)
+   if (plan.bitDepthChange || plan.channelRepack)
+      dstBuffer = new U8[pixels * dstFmt.channels * dstFmt.bytesPerChannel];
+
+   for (U32 i = 0; i < pixels; ++i)
    {
-      for (U32 y = 0; y < height; y++)
-      {
-         for (U32 x = 0; x < width; x++)
-         {
-            *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2;
-            src++;
-            *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2;
-            src++;
-            *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2;
-            src++;
-            *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2;
-            src += 5;
-         }
-         src += stride;   // skip
-      }
+      LinearPixel p = loadPixel(*srcBuffer, srcFmt, i);
+      storePixel(dstBuffer, dstFmt, i, p);
    }
-   else
+
+   if (dstBuffer != *srcBuffer)
    {
-      for (U32 y = 0; y < height; y++)
-      {
-         *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
-         src++;
-         *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
-         src++;
-         *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
-         src++;
-         *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
-         src += 5;
-
-         src += stride;   // skip
-      }
+      delete[](U8*)* srcBuffer;
+      *srcBuffer = (U8*)dstBuffer;
    }
+
+   return true;
 }
 
-void (*bitmapExtrude5551)(const void *srcMip, void *mip, U32 height, U32 width) = bitmapExtrude5551_c;
-void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeRGB_c;
-void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeRGBA_c;
-void (*bitmapExtrudeFPRGBA)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeFPRGBA_c;
+//--------------------------------------------------------------------------------
+// Entry point for GBitmap::setFormat
+bool bitmapALLConvertToOutput(U8** src, U32 pixels, GFXFormat srcFormat, GFXFormat dstFormat)
+{
+   const GBitmapFormatDesc& srcFmt = getFormatDesc(srcFormat);
+   const GBitmapFormatDesc& dstFmt = getFormatDesc(dstFormat);
+   return bitmapConvertFormat(src, pixels, srcFmt, dstFmt);
+}
 
+bool(*bitmapConvertToOutput)(U8** src, U32 pixels, GFXFormat srcFormat, GFXFormat dstFormat) = bitmapALLConvertToOutput;
 
 //--------------------------------------------------------------------------
 
@@ -238,7 +526,7 @@ void bitmapConvertRGB_to_1555_c(U8 *src, U32 pixels)
       U32 g = src[1] >> 3;
       U32 b = src[2] >> 3;
 
-#if defined(TORQUE_OS_MAC)
+#if defined(TORQUE_BIG_ENDIAN)
       *dst++ = 0x8000 | (b << 10) | (g << 5) | (r << 0);
 #else
       *dst++ = b | (g << 5) | (r << 10) | 0x8000;
@@ -260,7 +548,7 @@ void bitmapConvertRGB_to_5551_c(U8 *src, U32 pixels)
       U32 g = src[1] >> 3;
       U32 b = src[2] >> 3;
 
-#if defined(TORQUE_OS_MAC)
+#if defined(TORQUE_BIG_ENDIAN)
       *dst++ = (1 << 15) | (b << 10) | (g << 5) | (r << 0);
 #else
       *dst++ = (b << 1) | (g << 6) | (r << 11) | 1;

+ 132 - 5
Engine/source/gfx/bitmap/bitmapUtils.h

@@ -22,21 +22,148 @@
 
 #ifndef _BITMAPUTILS_H_
 #define _BITMAPUTILS_H_
-
+#ifndef _PLATFORM_H_
+#include "platform/platform.h"
+#endif
 #ifndef _TORQUE_TYPES_H_
 #include "platform/types.h"
 #endif
+#ifndef _GFXENUMS_H_
+#include "gfx/gfxEnums.h"
+#endif
+#ifndef _MMATHFN_H_
+#include "math/mMathFn.h"
+#endif
 
 extern void (*bitmapExtrude5551)(const void *srcMip, void *mip, U32 height, U32 width);
-extern void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 height, U32 width);
-extern void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 height, U32 width);
-extern void(*bitmapExtrudeFPRGBA)(const void *srcMip, void *mip, U32 height, U32 width);
+extern void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp);
+extern void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp);
+extern void (*bitmapExtrude16BitRGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp);
+extern void(*bitmapExtrudeFPRGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp);
+extern void(*bitmapExtrudeF32RGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp);
+
+extern void(*bitmapResizeToOutput)(const void* src, U32 srcHeight, U32 srcWidth, void* out, U32 outHeight, U32 outWidth, U32 bpp, GFXFormat format);
+extern bool(*bitmapConvertToOutput)(U8** src, U32 pixels, GFXFormat srcFormat, GFXFormat dstFormat);
+
 extern void (*bitmapConvertRGB_to_5551)(U8 *src, U32 pixels);
 extern void (*bitmapConvertRGB_to_1555)(U8 *src, U32 pixels);
 extern void (*bitmapConvertRGB_to_RGBX)( U8 **src, U32 pixels );
 extern void (*bitmapConvertRGBX_to_RGB)( U8 **src, U32 pixels );
 extern void (*bitmapConvertA8_to_RGBA)( U8 **src, U32 pixels );
 
-void bitmapExtrudeRGB_c(const void *srcMip, void *mip, U32 height, U32 width);
+//-----------------------------------------------------------------------------
+// Half <-> Float Conversion Utilities
+//-----------------------------------------------------------------------------
+
+inline F32 convertHalfToFloat(U16 h)
+{
+   U32 sign = (h >> 15) & 0x00000001;
+   U32 exp = (h >> 10) & 0x0000001F;
+   U32 mant = h & 0x000003FF;
+
+   U32 outSign = sign << 31;
+   U32 outExp, outMant;
+
+   if (exp == 0)
+   {
+      if (mant == 0)
+      {
+         // Zero
+         outExp = 0;
+         outMant = 0;
+      }
+      else
+      {
+         // Subnormal number -> normalize
+         exp = 1;
+         while ((mant & 0x00000400) == 0)
+         {
+            mant <<= 1;
+            exp -= 1;
+         }
+         mant &= 0x000003FF;
+         outExp = (exp + (127 - 15)) << 23;
+         outMant = mant << 13;
+      }
+   }
+   else if (exp == 31)
+   {
+      // Inf or NaN
+      outExp = 0xFF << 23;
+      outMant = mant ? (mant << 13) : 0;
+   }
+   else
+   {
+      // Normalized
+      outExp = (exp + (127 - 15)) << 23;
+      outMant = mant << 13;
+   }
+
+   U32 out = outSign | outExp | outMant;
+   F32 result;
+   dMemcpy(&result, &out, sizeof(F32));
+   return result;
+}
+
+inline U16 convertFloatToHalf(F32 f)
+{
+   U32 bits;
+   dMemcpy(&bits, &f, sizeof(U32));
+
+   U32 sign = (bits >> 16) & 0x00008000;
+   U32 exp = ((bits >> 23) & 0x000000FF) - (127 - 15);
+   U32 mant = bits & 0x007FFFFF;
+
+   if (exp <= 0)
+   {
+      if (exp < -10)
+         return (U16)sign; // Too small => 0
+      mant = (mant | 0x00800000) >> (1 - exp);
+      return (U16)(sign | (mant >> 13));
+   }
+   else if (exp == 0xFF - (127 - 15))
+   {
+      if (mant == 0)
+      {
+         // Inf
+         return (U16)(sign | 0x7C00);
+      }
+      else
+      {
+         // NaN
+         mant >>= 13;
+         return (U16)(sign | 0x7C00 | mant | (mant == 0));
+      }
+   }
+   else
+   {
+      if (exp > 30)
+      {
+         // Overflow => Inf
+         return (U16)(sign | 0x7C00);
+      }
+      return (U16)(sign | (exp << 10) | (mant >> 13));
+   }
+}
+
+// Convert a single 16-bit value (0..65535) to 8-bit (0..255)
+inline U8 convert16To8(U16 v16)
+{
+   // Take the top 8 bits as approximation
+   return U8(v16 >> 8);
+}
+
+// Convert a single 8-bit value (0..255) to 16-bit (0..65535)
+inline U16 convert8To16(U8 v8)
+{
+   // Replicate into high and low byte: 0->0, 255->0xFFFF
+   return (U16(v8) << 8) | v8;
+}
+
+inline U8 floatTo8(F32 v)
+{
+   return U8(mClamp(v * 255.f, 0.f, 255.f));
+}
+
 
 #endif //_BITMAPUTILS_H_

+ 13 - 36
Engine/source/gfx/bitmap/cubemapSaver.cpp

@@ -34,9 +34,7 @@
 
 namespace CubemapSaver
 {
-   const U32 CubeFaces = 6;
-
-   bool save(GFXCubemapHandle cubemap, const Torque::Path &path, GFXFormat compressionFormat)
+   bool save(GFXTexHandle cubemap, const Torque::Path &path, GFXFormat compressionFormat)
    {
       if (!cubemap.isValid())
       {
@@ -44,43 +42,24 @@ namespace CubemapSaver
          return false;
       }
 
-
-      GFXCubemap *pCubemap = cubemap.getPointer();
-      const U32 faceSize = pCubemap->getSize();
-      const U32 mipLevels = pCubemap->getMipMapLevels();
-
-      GFXFormat targetFmt = pCubemap->getFormat();
-      //setup render targets
-      GFXTexHandle pTextures[CubeFaces];
-
-      for (U32 face = 0; face < CubeFaces; face++)
-      {
-         pTextures[face].set(faceSize, faceSize, targetFmt,
-            &GFXTexturePersistentProfile, avar("%s() - (line %d)", __FUNCTION__, __LINE__),
-            mipLevels, GFXTextureManager::AA_MATCH_BACKBUFFER);
-
-         // yep t3d has funky z up, need to change the face order
-         GFX->copyResource(pTextures[face], pCubemap, GFXCubemap::zUpFaceIndex(face) );
-      }
-
-      GBitmap *pBitmaps[CubeFaces];
       bool error = false;
       const bool compressedFormat = ImageUtil::isCompressedFormat(compressionFormat);
+      const U32 faceSize = cubemap->getWidth();
+      const U32 mipLevels = cubemap->getMipLevels();
+      GFXFormat targetFmt = cubemap->getFormat();
       const bool hasMips = mipLevels > 1 ? true : false;
-      for (U32 i = 0; i < CubeFaces; i++)
+
+      GBitmap* temp = new GBitmap(faceSize, faceSize, hasMips, targetFmt, 6);
+      bool result = cubemap.copyToBmp(temp);
+      if (!result)
       {
-         pBitmaps[i] = new GBitmap(faceSize, faceSize, hasMips, targetFmt);
-         bool result = pTextures[i].copyToBmp(pBitmaps[i]);
-         if (!result)
-         {
-            Con::errorf("CubemapSaver: cubemap number %u failed to copy", i);
-            error = true;
-         }
+         Con::errorf("CubemapSaver: cubemap failed to copy");
+         error = true;
       }
 
       if (!error)
       {
-         DDSFile *pDds = DDSFile::createDDSCubemapFileFromGBitmaps(pBitmaps);
+         DDSFile *pDds = DDSFile::createDDSFileFromGBitmap(temp);
          if (pDds)
          {
             // compressed and floating point don't need swizzling
@@ -103,14 +82,12 @@ namespace CubemapSaver
       }
 
       //cleanup
-      for (U32 i = 0; i < CubeFaces; i++)
-         SAFE_DELETE(pBitmaps[i]);
-
+      SAFE_DELETE(temp);
 
       return true;
    }
 
-   bool getBitmaps(GFXCubemapHandle cubemap, GFXFormat compressionFormat, GBitmap* faceBitmaps[6])
+   bool getBitmaps(GFXTexHandle cubemap, GFXFormat compressionFormat, GBitmap* faceBitmaps[6])
    {
       if (!cubemap.isValid())
       {

+ 3 - 3
Engine/source/gfx/bitmap/cubemapSaver.h

@@ -33,9 +33,9 @@
 namespace CubemapSaver
 {
    // save cubemap handle to a dds cubemap with optional compression
-   bool save(GFXCubemapHandle cubemap, const Torque::Path &path, GFXFormat compressionFormat = GFXFormatR8G8B8A8);
+   bool save(GFXTexHandle cubemap, const Torque::Path &path, GFXFormat compressionFormat = GFXFormatR8G8B8A8);
 
-   bool getBitmaps(GFXCubemapHandle cubemap, GFXFormat compressionFormat, GBitmap* faceBitmaps[6]);
+   bool getBitmaps(GFXTexHandle cubemap, GFXFormat compressionFormat, GBitmap* faceBitmaps[6]);
 };
 
-#endif
+#endif

+ 74 - 32
Engine/source/gfx/bitmap/ddsFile.cpp

@@ -652,6 +652,12 @@ Resource<DDSFile> DDSFile::load( const Torque::Path &path, U32 dropMipCount )
 
 //------------------------------------------------------------------------------
 
+bool DDSFile::isCompressedFormat(GFXFormat fmt)
+{
+   return (fmt >= GFXFormatBC1 && fmt <= GFXFormatBC5) ||
+      (fmt >= GFXFormatBC1_SRGB && fmt <= GFXFormatBC3_SRGB);
+}
+
 DDSFile *DDSFile::createDDSFileFromGBitmap( const GBitmap *gbmp )
 {
    if( gbmp == NULL )
@@ -666,6 +672,11 @@ DDSFile *DDSFile::createDDSFileFromGBitmap( const GBitmap *gbmp )
    ret->mDepth = 0;
    ret->mFormat = gbmp->getFormat();
    ret->mFlags.set(RGBData);
+   if (gbmp->getNumFaces() == 6)
+   {
+      ret->mFlags.set(RGBData | CubeMapFlag | CubeMap_PosX_Flag | CubeMap_NegX_Flag | CubeMap_PosY_Flag |
+         CubeMap_NegY_Flag | CubeMap_PosZ_Flag | CubeMap_NegZ_Flag);
+   }
    ret->mBytesPerPixel = gbmp->getBytesPerPixel();
    ret->mMipMapCount = gbmp->getNumMipLevels();
    ret->mHasTransparency = gbmp->getHasTransparency();
@@ -685,36 +696,39 @@ DDSFile *DDSFile::createDDSFileFromGBitmap( const GBitmap *gbmp )
    if( ret->mMipMapCount > 1 )
       ret->mFlags.set(MipMapsFlag);
 
-   // One surface per GBitmap
-   ret->mSurfaces.push_back( new SurfaceData() );
-
-   // Load the mips
-   for( S32 i = 0; i < ret->mMipMapCount; i++ )
+   for (U32 face = 0; face < gbmp->getNumFaces(); face++)
    {
-      const U32 mipSz = ret->getSurfaceSize(i);
-      ret->mSurfaces.last()->mMips.push_back( new U8[mipSz] );
+      // One surface per GBitmap
+      ret->mSurfaces.push_back(new SurfaceData());
 
-      U8 *mipMem = ret->mSurfaces.last()->mMips.last();
-      
-      // If this is a straight copy, just do it, otherwise (ugh)
-      if( ret->mFormat == gbmp->getFormat() )
-         dMemcpy( mipMem, gbmp->getBits(i), mipSz );
-      else
+      // Load the mips
+      for (S32 i = 0; i < ret->mMipMapCount; i++)
       {
-         // Assumption:
-         AssertFatal( gbmp->getBytesPerPixel() + 1 == ret->mBytesPerPixel, "Assumption failed, not 24->32 bit straight convert." );
+         const U32 mipSz = ret->getSurfaceSize(i);
+         ret->mSurfaces.last()->mMips.push_back(new U8[mipSz]);
 
-         for( S32 pxl = 0; pxl < gbmp->getWidth(i) * gbmp->getHeight(i); pxl++ )
+         U8* mipMem = ret->mSurfaces.last()->mMips.last();
+
+         // If this is a straight copy, just do it, otherwise (ugh)
+         if (ret->mFormat == gbmp->getFormat())
+            dMemcpy(mipMem, gbmp->getBits(i, face), mipSz);
+         else
          {
-            U8 *dst = &mipMem[pxl * ret->mBytesPerPixel];
-            const U8 *src = &gbmp->getBits(i)[pxl * gbmp->getBytesPerPixel()];
-            dMemcpy( dst, src, gbmp->getBytesPerPixel() * sizeof(U8) );
-            dst[ret->mBytesPerPixel - 1] = 255;
-         } 
-      }
+            // Assumption:
+            AssertFatal(gbmp->getBytesPerPixel() + 1 == ret->mBytesPerPixel, "Assumption failed, not 24->32 bit straight convert.");
+
+            for (S32 pxl = 0; pxl < gbmp->getWidth(i) * gbmp->getHeight(i); pxl++)
+            {
+               U8* dst = &mipMem[pxl * ret->mBytesPerPixel];
+               const U8* src = &gbmp->getBits(i, face)[pxl * gbmp->getBytesPerPixel()];
+               dMemcpy(dst, src, gbmp->getBytesPerPixel() * sizeof(U8));
+               dst[ret->mBytesPerPixel - 1] = 255;
+            }
+         }
 
-      // Uncomment to debug-dump each mip level
-      //ret->mSurfaces.last()->dumpImage( ret, i, avar( "%d_Gbmp_xmip%d", ret, i ) );
+         // Uncomment to debug-dump each mip level
+         //ret->mSurfaces.last()->dumpImage( ret, i, avar( "%d_Gbmp_xmip%d", ret, i ) );
+      }
    }
 
    return ret;
@@ -777,22 +791,50 @@ DDSFile *DDSFile::createDDSCubemapFileFromGBitmaps(GBitmap **gbmps)
 
 bool DDSFile::decompressToGBitmap(GBitmap *dest)
 {
+   const bool isCube = isCubemap();
+   const U32 numFaces = isCube ? 6 : 1;
    // TBD: do we support other formats?
-   if (mFormat != GFXFormatBC1 && mFormat != GFXFormatBC2 && mFormat != GFXFormatBC3)
-      return false;
+   if (!isCompressedFormat(mFormat))
+   {
+      dest->allocateBitmapWithMips(getWidth(), getHeight(), getMipLevels(), mFormat, numFaces);
+      U32 numMips = getMipLevels();
+
+      for (U32 face = 0; face < numFaces; face++)
+      {
+         for (U32 i = 0; i < numMips; i++)
+         {
+            U8* addr = dest->getAddress(0, 0, i, face);
+
+            const U8* mipBuffer = mSurfaces[face]->mMips[i];
+            const U32 mipWidth = getWidth(i);
+            const U32 mipHeight = getHeight(i);
+
+            const U32 bpp = dest->getBytesPerPixel();
+            const U32 rowBytes = mipWidth * bpp;
 
-   dest->allocateBitmapWithMips(getWidth(), getHeight(), getMipLevels(), GFXFormatR8G8B8A8);
+            for (U32 y = 0; y < mipHeight; ++y)
+            {
+               dMemcpy(addr + y * rowBytes, mipBuffer + y * rowBytes, rowBytes);
+            }
+         }
+      }
+      return true;
+   }
+
+   dest->allocateBitmapWithMips(getWidth(), getHeight(), getMipLevels(), GFXFormatR8G8B8A8, numFaces);
 
    // Decompress and copy mips...
 
    U32 numMips = getMipLevels();
-
-   for (U32 i = 0; i < numMips; i++)
+   for (U32 face = 0; face < numFaces; face++)
    {
-      U8 *addr = dest->getAddress(0, 0, i);
-      const U8 *mipBuffer = mSurfaces[0]->mMips[i];
-      ImageUtil::decompress(mipBuffer, addr, getWidth(i), getHeight(i), mFormat);
+      for (U32 i = 0; i < numMips; i++)
+      {
+         U8* addr = dest->getAddress(0, 0, i, face);
+         const U8* mipBuffer = mSurfaces[face]->mMips[i];
+         ImageUtil::decompress(mipBuffer, addr, getWidth(i), getHeight(i), mFormat);
 
+      }
    }
 
    return true;

+ 2 - 0
Engine/source/gfx/bitmap/ddsFile.h

@@ -205,6 +205,8 @@ struct DDSFile
       mSurfaces.clear();
    }
 
+   bool isCompressedFormat(GFXFormat fmt);
+
    static DDSFile *createDDSFileFromGBitmap( const GBitmap *gbmp );
    //Create a single cubemap texture from 6 GBitmap
    static DDSFile *createDDSCubemapFileFromGBitmaps(GBitmap **gbmps);

File diff suppressed because it is too large
+ 513 - 278
Engine/source/gfx/bitmap/gBitmap.cpp


+ 34 - 19
Engine/source/gfx/bitmap/gBitmap.h

@@ -135,13 +135,15 @@ public:
    GBitmap(const U32  in_width,
            const U32  in_height,
            const bool in_extrudeMipLevels = false,
-           const GFXFormat in_format = GFXFormatR8G8B8 );
+           const GFXFormat in_format = GFXFormatR8G8B8,
+           const U32 in_numFaces = 1);
 
    // This builds a GBitmap with the R8G8B8A8 format using the passed in
    // data (assumes that there is width * height * 4 U8's in data)
    GBitmap(const U32  in_width,
            const U32  in_height,
-           const U8*  data );
+           const U8*  data,
+           const U32 in_numFaces = 1);
 
    virtual ~GBitmap();
 
@@ -163,12 +165,14 @@ public:
    void allocateBitmap(const U32  in_width,
                        const U32  in_height,
                        const bool in_extrudeMipLevels = false,
-                       const GFXFormat in_format = GFXFormatR8G8B8 );
+                       const GFXFormat in_format = GFXFormatR8G8B8,
+                       const U32 in_numFaces = 1);
 
    void allocateBitmapWithMips(const U32  in_width,
-      const U32  in_height,
-      const U32  in_numMips,
-      const GFXFormat in_format = GFXFormatR8G8B8);
+                              const U32  in_height,
+                              const U32  in_numMips,
+                              const GFXFormat in_format = GFXFormatR8G8B8,
+                              const U32 in_numFaces = 1);
 
    void extrudeMipLevels(bool clearBorders = false);
    void chopTopMips(U32 mipsToChop);
@@ -191,16 +195,18 @@ public:
 
    U32         getWidth(const U32 in_mipLevel  = 0) const;
    U32         getHeight(const U32 in_mipLevel = 0) const;
+   U32         _getFaceOffset(const U32 face = 0) const;
    U32         getDepth(const U32 in_mipLevel = 0) const;
 
-   U8*         getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0);
-   const U8*   getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0) const;
+   U8*         getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0, const U32 face = 0);
+   const U8*   getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0, const U32 face = 0) const;
 
-   const U8*   getBits(const U32 in_mipLevel = 0) const;
-   U8*         getWritableBits(const U32 in_mipLevel = 0);
+   const U8*   getBits(const U32 in_mipLevel = 0, const U32 face = 0) const;
+   U8*         getWritableBits(const U32 in_mipLevel = 0, const U32 face = 0);
 
    U32         getByteSize() const { return mByteSize; }
    U32         getBytesPerPixel() const { return mBytesPerPixel; }
+   U32         getFormatBytesPerPixel(GFXFormat fmt);
 
    U32         getSurfaceSize(const U32 mipLevel) const;
 
@@ -220,6 +226,7 @@ public:
    bool        getColor(const U32 x, const U32 y, ColorI& rColor) const;
    bool        setColor(const U32 x, const U32 y, const ColorI& rColor);
    U8          getChanelValueAt(U32 x, U32 y, U32 chan);
+   U32         getNumFaces() const { return mNumFaces; }
 
    /// This method will combine bitmapA and bitmapB using the operation specified
    /// by combineOp. The result will be stored in the bitmap that this method is
@@ -275,7 +282,7 @@ public:
 
 private:
    GFXFormat mInternalFormat;
-
+   U32 mNumFaces;    // default 1, set to 6 for cubemap
    U8* mBits; // Master bytes
    U32 mByteSize;
    U32 mWidth;
@@ -284,6 +291,7 @@ private:
 
    U32 mNumMipLevels;
    U32 mMipLevelOffsets[c_maxMipLevels];
+   U32 mFaceOffsets[6];  // Maximum 6 for cubemaps; could also dynamically allocate if needed
 
    bool mHasTransparency;
 
@@ -316,32 +324,39 @@ inline U32 GBitmap::getHeight(const U32 in_mipLevel) const
    return (retVal != 0) ? retVal : 1;
 }
 
-inline const U8* GBitmap::getBits(const U32 in_mipLevel) const
+inline U32 GBitmap::_getFaceOffset(const U32 face) const
+{
+   AssertFatal(face < mNumFaces, "GBitmap::_getFaceOffset: invalid face index");
+
+   return mFaceOffsets[face];
+}
+
+inline const U8* GBitmap::getBits(const U32 in_mipLevel, const U32 face) const
 {
    AssertFatal(in_mipLevel < mNumMipLevels,
                avar("GBitmap::getBits: mip level out of range: (%d, %d)",
                     in_mipLevel, mNumMipLevels));
 
-   return &mBits[mMipLevelOffsets[in_mipLevel]];
+   return &mBits[_getFaceOffset(face) + mMipLevelOffsets[in_mipLevel]];
 }
 
-inline U8* GBitmap::getWritableBits(const U32 in_mipLevel)
+inline U8* GBitmap::getWritableBits(const U32 in_mipLevel, const U32 face)
 {
    AssertFatal(in_mipLevel < mNumMipLevels,
                avar("GBitmap::getWritableBits: mip level out of range: (%d, %d)",
                     in_mipLevel, mNumMipLevels));
 
-   return &mBits[mMipLevelOffsets[in_mipLevel]];
+   return &mBits[_getFaceOffset(face) + mMipLevelOffsets[in_mipLevel]];
 }
 
-inline U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel)
+inline U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel, const U32 face)
 {
-   return (getWritableBits(mipLevel) + (U64)(((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel));
+   return (getWritableBits(mipLevel, face) + (U64)(((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel));
 }
 
-inline const U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel) const
+inline const U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel, const U32 face) const
 {
-   return (getBits(mipLevel) + ((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel);
+   return (getBits(mipLevel, face) + ((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel);
 }
 
 template<class T, dsize_t mapLength>

+ 96 - 149
Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp

@@ -27,6 +27,7 @@
 #include "core/stream/memStream.h"
 #include "core/strings/stringFunctions.h"
 #include "gfx/bitmap/gBitmap.h"
+#include "gfx/bitmap/bitmapUtils.h"
 #include "gfx/bitmap/imageUtils.h"
 #include "gfx/bitmap/loaders/ies/ies_loader.h"
 #include "platform/profiler.h"
@@ -41,12 +42,12 @@
 #ifndef STB_IMAGE_IMPLEMENTATION
 #define STB_IMAGE_IMPLEMENTATION
 #define STB_IMAGE_STATIC
-#include "stb_image.h"
+#include "gfx/bitmap/loaders/stb/stb_image.h"
 #endif
 
 #define STB_IMAGE_WRITE_IMPLEMENTATION
 #define STB_IMAGE_WRITE_STATIC
-#include "stb_image_write.h"
+#include "gfx/bitmap/loaders/stb/stb_image_write.h"
 
 #pragma warning(pop)
 
@@ -56,6 +57,38 @@ static bool sReadStreamSTB(Stream& stream, GBitmap* bitmap, U32 len);
 static bool sWriteSTB(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel);
 static bool sWriteStreamSTB(const String& bmType, Stream& stream, GBitmap* bitmap, U32 compressionLevel);
 
+static GFXFormat determineFormat(bool isHDR, bool is16Bit, int numChannels)
+{
+   if (isHDR)
+   {
+      // we force hdr to 4 channels.
+      return GFXFormatR32G32B32A32F;
+   }
+   else if (is16Bit)
+   {
+      switch (numChannels)
+      {
+      case 1: return GFXFormatL16;
+      case 2: return GFXFormatA8L8; // No native L16A16, but could add one later
+      case 3: return GFXFormatR16G16B16A16;
+      case 4: return GFXFormatR16G16B16A16;
+      }
+   }
+   else // 8-bit
+   {
+      switch (numChannels)
+      {
+      case 1: return GFXFormatA8;
+      case 2: return GFXFormatA8L8;
+      case 3: return GFXFormatR8G8B8;
+      case 4: return GFXFormatR8G8B8A8;
+      }
+   }
+
+   // fallback
+   return GFXFormatR8G8B8A8;
+}
+
 // stbi_write callback / rextimmy.
 static void stbiWriteFunc(void* context, void* data, int size)
 {
@@ -210,119 +243,55 @@ bool sReadSTB(const Torque::Path& path, GBitmap* bitmap)
 
    }
 
-   if (!stbi_info(path.getFullPath().c_str(), &x, &y, &channels))
-   {
-      const char* stbErr = stbi_failure_reason();
+   const char* filePath = path.getFullPath().c_str();
 
-      if (!stbErr)
-         stbErr = "Unknown Error!";
+   // Detect format
+   bool isHDR = stbi_is_hdr(filePath);
+   bool is16Bit = stbi_is_16_bit(filePath);
 
-      Con::errorf("STB get file info: %s", stbErr);
-   }
+   void* data = nullptr;
 
-   // do this to map 2 channels to 4, 2 channel not supported by gbitmap yet..
-   if (channels == 2)
-      channels = 4;
-   if (!ext.equal("png"))
-   {
-      if (stbi_is_16_bit(path.getFullPath().c_str()))
-      {
-         U16* data = stbi_load_16(path.getFullPath().c_str(), &x, &y, &n, channels);
-
-         // if succesful deal make the bitmap, else try other loaders.
-         if (data)
-         {
-            GFXFormat format;
-            if (n == 1)
-               format = GFXFormatL16;
-            else
-               format = GFXFormatR16G16B16A16; // not sure if this is correct.
-
-            bitmap->deleteImage();
-
-            // actually allocate the bitmap space...
-            bitmap->allocateBitmap(x, y,
-               false,            // don't extrude miplevels...
-               format);          // use determined format...
-
-            U16* pBase = (U16*)bitmap->getBits();
-
-            U32 rowBytes = bitmap->getByteSize();
-
-            dMemcpy(pBase, data, rowBytes);
-
-            stbi_image_free(data);
-
-            PROFILE_END();
-            return true;
-         }
-      }
+   if (isHDR) {
+      data = stbi_loadf(filePath, &x, &y, &n, 4);
    }
+   else if (is16Bit)
+      data = stbi_load_16(filePath, &x, &y, &n, 0);
+   else
+      data = stbi_load(filePath, &x, &y, &n, 0);
 
-   if (ext.equal("hdr"))
+   if (!data)
    {
-      // force load to 4 channel.
-      float* data = stbi_loadf(path.getFullPath().c_str(), &x, &y, &n, 0);
-
-      unsigned char* dataChar = stbi__hdr_to_ldr(data, x, y, n);
-      bitmap->deleteImage();
-      // actually allocate the bitmap space...
-      bitmap->allocateBitmap(x, y,
-         false,
-         GFXFormatR8G8B8);
-
-      U8* pBase = (U8*)bitmap->getBits();
-
-      U32 rowBytes = x * y * n;
-
-      dMemcpy(pBase, dataChar, rowBytes);
-
-      //stbi_image_free(data);
-      stbi_image_free(dataChar);
-
-      PROFILE_END();
-      return true;
-   }
-
-   unsigned char* data = stbi_load(path.getFullPath().c_str(), &x, &y, &n, channels);
-
-   bitmap->deleteImage();
-
-   GFXFormat format;
-
-   switch (channels) {
-   case  1:
-      format = GFXFormatA8;
-      break;
-   case 2:
-      format = GFXFormatA8L8;
-      break;
-   case 3:
-      format = GFXFormatR8G8B8;
-      break;
-   case 4:
-      format = GFXFormatR8G8B8A8;
-      break;
-   default:
       PROFILE_END();
+      Con::errorf("sReadSTB() - Failed to load %s: %s", filePath, stbi_failure_reason());
       return false;
    }
 
-   // actually allocate the bitmap space...
-   bitmap->allocateBitmap(x, y,
-      false,            // don't extrude miplevels...
-      format);          // use determined format...
-
-   U8* pBase = (U8*)bitmap->getBits();
+   // Determine internal GFX format
+   GFXFormat format = determineFormat(isHDR, is16Bit, n);
 
-   U32 rowBytes = bitmap->getByteSize();
-
-   dMemcpy(pBase, data, rowBytes);
+   // Allocate bitmap
+   bitmap->deleteImage();
+   bitmap->allocateBitmap(x, y, false, format);
+
+   //if (isHDR)
+   //{
+   //   U16* pBase = (U16*)bitmap->getBits();
+   //   const size_t totalPixels = (size_t)x * (size_t)y;
+   //   for (size_t i = 0; i < totalPixels * 4; ++i)
+   //   {
+   //      pBase[i] = convertFloatToHalf(reinterpret_cast<F32*>(data)[i]); // convert F32 -> U16
+   //   }
+   //}
+   //else
+   //{
+      U8* dst = (U8*)bitmap->getBits();
+      U32 byteSize = bitmap->getByteSize();
+      dMemcpy(dst, data, byteSize);
+   //}
 
    stbi_image_free(data);
-   // Check this bitmap for transparency
-   if (channels == 4)
-      bitmap->checkForTransparency();
+
+   bitmap->checkForTransparency();
 
    PROFILE_END();
    return true;
@@ -331,45 +300,36 @@ bool sReadSTB(const Torque::Path& path, GBitmap* bitmap)
 bool sReadStreamSTB(Stream& stream, GBitmap* bitmap, U32 len)
 {
    PROFILE_SCOPE(sReadStreamSTB);
-   // only used for font at the moment.
 
-   U8* data = new U8[len];
-   stream.read(len, data);
+   Vector<U8> data(len);
+   stream.read(len, data.address());
 
-   S32 width, height, comp = 0;
+   int x, y, n;
+   bool isHDR = stbi_is_hdr_from_memory(data.address(), len);
+   bool is16Bit = stbi_is_16_bit_from_memory(data.address(), len);
 
-   unsigned char* pixelData = stbi_load_from_memory((const U8*)data, (int)len, &width, &height, &comp, 0);
+   void* pixels = nullptr;
+   if (isHDR)
+      pixels = stbi_loadf_from_memory(data.address(), len, &x, &y, &n, 0);
+   else if (is16Bit)
+      pixels = stbi_load_16_from_memory(data.address(), len, &x, &y, &n, 0);
+   else
+      pixels = stbi_load_from_memory(data.address(), len, &x, &y, &n, 0);
 
-   if (!pixelData)
+   if (!pixels)
    {
-      const char* stbErr = stbi_failure_reason();
-
-      if (!stbErr)
-         stbErr = "Unknown Error!";
-
-      Con::errorf("sReadStreamSTB Error: %s", stbErr);
+      Con::errorf("sReadStreamSTB() - STB load failed: %s", stbi_failure_reason());
       return false;
    }
-   bitmap->deleteImage();
-
-   //work out what format we need to use - todo floating point?
-   GFXFormat fmt = GFXFormat_FIRST;
-   switch (comp)
-   {
-   case 1: fmt = GFXFormatA8; break;
-   case 2: fmt = GFXFormatA8L8; break; //todo check this
-   case 3: fmt = GFXFormatR8G8B8; break;
-   case 4: fmt = GFXFormatR8G8B8A8; break;
-   }
 
-   bitmap->allocateBitmap(width, height, false, fmt);
+   GFXFormat format = determineFormat(isHDR, is16Bit, n);
+   bitmap->deleteImage();
+   bitmap->allocateBitmap(x, y, false, format);
+   dMemcpy(bitmap->getWritableBits(0), pixels, bitmap->getByteSize());
 
-   U8* pBase = bitmap->getWritableBits(0);
-   U32 rowBytes = bitmap->getByteSize();
-   dMemcpy(pBase, pixelData, rowBytes);
+   stbi_image_free(pixels);
 
-   dFree(data);
-   dFree(pixelData);
+   bitmap->checkForTransparency();
 
    return true;
 }
@@ -401,47 +361,34 @@ bool sWriteSTB(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel)
    GFXFormat format = bitmap->getFormat();
    String ext = path.getExtension();
 
-
-   // we always have at least 1
-   U32 comp = 1;
-
-   if (format == GFXFormatR8G8B8)
-   {
-      comp = 3;
-   }
-   else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8 || format == GFXFormatR8G8B8A8_LINEAR_FORCE)
-   {
-      comp = 4;
-   }
-
    if (ext.equal("png"))
    {
       stbi_write_png_compression_level = compressionLevel;
-      if (stbi_write_png(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits(), 0))
+      if (stbi_write_png(path.getFullPath().c_str(), width, height, bytes, bitmap->getWritableBits(), 0))
          return true;
    }
 
    if (ext.equal("tga"))
    {
-      if (stbi_write_tga(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits()))
+      if (stbi_write_tga(path.getFullPath().c_str(), width, height, bytes, bitmap->getWritableBits()))
          return true;
    }
 
    if (ext.equal("bmp"))
    {
-      if (stbi_write_bmp(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits()))
+      if (stbi_write_bmp(path.getFullPath().c_str(), width, height, bytes, bitmap->getWritableBits()))
          return true;
    }
 
    if (ext.equal("jpg") || ext.equal("jpeg"))
    {
-      if (stbi_write_jpg(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits(), compressionLevel))
+      if (stbi_write_jpg(path.getFullPath().c_str(), width, height, bytes, bitmap->getWritableBits(), compressionLevel))
          return true;
    }
 
    if (ext.equal("hdr"))
    {
-      if (stbi_write_hdr(path.getFullPath().c_str(), width, height, comp, (const F32*)bitmap->getWritableBits()))
+      if (stbi_write_hdr(path.getFullPath().c_str(), width, height, bytes, (const F32*)bitmap->getWritableBits()))
          return true;
    }
 

+ 179 - 178
Engine/source/gfx/bitmap/loaders/stb/stb_image.h

@@ -1,4 +1,4 @@
-/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb
+/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb
                                   no warranty implied; use at your own risk
 
    Do this:
@@ -48,6 +48,8 @@ LICENSE
 
 RECENT REVISION HISTORY:
 
+      2.30  (2024-05-31) avoid erroneous gcc warning
+      2.29  (2023-05-xx) optimizations
       2.28  (2023-01-29) many error fixes, security errors, just tons of stuff
       2.27  (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
       2.26  (2020-07-13) many minor fixes
@@ -1072,8 +1074,8 @@ static int stbi__addints_valid(int a, int b)
    return a <= INT_MAX - b;
 }
 
-// returns 1 if the product of two signed shorts is valid, 0 on overflow.
-static int stbi__mul2shorts_valid(short a, short b)
+// returns 1 if the product of two ints fits in a signed short, 0 on overflow.
+static int stbi__mul2shorts_valid(int a, int b)
 {
    if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
    if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
@@ -3384,13 +3386,13 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
    return 1;
 }
 
-static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
+static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
 {
    // some JPEGs have junk at end, skip over it but if we find what looks
    // like a valid marker, resume there
    while (!stbi__at_eof(j->s)) {
-      int x = stbi__get8(j->s);
-      while (x == 255) { // might be a marker
+      stbi_uc x = stbi__get8(j->s);
+      while (x == 0xff) { // might be a marker
          if (stbi__at_eof(j->s)) return STBI__MARKER_none;
          x = stbi__get8(j->s);
          if (x != 0x00 && x != 0xff) {
@@ -4176,6 +4178,7 @@ typedef struct
 {
    stbi_uc *zbuffer, *zbuffer_end;
    int num_bits;
+   int hit_zeof_once;
    stbi__uint32 code_buffer;
 
    char *zout;
@@ -4242,9 +4245,20 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
    int b,s;
    if (a->num_bits < 16) {
       if (stbi__zeof(a)) {
-         return -1;   /* report error for unexpected end of data. */
+         if (!a->hit_zeof_once) {
+            // This is the first time we hit eof, insert 16 extra padding btis
+            // to allow us to keep going; if we actually consume any of them
+            // though, that is invalid data. This is caught later.
+            a->hit_zeof_once = 1;
+            a->num_bits += 16; // add 16 implicit zero bits
+         } else {
+            // We already inserted our extra 16 padding bits and are again
+            // out, this stream is actually prematurely terminated.
+            return -1;
+         }
+      } else {
+         stbi__fill_bits(a);
       }
-      stbi__fill_bits(a);
    }
    b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
    if (b) {
@@ -4309,6 +4323,13 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
          int len,dist;
          if (z == 256) {
             a->zout = zout;
+            if (a->hit_zeof_once && a->num_bits < 16) {
+               // The first time we hit zeof, we inserted 16 extra zero bits into our bit
+               // buffer so the decoder can just do its speculative decoding. But if we
+               // actually consumed any of those bits (which is the case when num_bits < 16),
+               // the stream actually read past the end so it is malformed.
+               return stbi__err("unexpected end","Corrupt PNG");
+            }
             return 1;
          }
          if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
@@ -4320,7 +4341,7 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
          dist = stbi__zdist_base[z];
          if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
          if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
-         if (zout + len > a->zout_end) {
+         if (len > a->zout_end - zout) {
             if (!stbi__zexpand(a, zout, len)) return 0;
             zout = a->zout;
          }
@@ -4464,6 +4485,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
       if (!stbi__parse_zlib_header(a)) return 0;
    a->num_bits = 0;
    a->code_buffer = 0;
+   a->hit_zeof_once = 0;
    do {
       final = stbi__zreceive(a,1);
       type = stbi__zreceive(a,2);
@@ -4619,9 +4641,8 @@ enum {
    STBI__F_up=2,
    STBI__F_avg=3,
    STBI__F_paeth=4,
-   // synthetic filters used for first scanline to avoid needing a dummy row of 0s
-   STBI__F_avg_first,
-   STBI__F_paeth_first
+   // synthetic filter used for first scanline to avoid needing a dummy row of 0s
+   STBI__F_avg_first
 };
 
 static stbi_uc first_row_filter[5] =
@@ -4630,29 +4651,56 @@ static stbi_uc first_row_filter[5] =
    STBI__F_sub,
    STBI__F_none,
    STBI__F_avg_first,
-   STBI__F_paeth_first
+   STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub
 };
 
 static int stbi__paeth(int a, int b, int c)
 {
-   int p = a + b - c;
-   int pa = abs(p-a);
-   int pb = abs(p-b);
-   int pc = abs(p-c);
-   if (pa <= pb && pa <= pc) return a;
-   if (pb <= pc) return b;
-   return c;
+   // This formulation looks very different from the reference in the PNG spec, but is
+   // actually equivalent and has favorable data dependencies and admits straightforward
+   // generation of branch-free code, which helps performance significantly.
+   int thresh = c*3 - (a + b);
+   int lo = a < b ? a : b;
+   int hi = a < b ? b : a;
+   int t0 = (hi <= thresh) ? lo : c;
+   int t1 = (thresh <= lo) ? hi : t0;
+   return t1;
 }
 
 static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
 
+// adds an extra all-255 alpha channel
+// dest == src is legal
+// img_n must be 1 or 3
+static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n)
+{
+   int i;
+   // must process data backwards since we allow dest==src
+   if (img_n == 1) {
+      for (i=x-1; i >= 0; --i) {
+         dest[i*2+1] = 255;
+         dest[i*2+0] = src[i];
+      }
+   } else {
+      STBI_ASSERT(img_n == 3);
+      for (i=x-1; i >= 0; --i) {
+         dest[i*4+3] = 255;
+         dest[i*4+2] = src[i*3+2];
+         dest[i*4+1] = src[i*3+1];
+         dest[i*4+0] = src[i*3+0];
+      }
+   }
+}
+
 // create the png data from post-deflated data
 static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
 {
-   int bytes = (depth == 16? 2 : 1);
+   int bytes = (depth == 16 ? 2 : 1);
    stbi__context *s = a->s;
    stbi__uint32 i,j,stride = x*out_n*bytes;
    stbi__uint32 img_len, img_width_bytes;
+   stbi_uc *filter_buf;
+   int all_ok = 1;
    int k;
    int img_n = s->img_n; // copy it into a local for later
 
@@ -4664,8 +4712,11 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
    a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
    if (!a->out) return stbi__err("outofmem", "Out of memory");
 
+   // note: error exits here don't need to clean up a->out individually,
+   // stbi__do_png always does on error.
    if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
    img_width_bytes = (((img_n * x * depth) + 7) >> 3);
+   if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG");
    img_len = (img_width_bytes + 1) * y;
 
    // we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
@@ -4673,189 +4724,137 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
    // so just check for raw_len < img_len always.
    if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
 
+   // Allocate two scan lines worth of filter workspace buffer.
+   filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0);
+   if (!filter_buf) return stbi__err("outofmem", "Out of memory");
+
+   // Filtering for low-bit-depth images
+   if (depth < 8) {
+      filter_bytes = 1;
+      width = img_width_bytes;
+   }
+
    for (j=0; j < y; ++j) {
-      stbi_uc *cur = a->out + stride*j;
-      stbi_uc *prior;
+      // cur/prior filter buffers alternate
+      stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes;
+      stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes;
+      stbi_uc *dest = a->out + stride*j;
+      int nk = width * filter_bytes;
       int filter = *raw++;
 
-      if (filter > 4)
-         return stbi__err("invalid filter","Corrupt PNG");
-
-      if (depth < 8) {
-         if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG");
-         cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
-         filter_bytes = 1;
-         width = img_width_bytes;
+      // check filter type
+      if (filter > 4) {
+         all_ok = stbi__err("invalid filter","Corrupt PNG");
+         break;
       }
-      prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above
 
       // if first row, use special filter that doesn't sample previous row
       if (j == 0) filter = first_row_filter[filter];
 
-      // handle first byte explicitly
-      for (k=0; k < filter_bytes; ++k) {
-         switch (filter) {
-            case STBI__F_none       : cur[k] = raw[k]; break;
-            case STBI__F_sub        : cur[k] = raw[k]; break;
-            case STBI__F_up         : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
-            case STBI__F_avg        : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
-            case STBI__F_paeth      : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
-            case STBI__F_avg_first  : cur[k] = raw[k]; break;
-            case STBI__F_paeth_first: cur[k] = raw[k]; break;
-         }
-      }
-
-      if (depth == 8) {
-         if (img_n != out_n)
-            cur[img_n] = 255; // first pixel
-         raw += img_n;
-         cur += out_n;
-         prior += out_n;
-      } else if (depth == 16) {
-         if (img_n != out_n) {
-            cur[filter_bytes]   = 255; // first pixel top byte
-            cur[filter_bytes+1] = 255; // first pixel bottom byte
-         }
-         raw += filter_bytes;
-         cur += output_bytes;
-         prior += output_bytes;
-      } else {
-         raw += 1;
-         cur += 1;
-         prior += 1;
+      // perform actual filtering
+      switch (filter) {
+      case STBI__F_none:
+         memcpy(cur, raw, nk);
+         break;
+      case STBI__F_sub:
+         memcpy(cur, raw, filter_bytes);
+         for (k = filter_bytes; k < nk; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]);
+         break;
+      case STBI__F_up:
+         for (k = 0; k < nk; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
+         break;
+      case STBI__F_avg:
+         for (k = 0; k < filter_bytes; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1));
+         for (k = filter_bytes; k < nk; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1));
+         break;
+      case STBI__F_paeth:
+         for (k = 0; k < filter_bytes; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0)
+         for (k = filter_bytes; k < nk; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes]));
+         break;
+      case STBI__F_avg_first:
+         memcpy(cur, raw, filter_bytes);
+         for (k = filter_bytes; k < nk; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1));
+         break;
       }
 
-      // this is a little gross, so that we don't switch per-pixel or per-component
-      if (depth < 8 || img_n == out_n) {
-         int nk = (width - 1)*filter_bytes;
-         #define STBI__CASE(f) \
-             case f:     \
-                for (k=0; k < nk; ++k)
-         switch (filter) {
-            // "none" filter turns into a memcpy here; make that explicit.
-            case STBI__F_none:         memcpy(cur, raw, nk); break;
-            STBI__CASE(STBI__F_sub)          { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;
-            STBI__CASE(STBI__F_up)           { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
-            STBI__CASE(STBI__F_avg)          { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;
-            STBI__CASE(STBI__F_paeth)        { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;
-            STBI__CASE(STBI__F_avg_first)    { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;
-            STBI__CASE(STBI__F_paeth_first)  { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;
-         }
-         #undef STBI__CASE
-         raw += nk;
-      } else {
-         STBI_ASSERT(img_n+1 == out_n);
-         #define STBI__CASE(f) \
-             case f:     \
-                for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
-                   for (k=0; k < filter_bytes; ++k)
-         switch (filter) {
-            STBI__CASE(STBI__F_none)         { cur[k] = raw[k]; } break;
-            STBI__CASE(STBI__F_sub)          { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;
-            STBI__CASE(STBI__F_up)           { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
-            STBI__CASE(STBI__F_avg)          { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;
-            STBI__CASE(STBI__F_paeth)        { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;
-            STBI__CASE(STBI__F_avg_first)    { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;
-            STBI__CASE(STBI__F_paeth_first)  { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;
-         }
-         #undef STBI__CASE
-
-         // the loop above sets the high byte of the pixels' alpha, but for
-         // 16 bit png files we also need the low byte set. we'll do that here.
-         if (depth == 16) {
-            cur = a->out + stride*j; // start at the beginning of the row again
-            for (i=0; i < x; ++i,cur+=output_bytes) {
-               cur[filter_bytes+1] = 255;
-            }
-         }
-      }
-   }
+      raw += nk;
 
-   // we make a separate pass to expand bits to pixels; for performance,
-   // this could run two scanlines behind the above code, so it won't
-   // intefere with filtering but will still be in the cache.
-   if (depth < 8) {
-      for (j=0; j < y; ++j) {
-         stbi_uc *cur = a->out + stride*j;
-         stbi_uc *in  = a->out + stride*j + x*out_n - img_width_bytes;
-         // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
-         // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
+      // expand decoded bits in cur to dest, also adding an extra alpha channel if desired
+      if (depth < 8) {
          stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
+         stbi_uc *in = cur;
+         stbi_uc *out = dest;
+         stbi_uc inb = 0;
+         stbi__uint32 nsmp = x*img_n;
 
-         // note that the final byte might overshoot and write more data than desired.
-         // we can allocate enough data that this never writes out of memory, but it
-         // could also overwrite the next scanline. can it overwrite non-empty data
-         // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
-         // so we need to explicitly clamp the final ones
-
+         // expand bits to bytes first
          if (depth == 4) {
-            for (k=x*img_n; k >= 2; k-=2, ++in) {
-               *cur++ = scale * ((*in >> 4)       );
-               *cur++ = scale * ((*in     ) & 0x0f);
+            for (i=0; i < nsmp; ++i) {
+               if ((i & 1) == 0) inb = *in++;
+               *out++ = scale * (inb >> 4);
+               inb <<= 4;
             }
-            if (k > 0) *cur++ = scale * ((*in >> 4)       );
          } else if (depth == 2) {
-            for (k=x*img_n; k >= 4; k-=4, ++in) {
-               *cur++ = scale * ((*in >> 6)       );
-               *cur++ = scale * ((*in >> 4) & 0x03);
-               *cur++ = scale * ((*in >> 2) & 0x03);
-               *cur++ = scale * ((*in     ) & 0x03);
+            for (i=0; i < nsmp; ++i) {
+               if ((i & 3) == 0) inb = *in++;
+               *out++ = scale * (inb >> 6);
+               inb <<= 2;
             }
-            if (k > 0) *cur++ = scale * ((*in >> 6)       );
-            if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
-            if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
-         } else if (depth == 1) {
-            for (k=x*img_n; k >= 8; k-=8, ++in) {
-               *cur++ = scale * ((*in >> 7)       );
-               *cur++ = scale * ((*in >> 6) & 0x01);
-               *cur++ = scale * ((*in >> 5) & 0x01);
-               *cur++ = scale * ((*in >> 4) & 0x01);
-               *cur++ = scale * ((*in >> 3) & 0x01);
-               *cur++ = scale * ((*in >> 2) & 0x01);
-               *cur++ = scale * ((*in >> 1) & 0x01);
-               *cur++ = scale * ((*in     ) & 0x01);
+         } else {
+            STBI_ASSERT(depth == 1);
+            for (i=0; i < nsmp; ++i) {
+               if ((i & 7) == 0) inb = *in++;
+               *out++ = scale * (inb >> 7);
+               inb <<= 1;
             }
-            if (k > 0) *cur++ = scale * ((*in >> 7)       );
-            if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
-            if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
-            if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
-            if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
-            if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
-            if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
          }
-         if (img_n != out_n) {
-            int q;
-            // insert alpha = 255
-            cur = a->out + stride*j;
+
+         // insert alpha=255 values if desired
+         if (img_n != out_n)
+            stbi__create_png_alpha_expand8(dest, dest, x, img_n);
+      } else if (depth == 8) {
+         if (img_n == out_n)
+            memcpy(dest, cur, x*img_n);
+         else
+            stbi__create_png_alpha_expand8(dest, cur, x, img_n);
+      } else if (depth == 16) {
+         // convert the image data from big-endian to platform-native
+         stbi__uint16 *dest16 = (stbi__uint16*)dest;
+         stbi__uint32 nsmp = x*img_n;
+
+         if (img_n == out_n) {
+            for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
+               *dest16 = (cur[0] << 8) | cur[1];
+         } else {
+            STBI_ASSERT(img_n+1 == out_n);
             if (img_n == 1) {
-               for (q=x-1; q >= 0; --q) {
-                  cur[q*2+1] = 255;
-                  cur[q*2+0] = cur[q];
+               for (i = 0; i < x; ++i, dest16 += 2, cur += 2) {
+                  dest16[0] = (cur[0] << 8) | cur[1];
+                  dest16[1] = 0xffff;
                }
             } else {
                STBI_ASSERT(img_n == 3);
-               for (q=x-1; q >= 0; --q) {
-                  cur[q*4+3] = 255;
-                  cur[q*4+2] = cur[q*3+2];
-                  cur[q*4+1] = cur[q*3+1];
-                  cur[q*4+0] = cur[q*3+0];
+               for (i = 0; i < x; ++i, dest16 += 4, cur += 6) {
+                  dest16[0] = (cur[0] << 8) | cur[1];
+                  dest16[1] = (cur[2] << 8) | cur[3];
+                  dest16[2] = (cur[4] << 8) | cur[5];
+                  dest16[3] = 0xffff;
                }
             }
          }
       }
-   } else if (depth == 16) {
-      // force the image data from big-endian to platform-native.
-      // this is done in a separate pass due to the decoding relying
-      // on the data being untouched, but could probably be done
-      // per-line during decode if care is taken.
-      stbi_uc *cur = a->out;
-      stbi__uint16 *cur16 = (stbi__uint16*)cur;
-
-      for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
-         *cur16 = (cur[0] << 8) | cur[1];
-      }
    }
 
+   STBI_FREE(filter_buf);
+   if (!all_ok) return 0;
+
    return 1;
 }
 
@@ -5161,9 +5160,11 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
                // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
                if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
                if (z->depth == 16) {
-                  for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
+                  for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning
+                     tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
                } else {
-                  for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
+                  for (k = 0; k < s->img_n && k < 3; ++k)
+                     tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
                }
             }
             break;
@@ -7984,4 +7985,4 @@ AUTHORS 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.
 ------------------------------------------------------------------------------
-*/
+*/

File diff suppressed because it is too large
+ 308 - 224
Engine/source/gfx/bitmap/loaders/stb/stb_image_resize2.h


+ 34 - 0
Engine/source/gfx/gfxAPI.cpp

@@ -180,3 +180,37 @@ ImplementEnumType( GFXBlendOp,
    { GFXBlendOpMax, "GFXBlendOpMax" }
 
 EndImplementEnumType;
+
+ImplementEnumType(GFXShaderConstType,
+   "The shader const types.\n"
+   "@ingroup GFX")
+
+   { GFXSCT_Uknown, "GFXSCT_Uknown" },
+   { GFXSCT_ConstBuffer, "GFXSCT_ConstBuffer" },
+   { GFXSCT_Float, "GFXSCT_Float" },
+   { GFXSCT_Float2, "GFXSCT_Float2" },
+   { GFXSCT_Float3, "GFXSCT_Float3" },
+   { GFXSCT_Float4, "GFXSCT_Float4" },
+   { GFXSCT_Float2x2, "GFXSCT_Float2x2" },
+   { GFXSCT_Float3x3, "GFXSCT_Float3x3" },
+   { GFXSCT_Float3x4, "GFXSCT_Float3x4" },
+   { GFXSCT_Float4x3, "GFXSCT_Float4x3" },
+   { GFXSCT_Float4x4, "GFXSCT_Float4x4" },
+   { GFXSCT_Int, "GFXSCT_Int" },
+   { GFXSCT_Int2, "GFXSCT_Int2" },
+   { GFXSCT_Int3, "GFXSCT_Int3" },
+   { GFXSCT_Int4, "GFXSCT_Int4" },
+   { GFXSCT_UInt, "GFXSCT_UInt" },
+   { GFXSCT_UInt2, "GFXSCT_UInt2" },
+   { GFXSCT_UInt3, "GFXSCT_UInt3" },
+   { GFXSCT_UInt4, "GFXSCT_UInt4" },
+   { GFXSCT_Bool, "GFXSCT_Bool" },
+   { GFXSCT_Bool2, "GFXSCT_Bool2" },
+   { GFXSCT_Bool3, "GFXSCT_Bool3" },
+   { GFXSCT_Bool4, "GFXSCT_Bool4" },
+   { GFXSCT_Sampler, "GFXSCT_Sampler" },
+   { GFXSCT_SamplerCube, "GFXSCT_SamplerCube" },
+   { GFXSCT_SamplerCubeArray, "GFXSCT_SamplerCubeArray" },
+   { GFXSCT_SamplerTextureArray, "GFXSCT_SamplerTextureArray" }
+
+EndImplementEnumType;

+ 2 - 0
Engine/source/gfx/gfxAPI.h

@@ -44,6 +44,7 @@ DefineEnumType( GFXTextureFilterType );
 DefineEnumType( GFXCullMode );
 DefineEnumType( GFXStencilOp );
 DefineEnumType( GFXBlendOp );
+DefineEnumType(GFXShaderConstType);
 DefineEnumType( GFXAdapterType );
 
 DECLARE_STRUCT( GFXVideoMode );
@@ -57,5 +58,6 @@ DefineConsoleType( TypeGFXTextureFilterType, GFXTextureFilterType );
 DefineConsoleType( TypeGFXCullMode, GFXCullMode );
 DefineConsoleType( TypeGFXStencilOp, GFXStencilOp );
 DefineConsoleType( TypeGFXBlendOp, GFXBlendOp );
+DefineConsoleType( TypeGFXShaderConstType, GFXShaderConstType);
 
 #endif // !_GFXAPI_H_

+ 6 - 2
Engine/source/gfx/gfxShader.cpp

@@ -42,8 +42,12 @@ GFXShader::GFXShader()
 
 GFXShader::~GFXShader()
 {
-   Torque::FS::RemoveChangeNotification( mVertexFile, this, &GFXShader::_onFileChanged );
-   Torque::FS::RemoveChangeNotification( mPixelFile, this, &GFXShader::_onFileChanged );
+   if (!mVertexFile.isEmpty())
+      Torque::FS::RemoveChangeNotification( mVertexFile, this, &GFXShader::_onFileChanged );
+   if (!mPixelFile.isEmpty())
+      Torque::FS::RemoveChangeNotification( mPixelFile, this, &GFXShader::_onFileChanged );
+   if (!mGeometryFile.isEmpty())
+      Torque::FS::RemoveChangeNotification(mGeometryFile, this, &GFXShader::_onFileChanged);
 
    SAFE_DELETE(mInstancingFormat);
 }

+ 3 - 1
Engine/source/gfx/gfxShader.h

@@ -77,7 +77,9 @@ enum GFXShaderStage
    GEOMETRY_SHADER   = BIT(2),
    DOMAIN_SHADER     = BIT(3),
    HULL_SHADER       = BIT(4),
-   COMPUTE_SHADER    = BIT(5)
+   COMPUTE_SHADER    = BIT(5),
+   ALL_STAGES = VERTEX_SHADER | PIXEL_SHADER | GEOMETRY_SHADER |
+   DOMAIN_SHADER | HULL_SHADER | COMPUTE_SHADER
 };
 
 /// Instances of this struct are returned GFXShaderConstBuffer

+ 1 - 1
Engine/source/gfx/gfxTarget.h

@@ -178,7 +178,7 @@ public:
    /// @param mipLevel What level of this texture are we rendering to?
    /// @param zOffset  If this is a depth texture, what z level are we 
    ///                 rendering to?
-   virtual void attachTexture(RenderSlot slot, GFXTextureObject *tex, U32 mipLevel=0, U32 zOffset = 0) = 0;
+   virtual void attachTexture(RenderSlot slot, GFXTextureObject *tex, U32 mipLevel=0, U32 zOffset = 0, U32 faceIndex = 0) = 0;
 
    /// Support binding to cubemaps.
    ///

+ 6 - 6
Engine/source/gfx/gfxTextureHandle.cpp

@@ -137,19 +137,19 @@ bool GFXTexHandle::set( DDSFile *dds, GFXTextureProfile *profile, bool deleteDDS
    return isValid();
 }
 
-GFXTexHandle::GFXTexHandle( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels, S32 antialiasLevel)
+GFXTexHandle::GFXTexHandle( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels, S32 antialiasLevel, U32 arraySize)
 {
-   set( width, height, format, profile, desc, numMipLevels, antialiasLevel );
+   set( width, height, format, profile, desc, numMipLevels, antialiasLevel, arraySize );
 }
 
-bool GFXTexHandle::set( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels, S32 antialiasLevel)
+bool GFXTexHandle::set( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels, S32 antialiasLevel, U32 arraySize)
 {
    // Clear the existing texture first, so that
    // its memory is free for the new allocation.
    free();
 
    // Create and set the new texture.
-   StrongObjectRef::set( TEXMGR->createTexture( width, height, format, profile, numMipLevels, antialiasLevel ) );
+   StrongObjectRef::set( TEXMGR->createTexture( width, height, format, profile, numMipLevels, antialiasLevel, arraySize) );
 
    #ifdef TORQUE_DEBUG
       if ( getPointer() )
@@ -159,14 +159,14 @@ bool GFXTexHandle::set( U32 width, U32 height, GFXFormat format, GFXTextureProfi
    return isValid();
 }
 
-bool GFXTexHandle::set(U32 width, U32 height, U32 depth, GFXFormat format, GFXTextureProfile* profile, const String& desc, U32 numMipLevels)
+bool GFXTexHandle::set(U32 width, U32 height, U32 depth, GFXFormat format, GFXTextureProfile* profile, const String& desc, U32 numMipLevels, U32 arraySize)
 {
    // Clear the existing texture first, so that
    // its memory is free for the new allocation.
    free();
 
    // Create and set the new texture.
-   StrongObjectRef::set(TEXMGR->createTexture(width, height, depth, format, profile, numMipLevels));
+   StrongObjectRef::set(TEXMGR->createTexture(width, height, depth, format, profile, numMipLevels, arraySize));
 
    #ifdef TORQUE_DEBUG
       if ( getPointer() )

+ 4 - 3
Engine/source/gfx/gfxTextureHandle.h

@@ -58,9 +58,9 @@ public:
    bool set( DDSFile *bmp, GFXTextureProfile *profile, bool deleteDDS, const String &desc );
 
    // Sized bitmap
-   GFXTexHandle( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels = 1, S32 antialiasLevel = 0);
-   bool set( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels = 1, S32 antialiasLevel = 0);
-   bool set( U32 width, U32 height, U32 depth, GFXFormat format, GFXTextureProfile* profile, const String& desc, U32 numMipLevels = 1);
+   GFXTexHandle( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels = 1, S32 antialiasLevel = 0, U32 arraySize = 1);
+   bool set( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels = 1, S32 antialiasLevel = 0, U32 arraySize = 1);
+   bool set( U32 width, U32 height, U32 depth, GFXFormat format, GFXTextureProfile* profile, const String& desc, U32 numMipLevels = 1, U32 arraySize = 1);
 
    /// Returns the width and height as a point.
    Point2I getWidthHeight() const { return getPointer() ? Point2I( getPointer()->getWidth(), getPointer()->getHeight() ) : Point2I::Zero; }
@@ -68,6 +68,7 @@ public:
    U32 getWidth() const    { return getPointer() ? getPointer()->getWidth()  : 0; }
    U32 getHeight() const   { return getPointer() ? getPointer()->getHeight() : 0; }
    U32 getDepth() const    { return getPointer() ? getPointer()->getDepth()  : 0; }
+   U32 getArraySize() const    { return getPointer() ? getPointer()->getArraySize()  : 0; }
    GFXFormat getFormat() const { return getPointer() ? getPointer()->getFormat() : GFXFormat_COUNT; }
    
    /// Reloads the texture.

+ 7 - 6
Engine/source/gfx/gfxTextureManager.cpp

@@ -361,7 +361,7 @@ GFXTextureObject *GFXTextureManager::_createTexture(  GBitmap *bmp,
       if (  inObj->getWidth() != realWidth ||
             inObj->getHeight() != realHeight ||
             inObj->getFormat() != realFmt )
-         ret = _createTextureObject( realHeight, realWidth, 0, realFmt, profile, numMips, false, 0, inObj );
+         ret = _createTextureObject( realHeight, realWidth, 0, realFmt, profile, numMips, false, 0, 1, inObj );
       else
          ret = inObj;
    }
@@ -569,7 +569,7 @@ GFXTextureObject *GFXTextureManager::_createTexture(  DDSFile *dds,
             inObj->getMipLevels() != numMips )
          ret = _createTextureObject(   dds->getHeight(), dds->getWidth(), 0, 
                                        fmt, profile, numMips, 
-                                       true, 0, inObj );
+                                       true, 0, 1, inObj );
       else
          ret = inObj;
    }
@@ -744,7 +744,7 @@ GFXTextureObject *GFXTextureManager::createTexture(  U32 width, U32 height, void
    return createTexture( bmp, String::EmptyString, profile, true );
 }
 
-GFXTextureObject *GFXTextureManager::createTexture( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, U32 numMipLevels, S32 antialiasLevel )
+GFXTextureObject *GFXTextureManager::createTexture( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, U32 numMipLevels, S32 antialiasLevel, U32 arraySize)
 {
    // Deal with sizing issues...
    U32 localWidth = width;
@@ -783,7 +783,7 @@ GFXTextureObject *GFXTextureManager::createTexture( U32 width, U32 height, GFXFo
    // Create the texture if we didn't get one from the pool.
    if ( !outTex )
    {
-      outTex = _createTextureObject( localHeight, localWidth, 0, format, profile, numMips, false, antialiasLevel );
+      outTex = _createTextureObject( localHeight, localWidth, 0, format, profile, numMips, false, antialiasLevel, arraySize );
 
       // Make sure we add it to the pool.
       if ( outTex && profile->isPooled() )
@@ -814,12 +814,13 @@ GFXTextureObject *GFXTextureManager::createTexture(   U32 width,
                                                       U32 depth,
                                                       GFXFormat format,
                                                       GFXTextureProfile *profile,
-                                                      U32 numMipLevels)
+                                                      U32 numMipLevels,
+                                                      U32 arraySize)
 {
    PROFILE_SCOPE( GFXTextureManager_CreateTexture_3D );
 
    // Create texture...
-   GFXTextureObject *ret = _createTextureObject( height, width, depth, format, profile, numMipLevels );
+   GFXTextureObject *ret = _createTextureObject( height, width, depth, format, profile, numMipLevels, arraySize );
 
    if(!ret)
    {

+ 6 - 3
Engine/source/gfx/gfxTextureManager.h

@@ -120,14 +120,16 @@ public:
       U32 depth,
       GFXFormat format,
       GFXTextureProfile *profile,
-      U32 numMipLevels = 1);
+      U32 numMipLevels = 1,
+      U32 arraySize = 1);
 
    virtual GFXTextureObject *createTexture(  U32 width,
       U32 height,
       GFXFormat format,
       GFXTextureProfile *profile,
       U32 numMipLevels,
-      S32 antialiasLevel);
+      S32 antialiasLevel,
+      U32 arraySize = 1);
 
    Torque::Path validatePath(const Torque::Path &path);
    GBitmap *loadUncompressedTexture(const Torque::Path& path, GFXTextureProfile* profile, U32 width, U32 height, bool genMips = false);
@@ -319,7 +321,8 @@ protected:
                                                    GFXTextureProfile *profile, 
                                                    U32 numMipLevels, 
                                                    bool forceMips = false, 
-                                                   S32 antialiasLevel = 0, 
+                                                   S32 antialiasLevel = 0,
+                                                   U32 arraySize = 1,
                                                    GFXTextureObject *inTex = NULL ) = 0;
 
    /// Load a texture from a proper DDSFile instance.

+ 2 - 0
Engine/source/gfx/gfxTextureObject.cpp

@@ -99,6 +99,8 @@ GFXTextureObject::GFXTextureObject(GFXDevice *aDevice, GFXTextureProfile *aProfi
 
    mHasTransparency = false;
 
+   mArraySize = 1;
+
 #if defined(TORQUE_DEBUG)
    // Active object tracking.
    smActiveTOCount++;

+ 12 - 2
Engine/source/gfx/gfxTextureObject.h

@@ -45,6 +45,7 @@ class GFXTextureProfile;
 class GBitmap;
 struct DDSFile;
 class RectI;
+class GFXTexHandle;
 
 /// Contains information on a locked region of a texture.
 ///
@@ -92,6 +93,8 @@ public:
 
    bool mDead;
 
+   U32 mArraySize;
+
    /// The device this texture belongs to.
    GFXDevice *mDevice;   
 
@@ -150,10 +153,13 @@ public:
    U32 getBitmapHeight() const { return mBitmapSize.y; }
    U32 getBitmapDepth() const { return mBitmapSize.z; }
    GFXFormat getFormat() const { return mFormat; }
+   U32 getArraySize() const { return mArraySize; }
 
    /// Returns true if this texture is a render target.
    bool isRenderTarget() const { return mProfile->isRenderTarget(); }
 
+   bool isCubeMap() const { return mProfile->isCubeMap(); }
+
    /// Returns the file path to the texture if
    /// it was loaded from disk.
    const String& getPath() const { return mPath; }
@@ -167,11 +173,11 @@ public:
 
    /// Acquire a lock on part of the texture. The GFXLockedRect returned
    /// is managed by the GFXTextureObject and does not need to be freed.
-   virtual GFXLockedRect * lock( U32 mipLevel = 0, RectI *inRect = NULL ) = 0;
+   virtual GFXLockedRect * lock( U32 mipLevel = 0, RectI *inRect = NULL, U32 faceIndex = 0) = 0;
 
    /// Releases a lock previously acquired. Note that the mipLevel parameter
    /// must match the corresponding lock!
-   virtual void unlock( U32 mipLevel = 0) = 0;
+   virtual void unlock( U32 mipLevel = 0, U32 faceIndex = 0) = 0;
 
    // copy the texture data into the specified bitmap.  
    //   - this texture object must be a render target.  the function will assert if this is not the case.
@@ -182,6 +188,10 @@ public:
    //   - this process is not fast.
    virtual bool copyToBmp(GBitmap* bmp) = 0;
 
+   virtual void updateTextureSlot(const GFXTexHandle& texHandle, const U32 slot, const S32 face = -1) = 0;
+   virtual void copyTo(GFXTextureObject* dstTex) = 0;
+   virtual void generateMipMaps() = 0;
+
    #ifdef TORQUE_DEBUG
 
    // It is important for any derived objects to define this method

+ 15 - 0
Engine/source/gfx/gfxTextureProfile.cpp

@@ -83,6 +83,21 @@ GFX_ImplementTextureProfile(GFXDynamicTextureSRGBProfile,
                             GFXTextureProfile::DiffuseMap,
                             GFXTextureProfile::Dynamic | GFXTextureProfile::SRGB,
                             GFXTextureProfile::NONE);
+GFX_ImplementTextureProfile(GFXDynamicCubemapTextureProfile,
+                           GFXTextureProfile::DiffuseMap,
+                           GFXTextureProfile::Dynamic | GFXTextureProfile::CubeMap,
+                           GFXTextureProfile::NONE);
+GFX_ImplementTextureProfile(GFXCubemapRenderTargetProfile,
+                           GFXTextureProfile::DiffuseMap,
+                           GFXTextureProfile::PreserveSize | GFXTextureProfile::RenderTarget | GFXTextureProfile::CubeMap,
+                           GFXTextureProfile::NONE);
+GFX_ImplementTextureProfile(GFXCubemapStaticTextureProfile, GFXTextureProfile::DiffuseMap,
+                           GFXTextureProfile::Static | GFXTextureProfile::CubeMap,
+                           GFXTextureProfile::NONE);
+GFX_ImplementTextureProfile(GFXCubemapTexturePersistentProfile,
+                           GFXTextureProfile::DiffuseMap,
+                           GFXTextureProfile::PreserveSize | GFXTextureProfile::Static | GFXTextureProfile::KeepBitmap | GFXTextureProfile::CubeMap,
+                           GFXTextureProfile::NONE);
 
 //-----------------------------------------------------------------------------
 

+ 16 - 6
Engine/source/gfx/gfxTextureProfile.h

@@ -88,7 +88,7 @@ public:
       KeepBitmap     = BIT(7),  ///< Always keep a copy of this texture's bitmap. (Potentially in addition to the API managed copy?)
       ZTarget        = BIT(8),  ///< This texture will be used as a Z target.
       SRGB           = BIT(9),  ///< sRGB texture
-
+      CubeMap        = BIT(10), ///< Cubemap texture
       /// Track and pool textures of this type for reuse.
       ///
       /// You should use this profile flag sparingly.  Odd
@@ -96,16 +96,15 @@ public:
       /// the pool to contain unused textures which will remain
       /// in memory until a flush occurs.
       ///
-      Pooled = BIT(10), 
+      Pooled = BIT(11), 
 
       /// A hint that the device is not allowed to discard the content
       /// of a target texture after presentation or deactivated.
       ///
       /// This is mainly a depth buffer optimization.
-      NoDiscard = BIT(11),
+      NoDiscard = BIT(12),
 
-      
-      NoModify = BIT(11)
+      NoModify = BIT(12)
 
    };
 
@@ -173,6 +172,7 @@ public:
    inline bool isPooled() const { return testFlag(Pooled); }
    inline bool canDiscard() const { return !testFlag(NoDiscard); }
    inline bool isSRGB() const { return testFlag(SRGB); }
+   inline bool isCubeMap() const { return testFlag(CubeMap); }
    //compare profile flags for equality
    inline bool compareFlags(const GFXTextureProfile& in_Cmp) const{ return (mProfile == in_Cmp.mProfile); }
 private:
@@ -209,7 +209,7 @@ private:
 #define GFX_DeclareTextureProfile(name)  extern GFXTextureProfile name
 #define GFX_ImplementTextureProfile(name, type,  flags, compression) GFXTextureProfile name(#name, type, flags, compression)
 
-// Default Texture profiles
+// Default 2D Texture profiles
 // Texture we can render to.
 GFX_DeclareTextureProfile(GFXRenderTargetProfile);
 GFX_DeclareTextureProfile(GFXRenderTargetSRGBProfile);
@@ -231,4 +231,14 @@ GFX_DeclareTextureProfile(GFXZTargetProfile);
 GFX_DeclareTextureProfile(GFXDynamicTextureProfile);
 GFX_DeclareTextureProfile(GFXDynamicTextureSRGBProfile);
 
+// Default Cubemap Texture profiles
+// Dynamic Texure
+GFX_DeclareTextureProfile(GFXDynamicCubemapTextureProfile);
+// Texture we can render to.
+GFX_DeclareTextureProfile(GFXCubemapRenderTargetProfile);
+// Standard static diffuse textures
+GFX_DeclareTextureProfile(GFXCubemapStaticTextureProfile);
+// Standard static diffuse textures that are persistent in memory
+GFX_DeclareTextureProfile(GFXCubemapTexturePersistentProfile);
+
 #endif

+ 280 - 101
Engine/source/gfx/gl/gfxGLTextureManager.cpp

@@ -55,6 +55,7 @@ GFXTextureObject *GFXGLTextureManager::_createTextureObject(   U32 height,
                                                                U32 numMipLevels,
                                                                bool forceMips,
                                                                S32 antialiasLevel,
+                                                               U32 arraySize,   
                                                                GFXTextureObject *inTex )
 {
    AssertFatal(format >= 0 && format < GFXFormat_COUNT, "GFXGLTextureManager::_createTexture - invalid format!");
@@ -73,7 +74,7 @@ GFXTextureObject *GFXGLTextureManager::_createTextureObject(   U32 height,
       retTex->registerResourceWithDevice( GFX );
    }
 
-   innerCreateTexture(retTex, height, width, depth, format, profile, numMipLevels, forceMips);
+   innerCreateTexture(retTex, height, width, depth, format, profile, numMipLevels, forceMips, arraySize);
 
    return retTex;
 }
@@ -89,19 +90,40 @@ void GFXGLTextureManager::innerCreateTexture( GFXGLTextureObject *retTex,
                                                GFXFormat format, 
                                                GFXTextureProfile *profile, 
                                                U32 numMipLevels,
-                                               bool forceMips)
+                                               bool forceMips,
+                                               U32 arraySize)
 {
    // No 24 bit formats.  They trigger various oddities because hardware (and Apple's drivers apparently...) don't natively support them.
    if (format == GFXFormatR8G8B8)
       format = GFXFormatR8G8B8A8;
    else if (format == GFXFormatR8G8B8_SRGB)
       format = GFXFormatR8G8B8A8_SRGB;
-      
+
+   retTex->mProfile = profile;
    retTex->mFormat = format;
    retTex->mIsZombie = false;
    retTex->mIsNPoT2 = false;
    
-   GLenum binding = ( (height == 1 || width == 1) && ( height != width ) ) ? GL_TEXTURE_1D : ( (depth == 0) ? GL_TEXTURE_2D : GL_TEXTURE_3D );
+   const bool isCube = profile->isCubeMap();
+   GLenum binding;
+
+   if (isCube)
+   {
+      binding = (arraySize > 1) ? GL_TEXTURE_CUBE_MAP_ARRAY : GL_TEXTURE_CUBE_MAP;
+   }
+   else
+   {
+      const bool is3D = (depth > 1);
+      const bool is1D = (height == 1 && width > 1);
+
+      if (is3D)
+         binding = GL_TEXTURE_3D;
+      else if (is1D)
+         binding = (arraySize > 1) ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D;
+      else
+         binding = (arraySize > 1) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
+   }
+
    if((profile->testFlag(GFXTextureProfile::RenderTarget) || profile->testFlag(GFXTextureProfile::ZTarget)) && (!isPow2(width) || !isPow2(height)) && !depth)
       retTex->mIsNPoT2 = true;
    retTex->mBinding = binding;
@@ -155,55 +177,155 @@ void GFXGLTextureManager::innerCreateTexture( GFXGLTextureObject *retTex,
       retTex->mMipLevels = getMaxMipmaps(width, height, 1);
 
     glTexParameteri(binding, GL_TEXTURE_MAX_LEVEL, retTex->mMipLevels-1 );
-    
-    if( GFXGL->mCapabilities.textureStorage )
+
+    bool hasTexStorage = false;
+    // not supported when creating these.
+    if (arraySize > 1 || isCube || profile->isDynamic())
+       hasTexStorage = false;
+
+    const bool isCompressed = ImageUtil::isCompressedFormat(format);
+
+    // --- Allocation by binding ---
+    if (binding == GL_TEXTURE_CUBE_MAP)
     {
-        if(binding == GL_TEXTURE_2D)
-            glTexStorage2D( retTex->getBinding(), retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height );
-        else if(binding == GL_TEXTURE_1D)
-            glTexStorage1D( retTex->getBinding(), retTex->mMipLevels, GFXGLTextureInternalFormat[format], getMax(width, height) );
-        else
-            glTexStorage3D( retTex->getBinding(), retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height, depth );
+       // Single cubemap: prefer glTexStorage2D if available, else per-face texImage2D
+       if (hasTexStorage)
+       {
+          // Some drivers accept texStorage2D with GL_TEXTURE_CUBE_MAP
+          glTexStorage2D(GL_TEXTURE_CUBE_MAP, retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height);
+       }
+       else
+       {
+          // Explicitly allocate each face/level
+          for (U32 face = 0; face < 6; ++face)
+          {
+             for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
+             {
+                U32 mipW = getMax(1u, width >> mip);
+                U32 mipH = getMax(1u, height >> mip);
+
+                if (isCompressed)
+                {
+                   U32 size = getCompressedSurfaceSize(format, width, height, mip);
+                   glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mip, GFXGLTextureInternalFormat[format], mipW, mipH, 0, size, nullptr);
+                }
+                else
+                {
+                   glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mip, GFXGLTextureInternalFormat[format], mipW, mipH, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], nullptr);
+                }
+             }
+          }
+       }
     }
-    else
+    else if (binding == GL_TEXTURE_CUBE_MAP_ARRAY)
     {
-        //If it wasn't for problems on amd drivers this next part could be really simplified and we wouldn't need to go through manually creating our
-        //mipmap pyramid and instead just use glGenerateMipmap
-        if(ImageUtil::isCompressedFormat(format))
-        {
-            AssertFatal(binding == GL_TEXTURE_2D, 
-            "GFXGLTextureManager::innerCreateTexture - Only compressed 2D textures are supported");
-
-            U32 tempWidth = width;
-            U32 tempHeight = height;
-            U32 size = getCompressedSurfaceSize(format,height,width);
-            //Fill compressed images with 0's
-            U8 *pTemp = (U8*)dMalloc(sizeof(U8)*size);
-            dMemset(pTemp,0,size);
-     
-            for(U32 i=0;i< retTex->mMipLevels;i++)
-            {
-                tempWidth = getMax( U32(1), width >> i );
-                tempHeight = getMax( U32(1), height >> i );
-                size = getCompressedSurfaceSize(format,width,height,i);
-                glCompressedTexImage2D(binding,i,GFXGLTextureInternalFormat[format],tempWidth,tempHeight,0,size,pTemp);
-            }
-
-            dFree(pTemp);
-        }
-        else
-        {   
-            if(binding == GL_TEXTURE_2D)
-                glTexImage2D(binding, 0, GFXGLTextureInternalFormat[format], width, height, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
-            else if(binding == GL_TEXTURE_1D)
-                glTexImage1D(binding, 0, GFXGLTextureInternalFormat[format], (width > 1 ? width : height), 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
-            else
-                glTexImage3D(GL_TEXTURE_3D, 0, GFXGLTextureInternalFormat[format], width, height, depth, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
-
-            if(retTex->mMipLevels > 1)
-                glGenerateMipmap(binding);
-        }
+       // cube-map array: layers = arraySize * 6
+       U32 layers = getMax(1u, arraySize) * 6u;
+       if (hasTexStorage)
+       {
+          glTexStorage3D(GL_TEXTURE_CUBE_MAP_ARRAY, retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height, layers);
+       }
+       else
+       {
+          // fallback to glTexImage3D with NULL data
+          for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
+          {
+             U32 mipW = getMax(1u, width >> mip);
+             U32 mipH = getMax(1u, height >> mip);
+             glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, GFXGLTextureInternalFormat[format], mipW, mipH, layers, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
+          }
+       }
     }
+    else if (binding == GL_TEXTURE_2D_ARRAY)
+    {
+       // 2D texture array: depth = arraySize (layers)
+       U32 layers = getMax(1u, arraySize);
+       if (hasTexStorage)
+       {
+          glTexStorage3D(GL_TEXTURE_2D_ARRAY, retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height, layers);
+       }
+       else
+       {
+          for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
+          {
+             U32 mipW = getMax(1u, width >> mip);
+             U32 mipH = getMax(1u, height >> mip);
+             glTexImage3D(GL_TEXTURE_2D_ARRAY, mip, GFXGLTextureInternalFormat[format], mipW, mipH, layers, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
+          }
+       }
+    }
+    else if (binding == GL_TEXTURE_1D_ARRAY)
+    {
+       // 1D array stored as GL_TEXTURE_1D_ARRAY. glTexStorage2D can be used for 1D arrays with height=layers on many drivers.
+       U32 layers = getMax(1u, arraySize);
+       if (hasTexStorage)
+       {
+          // glTexStorage2D works for GL_TEXTURE_1D_ARRAY (width, layers)
+          glTexStorage2D(GL_TEXTURE_1D_ARRAY, retTex->mMipLevels, GFXGLTextureInternalFormat[format], getMax(width, height), layers);
+       }
+       else
+       {
+          // fallback: allocate as 2D where the "height" dimension is layers via glTexImage2D? Not ideal.
+          // Safer: use glTexImage2D with target GL_TEXTURE_1D_ARRAY is invalid; instead use glTexImage3D with depth=layers
+          for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
+          {
+             U32 mipW = getMax(1u, getMax(width, height) >> mip);
+             glTexImage3D(GL_TEXTURE_1D_ARRAY, mip, GFXGLTextureInternalFormat[format], mipW, layers, 1, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
+          }
+       }
+    }
+    else if (binding == GL_TEXTURE_1D)
+    {
+       if (hasTexStorage)
+          glTexStorage1D(GL_TEXTURE_1D, retTex->mMipLevels, GFXGLTextureInternalFormat[format], getMax(width, height));
+       else
+       {
+          for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
+          {
+             U32 mipW = getMax(1u, getMax(width, height) >> mip);
+             glTexImage1D(GL_TEXTURE_1D, mip, GFXGLTextureInternalFormat[format], mipW, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
+          }
+       }
+    }
+    else if (binding == GL_TEXTURE_3D)
+    {
+       if (hasTexStorage)
+          glTexStorage3D(GL_TEXTURE_3D, retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height, depth);
+       else
+       {
+          for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
+          {
+             U32 mipW = getMax(1u, width >> mip);
+             U32 mipH = getMax(1u, height >> mip);
+             U32 mipD = getMax(1u, depth >> mip);
+             glTexImage3D(GL_TEXTURE_3D, mip, GFXGLTextureInternalFormat[format], mipW, mipH, mipD, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
+          }
+       }
+    }
+    else // GL_TEXTURE_2D (default)
+    {
+       if (hasTexStorage)
+          glTexStorage2D(GL_TEXTURE_2D, retTex->mMipLevels, GFXGLTextureInternalFormat[format], width, height);
+       else
+       {
+          for (U32 mip = 0; mip < retTex->mMipLevels; ++mip)
+          {
+             U32 mipW = getMax(1u, width >> mip);
+             U32 mipH = getMax(1u, height >> mip);
+
+             if (isCompressed)
+             {
+                U32 size = getCompressedSurfaceSize(format, width, height, mip);
+                glCompressedTexImage2D(GL_TEXTURE_2D, mip, GFXGLTextureInternalFormat[format], mipW, mipH, 0, size, nullptr);
+             }
+             else
+             {
+                glTexImage2D(GL_TEXTURE_2D, mip, GFXGLTextureInternalFormat[format], mipW, mipH, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL);
+             }
+          }
+       }
+    }
+
    
    // Complete the texture
    // Complete the texture - this does get changed later but we need to complete the texture anyway
@@ -221,14 +343,20 @@ void GFXGLTextureManager::innerCreateTexture( GFXGLTextureObject *retTex,
    if(GFXGLTextureSwizzle[format])         
       glTexParameteriv(binding, GL_TEXTURE_SWIZZLE_RGBA, GFXGLTextureSwizzle[format]);   
    
-   // Get the size from GL (you never know...)
-   GLint texHeight, texWidth, texDepth = 0;
-   
-   glGetTexLevelParameteriv(binding, 0, GL_TEXTURE_WIDTH, &texWidth);
-   glGetTexLevelParameteriv(binding, 0, GL_TEXTURE_HEIGHT, &texHeight);
-   if(binding == GL_TEXTURE_3D)
-      glGetTexLevelParameteriv(binding, 0, GL_TEXTURE_DEPTH, &texDepth);
-   
+   GLint texHeight = 0, texWidth = 0, texDepth = 0;
+
+   GLenum queryTarget = binding;
+   if (binding == GL_TEXTURE_CUBE_MAP)
+   {
+      // Query a specific face, e.g. +X
+      queryTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
+   }
+
+   glGetTexLevelParameteriv(queryTarget, 0, GL_TEXTURE_WIDTH, &texWidth);
+   glGetTexLevelParameteriv(queryTarget, 0, GL_TEXTURE_HEIGHT, &texHeight);
+   if (binding == GL_TEXTURE_3D)
+      glGetTexLevelParameteriv(GL_TEXTURE_3D, 0, GL_TEXTURE_DEPTH, &texDepth);
+
    retTex->mTextureSize.set(texWidth, texHeight, texDepth);
 }
 
@@ -236,7 +364,7 @@ void GFXGLTextureManager::innerCreateTexture( GFXGLTextureObject *retTex,
 // loadTexture - GBitmap
 //-----------------------------------------------------------------------------
 
-static void _textureUpload(const S32 width, const S32 height,const S32 bytesPerPixel,const GFXGLTextureObject* texture, const GFXFormat fmt, const U8* data,const S32 mip=0, Swizzle<U8, 4> *pSwizzle = NULL)
+static void _textureUpload(const S32 width, const S32 height,const S32 bytesPerPixel,const GFXGLTextureObject* texture, const GFXFormat fmt, const U8* data,const S32 mip=0, const U32 face = 0, Swizzle<U8, 4> *pSwizzle = NULL)
 {
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->getBuffer());
    U32 bufSize = width * height * bytesPerPixel;
@@ -256,7 +384,9 @@ static void _textureUpload(const S32 width, const S32 height,const S32 bytesPerP
       glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, bufSize, data);
    }
 
-   if (texture->getBinding() == GL_TEXTURE_2D)
+   if(texture->getBinding() == GL_TEXTURE_CUBE_MAP)
+      glTexSubImage2D(GFXGLFaceType[face], mip, 0, 0, width, height, GFXGLTextureFormat[fmt], GFXGLTextureType[fmt], NULL);
+   else if (texture->getBinding() == GL_TEXTURE_2D)
       glTexSubImage2D(texture->getBinding(), mip, 0, 0, width, height, GFXGLTextureFormat[fmt], GFXGLTextureType[fmt], NULL);
    else
       glTexSubImage1D(texture->getBinding(), mip, 0, (width > 1 ? width : height), GFXGLTextureFormat[fmt], GFXGLTextureType[fmt], NULL);
@@ -266,76 +396,125 @@ static void _textureUpload(const S32 width, const S32 height,const S32 bytesPerP
 
 bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL)
 {
-   PROFILE_SCOPE(GFXGLTextureManager_loadTexture);
+   PROFILE_SCOPE(GFXGLTextureManager_loadTextureGBitmap);
    GFXGLTextureObject *texture = static_cast<GFXGLTextureObject*>(aTexture);
-   
-   AssertFatal(texture->getBinding() == GL_TEXTURE_1D || texture->getBinding() == GL_TEXTURE_2D, 
-      "GFXGLTextureManager::_loadTexture(GBitmap) - This method can only be used with 1D/2D textures");
+
+   const GLenum target = texture->getBinding();
+
+   AssertFatal(target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP,
+      "GFXGLTextureManager::_loadTexture(GBitmap) - This method can only be used with 1D/2D and CubeMap textures");
       
    if(texture->getBinding() == GL_TEXTURE_3D)
       return false;
-         
-   // No 24bit formats.
-   if(pDL->getFormat() == GFXFormatR8G8B8)
-      pDL->setFormat(GFXFormatR8G8B8A8);
-   else if (pDL->getFormat() == GFXFormatR8G8B8_SRGB)
-      pDL->setFormat(GFXFormatR8G8B8A8_SRGB);
+   //      
+   //// No 24bit formats.
+   //if(pDL->getFormat() == GFXFormatR8G8B8)
+   //   pDL->setFormat(GFXFormatR8G8B8A8);
+   //else if (pDL->getFormat() == GFXFormatR8G8B8_SRGB)
+   //   pDL->setFormat(GFXFormatR8G8B8A8_SRGB);
+
    // Bind to edit
    PRESERVE_TEXTURE(texture->getBinding());
    glBindTexture(texture->getBinding(), texture->getHandle());
 
-  _textureUpload(pDL->getWidth(),pDL->getHeight(),pDL->getBytesPerPixel(),texture,pDL->getFormat(), pDL->getBits(), 0);
+   const U32 mipLevels = texture->getMipLevels();
+   const bool isCubemap = (target == GL_TEXTURE_CUBE_MAP) && pDL->getNumFaces() > 1;
+   U32 faceCount = isCubemap ? 6 : 1;
 
-  if(!ImageUtil::isCompressedFormat(pDL->getFormat()))
-   glGenerateMipmap(texture->getBinding());
-   
+   for (U32 mip = 0; mip < mipLevels; mip++)
+   {
+      const GLsizei width = getMax(1u, pDL->getWidth(mip));
+      const GLsizei height = getMax(1u, pDL->getHeight(mip));
+      for (U32 face = 0; face < faceCount; ++face)
+      {
+         _textureUpload(width, height, pDL->getBytesPerPixel(), texture, pDL->getFormat(), pDL->getBits(mip,face), mip, face);
+      }
+   }
+
+   if(!ImageUtil::isCompressedFormat(pDL->getFormat()))
+      glGenerateMipmap(texture->getBinding());
+
+   glBindTexture(target, 0);
    return true;
 }
 
 bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds)
 {
+	PROFILE_SCOPE(GFXGLTextureManager_loadTextureDDS);
    GFXGLTextureObject* texture = static_cast<GFXGLTextureObject*>(aTexture);
-   
-   AssertFatal(texture->getBinding() == GL_TEXTURE_2D, 
-      "GFXGLTextureManager::_loadTexture(DDSFile) - This method can only be used with 2D textures");
-      
-   if(texture->getBinding() != GL_TEXTURE_2D)
-      return false;
-   
-   PRESERVE_TEXTURE(texture->getBinding());
-   glBindTexture(texture->getBinding(), texture->getHandle());
-   U32 numMips = dds->mSurfaces[0]->mMips.size();
+
+   const GLenum target = texture->getBinding();
+
+   const bool isCube = texture->getBinding() == GL_TEXTURE_CUBE_MAP && dds->isCubemap();
+   const bool isCompressed = ImageUtil::isCompressedFormat(texture->mFormat);
+
+   AssertFatal(target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP,
+	   "GFXGLTextureManager::_loadTexture(DDS) - This method can only be used with 1D/2D and CubeMap textures");
+
+   if (texture->getBinding() == GL_TEXTURE_3D)
+	   return false;
+
+   PRESERVE_TEXTURE(target);
+   glBindTexture(target, texture->getHandle());
+
+   const U32 numFaces = isCube ? 6 : 1;
+   const U32 numMips = dds->mSurfaces[0]->mMips.size();
    const GFXFormat fmt = texture->mFormat;
 
-   for(U32 i = 0; i < numMips; i++)
+   for (U32 face = 0; face < numFaces; ++face)
    {
-      PROFILE_SCOPE(GFXGLTexMan_loadSurface);
+      // Skip empty surfaces
+      if (!dds->mSurfaces[face])
+         continue;
 
-      if(ImageUtil::isCompressedFormat(texture->mFormat))
+      for (U32 mip = 0; mip < numMips; ++mip)
       {
-         if((!isPow2(dds->getWidth()) || !isPow2(dds->getHeight())) && GFX->getCardProfiler()->queryProfile("GL::Workaround::noCompressedNPoTTextures"))
+         const U32 mipWidth = getMax(1u, dds->getWidth(mip));
+         const U32 mipHeight = getMax(1u, dds->getHeight(mip));
+
+			GLenum uploadTarget = target;
+         if (isCube)
+            uploadTarget = GFXGLFaceType[face];
+
+         if (isCompressed)
          {
-            U8* uncompressedTex = new U8[dds->getWidth(i) * dds->getHeight(i) * 4];
-            ImageUtil::decompress(dds->mSurfaces[0]->mMips[i],uncompressedTex, dds->getWidth(i), dds->getHeight(i), fmt);
-            glTexSubImage2D(texture->getBinding(), i, 0, 0, dds->getWidth(i), dds->getHeight(i), GL_RGBA, GL_UNSIGNED_BYTE, uncompressedTex);
-            delete[] uncompressedTex;
+            // Handle NPOT workaround
+            if ((!isPow2(mipWidth) || !isPow2(mipHeight)) && GFX->getCardProfiler()->queryProfile("GL::Workaround::noCompressedNPoTTextures"))
+            {
+               U8* uncompressedTex = new U8[mipWidth * mipHeight * 4];
+               ImageUtil::decompress(dds->mSurfaces[face]->mMips[mip], uncompressedTex, mipWidth, mipHeight, fmt);
+               glTexSubImage2D(uploadTarget,
+                  mip, 0, 0, mipWidth, mipHeight, GL_RGBA, GL_UNSIGNED_BYTE, uncompressedTex
+               );
+               delete[] uncompressedTex;
+            }
+            else
+            {
+               glCompressedTexImage2D(uploadTarget,
+                  mip, GFXGLTextureInternalFormat[fmt], mipWidth, mipHeight, 0,
+                  dds->getSurfaceSize(mip), dds->mSurfaces[face]->mMips[mip]
+               );
+            }
          }
          else
-            glCompressedTexSubImage2D(texture->getBinding(), i, 0, 0, dds->getWidth(i), dds->getHeight(i), GFXGLTextureInternalFormat[fmt], dds->getSurfaceSize(dds->getHeight(), dds->getWidth(), i), dds->mSurfaces[0]->mMips[i]);
-      }
-      else
-      {
-         Swizzle<U8, 4> *pSwizzle = NULL;
-         if (fmt == GFXFormatR8G8B8A8 || fmt == GFXFormatR8G8B8X8 || fmt == GFXFormatR8G8B8A8_SRGB || fmt == GFXFormatR8G8B8A8_LINEAR_FORCE || fmt == GFXFormatB8G8R8A8)
-            pSwizzle = &Swizzles::bgra;
+         {
+            Swizzle<U8, 4>* pSwizzle = nullptr;
+            if (fmt == GFXFormatR8G8B8A8 || fmt == GFXFormatR8G8B8X8 || fmt == GFXFormatR8G8B8A8_SRGB ||
+               fmt == GFXFormatR8G8B8A8_LINEAR_FORCE || fmt == GFXFormatB8G8R8A8)
+               pSwizzle = &Swizzles::bgra;
+
+            _textureUpload(
+               mipWidth, mipHeight, dds->mBytesPerPixel, texture, fmt,
+               dds->mSurfaces[face]->mMips[mip], mip, face, pSwizzle);
+         }
 
-         _textureUpload(dds->getWidth(i), dds->getHeight(i),dds->mBytesPerPixel, texture, fmt, dds->mSurfaces[0]->mMips[i],i, pSwizzle);
       }
    }
 
-   if(numMips !=1 && !ImageUtil::isCompressedFormat(texture->mFormat))
+   if (numMips != 1 && !isCompressed)
       glGenerateMipmap(texture->getBinding());
-   
+
+   glBindTexture(target, 0);
    return true;
 }
 

+ 3 - 2
Engine/source/gfx/gl/gfxGLTextureManager.h

@@ -45,6 +45,7 @@ protected:
                                              U32 numMipLevels,
                                              bool forceMips = false,
                                              S32 antialiasLevel = 0,
+                                             U32 arraySize = 1,
                                              GFXTextureObject *inTex = NULL ) override;
    bool _loadTexture(GFXTextureObject *texture, DDSFile *dds) override;
    bool _loadTexture(GFXTextureObject *texture, GBitmap *bmp) override;
@@ -56,7 +57,7 @@ private:
    friend class GFXGLTextureObject;
    
    /// Creates internal GL texture
-   void innerCreateTexture(GFXGLTextureObject *obj, U32 height, U32 width, U32 depth, GFXFormat format, GFXTextureProfile *profile, U32 numMipLevels, bool forceMips = false);
+   void innerCreateTexture(GFXGLTextureObject *obj, U32 height, U32 width, U32 depth, GFXFormat format, GFXTextureProfile *profile, U32 numMipLevels, bool forceMips = false, U32 arraySize = 1);
 };
 
-#endif
+#endif

+ 215 - 22
Engine/source/gfx/gl/gfxGLTextureObject.cpp

@@ -64,7 +64,7 @@ GFXGLTextureObject::~GFXGLTextureObject()
    kill();
 }
 
-GFXLockedRect* GFXGLTextureObject::lock(U32 mipLevel, RectI *inRect)
+GFXLockedRect* GFXGLTextureObject::lock(U32 mipLevel /*= 0*/, RectI* inRect /*= NULL*/, U32 faceIndex /*= 0*/)
 {
    //AssertFatal(mBinding != GL_TEXTURE_3D, "GFXGLTextureObject::lock - We don't support locking 3D textures yet");
    U32 width = mTextureSize.x >> mipLevel;
@@ -100,7 +100,7 @@ GFXLockedRect* GFXGLTextureObject::lock(U32 mipLevel, RectI *inRect)
    return &mLockedRect;
 }
 
-void GFXGLTextureObject::unlock(U32 mipLevel)
+void GFXGLTextureObject::unlock(U32 mipLevel /*= 0*/, U32 faceIndex /*= 0*/)
 {
    if(!mLockedRect.bits)
       return;
@@ -175,38 +175,231 @@ bool GFXGLTextureObject::copyToBmp(GBitmap * bmp)
 
    FrameAllocatorMarker mem;
 
+   const bool isCubemap = (mBinding == GL_TEXTURE_CUBE_MAP);
+   const U32 numFaces = isCubemap ? 6 : 1;
 
-   U32 mipLevels = getMipLevels();
-   for (U32 mip = 0; mip < mipLevels; mip++)
+   for (U32 mip = 0; mip < getMipLevels(); mip++)
    {
-      U32 srcPixelCount = bmp->getSurfaceSize(mip)/ srcBytesPerPixel;
+      U32 width = getWidth() >> mip;
+      U32 height = getHeight() >> mip;
+      if (width == 0) width = 1;
+      if (height == 0) height = 1;
+
+      // Check if multisampled
+      GLint samples = 0;
+      GLenum target = mBinding;
+      if (mBinding == GL_TEXTURE_CUBE_MAP)
+         target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
+
+      glGetTexLevelParameteriv(target, mip, GL_TEXTURE_SAMPLES, &samples);
+      if (samples > 0)
+      {
+         Con::warnf("GFXGLTextureObject::copyToBmp - Texture is multisampled (%d samples) at mip %d; resolve first.", samples, mip);
+         return false;
+      }
+
+      for (U32 face = 0; face < numFaces; face++)
+      {
+         GLenum faceTarget = isCubemap ? GFXGLFaceType[face] : mBinding;
+
+         U32 pixelCount = width * height;
+         U8* srcPixels = (U8*)mem.alloc(pixelCount * srcBytesPerPixel);
+         U8* dest = bmp->getWritableBits(mip, face);
+
+         if (!dest)
+         {
+            Con::errorf("GFXGLTextureObject::copyToBmp - No destination bits for mip=%u face=%u", mip, face);
+            continue;
+         }
+
+         // Read texture data
+         glGetTexImage(faceTarget, mip, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], srcPixels);
+
+         if (mFormat == GFXFormatR16G16B16A16F)
+         {
+            dMemcpy(dest, srcPixels, pixelCount * srcBytesPerPixel);
+         }
+         else
+         {
+            // Simple 8-bit per channel copy (RGBA)
+            U8* src = srcPixels;
+            for (U32 i = 0; i < pixelCount; ++i)
+            {
+               dest[0] = src[0];
+               dest[1] = src[1];
+               dest[2] = src[2];
+               if (dstBytesPerPixel == 4)
+                  dest[3] = src[3];
+
+               src += srcBytesPerPixel;
+               dest += dstBytesPerPixel;
+            }
+         }
+      } // face
+   } // mip
+
+   glBindTexture(mBinding, 0);
+   return true;
+}
+
+void GFXGLTextureObject::updateTextureSlot(const GFXTexHandle& texHandle, const U32 slot, const S32 face)
+{
+   if (!texHandle.isValid())
+      return;
+
+   GFXGLTextureObject* srcTex = static_cast<GFXGLTextureObject*>(texHandle.getPointer());
+   if (!srcTex || srcTex->getHandle() == 0)
+      return;
+
+   const GLenum dstTarget = mBinding;             // destination binding (this)
+   const GLenum srcTarget = srcTex->getBinding(); // source binding
+   const bool srcIsCube = (srcTarget == GL_TEXTURE_CUBE_MAP || srcTarget == GL_TEXTURE_CUBE_MAP_ARRAY);
+
+   // Determine list of faces to copy from source
+   U32 firstFace = 0;
+   U32 faceCount = 1;
+   if (face >= 0)
+   {
+      firstFace = (U32)face;
+      faceCount = 1;
+   }
+   else if (srcIsCube)
+   {
+      firstFace = 0;
+      faceCount = 6;
+   }
+   else
+   {
+      firstFace = 0;
+      faceCount = 1;
+   }
 
-      U8* dest = bmp->getWritableBits(mip);
-      U8* orig = (U8*)mem.alloc(srcPixelCount * srcBytesPerPixel);
+   // Ensure textures are valid
+   if (!glIsTexture(mHandle) || !glIsTexture(srcTex->getHandle()))
+   {
+      Con::errorf("updateTextureSlot: invalid GL texture handle src=%u dst=%u", srcTex->getHandle(), mHandle);
+      return;
+   }
 
-      glGetTexImage(mBinding, mip, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], orig);
-      if (mFormat == GFXFormatR16G16B16A16F)
+   // If copyImage supported, prefer that. We'll copy face-by-face (one-layer depth = 1)
+   if (GFXGL->mCapabilities.copyImage)
+   {
+      for (U32 mip = 0; mip < getMipLevels(); ++mip)
       {
-         dMemcpy(dest, orig, srcPixelCount * srcBytesPerPixel);
+         const GLsizei mipW = getMax(1u, srcTex->getWidth() >> mip);
+         const GLsizei mipH = getMax(1u, srcTex->getHeight() >> mip);
+
+         for (U32 f = firstFace; f < firstFace + faceCount; ++f)
+         {
+            // Compute source z offset (for cube arrays it's layer index; for cubemap it's face index)
+            GLint srcZ = 0;
+            if (srcTarget == GL_TEXTURE_CUBE_MAP_ARRAY)
+            {
+               srcZ = f;
+            }
+            else if (srcTarget == GL_TEXTURE_CUBE_MAP)
+            {
+               srcZ = f;
+            }
+            else
+            {
+               srcZ = 0; // 2D source
+            }
+
+            // Compute destination layer (z offset) depending on destination type
+            GLint dstZ = 0;
+            if (dstTarget == GL_TEXTURE_CUBE_MAP_ARRAY)
+            {
+               // each slot is a whole cubemap => slot * 6 + faceIndex
+               dstZ = (GLint)(slot * 6 + f);
+            }
+            else if (dstTarget == GL_TEXTURE_2D_ARRAY)
+            {
+               dstZ = (GLint)slot; // each slot is a single layer
+            }
+            else if (dstTarget == GL_TEXTURE_CUBE_MAP)
+            {
+               dstZ = (GLint)f;
+            }
+            else
+            {
+               dstZ = 0; // 2D texture target
+            }
+
+            // Copy single layer/face at this mip
+            glCopyImageSubData(
+               srcTex->getHandle(), srcTarget, mip, 0, 0, srcZ,
+               mHandle, dstTarget, mip, 0, 0, dstZ,
+               mipW, mipH, 1
+            );
+
+            GLenum err = glGetError();
+            if (err != GL_NO_ERROR)
+               Con::errorf("glCopyImageSubData failed with 0x%X (mip=%u face=%u)", err, mip, f);
+         }
       }
-      else
+
+      return;
+   }
+
+   // Fallback: CPU-side copy using glGetTexImage + glTexSubImage
+   for (U32 mip = 0; mip < getMipLevels() && mip < srcTex->getMipLevels(); ++mip)
+   {
+      const GLsizei mipW = getMax(1u, srcTex->getWidth() >> mip);
+      const GLsizei mipH = getMax(1u, srcTex->getHeight() >> mip);
+      const U32 pixelSize = GFXFormat_getByteSize(mFormat); // assuming same fmt for src/dst
+      const U32 dataSize = mipW * mipH * pixelSize;
+
+      FrameAllocatorMarker mem;
+      U8* buffer = (U8*)mem.alloc(dataSize);
+
+      glBindTexture(srcTarget, srcTex->getHandle());
+      glBindTexture(dstTarget, mHandle);
+
+      for (U32 f = firstFace; f < firstFace + faceCount; ++f)
       {
-         for (int i = 0; i < srcPixelCount; ++i)
+         GLenum srcFaceTarget = srcTarget;
+         if (srcTarget == GL_TEXTURE_CUBE_MAP)
+            srcFaceTarget = GFXGLFaceType[f];
+
+         // read pixels from source
+         glGetTexImage(srcFaceTarget, mip, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buffer);
+
+         GLint dstLayer = 0;
+         if (dstTarget == GL_TEXTURE_CUBE_MAP_ARRAY)
+            dstLayer = (GLint)(slot * 6 + f);
+         else if (dstTarget == GL_TEXTURE_2D_ARRAY)
+            dstLayer = (GLint)slot;
+         else if (dstTarget == GL_TEXTURE_CUBE_MAP)
+            dstLayer = (GLint)f;
+         else
+            dstLayer = 0;
+
+         if (dstTarget == GL_TEXTURE_CUBE_MAP)
+         {
+            GLenum dstFaceTarget = GFXGLFaceType[f];
+            glTexSubImage2D(dstFaceTarget, mip, 0, 0, mipW, mipH,
+               GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buffer);
+         }
+         else if (dstTarget == GL_TEXTURE_2D)
          {
-            dest[0] = orig[0];
-            dest[1] = orig[1];
-            dest[2] = orig[2];
-            if (dstBytesPerPixel == 4)
-               dest[3] = orig[3];
-
-            orig += srcBytesPerPixel;
-            dest += dstBytesPerPixel;
+            glTexSubImage2D(GL_TEXTURE_2D, mip, 0, 0, mipW, mipH,
+               GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buffer);
+         }
+         else if (dstTarget == GL_TEXTURE_2D_ARRAY || dstTarget == GL_TEXTURE_CUBE_MAP_ARRAY)
+         {
+            glTexSubImage3D(dstTarget, mip, 0, 0, dstLayer, mipW, mipH, 1,
+               GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], buffer);
          }
       }
+
+      glBindTexture(dstTarget, 0);
+      glBindTexture(srcTarget, 0);
    }
-   glBindTexture(mBinding, 0);
+}
 
-   return true;
+void GFXGLTextureObject::copyTo(GFXTextureObject* dstTex)
+{
 }
 
 void GFXGLTextureObject::initSamplerState(const GFXSamplerStateDesc &ssd)

+ 8 - 4
Engine/source/gfx/gl/gfxGLTextureObject.h

@@ -26,7 +26,9 @@
 #include "gfx/gfxTextureObject.h"
 #include "gfx/gl/tGL/tGL.h"
 #include "gfx/gfxStateBlock.h"
-
+#ifndef _MRECT_H_
+#include "math/mRect.h"
+#endif
 class GFXGLDevice;
 
 class GFXGLTextureObject : public GFXTextureObject 
@@ -64,11 +66,13 @@ public:
 
    /// Get/set data from texture (for dynamic textures and render targets)
    /// @attention DO NOT READ FROM THE RETURNED RECT! It is not guaranteed to work and may incur significant performance penalties.
-   GFXLockedRect* lock(U32 mipLevel = 0, RectI *inRect = NULL) override;
-   void unlock(U32 mipLevel = 0 ) override;
+   GFXLockedRect* lock(U32 mipLevel = 0, RectI *inRect = NULL, U32 faceIndex = 0) override;
+   void unlock(U32 mipLevel = 0, U32 faceIndex = 0) override;
 
    bool copyToBmp(GBitmap *) override; ///< Not implemented
-   
+   void updateTextureSlot(const GFXTexHandle& texHandle, const U32 slot, const S32 face = -1) override;
+   void copyTo(GFXTextureObject* dstTex) override;
+   void generateMipMaps() override {};
    bool mIsNPoT2;
 
    // GFXResource interface

+ 98 - 135
Engine/source/gfx/gl/gfxGLTextureTarget.cpp

@@ -38,9 +38,9 @@ public:
       mipLevel(_mipLevel), zOffset(_zOffset)
    {
    }
-   
+
    virtual ~_GFXGLTargetDesc() {}
-   
+
    virtual U32 getHandle() = 0;
    virtual U32 getWidth() = 0;
    virtual U32 getHeight() = 0;
@@ -49,10 +49,10 @@ public:
    virtual GLenum getBinding() = 0;
    virtual GFXFormat getFormat() = 0;
    virtual bool isCompatible(const GFXGLTextureObject* tex) = 0;
-   
+
    U32 getMipLevel() { return mipLevel; }
    U32 getZOffset() { return zOffset; }
-   
+
 private:
    U32 mipLevel;
    U32 zOffset;
@@ -62,19 +62,21 @@ private:
 class _GFXGLTextureTargetDesc : public _GFXGLTargetDesc
 {
 public:
-   _GFXGLTextureTargetDesc(GFXGLTextureObject* tex, U32 _mipLevel, U32 _zOffset) 
-      : _GFXGLTargetDesc(_mipLevel, _zOffset), mTex(tex)
+  
+   _GFXGLTextureTargetDesc(GFXGLTextureObject* tex, U32 _mipLevel, U32 _zOffset, U32 _face = 0, bool isCube = false)
+      : _GFXGLTargetDesc(_mipLevel, _zOffset), mTex(tex), mFace(_face), mIsCube(isCube)
    {
    }
-   
+
    virtual ~_GFXGLTextureTargetDesc() {}
-   
+
    U32 getHandle() override { return mTex->getHandle(); }
    U32 getWidth() override { return mTex->getWidth(); }
    U32 getHeight() override { return mTex->getHeight(); }
    U32 getDepth() override { return mTex->getDepth(); }
+   U32 getFace() { return mFace; }
    bool hasMips() override { return mTex->mMipLevels != 1; }
-   GLenum getBinding() override { return mTex->getBinding(); }
+   GLenum getBinding() override { return mIsCube ? GFXGLFaceType[mFace] : mTex->getBinding(); }
    GFXFormat getFormat() override { return mTex->getFormat(); }
    bool isCompatible(const GFXGLTextureObject* tex) override
    {
@@ -82,40 +84,12 @@ public:
          && mTex->getWidth() == tex->getWidth()
          && mTex->getHeight() == tex->getHeight();
    }
-   GFXGLTextureObject* getTextureObject() const {return mTex; }
-   
-private:
-   StrongRefPtr<GFXGLTextureObject> mTex;
-};
+   GFXGLTextureObject* getTextureObject() const { return mTex; }
 
-/// Internal struct used to track Cubemap texture information for FBO attachment
-class _GFXGLCubemapTargetDesc : public _GFXGLTargetDesc
-{
-public:
-   _GFXGLCubemapTargetDesc(GFXGLCubemap* tex, U32 _face, U32 _mipLevel, U32 _zOffset) 
-      : _GFXGLTargetDesc(_mipLevel, _zOffset), mTex(tex), mFace(_face)
-   {
-   }
-   
-   virtual ~_GFXGLCubemapTargetDesc() {}
-   
-   U32 getHandle() override { return mTex->getHandle(); }
-   U32 getWidth() override { return mTex->getWidth(); }
-   U32 getHeight() override { return mTex->getHeight(); }
-   U32 getDepth() override { return 0; }
-   bool hasMips() override { return mTex->getMipMapLevels() != 1; }
-   GLenum getBinding() override { return GFXGLCubemap::getEnumForFaceNumber(mFace); }
-   GFXFormat getFormat() override { return mTex->getFormat(); }
-   bool isCompatible(const GFXGLTextureObject* tex) override
-   {
-      return mTex->getFormat() == tex->getFormat()
-         && mTex->getWidth() == tex->getWidth()
-         && mTex->getHeight() == tex->getHeight();
-   }
-   
 private:
-   StrongRefPtr<GFXGLCubemap> mTex;
+   StrongRefPtr<GFXGLTextureObject> mTex;
    U32 mFace;
+   bool mIsCube;
 };
 
 // Internal implementations
@@ -123,9 +97,9 @@ class _GFXGLTextureTargetImpl // TODO OPENGL remove and implement on GFXGLTextur
 {
 public:
    GFXGLTextureTarget* mTarget;
-   
+
    virtual ~_GFXGLTextureTargetImpl() {}
-   
+
    virtual void applyState() = 0;
    virtual void makeActive() = 0;
    virtual void finish() = 0;
@@ -137,10 +111,10 @@ class _GFXGLTextureTargetFBOImpl : public _GFXGLTextureTargetImpl
 public:
    GLuint mFramebuffer;
    bool mGenMips;
-   
+
    _GFXGLTextureTargetFBOImpl(GFXGLTextureTarget* target);
    virtual ~_GFXGLTextureTargetFBOImpl();
-   
+
    void applyState() override;
    void makeActive() override;
    void finish() override;
@@ -159,42 +133,42 @@ _GFXGLTextureTargetFBOImpl::~_GFXGLTextureTargetFBOImpl()
 }
 
 void _GFXGLTextureTargetFBOImpl::applyState()
-{   
+{
    // REMINDER: When we implement MRT support, check against GFXGLDevice::getNumRenderTargets()
-   
+
    PRESERVE_FRAMEBUFFER();
    glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
    glEnable(GL_FRAMEBUFFER_SRGB);
    bool drawbufs[16];
    int bufsize = 0;
    for (int i = 0; i < 16; i++)
-           drawbufs[i] = false;
+      drawbufs[i] = false;
    bool hasColor = false;
-   for(int i = 0; i < GFXGL->getNumRenderTargets(); ++i)
-   {   
-      _GFXGLTargetDesc* color = mTarget->getTargetDesc( static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0+i ));
-      if(color)
+   for (int i = 0; i < GFXGL->getNumRenderTargets(); ++i)
+   {
+      _GFXGLTargetDesc* color = mTarget->getTargetDesc(static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0 + i));
+      if (color)
       {
          hasColor = true;
          const GLenum binding = color->getBinding();
-         if( binding == GL_TEXTURE_2D || (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) )
-            glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding( ), color->getHandle( ), color->getMipLevel( ) );
-         else if( binding == GL_TEXTURE_1D )
-            glFramebufferTexture1D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding( ), color->getHandle( ), color->getMipLevel( ) );
-         else if( binding == GL_TEXTURE_3D )
-            glFramebufferTexture3D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding( ), color->getHandle( ), color->getMipLevel( ), color->getZOffset( ) );
+         if (binding == GL_TEXTURE_2D || (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
+            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding(), color->getHandle(), color->getMipLevel());
+         else if (binding == GL_TEXTURE_1D)
+            glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding(), color->getHandle(), color->getMipLevel());
+         else if (binding == GL_TEXTURE_3D)
+            glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, color->getBinding(), color->getHandle(), color->getMipLevel(), color->getZOffset());
          else
-             Con::errorf("_GFXGLTextureTargetFBOImpl::applyState - Bad binding");
+            Con::errorf("_GFXGLTextureTargetFBOImpl::applyState - Bad binding");
       }
       else
       {
          // Clears the texture (note that the binding is irrelevent)
-         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_TEXTURE_2D, 0, 0);
+         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, 0, 0);
       }
    }
-   
+
    _GFXGLTargetDesc* depthStecil = mTarget->getTargetDesc(GFXTextureTarget::DepthStencil);
-   if(depthStecil)
+   if (depthStecil)
    {
       // Certain drivers have issues with depth only FBOs.  That and the next two asserts assume we have a color target.
       AssertFatal(hasColor, "GFXGLTextureTarget::applyState() - Cannot set DepthStencil target without Color0 target!");
@@ -206,40 +180,40 @@ void _GFXGLTextureTargetFBOImpl::applyState()
       glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
    }
 
-   GLenum *buf = new GLenum[bufsize];
+   GLenum* buf = new GLenum[bufsize];
    int count = 0;
    for (int i = 0; i < bufsize; i++)
    {
-           if (drawbufs[i])
-           {
-                   buf[count] = GL_COLOR_ATTACHMENT0 + i;
-                   count++;
-           }
+      if (drawbufs[i])
+      {
+         buf[count] = GL_COLOR_ATTACHMENT0 + i;
+         count++;
+      }
    }
- 
+
    glDrawBuffers(bufsize, buf);
- 
+
    delete[] buf;
    CHECK_FRAMEBUFFER_STATUS();
 }
 
 void _GFXGLTextureTargetFBOImpl::makeActive()
 {
-    glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
-    GFXGL->getOpenglCache()->setCacheBinded(GL_FRAMEBUFFER, mFramebuffer);
-
-    int i = 0;
-    GLenum draws[16];
-    for( i = 0; i < GFXGL->getNumRenderTargets(); ++i)
-    {
-        _GFXGLTargetDesc* color = mTarget->getTargetDesc( static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0+i ));
-        if(color)
-            draws[i] = GL_COLOR_ATTACHMENT0 + i;
-        else
-            break;
-    }
-
-    glDrawBuffers( i, draws );
+   glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+   GFXGL->getOpenglCache()->setCacheBinded(GL_FRAMEBUFFER, mFramebuffer);
+
+   int i = 0;
+   GLenum draws[16];
+   for (i = 0; i < GFXGL->getNumRenderTargets(); ++i)
+   {
+      _GFXGLTargetDesc* color = mTarget->getTargetDesc(static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0 + i));
+      if (color)
+         draws[i] = GL_COLOR_ATTACHMENT0 + i;
+      else
+         break;
+   }
+
+   glDrawBuffers(i, draws);
 }
 
 void _GFXGLTextureTargetFBOImpl::finish()
@@ -250,20 +224,20 @@ void _GFXGLTextureTargetFBOImpl::finish()
    if (!mGenMips)
       return;
 
-   for(int i = 0; i < GFXGL->getNumRenderTargets(); ++i)
-   {   
-      _GFXGLTargetDesc* color = mTarget->getTargetDesc( static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0+i ) );
-      if(!color || !(color->hasMips()))
+   for (int i = 0; i < GFXGL->getNumRenderTargets(); ++i)
+   {
+      _GFXGLTargetDesc* color = mTarget->getTargetDesc(static_cast<GFXTextureTarget::RenderSlot>(GFXTextureTarget::Color0 + i));
+      if (!color || !(color->hasMips()))
          continue;
-   
+
       // Generate mips if necessary
       // Assumes a 2D texture.
       GLenum binding = color->getBinding();
       binding = (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ? GL_TEXTURE_CUBE_MAP : binding;
 
-      PRESERVE_TEXTURE( binding );
-      glBindTexture( binding, color->getHandle() );
-      glGenerateMipmap( binding );
+      PRESERVE_TEXTURE(binding);
+      glBindTexture(binding, color->getHandle());
+      glGenerateMipmap(binding);
    }
 }
 
@@ -271,13 +245,13 @@ void _GFXGLTextureTargetFBOImpl::finish()
 GFXGLTextureTarget::GFXGLTextureTarget(bool genMips) : mCopyFboSrc(0), mCopyFboDst(0)
 {
    mGenMips = genMips;
-   for(U32 i=0; i<MaxRenderSlotId; i++)
+   for (U32 i = 0; i < MaxRenderSlotId; i++)
       mTargets[i] = NULL;
-   
-   GFXTextureManager::addEventDelegate( this, &GFXGLTextureTarget::_onTextureEvent );
+
+   GFXTextureManager::addEventDelegate(this, &GFXGLTextureTarget::_onTextureEvent);
 
    _impl = new _GFXGLTextureTargetFBOImpl(this);
-    
+
    glGenFramebuffers(1, &mCopyFboSrc);
    glGenFramebuffers(1, &mCopyFboDst);
 }
@@ -292,7 +266,7 @@ GFXGLTextureTarget::~GFXGLTextureTarget()
 
 const Point2I GFXGLTextureTarget::getSize()
 {
-   if(mTargets[Color0].isValid())
+   if (mTargets[Color0].isValid())
       return Point2I(mTargets[Color0]->getWidth(), mTargets[Color0]->getHeight());
 
    return Point2I(0, 0);
@@ -300,61 +274,50 @@ const Point2I GFXGLTextureTarget::getSize()
 
 GFXFormat GFXGLTextureTarget::getFormat()
 {
-   if(mTargets[Color0].isValid())
+   if (mTargets[Color0].isValid())
       return mTargets[Color0]->getFormat();
 
    return GFXFormatR8G8B8A8;
 }
 
-void GFXGLTextureTarget::attachTexture( RenderSlot slot, GFXTextureObject *tex, U32 mipLevel/*=0*/, U32 zOffset /*= 0*/ )
+void GFXGLTextureTarget::attachTexture(RenderSlot slot, GFXTextureObject* tex, U32 mipLevel/*=0*/, U32 zOffset /*= 0*/, U32 face /*= 0*/)
 {
-   if( tex == GFXTextureTarget::sDefaultDepthStencil )
+   if (tex == GFXTextureTarget::sDefaultDepthStencil)
       tex = GFXGL->getDefaultDepthTex();
 
+   // are we readding the same thing, face and all?
    _GFXGLTextureTargetDesc* mTex = static_cast<_GFXGLTextureTargetDesc*>(mTargets[slot].ptr());
-   if( (!tex && !mTex) || (mTex && mTex->getTextureObject() == tex) )
+   if ((!tex && !mTex) || (mTex && mTex->getTextureObject() == tex && mTex->getFace() == face))
       return;
-   
+
    // Triggers an update when we next render
    invalidateState();
 
    // We stash the texture and info into an internal struct.
    GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(tex);
-   if(tex && tex != GFXTextureTarget::sDefaultDepthStencil)
-      mTargets[slot] = new _GFXGLTextureTargetDesc(glTexture, mipLevel, zOffset);
+   if (tex && tex != GFXTextureTarget::sDefaultDepthStencil)
+   {
+      mTargets[slot] = new _GFXGLTextureTargetDesc(glTexture, mipLevel, zOffset, face, glTexture->isCubeMap());
+   }
    else
       mTargets[slot] = NULL;
 }
 
-void GFXGLTextureTarget::attachTexture( RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel/*=0*/ )
+void GFXGLTextureTarget::attachTexture(RenderSlot slot, GFXCubemap* tex, U32 face, U32 mipLevel/*=0*/)
 {
-   // No depth cubemaps, sorry
-   AssertFatal(slot != DepthStencil, "GFXGLTextureTarget::attachTexture (cube) - Cube depth textures not supported!");
-   if(slot == DepthStencil)
-      return;
-    
-   // Triggers an update when we next render
-   invalidateState();
-   
-   // We stash the texture and info into an internal struct.
-   GFXGLCubemap* glTexture = static_cast<GFXGLCubemap*>(tex);
-   if(tex)
-      mTargets[slot] = new _GFXGLCubemapTargetDesc(glTexture, face, mipLevel, 0);
-   else
-      mTargets[slot] = NULL;
 }
 
 void GFXGLTextureTarget::clearAttachments()
 {
    deactivate();
-   for(S32 i=1; i<MaxRenderSlotId; i++)
+   for (S32 i = 1; i < MaxRenderSlotId; i++)
       attachTexture((RenderSlot)i, NULL);
 }
 
 void GFXGLTextureTarget::zombify()
 {
    invalidateState();
-   
+
    // Will be recreated in applyState
    _impl = NULL;
 }
@@ -376,15 +339,15 @@ void GFXGLTextureTarget::deactivate()
 
 void GFXGLTextureTarget::applyState()
 {
-   if(!isPendingState())
+   if (!isPendingState())
       return;
 
    // So we don't do this over and over again
    stateApplied();
-   
-   if(_impl.isNull())
+
+   if (_impl.isNull())
       _impl = new _GFXGLTextureTargetFBOImpl(this);
-           
+
    _impl->applyState();
 }
 
@@ -394,7 +357,7 @@ _GFXGLTargetDesc* GFXGLTextureTarget::getTargetDesc(RenderSlot slot) const
    return mTargets[slot].ptr();
 }
 
-void GFXGLTextureTarget::_onTextureEvent( GFXTexCallbackCode code )
+void GFXGLTextureTarget::_onTextureEvent(GFXTexCallbackCode code)
 {
    invalidateState();
 }
@@ -403,7 +366,7 @@ const String GFXGLTextureTarget::describeSelf() const
 {
    String ret = String::ToString("   Color0 Attachment: %i", mTargets[Color0].isValid() ? mTargets[Color0]->getHandle() : 0);
    ret += String::ToString("   Depth Attachment: %i", mTargets[DepthStencil].isValid() ? mTargets[DepthStencil]->getHandle() : 0);
-   
+
    return ret;
 }
 
@@ -416,27 +379,27 @@ void GFXGLTextureTarget::resolveTo(GFXTextureObject* obj)
    AssertFatal(dynamic_cast<GFXGLTextureObject*>(obj), "GFXGLTextureTarget::resolveTo - Incorrect type of texture, expected a GFXGLTextureObject");
    GFXGLTextureObject* glTexture = static_cast<GFXGLTextureObject*>(obj);
 
-   if( GFXGL->mCapabilities.copyImage && mTargets[Color0]->isCompatible(glTexture) )
+   if (GFXGL->mCapabilities.copyImage && mTargets[Color0]->isCompatible(glTexture))
    {
-      GLenum binding = mTargets[Color0]->getBinding();      
+      GLenum binding = mTargets[Color0]->getBinding();
       binding = (binding >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && binding <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ? GL_TEXTURE_CUBE_MAP : binding;
       U32 srcStartDepth = binding == GL_TEXTURE_CUBE_MAP ? mTargets[Color0]->getBinding() - GL_TEXTURE_CUBE_MAP_POSITIVE_X : 0;
       glCopyImageSubData(
-        mTargets[Color0]->getHandle(), binding, 0, 0, 0, srcStartDepth,
-        glTexture->getHandle(), glTexture->getBinding(), 0, 0, 0, 0,
-        mTargets[Color0]->getWidth(), mTargets[Color0]->getHeight(), 1);
+         mTargets[Color0]->getHandle(), binding, 0, 0, 0, srcStartDepth,
+         glTexture->getHandle(), glTexture->getBinding(), 0, 0, 0, 0,
+         mTargets[Color0]->getWidth(), mTargets[Color0]->getHeight(), 1);
 
       return;
    }
 
    PRESERVE_FRAMEBUFFER();
-   
+
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mCopyFboDst);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, glTexture->getBinding(), glTexture->getHandle(), 0);
-   
+
    glBindFramebuffer(GL_READ_FRAMEBUFFER, mCopyFboSrc);
    glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTargets[Color0]->getBinding(), mTargets[Color0]->getHandle(), 0);
-   
+
    glBlitFramebuffer(0, 0, mTargets[Color0]->getWidth(), mTargets[Color0]->getHeight(),
       0, 0, glTexture->getWidth(), glTexture->getHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
 }

+ 11 - 11
Engine/source/gfx/gl/gfxGLTextureTarget.h

@@ -50,35 +50,35 @@ public:
 
    const Point2I getSize() override;
    GFXFormat getFormat() override;
-   void attachTexture(RenderSlot slot, GFXTextureObject *tex, U32 mipLevel=0, U32 zOffset = 0) override;
-   void attachTexture(RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel=0) override;
+   void attachTexture(RenderSlot slot, GFXTextureObject* tex, U32 mipLevel = 0, U32 zOffset = 0, U32 face = 0) override;
+   void attachTexture(RenderSlot slot, GFXCubemap* tex, U32 face, U32 mipLevel = 0) override;
    virtual void clearAttachments();
 
    /// Functions to query internal state
    /// @{
-   
+
    /// Returns the internal structure for the given slot.  This should only be called by our internal implementations.
    _GFXGLTargetDesc* getTargetDesc(RenderSlot slot) const;
 
    /// @}
-   
+
    void deactivate() override;
    void zombify() override;
    void resurrect() override;
    const String describeSelf() const override;
-   
+
    void resolve() override;
-   
+
    void resolveTo(GFXTextureObject* obj) override;
-   
+
 protected:
 
    friend class GFXGLDevice;
 
    /// The callback used to get texture events.
    /// @see GFXTextureManager::addEventDelegate
-   void _onTextureEvent( GFXTexCallbackCode code );
-   
+   void _onTextureEvent(GFXTexCallbackCode code);
+
    /// Pointer to our internal implementation
    AutoPtr<_GFXGLTextureTargetImpl> _impl;
 
@@ -87,10 +87,10 @@ protected:
 
    /// These redirect to our internal implementation
    /// @{
-   
+
    void applyState();
    void makeActive();
-   
+
    /// @}
 
    //copy FBO

+ 22 - 3
Engine/source/gfx/gl/sdl/gfxGLDevice.sdl.cpp

@@ -103,8 +103,6 @@ void GFXGLDevice::enumerateAdapters( Vector<GFXAdapter*> &adapterList )
    }
 
    SDL_ClearError();
-   SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
-   SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 1);
 
@@ -140,6 +138,10 @@ void GFXGLDevice::enumerateAdapters( Vector<GFXAdapter*> &adapterList )
       return;
    }
 
+   // Set our sdl attribute to use this version.
+   SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
+   SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
+
    //check for required extensions
    if (!gglHasExtension(ARB_texture_cube_map_array))
    {
@@ -168,7 +170,24 @@ void GFXGLDevice::enumerateAdapters( Vector<GFXAdapter*> &adapterList )
       dStrcpy(toAdd->mName, "OpenGL", GFXAdapter::MaxAdapterNameLen);
 
    toAdd->mType = OpenGL;
-   toAdd->mShaderModel = 0.f;
+   F32 shaderModel = 3.3f;
+   if (major == 4)
+   {
+      if (minor == 0)
+         shaderModel = 4.00f;  // GLSL 4.00
+      else if (minor == 1)
+         shaderModel = 4.10f;  // GLSL 4.10
+      else if (minor == 2)
+         shaderModel = 4.20f;  // GLSL 4.20
+      else if (minor == 3)
+         shaderModel = 4.30f;  // GLSL 4.30
+      else if (minor == 4)
+         shaderModel = 4.40f;  // GLSL 4.40
+      else if (minor == 5)
+         shaderModel = 4.50f;  // GLSL 4.50
+      else if (minor == 6)
+         shaderModel = 4.60f;  // GLSL 4.60
+   }
    toAdd->mCreateDeviceInstanceDelegate = mCreateDeviceInstance;
 
    // Enumerate all available resolutions:

+ 23 - 9
Engine/source/gfx/sim/cubemapData.cpp

@@ -45,7 +45,15 @@ CubemapData::CubemapData()
 
 CubemapData::~CubemapData()
 {
-   mCubemap = NULL;
+   if (mCubeMapAsset.notNull())
+   {
+      mCubeMapAsset.clear();
+   }
+
+   if (mCubemap)
+   {
+      mCubemap.free();
+   }
 }
 
 ConsoleDocClass( CubemapData, 
@@ -101,7 +109,7 @@ void CubemapData::createMap()
        //check mCubeMapFile first
        if (mCubeMapAsset.notNull())
        {
-          mCubemap = TEXMGR->createCubemap(mCubeMapAsset->getImageFile());
+          mCubemap = mCubeMapAsset->getTexture(&GFXCubemapStaticTextureProfile);
           return;
        }
        else
@@ -125,11 +133,14 @@ void CubemapData::createMap()
 
        if( initSuccess )
        {
-           mCubemap = GFX->createCubemap();
-           if (mCubeMapFaceAsset->isNull())
-              return;
+          if (mCubeMapFaceAsset->isNull())
+             return;
 
-           mCubemap->initStatic(mCubeMapFaceTex);
+          mCubemap.set(mCubeMapFaceTex->getWidth(), mCubeMapFaceTex->getHeight(), mCubeMapFaceTex->getFormat(), &GFXCubemapStaticTextureProfile, "CubemapData-InitTexture", mCubeMapFaceTex->getPointer()->getMipLevels());
+          for (U32 i = 0; i < 6; i++)
+          {
+             mCubemap->updateTextureSlot(mCubeMapFaceTex[i],0, i);
+          }
        }
    }
 }
@@ -141,7 +152,7 @@ void CubemapData::updateFaces()
    //check mCubeMapFile first
    if (mCubeMapAsset.notNull())
    {
-      mCubemap = TEXMGR->createCubemap(mCubeMapAsset->getImageFile());
+      mCubemap = mCubeMapAsset->getTexture(&GFXCubemapStaticTextureProfile);
       return;
    }
    else
@@ -166,11 +177,14 @@ void CubemapData::updateFaces()
 	if( initSuccess )
 	{
 		mCubemap = NULL;
-		mCubemap = GFX->createCubemap();
       if (mCubeMapFaceAsset->isNull())
          return;
 
-		mCubemap->initStatic(mCubeMapFaceTex);
+      mCubemap.set(mCubeMapFaceTex->getWidth(), mCubeMapFaceTex->getHeight(), GFXFormatR16G16B16A16F, &GFXCubemapStaticTextureProfile, "CubemapData-InitTexture", mCubeMapFaceTex->getFormat());
+      for (U32 i = 0; i < 6; i++)
+      {
+         mCubemap->updateTextureSlot(mCubeMapFaceTex[i], 0, i);
+      }
 	}
 }
 

+ 1 - 1
Engine/source/gfx/sim/cubemapData.h

@@ -48,7 +48,7 @@ class CubemapData : public SimObject
 
 public:   
 
-   GFXCubemapHandle  mCubemap;
+   GFXTexHandle  mCubemap;
 
    CubemapData();
    ~CubemapData();

+ 10 - 36
Engine/source/lighting/shadowMap/cubeLightShadowMap.cpp

@@ -35,26 +35,11 @@
 #include "gfx/util/gfxFrustumSaver.h"
 #include "math/mathUtils.h"
 
-
 CubeLightShadowMap::CubeLightShadowMap( LightInfo *light )
    : Parent( light )
 {
 }
 
-bool CubeLightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc )
-{
-   if ( currTexFlag == Material::DynamicLight )
-   {
-      S32 reg = lsc->mShadowMapSC->getSamplerRegister();
-   	if ( reg != -1 )
-      	GFX->setCubeTexture( reg, mCubemap );
-
-      return true;
-   }
-
-   return false;
-}
-
 void CubeLightShadowMap::setShaderParameters(   GFXShaderConstBuffer *params, 
                                                 LightingShaderConstants *lsc )
 {
@@ -77,12 +62,6 @@ void CubeLightShadowMap::setShaderParameters(   GFXShaderConstBuffer *params,
    params->setSafe( lsc->mShadowSoftnessConst, p->shadowSoftness * ( 1.0f / mTexSize ) );
 }
 
-void CubeLightShadowMap::releaseTextures()
-{
-   Parent::releaseTextures();
-   mCubemap = NULL;
-}
-
 void CubeLightShadowMap::_render(   RenderPassManager* renderPass,
                                     const SceneRenderState *diffuseState )
 {
@@ -92,15 +71,16 @@ void CubeLightShadowMap::_render(   RenderPassManager* renderPass,
    const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true;
 
    const U32 texSize = getBestTexSize();
-
-   if (  mCubemap.isNull() || 
-         mTexSize != texSize )
+   if (mShadowMapTex.isNull() ||
+      mTexSize != texSize)
    {
       mTexSize = texSize;
-      mCubemap = GFX->createCubemap();
-      mCubemap->initDynamic( mTexSize, LightShadowMap::ShadowMapFormat );
-   }
 
+      mShadowMapTex.set(mTexSize, mTexSize,
+         ShadowMapFormat, &CubeShadowMapProfile,
+         "CubeLightShadowMap");
+      mShadowMapDepth = _getDepthTarget(mShadowMapTex->getWidth(), mShadowMapTex->getHeight());
+   }
    // Setup the world to light projection which is used
    // in the shader to transform the light vector for the
    // shadow lookup.
@@ -155,20 +135,14 @@ void CubeLightShadowMap::_render(   RenderPassManager* renderPass,
       GFXDEBUGEVENT_START( CubeLightShadowMap_Render_Face, ColorI::RED );
 
       // create camera matrix
-      VectorF cross = mCross(vUpVec, vLookatPt);
-      cross.normalizeSafe();
-
       MatrixF lightMatrix(true);
-      lightMatrix.setColumn(0, cross);
-      lightMatrix.setColumn(1, vLookatPt);
-      lightMatrix.setColumn(2, vUpVec);
-      lightMatrix.setPosition( mLight->getPosition() );
+      lightMatrix.LookAt(mLight->getPosition(), vLookatPt, vUpVec);
       lightMatrix.inverse();
 
       GFX->setWorldMatrix( lightMatrix );
 
-      mTarget->attachTexture(GFXTextureTarget::Color0, mCubemap, i);
-      mTarget->attachTexture(GFXTextureTarget::DepthStencil, _getDepthTarget( mTexSize, mTexSize ));
+      mTarget->attachTexture(GFXTextureTarget::Color0, mShadowMapTex,0,0, i);
+      mTarget->attachTexture(GFXTextureTarget::DepthStencil, _getDepthTarget(mShadowMapTex->getWidth(), mShadowMapTex->getHeight()));
       GFX->setActiveRenderTarget(mTarget);
       GFX->clear( GFXClearTarget | GFXClearStencil | GFXClearZBuffer, ColorI(255,255,255,255), 0.0f, 0 );
 

+ 0 - 9
Engine/source/lighting/shadowMap/cubeLightShadowMap.h

@@ -39,18 +39,9 @@ public:
 
    CubeLightShadowMap( LightInfo *light );
 
-   // LightShadowMap
-   bool hasShadowTex() const override { return mCubemap.isValid(); }
    ShadowType getShadowType() const override { return ShadowType_CubeMap; }
    void _render( RenderPassManager* renderPass, const SceneRenderState *diffuseState ) override;
    void setShaderParameters( GFXShaderConstBuffer* params, LightingShaderConstants* lsc ) override;
-   void releaseTextures() override;
-   bool setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc ) override;
-
-protected:   
-
-   /// The shadow cubemap.
-   GFXCubemapHandle mCubemap;
 
 };
 

+ 7 - 0
Engine/source/lighting/shadowMap/lightShadowMap.cpp

@@ -72,6 +72,13 @@ GFX_ImplementTextureProfile( ShadowMapProfile,
                               GFXTextureProfile::Pooled,
                               GFXTextureProfile::NONE );
 
+GFX_ImplementTextureProfile(CubeShadowMapProfile,
+                              GFXTextureProfile::DiffuseMap,
+                              GFXTextureProfile::PreserveSize |
+                              GFXTextureProfile::RenderTarget |
+                              GFXTextureProfile::Pooled | GFXTextureProfile::CubeMap,
+                              GFXTextureProfile::NONE);
+
 GFX_ImplementTextureProfile( ShadowMapZProfile,
                              GFXTextureProfile::DiffuseMap, 
                              GFXTextureProfile::PreserveSize | 

+ 1 - 0
Engine/source/lighting/shadowMap/lightShadowMap.h

@@ -266,6 +266,7 @@ protected:
 };
 
 GFX_DeclareTextureProfile( ShadowMapProfile );
+GFX_DeclareTextureProfile( CubeShadowMapProfile );
 GFX_DeclareTextureProfile( ShadowMapZProfile );
 
 

+ 3 - 3
Engine/source/materials/materialDefinition.h

@@ -150,7 +150,7 @@ public:
       TextureTable mTextures;
 
       /// The cubemap for this stage.
-      GFXCubemap* mCubemap;
+      GFXTexHandle mCubemap;
 
    public:
 
@@ -204,10 +204,10 @@ public:
       void getFeatureSet(FeatureSet* outFeatures) const;
 
       /// Returns the stage cubemap.
-      GFXCubemap* getCubemap() const { return mCubemap; }
+      GFXTexHandle getCubemap() const { return mCubemap; }
 
       /// Set the stage cubemap.
-      void setCubemap(GFXCubemap* cubemap) { mCubemap = cubemap; }
+      void setCubemap(GFXTexHandle cubemap) { mCubemap = cubemap; }
 
    };
 

+ 2 - 2
Engine/source/materials/processedCustomMaterial.cpp

@@ -377,12 +377,12 @@ void ProcessedCustomMaterial::setTextureStages( SceneRenderState *state, const S
             }
          case Material::Cube:
             {
-               GFX->setCubeTexture( samplerRegister, rpd->mCubeMap );
+               GFX->setTexture( samplerRegister, rpd->mCubeMap );
                break;
             }
          case Material::SGCube:
             {
-               GFX->setCubeTexture( samplerRegister, sgData.cubemap );
+               GFX->setTexture( samplerRegister, sgData.cubemap );
                break;
             }
          case Material::BackBuff:

+ 1 - 1
Engine/source/materials/processedMaterial.h

@@ -77,7 +77,7 @@ public:
    /// The cubemap to use when the texture type is
    /// set to Material::Cube.
    /// @see mTexType
-   GFXCubemapHandle mCubeMap;
+   GFXTexHandle mCubeMap;
 
    U32 mNumTex;
 

+ 3 - 3
Engine/source/materials/processedShaderMaterial.cpp

@@ -854,11 +854,11 @@ void ProcessedShaderMaterial::setTextureStages( SceneRenderState *state, const S
             break;
 
          case Material::Cube:
-            GFX->setCubeTexture( i, rpd->mCubeMap );
+            GFX->setTexture( i, rpd->mCubeMap );
             break;
 
          case Material::SGCube:
-            GFX->setCubeTexture( i, sgData.cubemap );
+            GFX->setTexture( i, sgData.cubemap );
             break;
 
          case Material::BackBuff:
@@ -1333,7 +1333,7 @@ void ProcessedShaderMaterial::setSceneInfo(SceneRenderState * state, const Scene
       }
    }
    if (sgData.cubemap)
-      shaderConsts->setSafe(handles->mCubeMipsSC, (F32)sgData.cubemap->getMipMapLevels());
+      shaderConsts->setSafe(handles->mCubeMipsSC, (F32)sgData.cubemap->getMipLevels());
    else
       shaderConsts->setSafe(handles->mCubeMipsSC, (F32)getBinLog2(PROBEMGR->getProbeTexSize()));
 

+ 1 - 1
Engine/source/materials/sceneData.h

@@ -83,7 +83,7 @@ struct SceneData
   
    // misc
    const MatrixF *objTrans;
-   GFXCubemap *cubemap;
+   GFXTexHandle cubemap;
    F32 visibility;
 
    /// Enables wireframe rendering for the object.

+ 1 - 1
Engine/source/platformWin32/winAsmBlit.cpp

@@ -195,7 +195,7 @@ void bitmapConvertRGB_to_5551_mmx(U8 *src, U32 pixels)
 void PlatformBlitInit()
 {
    bitmapExtrude5551 = bitmapExtrude5551_asm;
-   bitmapExtrudeRGB  = bitmapExtrudeRGB_c;
+   //bitmapExtrudeRGB  = bitmapExtrudeRGB_c;
 
    if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX)
    {

+ 1 - 1
Engine/source/renderInstance/renderDeferredMgr.cpp

@@ -374,7 +374,7 @@ void RenderDeferredMgr::render( SceneRenderState *state )
 
    // init loop data
    GFXTextureObject *lastLM = NULL;
-   GFXCubemap *lastCubemap = NULL;
+   GFXTexHandle lastCubemap = NULL;
    GFXTextureObject *lastReflectTex = NULL;
    GFXTextureObject *lastAccuTex = NULL;
    

+ 1 - 1
Engine/source/renderInstance/renderMeshMgr.cpp

@@ -113,7 +113,7 @@ void RenderMeshMgr::render(SceneRenderState * state)
 
    // init loop data
    GFXTextureObject *lastLM = NULL;
-   GFXCubemap *lastCubemap = NULL;
+   GFXTexHandle lastCubemap = NULL;
    GFXTextureObject *lastReflectTex = NULL;
    GFXTextureObject *lastMiscTex = NULL;
    GFXTextureObject *lastAccuTex = NULL;

+ 1 - 1
Engine/source/renderInstance/renderPassManager.h

@@ -382,7 +382,7 @@ struct MeshRenderInst : public RenderInst
    GFXTextureObject *reflectTex;
    GFXTextureObject *miscTex;
    GFXTextureObject *accuTex;
-   GFXCubemap   *cubemap;
+   GFXTexHandle cubemap;
 
    /// @name Hardware Skinning
    /// {

+ 26 - 23
Engine/source/renderInstance/renderProbeMgr.cpp

@@ -221,6 +221,9 @@ RenderProbeMgr::~RenderProbeMgr()
          SAFE_DELETE(i->value);
    }
    mConstantLookup.clear();
+
+   mIrradianceArray.free();
+   mPrefilterArray.free();
 }
 
 bool RenderProbeMgr::onAdd()
@@ -228,13 +231,11 @@ bool RenderProbeMgr::onAdd()
    if (!Parent::onAdd())
       return false;
 
-   mIrradianceArray = GFXCubemapArrayHandle(GFX->createCubemapArray());
-   mPrefilterArray = GFXCubemapArrayHandle(GFX->createCubemapArray());
-
    U32 scaledSize = getProbeTexSize();
    //pre-allocate a few slots
-   mIrradianceArray->init(PROBE_ARRAY_SLOT_BUFFER_SIZE, scaledSize, PROBE_FORMAT);
-   mPrefilterArray->init(PROBE_ARRAY_SLOT_BUFFER_SIZE, scaledSize, PROBE_FORMAT);
+   mIrradianceArray.set(scaledSize, scaledSize, PROBE_FORMAT, &GFXCubemapStaticTextureProfile, "RenderProbeMgr::mIrradianceArray", 0,0, PROBE_ARRAY_SLOT_BUFFER_SIZE);
+   mPrefilterArray.set(scaledSize, scaledSize, PROBE_FORMAT, &GFXCubemapStaticTextureProfile, "RenderProbeMgr::mPrefilterArray", 0,0, PROBE_ARRAY_SLOT_BUFFER_SIZE);
+
    mCubeSlotCount = PROBE_ARRAY_SLOT_BUFFER_SIZE;
 
    String brdfTexturePath = GFXTextureManager::getBRDFTexturePath();
@@ -376,12 +377,12 @@ void RenderProbeMgr::registerProbe(ReflectionProbe::ProbeInfo* newProbe)
    if (cubeIndex >= mCubeSlotCount)
    {
       //alloc temp array handles
-      GFXCubemapArrayHandle irr = GFXCubemapArrayHandle(GFX->createCubemapArray());
-      GFXCubemapArrayHandle prefilter = GFXCubemapArrayHandle(GFX->createCubemapArray());
+      GFXTexHandle irr;
+      GFXTexHandle prefilter;
 
       U32 scaledSize = getProbeTexSize();
-      irr->init(mCubeSlotCount + PROBE_ARRAY_SLOT_BUFFER_SIZE, scaledSize, PROBE_FORMAT);
-      prefilter->init(mCubeSlotCount + PROBE_ARRAY_SLOT_BUFFER_SIZE, scaledSize, PROBE_FORMAT);
+      irr.set(scaledSize, scaledSize, PROBE_FORMAT, &GFXCubemapStaticTextureProfile, "RenderProbeMgr::mIrradianceArray_temp_expansion", 0, 0, mCubeSlotCount + PROBE_ARRAY_SLOT_BUFFER_SIZE);
+      prefilter.set(scaledSize, scaledSize, PROBE_FORMAT, &GFXCubemapStaticTextureProfile, "RenderProbeMgr::mPrefilterArray_temp_expansion", 0, 0, mCubeSlotCount + PROBE_ARRAY_SLOT_BUFFER_SIZE);
 
       mIrradianceArray->copyTo(irr);
       mPrefilterArray->copyTo(prefilter);
@@ -390,6 +391,9 @@ void RenderProbeMgr::registerProbe(ReflectionProbe::ProbeInfo* newProbe)
       mIrradianceArray = irr;
       mPrefilterArray = prefilter;
 
+      irr.free();
+      prefilter.free();
+
       mCubeSlotCount += PROBE_ARRAY_SLOT_BUFFER_SIZE;
    }
 
@@ -466,15 +470,13 @@ void RenderProbeMgr::updateProbeTexture(ReflectionProbe::ProbeInfo* probeInfo)
       return;
    U32 scaledSize = getProbeTexSize();
    //Some basic sanity checking that we have valid cubemaps to work with
-   if (probeInfo->mIrradianceCubemap.isNull() || !probeInfo->mIrradianceCubemap->isInitialized() ||
-      probeInfo->mIrradianceCubemap->getSize() != scaledSize)
+   if (probeInfo->mIrradianceCubemap.isNull() || probeInfo->mIrradianceCubemap->getWidth() != scaledSize)
    {
       Con::errorf("RenderProbeMgr::updateProbeTexture() - tried to update a probe's texture with an invalid or uninitialized irradiance map!");
       return;
    }
 
-   if (probeInfo->mPrefilterCubemap.isNull() || !probeInfo->mPrefilterCubemap->isInitialized() ||
-      probeInfo->mPrefilterCubemap->getSize() != scaledSize)
+   if (probeInfo->mPrefilterCubemap.isNull() || probeInfo->mPrefilterCubemap->getWidth() != scaledSize)
    {
       Con::errorf("RenderProbeMgr::updateProbeTexture() - tried to update a probe's texture with an invalid or uninitialized specular map!");
       return;
@@ -482,12 +484,13 @@ void RenderProbeMgr::updateProbeTexture(ReflectionProbe::ProbeInfo* probeInfo)
 
    //Run the update on the array pair with the probe's cubemaps and index
    const U32 cubeIndex = probe->mCubemapIndex;
-   mIrradianceArray->updateTexture(probeInfo->mIrradianceCubemap, cubeIndex);
-   mPrefilterArray->updateTexture(probeInfo->mPrefilterCubemap, cubeIndex);
+   mIrradianceArray->updateTextureSlot(probeInfo->mIrradianceCubemap, cubeIndex);
+
+   mPrefilterArray->updateTextureSlot(probeInfo->mPrefilterCubemap, cubeIndex);
 
 #ifdef TORQUE_DEBUG
    Con::warnf("UpdatedProbeTexture - probe id: %u on cubeIndex %u, Irrad validity: %d, Prefilter validity: %d", probeInfo->mObject->getId(), cubeIndex,
-      probeInfo->mIrradianceCubemap->isInitialized(), probeInfo->mPrefilterCubemap->isInitialized());
+      probeInfo->mIrradianceCubemap.isValid(), probeInfo->mPrefilterCubemap.isValid());
 #endif
 }
 
@@ -616,8 +619,8 @@ void RenderProbeMgr::bakeProbe(ReflectionProbe* probe)
       clientProbe->createClientResources();
 
       //Prep it with whatever resolution we've dictated for our bake
-      clientProbe->mIrridianceMap->mCubemap->initDynamic(resolution, reflectFormat);
-      clientProbe->mPrefilterMap->mCubemap->initDynamic(resolution, reflectFormat);
+      clientProbe->mIrridianceMap->mCubemap.set(resolution, resolution, reflectFormat, &GFXCubemapRenderTargetProfile, "ReflectionProbe::mIrridianceMap", 0);
+      clientProbe->mPrefilterMap->mCubemap.set(resolution, resolution, reflectFormat, &GFXCubemapRenderTargetProfile, "ReflectionProbe::mPrefilterMap", 0);
 
       GFXTextureTargetRef renderTarget = GFX->allocRenderToTextureTarget(false);
       clientProbe->mPrefilterMap->mCubemap = cubeRefl.getCubemap();
@@ -807,9 +810,9 @@ void RenderProbeMgr::_update4ProbeConsts(const SceneData& sgData,
       shaderConsts->setSafe(probeShaderConsts->mSkylightDampSC, (int)probeSet.skyLightDamp);
 
       if (probeShaderConsts->mProbeSpecularCubemapArraySC->getSamplerRegister() != -1)
-         GFX->setCubeArrayTexture(probeShaderConsts->mProbeSpecularCubemapArraySC->getSamplerRegister(), mPrefilterArray);
+         GFX->setTexture(probeShaderConsts->mProbeSpecularCubemapArraySC->getSamplerRegister(), mPrefilterArray);
       if (probeShaderConsts->mProbeIrradianceCubemapArraySC->getSamplerRegister() != -1)
-         GFX->setCubeArrayTexture(probeShaderConsts->mProbeIrradianceCubemapArraySC->getSamplerRegister(), mIrradianceArray);
+         GFX->setTexture(probeShaderConsts->mProbeIrradianceCubemapArraySC->getSamplerRegister(), mIrradianceArray);
 
       shaderConsts->setSafe(probeShaderConsts->mMaxProbeDrawDistanceSC, smMaxProbeDrawDistance);
    }
@@ -875,8 +878,8 @@ void RenderProbeMgr::render( SceneRenderState *state )
    mProbeArrayEffect->setShaderMacro("MAX_PROBES", probePerFrame);
 
    mProbeArrayEffect->setTexture(3, mBRDFTexture);
-   mProbeArrayEffect->setCubemapArrayTexture(4, mPrefilterArray);
-   mProbeArrayEffect->setCubemapArrayTexture(5, mIrradianceArray);
+   mProbeArrayEffect->setTexture(4, mPrefilterArray);
+   mProbeArrayEffect->setTexture(5, mIrradianceArray);
    mProbeArrayEffect->setTexture(6, mWetnessTexture);
    //ssao mask
    if (AdvancedLightBinManager::smUseSSAOMask)
@@ -898,7 +901,7 @@ void RenderProbeMgr::render( SceneRenderState *state )
    mProbeArrayEffect->setShaderConst("$skylightCubemapIdx", (S32)mProbeData.skyLightIdx);
    mProbeArrayEffect->setShaderConst(ShaderGenVars::skylightDamp, mProbeData.skyLightDamp);
 
-   mProbeArrayEffect->setShaderConst("$cubeMips", (float)mPrefilterArray->getMipMapLevels());
+   mProbeArrayEffect->setShaderConst("$cubeMips", (float)mPrefilterArray->getMipLevels());
 
    //also set up some colors
    Vector<Point4F> contribColors;

+ 2 - 8
Engine/source/renderInstance/renderProbeMgr.h

@@ -184,7 +184,6 @@ public:
    static F32 smMaxProbeDrawDistance;
    static S32 smMaxProbesPerFrame;
    static S32 smProbeBakeResolution;
-   SceneRenderState *mState;
 private:
    /// <summary>
    /// List of registered probes. These are not necessarily rendered in a given frame
@@ -246,12 +245,12 @@ private:
    /// <summary>
    /// The prefilter cubemap array
    /// </summary>
-   GFXCubemapArrayHandle mPrefilterArray;
+   GFXTexHandle mPrefilterArray;
 
    /// <summary>
    /// The irradiance cubemap array
    /// </summary>
-   GFXCubemapArrayHandle mIrradianceArray;
+   GFXTexHandle mIrradianceArray;
 
    //Utilized in forward rendering
 
@@ -291,11 +290,6 @@ private:
    /// </summary>
    bool mUseHDRCaptures;
 
-   /// <summary>
-   /// holds the normal render state for light fade so we can capture them before and restore them after baking
-   /// </summary>
-   S32 mRenderMaximumNumOfLights;
-   bool mRenderUseLightFade;
 protected:
    /// The current active light manager.
    static RenderProbeMgr* smProbeManager;

+ 1 - 1
Engine/source/renderInstance/renderTranslucentMgr.cpp

@@ -142,7 +142,7 @@ void RenderTranslucentMgr::render( SceneRenderState *state )
 
    // init loop data
    GFXTextureObject *lastLM = NULL;
-   GFXCubemap *lastCubemap = NULL;
+   GFXTexHandle lastCubemap = NULL;
    GFXTextureObject *lastReflectTex = NULL;
    GFXTextureObject *lastAccuTex = NULL;
 

+ 13 - 14
Engine/source/scene/reflector.cpp

@@ -27,6 +27,7 @@
 #include "gfx/gfxCubemap.h"
 #include "gfx/gfxDebugEvent.h"
 #include "gfx/gfxTransformSaver.h"
+#include "gfx/util/gfxFrustumSaver.h"
 #include "scene/sceneManager.h"
 #include "scene/sceneRenderState.h"
 #include "core/stream/bitStream.h"
@@ -319,15 +320,12 @@ void CubeReflector::updateReflection( const ReflectParams &params, Point3F expli
          mCubemap.isNull() ||
          mCubemap->getFormat() != reflectFormat )
    {
-      mCubemap = GFX->createCubemap();
-      mCubemap->initDynamic( texDim, reflectFormat);
+      mCubemap.set(texDim, texDim, reflectFormat, &GFXCubemapRenderTargetProfile, "CubeReflector::updateReflection", 0);
    }
 
    if ( mRenderTarget.isNull() )
       mRenderTarget = GFX->allocRenderToTextureTarget();   
 
-   mDepthBuff = LightShadowMap::_getDepthTarget(texDim, texDim);
-   mRenderTarget->attachTexture(GFXTextureTarget::DepthStencil, mDepthBuff);
    F32 oldVisibleDist = gClientSceneGraph->getVisibleDistance();
    gClientSceneGraph->setVisibleDistance( mDesc->farDist );   
 
@@ -335,15 +333,17 @@ void CubeReflector::updateReflection( const ReflectParams &params, Point3F expli
    TSShapeInstance::smDetailAdjust *= mDesc->detailAdjust;
 
    // store current matrices
+   GFXFrustumSaver fsaver;
    GFXTransformSaver saver;
-
-   // set projection to 90 degrees vertical and horizontal
-   F32 left, right, top, bottom;
-   MathUtils::makeFrustum(&left, &right, &top, &bottom, M_HALFPI_F, 1.0f, mDesc->nearDist);
-   GFX->setFrustum(left, right, bottom, top, mDesc->nearDist, mDesc->farDist);
+   {
+      // set projection to 90 degrees vertical and horizontal
+      F32 left, right, top, bottom;
+      MathUtils::makeFrustum(&left, &right, &top, &bottom, M_HALFPI_F, 1.0f, mDesc->nearDist);
+      GFX->setFrustum(left, right, bottom, top, mDesc->nearDist, mDesc->farDist);
+   }
 
    GFX->pushActiveRenderTarget();
-   for (S32 i = 5; i >= 0; i--) {
+   for (S32 i = 0; i < 6; i++) {
       updateFace(params, i, explicitPostion);
    }
    GFX->popActiveRenderTarget();
@@ -352,7 +352,6 @@ void CubeReflector::updateReflection( const ReflectParams &params, Point3F expli
 
    mCubemap->generateMipMaps();
 
-
    gClientSceneGraph->setVisibleDistance(oldVisibleDist);
 
    mIsRendering = false;
@@ -413,19 +412,19 @@ void CubeReflector::updateFace( const ReflectParams &params, U32 faceidx, Point3
 
    GFX->setWorldMatrix(lightMatrix);
    GFX->clearTextureStateImmediate(0);
-   mRenderTarget->attachTexture( GFXTextureTarget::Color0, mCubemap, faceidx );   // Setup textures and targets...
    S32 texDim = mDesc->texSize;
    texDim = getMax(texDim, 32);
+   mRenderTarget->attachTexture(GFXTextureTarget::Color0, mCubemap, 0, 0, faceidx);   // Setup textures and targets...
    mRenderTarget->attachTexture(GFXTextureTarget::DepthStencil, LightShadowMap::_getDepthTarget(texDim, texDim));
    
-   GFX->setActiveRenderTarget(mRenderTarget);
+   GFX->setActiveRenderTarget(mRenderTarget, true);
    GFX->clear( GFXClearStencil | GFXClearTarget | GFXClearZBuffer, gCanvasClearColor, 1.0f, 0);
 
    SceneRenderState reflectRenderState
    (
       gClientSceneGraph,
       SPT_Reflect,
-      SceneCameraState::fromGFX()
+      SceneCameraState::fromGFXWithViewport(GFX->getViewport())
    );
 
    reflectRenderState.getMaterialDelegate().bind( REFLECTMGR, &ReflectionManager::getReflectionMaterial );

+ 3 - 3
Engine/source/scene/reflector.h

@@ -145,7 +145,7 @@ class CubeReflector : public ReflectorBase
 public:
 
    CubeReflector();
-   virtual ~CubeReflector() {}
+   virtual ~CubeReflector() { mCubemap.free(); }
 
    void registerReflector( SceneObject *inObject,
                            ReflectorDesc *inDesc );
@@ -153,7 +153,7 @@ public:
    void unregisterReflector() override;
    void updateReflection( const ReflectParams &params, Point3F explicitPostion = Point3F::Max) override;
 
-   GFXCubemap* getCubemap() const { return mCubemap; }
+   GFXTexHandle getCubemap() const { return mCubemap; }
 
    void updateFace( const ReflectParams &params, U32 faceidx, Point3F explicitPostion = Point3F::Max);
    F32 calcFaceScore( const ReflectParams &params, U32 faceidx );
@@ -162,7 +162,7 @@ protected:
 
    GFXTexHandle mDepthBuff;
    GFXTextureTargetRef mRenderTarget;   
-   GFXCubemapHandle  mCubemap;
+   GFXTexHandle  mCubemap;
    U32 mLastTexSize;
 
    class CubeFaceReflector : public ReflectorBase

+ 3 - 3
Engine/source/ts/tsRenderState.h

@@ -74,7 +74,7 @@ protected:
    
    const SceneRenderState *mState;
 
-   GFXCubemap *mCubemap;
+   GFXTexHandle mCubemap;
 
    /// Used to override the normal
    /// fade value of an object.
@@ -134,8 +134,8 @@ public:
    void setSceneState( const SceneRenderState *state ) { mState = state; }
 
    ///@see mCubemap
-   GFXCubemap* getCubemap() const { return mCubemap; }
-   void setCubemap( GFXCubemap *cubemap ) { mCubemap = cubemap; }
+   GFXTexHandle getCubemap() const { return mCubemap; }
+   void setCubemap(GFXTexHandle cubemap ) { mCubemap = cubemap; }
 
    ///@see mFadeOverride
    F32 getFadeOverride() const { return mFadeOverride; }

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