From 21eaa1cba08f3b888110f1aa01225f05df7560d4 Mon Sep 17 00:00:00 2001 From: Leo Reyes Date: Tue, 2 Feb 2021 10:34:54 -0800 Subject: [PATCH] Added support to load numbered frame sequences from a directory. The format is: frame_seq = path, fps, is_lightmap, bloom-intensity, black-to-alpha Animated DC covers need to have is_lightmap = 0 and black-to-alpha = 1. Also, there must be a cover texture or the animation won't be activated (because there's nothing to replace). --- impl11/ddraw/Direct3DDevice.cpp | 6 +- impl11/ddraw/Materials.cpp | 116 +++++++++++++++++++- impl11/ddraw/Materials.h | 5 +- impl11/shaders/PixelShaderAnimLightMap.hlsl | 9 +- impl11/shaders/PixelShaderDC.hlsl | 16 ++- impl11/shaders/PixelShaderEmptyDC.hlsl | 9 +- impl11/shaders/PixelShaderNoGlass.hlsl | 5 +- impl11/shaders/PixelShaderTextureCommon.h | 4 +- impl11/shaders/shader_common.h | 1 + 9 files changed, 152 insertions(+), 19 deletions(-) diff --git a/impl11/ddraw/Direct3DDevice.cpp b/impl11/ddraw/Direct3DDevice.cpp index b5ee5e18..4535d7a0 100644 --- a/impl11/ddraw/Direct3DDevice.cpp +++ b/impl11/ddraw/Direct3DDevice.cpp @@ -4991,7 +4991,9 @@ HRESULT Direct3DDevice::Execute( ATCIndex = lastTextureSelected->material.LightMapATCIndex; } else { - resources->InitPixelShader(resources->_noGlassPS); + // If we're rendering a DC element, we don't want to replace the shader + if (g_PSCBuffer.DynCockpitSlots == 0) + resources->InitPixelShader(resources->_noGlassPS); ATCIndex = lastTextureSelected->material.TextureATCIndex; } @@ -5001,6 +5003,8 @@ HRESULT Direct3DDevice::Execute( //int rand_idx = rand() % lastTextureSelected->material.LightMapSequence.size(); int extraTexIdx = atc->Sequence[idx].ExtraTextureIndex; + if (atc->BlackToAlpha) + g_PSCBuffer.special_control = SPECIAL_CONTROL_BLACK_TO_ALPHA; /* // DEBUG diff --git a/impl11/ddraw/Materials.cpp b/impl11/ddraw/Materials.cpp index 73807bc6..02b780fe 100644 --- a/impl11/ddraw/Materials.cpp +++ b/impl11/ddraw/Materials.cpp @@ -2,10 +2,14 @@ #include "Materials.h" #include "utils.h" #include +#include +#include #include "Vectors.h" #include "ShadowMapping.h" #include "DeviceResources.h" +namespace fs = std::filesystem; + bool g_bReloadMaterialsEnabled = false; Material g_DefaultGlobalMaterial; @@ -84,6 +88,16 @@ bool LoadLightColor(char* buf, Vector3* Light) return true; } +void ListFilesInDir(char *path) +{ + std::string s_path = std::string("Animations\\") + std::string(path); + log_debug("[DBG] Listing files under path: %s", s_path.c_str()); + for (const auto & entry : fs::directory_iterator(s_path)) { + // Surely there's a better way to get the filename... + log_debug("[DBG] file: %s", entry.path().filename().string().c_str()); + } +} + #define SKIP_WHITESPACES(s) {\ while (*s != 0 && *s == ' ') s++;\ if (*s == 0) return false;\ @@ -185,6 +199,74 @@ bool LoadTextureSequence(char *buf, std::vector &tex_sequence) { return true; } +bool LoadFrameSequence(char *buf, std::vector &tex_sequence, int *is_lightmap, int *black_to_alpha) { + int res = 0; + char *s = NULL, *t = NULL, path[256]; + float fps = 30.0f, intensity = 0.0f; + *is_lightmap = 0, *black_to_alpha = 1; + + //log_debug("[DBG] [MAT] Reading texture sequence"); + s = strchr(buf, '='); + if (s == NULL) return false; + + // Skip the equals sign: + s += 1; + + // Skip to the next comma + t = s; + while (*t != 0 && *t != ',') t++; + // If we reached the end of the string, that's an error + if (*t == 0) return false; + // End this string on the comma so that we can parse a string + *t = 0; + // Parse the texture name + try { + res = sscanf_s(s, "%s", path, 256); + if (res < 1) log_debug("[DBG] [MAT] Error reading path in '%s'", s); + } + catch (...) { + log_debug("[DBG] [MAT} Could not read path in '%s'", s); + return false; + } + + // Skip the comma + s = t; s += 1; + SKIP_WHITESPACES(s); + + // Parse the remaining fields: fps, lightmap, intensity, black-to-alpha + try { + res = sscanf_s(s, "%f, %d, %f, %d", &fps, is_lightmap, &intensity, black_to_alpha); + if (res < 4) { + log_debug("[DBG] [MAT] Error (using defaults), expected at least 4 elements in %s", s); + } + log_debug("[DBG] [MAT] frame_seq read: %f, %d, %f, %d", fps, *is_lightmap, intensity, *black_to_alpha); + } + catch (...) { + log_debug("[DBG] [MAT] Could not read (fps, lightmap, intensity, black-to-alpha) from %s", s); + return false; + } + + // We just finished reading the path where a frame sequence is stored + // Now we need to load all the frames in that path into a tex_seq_elem + TexSeqElem tex_seq_elem; + std::string s_path = std::string("Animations\\") + std::string(path); + //log_debug("[DBG] Listing files under path: %s", s_path.c_str()); + for (const auto & entry : fs::directory_iterator(s_path)) { + // Surely there's a better way to get the filename... + std::string filename = std::string(path) + "\\" + entry.path().filename().string(); + //log_debug("[DBG] file: %s", filename.c_str()); + + strcpy_s(tex_seq_elem.texname, MAX_TEX_SEQ_NAME, filename.c_str()); + tex_seq_elem.seconds = 1.0f / fps; + tex_seq_elem.intensity = intensity; + // The texture itself will be loaded later. So the reference index is initialized to -1 here: + tex_seq_elem.ExtraTextureIndex = -1; + tex_sequence.push_back(tex_seq_elem); + } + + return true; +} + void ReadMaterialLine(char* buf, Material* curMaterial) { char param[256], svalue[512]; float fValue = 0.0f; @@ -319,10 +401,10 @@ void ReadMaterialLine(char* buf, Material* curMaterial) { _stricmp(param, "anim_seq") == 0 || _stricmp(param, "anim_rand") == 0) { // TOOD: Add support for multiline sequences - // TODO: Fix the alpha issues with side-loaded animations (remove the artifacts around the edges) AnimatedTexControl atc; atc.IsRandom = (_stricmp(param, "lightmap_rand") == 0) || (_stricmp(param, "anim_rand") == 0); - atc.IsLightMap = (_stricmp(param, "lightmap_rand") == 0) || (_stricmp(param, "lightmap_seq") == 0); + bool IsLightMap = (_stricmp(param, "lightmap_rand") == 0) || (_stricmp(param, "lightmap_seq") == 0); + atc.BlackToAlpha = false; // Clear the current Sequence and release the associated textures in the DeviceResources... // TODO: Either release the ExtraTextures pointed at by LightMapSequence, or garbage-collect them // later... @@ -337,7 +419,7 @@ void ReadMaterialLine(char* buf, Material* curMaterial) { atc.TimeLeft = atc.Sequence[0].seconds; // Add a reference to this material on the list of animated materials g_AnimatedMaterials.push_back(atc); - if (atc.IsLightMap) + if (IsLightMap) curMaterial->LightMapATCIndex = g_AnimatedMaterials.size() - 1; else curMaterial->TextureATCIndex = g_AnimatedMaterials.size() - 1; @@ -354,6 +436,34 @@ void ReadMaterialLine(char* buf, Material* curMaterial) { log_debug("[DBG] [MAT] ERROR: No Animation Data Loaded for [%s]", buf); } } + else if (_stricmp(param, "frame_seq") == 0) { + AnimatedTexControl atc; + int is_lightmap, black_to_alpha; + atc.IsRandom = 0; + // Clear the current Sequence and release the associated textures in the DeviceResources... + // TODO: Either release the ExtraTextures pointed at by LightMapSequence, or garbage-collect them + // later... + atc.Sequence.clear(); + log_debug("[DBG] [MAT] Loading Frame Sequence data for [%s]", buf); + if (!LoadFrameSequence(buf, atc.Sequence, &is_lightmap, &black_to_alpha)) + log_debug("[DBG] [MAT] Error loading animated LightMap/Texture data for [%s], syntax error?", buf); + if (atc.Sequence.size() > 0) { + log_debug("[DBG] [MAT] Sequence.size() = %d for Texture [%s]", atc.Sequence.size(), buf); + // Initialize the timer for this animation + atc.AnimIdx = 0; + atc.TimeLeft = atc.Sequence[0].seconds; + atc.BlackToAlpha = (bool)black_to_alpha; + // Add a reference to this material on the list of animated materials + g_AnimatedMaterials.push_back(atc); + if (is_lightmap) + curMaterial->LightMapATCIndex = g_AnimatedMaterials.size() - 1; + else + curMaterial->TextureATCIndex = g_AnimatedMaterials.size() - 1; + } + else { + log_debug("[DBG] [MAT] ERROR: No Animation Data Loaded for [%s]", buf); + } + } /* else if (_stricmp(param, "LavaNormalMult") == 0) { LoadLightColor(buf, &(curMaterial->LavaNormalMult)); diff --git a/impl11/ddraw/Materials.h b/impl11/ddraw/Materials.h index f1b1baba..23db1c24 100644 --- a/impl11/ddraw/Materials.h +++ b/impl11/ddraw/Materials.h @@ -28,15 +28,14 @@ typedef struct AnimatedTexControlStruct { std::vector Sequence; int AnimIdx; // This is the current index in the Sequence, it can increase monotonically, or it can be random. float TimeLeft; // Time left for the current index in the sequence. - bool IsRandom; - bool IsLightMap; // True if the current ATC is a LightMap animation + bool IsRandom, BlackToAlpha; AnimatedTexControlStruct() { Sequence.clear(); AnimIdx = 0; TimeLeft = 1.0f; IsRandom = false; - IsLightMap = true; + BlackToAlpha = false; } // Updates the timer/index on the current animated material. Only call this function diff --git a/impl11/shaders/PixelShaderAnimLightMap.hlsl b/impl11/shaders/PixelShaderAnimLightMap.hlsl index c7027765..056ec368 100644 --- a/impl11/shaders/PixelShaderAnimLightMap.hlsl +++ b/impl11/shaders/PixelShaderAnimLightMap.hlsl @@ -37,6 +37,11 @@ PixelShaderOutput main(PixelShaderInput input) PixelShaderOutput output; float4 texelColor = texture0.Sample(sampler0, input.tex); float alpha = texelColor.w; + float3 color = texelColor.rgb; + float3 HSV = RGBtoHSV(color); + if (special_control == SPECIAL_CONTROL_BLACK_TO_ALPHA) + alpha = HSV.z; + float3 diffuse = lerp(input.color.xyz, 1.0, fDisableDiffuse); float3 P = input.pos3D.xyz; float SSAOAlpha = saturate(min(alpha - fSSAOAlphaOfs, fPosNormalAlpha)); @@ -46,6 +51,8 @@ PixelShaderOutput main(PixelShaderInput input) output.pos3D = float4(P, SSAOAlpha); output.ssMask = 0; + + // hook_normals code: float3 N = normalize(input.normal.xyz * 2.0 - 1.0); N.y = -N.y; // Invert the Y axis, originally Y+ is down @@ -69,9 +76,7 @@ PixelShaderOutput main(PixelShaderInput input) //output.pos3D = 0; //output.normal = 0; //output.diffuse = 0; - float3 color = texelColor.rgb; // This is a light texture, process the bloom mask accordingly - float3 HSV = RGBtoHSV(color); float val = HSV.z; // Enhance = true if (bIsLightTexture > 1) { diff --git a/impl11/shaders/PixelShaderDC.hlsl b/impl11/shaders/PixelShaderDC.hlsl index 66b8c5f3..89bddf3f 100644 --- a/impl11/shaders/PixelShaderDC.hlsl +++ b/impl11/shaders/PixelShaderDC.hlsl @@ -86,8 +86,12 @@ uint getBGColor(uint i) { PixelShaderOutput main(PixelShaderInput input) { PixelShaderOutput output; - float4 coverColor = texture0.Sample(sampler0, input.tex); // coverColor is the cover texture - const float coverAlpha = coverColor.w; // alpha of the cover texture + float4 coverColor = texture0.Sample(sampler0, input.tex); // coverColor/texelColor is the cover texture + float coverAlpha = coverColor.w; // alpha of the cover texture + float3 HSV = RGBtoHSV(coverColor.rgb); + if (special_control == SPECIAL_CONTROL_BLACK_TO_ALPHA) + coverAlpha = HSV.z; + // DEBUG: Make the cover texture transparent to show the DC contents clearly //const float coverAlpha = 0.0; // DEBUG @@ -176,13 +180,13 @@ PixelShaderOutput main(PixelShaderInput input) // on areas with a high lightness value // coverColor is the cover_texture right now - float3 HSV = RGBtoHSV(coverColor.xyz); + //float3 HSV = RGBtoHSV(coverColor.xyz); float brightness = ct_brightness; // The cover texture is bright enough, go shadeless and make it brighter if (HSV.z * coverAlpha >= 0.8) { diffuse = 1; // Increase the brightness: - HSV = RGBtoHSV(coverColor.xyz); + //HSV = RGBtoHSV(coverColor.xyz); // Redundant HSV.z *= 1.2; coverColor.xyz = HSVtoRGB(HSV); output.bloom = float4(fBloomStrength * coverColor.xyz, 1); @@ -216,7 +220,9 @@ PixelShaderOutput main(PixelShaderInput input) output.ssMask = float4(0.0, 1.0, 0.0, 1.0); // No NM, White Spec Val, unused } + // At this point, coverColor is the blended cover texture (if it exists) and the HUD contents if (dc_bloom) { + // coverColor may have changed, we need to convert to HSV again float3 HSV = RGBtoHSV(coverColor.xyz); if (HSV.z >= 0.8) { diffuse = 1.0; @@ -227,7 +233,7 @@ PixelShaderOutput main(PixelShaderInput input) } } - output.color = float4(diffuse * coverColor.xyz, coverColor.w); + output.color = float4(diffuse * coverColor.xyz, coverAlpha); if (bInHyperspace) output.color.a = 1.0; // Text DC elements can be made to float inside the cockpit. In that case, we might want diff --git a/impl11/shaders/PixelShaderEmptyDC.hlsl b/impl11/shaders/PixelShaderEmptyDC.hlsl index 0a17cce3..8788a9fa 100644 --- a/impl11/shaders/PixelShaderEmptyDC.hlsl +++ b/impl11/shaders/PixelShaderEmptyDC.hlsl @@ -9,9 +9,11 @@ #include "PixelShaderTextureCommon.h" #include "DC_common.h" +// Cover texture Texture2D texture0 : register(t0); SamplerState sampler0 : register(s0); +// HUD offscreen buffer Texture2D texture1 : register(t1); SamplerState sampler1 : register(s1); @@ -43,6 +45,9 @@ PixelShaderOutput main(PixelShaderInput input) PixelShaderOutput output; float4 texelColor = texture0.Sample(sampler0, input.tex); float alpha = texelColor.w; + float3 HSV = RGBtoHSV(texelColor.rgb); // texelColor is the cover texture + if (special_control == SPECIAL_CONTROL_BLACK_TO_ALPHA) + alpha = HSV.z; float3 diffuse = lerp(input.color.xyz, 1.0, fDisableDiffuse); // Zero-out the bloom mask. output.bloom = float4(0, 0, 0, 0); @@ -70,14 +75,14 @@ PixelShaderOutput main(PixelShaderInput input) // We assume the caller will set this pixel shader iff DynCockpitSlots == 0 && bUseCoverTexture > 0 // texelColor is the cover_texture right now - float3 HSV = RGBtoHSV(texelColor.xyz); + //float3 HSV = RGBtoHSV(texelColor.xyz); float4 hud_texelColor = float4(0, 0, 0, 1); float brightness = ct_brightness; if (HSV.z * alpha >= 0.8) { // The cover texture is bright enough, go shadeless and make it brighter diffuse = float3(1, 1, 1); // Increase the brightness: - HSV = RGBtoHSV(texelColor.xyz); + //HSV = RGBtoHSV(texelColor.xyz); // Redundant line HSV.z *= 1.2; texelColor.xyz = HSVtoRGB(HSV); output.bloom = float4(fBloomStrength * texelColor.xyz, 1); diff --git a/impl11/shaders/PixelShaderNoGlass.hlsl b/impl11/shaders/PixelShaderNoGlass.hlsl index 161074a6..4f9c4768 100644 --- a/impl11/shaders/PixelShaderNoGlass.hlsl +++ b/impl11/shaders/PixelShaderNoGlass.hlsl @@ -43,6 +43,9 @@ PixelShaderOutput main(PixelShaderInput input) PixelShaderOutput output; float4 texelColor = texture0.Sample(sampler0, input.tex); float alpha = texelColor.a; + float3 HSV = RGBtoHSV(texelColor.rgb); + if (special_control == SPECIAL_CONTROL_BLACK_TO_ALPHA) + alpha = HSV.z; float3 diffuse = lerp(input.color.xyz, 1.0, fDisableDiffuse); float3 P = input.pos3D.xyz; @@ -52,6 +55,7 @@ PixelShaderOutput main(PixelShaderInput input) output.color = texelColor; output.pos3D = float4(P, SSAOAlpha); output.ssMask = 0; + // hook_normals code: float3 N = normalize(input.normal.xyz * 2.0 - 1.0); @@ -67,7 +71,6 @@ PixelShaderOutput main(PixelShaderInput input) output.ssMask = float4(fNMIntensity, fSpecVal, fAmbient, alpha); // bloom - float3 HSV = RGBtoHSV(texelColor.rgb); if (HSV.z >= 0.8) { float bloom_alpha = saturate(fBloomStrength); diffuse = 1.0; diff --git a/impl11/shaders/PixelShaderTextureCommon.h b/impl11/shaders/PixelShaderTextureCommon.h index f073a441..13afb00f 100644 --- a/impl11/shaders/PixelShaderTextureCommon.h +++ b/impl11/shaders/PixelShaderTextureCommon.h @@ -2,8 +2,8 @@ cbuffer ConstantBuffer : register(b0) { float brightness; // Used to dim some elements to prevent the Bloom effect -- mostly for ReShade compatibility - uint DynCockpitSlots; // (Unused here) How many DC slots will be used. - uint bUseCoverTexture; // (Unused here) When set, use the first texture as cover texture for the dynamic cockpit + uint DynCockpitSlots; // (DC) How many DC slots will be used. + uint bUseCoverTexture; // (DC) When set, use the first texture as cover texture for the dynamic cockpit uint bInHyperspace; // 1 if we're rendering while in hyperspace // 16 bytes diff --git a/impl11/shaders/shader_common.h b/impl11/shaders/shader_common.h index 283bc0e1..4b719916 100644 --- a/impl11/shaders/shader_common.h +++ b/impl11/shaders/shader_common.h @@ -51,6 +51,7 @@ LaserPointerCBuffer : register(b8) #define SPECIAL_CONTROL_HIGHLIGHT 5 // Used for debugging purposes (to highlight specific elements) #define SPECIAL_CONTROL_NO_COLOR_ALPHA 6 // When this is set, the alpha value for the color output is forced to 0. Used in AlphaToBloom.hlsl #define SPECIAL_CONTROL_EXPLOSION 7 +#define SPECIAL_CONTROL_BLACK_TO_ALPHA 8 // Used when rendering animated textures so that black becomes transparent // Register slot for the metric reconstruction constant buffer #define METRIC_REC_CB_SLOT 6