From 226b9fd7c8d41f590216b54582c7f69304428909 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 25 Oct 2025 14:12:55 -0500 Subject: [PATCH 01/19] probeinfo atlas backend work --- .../source/renderInstance/renderProbeMgr.cpp | 122 +++++++++++++++++- Engine/source/renderInstance/renderProbeMgr.h | 11 +- Engine/source/shaderGen/shaderGenVars.cpp | 1 + Engine/source/shaderGen/shaderGenVars.h | 1 + .../scripts/advancedLighting_Shaders.tscript | 2 + .../game/core/rendering/shaders/lighting.hlsl | 58 +++++++++ .../advanced/reflectionProbeArrayP.hlsl | 7 + .../game/core/rendering/shaders/torque.hlsl | 35 +++++ 8 files changed, 234 insertions(+), 3 deletions(-) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index 3eda073627..a7eb1767b1 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -139,6 +139,7 @@ void ProbeShaderConstants::init(GFXShader* shader) mProbeCountSC = shader->getShaderConstHandle(ShaderGenVars::probeCount); mBRDFTextureMap = shader->getShaderConstHandle(ShaderGenVars::BRDFTextureMap); + mProbeAtlasMap = shader->getShaderConstHandle(ShaderGenVars::ProbeAtlasMap); mWetnessTextureMap = shader->getShaderConstHandle(ShaderGenVars::WetnessTextureMap); mSkylightCubemapIdxSC = shader->getShaderConstHandle(ShaderGenVars::skylightCubemapIdx); @@ -177,7 +178,8 @@ RenderProbeMgr::RenderProbeMgr() mSkylightCubemapIdx(-1), mSkylightDamp(true), mCubeMapCount(0), - mUseHDRCaptures(true) + mUseHDRCaptures(true), + mProbeAtlas(NULL) { mEffectiveProbeCount = 0; mMipCount = 0; @@ -800,6 +802,11 @@ void RenderProbeMgr::_update4ProbeConsts(const SceneData& sgData, if (mBRDFTexture.isValid() && probeShaderConsts->mBRDFTextureMap->getSamplerRegister() != -1) GFX->setTexture(probeShaderConsts->mBRDFTextureMap->getSamplerRegister(), mBRDFTexture); + + if (mBRDFTexture.isValid() && probeShaderConsts->mBRDFTextureMap->getSamplerRegister() != -1) + GFX->setTexture(probeShaderConsts->mBRDFTextureMap->getSamplerRegister(), mBRDFTexture); + + if (mWetnessTexture.isValid() && probeShaderConsts->mWetnessTextureMap->getSamplerRegister() != -1) GFX->setTexture(probeShaderConsts->mWetnessTextureMap->getSamplerRegister(), mWetnessTexture); @@ -894,6 +901,14 @@ void RenderProbeMgr::render( SceneRenderState *state ) { mProbeArrayEffect->setTexture(7, GFXTexHandle(NULL)); } + + String atlasTexturePath = Con::getVariable("$Probes::AtlasTexture"); + if (mProbeAtlasTexture.set(atlasTexturePath, &GFXTexturePersistentProfile, "atlasTexture")) + { + mProbeArrayEffect->setTexture(8, mProbeAtlasTexture); + //mProbeArrayEffect->setShaderConst("$numAtlasEntries", (S32)10); //make adaptive + } + mProbeArrayEffect->setShaderConst("$numProbes", (S32)mProbeData.effectiveProbeCount); mProbeArrayEffect->setShaderConst("$skylightCubemapIdx", (S32)mProbeData.skyLightIdx); mProbeArrayEffect->setShaderConst(ShaderGenVars::skylightDamp, mProbeData.skyLightDamp); @@ -939,7 +954,112 @@ void RenderProbeMgr::render( SceneRenderState *state ) // Make sure the effect is gonna render. getProbeArrayEffect()->setSkip(false); } +#pragma pack(push, 1) +struct ProbeSerialize +{ + U8 ambientCol[4]{255,0,0,255}; + F32 sh[9]; + F32 worldPos[3]{ 0, 0, 0 }; + F32 rot[3]{ 1.0, 0.0, 0.0 }; + F32 offset[3]{ 0,0,0 }; + U32 flags = BIT(0); //[0]canDamp + U8 type = 0; + U8 radius = 5; + U8 scale = 10; + U8 attenuation = 0; +}; +#pragma pack(pop) +void RenderProbeMgr::serializeProbes() +{ + U32 probeDataLength = (U32)(sizeof(ProbeSerialize) / 4); + U32 count = 10; + ProbeSerialize* saveBuffer = (ProbeSerialize*)dMalloc(sizeof(ProbeSerialize) * count); + for (U32 i = 0; i < count; i++) + { + ProbeSerialize pSer; + for (U32 SHID = 0; SHID < 9; SHID++) + { + pSer.sh[SHID]= 0.0f; + } + pSer.worldPos[0] = 5 + i*10; + pSer.worldPos[1] = 5 + i*10; + pSer.worldPos[2] = 5 + i*10; + + saveBuffer[i] = pSer; // ensures defaults are applied + + } + + if (mProbeAtlas) + delete(mProbeAtlas); + + mProbeAtlas = new GBitmap(probeDataLength, count, (U8*)saveBuffer); + mProbeAtlas->writeBitmap("bmp", "probeinfoAtlas.bmp"); + free(saveBuffer); +} + +F32 RenderProbeMgr::unpackF32(ColorI in) +{ + U8 convert[4]{ in.red, in.green, in.blue, in.alpha}; + F32 out; + std::memcpy(&out, &convert, sizeof(F32)); + return out; +} + +void RenderProbeMgr::testProbeAtlas() +{ + if (mProbeAtlas) + delete(mProbeAtlas); + + mProbeAtlas = new GBitmap(); + mProbeAtlas->readBitmap("bmp", "probeinfoAtlas.bmp"); + + ProbeSerialize check; + ColorI tCol; + U32 i = 0; + mProbeAtlas->getColor(i, 0, tCol); + check.ambientCol[0] = tCol.red; + check.ambientCol[1] = tCol.green; + check.ambientCol[2] = tCol.blue; + check.ambientCol[3] = tCol.alpha; + for (U32 shID = 0; shID < 9; shID++) + { + mProbeAtlas->getColor(++i, 0, tCol); + check.sh[shID] = unpackF32(tCol); + } + mProbeAtlas->getColor(++i, 0, tCol); + check.worldPos[0] = unpackF32(tCol); + mProbeAtlas->getColor(++i, 0, tCol); + check.worldPos[1] = unpackF32(tCol); + mProbeAtlas->getColor(++i, 0, tCol); + check.worldPos[2] = unpackF32(tCol); + mProbeAtlas->getColor(++i, 0, tCol); + check.rot[0] = unpackF32(tCol); + mProbeAtlas->getColor(++i, 0, tCol); + check.rot[1] = unpackF32(tCol); + mProbeAtlas->getColor(++i, 0, tCol); + check.rot[2] = unpackF32(tCol); + mProbeAtlas->getColor(++i, 0, tCol); + check.offset[0] = unpackF32(tCol); + mProbeAtlas->getColor(++i, 0, tCol); + check.offset[1] = unpackF32(tCol); + mProbeAtlas->getColor(++i, 0, tCol); + check.offset[2] = unpackF32(tCol); + Con::warnf(" ambient: %i %i %i %i", check.ambientCol[0], check.ambientCol[1], check.ambientCol[2], check.ambientCol[3]); + Con::warnf(" sh: %f %f %f %f", check.sh[0], check.sh[1], check.sh[2], check.sh[3]); + Con::warnf("worldPos: %f %f %f", check.worldPos[0], check.worldPos[1], check.worldPos[2]); + Con::warnf(" rot: %f %f %f", check.rot[0], check.rot[1], check.rot[2]); + Con::warnf(" offset: %f %f %f", check.offset[0], check.offset[1], check.offset[2]); +} + +DefineEngineFunction(bakeProbeAtlas, void, (), ,"") +{ + PROBEMGR->serializeProbes(); +} +DefineEngineFunction(testProbeAtlas, void, (), , "") +{ + PROBEMGR->testProbeAtlas(); +} //============================================================================= // Console functions //============================================================================= diff --git a/Engine/source/renderInstance/renderProbeMgr.h b/Engine/source/renderInstance/renderProbeMgr.h index 55dbd43156..2f9a0ce3af 100644 --- a/Engine/source/renderInstance/renderProbeMgr.h +++ b/Engine/source/renderInstance/renderProbeMgr.h @@ -98,9 +98,10 @@ struct ProbeShaderConstants GFXShaderConstHandle *mProbeCountSC; GFXShaderConstHandle *mBRDFTextureMap; - GFXShaderConstHandle* mWetnessTextureMap; + GFXShaderConstHandle *mProbeAtlasMap; + GFXShaderConstHandle *mWetnessTextureMap; GFXShaderConstHandle *mSkylightCubemapIdxSC; - GFXShaderConstHandle* mSkylightDampSC; + GFXShaderConstHandle *mSkylightDampSC; GFXShaderConstHandle* mMaxProbeDrawDistanceSC; @@ -279,6 +280,7 @@ class RenderProbeMgr : public RenderBinManager /// The BRDF texture used in PBR math calculations /// GFXTexHandle mBRDFTexture; + GFXTexHandle mProbeAtlasTexture; GFXTexHandle mWetnessTexture; /// @@ -484,6 +486,11 @@ class RenderProbeMgr : public RenderBinManager void render(SceneRenderState * state) override; void clear() override { mActiveProbes.clear(); Parent::clear(); } + + GBitmap* mProbeAtlas; + void serializeProbes(); + void testProbeAtlas(); + F32 unpackF32(ColorI in); }; RenderProbeMgr* RenderProbeMgr::getProbeManager() diff --git a/Engine/source/shaderGen/shaderGenVars.cpp b/Engine/source/shaderGen/shaderGenVars.cpp index e6a3348930..7493018e84 100644 --- a/Engine/source/shaderGen/shaderGenVars.cpp +++ b/Engine/source/shaderGen/shaderGenVars.cpp @@ -93,6 +93,7 @@ const String ShaderGenVars::irradianceCubemapAR("$IrradianceCubemapAR"); const String ShaderGenVars::probeCount("$inNumProbes"); const String ShaderGenVars::BRDFTextureMap("$BRDFTexture"); +const String ShaderGenVars::ProbeAtlasMap("$probeAtlas"); const String ShaderGenVars::WetnessTextureMap("$WetnessTexture"); const String ShaderGenVars::maxProbeDrawDistance("$maxProbeDrawDistance"); diff --git a/Engine/source/shaderGen/shaderGenVars.h b/Engine/source/shaderGen/shaderGenVars.h index 4acc97fa88..4e021c21f4 100644 --- a/Engine/source/shaderGen/shaderGenVars.h +++ b/Engine/source/shaderGen/shaderGenVars.h @@ -104,6 +104,7 @@ struct ShaderGenVars const static String probeCount; const static String BRDFTextureMap; + const static String ProbeAtlasMap; const static String WetnessTextureMap; const static String maxProbeDrawDistance; diff --git a/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript b/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript index 1fcee60793..1f1df2fd98 100644 --- a/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript +++ b/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript @@ -291,6 +291,7 @@ singleton shaderData( PrefiterCubemapShader ) }; // +$Probes::AtlasTexture = "probeinfoAtlas.bmp"; singleton ShaderData( PFX_ReflectionProbeArray ) { DXVertexShaderFile = $Core::CommonShaderPath @ "/postFX/postFxV.hlsl"; @@ -307,6 +308,7 @@ singleton ShaderData( PFX_ReflectionProbeArray ) samplerNames[5] = "$irradianceCubemapAR"; samplerNames[6] = "$WetnessTexture"; samplerNames[7] = "$ssaoMask"; + samplerNames[8] = "$probeAtlas"; pixVersion = 2.0; }; diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index fa381711f4..22b0459035 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -86,6 +86,64 @@ inline float3 getDistanceVectorToPlane( float negFarPlaneDotEye, float3 directio return direction.xyz * t; } +struct ProbeInfo +{ + float4 ambientCol; + float4 sh[9]; + float3 worldPos; + float3 rot; + float3 offset; + int flags; + float4 probeConfigData; +}; + +inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex), int slot, int atlasLen) +{ + ProbeInfo probeInfo = (ProbeInfo)0; + + float slotLoc = slot/atlasLen; + float entryLoc = 0; + float entryStep = 1/(84/4); + probeInfo.ambientCol = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); + int i; + [unroll] + for (i=0;i<9;i++) + { + entryLoc+=entryStep; + probeInfo.sh[i] = RGBAtoFloat(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); + } + + float4 coordCol[3]; + [unroll] + for (i=0;i<0;i++) + { + entryLoc+=entryStep; + coordCol[i] = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); + } + probeInfo.worldPos = RGBAstoCoord(coordCol); + [unroll] + for (i=0;i<0;i++) + { + entryLoc+=entryStep; + coordCol[i] = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); + } + probeInfo.rot = RGBAstoCoord(coordCol); + [unroll] + for (i=0;i<0;i++) + { + entryLoc+=entryStep; + coordCol[i] = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); + } + probeInfo.offset = RGBAstoCoord(coordCol); + + entryLoc+=entryStep; + probeInfo.flags = RGBAtoInt(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); + + entryLoc+=entryStep; + probeInfo.probeConfigData= TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); + return probeInfo; +} + struct Surface { float3 P; // world space position diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl index df444d6668..c6f5f68993 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl @@ -26,6 +26,9 @@ TORQUE_UNIFORM_SAMPLER2D(WetnessTexture, 6); TORQUE_UNIFORM_SAMPLER2D(ssaoMask, 7); uniform float4 rtParams7; #endif + +TORQUE_UNIFORM_SAMPLER2D(probeAtlas, 8); + uniform float accumTime; uniform float dampness; @@ -205,10 +208,14 @@ float4 main(PFXVertToPix IN) : SV_TARGET float3 finalColor = diffuse + specularCol*horizon; finalColor *= surface.ao; + ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas),0,10); + return (probeInfo.ambientCol); + if(isCapturing == 1) return float4(lerp(finalColor, surface.baseColor.rgb,surface.metalness),0); else { return float4((finalColor*ambientColor), 0); } + } diff --git a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl index 8f4e30a222..e3c9e7bda1 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl @@ -30,6 +30,41 @@ static float M_PI_F = 3.14159265358979323846f; static float M_2PI_F = 6.28318530717958647692f; static float M_1OVER_PI_F = 0.31830988618f; +float RGBAtoFloat(float4 INrgba) +{ + // These constants represent the powers of 2 (2^0, 2^8, 2^16, 2^24) + // and are used to reconstruct the 32-bit integer representation of the float. + const float4 bitShifts = float4(1.0, 256.0, 65536.0, 16777216.0); + + // Reconstruct the 32-bit integer value from the RGBA components. + // Each channel is effectively a byte, so we multiply by increasing powers of 2. + float decodedInt = dot(INrgba * 255.0, bitShifts); + + // Now, cast the reconstructed integer to a float. + // This relies on the hardware's interpretation of the bit pattern as a float. + return asfloat(asuint(decodedInt)); +} + +float3 RGBAstoCoord(float4 inCol[3] ) +{ + return float3(RGBAtoFloat(inCol[0]),RGBAtoFloat(inCol[1]),RGBAtoFloat(inCol[2])); +} + +int RGBAtoInt(float4 INrgba) +{ + // These constants represent the powers of 2 (2^0, 2^8, 2^16, 2^24) + // and are used to reconstruct the 32-bit integer representation of the float. + const float4 bitShifts = float4(1.0, 256.0, 65536.0, 16777216.0); + + // Reconstruct the 32-bit integer value from the RGBA components. + // Each channel is effectively a byte, so we multiply by increasing powers of 2. + float decodedInt = dot(INrgba * 255.0, bitShifts); + + // Now, cast the reconstructed integer to an int. + // This relies on the hardware's interpretation of the bit pattern as a int. + return asuint(decodedInt); +} + /// Calculate fog based on a start and end positions in worldSpace. float computeSceneFog( float3 startPos, float3 endPos, From 2f6097aa27764455dc84412f1b792a6edaa225c1 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 25 Oct 2025 16:27:10 -0500 Subject: [PATCH 02/19] ditch seperate position and rotation in favor of a F32 4x4. wont need offset for general tones. shift flags to last --- .../source/renderInstance/renderProbeMgr.cpp | 61 ++++++++++--------- .../game/core/rendering/shaders/lighting.hlsl | 37 ++++++----- .../advanced/reflectionProbeArrayP.hlsl | 6 +- 3 files changed, 54 insertions(+), 50 deletions(-) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index a7eb1767b1..780d476df0 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -959,14 +959,12 @@ struct ProbeSerialize { U8 ambientCol[4]{255,0,0,255}; F32 sh[9]; - F32 worldPos[3]{ 0, 0, 0 }; - F32 rot[3]{ 1.0, 0.0, 0.0 }; - F32 offset[3]{ 0,0,0 }; - U32 flags = BIT(0); //[0]canDamp - U8 type = 0; + F32 xForm[4][4]; + U8 type = 0;//(U8)(ProbeInfo::Box); U8 radius = 5; U8 scale = 10; U8 attenuation = 0; + U32 flags = BIT(0); //[0]canDamp }; #pragma pack(pop) void RenderProbeMgr::serializeProbes() @@ -981,10 +979,14 @@ void RenderProbeMgr::serializeProbes() { pSer.sh[SHID]= 0.0f; } - pSer.worldPos[0] = 5 + i*10; - pSer.worldPos[1] = 5 + i*10; - pSer.worldPos[2] = 5 + i*10; - + MatrixF inmat = MatrixF(Point3F(0,0,0), Point3F(5 + i * 10, 5 + i * 10, 5 + i * 10)); + for (U32 x = 0; x < 4; x++) + { + for (U32 y = 0; y < 4; y++) + { + pSer.xForm[x][y] = inmat(x, y); + } + } saveBuffer[i] = pSer; // ensures defaults are applied } @@ -1027,28 +1029,31 @@ void RenderProbeMgr::testProbeAtlas() check.sh[shID] = unpackF32(tCol); } mProbeAtlas->getColor(++i, 0, tCol); - check.worldPos[0] = unpackF32(tCol); - mProbeAtlas->getColor(++i, 0, tCol); - check.worldPos[1] = unpackF32(tCol); - mProbeAtlas->getColor(++i, 0, tCol); - check.worldPos[2] = unpackF32(tCol); - mProbeAtlas->getColor(++i, 0, tCol); - check.rot[0] = unpackF32(tCol); - mProbeAtlas->getColor(++i, 0, tCol); - check.rot[1] = unpackF32(tCol); - mProbeAtlas->getColor(++i, 0, tCol); - check.rot[2] = unpackF32(tCol); - mProbeAtlas->getColor(++i, 0, tCol); - check.offset[0] = unpackF32(tCol); - mProbeAtlas->getColor(++i, 0, tCol); - check.offset[1] = unpackF32(tCol); + MatrixF inmat; + for (U32 x = 0; x < 4; x++) + { + for (U32 y = 0; y < 4; y++) + { + mProbeAtlas->getColor(++i, 0, tCol); + check.xForm[x][y] = unpackF32(tCol); + inmat(x, y) = check.xForm[x][y]; + } + } + mProbeAtlas->getColor(++i, 0, tCol); - check.offset[2] = unpackF32(tCol); + check.type = tCol.red; + check.radius = tCol.blue; + check.scale = tCol.green; + check.attenuation = tCol.alpha; + Con::warnf(" ambient: %i %i %i %i", check.ambientCol[0], check.ambientCol[1], check.ambientCol[2], check.ambientCol[3]); Con::warnf(" sh: %f %f %f %f", check.sh[0], check.sh[1], check.sh[2], check.sh[3]); - Con::warnf("worldPos: %f %f %f", check.worldPos[0], check.worldPos[1], check.worldPos[2]); - Con::warnf(" rot: %f %f %f", check.rot[0], check.rot[1], check.rot[2]); - Con::warnf(" offset: %f %f %f", check.offset[0], check.offset[1], check.offset[2]); + Con::warnf("worldPos: %f %f %f", inmat.getPosition().x, inmat.getPosition().y, inmat.getPosition().z); + Con::warnf("rotation: %f %f %f", inmat.getForwardVector().x, inmat.getForwardVector().y, inmat.getForwardVector().z); + Con::warnf(" type: %i", check.type); + Con::warnf(" radius: %f", check.radius); + Con::warnf(" scale: %f", check.scale); + Con::warnf(" atten: %f", check.attenuation); } DefineEngineFunction(bakeProbeAtlas, void, (), ,"") diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index 22b0459035..05d5c5a751 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -90,20 +90,19 @@ struct ProbeInfo { float4 ambientCol; float4 sh[9]; - float3 worldPos; - float3 rot; + float4x4 xform; float3 offset; int flags; float4 probeConfigData; }; -inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex), int slot, int atlasLen) +inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, int slot, int atlasLen) { ProbeInfo probeInfo = (ProbeInfo)0; float slotLoc = slot/atlasLen; float entryLoc = 0; - float entryStep = 1/(84/4); + float entryStep = 1/(112/4); probeInfo.ambientCol = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); int i; [unroll] @@ -113,35 +112,33 @@ inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex), int slot, int atlas probeInfo.sh[i] = RGBAtoFloat(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); } - float4 coordCol[3]; [unroll] - for (i=0;i<0;i++) + for (int x=0;x<3;x++) { - entryLoc+=entryStep; - coordCol[i] = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); + [unroll] + for (int y=0;y<3;y++) + { + entryLoc+=entryStep; + probeInfo.xform[x][y] = RGBAtoFloat(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); + } } - probeInfo.worldPos = RGBAstoCoord(coordCol); - [unroll] - for (i=0;i<0;i++) - { - entryLoc+=entryStep; - coordCol[i] = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); - } - probeInfo.rot = RGBAstoCoord(coordCol); + + float4 coordCol[3]; [unroll] - for (i=0;i<0;i++) + for (i=0;i<3;i++) { entryLoc+=entryStep; coordCol[i] = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); } probeInfo.offset = RGBAstoCoord(coordCol); - entryLoc+=entryStep; - probeInfo.flags = RGBAtoInt(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); - entryLoc+=entryStep; probeInfo.probeConfigData= TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); return probeInfo; + + entryLoc+=entryStep; + probeInfo.flags = RGBAtoInt(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); + } struct Surface diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl index c6f5f68993..89a042bbf2 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl @@ -208,8 +208,10 @@ float4 main(PFXVertToPix IN) : SV_TARGET float3 finalColor = diffuse + specularCol*horizon; finalColor *= surface.ao; - ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas),0,10); - return (probeInfo.ambientCol); + ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas), eyePosWorld, 0,10); + float contribution = defineBoxSpaceInfluence(surface.P, probeInfo.xform, probeInfo.probeConfigData.a); + contribution = max(contribution,0); + return lerp(float4(0,0,1,0), probeInfo.ambientCol,contribution); if(isCapturing == 1) return float4(lerp(finalColor, surface.baseColor.rgb,surface.metalness),0); From 78c1c7695958fe8bd76d84989acbd5537c9fb8b4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 25 Oct 2025 17:01:59 -0500 Subject: [PATCH 03/19] xform pack analysis --- Engine/source/renderInstance/renderProbeMgr.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index 780d476df0..eb2f927fad 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -979,7 +979,7 @@ void RenderProbeMgr::serializeProbes() { pSer.sh[SHID]= 0.0f; } - MatrixF inmat = MatrixF(Point3F(0,0,0), Point3F(5 + i * 10, 5 + i * 10, 5 + i * 10)); + MatrixF inmat = MatrixF(Point3F(0,0,0), Point3F(5 + i * 10, 5 + i * 10, 0)); for (U32 x = 0; x < 4; x++) { for (U32 y = 0; y < 4; y++) @@ -1054,6 +1054,9 @@ void RenderProbeMgr::testProbeAtlas() Con::warnf(" radius: %f", check.radius); Con::warnf(" scale: %f", check.scale); Con::warnf(" atten: %f", check.attenuation); + inmat.dumpMatrix("inmat"); + MatrixF groundTruth = MatrixF(Point3F(0, 0, 0), Point3F(5, 5, 0)); + groundTruth.dumpMatrix("groundTruth"); } DefineEngineFunction(bakeProbeAtlas, void, (), ,"") From 6c165dde0f17b28d4068212589493540d0ade107 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 25 Oct 2025 17:16:43 -0500 Subject: [PATCH 04/19] clean up *some* stray miseads, but not all aparantly --- Engine/source/renderInstance/renderProbeMgr.cpp | 4 ---- .../game/core/rendering/shaders/lighting.hlsl | 15 +++------------ 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index eb2f927fad..ea64bdc2cf 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -1028,7 +1028,6 @@ void RenderProbeMgr::testProbeAtlas() mProbeAtlas->getColor(++i, 0, tCol); check.sh[shID] = unpackF32(tCol); } - mProbeAtlas->getColor(++i, 0, tCol); MatrixF inmat; for (U32 x = 0; x < 4; x++) { @@ -1054,9 +1053,6 @@ void RenderProbeMgr::testProbeAtlas() Con::warnf(" radius: %f", check.radius); Con::warnf(" scale: %f", check.scale); Con::warnf(" atten: %f", check.attenuation); - inmat.dumpMatrix("inmat"); - MatrixF groundTruth = MatrixF(Point3F(0, 0, 0), Point3F(5, 5, 0)); - groundTruth.dumpMatrix("groundTruth"); } DefineEngineFunction(bakeProbeAtlas, void, (), ,"") diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index 05d5c5a751..5081dac444 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -113,27 +113,18 @@ inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, } [unroll] - for (int x=0;x<3;x++) + for (int x=0;x<4;x++) { [unroll] - for (int y=0;y<3;y++) + for (int y=0;y<4;y++) { entryLoc+=entryStep; probeInfo.xform[x][y] = RGBAtoFloat(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); } } - float4 coordCol[3]; - [unroll] - for (i=0;i<3;i++) - { - entryLoc+=entryStep; - coordCol[i] = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); - } - probeInfo.offset = RGBAstoCoord(coordCol); - entryLoc+=entryStep; - probeInfo.probeConfigData= TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); + probeInfo.probeConfigData = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); return probeInfo; entryLoc+=entryStep; From 63f7a4a731ba84e5d617c9d85ec87e72d1e812a3 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 25 Oct 2025 21:10:17 -0500 Subject: [PATCH 05/19] shift cnverter over to explicitly using uint4+bit math create a defineFauxSpaceInfluence method for use with the true worldspace values stored off in the atlas TODO: make it actually work instead of hugging origin with no rotation --- .../source/renderInstance/renderProbeMgr.cpp | 6 ++-- .../game/core/rendering/shaders/lighting.hlsl | 16 +++++++--- .../advanced/reflectionProbeArrayP.hlsl | 4 +-- .../game/core/rendering/shaders/torque.hlsl | 31 ++++--------------- 4 files changed, 24 insertions(+), 33 deletions(-) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index ea64bdc2cf..605a641ffa 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -979,7 +979,9 @@ void RenderProbeMgr::serializeProbes() { pSer.sh[SHID]= 0.0f; } - MatrixF inmat = MatrixF(Point3F(0,0,0), Point3F(5 + i * 10, 5 + i * 10, 0)); + MatrixF inmat = MatrixF(Point3F(45,0,0), Point3F(5 + i * 10, 5 + i * 10, 0)); + inmat.scale(10); + inmat.inverse(); for (U32 x = 0; x < 4; x++) { for (U32 y = 0; y < 4; y++) @@ -1038,7 +1040,7 @@ void RenderProbeMgr::testProbeAtlas() inmat(x, y) = check.xForm[x][y]; } } - + inmat.inverse(); mProbeAtlas->getColor(++i, 0, tCol); check.type = tCol.red; check.radius = tCol.blue; diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index 5081dac444..8213322f77 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -89,7 +89,7 @@ inline float3 getDistanceVectorToPlane( float negFarPlaneDotEye, float3 directio struct ProbeInfo { float4 ambientCol; - float4 sh[9]; + float sh[9]; float4x4 xform; float3 offset; int flags; @@ -122,14 +122,13 @@ inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, probeInfo.xform[x][y] = RGBAtoFloat(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); } } - + //probeInfo.xform[3].xyz = float3(1,1,0); entryLoc+=entryStep; probeInfo.probeConfigData = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); - return probeInfo; entryLoc+=entryStep; probeInfo.flags = RGBAtoInt(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); - + return probeInfo; } struct Surface @@ -458,6 +457,15 @@ float defineBoxSpaceInfluence(float3 wsPosition, float4x4 worldToObj, float atte return saturate(smoothstep(baseVal, (baseVal-attenuation/2), dist)); } +float defineFauxSpaceInfluence(float3 wsPosition, float4x4 worldMat, float attenuation) +{ + float3 surfPosLS = wsPosition-worldMat[3].xyz; + + float baseVal = 0.25; + float dist = getDistBoxToPoint(surfPosLS, float3(baseVal, baseVal, baseVal)); + return saturate(smoothstep(baseVal, (baseVal-attenuation/2), dist)); +} + // Box Projected IBL Lighting // Based on: http://www.gamedev.net/topic/568829-box-projected-cubemap-environment-mapping/ // and https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl index 89a042bbf2..463933c30b 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl @@ -208,8 +208,8 @@ float4 main(PFXVertToPix IN) : SV_TARGET float3 finalColor = diffuse + specularCol*horizon; finalColor *= surface.ao; - ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas), eyePosWorld, 0,10); - float contribution = defineBoxSpaceInfluence(surface.P, probeInfo.xform, probeInfo.probeConfigData.a); + ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas), eyePosWorld,0,10); + float contribution = defineFauxSpaceInfluence(surface.P, probeInfo.xform, probeInfo.probeConfigData.a); contribution = max(contribution,0); return lerp(float4(0,0,1,0), probeInfo.ambientCol,contribution); diff --git a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl index e3c9e7bda1..58d3eed16e 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl @@ -30,39 +30,20 @@ static float M_PI_F = 3.14159265358979323846f; static float M_2PI_F = 6.28318530717958647692f; static float M_1OVER_PI_F = 0.31830988618f; -float RGBAtoFloat(float4 INrgba) +float RGBAtoFloat(uint4 INrgba) { - // These constants represent the powers of 2 (2^0, 2^8, 2^16, 2^24) - // and are used to reconstruct the 32-bit integer representation of the float. - const float4 bitShifts = float4(1.0, 256.0, 65536.0, 16777216.0); - - // Reconstruct the 32-bit integer value from the RGBA components. - // Each channel is effectively a byte, so we multiply by increasing powers of 2. - float decodedInt = dot(INrgba * 255.0, bitShifts); - - // Now, cast the reconstructed integer to a float. - // This relies on the hardware's interpretation of the bit pattern as a float. - return asfloat(asuint(decodedInt)); + int convert = (INrgba.z << 24) | (INrgba.b << 16) | (INrgba.g << 8) | INrgba.r; + return asfloat(convert); } -float3 RGBAstoCoord(float4 inCol[3] ) +float3 RGBAstoCoord(uint4 inCol[3] ) { return float3(RGBAtoFloat(inCol[0]),RGBAtoFloat(inCol[1]),RGBAtoFloat(inCol[2])); } -int RGBAtoInt(float4 INrgba) +int RGBAtoInt(uint4 INrgba) { - // These constants represent the powers of 2 (2^0, 2^8, 2^16, 2^24) - // and are used to reconstruct the 32-bit integer representation of the float. - const float4 bitShifts = float4(1.0, 256.0, 65536.0, 16777216.0); - - // Reconstruct the 32-bit integer value from the RGBA components. - // Each channel is effectively a byte, so we multiply by increasing powers of 2. - float decodedInt = dot(INrgba * 255.0, bitShifts); - - // Now, cast the reconstructed integer to an int. - // This relies on the hardware's interpretation of the bit pattern as a int. - return asuint(decodedInt); + return asuint((INrgba.z << 24) | (INrgba.b << 16) | (INrgba.g << 8) | INrgba.r); } /// Calculate fog based on a start and end positions in worldSpace. From 3a3b06d82f41a7e7603f09b9a293a40b59053fbc Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 26 Oct 2025 14:30:17 -0500 Subject: [PATCH 06/19] reading the atlast now requires running testProbeAtlas alt decoder. exoplicitly just look at mip0 --- Engine/source/renderInstance/renderProbeMgr.cpp | 13 ++++++++----- .../game/core/rendering/shaders/lighting.hlsl | 14 +++++++------- .../lighting/advanced/reflectionProbeArrayP.hlsl | 2 +- .../game/core/rendering/shaders/torque.hlsl | 9 +++++++++ 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index 605a641ffa..2e2410f197 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -902,11 +902,13 @@ void RenderProbeMgr::render( SceneRenderState *state ) mProbeArrayEffect->setTexture(7, GFXTexHandle(NULL)); } - String atlasTexturePath = Con::getVariable("$Probes::AtlasTexture"); - if (mProbeAtlasTexture.set(atlasTexturePath, &GFXTexturePersistentProfile, "atlasTexture")) + if (mProbeAtlas) { - mProbeArrayEffect->setTexture(8, mProbeAtlasTexture); - //mProbeArrayEffect->setShaderConst("$numAtlasEntries", (S32)10); //make adaptive + if (mProbeAtlasTexture.set(mProbeAtlas, &GFXTexturePersistentProfile, false, "atlasTexture")) + { + mProbeArrayEffect->setTexture(8, mProbeAtlasTexture); + //mProbeArrayEffect->setShaderConst("$numAtlasEntries", (S32)10); //make adaptive + } } mProbeArrayEffect->setShaderConst("$numProbes", (S32)mProbeData.effectiveProbeCount); @@ -1015,7 +1017,8 @@ void RenderProbeMgr::testProbeAtlas() delete(mProbeAtlas); mProbeAtlas = new GBitmap(); - mProbeAtlas->readBitmap("bmp", "probeinfoAtlas.bmp"); + String atlasTexturePath = Con::getVariable("$Probes::AtlasTexture"); + mProbeAtlas->readBitmap("bmp", atlasTexturePath); ProbeSerialize check; ColorI tCol; diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index 8213322f77..3b4d09177a 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -102,14 +102,14 @@ inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, float slotLoc = slot/atlasLen; float entryLoc = 0; - float entryStep = 1/(112/4); - probeInfo.ambientCol = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); + float entryStep = 1/(112/4-1); + probeInfo.ambientCol = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc,slotLoc,0,0)); int i; [unroll] for (i=0;i<9;i++) { entryLoc+=entryStep; - probeInfo.sh[i] = RGBAtoFloat(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); + probeInfo.sh[i] = RGBAtoFloat(TORQUE_TEX2DLOD(atlasTex, float4(entryLoc,slotLoc,0,0))); } [unroll] @@ -119,15 +119,15 @@ inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, for (int y=0;y<4;y++) { entryLoc+=entryStep; - probeInfo.xform[x][y] = RGBAtoFloat(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); + probeInfo.xform[x][y] = decode32(TORQUE_TEX2DLOD(atlasTex, float4(entryLoc,slotLoc,0,0))); } } - //probeInfo.xform[3].xyz = float3(1,1,0); + probeInfo.xform[3].z = 0; entryLoc+=entryStep; - probeInfo.probeConfigData = TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc)); + probeInfo.probeConfigData = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc,slotLoc,0,0)); entryLoc+=entryStep; - probeInfo.flags = RGBAtoInt(TORQUE_TEX2D(atlasTex, float2(entryLoc,slotLoc))); + probeInfo.flags = RGBAtoInt(TORQUE_TEX2DLOD(atlasTex, float4(entryLoc,slotLoc,0,0))); return probeInfo; } diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl index 463933c30b..911016984b 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl @@ -209,7 +209,7 @@ float4 main(PFXVertToPix IN) : SV_TARGET finalColor *= surface.ao; ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas), eyePosWorld,0,10); - float contribution = defineFauxSpaceInfluence(surface.P, probeInfo.xform, probeInfo.probeConfigData.a); + float contribution = defineFauxSpaceInfluence(surface.P, probeInfo.xform, probeInfo.probeConfigData.a); contribution = max(contribution,0); return lerp(float4(0,0,1,0), probeInfo.ambientCol,contribution); diff --git a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl index 58d3eed16e..fd0f8bc81e 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl @@ -30,6 +30,15 @@ static float M_PI_F = 3.14159265358979323846f; static float M_2PI_F = 6.28318530717958647692f; static float M_1OVER_PI_F = 0.31830988618f; +float decode32(float4 rgba) +{ + float Sign = 1.0 - step(128.0,rgba[0])*2.0; + float Exponent = 2.0 * (rgba[0] % 128.0) + step(128.0,rgba[1]) - 127.0; + float Mantissa = (rgba[1] % 128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); + float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + return Result; +} + float RGBAtoFloat(uint4 INrgba) { int convert = (INrgba.z << 24) | (INrgba.b << 16) | (INrgba.g << 8) | INrgba.r; From 64a100b5da899107aaa47f1a0bcf3faac5d8f6ae Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 26 Oct 2025 18:47:25 -0500 Subject: [PATCH 07/19] fixed converters using known inputs --- .../game/core/rendering/shaders/torque.hlsl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl index fd0f8bc81e..79fea96573 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl @@ -32,16 +32,22 @@ static float M_1OVER_PI_F = 0.31830988618f; float decode32(float4 rgba) { - float Sign = 1.0 - step(128.0,rgba[0])*2.0; - float Exponent = 2.0 * (rgba[0] % 128.0) + step(128.0,rgba[1]) - 127.0; - float Mantissa = (rgba[1] % 128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000); - float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); + float Sign = 1.0 - step(128.0,rgba.a)*2.0; + float Exponent = 2.0 * (rgba.a % 128.0) + step(128.0,rgba.b) - 127.0; + float Mantissa = (rgba.b % 128.0)*65536.0 + rgba.g*256.0 +rgba.r + float(0x800000); + float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 )); return Result; } float RGBAtoFloat(uint4 INrgba) { - int convert = (INrgba.z << 24) | (INrgba.b << 16) | (INrgba.g << 8) | INrgba.r; + int convert = (INrgba.a << 24) | (INrgba.b << 16) | (INrgba.g << 8) | INrgba.r; + return asfloat(convert); +} + +float faux5() +{ + int convert = (64 << 24) | (160 << 16) | (0<< 8) | 0; return asfloat(convert); } From 945b233790a0074595719bcaf6747fc309d48dfd Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 3 Nov 2025 14:55:36 -0600 Subject: [PATCH 08/19] bmp hook for imageasset importing set atlas to use texture slot 7, and ssoa to use 8 testrun validation for u8 packing. findings: don't use inverse for testcase to allow for better verification. *do* ensurewe're writing floats, not doubles downcast --- Engine/source/T3D/assets/assetImporter.cpp | 2 +- .../source/renderInstance/renderProbeMgr.cpp | 44 +++++++++++-------- .../advanced/reflectionProbeArrayP.hlsl | 6 +-- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/Engine/source/T3D/assets/assetImporter.cpp b/Engine/source/T3D/assets/assetImporter.cpp index 3fb908cb82..2e96384fcc 100644 --- a/Engine/source/T3D/assets/assetImporter.cpp +++ b/Engine/source/T3D/assets/assetImporter.cpp @@ -813,7 +813,7 @@ String AssetImporter::getAssetTypeByFile(Torque::Path filePath) if (fileExt == String("dts") && fileName.endsWith("cached")) return ""; - if (fileExt == String("png") || fileExt == String("jpg") || fileExt == String("jpeg") || fileExt == String("dds")) + if (fileExt == String("png") || fileExt == String("jpg") || fileExt == String("jpeg") || fileExt == String("dds") || fileExt == String("bmp")) return "ImageAsset"; else if (fileExt == String("dae") || fileExt == String("fbx") || fileExt == String("blend") || fileExt == String("obj") || fileExt == String("dts") || fileExt == String("gltf") || fileExt == String("glb")) return "ShapeAsset"; diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index 2e2410f197..a36c1f0beb 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -885,32 +885,33 @@ void RenderProbeMgr::render( SceneRenderState *state ) mProbeArrayEffect->setCubemapArrayTexture(4, mPrefilterArray); mProbeArrayEffect->setCubemapArrayTexture(5, mIrradianceArray); mProbeArrayEffect->setTexture(6, mWetnessTexture); - //ssao mask - if (AdvancedLightBinManager::smUseSSAOMask) + + if (mProbeAtlas) { - //find ssaoMask - NamedTexTargetRef ssaoTarget = NamedTexTarget::find("ssaoMask"); - GFXTextureObject* pTexObj = ssaoTarget->getTexture(); - if (pTexObj) + if (mProbeAtlasTexture.set(mProbeAtlas, &GFXTexturePersistentProfile, false, "atlasTexture")) { - mProbeArrayEffect->setShaderMacro("USE_SSAO_MASK"); - mProbeArrayEffect->setTexture(7, pTexObj); + mProbeArrayEffect->setTexture(7, mProbeAtlasTexture); + //mProbeArrayEffect->setShaderConst("$numAtlasEntries", (S32)10); //make adaptive } } else { mProbeArrayEffect->setTexture(7, GFXTexHandle(NULL)); } - - if (mProbeAtlas) + //ssao mask + if (AdvancedLightBinManager::smUseSSAOMask) { - if (mProbeAtlasTexture.set(mProbeAtlas, &GFXTexturePersistentProfile, false, "atlasTexture")) + //find ssaoMask + NamedTexTargetRef ssaoTarget = NamedTexTarget::find("ssaoMask"); + GFXTextureObject* pTexObj = ssaoTarget->getTexture(); + if (pTexObj) { - mProbeArrayEffect->setTexture(8, mProbeAtlasTexture); - //mProbeArrayEffect->setShaderConst("$numAtlasEntries", (S32)10); //make adaptive + mProbeArrayEffect->setShaderMacro("USE_SSAO_MASK"); + mProbeArrayEffect->setTexture(8, pTexObj); } } + mProbeArrayEffect->setShaderConst("$numProbes", (S32)mProbeData.effectiveProbeCount); mProbeArrayEffect->setShaderConst("$skylightCubemapIdx", (S32)mProbeData.skyLightIdx); mProbeArrayEffect->setShaderConst(ShaderGenVars::skylightDamp, mProbeData.skyLightDamp); @@ -972,8 +973,9 @@ struct ProbeSerialize void RenderProbeMgr::serializeProbes() { U32 probeDataLength = (U32)(sizeof(ProbeSerialize) / 4); - U32 count = 10; - ProbeSerialize* saveBuffer = (ProbeSerialize*)dMalloc(sizeof(ProbeSerialize) * count); + U32 count = 1; + U32 probeAllocSize = sizeof(ProbeSerialize) * count; + ProbeSerialize* saveBuffer = (ProbeSerialize*)dMalloc(probeAllocSize); for (U32 i = 0; i < count; i++) { ProbeSerialize pSer; @@ -981,9 +983,8 @@ void RenderProbeMgr::serializeProbes() { pSer.sh[SHID]= 0.0f; } - MatrixF inmat = MatrixF(Point3F(45,0,0), Point3F(5 + i * 10, 5 + i * 10, 0)); + MatrixF inmat = MatrixF(Point3F(0,0,0), Point3F(5.0f + i * 10, 5.0f + i * 10, 0)); inmat.scale(10); - inmat.inverse(); for (U32 x = 0; x < 4; x++) { for (U32 y = 0; y < 4; y++) @@ -997,6 +998,12 @@ void RenderProbeMgr::serializeProbes() if (mProbeAtlas) delete(mProbeAtlas); + U8* checkPack = (U8*)dMalloc(probeAllocSize); + memcpy(checkPack, saveBuffer, probeAllocSize); + for (U32 i = 0; i < probeAllocSize / count; i+=4) + { + Con::warnf("packed: (%i,%i,%i,%i)", checkPack[i], checkPack[i+1], checkPack[i+2], checkPack[i+3]); + } mProbeAtlas = new GBitmap(probeDataLength, count, (U8*)saveBuffer); mProbeAtlas->writeBitmap("bmp", "probeinfoAtlas.bmp"); @@ -1008,6 +1015,8 @@ F32 RenderProbeMgr::unpackF32(ColorI in) U8 convert[4]{ in.red, in.green, in.blue, in.alpha}; F32 out; std::memcpy(&out, &convert, sizeof(F32)); + if (in.alpha == 64) Con::warnf("FOUND 64 in ALPHA!"); + if (in.red == 64) Con::warnf("FOUND 64 in RED!"); return out; } @@ -1043,7 +1052,6 @@ void RenderProbeMgr::testProbeAtlas() inmat(x, y) = check.xForm[x][y]; } } - inmat.inverse(); mProbeAtlas->getColor(++i, 0, tCol); check.type = tCol.red; check.radius = tCol.blue; diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl index 911016984b..9d67195640 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl @@ -22,13 +22,13 @@ TORQUE_UNIFORM_SAMPLERCUBEARRAY(specularCubemapAR, 4); TORQUE_UNIFORM_SAMPLERCUBEARRAY(irradianceCubemapAR, 5); TORQUE_UNIFORM_SAMPLER2D(WetnessTexture, 6); +TORQUE_UNIFORM_SAMPLER2D(probeAtlas, 7); + #ifdef USE_SSAO_MASK -TORQUE_UNIFORM_SAMPLER2D(ssaoMask, 7); +TORQUE_UNIFORM_SAMPLER2D(ssaoMask, 8); uniform float4 rtParams7; #endif -TORQUE_UNIFORM_SAMPLER2D(probeAtlas, 8); - uniform float accumTime; uniform float dampness; From 58d9306e730d417516bfe2cfc427db262201e5f5 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 4 Nov 2025 13:14:36 -0600 Subject: [PATCH 09/19] further unpacking work --- .../source/renderInstance/renderProbeMgr.cpp | 4 +-- .../game/core/rendering/shaders/lighting.hlsl | 31 ++++++++++--------- .../advanced/reflectionProbeArrayP.hlsl | 8 ++--- .../game/core/rendering/shaders/torque.hlsl | 2 +- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index a36c1f0beb..712a2ca92e 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -960,7 +960,7 @@ void RenderProbeMgr::render( SceneRenderState *state ) #pragma pack(push, 1) struct ProbeSerialize { - U8 ambientCol[4]{255,0,0,255}; + U8 ambientCol[4]{0,255,0,255}; F32 sh[9]; F32 xForm[4][4]; U8 type = 0;//(U8)(ProbeInfo::Box); @@ -983,7 +983,7 @@ void RenderProbeMgr::serializeProbes() { pSer.sh[SHID]= 0.0f; } - MatrixF inmat = MatrixF(Point3F(0,0,0), Point3F(5.0f + i * 10, 5.0f + i * 10, 0)); + MatrixF inmat = MatrixF(Point3F(0,0,0), Point3F(5.0f + i * 10, 5.0f + i * 10, 0.0f)); inmat.scale(10); for (U32 x = 0; x < 4; x++) { diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index 3b4d09177a..a19b7f3a09 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -101,33 +101,34 @@ inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, ProbeInfo probeInfo = (ProbeInfo)0; float slotLoc = slot/atlasLen; - float entryLoc = 0; - float entryStep = 1/(112/4-1); - probeInfo.ambientCol = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc,slotLoc,0,0)); - int i; + float entryLoc[28]; + for (int locPos; locPos<28;locPos++) + { + entryLoc[locPos] = locPos/28.0; + } + int entry = 0; + probeInfo.ambientCol = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)); [unroll] - for (i=0;i<9;i++) + for (int shID=0;shID<9;shID++) { - entryLoc+=entryStep; - probeInfo.sh[i] = RGBAtoFloat(TORQUE_TEX2DLOD(atlasTex, float4(entryLoc,slotLoc,0,0))); + entry++; + probeInfo.sh[shID] = RGBAtoFloat(TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0))*512); } - [unroll] for (int x=0;x<4;x++) { [unroll] for (int y=0;y<4;y++) { - entryLoc+=entryStep; - probeInfo.xform[x][y] = decode32(TORQUE_TEX2DLOD(atlasTex, float4(entryLoc,slotLoc,0,0))); + entry++; + probeInfo.xform[y][x] = decode32(uint4(floor(TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0))*512))); } } - probeInfo.xform[3].z = 0; - entryLoc+=entryStep; - probeInfo.probeConfigData = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc,slotLoc,0,0)); + entry++; + probeInfo.probeConfigData = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)); - entryLoc+=entryStep; - probeInfo.flags = RGBAtoInt(TORQUE_TEX2DLOD(atlasTex, float4(entryLoc,slotLoc,0,0))); + entry++; + probeInfo.flags = RGBAtoInt(TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0))*512); return probeInfo; } diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl index 9d67195640..f97d4969e2 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl @@ -198,9 +198,9 @@ float4 main(PFXVertToPix IN) : SV_TARGET float2 envBRDF = TORQUE_TEX2DLOD(BRDFTexture, float4(surface.NdotV, surface.roughness,0,0)).rg; float3 diffuse = irradiance * lerp(surface.baseColor.rgb, float3(0.04f,0.04f,0.04f), surface.metalness); - float3 specularCol = (specular * surface.f0 * envBRDF.x + envBRDF.y)*surface.metalness; + float3 specularCol = (specular * surface.f0 * envBRDF.x + envBRDF.y)*surface.metalness; - float horizonOcclusion = 1.3; + float horizonOcclusion = 1.3; float horizon = saturate( 1 + horizonOcclusion * dot(surface.R, surface.N)); horizon *= horizon; @@ -208,10 +208,10 @@ float4 main(PFXVertToPix IN) : SV_TARGET float3 finalColor = diffuse + specularCol*horizon; finalColor *= surface.ao; - ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas), eyePosWorld,0,10); + ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas), eyePosWorld,0,1); float contribution = defineFauxSpaceInfluence(surface.P, probeInfo.xform, probeInfo.probeConfigData.a); contribution = max(contribution,0); - return lerp(float4(0,0,1,0), probeInfo.ambientCol,contribution); + return lerp(float4(0,0,1,0), probeInfo.ambientCol,contribution); if(isCapturing == 1) return float4(lerp(finalColor, surface.baseColor.rgb,surface.metalness),0); diff --git a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl index 79fea96573..a1bd772a49 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/torque.hlsl @@ -30,7 +30,7 @@ static float M_PI_F = 3.14159265358979323846f; static float M_2PI_F = 6.28318530717958647692f; static float M_1OVER_PI_F = 0.31830988618f; -float decode32(float4 rgba) +float decode32(uint4 rgba) { float Sign = 1.0 - step(128.0,rgba.a)*2.0; float Exponent = 2.0 * (rgba.a % 128.0) + step(128.0,rgba.b) - 127.0; From 401c76355a858a675c7daa0b276d220780700e92 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 5 Nov 2025 17:09:14 -0600 Subject: [PATCH 10/19] bunch of bitmap changes from mar. preliminary R32 format support shader shifted to direct float lookups probe atlas in an r32 'dds' experiment todo: gmap variant --- Engine/source/gfx/bitmap/bitmapUtils.cpp | 201 +++----- Engine/source/gfx/bitmap/bitmapUtils.h | 107 ++++- Engine/source/gfx/bitmap/ddsData.h | 9 +- Engine/source/gfx/bitmap/ddsFile.cpp | 106 +++-- Engine/source/gfx/bitmap/ddsFile.h | 2 + Engine/source/gfx/bitmap/gBitmap.cpp | 443 ++++++++++++------ Engine/source/gfx/bitmap/gBitmap.h | 53 ++- Engine/source/gfx/bitmap/imageUtils.cpp | 2 +- .../source/gfx/bitmap/loaders/bitmapSTB.cpp | 219 ++++----- Engine/source/platformWin32/winAsmBlit.cpp | 2 +- .../source/renderInstance/renderProbeMgr.cpp | 84 +++- Engine/source/renderInstance/renderProbeMgr.h | 1 + .../scripts/advancedLighting_Shaders.tscript | 2 +- .../game/core/rendering/shaders/lighting.hlsl | 31 +- .../game/core/rendering/shaders/torque.hlsl | 2 +- Templates/BaseGame/game/data/defaults.tscript | 4 +- 16 files changed, 782 insertions(+), 486 deletions(-) diff --git a/Engine/source/gfx/bitmap/bitmapUtils.cpp b/Engine/source/gfx/bitmap/bitmapUtils.cpp index 3dd6388529..a9206d7c0e 100644 --- a/Engine/source/gfx/bitmap/bitmapUtils.cpp +++ b/Engine/source/gfx/bitmap/bitmapUtils.cpp @@ -67,7 +67,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 +81,80 @@ 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; - - if (srcWidth != 1) - { - for(U32 y = 0; y < height; y++) - { - for(U32 x = 0; x < width; x++) - { - *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; - } - src += stride; // skip - } - } - else - { - 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 - } - } -} - -//-------------------------------------------------------------------------- -void bitmapExtrudeRGBA_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) * 4 : 0; - - U32 width = srcWidth >> 1; - U32 height = srcHeight >> 1; - if (width == 0) width = 1; - if (height == 0) height = 1; - if (srcWidth != 1) - { - 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 - } - } - else - { - 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 - } - } -} - -void bitmapExtrudeFPRGBA_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) +template +void bitmapExtrudeGeneric( + const T* src, T* dst, + U32 srcWidth, U32 srcHeight, + U32 channels, U32 bpp) { - const U16 *src = (const U16 *)srcMip; - U16 *dst = (U16 *)mip; - U32 stride = srcHeight != 1 ? (srcWidth) * 8 : 0; - - U32 width = srcWidth >> 1; - U32 height = srcHeight >> 1; - if (width == 0) width = 1; - if (height == 0) height = 1; + 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[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; + 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 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 - { - 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 } } } -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; - +// 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; //-------------------------------------------------------------------------- @@ -238,7 +167,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 +189,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; diff --git a/Engine/source/gfx/bitmap/bitmapUtils.h b/Engine/source/gfx/bitmap/bitmapUtils.h index 489a8f296f..ce860b39af 100644 --- a/Engine/source/gfx/bitmap/bitmapUtils.h +++ b/Engine/source/gfx/bitmap/bitmapUtils.h @@ -22,21 +22,118 @@ #ifndef _BITMAPUTILS_H_ #define _BITMAPUTILS_H_ - +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif #ifndef _TORQUE_TYPES_H_ #include "platform/types.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 (*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)); + } +} #endif //_BITMAPUTILS_H_ diff --git a/Engine/source/gfx/bitmap/ddsData.h b/Engine/source/gfx/bitmap/ddsData.h index ac49e4392b..bededb8e51 100644 --- a/Engine/source/gfx/bitmap/ddsData.h +++ b/Engine/source/gfx/bitmap/ddsData.h @@ -281,6 +281,7 @@ namespace dds #define DDS_MAGIC 0x20534444 // "DDS " #define DDS_FOURCC 0x00000004 // DDPF_FOURCC #define DDS_RGB 0x00000040 // DDPF_RGB + #define DDS_R32F 0x00000072 // R32F Fourcc #define DDS_RGBA 0x00000041 // DDPF_RGB | DDPF_ALPHAPIXELS #define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE #define DDS_LUMINANCEA 0x00020001 // DDPF_LUMINANCE | DDPF_ALPHAPIXELS @@ -517,6 +518,9 @@ namespace dds const DDS_PIXELFORMAT DDSPF_V16U16 = { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }; + const DDS_PIXELFORMAT DDSPF_F32 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, DDS_R32F, 32, 0, 0, 0, 0 }; + // D3DFMT_A2R10G10B10/D3DFMT_A2B10G10R10 should be written using DX10 extension to avoid D3DX 10:10:10:2 reversal issue // This indicates the DDS_HEADER_DXT10 extension is present (the format is in dxgiFormat) @@ -547,6 +551,7 @@ namespace dds case GFXFormatB8G8R8A8: return DDSPF_A8B8G8R8; case GFXFormatR16G16B16A16F: case GFXFormatR32G32B32A32F: return DDSPF_DX10; + case GFXFormatR32F: return DDSPF_F32; //compressed case GFXFormatBC1: return DDSPF_DXT1; case GFXFormatBC2: return DDSPF_DXT3; @@ -712,6 +717,8 @@ namespace dds return GFXFormatR5G6B5; else if (format == DDSPF_A1R5G5B5) return GFXFormatR5G5B5A1; + else if (format == DDSPF_F32) + return GFXFormatR32F; else { Con::errorf("dds::getGFXFormat: unknown format"); @@ -905,4 +912,4 @@ namespace dds } -#endif \ No newline at end of file +#endif diff --git a/Engine/source/gfx/bitmap/ddsFile.cpp b/Engine/source/gfx/bitmap/ddsFile.cpp index f204450587..b518af9489 100644 --- a/Engine/source/gfx/bitmap/ddsFile.cpp +++ b/Engine/source/gfx/bitmap/ddsFile.cpp @@ -652,6 +652,12 @@ Resource 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; diff --git a/Engine/source/gfx/bitmap/ddsFile.h b/Engine/source/gfx/bitmap/ddsFile.h index 84b515f86e..5ad0a1baa0 100644 --- a/Engine/source/gfx/bitmap/ddsFile.h +++ b/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); diff --git a/Engine/source/gfx/bitmap/gBitmap.cpp b/Engine/source/gfx/bitmap/gBitmap.cpp index 4c85537ad0..d97777a4b9 100644 --- a/Engine/source/gfx/bitmap/gBitmap.cpp +++ b/Engine/source/gfx/bitmap/gBitmap.cpp @@ -49,9 +49,11 @@ GBitmap::GBitmap() mHeight(0), mBytesPerPixel(0), mNumMipLevels(0), - mHasTransparency(false) + mHasTransparency(false), + mNumFaces(1) { std::fill_n(mMipLevelOffsets, c_maxMipLevels, 0xffffffff); + std::fill_n(mFaceOffsets, 6, 0xffffffff); } GBitmap::GBitmap(const GBitmap& rCopy) @@ -67,33 +69,42 @@ GBitmap::GBitmap(const GBitmap& rCopy) mBytesPerPixel = rCopy.mBytesPerPixel; mNumMipLevels = rCopy.mNumMipLevels; dMemcpy(mMipLevelOffsets, rCopy.mMipLevelOffsets, sizeof(mMipLevelOffsets)); + dMemcpy(mFaceOffsets, rCopy.mFaceOffsets, sizeof(mFaceOffsets)); mHasTransparency = rCopy.mHasTransparency; + mNumFaces = rCopy.mNumFaces; } GBitmap::GBitmap(const U32 in_width, const U32 in_height, const bool in_extrudeMipLevels, - const GFXFormat in_format) + const GFXFormat in_format, + const U32 in_numFaces) : mBits(NULL), - mByteSize(0) + mByteSize(0), + mNumFaces(in_numFaces) { for (U32 i = 0; i < c_maxMipLevels; i++) mMipLevelOffsets[i] = 0xffffffff; - allocateBitmap(in_width, in_height, in_extrudeMipLevels, in_format); + for(U32 i = 0; i < 6; i++) + mFaceOffsets[i] = 0xffffffff; + + allocateBitmap(in_width, in_height, in_extrudeMipLevels, in_format, in_numFaces); mHasTransparency = false; } GBitmap::GBitmap(const U32 in_width, const U32 in_height, - const U8* data ) + const U8* data, + const U32 in_numFaces) : mBits(NULL), - mByteSize(0) + mByteSize(0), + mNumFaces(in_numFaces) { - allocateBitmap(in_width, in_height, false, GFXFormatR8G8B8A8); + allocateBitmap(in_width, in_height, false, GFXFormatR8G8B8A8, in_numFaces); mHasTransparency = false; @@ -126,6 +137,65 @@ GBitmap::~GBitmap() //-------------------------------------------------------------------------- +U32 GBitmap::getFormatBytesPerPixel(GFXFormat fmt) +{ + switch (fmt) + { + // 8-bit formats + case GFXFormatA8: + case GFXFormatL8: + case GFXFormatA4L4: + return 1; + + // 16-bit formats + case GFXFormatR5G6B5: + case GFXFormatR5G5B5A1: + case GFXFormatR5G5B5X1: + case GFXFormatA8L8: + case GFXFormatL16: + case GFXFormatR16F: + case GFXFormatR16G16: + case GFXFormatR16G16F: + case GFXFormatD16: + return 2; + + // 24-bit formats + case GFXFormatR8G8B8: + case GFXFormatR8G8B8_SRGB: + return 3; + + // 32-bit formats + case GFXFormatR8G8B8A8: + case GFXFormatR8G8B8X8: + case GFXFormatB8G8R8A8: + case GFXFormatR8G8B8A8_SRGB: + case GFXFormatR32F: + case GFXFormatR10G10B10A2: + case GFXFormatR11G11B10: + case GFXFormatD24X8: + case GFXFormatD24S8: + case GFXFormatD24FS8: + case GFXFormatR8G8B8A8_LINEAR_FORCE: + return 4; + + // 64-bit formats + case GFXFormatR16G16B16A16: + case GFXFormatR16G16B16A16F: + case GFXFormatD32FS8X24: + return 8; + + // 128-bit formats + case GFXFormatR32G32B32A32F: + return 16; + + default: + AssertWarn(false, "getFormatBytesPerPixel() - Unknown or compressed format"); + return 4; + } +} + +//-------------------------------------------------------------------------- + void GBitmap::sRegisterFormat( const GBitmap::Registration ® ) { U32 insert = sRegistrations.size(); @@ -268,7 +338,7 @@ void GBitmap::copyRect(const GBitmap *src, const RectI &srcRect, const Point2I & } //-------------------------------------------------------------------------- -void GBitmap::allocateBitmap(const U32 in_width, const U32 in_height, const bool in_extrudeMipLevels, const GFXFormat in_format ) +void GBitmap::allocateBitmap(const U32 in_width, const U32 in_height, const bool in_extrudeMipLevels, const GFXFormat in_format, const U32 in_numFaces) { //-------------------------------------- Some debug checks... U32 svByteSize = mByteSize; @@ -284,37 +354,14 @@ void GBitmap::allocateBitmap(const U32 in_width, const U32 in_height, const bool mInternalFormat = in_format; mWidth = in_width; mHeight = in_height; + mNumFaces = in_numFaces; - mBytesPerPixel = 1; - switch (mInternalFormat) - { - case GFXFormatA8: - case GFXFormatL8: mBytesPerPixel = 1; - break; - case GFXFormatR8G8B8: mBytesPerPixel = 3; - break; - case GFXFormatR8G8B8A8_LINEAR_FORCE: - case GFXFormatR8G8B8X8: - case GFXFormatR8G8B8A8: mBytesPerPixel = 4; - break; - case GFXFormatL16: - case GFXFormatR5G6B5: - case GFXFormatR5G5B5A1: mBytesPerPixel = 2; - break; - case GFXFormatR16G16B16A16F: - case GFXFormatR16G16B16A16: mBytesPerPixel = 8; - break; - default: - AssertFatal(false, "GBitmap::GBitmap: misunderstood format specifier"); - break; - } + mBytesPerPixel = getFormatBytesPerPixel(mInternalFormat); // Set up the mip levels, if necessary... mNumMipLevels = 1; - U32 allocPixels = in_width * in_height * mBytesPerPixel; mMipLevelOffsets[0] = 0; - if (in_extrudeMipLevels == true) { U32 currWidth = in_width; @@ -330,7 +377,6 @@ void GBitmap::allocateBitmap(const U32 in_width, const U32 in_height, const bool if (currHeight == 0) currHeight = 1; mNumMipLevels++; - allocPixels += currWidth * currHeight * mBytesPerPixel; } U32 expectedMips = mFloor(mLog2(mMax(in_width, in_height))) + 1; @@ -338,8 +384,17 @@ void GBitmap::allocateBitmap(const U32 in_width, const U32 in_height, const bool } AssertFatal(mNumMipLevels <= c_maxMipLevels, "GBitmap::allocateBitmap: too many miplevels"); + U32 faceStride = 0; + for (U32 mip = 0; mip < mNumMipLevels; mip++) + faceStride += getWidth(mip) * getHeight(mip) * mBytesPerPixel; + + for (U32 face = 0; face < mNumFaces; face++) + mFaceOffsets[face] = face * faceStride; + + U32 allocBytes = faceStride * mNumFaces; + // Set up the memory... - mByteSize = allocPixels; + mByteSize = allocBytes; mBits = new U8[mByteSize]; dMemset(mBits, 0xFF, mByteSize); @@ -352,7 +407,7 @@ void GBitmap::allocateBitmap(const U32 in_width, const U32 in_height, const bool } //-------------------------------------------------------------------------- -void GBitmap::allocateBitmapWithMips(const U32 in_width, const U32 in_height, const U32 in_numMips, const GFXFormat in_format) +void GBitmap::allocateBitmapWithMips(const U32 in_width, const U32 in_height, const U32 in_numMips, const GFXFormat in_format, const U32 in_numFaces) { //-------------------------------------- Some debug checks... U32 svByteSize = mByteSize; @@ -363,36 +418,14 @@ void GBitmap::allocateBitmapWithMips(const U32 in_width, const U32 in_height, co mInternalFormat = in_format; mWidth = in_width; mHeight = in_height; + mNumFaces = in_numFaces; - mBytesPerPixel = 1; - switch (mInternalFormat) - { - case GFXFormatA8: - case GFXFormatL8: mBytesPerPixel = 1; - break; - case GFXFormatR8G8B8: mBytesPerPixel = 3; - break; - case GFXFormatR8G8B8X8: - case GFXFormatR8G8B8A8: mBytesPerPixel = 4; - break; - case GFXFormatL16: - case GFXFormatR5G6B5: - case GFXFormatR5G5B5A1: mBytesPerPixel = 2; - break; - case GFXFormatR16G16B16A16F: - case GFXFormatR16G16B16A16: mBytesPerPixel = 8; - break; - default: - AssertFatal(false, "GBitmap::GBitmap: misunderstood format specifier"); - break; - } + mBytesPerPixel = getFormatBytesPerPixel(mInternalFormat); // Set up the mip levels, if necessary... mNumMipLevels = 1; - U32 allocPixels = in_width * in_height * mBytesPerPixel; mMipLevelOffsets[0] = 0; - if (in_numMips != 0) { U32 currWidth = in_width; @@ -408,13 +441,21 @@ void GBitmap::allocateBitmapWithMips(const U32 in_width, const U32 in_height, co if (currHeight == 0) currHeight = 1; mNumMipLevels++; - allocPixels += currWidth * currHeight * mBytesPerPixel; } while ((currWidth != 1 || currHeight != 1) && (mNumMipLevels != in_numMips)); } AssertFatal(mNumMipLevels <= c_maxMipLevels, "GBitmap::allocateBitmap: too many miplevels"); + U32 faceStride = 0; + for (U32 mip = 0; mip < mNumMipLevels; mip++) + faceStride += getWidth(mip) * getHeight(mip) * mBytesPerPixel; + + for (U32 face = 0; face < mNumFaces; face++) + mFaceOffsets[face] = face * faceStride; + + U32 allocBytes = faceStride * mNumFaces; + // Set up the memory... - mByteSize = allocPixels; + mByteSize = allocBytes; mBits = new U8[mByteSize]; dMemset(mBits, 0xFF, mByteSize); @@ -444,26 +485,43 @@ void GBitmap::extrudeMipLevels(bool clearBorders) case GFXFormatR8G8B8: { for(U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1)); + bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1), mBytesPerPixel); break; } case GFXFormatR8G8B8A8: case GFXFormatR8G8B8X8: + case GFXFormatB8G8R8A8: + case GFXFormatR8G8B8A8_SRGB: { for(U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrudeRGBA(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1)); + bitmapExtrudeRGBA(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1), mBytesPerPixel); + break; + } + + case GFXFormatR16G16B16A16: + { + for (U32 i = 1; i < mNumMipLevels; i++) + bitmapExtrude16BitRGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1), mBytesPerPixel); break; } case GFXFormatR16G16B16A16F: { for (U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrudeFPRGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1)); + bitmapExtrudeFPRGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1), mBytesPerPixel); break; } - + + case GFXFormatR32G32B32A32F: + { + for (U32 i = 1; i < mNumMipLevels; i++) + bitmapExtrudeF32RGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1), mBytesPerPixel); + break; + } + default: + Con::warnf("GBitmap::extrudeMipLevels() - Unsupported format %d", getFormat()); break; } if (clearBorders) @@ -538,7 +596,7 @@ void GBitmap::extrudeMipLevelsDetail() allocateBitmap(getWidth(), getHeight(), true, getFormat()); for (i = 1; i < mNumMipLevels; i++) { - bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1)); + bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1), mBytesPerPixel); } // Ok, now that we have the levels extruded, we need to move the lower miplevels @@ -688,40 +746,43 @@ bool GBitmap::checkForTransparency() { mHasTransparency = false; + if (!mBits || mByteSize == 0) + return false; + ColorI pixel(255, 255, 255, 255); + // Only check formats that can *possibly* have alpha. switch (mInternalFormat) { - // Non-transparent formats - case GFXFormatL8: - case GFXFormatL16: - case GFXFormatR8G8B8: - case GFXFormatR5G6B5: - break; - // Transparent formats - case GFXFormatA8: - case GFXFormatR8G8B8A8: - case GFXFormatR5G5B5A1: - // Let getColor() do the heavy lifting - for (U32 x = 0; x < mWidth; x++) + case GFXFormatA8: + case GFXFormatA4L4: + case GFXFormatA8L8: + case GFXFormatR5G5B5A1: + case GFXFormatR8G8B8A8: + case GFXFormatB8G8R8A8: + case GFXFormatR8G8B8A8_SRGB: + case GFXFormatR10G10B10A2: + case GFXFormatR16G16B16A16: + case GFXFormatR16G16B16A16F: + case GFXFormatR32G32B32A32F: + break; // alpha-capable + default: + return false; // skip formats with no alpha + } + + for (U32 x = 0; x < mWidth; x++) + { + for (U32 y = 0; y < mHeight; y++) + { + if (getColor(x, y, pixel)) { - for (U32 y = 0; y < mHeight; y++) + if (pixel.alpha < 255) { - if (getColor(x, y, pixel)) - { - if (pixel.alpha < 255) - { - mHasTransparency = true; - break; - } - } + mHasTransparency = true; + break; } } - - break; - default: - AssertFatal(false, "GBitmap::checkForTransparency: misunderstood format specifier"); - break; + } } return mHasTransparency; @@ -770,40 +831,144 @@ bool GBitmap::getColor(const U32 x, const U32 y, ColorI& rColor) const if (x >= mWidth || y >= mHeight) return false; - const U8* pLoc = getAddress(x, y); + const U8* p = getAddress(x, y); - switch (mInternalFormat) { - case GFXFormatA8: - case GFXFormatL8: - rColor.set( *pLoc, *pLoc, *pLoc, *pLoc ); + switch (mInternalFormat) + { + // --- 8-bit --- + case GFXFormatA8: + rColor.set(255, 255, 255, p[0]); break; - case GFXFormatL16: - rColor.set(U8(U16((pLoc[0] << 8) + pLoc[1])), 0, 0, 0); - break; - case GFXFormatR8G8B8: - case GFXFormatR8G8B8X8: - rColor.set( pLoc[0], pLoc[1], pLoc[2], 255 ); + + case GFXFormatL8: + rColor.set(p[0], p[0], p[0], 255); break; - case GFXFormatR8G8B8A8: - rColor.set( pLoc[0], pLoc[1], pLoc[2], pLoc[3] ); + case GFXFormatA4L4: + { + U8 v = p[0]; + U8 lum = (v & 0x0F) * 17; + U8 alp = ((v >> 4) & 0x0F) * 17; + rColor.set(lum, lum, lum, alp); break; + } - case GFXFormatR5G5B5A1: -#if defined(TORQUE_OS_MAC) - rColor.set( (*((U16*)pLoc) >> 0) & 0x1F, - (*((U16*)pLoc) >> 5) & 0x1F, - (*((U16*)pLoc) >> 10) & 0x1F, - ((*((U16*)pLoc) >> 15) & 0x01) ? 255 : 0 ); + // --- 16-bit --- + case GFXFormatR5G6B5: + { + U16 c = ((U16*)p)[0]; +#ifdef TORQUE_BIG_ENDIAN + c = convertLEndianToHost(c); +#endif + U8 r = (c >> 11) & 0x1F; + U8 g = (c >> 5) & 0x3F; + U8 b = c & 0x1F; + rColor.set((r << 3) | (r >> 2), + (g << 2) | (g >> 4), + (b << 3) | (b >> 2), + 255); + break; + } + + case GFXFormatR5G5B5A1: + { + U16 c = ((U16*)p)[0]; +#ifdef TORQUE_BIG_ENDIAN + c = convertLEndianToHost(c); +#endif + U8 r = (c >> 11) & 0x1F; + U8 g = (c >> 6) & 0x1F; + U8 b = (c >> 1) & 0x1F; + U8 a = (c & 0x01) ? 255 : 0; + rColor.set((r << 3) | (r >> 2), + (g << 3) | (g >> 2), + (b << 3) | (b >> 2), + a); + break; + } + + case GFXFormatA8L8: + { + U16 c = ((U16*)p)[0]; +#ifdef TORQUE_BIG_ENDIAN + c = convertLEndianToHost(c); +#endif + U8 l = c & 0xFF; + U8 a = (c >> 8) & 0xFF; + rColor.set(l, l, l, a); + break; + } + + case GFXFormatL16: + { + U16 l = ((U16*)p)[0]; +#ifdef TORQUE_BIG_ENDIAN + l = convertLEndianToHost(l); +#endif + U8 lum = l >> 8; + rColor.set(lum, lum, lum, 255); + break; + } + + // --- 24-bit --- + case GFXFormatR8G8B8: + case GFXFormatR8G8B8_SRGB: + rColor.set(p[0], p[1], p[2], 255); + break; + + // --- 32-bit --- + case GFXFormatR8G8B8A8: + case GFXFormatR8G8B8A8_SRGB: + rColor.set(p[0], p[1], p[2], p[3]); + break; + + case GFXFormatB8G8R8A8: + rColor.set(p[2], p[1], p[0], p[3]); + break; + + case GFXFormatR8G8B8X8: + rColor.set(p[0], p[1], p[2], 255); + break; + + // --- 64-bit (16 bits per channel) --- + case GFXFormatR16G16B16A16: + { + const U16* v = (U16*)p; +#ifdef TORQUE_BIG_ENDIAN + rColor.set(v[2] >> 8, v[1] >> 8, v[0] >> 8, v[3] >> 8); // fallback #else - rColor.set( *((U16*)pLoc) >> 11, - (*((U16*)pLoc) >> 6) & 0x1f, - (*((U16*)pLoc) >> 1) & 0x1f, - (*((U16*)pLoc) & 1) ? 255 : 0 ); + rColor.set(v[0] >> 8, v[1] >> 8, v[2] >> 8, v[3] >> 8); #endif break; + } - default: + // --- 64-bit float --- + case GFXFormatR16G16B16A16F: + { + const U16* v = (U16*)p; + F32 r = convertHalfToFloat(v[0]); + F32 g = convertHalfToFloat(v[1]); + F32 b = convertHalfToFloat(v[2]); + F32 a = convertHalfToFloat(v[3]); + rColor.set(mClamp(r * 255.0f, 0.0f, 255.0f), + mClamp(g * 255.0f, 0.0f, 255.0f), + mClamp(b * 255.0f, 0.0f, 255.0f), + mClamp(a * 255.0f, 0.0f, 255.0f)); + break; + } + + // --- 128-bit float --- + case GFXFormatR32G32B32A32F: + { + const F32* v = (F32*)p; + rColor.set(mClamp(v[0] * 255.0f, 0.0f, 255.0f), + mClamp(v[1] * 255.0f, 0.0f, 255.0f), + mClamp(v[2] * 255.0f, 0.0f, 255.0f), + mClamp(v[3] * 255.0f, 0.0f, 255.0f)); + break; + } + + default: AssertFatal(false, "Bad internal format"); return false; } @@ -1124,6 +1289,7 @@ void GBitmap::copyChannel( U32 index, GBitmap *outBitmap ) const bool GBitmap::read(Stream& io_rStream) { + PROFILE_SCOPE(GBitmap_Read); // Handle versioning U32 version; io_rStream.read(&version); @@ -1133,23 +1299,7 @@ bool GBitmap::read(Stream& io_rStream) U32 fmt; io_rStream.read(&fmt); mInternalFormat = GFXFormat(fmt); - mBytesPerPixel = 1; - switch (mInternalFormat) { - case GFXFormatA8: - case GFXFormatL8: mBytesPerPixel = 1; - break; - case GFXFormatR8G8B8: mBytesPerPixel = 3; - break; - case GFXFormatR8G8B8A8: mBytesPerPixel = 4; - break; - case GFXFormatL16: - case GFXFormatR5G6B5: - case GFXFormatR5G5B5A1: mBytesPerPixel = 2; - break; - default: - AssertFatal(false, "GBitmap::read: misunderstood format specifier"); - break; - } + mBytesPerPixel = getFormatBytesPerPixel(mInternalFormat); io_rStream.read(&mByteSize); @@ -1170,6 +1320,7 @@ bool GBitmap::read(Stream& io_rStream) bool GBitmap::write(Stream& io_rStream) const { + PROFILE_SCOPE(GBitmap_Write); // Handle versioning io_rStream.write(csFileVersion); @@ -1266,8 +1417,31 @@ template<> void *Resource::create(const Torque::Path &path) Con::printf( "Resource::create - [%s]", path.getFullPath().c_str() ); #endif + GBitmap* bmp = new GBitmap; FileStream stream; + Torque::Path dbm = path; + dbm.setExtension("dbm"); + if (Torque::FS::IsFile(dbm)) + { + + Torque::FS::FileNodeRef assetFile = Torque::FS::GetFileNode(path); + Torque::FS::FileNodeRef compiledFile = Torque::FS::GetFileNode(dbm); + + if (assetFile != NULL && compiledFile != NULL) + { + if (compiledFile->getModifiedTime() >= assetFile->getModifiedTime()) + { +#ifdef TORQUE_DEBUG_RES_MANAGER + Con::printf("Resource::create - Loading cached image file: %s", dbm.getFullPath().c_str()); +#endif + stream.open(dbm.getFullPath(), Torque::FS::File::Read); + bmp->read(stream); + return bmp; + } + } + } + stream.open( path.getFullPath(), Torque::FS::File::Read ); if ( stream.getStatus() != Stream::Ok ) @@ -1276,7 +1450,6 @@ template<> void *Resource::create(const Torque::Path &path) return NULL; } - GBitmap *bmp = new GBitmap; const String extension = path.getExtension(); if( !bmp->readBitmap( extension, path ) ) { diff --git a/Engine/source/gfx/bitmap/gBitmap.h b/Engine/source/gfx/bitmap/gBitmap.h index 558c5ec102..4a32531f98 100644 --- a/Engine/source/gfx/bitmap/gBitmap.h +++ b/Engine/source/gfx/bitmap/gBitmap.h @@ -135,13 +135,15 @@ class GBitmap 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 @@ class GBitmap 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 @@ class GBitmap 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 @@ class GBitmap 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 @@ class GBitmap private: GFXFormat mInternalFormat; - + U32 mNumFaces; // default 1, set to 6 for cubemap U8* mBits; // Master bytes U32 mByteSize; U32 mWidth; @@ -284,6 +291,7 @@ class GBitmap 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 diff --git a/Engine/source/gfx/bitmap/imageUtils.cpp b/Engine/source/gfx/bitmap/imageUtils.cpp index 3426eecd3f..283b5c4069 100644 --- a/Engine/source/gfx/bitmap/imageUtils.cpp +++ b/Engine/source/gfx/bitmap/imageUtils.cpp @@ -24,7 +24,7 @@ #include "gfx/bitmap/imageUtils.h" #include "gfx/bitmap/ddsFile.h" #include "platform/threads/threadPool.h" -#include "squish/squish.h" +#include "squish.h" namespace ImageUtil { diff --git a/Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp b/Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp index a0e8cf9a8b..4a984539fe 100644 --- a/Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp +++ b/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" @@ -56,6 +57,43 @@ 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) + { + switch (numChannels) + { + case 1: return GFXFormatR32F; + case 2: return GFXFormatR16G16F; // approximate, most HDRs are 3 or 4 channel though + case 3: return GFXFormatR16G16B16A16F; // store RGB in RGBA + case 4: return GFXFormatR16G16B16A16F; + } + } + 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 +248,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); - } - - // 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(); + void* data = nullptr; - // 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, 4); + 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; + Con::errorf("sReadSTB() - Failed to load %s: %s", filePath, stbi_failure_reason()); + return false; } - unsigned char* data = stbi_load(path.getFullPath().c_str(), &x, &y, &n, channels); + // Determine internal GFX format + GFXFormat format = determineFormat(isHDR, is16Bit, n); + // Allocate bitmap bitmap->deleteImage(); + bitmap->allocateBitmap(x, y, false, format); - 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(); - return false; + 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(data)[i]); // convert F32 -> U16 + } + } + else + { + U8* dst = (U8*)bitmap->getBits(); + U32 byteSize = bitmap->getByteSize(); + dMemcpy(dst, data, byteSize); } - - // actually allocate the bitmap space... - bitmap->allocateBitmap(x, y, - false, // don't extrude miplevels... - format); // use determined format... - - U8* pBase = (U8*)bitmap->getBits(); - - U32 rowBytes = bitmap->getByteSize(); - - dMemcpy(pBase, data, rowBytes); stbi_image_free(data); - // Check this bitmap for transparency - if (channels == 4) - bitmap->checkForTransparency(); + + bitmap->checkForTransparency(); PROFILE_END(); return true; @@ -331,45 +305,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 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; } diff --git a/Engine/source/platformWin32/winAsmBlit.cpp b/Engine/source/platformWin32/winAsmBlit.cpp index 8dd6dc413a..c6b96488bc 100644 --- a/Engine/source/platformWin32/winAsmBlit.cpp +++ b/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) { diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index 712a2ca92e..97888f73a2 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -179,7 +179,8 @@ RenderProbeMgr::RenderProbeMgr() mSkylightDamp(true), mCubeMapCount(0), mUseHDRCaptures(true), - mProbeAtlas(NULL) + mProbeAtlas(NULL), + mProbeAtlasDDS(NULL) { mEffectiveProbeCount = 0; mMipCount = 0; @@ -886,9 +887,9 @@ void RenderProbeMgr::render( SceneRenderState *state ) mProbeArrayEffect->setCubemapArrayTexture(5, mIrradianceArray); mProbeArrayEffect->setTexture(6, mWetnessTexture); - if (mProbeAtlas) + if (mProbeAtlasDDS) { - if (mProbeAtlasTexture.set(mProbeAtlas, &GFXTexturePersistentProfile, false, "atlasTexture")) + if (mProbeAtlasTexture.set(mProbeAtlasDDS, &GFXTexturePersistentProfile, false, "atlasTexture")) { mProbeArrayEffect->setTexture(7, mProbeAtlasTexture); //mProbeArrayEffect->setShaderConst("$numAtlasEntries", (S32)10); //make adaptive @@ -957,22 +958,23 @@ void RenderProbeMgr::render( SceneRenderState *state ) // Make sure the effect is gonna render. getProbeArrayEffect()->setSkip(false); } -#pragma pack(push, 1) + struct ProbeSerialize { - U8 ambientCol[4]{0,255,0,255}; + F32 ambientCol[4]{0,1.0,0,1.0}; F32 sh[9]; F32 xForm[4][4]; - U8 type = 0;//(U8)(ProbeInfo::Box); - U8 radius = 5; - U8 scale = 10; - U8 attenuation = 0; + F32 offset[3]{ 0.0,0.0,0.0 }; + F32 type = 0;//(U8)(ProbeInfo::Box); + F32 radius = 5; + F32 scale = 10; + F32 attenuation = 0; U32 flags = BIT(0); //[0]canDamp }; -#pragma pack(pop) + +#define probeDataLength (U32)(sizeof(ProbeSerialize) / 4) void RenderProbeMgr::serializeProbes() { - U32 probeDataLength = (U32)(sizeof(ProbeSerialize) / 4); U32 count = 1; U32 probeAllocSize = sizeof(ProbeSerialize) * count; ProbeSerialize* saveBuffer = (ProbeSerialize*)dMalloc(probeAllocSize); @@ -983,7 +985,7 @@ void RenderProbeMgr::serializeProbes() { pSer.sh[SHID]= 0.0f; } - MatrixF inmat = MatrixF(Point3F(0,0,0), Point3F(5.0f + i * 10, 5.0f + i * 10, 0.0f)); + MatrixF inmat = MatrixF(Point3F(0,0,0), Point3F(-5.0f + i * 10, 100.0f + i * 10, 0.0f)); inmat.scale(10); for (U32 x = 0; x < 4; x++) { @@ -998,15 +1000,30 @@ void RenderProbeMgr::serializeProbes() if (mProbeAtlas) delete(mProbeAtlas); - U8* checkPack = (U8*)dMalloc(probeAllocSize); + F32* checkPack = (F32*)dMalloc(probeAllocSize); memcpy(checkPack, saveBuffer, probeAllocSize); - for (U32 i = 0; i < probeAllocSize / count; i+=4) + for (U32 i = 0; i < probeDataLength; i++) { - Con::warnf("packed: (%i,%i,%i,%i)", checkPack[i], checkPack[i+1], checkPack[i+2], checkPack[i+3]); + Con::warnf("packed[%i]: %f",i, checkPack[i]); } - mProbeAtlas = new GBitmap(probeDataLength, count, (U8*)saveBuffer); - mProbeAtlas->writeBitmap("bmp", "probeinfoAtlas.bmp"); + //mProbeAtlas = new GBitmap(probeDataLength, count, (U8*)saveBuffer); + //mProbeAtlas->writeBitmap("bmp", "probeinfoAtlas.bmp"); + + if (mProbeAtlasDDS) + delete(mProbeAtlasDDS); + mProbeAtlasDDS = new DDSFile(); + mProbeAtlasDDS->mFormat = GFXFormatR32F; + mProbeAtlasDDS->mBytesPerPixel = 4; + mProbeAtlasDDS->mHeight = count; + mProbeAtlasDDS->mWidth = probeDataLength; + mProbeAtlasDDS->mMipMapCount = 1; + mProbeAtlasDDS->mSurfaces.push_back(new DDSFile::SurfaceData); + mProbeAtlasDDS->mSurfaces.last()->mMips.push_back(reinterpret_cast(checkPack)); + + FileStream* out = FileStream::createAndOpen(Con::getVariable("$Probes::AtlasTexture"), Torque::FS::File::Write); + + mProbeAtlasDDS->write(*out); free(saveBuffer); } @@ -1021,14 +1038,44 @@ F32 RenderProbeMgr::unpackF32(ColorI in) } void RenderProbeMgr::testProbeAtlas() -{ +{/* if (mProbeAtlas) delete(mProbeAtlas); mProbeAtlas = new GBitmap(); String atlasTexturePath = Con::getVariable("$Probes::AtlasTexture"); mProbeAtlas->readBitmap("bmp", atlasTexturePath); + */ + + U32 count = 1; + if (mProbeAtlasDDS) + delete(mProbeAtlasDDS); + mProbeAtlasDDS = new DDSFile(); + mProbeAtlasDDS->load(Con::getVariable("$Probes::AtlasTexture"),0); + + U32 probeAllocSize = sizeof(F32) * probeDataLength * count; + ProbeSerialize* readBuffer = (ProbeSerialize*)dMalloc(probeAllocSize); + memcpy(readBuffer, mProbeAtlasDDS->mSurfaces[0]->mMips[0], probeAllocSize); + + MatrixF inmat; + for (U32 x = 0; x < 4; x++) + { + for (U32 y = 0; y < 4; y++) + { + inmat(x, y) = readBuffer[0].xForm[x][y]; + } + } + + Con::warnf(" ambient: %f %f %f %f", readBuffer[0].ambientCol[0], readBuffer[0].ambientCol[1], readBuffer[0].ambientCol[2], readBuffer[0].ambientCol[3]); + Con::warnf(" sh: %f %f %f %f", readBuffer[0].sh[0], readBuffer[0].sh[1], readBuffer[0].sh[2], readBuffer[0].sh[3]); + Con::warnf("worldPos: %f %f %f", inmat.getPosition().x, inmat.getPosition().y, inmat.getPosition().z); + Con::warnf("rotation: %f %f %f", inmat.getForwardVector().x, inmat.getForwardVector().y, inmat.getForwardVector().z); + Con::warnf(" type: %f", readBuffer[0].type); + Con::warnf(" radius: %f", readBuffer[0].radius); + Con::warnf(" scale: %f", readBuffer[0].scale); + Con::warnf(" atten: %f", readBuffer[0].attenuation); + /* ProbeSerialize check; ColorI tCol; U32 i = 0; @@ -1066,6 +1113,7 @@ void RenderProbeMgr::testProbeAtlas() Con::warnf(" radius: %f", check.radius); Con::warnf(" scale: %f", check.scale); Con::warnf(" atten: %f", check.attenuation); + */ } DefineEngineFunction(bakeProbeAtlas, void, (), ,"") diff --git a/Engine/source/renderInstance/renderProbeMgr.h b/Engine/source/renderInstance/renderProbeMgr.h index 2f9a0ce3af..2b26f297de 100644 --- a/Engine/source/renderInstance/renderProbeMgr.h +++ b/Engine/source/renderInstance/renderProbeMgr.h @@ -488,6 +488,7 @@ class RenderProbeMgr : public RenderBinManager void clear() override { mActiveProbes.clear(); Parent::clear(); } GBitmap* mProbeAtlas; + DDSFile* mProbeAtlasDDS; void serializeProbes(); void testProbeAtlas(); F32 unpackF32(ColorI in); diff --git a/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript b/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript index 1f1df2fd98..03c25a2aae 100644 --- a/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript +++ b/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript @@ -291,7 +291,7 @@ singleton shaderData( PrefiterCubemapShader ) }; // -$Probes::AtlasTexture = "probeinfoAtlas.bmp"; + singleton ShaderData( PFX_ReflectionProbeArray ) { DXVertexShaderFile = $Core::CommonShaderPath @ "/postFX/postFxV.hlsl"; diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index a19b7f3a09..bede58d6e3 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -92,27 +92,35 @@ struct ProbeInfo float sh[9]; float4x4 xform; float3 offset; - int flags; float4 probeConfigData; + int flags; }; +#define PROBEINFO_DATA_LEN ((148/4)-1) inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, int slot, int atlasLen) { ProbeInfo probeInfo = (ProbeInfo)0; float slotLoc = slot/atlasLen; - float entryLoc[28]; - for (int locPos; locPos<28;locPos++) + float entryLoc[PROBEINFO_DATA_LEN]; + for (int locPos; locPos Date: Wed, 5 Nov 2025 21:47:39 -0600 Subject: [PATCH 11/19] gbitmap with data taking a format. setcolor attempting to account for r32f --- Engine/source/gfx/bitmap/gBitmap.cpp | 15 ++++-- Engine/source/gfx/bitmap/gBitmap.h | 3 +- .../source/renderInstance/renderProbeMgr.cpp | 46 ++++++++++++++----- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/Engine/source/gfx/bitmap/gBitmap.cpp b/Engine/source/gfx/bitmap/gBitmap.cpp index d97777a4b9..c24b40aa93 100644 --- a/Engine/source/gfx/bitmap/gBitmap.cpp +++ b/Engine/source/gfx/bitmap/gBitmap.cpp @@ -99,12 +99,13 @@ GBitmap::GBitmap(const U32 in_width, GBitmap::GBitmap(const U32 in_width, const U32 in_height, const U8* data, - const U32 in_numFaces) + const U32 in_numFaces, + const GFXFormat in_format) : mBits(NULL), mByteSize(0), mNumFaces(in_numFaces) { - allocateBitmap(in_width, in_height, false, GFXFormatR8G8B8A8, in_numFaces); + allocateBitmap(in_width, in_height, false, in_format, in_numFaces); mHasTransparency = false; @@ -993,9 +994,13 @@ bool GBitmap::setColor(const U32 x, const U32 y, const ColorI& rColor) *pLoc = rColor.alpha; break; - case GFXFormatL16: - dMemcpy(pLoc, &rColor, 2 * sizeof(U8)); - break; + case GFXFormatL16: + dMemcpy(pLoc, &rColor, 2 * sizeof(U8)); + break; + + case GFXFormatR32F: + dMemcpy(pLoc, &rColor, 4 * sizeof(U8)); + break; case GFXFormatR8G8B8: dMemcpy( pLoc, &rColor, 3 * sizeof( U8 ) ); diff --git a/Engine/source/gfx/bitmap/gBitmap.h b/Engine/source/gfx/bitmap/gBitmap.h index 4a32531f98..2280ea7ea5 100644 --- a/Engine/source/gfx/bitmap/gBitmap.h +++ b/Engine/source/gfx/bitmap/gBitmap.h @@ -143,7 +143,8 @@ class GBitmap GBitmap(const U32 in_width, const U32 in_height, const U8* data, - const U32 in_numFaces = 1); + const U32 in_numFaces = 1, + const GFXFormat in_format = GFXFormatR8G8B8A8); virtual ~GBitmap(); diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index 97888f73a2..75815875f2 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -975,7 +975,7 @@ struct ProbeSerialize #define probeDataLength (U32)(sizeof(ProbeSerialize) / 4) void RenderProbeMgr::serializeProbes() { - U32 count = 1; + U32 count = 10; U32 probeAllocSize = sizeof(ProbeSerialize) * count; ProbeSerialize* saveBuffer = (ProbeSerialize*)dMalloc(probeAllocSize); for (U32 i = 0; i < count; i++) @@ -1007,8 +1007,21 @@ void RenderProbeMgr::serializeProbes() Con::warnf("packed[%i]: %f",i, checkPack[i]); } - //mProbeAtlas = new GBitmap(probeDataLength, count, (U8*)saveBuffer); - //mProbeAtlas->writeBitmap("bmp", "probeinfoAtlas.bmp"); + /* + mProbeAtlas = new GBitmap(probeDataLength, count, reinterpret_cast(checkPack), 1, GFXFormatR32F); + + FileStream out; + out.open(Con::getVariable("$Probes::AtlasTexture"), Torque::FS::File::Write); + if (out.getStatus() != out::Ok) + { + Con::errorf("RenderProbeMgr::serializeProbes - failed to open probeAtlastTexture"); + } + else + { + mProbeAtlas->write(out); + out.close(); + } + */ if (mProbeAtlasDDS) delete(mProbeAtlasDDS); @@ -1022,8 +1035,8 @@ void RenderProbeMgr::serializeProbes() mProbeAtlasDDS->mSurfaces.last()->mMips.push_back(reinterpret_cast(checkPack)); FileStream* out = FileStream::createAndOpen(Con::getVariable("$Probes::AtlasTexture"), Torque::FS::File::Write); - mProbeAtlasDDS->write(*out); + out->close(); free(saveBuffer); } @@ -1038,16 +1051,25 @@ F32 RenderProbeMgr::unpackF32(ColorI in) } void RenderProbeMgr::testProbeAtlas() -{/* +{ + U32 count = 10; + /* if (mProbeAtlas) delete(mProbeAtlas); - mProbeAtlas = new GBitmap(); - String atlasTexturePath = Con::getVariable("$Probes::AtlasTexture"); - mProbeAtlas->readBitmap("bmp", atlasTexturePath); + FileStream stream; + stream.open(Con::getVariable("$Probes::AtlasTexture"), Torque::FS::File::Read); + if (stream.getStatus() != Stream::Ok) + { + Con::errorf("RenderProbeMgr::testProbeAtlas - failed to open probeAtlastTexture"); + } + else + { + mProbeAtlas->read(stream); + stream.close(); + } */ - - U32 count = 1; + if (mProbeAtlasDDS) delete(mProbeAtlasDDS); mProbeAtlasDDS = new DDSFile(); @@ -1055,7 +1077,9 @@ void RenderProbeMgr::testProbeAtlas() U32 probeAllocSize = sizeof(F32) * probeDataLength * count; ProbeSerialize* readBuffer = (ProbeSerialize*)dMalloc(probeAllocSize); - memcpy(readBuffer, mProbeAtlasDDS->mSurfaces[0]->mMips[0], probeAllocSize); + //const void* dataStart = mProbeAtlas->getBits(); + const void* dataStart = mProbeAtlasDDS->mSurfaces[0]->mMips[0]; + memcpy(readBuffer, dataStart, probeAllocSize); MatrixF inmat; for (U32 x = 0; x < 4; x++) From 81a27e2e43774d884e1f3ae151ab45ecfb2af5df Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 6 Nov 2025 11:54:28 -0600 Subject: [PATCH 12/19] fix hlsl array size/serialization --- .../game/core/rendering/shaders/lighting.hlsl | 41 ++++++++++++++----- .../advanced/reflectionProbeArrayP.hlsl | 4 +- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index bede58d6e3..596cc949bc 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -85,7 +85,20 @@ inline float3 getDistanceVectorToPlane( float negFarPlaneDotEye, float3 directio return direction.xyz * t; } - +/* +struct ProbeSerialize +{ + F32 ambientCol[4]{0,1.0,0,1.0}; + F32 sh[9]; + F32 xForm[4][4]; + F32 offset[3]{ 0.0,0.0,0.0 }; + F32 type = 0;//(U8)(ProbeInfo::Box); + F32 radius = 5; + F32 scale = 10; + F32 attenuation = 0; + U32 flags = BIT(0); //[0]canDamp +}; +*/ struct ProbeInfo { float4 ambientCol; @@ -96,16 +109,16 @@ struct ProbeInfo int flags; }; -#define PROBEINFO_DATA_LEN ((148/4)-1) -inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, int slot, int atlasLen) +#define PROBEINFO_DATA_LEN (148/4) +inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, float slot, float atlasLen) { ProbeInfo probeInfo = (ProbeInfo)0; float slotLoc = slot/atlasLen; float entryLoc[PROBEINFO_DATA_LEN]; - for (int locPos; locPos Date: Thu, 6 Nov 2025 17:06:44 -0600 Subject: [PATCH 13/19] ditch SSH for now. add refscale. fix sampler. --- .../source/renderInstance/renderProbeMgr.cpp | 10 +++---- .../scripts/advancedLighting_Shaders.tscript | 3 ++- .../game/core/rendering/shaders/lighting.hlsl | 27 ++++++++++--------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index 75815875f2..a63349bf56 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -958,19 +958,20 @@ void RenderProbeMgr::render( SceneRenderState *state ) // Make sure the effect is gonna render. getProbeArrayEffect()->setSkip(false); } - +#pragma pack(push, 4) struct ProbeSerialize { F32 ambientCol[4]{0,1.0,0,1.0}; - F32 sh[9]; F32 xForm[4][4]; F32 offset[3]{ 0.0,0.0,0.0 }; + F32 refScale[3]{ 0.0,0.0,0.0 }; F32 type = 0;//(U8)(ProbeInfo::Box); F32 radius = 5; F32 scale = 10; F32 attenuation = 0; U32 flags = BIT(0); //[0]canDamp }; +#pragma pop(pop) #define probeDataLength (U32)(sizeof(ProbeSerialize) / 4) void RenderProbeMgr::serializeProbes() @@ -981,10 +982,6 @@ void RenderProbeMgr::serializeProbes() for (U32 i = 0; i < count; i++) { ProbeSerialize pSer; - for (U32 SHID = 0; SHID < 9; SHID++) - { - pSer.sh[SHID]= 0.0f; - } MatrixF inmat = MatrixF(Point3F(0,0,0), Point3F(-5.0f + i * 10, 100.0f + i * 10, 0.0f)); inmat.scale(10); for (U32 x = 0; x < 4; x++) @@ -1091,7 +1088,6 @@ void RenderProbeMgr::testProbeAtlas() } Con::warnf(" ambient: %f %f %f %f", readBuffer[0].ambientCol[0], readBuffer[0].ambientCol[1], readBuffer[0].ambientCol[2], readBuffer[0].ambientCol[3]); - Con::warnf(" sh: %f %f %f %f", readBuffer[0].sh[0], readBuffer[0].sh[1], readBuffer[0].sh[2], readBuffer[0].sh[3]); Con::warnf("worldPos: %f %f %f", inmat.getPosition().x, inmat.getPosition().y, inmat.getPosition().z); Con::warnf("rotation: %f %f %f", inmat.getForwardVector().x, inmat.getForwardVector().y, inmat.getForwardVector().z); Con::warnf(" type: %f", readBuffer[0].type); diff --git a/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript b/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript index 03c25a2aae..43dce9fc94 100644 --- a/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript +++ b/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript @@ -338,5 +338,6 @@ singleton GFXStateBlockData( PFX_ReflectionProbeArrayStateBlock ) samplerStates[4] = SamplerClampLinear; samplerStates[5] = SamplerClampLinear; samplerStates[6] = SamplerWrapPoint; - samplerStates[7] = SamplerClampPoint; + samplerStates[7] = SamplerWrapPoint; + samplerStates[8] = SamplerClampPoint; }; \ No newline at end of file diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index 596cc949bc..a25c4d8171 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -89,9 +89,9 @@ inline float3 getDistanceVectorToPlane( float negFarPlaneDotEye, float3 directio struct ProbeSerialize { F32 ambientCol[4]{0,1.0,0,1.0}; - F32 sh[9]; F32 xForm[4][4]; F32 offset[3]{ 0.0,0.0,0.0 }; + F32 refscale[3]{ 0.0,0.0,0.0 }; F32 type = 0;//(U8)(ProbeInfo::Box); F32 radius = 5; F32 scale = 10; @@ -102,14 +102,14 @@ struct ProbeSerialize struct ProbeInfo { float4 ambientCol; - float sh[9]; float4x4 xform; float3 offset; + float3 refScale; float4 probeConfigData; int flags; }; -#define PROBEINFO_DATA_LEN (148/4) +#define PROBEINFO_DATA_LEN (124/4) inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, float slot, float atlasLen) { ProbeInfo probeInfo = (ProbeInfo)0; @@ -129,19 +129,13 @@ inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, entry++; //3 probeInfo.ambientCol.a = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; - [unroll] - for (int shID=0;shID<9;shID++) - { - entry++; //12 - probeInfo.sh[shID] = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; - } [unroll] for (int x=0;x<4;x++) { [unroll] for (int y=0;y<4;y++) { - entry++; //28 + entry++; //19 probeInfo.xform[y][x] = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; } } @@ -149,19 +143,26 @@ inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, probeInfo.offset.x = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; entry++; probeInfo.offset.y = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; - entry++; //31 + entry++; //22 probeInfo.offset.z = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + entry++; + probeInfo.refScale.x = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + entry++; + probeInfo.refScale.y = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + entry++; //25 + probeInfo.refScale.z = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + entry++; probeInfo.probeConfigData.r = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; entry++; probeInfo.probeConfigData.g = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; entry++; probeInfo.probeConfigData.b = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; - entry++; //35 + entry++; //29 probeInfo.probeConfigData.a = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; - entry++; //36 + entry++; //30 probeInfo.flags = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; return probeInfo; } From b3e09cb8ddb152212d971ca8b14fcc4103a3a60c Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 6 Nov 2025 17:49:02 -0600 Subject: [PATCH 14/19] use direct pixel lookup instead of playing percentage games --- Engine/source/gfx/bitmap/gBitmap.cpp | 9 +-- Engine/source/gfx/bitmap/gBitmap.h | 3 +- .../game/core/rendering/shaders/lighting.hlsl | 56 ++++++------------- .../advanced/reflectionProbeArrayP.hlsl | 2 +- .../core/rendering/shaders/shaderModel.hlsl | 2 + 5 files changed, 24 insertions(+), 48 deletions(-) diff --git a/Engine/source/gfx/bitmap/gBitmap.cpp b/Engine/source/gfx/bitmap/gBitmap.cpp index c24b40aa93..cb7811d7cc 100644 --- a/Engine/source/gfx/bitmap/gBitmap.cpp +++ b/Engine/source/gfx/bitmap/gBitmap.cpp @@ -99,13 +99,12 @@ GBitmap::GBitmap(const U32 in_width, GBitmap::GBitmap(const U32 in_width, const U32 in_height, const U8* data, - const U32 in_numFaces, - const GFXFormat in_format) + const U32 in_numFaces) : mBits(NULL), mByteSize(0), mNumFaces(in_numFaces) { - allocateBitmap(in_width, in_height, false, in_format, in_numFaces); + allocateBitmap(in_width, in_height, false, GFXFormatR8G8B8A8, in_numFaces); mHasTransparency = false; @@ -998,10 +997,6 @@ bool GBitmap::setColor(const U32 x, const U32 y, const ColorI& rColor) dMemcpy(pLoc, &rColor, 2 * sizeof(U8)); break; - case GFXFormatR32F: - dMemcpy(pLoc, &rColor, 4 * sizeof(U8)); - break; - case GFXFormatR8G8B8: dMemcpy( pLoc, &rColor, 3 * sizeof( U8 ) ); break; diff --git a/Engine/source/gfx/bitmap/gBitmap.h b/Engine/source/gfx/bitmap/gBitmap.h index 2280ea7ea5..4a32531f98 100644 --- a/Engine/source/gfx/bitmap/gBitmap.h +++ b/Engine/source/gfx/bitmap/gBitmap.h @@ -143,8 +143,7 @@ class GBitmap GBitmap(const U32 in_width, const U32 in_height, const U8* data, - const U32 in_numFaces = 1, - const GFXFormat in_format = GFXFormatR8G8B8A8); + const U32 in_numFaces = 1); virtual ~GBitmap(); diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index a25c4d8171..3ebf39dbbd 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -85,20 +85,7 @@ inline float3 getDistanceVectorToPlane( float negFarPlaneDotEye, float3 directio return direction.xyz * t; } -/* -struct ProbeSerialize -{ - F32 ambientCol[4]{0,1.0,0,1.0}; - F32 xForm[4][4]; - F32 offset[3]{ 0.0,0.0,0.0 }; - F32 refscale[3]{ 0.0,0.0,0.0 }; - F32 type = 0;//(U8)(ProbeInfo::Box); - F32 radius = 5; - F32 scale = 10; - F32 attenuation = 0; - U32 flags = BIT(0); //[0]canDamp -}; -*/ + struct ProbeInfo { float4 ambientCol; @@ -109,25 +96,18 @@ struct ProbeInfo int flags; }; -#define PROBEINFO_DATA_LEN (124/4) -inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, float slot, float atlasLen) +inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, int slot) { ProbeInfo probeInfo = (ProbeInfo)0; - float slotLoc = slot/atlasLen; - float entryLoc[PROBEINFO_DATA_LEN]; - for (int locPos; locPos<(PROBEINFO_DATA_LEN);locPos++) - { - entryLoc[locPos] = locPos/float(PROBEINFO_DATA_LEN-1); - } int entry = 0; - probeInfo.ambientCol.r = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.ambientCol.r = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; - probeInfo.ambientCol.g = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.ambientCol.g = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; - probeInfo.ambientCol.b = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.ambientCol.b = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; //3 - probeInfo.ambientCol.a = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.ambientCol.a = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; [unroll] for (int x=0;x<4;x++) @@ -136,34 +116,34 @@ inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, for (int y=0;y<4;y++) { entry++; //19 - probeInfo.xform[y][x] = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.xform[y][x] = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; } } entry++; - probeInfo.offset.x = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.offset.x = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; - probeInfo.offset.y = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.offset.y = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; //22 - probeInfo.offset.z = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.offset.z = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; - probeInfo.refScale.x = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.refScale.x = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; - probeInfo.refScale.y = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.refScale.y = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; //25 - probeInfo.refScale.z = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.refScale.z = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; - probeInfo.probeConfigData.r = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.probeConfigData.r = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; - probeInfo.probeConfigData.g = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.probeConfigData.g = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; - probeInfo.probeConfigData.b = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.probeConfigData.b = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; //29 - probeInfo.probeConfigData.a = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.probeConfigData.a = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; //30 - probeInfo.flags = TORQUE_TEX2DLOD(atlasTex, float4(entryLoc[entry],slotLoc,0,0)).r; + probeInfo.flags = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; return probeInfo; } diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl index 5cb296a5c3..9234c8a8c9 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl @@ -208,7 +208,7 @@ float4 main(PFXVertToPix IN) : SV_TARGET float3 finalColor = diffuse + specularCol*horizon; finalColor *= surface.ao; - ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas), eyePosWorld,0.0,10.0); + ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas), eyePosWorld,0.0); float contribution = defineFauxSpaceInfluence(surface.P, probeInfo.xform, probeInfo.probeConfigData.a); contribution = max(contribution,0); return lerp(float4(0,0,1,0), probeInfo.ambientCol,contribution); diff --git a/Templates/BaseGame/game/core/rendering/shaders/shaderModel.hlsl b/Templates/BaseGame/game/core/rendering/shaders/shaderModel.hlsl index d34869c3b9..dab073c940 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/shaderModel.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/shaderModel.hlsl @@ -52,6 +52,8 @@ #define TORQUE_TEX2DLOD(tex,coords) texture_##tex.SampleLevel(tex,coords.xy,coords.w) #define TORQUE_TEXCUBELOD(tex,coords) texture_##tex.SampleLevel(tex,coords.xyz,coords.w) #define TORQUE_TEXCUBEARRAYLOD(tex,coords,id,lod) texture_##tex.SampleLevel(tex,float4(coords.xyz,id),lod) + +#define TORQUE_TEX2D_PIX(tex,coords) texture_##tex.Load(int3(coords.xy,0)) // Tex2d comparison #define TORQUE_TEX2DCMP(tex,coords,compare) texture_##tex.SampleCmpLevelZero(tex,coords,compare) From 3ab669237c195944c3c7d68fdbb7177d163faa94 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 6 Nov 2025 20:24:14 -0600 Subject: [PATCH 15/19] typofix. gl conversion todo: see why it doesn't seem to be passing the texture along --- .../source/renderInstance/renderProbeMgr.cpp | 2 +- .../core/rendering/shaders/gl/lighting.glsl | 73 ++++++++++++++++++- .../game/core/rendering/shaders/lighting.hlsl | 2 +- .../advanced/gl/reflectionProbeArrayP.glsl | 7 ++ .../advanced/reflectionProbeArrayP.hlsl | 2 +- 5 files changed, 81 insertions(+), 5 deletions(-) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index a63349bf56..0755009550 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -971,7 +971,7 @@ struct ProbeSerialize F32 attenuation = 0; U32 flags = BIT(0); //[0]canDamp }; -#pragma pop(pop) +#pragma pack(pop) #define probeDataLength (U32)(sizeof(ProbeSerialize) / 4) void RenderProbeMgr::serializeProbes() diff --git a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl index 0e5b7a585c..e2125ba08c 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl @@ -84,6 +84,65 @@ vec3 getDistanceVectorToPlane( float negFarPlaneDotEye, vec3 direction, vec4 pla return direction.xyz * t; } +struct ProbeInfo +{ + vec4 ambientCol; + mat4 xform; + vec3 offset; + vec3 refScale; + vec4 probeConfigData; + int flags; +}; + +ProbeInfo createProbeinfo(sampler2D atlasTex, vec3 eyePosWorld, int slot) +{ + ProbeInfo probeInfo; + + int entry = 0; + probeInfo.ambientCol.r = texelFetch(atlasTex, ivec2(entry,slot),0).r; + entry++; + probeInfo.ambientCol.g = texelFetch(atlasTex, ivec2(entry,slot),0).r; + entry++; + probeInfo.ambientCol.b = texelFetch(atlasTex, ivec2(entry,slot),0).r; + entry++; //3 + probeInfo.ambientCol.a = texelFetch(atlasTex, ivec2(entry,slot),0).r; + + for (int x=0;x<4;x++) + { + for (int y=0;y<4;y++) + { + entry++; //19 + probeInfo.xform[y][x] = texelFetch(atlasTex, ivec2(entry,slot),0).r; + } + } + entry++; + probeInfo.offset.x = texelFetch(atlasTex, ivec2(entry,slot),0).r; + entry++; + probeInfo.offset.y = texelFetch(atlasTex, ivec2(entry,slot),0).r; + entry++; //22 + probeInfo.offset.z = texelFetch(atlasTex, ivec2(entry,slot),0).r; + + entry++; + probeInfo.refScale.x = texelFetch(atlasTex, ivec2(entry,slot),0).r; + entry++; + probeInfo.refScale.y = texelFetch(atlasTex, ivec2(entry,slot),0).r; + entry++; //25 + probeInfo.refScale.z = texelFetch(atlasTex, ivec2(entry,slot),0).r; + + entry++; + probeInfo.probeConfigData.r = texelFetch(atlasTex, ivec2(entry,slot),0).r; + entry++; + probeInfo.probeConfigData.g = texelFetch(atlasTex, ivec2(entry,slot),0).r; + entry++; + probeInfo.probeConfigData.b = texelFetch(atlasTex, ivec2(entry,slot),0).r; + entry++; //29 + probeInfo.probeConfigData.a = texelFetch(atlasTex, ivec2(entry,slot),0).r; + + entry++; //30 + probeInfo.flags = int(texelFetch(atlasTex, ivec2(entry,slot),0).r); + return probeInfo; +} + struct Surface { vec3 P; // world space position @@ -389,7 +448,7 @@ vec4 compute4Lights( Surface surface, //Probe IBL stuff float defineSphereSpaceInfluence(vec3 wsPosition, vec3 wsProbePosition, float radius, float atten) { - float3 L = (wsProbePosition.xyz - wsPosition); + vec3 L = (wsProbePosition.xyz - wsPosition); float innerRadius = radius-(radius*atten); float contribution = 1.0-pow(saturate(length(L)/mix(radius, innerRadius, atten)), M_2PI_F*(1.0-atten)); return saturate(contribution); @@ -409,6 +468,16 @@ float defineBoxSpaceInfluence(vec3 wsPosition, mat4 worldToObj, float attenuatio return saturate(smoothstep(baseVal, (baseVal-attenuation/2), dist)); } +float defineFauxSpaceInfluence(vec3 wsPosition, mat4 worldMat, float attenuation) +{ + vec3 surfPosLS = wsPosition-worldMat[3].xyz; + + float baseVal = 0.25; + float dist = getDistBoxToPoint(surfPosLS, vec3(baseVal, baseVal, baseVal)); + return saturate(smoothstep(baseVal, (baseVal-attenuation/2), dist)); +} + + // Box Projected IBL Lighting // Based on: http://www.gamedev.net/topic/568829-box-projected-cubemap-environment-mapping/ // and https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ @@ -449,7 +518,7 @@ void dampen(inout Surface surface, sampler2D WetnessTexture, float accumTime, fl wetness = pow(wetness*ang*degree,3); surface.roughness = lerp(surface.roughness, 0.04f, wetness); - surface.baseColor.rgb = lerp(surface.baseColor.rgb, surface.baseColor.rgb*0.6+float3(0.4,0.4,0.4)*wetness, wetness); + surface.baseColor.rgb = lerp(surface.baseColor.rgb, surface.baseColor.rgb*0.6+vec3(0.4,0.4,0.4)*wetness, wetness); surface.metalness = lerp(surface.metalness, 0.96, wetness); updateSurface(surface); } diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index 3ebf39dbbd..ada6dd2ac8 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -92,7 +92,7 @@ struct ProbeInfo float4x4 xform; float3 offset; float3 refScale; - float4 probeConfigData; + float4 probeConfigData; int flags; }; diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/reflectionProbeArrayP.glsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/reflectionProbeArrayP.glsl index 9d4a589cb3..ec3fba8599 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/reflectionProbeArrayP.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/reflectionProbeArrayP.glsl @@ -26,6 +26,7 @@ uniform int numProbes; uniform samplerCubeArray specularCubemapAR; uniform samplerCubeArray irradianceCubemapAR; uniform sampler2D WetnessTexture; +uniform sampler2D probeAtlas; #ifdef USE_SSAO_MASK uniform sampler2D ssaoMask; uniform vec4 rtParams7; @@ -215,6 +216,12 @@ void main() // Final color output after environment lighting vec3 finalColor = diffuse + specularCol * horizon; finalColor *= surface.ao; + ProbeInfo probeInfo = createProbeinfo(probeAtlas, eyePosWorld,0); + + float contribution = defineFauxSpaceInfluence(surface.P, probeInfo.xform, probeInfo.probeConfigData.a); + contribution = max(contribution,0); + OUT_col = lerp(vec4(0,0,1.0,0), probeInfo.ambientCol,contribution); + return; if(isCapturing == 1) OUT_col = vec4(lerp((finalColor), surface.baseColor.rgb,surface.metalness),0); diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl index 9234c8a8c9..0f6f88c402 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/reflectionProbeArrayP.hlsl @@ -208,7 +208,7 @@ float4 main(PFXVertToPix IN) : SV_TARGET float3 finalColor = diffuse + specularCol*horizon; finalColor *= surface.ao; - ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas), eyePosWorld,0.0); + ProbeInfo probeInfo = createProbeinfo(TORQUE_SAMPLER2D_MAKEARG(probeAtlas), eyePosWorld,0); float contribution = defineFauxSpaceInfluence(surface.P, probeInfo.xform, probeInfo.probeConfigData.a); contribution = max(contribution,0); return lerp(float4(0,0,1,0), probeInfo.ambientCol,contribution); From fe1d46ebdbce705ad5321680caf1b1634b5df9de Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 9 Nov 2025 08:39:19 -0600 Subject: [PATCH 16/19] add GFXFormatR32F to getGFXFormat --- Engine/source/gfx/bitmap/ddsData.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Engine/source/gfx/bitmap/ddsData.h b/Engine/source/gfx/bitmap/ddsData.h index bededb8e51..21b6bd5621 100644 --- a/Engine/source/gfx/bitmap/ddsData.h +++ b/Engine/source/gfx/bitmap/ddsData.h @@ -741,6 +741,7 @@ namespace dds case D3DFMT_A16B16G16R16F: return GFXFormatR16G16B16A16F; case D3DFMT_A32B32G32R32F: return GFXFormatR32G32B32A32F; case D3DFMT_G16R16F: return GFXFormatR16G16F; + case D3DFMT_R32F: return GFXFormatR32F; default: { Con::errorf("dds::getGFXFormatFourcc: unknown format"); From b6544d3b4f3ad2e8c56b1d4eed8d3869f1de0d15 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 9 Nov 2025 09:57:32 -0600 Subject: [PATCH 17/19] shift to leveraging Resource since it would appear that DDSFile->Load doesn't load into the calling ddsfile --- .../source/renderInstance/renderProbeMgr.cpp | 122 +++--------------- Engine/source/renderInstance/renderProbeMgr.h | 4 +- 2 files changed, 17 insertions(+), 109 deletions(-) diff --git a/Engine/source/renderInstance/renderProbeMgr.cpp b/Engine/source/renderInstance/renderProbeMgr.cpp index 0755009550..077f867ac9 100644 --- a/Engine/source/renderInstance/renderProbeMgr.cpp +++ b/Engine/source/renderInstance/renderProbeMgr.cpp @@ -179,8 +179,7 @@ RenderProbeMgr::RenderProbeMgr() mSkylightDamp(true), mCubeMapCount(0), mUseHDRCaptures(true), - mProbeAtlas(NULL), - mProbeAtlasDDS(NULL) + mProbeAtlas(NULL) { mEffectiveProbeCount = 0; mMipCount = 0; @@ -887,9 +886,9 @@ void RenderProbeMgr::render( SceneRenderState *state ) mProbeArrayEffect->setCubemapArrayTexture(5, mIrradianceArray); mProbeArrayEffect->setTexture(6, mWetnessTexture); - if (mProbeAtlasDDS) + if (mProbeAtlas) { - if (mProbeAtlasTexture.set(mProbeAtlasDDS, &GFXTexturePersistentProfile, false, "atlasTexture")) + if (mProbeAtlasTexture.set(mProbeAtlas, &GFXTexturePersistentProfile, false, "atlasTexture")) { mProbeArrayEffect->setTexture(7, mProbeAtlasTexture); //mProbeArrayEffect->setShaderConst("$numAtlasEntries", (S32)10); //make adaptive @@ -995,8 +994,6 @@ void RenderProbeMgr::serializeProbes() } - if (mProbeAtlas) - delete(mProbeAtlas); F32* checkPack = (F32*)dMalloc(probeAllocSize); memcpy(checkPack, saveBuffer, probeAllocSize); for (U32 i = 0; i < probeDataLength; i++) @@ -1004,78 +1001,31 @@ void RenderProbeMgr::serializeProbes() Con::warnf("packed[%i]: %f",i, checkPack[i]); } - /* - mProbeAtlas = new GBitmap(probeDataLength, count, reinterpret_cast(checkPack), 1, GFXFormatR32F); - - FileStream out; - out.open(Con::getVariable("$Probes::AtlasTexture"), Torque::FS::File::Write); - if (out.getStatus() != out::Ok) - { - Con::errorf("RenderProbeMgr::serializeProbes - failed to open probeAtlastTexture"); - } - else - { - mProbeAtlas->write(out); - out.close(); - } - */ - - if (mProbeAtlasDDS) - delete(mProbeAtlasDDS); - mProbeAtlasDDS = new DDSFile(); - mProbeAtlasDDS->mFormat = GFXFormatR32F; - mProbeAtlasDDS->mBytesPerPixel = 4; - mProbeAtlasDDS->mHeight = count; - mProbeAtlasDDS->mWidth = probeDataLength; - mProbeAtlasDDS->mMipMapCount = 1; - mProbeAtlasDDS->mSurfaces.push_back(new DDSFile::SurfaceData); - mProbeAtlasDDS->mSurfaces.last()->mMips.push_back(reinterpret_cast(checkPack)); + DDSFile dataFile; + dataFile.mFormat = GFXFormatR32F; + dataFile.mBytesPerPixel = 4; + dataFile.mHeight = count; + dataFile.mWidth = probeDataLength; + dataFile.mMipMapCount = 1; + dataFile.mSurfaces.push_back(new DDSFile::SurfaceData); + dataFile.mSurfaces.last()->mMips.push_back(reinterpret_cast(checkPack)); FileStream* out = FileStream::createAndOpen(Con::getVariable("$Probes::AtlasTexture"), Torque::FS::File::Write); - mProbeAtlasDDS->write(*out); + dataFile.write(*out); out->close(); free(saveBuffer); -} - -F32 RenderProbeMgr::unpackF32(ColorI in) -{ - U8 convert[4]{ in.red, in.green, in.blue, in.alpha}; - F32 out; - std::memcpy(&out, &convert, sizeof(F32)); - if (in.alpha == 64) Con::warnf("FOUND 64 in ALPHA!"); - if (in.red == 64) Con::warnf("FOUND 64 in RED!"); - return out; + mProbeAtlas = dataFile.load(Con::getVariable("$Probes::AtlasTexture"), 0); } void RenderProbeMgr::testProbeAtlas() { U32 count = 10; - /* - if (mProbeAtlas) - delete(mProbeAtlas); - mProbeAtlas = new GBitmap(); - FileStream stream; - stream.open(Con::getVariable("$Probes::AtlasTexture"), Torque::FS::File::Read); - if (stream.getStatus() != Stream::Ok) - { - Con::errorf("RenderProbeMgr::testProbeAtlas - failed to open probeAtlastTexture"); - } - else - { - mProbeAtlas->read(stream); - stream.close(); - } - */ - - if (mProbeAtlasDDS) - delete(mProbeAtlasDDS); - mProbeAtlasDDS = new DDSFile(); - mProbeAtlasDDS->load(Con::getVariable("$Probes::AtlasTexture"),0); + DDSFile dataFile; + mProbeAtlas = dataFile.load(Con::getVariable("$Probes::AtlasTexture"),0); U32 probeAllocSize = sizeof(F32) * probeDataLength * count; ProbeSerialize* readBuffer = (ProbeSerialize*)dMalloc(probeAllocSize); - //const void* dataStart = mProbeAtlas->getBits(); - const void* dataStart = mProbeAtlasDDS->mSurfaces[0]->mMips[0]; + const void* dataStart = mProbeAtlas->mSurfaces[0]->mMips[0]; memcpy(readBuffer, dataStart, probeAllocSize); MatrixF inmat; @@ -1094,46 +1044,6 @@ void RenderProbeMgr::testProbeAtlas() Con::warnf(" radius: %f", readBuffer[0].radius); Con::warnf(" scale: %f", readBuffer[0].scale); Con::warnf(" atten: %f", readBuffer[0].attenuation); - - /* - ProbeSerialize check; - ColorI tCol; - U32 i = 0; - mProbeAtlas->getColor(i, 0, tCol); - check.ambientCol[0] = tCol.red; - check.ambientCol[1] = tCol.green; - check.ambientCol[2] = tCol.blue; - check.ambientCol[3] = tCol.alpha; - for (U32 shID = 0; shID < 9; shID++) - { - mProbeAtlas->getColor(++i, 0, tCol); - check.sh[shID] = unpackF32(tCol); - } - MatrixF inmat; - for (U32 x = 0; x < 4; x++) - { - for (U32 y = 0; y < 4; y++) - { - mProbeAtlas->getColor(++i, 0, tCol); - check.xForm[x][y] = unpackF32(tCol); - inmat(x, y) = check.xForm[x][y]; - } - } - mProbeAtlas->getColor(++i, 0, tCol); - check.type = tCol.red; - check.radius = tCol.blue; - check.scale = tCol.green; - check.attenuation = tCol.alpha; - - Con::warnf(" ambient: %i %i %i %i", check.ambientCol[0], check.ambientCol[1], check.ambientCol[2], check.ambientCol[3]); - Con::warnf(" sh: %f %f %f %f", check.sh[0], check.sh[1], check.sh[2], check.sh[3]); - Con::warnf("worldPos: %f %f %f", inmat.getPosition().x, inmat.getPosition().y, inmat.getPosition().z); - Con::warnf("rotation: %f %f %f", inmat.getForwardVector().x, inmat.getForwardVector().y, inmat.getForwardVector().z); - Con::warnf(" type: %i", check.type); - Con::warnf(" radius: %f", check.radius); - Con::warnf(" scale: %f", check.scale); - Con::warnf(" atten: %f", check.attenuation); - */ } DefineEngineFunction(bakeProbeAtlas, void, (), ,"") diff --git a/Engine/source/renderInstance/renderProbeMgr.h b/Engine/source/renderInstance/renderProbeMgr.h index 2b26f297de..7c308d6cda 100644 --- a/Engine/source/renderInstance/renderProbeMgr.h +++ b/Engine/source/renderInstance/renderProbeMgr.h @@ -487,11 +487,9 @@ class RenderProbeMgr : public RenderBinManager void clear() override { mActiveProbes.clear(); Parent::clear(); } - GBitmap* mProbeAtlas; - DDSFile* mProbeAtlasDDS; + Resource mProbeAtlas; void serializeProbes(); void testProbeAtlas(); - F32 unpackF32(ColorI in); }; RenderProbeMgr* RenderProbeMgr::getProbeManager() From 6df8ba5c53b4f0833e5235fb8b26b88b58df3b25 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 9 Nov 2025 10:38:30 -0600 Subject: [PATCH 18/19] fix probeatlas mapping fix bitmask flags via float to int/uint reinterpret -converters --- .../core/lighting/scripts/advancedLighting_Shaders.tscript | 4 ++-- .../BaseGame/game/core/rendering/shaders/gl/lighting.glsl | 2 +- Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl | 2 +- Templates/BaseGame/game/data/defaults.tscript | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript b/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript index 43dce9fc94..8352beaa08 100644 --- a/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript +++ b/Templates/BaseGame/game/core/lighting/scripts/advancedLighting_Shaders.tscript @@ -307,8 +307,8 @@ singleton ShaderData( PFX_ReflectionProbeArray ) samplerNames[4] = "$specularCubemapAR"; samplerNames[5] = "$irradianceCubemapAR"; samplerNames[6] = "$WetnessTexture"; - samplerNames[7] = "$ssaoMask"; - samplerNames[8] = "$probeAtlas"; + samplerNames[7] = "$probeAtlas"; + samplerNames[8] = "$ssaoMask"; pixVersion = 2.0; }; diff --git a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl index e2125ba08c..6b3d8e3115 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl @@ -139,7 +139,7 @@ ProbeInfo createProbeinfo(sampler2D atlasTex, vec3 eyePosWorld, int slot) probeInfo.probeConfigData.a = texelFetch(atlasTex, ivec2(entry,slot),0).r; entry++; //30 - probeInfo.flags = int(texelFetch(atlasTex, ivec2(entry,slot),0).r); + probeInfo.flags = floatBitsToInt(texelFetch(atlasTex, ivec2(entry,slot),0).r); return probeInfo; } diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index ada6dd2ac8..9aec516db6 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -143,7 +143,7 @@ inline ProbeInfo createProbeinfo(TORQUE_SAMPLER2D(atlasTex),float3 eyePosWorld, probeInfo.probeConfigData.a = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; entry++; //30 - probeInfo.flags = TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r; + probeInfo.flags = asuint(TORQUE_TEX2D_PIX(atlasTex, int2(entry,slot)).r); return probeInfo; } diff --git a/Templates/BaseGame/game/data/defaults.tscript b/Templates/BaseGame/game/data/defaults.tscript index 57fb261af7..61b9ba1162 100644 --- a/Templates/BaseGame/game/data/defaults.tscript +++ b/Templates/BaseGame/game/data/defaults.tscript @@ -212,4 +212,4 @@ $pref::PostFX::EnableSSAO = 1; $pref::PostFX::EnableHDRBloom = 1; -$Probes::AtlasTexture = "probeinfoAtlas.dds"; \ No newline at end of file +$Probes::AtlasTexture = "probeinfoAtlas.dbm"; \ No newline at end of file From 68de81d3a79a0877f2336e01c93a7f6b74c866b0 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 22 Dec 2025 17:44:21 -0600 Subject: [PATCH 19/19] undo eroneous rollback bits --- Engine/source/gfx/bitmap/bitmapUtils.cpp | 359 ++++++++++++++ Engine/source/gfx/bitmap/gBitmap.cpp | 462 ++++++++++-------- Engine/source/gfx/bitmap/imageUtils.cpp | 2 +- .../source/gfx/bitmap/loaders/bitmapSTB.cpp | 62 +-- 4 files changed, 647 insertions(+), 238 deletions(-) diff --git a/Engine/source/gfx/bitmap/bitmapUtils.cpp b/Engine/source/gfx/bitmap/bitmapUtils.cpp index a9206d7c0e..903fde81eb 100644 --- a/Engine/source/gfx/bitmap/bitmapUtils.cpp +++ b/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) { @@ -156,6 +161,360 @@ void (*bitmapExtrude16BitRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 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) + { + // ---- 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 bitmapStbResizeToOutput(const void* src, U32 srcHeight, U32 srcWidth, void* out, U32 outHeight, U32 outWidth, U32 bpp, GFXFormat format) +{ + StbResizeDesc desc; + if (!getStbResizeDesc(format, desc)) + { + return; + } + + 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); +} + +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) + { + // 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) + { + 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; + } + } + 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) + { + float v = 0.f; + switch (fmt.semantic[c]) + { + 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; + } + } +} + +//-------------------------------------------------------------------------------- +// Main generalized converter +bool bitmapConvertFormat(U8** srcBuffer, U32 pixels, const GBitmapFormatDesc& srcFmt, const GBitmapFormatDesc& dstFmt) +{ + ConversionPlan plan = decideConversion(srcFmt, dstFmt); + if (!plan.bitDepthChange && !plan.channelRepack && !plan.colorspaceChange) + return true; // nothing to do + + void* dstBuffer = *srcBuffer; + + if (plan.bitDepthChange || plan.channelRepack) + dstBuffer = new U8[pixels * dstFmt.channels * dstFmt.bytesPerChannel]; + + for (U32 i = 0; i < pixels; ++i) + { + LinearPixel p = loadPixel(*srcBuffer, srcFmt, i); + storePixel(dstBuffer, dstFmt, i, p); + } + + if (dstBuffer != *srcBuffer) + { + delete[](U8*)* srcBuffer; + *srcBuffer = (U8*)dstBuffer; + } + + return true; +} + +//-------------------------------------------------------------------------------- +// 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; + //-------------------------------------------------------------------------- void bitmapConvertRGB_to_1555_c(U8 *src, U32 pixels) diff --git a/Engine/source/gfx/bitmap/gBitmap.cpp b/Engine/source/gfx/bitmap/gBitmap.cpp index cb7811d7cc..8da4526546 100644 --- a/Engine/source/gfx/bitmap/gBitmap.cpp +++ b/Engine/source/gfx/bitmap/gBitmap.cpp @@ -154,8 +154,6 @@ U32 GBitmap::getFormatBytesPerPixel(GFXFormat fmt) case GFXFormatA8L8: case GFXFormatL16: case GFXFormatR16F: - case GFXFormatR16G16: - case GFXFormatR16G16F: case GFXFormatD16: return 2; @@ -175,6 +173,8 @@ U32 GBitmap::getFormatBytesPerPixel(GFXFormat fmt) case GFXFormatD24X8: case GFXFormatD24S8: case GFXFormatD24FS8: + case GFXFormatR16G16: + case GFXFormatR16G16F: case GFXFormatR8G8B8A8_LINEAR_FORCE: return 4; @@ -473,57 +473,29 @@ void GBitmap::extrudeMipLevels(bool clearBorders) if(mNumMipLevels == 1) allocateBitmap(getWidth(), getHeight(), true, getFormat()); - switch (getFormat()) - { - case GFXFormatR5G5B5A1: - { - for(U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrude5551(getBits(i - 1), getWritableBits(i), getHeight(i), getWidth(i)); - break; - } - - case GFXFormatR8G8B8: - { - for(U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1), mBytesPerPixel); - break; - } - - case GFXFormatR8G8B8A8: - case GFXFormatR8G8B8X8: - case GFXFormatB8G8R8A8: - case GFXFormatR8G8B8A8_SRGB: - { - for(U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrudeRGBA(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1), mBytesPerPixel); - break; - } - - case GFXFormatR16G16B16A16: - { - for (U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrude16BitRGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1), mBytesPerPixel); - break; - } - - case GFXFormatR16G16B16A16F: - { - for (U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrudeFPRGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1), mBytesPerPixel); - break; - } - case GFXFormatR32G32B32A32F: + if (getFormat() == GFXFormatR5G5B5A1) + { + for (U32 i = 1; i < mNumMipLevels; i++) + bitmapExtrude5551(getBits(i - 1), getWritableBits(i), getHeight(i), getWidth(i)); + } + else + { + for (U32 i = 1; i < mNumMipLevels; i++) { - for (U32 i = 1; i < mNumMipLevels; i++) - bitmapExtrudeF32RGBA(getBits(i - 1), getWritableBits(i), getHeight(i - 1), getWidth(i - 1), mBytesPerPixel); - break; + bitmapResizeToOutput( + getBits(i - 1), + getHeight(i - 1), + getWidth(i - 1), + getWritableBits(i), + getHeight(i), + getWidth(i), + mBytesPerPixel, + getFormat() + ); } - - default: - Con::warnf("GBitmap::extrudeMipLevels() - Unsupported format %d", getFormat()); - break; } + if (clearBorders) { for (U32 i = 1; i= mWidth || y >= mHeight) return false; - - const U8* p = getAddress(x, y); + U32 targMip = getNumMipLevels() < mipLevel ? getNumMipLevels()-1 : mipLevel; + U32 targFace = getNumFaces() < face ? getNumFaces()-1 : face; + const U8* p = getAddress(x, y, targMip, targFace); switch (mInternalFormat) { @@ -905,8 +801,18 @@ bool GBitmap::getColor(const U32 x, const U32 y, ColorI& rColor) const #ifdef TORQUE_BIG_ENDIAN l = convertLEndianToHost(l); #endif - U8 lum = l >> 8; - rColor.set(lum, lum, lum, 255); + rColor.set(convert16To8(l), convert16To8(l), convert16To8(l), 255); + break; + } + case GFXFormatR16F: + { + const U16* v = (U16*)p; + rColor.set( + floatTo8(convertHalfToFloat(v[0])), + 0, + 0, + 255 + ); break; } @@ -916,55 +822,98 @@ bool GBitmap::getColor(const U32 x, const U32 y, ColorI& rColor) const rColor.set(p[0], p[1], p[2], 255); break; - // --- 32-bit --- + // --- 32-bit --- + case GFXFormatR32F: + { + const F32* v = (F32*)p; + rColor.set( + floatTo8(v[0]), // red + 0, // green + 0, // blue + 255 // alpha + ); + break; + } + case GFXFormatR16G16: + { + const U16* v = (U16*)p; +#ifdef TORQUE_BIG_ENDIAN + U16 r = convertLEndianToHost(v[0]); + U16 g = convertLEndianToHost(v[1]); +#else + U16 r = v[0]; + U16 g = v[1]; +#endif + rColor.set( + convert16To8(r), // red + convert16To8(g), // green + 0, // blue + 255 // alpha + ); + break; + } + case GFXFormatR16G16F: + { + const U16* v = (U16*)p; + rColor.set( + floatTo8(convertHalfToFloat(v[0])), + floatTo8(convertHalfToFloat(v[1])), + 0, + 255 + ); + break; + } + case GFXFormatR8G8B8A8: case GFXFormatR8G8B8A8_SRGB: - rColor.set(p[0], p[1], p[2], p[3]); + case GFXFormatR8G8B8X8: + rColor.set(p[0], p[1], p[2], (mInternalFormat == GFXFormatR8G8B8X8) ? 255 : p[3]); break; case GFXFormatB8G8R8A8: rColor.set(p[2], p[1], p[0], p[3]); break; - case GFXFormatR8G8B8X8: - rColor.set(p[0], p[1], p[2], 255); - break; - - // --- 64-bit (16 bits per channel) --- + // --- 64-bit --- case GFXFormatR16G16B16A16: { const U16* v = (U16*)p; #ifdef TORQUE_BIG_ENDIAN - rColor.set(v[2] >> 8, v[1] >> 8, v[0] >> 8, v[3] >> 8); // fallback + rColor.set( + convert16To8(v[2]), + convert16To8(v[1]), + convert16To8(v[0]), + convert16To8(v[3])); #else - rColor.set(v[0] >> 8, v[1] >> 8, v[2] >> 8, v[3] >> 8); + rColor.set( + convert16To8(v[0]), + convert16To8(v[1]), + convert16To8(v[2]), + convert16To8(v[3])); #endif break; } - // --- 64-bit float --- case GFXFormatR16G16B16A16F: { - const U16* v = (U16*)p; - F32 r = convertHalfToFloat(v[0]); - F32 g = convertHalfToFloat(v[1]); - F32 b = convertHalfToFloat(v[2]); - F32 a = convertHalfToFloat(v[3]); - rColor.set(mClamp(r * 255.0f, 0.0f, 255.0f), - mClamp(g * 255.0f, 0.0f, 255.0f), - mClamp(b * 255.0f, 0.0f, 255.0f), - mClamp(a * 255.0f, 0.0f, 255.0f)); + const U16* v = (const U16*)p; + rColor.set(floatTo8( + convertHalfToFloat(v[0])), + floatTo8(convertHalfToFloat(v[1])), + floatTo8(convertHalfToFloat(v[2])), + floatTo8(convertHalfToFloat(v[3]))); break; } - // --- 128-bit float --- + // --- 128-bit --- case GFXFormatR32G32B32A32F: { - const F32* v = (F32*)p; - rColor.set(mClamp(v[0] * 255.0f, 0.0f, 255.0f), - mClamp(v[1] * 255.0f, 0.0f, 255.0f), - mClamp(v[2] * 255.0f, 0.0f, 255.0f), - mClamp(v[3] * 255.0f, 0.0f, 255.0f)); + const F32* v = (const F32*)p; + rColor.set( + floatTo8(v[0]), + floatTo8(v[1]), + floatTo8(v[2]), + floatTo8(v[3])); break; } @@ -985,45 +934,158 @@ bool GBitmap::setColor(const U32 x, const U32 y, const ColorI& rColor) if (x >= mWidth || y >= mHeight) return false; - U8* pLoc = getAddress(x, y); + U8* p = getAddress(x, y); + + switch (mInternalFormat) + { - switch (mInternalFormat) { - case GFXFormatA8: - case GFXFormatL8: - *pLoc = rColor.alpha; + // --- 8-bit --- + case GFXFormatA8: + *p = rColor.alpha; break; - case GFXFormatL16: - dMemcpy(pLoc, &rColor, 2 * sizeof(U8)); - break; + case GFXFormatL8: + *p = rColor.red; // L = R channel + break; - case GFXFormatR8G8B8: - dMemcpy( pLoc, &rColor, 3 * sizeof( U8 ) ); + case GFXFormatA4L4: + { + U8 lum = rColor.red / 17; + U8 alp = rColor.alpha / 17; + *p = (alp << 4) | (lum & 0x0F); + break; + } + + // --- 16-bit --- + case GFXFormatR5G6B5: + { + U16 r = rColor.red * 31 / 255; + U16 g = rColor.green * 63 / 255; + U16 b = rColor.blue * 31 / 255; +#ifdef TORQUE_BIG_ENDIAN + * (U16*)p = (r << 11) | (g << 5) | b; +#else + * (U16*)p = (b) | (g << 5) | (r << 11); +#endif break; + } - case GFXFormatR8G8B8A8: - case GFXFormatR8G8B8X8: - dMemcpy( pLoc, &rColor, 4 * sizeof( U8 ) ); + case GFXFormatR5G5B5A1: + { + U16 r = rColor.red * 31 / 255; + U16 g = rColor.green * 31 / 255; + U16 b = rColor.blue * 31 / 255; + U16 a = (rColor.alpha > 0) ? 1 : 0; +#ifdef TORQUE_BIG_ENDIAN + * (U16*)p = (a << 15) | (b << 10) | (g << 5) | r; +#else + * (U16*)p = (r << 11) | (g << 6) | (b << 1) | a; +#endif break; - - case GFXFormatR5G6B5: - #ifdef TORQUE_OS_MAC - *((U16*)pLoc) = (rColor.red << 11) | (rColor.green << 5) | (rColor.blue << 0) ; - #else - *((U16*)pLoc) = (rColor.blue << 0) | (rColor.green << 5) | (rColor.red << 11); - #endif + } + + case GFXFormatA8L8: + { + U16 l = rColor.red; + U16 a = rColor.alpha; +#ifdef TORQUE_BIG_ENDIAN + * (U16*)p = (a << 8) | l; +#else + * (U16*)p = (l) | (a << 8); +#endif break; + } - case GFXFormatR5G5B5A1: - #ifdef TORQUE_OS_MAC - *((U16*)pLoc) = (((rColor.alpha>0) ? 1 : 0)<<15) | (rColor.blue << 10) | (rColor.green << 5) | (rColor.red << 0); - #else - *((U16*)pLoc) = (rColor.blue << 1) | (rColor.green << 6) | (rColor.red << 11) | ((rColor.alpha>0) ? 1 : 0); - #endif + case GFXFormatL16: + *(U16*)p = convert8To16(rColor.red); break; - default: - AssertFatal(false, "Bad internal format"); + case GFXFormatR16F: + { + U16* v = (U16*)p; + v[0] = convertFloatToHalf(rColor.red / 255.f); + break; + } + + // --- 24-bit --- + case GFXFormatR8G8B8: + case GFXFormatR8G8B8_SRGB: + p[0] = rColor.red; + p[1] = rColor.green; + p[2] = rColor.blue; + break; + + // --- 32-bit --- + case GFXFormatR32F: + { + F32* v = (F32*)p; + v[0] = rColor.red / 255.f; + break; + } + case GFXFormatR16G16: + { + U16* v = (U16*)p; + v[0] = convert8To16(rColor.red); + v[1] = convert8To16(rColor.green); + break; + } + case GFXFormatR16G16F: + { + U16* v = (U16*)p; + v[0] = convertFloatToHalf(rColor.red / 255.f); + v[1] = convertFloatToHalf(rColor.green / 255.f); + break; + } + case GFXFormatR8G8B8A8: + case GFXFormatR8G8B8A8_SRGB: + case GFXFormatR8G8B8X8: + p[0] = rColor.red; + p[1] = rColor.green; + p[2] = rColor.blue; + p[3] = (mInternalFormat == GFXFormatR8G8B8X8) ? 255 : rColor.alpha; + break; + + case GFXFormatB8G8R8A8: + p[0] = rColor.blue; + p[1] = rColor.green; + p[2] = rColor.red; + p[3] = rColor.alpha; + break; + + // --- 64-bit --- + case GFXFormatR16G16B16A16: + { + U16* v = (U16*)p; + v[0] = convert8To16(rColor.red); + v[1] = convert8To16(rColor.green); + v[2] = convert8To16(rColor.blue); + v[3] = convert8To16(rColor.alpha); + break; + } + + case GFXFormatR16G16B16A16F: + { + U16* v = (U16*)p; + v[0] = convertFloatToHalf(rColor.red / 255.f); + v[1] = convertFloatToHalf(rColor.green / 255.f); + v[2] = convertFloatToHalf(rColor.blue / 255.f); + v[3] = convertFloatToHalf(rColor.alpha / 255.f); + break; + } + + // --- 128-bit --- + case GFXFormatR32G32B32A32F: + { + F32* v = (F32*)p; + v[0] = rColor.red / 255.f; + v[1] = rColor.green / 255.f; + v[2] = rColor.blue / 255.f; + v[3] = rColor.alpha / 255.f; + break; + } + + default: + AssertFatal(false, "Bad internal format in setColor"); return false; } @@ -1035,7 +1097,7 @@ U8 GBitmap::getChanelValueAt(U32 x, U32 y, U32 chan) { ColorI pixelColor = ColorI(255,255,255,255); getColor(x, y, pixelColor); - if (mInternalFormat == GFXFormatL16) + if (mInternalFormat == GFXFormatL16 || mInternalFormat == GFXFormatL8) { chan = 0; } @@ -1584,6 +1646,7 @@ DefineEngineFunction(saveScaledImage, bool, (const char* bitmapSource, const cha "Saving it out to the destination path.\n") { bool isDDS = false; + bool isHDR = false; if (bitmapSource == 0 || bitmapSource[0] == '\0' || bitmapDest == 0 || bitmapDest[0] == '\0') { @@ -1602,6 +1665,9 @@ DefineEngineFunction(saveScaledImage, bool, (const char* bitmapSource, const cha { if (String::ToLower(ret) == String(".dds")) isDDS = true; + + if (String::ToLower(ret) == String(".hdr")) + isHDR = true; } else { @@ -1635,6 +1701,8 @@ DefineEngineFunction(saveScaledImage, bool, (const char* bitmapSource, const cha if (isPow2(image->getWidth()) && isPow2(image->getHeight())) image->extrudeMipLevels(); + image->setFormat(GFXFormatR8G8B8A8); + U32 mipCount = image->getNumMipLevels(); U32 targetMips = mFloor(mLog2((F32)(resolutionSize ? resolutionSize : 256))) + 1; diff --git a/Engine/source/gfx/bitmap/imageUtils.cpp b/Engine/source/gfx/bitmap/imageUtils.cpp index 283b5c4069..3426eecd3f 100644 --- a/Engine/source/gfx/bitmap/imageUtils.cpp +++ b/Engine/source/gfx/bitmap/imageUtils.cpp @@ -24,7 +24,7 @@ #include "gfx/bitmap/imageUtils.h" #include "gfx/bitmap/ddsFile.h" #include "platform/threads/threadPool.h" -#include "squish.h" +#include "squish/squish.h" namespace ImageUtil { diff --git a/Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp b/Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp index 4a984539fe..b9006b7e0f 100644 --- a/Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp +++ b/Engine/source/gfx/bitmap/loaders/bitmapSTB.cpp @@ -42,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) @@ -61,13 +61,8 @@ static GFXFormat determineFormat(bool isHDR, bool is16Bit, int numChannels) { if (isHDR) { - switch (numChannels) - { - case 1: return GFXFormatR32F; - case 2: return GFXFormatR16G16F; // approximate, most HDRs are 3 or 4 channel though - case 3: return GFXFormatR16G16B16A16F; // store RGB in RGBA - case 4: return GFXFormatR16G16B16A16F; - } + // we force hdr to 4 channels. + return GFXFormatR32G32B32A32F; } else if (is16Bit) { @@ -260,7 +255,7 @@ bool sReadSTB(const Torque::Path& path, GBitmap* bitmap) data = stbi_loadf(filePath, &x, &y, &n, 4); } else if (is16Bit) - data = stbi_load_16(filePath, &x, &y, &n, 4); + data = stbi_load_16(filePath, &x, &y, &n, 0); else data = stbi_load(filePath, &x, &y, &n, 0); @@ -278,21 +273,21 @@ bool sReadSTB(const Torque::Path& path, GBitmap* 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(data)[i]); // convert F32 -> U16 - } - } - else - { + //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(data)[i]); // convert F32 -> U16 + // } + //} + //else + //{ U8* dst = (U8*)bitmap->getBits(); U32 byteSize = bitmap->getByteSize(); dMemcpy(dst, data, byteSize); - } + //} stbi_image_free(data); @@ -366,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; }