diff --git a/TheForceEngine/Shaders/gpu_render_wall.frag b/TheForceEngine/Shaders/gpu_render_wall.frag index c324efe3d..16a17ce54 100644 --- a/TheForceEngine/Shaders/gpu_render_wall.frag +++ b/TheForceEngine/Shaders/gpu_render_wall.frag @@ -223,13 +223,29 @@ void main() else #endif { + #ifdef OPT_TRUE_COLOR + vec3 tint; + baseColor = sampleTexture(Frag_TextureId, uv, sky, flip, applyFlatWarp, tint); + // Per-texture color correction factor. + if (light < 31.0) + { + float tintFactor = clamp((31.0 - light) / 15.0, 0.0, 1.0) * (1.0 - clamp((baseColor.a - 0.5) * 2.0, 0.0, 1.0)); + baseColor.rgb *= mix(vec3(1.0), tint, tintFactor); + } + #else baseColor = sampleTexture(Frag_TextureId, uv, sky, flip, applyFlatWarp); + #endif } // Handle sky fading. if (skyFade > 0.0 && getBayerIndex(uv) < skyFade) { + #ifdef OPT_TRUE_COLOR + vec3 tint; // ignore. + baseColor = sampleTexture(Frag_TextureId, vec2(256.0, yLimit), sky, flip, false, tint); + #else baseColor = sampleTexture(Frag_TextureId, vec2(256.0, yLimit), sky, flip, false); + #endif } // Compute emissive. diff --git a/TheForceEngine/Shaders/textureSampleFunc.h b/TheForceEngine/Shaders/textureSampleFunc.h index fad83195b..089082153 100644 --- a/TheForceEngine/Shaders/textureSampleFunc.h +++ b/TheForceEngine/Shaders/textureSampleFunc.h @@ -4,6 +4,8 @@ uniform isamplerBuffer TextureTable; uniform sampler2D Colormap; // The color map has full RGB pre-backed in. uniform sampler2D Palette; +uniform uint TextureSettings; + #ifdef OPT_TRUE_COLOR uniform vec4 TexSamplingParam; #endif @@ -58,8 +60,8 @@ vec2 wrapCoordF(vec2 uv, vec2 edge) uv += vec2(0.5,-0.5); uv = fmod(uv, edge); - uv.x += (uv.x < 0) ? edge.x : 0; - uv.y += (uv.y < 0) ? edge.y : 0; + uv.x += (uv.x < 0.0) ? edge.x : 0.0; + uv.y += (uv.y < 0.0) ? edge.y : 0.0; uv -= vec2(0.5,-0.5); return uv; @@ -70,8 +72,8 @@ vec2 wrapCoordFlatF(vec2 uv, vec2 edge) { float coord = fmod(uv.x, 64.0) * 64.0 + fmod(uv.y, 64.0); uv = wrapCoordF(vec2(coord / edge.y, coord), edge); - uv.x += (uv.x < 0) ? edge.x : 0; - uv.y += (uv.y < 0) ? edge.y : 0; + uv.x += (uv.x < 0.0) ? edge.x : 0.0; + uv.y += (uv.y < 0.0) ? edge.y : 0.0; return uv; } @@ -94,9 +96,25 @@ vec2 bilinearDither(vec2 uv) // TODO: Make this optional. #define OPT_MIPMAPPING 1 +vec3 getHalfTint(ivec2 packedColor) +{ + int r = (packedColor.x >> 15) & 255; + int g = (packedColor.x >> 23) & 255; + int b = (packedColor.y >> 15) & 255; + + float scale = 1.0 / 255.0; + vec3 tint; + tint.x = float(r) * scale; + tint.y = float(g) * scale; + tint.z = float(b) * scale; + return tint; +} + vec4 sampleTexture(int id, vec2 uv) { ivec4 sampleData = texelFetch(TextureTable, id); + sampleData.zw &= ivec2(32767); + vec2 baseUv = uv; uv = bilinearSharpness(uv, TexSamplingParam.x); @@ -113,9 +131,12 @@ vec4 sampleTexture(int id, vec2 uv) #endif } -vec4 sampleTexture(int id, vec2 uv, bool sky, bool flip, bool applyFlatWarp) +vec4 sampleTexture(int id, vec2 uv, bool sky, bool flip, bool applyFlatWarp, out vec3 tint) { ivec4 sampleData = texelFetch(TextureTable, id); + tint = TextureSettings == 0u ? vec3(1.0) : getHalfTint(sampleData.zw); + sampleData.zw &= ivec2(32767); + vec2 baseUv = uv; uv = bilinearSharpness(uv, TexSamplingParam.x); @@ -172,6 +193,8 @@ vec4 sampleTexture(int id, vec2 uv, bool sky, bool flip, bool applyFlatWarp) vec4 sampleTextureClamp(int id, vec2 uv) { ivec4 sampleData = texelFetch(TextureTable, id); + sampleData.zw &= ivec2(32767); + uv = bilinearSharpness(uv, TexSamplingParam.x); vec3 uv3 = vec3(uv, 0.0); @@ -184,6 +207,8 @@ vec4 sampleTextureClamp(int id, vec2 uv) vec4 sampleTextureClamp(int id, vec2 uv, bool opaque) { ivec4 sampleData = texelFetch(TextureTable, id); + sampleData.zw &= ivec2(32767); + vec2 baseUv = uv; uv = bilinearSharpness(uv, TexSamplingParam.x); @@ -208,6 +233,8 @@ vec4 sampleTextureClamp(int id, vec2 uv, bool opaque) float sampleTexture(int id, vec2 uv) { ivec4 sampleData = texelFetch(TextureTable, id); + sampleData.zw &= ivec2(32767); + ivec3 iuv; #ifdef OPT_BILINEAR_DITHER @@ -227,6 +254,8 @@ float sampleTexture(int id, vec2 uv) float sampleTexture(int id, vec2 uv, bool sky, bool flip, bool applyFlatWarp) { ivec4 sampleData = texelFetch(TextureTable, id); + sampleData.zw &= ivec2(32767); + ivec3 iuv; #ifdef OPT_BILINEAR_DITHER @@ -274,6 +303,8 @@ float sampleTexture(int id, vec2 uv, bool sky, bool flip, bool applyFlatWarp) float sampleTextureClamp(int id, vec2 uv) { ivec4 sampleData = texelFetch(TextureTable, id); + sampleData.zw &= ivec2(32767); + ivec3 iuv; #ifdef OPT_BILINEAR_DITHER @@ -297,6 +328,8 @@ float sampleTextureClamp(int id, vec2 uv) float sampleTextureClamp(int id, vec2 uv, bool opaque) { ivec4 sampleData = texelFetch(TextureTable, id); + sampleData.zw &= ivec2(32767); + ivec3 iuv; #ifdef OPT_BILINEAR_DITHER diff --git a/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/modelGPU.cpp b/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/modelGPU.cpp index ad0c0ea9c..9469e1bf6 100644 --- a/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/modelGPU.cpp +++ b/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/modelGPU.cpp @@ -37,6 +37,7 @@ namespace TFE_Jedi { extern s32 s_drawnObjCount; extern SecObject* s_drawnObj[]; + extern u32 s_textureSettings; enum ModelShader { @@ -116,6 +117,7 @@ namespace TFE_Jedi s32 texSamplingParamId; s32 palFxLumMask; s32 palFxFlash; + s32 textureSettings; }; static ShaderInputs s_shaderInputs[MGPU_SHADER_COUNT]; static std::vector s_modelDrawList[MGPU_SHADER_COUNT]; @@ -196,6 +198,7 @@ namespace TFE_Jedi s_shaderInputs[variant].texSamplingParamId = shader->getVariableId("TexSamplingParam"); s_shaderInputs[variant].palFxLumMask = shader->getVariableId("PalFxLumMask"); s_shaderInputs[variant].palFxFlash = shader->getVariableId("PalFxFlash"); + s_shaderInputs[variant].textureSettings = shader->getVariableId("TextureSettings"); shader->bindTextureNameToSlot("Palette", 0); shader->bindTextureNameToSlot("Colormap", 1); @@ -827,6 +830,10 @@ namespace TFE_Jedi shader->setVariable(s_shaderInputs[s].palFxLumMask, SVT_VEC3, lumMask.m); shader->setVariable(s_shaderInputs[s].palFxFlash, SVT_VEC3, palFx.m); } + if (s_shaderInputs[s].textureSettings >= 0) + { + shader->setVariable(s_shaderInputs[s].textureSettings, SVT_USCALAR, &s_textureSettings); + } if (s == MGPU_SHADER_TRANS && s_shaderSettings.trueColor) { diff --git a/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/rsectorGPU.cpp b/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/rsectorGPU.cpp index 7f0fd09cc..2cd4bf5bc 100644 --- a/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/rsectorGPU.cpp +++ b/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/rsectorGPU.cpp @@ -90,6 +90,7 @@ namespace TFE_Jedi s32 texSamplingParamId; s32 palFxLumMask; s32 palFxFlash; + s32 textureSettings; }; struct ShaderSkyInputs { @@ -144,6 +145,7 @@ namespace TFE_Jedi static Vec3f s_clipObjPos; static JBool s_flushCache = JFALSE; + u32 s_textureSettings = 1u; extern Mat3 s_cameraMtx; extern Mat4 s_cameraProj; @@ -171,6 +173,7 @@ namespace TFE_Jedi s_shaderInputs[SPRITE_PASS].texSamplingParamId = s_spriteShader.getVariableId("TexSamplingParam"); s_shaderInputs[SPRITE_PASS].palFxLumMask = s_spriteShader.getVariableId("PalFxLumMask"); s_shaderInputs[SPRITE_PASS].palFxFlash = s_spriteShader.getVariableId("PalFxFlash"); + s_shaderInputs[SPRITE_PASS].textureSettings = s_spriteShader.getVariableId("TextureSettings"); s_cameraRightId = s_spriteShader.getVariableId("CameraRight"); s_spriteShader.bindTextureNameToSlot("DrawListPosXZ_Texture", 0); @@ -206,6 +209,7 @@ namespace TFE_Jedi s_shaderInputs[index].texSamplingParamId = s_wallShader[index].getVariableId("TexSamplingParam"); s_shaderInputs[index].palFxLumMask = s_wallShader[index].getVariableId("PalFxLumMask"); s_shaderInputs[index].palFxFlash = s_wallShader[index].getVariableId("PalFxFlash"); + s_shaderInputs[index].textureSettings = s_wallShader[index].getVariableId("TextureSettings"); s_shaderSkyInputs[index].skyParallaxId = s_wallShader[index].getVariableId("SkyParallax"); s_shaderSkyInputs[index].skyParam0Id = s_wallShader[index].getVariableId("SkyParam0"); @@ -426,7 +430,6 @@ namespace TFE_Jedi TFE_Image::writeImage("ColorMap.png", 256-32, 32, outImage); #endif - // for (s32 i = 1; i < 32; i++) { palIndex = getColormapWhiterampColor(i); @@ -474,6 +477,7 @@ namespace TFE_Jedi // RGB = Multiply color, A = ? // RGB = Fog color, A = blendFactor Vec4f mulRamp[32], fogRamp[32]; + bool ignoreTextureTint = false; for (s32 i = 0; i < fogRegionCount; i++) { curRegion = ®ions[i]; @@ -483,6 +487,11 @@ namespace TFE_Jedi Vec3f mulColor = curRegion->start == 31 ? white : curRegion->startColor; Vec3f fogColor = curRegion->endColor; + if (fogColor.x > 0.1f || fogColor.y > 0.1f || fogColor.z > 0.1f) + { + ignoreTextureTint = true; + } + for (s32 j = curRegion->start; j >= curRegion->end; j--) { f32 fogFactor = f32(curRegion->start - j) / f32(curRegion->start - curRegion->end); @@ -522,6 +531,7 @@ namespace TFE_Jedi #endif TFE_RenderBackend::freeTexture(s_trueColorMapping); s_trueColorMapping = TFE_RenderBackend::createTexture(64, 1, mappingTable); + s_textureSettings = (ignoreTextureTint) ? 0 : 1; // Generate a comparison color map. #if SHOW_TRUE_COLOR_COMPARISION @@ -1912,6 +1922,10 @@ namespace TFE_Jedi shader->setVariable(s_shaderInputs[pass].palFxLumMask, SVT_VEC3, lumMask.m); shader->setVariable(s_shaderInputs[pass].palFxFlash, SVT_VEC3, palFx.m); } + if (s_shaderInputs[pass].textureSettings >= 0) + { + shader->setVariable(s_shaderInputs[pass].textureSettings, SVT_USCALAR, &s_textureSettings); + } // Draw the sector display list. sdisplayList_draw(pass); @@ -1998,6 +2012,10 @@ namespace TFE_Jedi s_spriteShader.setVariable(s_shaderInputs[SPRITE_PASS].palFxLumMask, SVT_VEC3, lumMask.m); s_spriteShader.setVariable(s_shaderInputs[SPRITE_PASS].palFxFlash, SVT_VEC3, palFx.m); } + if (s_shaderInputs[SPRITE_PASS].textureSettings >= 0) + { + s_spriteShader.setVariable(s_shaderInputs[SPRITE_PASS].textureSettings, SVT_USCALAR, &s_textureSettings); + } // Draw the sector display list. sprdisplayList_draw(); diff --git a/TheForceEngine/TFE_RenderShared/texturePacker.cpp b/TheForceEngine/TFE_RenderShared/texturePacker.cpp index e4a15d608..c441840e0 100644 --- a/TheForceEngine/TFE_RenderShared/texturePacker.cpp +++ b/TheForceEngine/TFE_RenderShared/texturePacker.cpp @@ -410,22 +410,68 @@ namespace TFE_Jedi } } - void packNode(const TextureNode* node, const TextureData* texData, Vec4i* tableEntry, s32 paddingX, s32 paddingY, s32 mipCount) + f32 getSaturation(u32 color) { - if (texData->width == 128 && texData->height == 256) - { - static s32 _x = 0; - _x++; - } + if ((color & 0x00ffffff) == 0) { return 0.0f; } + + const f32 scale = 1.0f / 255.0f; + const f32 r = f32(color & 0xff) * scale; + const f32 g = f32((color >> 8) & 0xff) * scale; + const f32 b = f32((color >> 16) & 0xff) * scale; + + const f32 maxc = max(r, max(g, b)); + const f32 minc = min(r, min(g, b)); + const f32 lum = maxc - minc; + + const f32 sat = (lum < 0.5f) ? (maxc - minc) / (maxc + minc) : (maxc - minc) / (2.0f - maxc - minc); + return sat; + } + f64 addMultiplier(u32 cFull, u32 cHalf, f64* accum) + { + if ((cFull & 0x00ffffff) == 0) { return 0.0; } + + u32 r[2] = { cFull & 0xff, cHalf & 0xff }; + u32 g[2] = { (cFull >> 8) & 0xff, (cHalf >> 8) & 0xff }; + u32 b[2] = { (cFull >> 16) & 0xff, (cHalf >> 16) & 0xff }; + + f64 scale = 1.0 / 255.0; + f64 rFull = f64(r[0]) * scale; + f64 gFull = f64(g[0]) * scale; + f64 bFull = f64(b[0]) * scale; + f64 rHalf = f64(r[1]) * scale; + f64 gHalf = f64(g[1]) * scale; + f64 bHalf = f64(b[1]) * scale; + + f64 mR = rFull > FLT_EPSILON ? rHalf / rFull : -1.0; + f64 mG = gFull > FLT_EPSILON ? gHalf / gFull : -1.0; + f64 mB = bFull > FLT_EPSILON ? bHalf / bFull : -1.0; + + accum[0] += mR > FLT_EPSILON ? mR : 0.0; + accum[1] += mG > FLT_EPSILON ? mG : 0.0; + accum[2] += mB > FLT_EPSILON ? mB : 0.0; + return 1.0; + } + + void packNode(const TextureNode* node, const TextureData* texData, Vec4i* tableEntry, s32 paddingX, s32 paddingY, s32 mipCount) + { // Copy the texture into place. s32 offsetX = paddingX / 2; s32 offsetY = paddingY / 2; const u8* srcImage = texData->image; + + Vec3f halfTint = { 1.0f, 1.0f, 1.0f }; + s32 grayScaleCount = 0; + s32 grayScaleCountHalf = 0; + s32 totalCount = 0; if (s_texturePacker->trueColor) { const u32* pal = getPalette(texData->palIndex); const u8* remap = &TFE_DarkForces::s_levelColorMap[31 << 8]; + const u8* remapHalf = &TFE_DarkForces::s_levelColorMap[16 << 8]; + + f64 accum[3] = { 0.0 }; + f64 accumCount = 0.0; u32* output = (u32*)getWritePointer(s_currentPage, node->rect.x, node->rect.y, 0); for (s32 y = 0; y < texData->height+paddingY; y++, output += s_texturePacker->width) @@ -458,6 +504,22 @@ namespace TFE_Jedi else { output[x] = palIndex == 0 ? 0u : pal[remap[palIndex]]; + f32 sat = getSaturation(output[x]); + if (sat < 0.1f) + { + grayScaleCount++; + } + totalCount++; + + // Also figure out the average multiplier. + u32 halfValue = palIndex == 0 ? 0u : pal[remapHalf[palIndex]]; + accumCount += addMultiplier(output[x], halfValue, accum); + + sat = getSaturation(halfValue); + if (sat < 0.1f) + { + grayScaleCountHalf++; + } } if (!(texData->flags & INDEXED)) @@ -467,6 +529,24 @@ namespace TFE_Jedi } } + if (accumCount > 0.0) + { + // Try to guess at which textures have areas that should not be tinted. + if (grayScaleCount > totalCount / 4 && grayScaleCountHalf > 3 * totalCount / 8) + { + halfTint = { 1.0f, 1.0f, 1.0f }; + } + else + { + const f64 scale = 1.0 / max(accum[0], max(accum[1], accum[2])); + accum[0] *= scale; + accum[1] *= scale; + accum[2] *= scale; + + halfTint = { f32(accum[0]), f32(accum[1]), f32(accum[2]) }; + } + } + u32* source = (u32*)getWritePointer(s_currentPage, node->rect.x, node->rect.y, 0); u32 w = texData->width + paddingX; u32 h = texData->height + paddingY; @@ -503,6 +583,13 @@ namespace TFE_Jedi // Page the page index into the x offset. tableEntry->x |= (s_currentPage << 12); + + // Half color tint packed. + s32 r = s32(halfTint.x * 255.0); + s32 g = s32(halfTint.y * 255.0); + s32 b = s32(halfTint.z * 255.0); + tableEntry->z |= ((r << 15) | (g << 23)); + tableEntry->w |= (b << 15); } void packNodeDeltaTex(const TextureNode* node, const TextureData* texData, Vec4i* tableEntry, s32 paddingX, s32 paddingY) diff --git a/TheForceEngine/gitVersion.h b/TheForceEngine/gitVersion.h index bcd0032d8..6bb1e0fdc 100644 --- a/TheForceEngine/gitVersion.h +++ b/TheForceEngine/gitVersion.h @@ -1,3 +1,3 @@ const char c_gitVersion[] = R"( -v1.09.500-4-g609ff030 +v1.09.510 )";