diff --git a/BSPConvert.Cmd/Program.cs b/BSPConvert.Cmd/Program.cs index 22e0eaa..f84151d 100644 --- a/BSPConvert.Cmd/Program.cs +++ b/BSPConvert.Cmd/Program.cs @@ -23,6 +23,9 @@ class Options [Option("nozones", Required = false, HelpText = "Ignore timer zone triggers.")] public bool IgnoreZones { get; set; } + [Option("smoothshadows", Required = false, Default = 1, HelpText = "Increase color range for smoother shadows. Higher values may negatively impact brighter areas [1-16].")] + public int SmoothShadows { get; set; } + //[Option("oldbsp", Required = false, HelpText = "Use BSP version 20 (HL2 / CS:S).")] //public bool OldBSP { get; set; } @@ -56,6 +59,9 @@ static void RunCommand(Options options) if (options.DisplacementPower < 2 || options.DisplacementPower > 4) throw new ArgumentOutOfRangeException("Displacement power must be between 2 and 4."); + if (options.SmoothShadows < 1 || options.SmoothShadows > 16) + throw new ArgumentOutOfRangeException("Smooth shadow multiplier must be between 1 and 16."); + if (options.OutputDirectory == null) options.OutputDirectory = Path.GetDirectoryName(options.InputFiles.First()); @@ -68,6 +74,7 @@ static void RunCommand(Options options) DisplacementPower = options.DisplacementPower, minDamageToConvertTrigger = options.MinDamageToConvertTrigger, ignoreZones = options.IgnoreZones, + smoothShadows = options.SmoothShadows, //oldBSP = options.OldBSP, prefix = options.Prefix, inputFile = inputEntry, diff --git a/BSPConvert.Lib/Source/BSPConverter.cs b/BSPConvert.Lib/Source/BSPConverter.cs index 58c4e49..2269523 100644 --- a/BSPConvert.Lib/Source/BSPConverter.cs +++ b/BSPConvert.Lib/Source/BSPConverter.cs @@ -57,6 +57,7 @@ public int DisplacementPower } public int minDamageToConvertTrigger; public bool ignoreZones; + public int smoothShadows; public bool oldBSP; public string prefix; public string inputFile; @@ -220,7 +221,7 @@ private void CreatePakFile() private void ConvertMaterials() { - var materialConverter = new MaterialConverter(contentManager.ContentDir, shaderDict); + var materialConverter = new MaterialConverter(contentManager.ContentDir, shaderDict, options.smoothShadows); foreach (var texture in quakeBsp.Textures) materialConverter.Convert(texture.Name); } @@ -1468,7 +1469,8 @@ private void ConvertInternalLightmaps() var color = ColorUtil.ConvertQ3LightmapToColorRGBExp32( qLightmapData[q3LightmapOffset + index * 3 + 0], qLightmapData[q3LightmapOffset + index * 3 + 1], - qLightmapData[q3LightmapOffset + index * 3 + 2]); + qLightmapData[q3LightmapOffset + index * 3 + 2], + 4 * options.smoothShadows); // internal lightmaps need * 4 brightness, darker than external lightmaps. lmColors.Add(color); } @@ -1536,7 +1538,8 @@ private void ConvertExternalLightmaps() var color = ColorUtil.ConvertQ3LightmapToColorRGBExp32( lmData.data[index * 3 + 0], lmData.data[index * 3 + 1], - lmData.data[index * 3 + 2]); + lmData.data[index * 3 + 2], + options.smoothShadows); lmColors.Add(color); } diff --git a/BSPConvert.Lib/Source/MaterialConverter.cs b/BSPConvert.Lib/Source/MaterialConverter.cs index 9b54b49..40b8848 100644 --- a/BSPConvert.Lib/Source/MaterialConverter.cs +++ b/BSPConvert.Lib/Source/MaterialConverter.cs @@ -8,6 +8,7 @@ namespace BSPConvert.Lib { public class MaterialConverter { + private int smoothShadows; private string pk3Dir; private Dictionary shaderDict; private Dictionary pk3ImageDict; @@ -23,8 +24,9 @@ public class MaterialConverter "up" }; - public MaterialConverter(string pk3Dir, Dictionary shaderDict) + public MaterialConverter(string pk3Dir, Dictionary shaderDict, int smoothShadows) { + this.smoothShadows = smoothShadows; this.pk3Dir = pk3Dir; this.shaderDict = shaderDict; pk3ImageDict = GetImageLookupDictionary(pk3Dir); @@ -201,7 +203,7 @@ private string GenerateUnlitVMT(Shader shader) sb.AppendLine("UnlitGeneric"); sb.AppendLine("{"); - AppendShaderParameters(sb, shader); + AppendShaderParameters(sb, shader, false); sb.AppendLine("}"); @@ -214,14 +216,14 @@ private string GenerateLitVMT(Shader shader) sb.AppendLine("LightmappedGeneric"); sb.AppendLine("{"); - AppendShaderParameters(sb, shader); + AppendShaderParameters(sb, shader, true); sb.AppendLine("}"); return sb.ToString(); } - private void AppendShaderParameters(StringBuilder sb, Shader shader) + private void AppendShaderParameters(StringBuilder sb, Shader shader, bool lightmapped) { var stages = shader.GetImageStages(); var textureStage = stages.FirstOrDefault(x => x.bundles[0].tcGen != TexCoordGen.TCGEN_ENVIRONMENT_MAPPED && x.bundles[0].tcGen != TexCoordGen.TCGEN_LIGHTMAP); @@ -233,8 +235,19 @@ private void AppendShaderParameters(StringBuilder sb, Shader shader) if (textureStage.rgbGen.HasFlag(ColorGen.CGEN_CONST)) { var color = textureStage.constantColor; - var colorStr = $"{color[0]} {color[1]} {color[2]}"; - sb.AppendLine("\t$color \"{" + colorStr + "}\""); + + var r = ColorUtil.GammaToLinear(color[0]); // gamma correct rgb values + var g = ColorUtil.GammaToLinear(color[1]); + var b = ColorUtil.GammaToLinear(color[2]); + + var colorStr = lightmapped ? $"{Math.Round(r / smoothShadows, 2)} {Math.Round(g / smoothShadows, 2)} {Math.Round(b / smoothShadows, 2)}" : $"{r} {g} {b}"; + + sb.AppendLine("\t$color2 \"{" + colorStr + "}\""); + } + else if (lightmapped && smoothShadows > 1) + { + var shadowCorrection = Math.Round(255f / smoothShadows); + sb.AppendLine($$""" $color2 "{ {{shadowCorrection}} {{shadowCorrection}} {{shadowCorrection}} }" """); } if (textureStage.alphaGen.HasFlag(AlphaGen.AGEN_CONST)) @@ -459,12 +472,27 @@ private void CreateDefaultVMT(string texture) private string GenerateDefaultLitVMT(string texture) { - return $$""" + if (smoothShadows > 1) // gamma correct textures to match modified lightmap brightness + { + var shadowCorrection = Math.Round(255f / smoothShadows); + + return $$""" LightmappedGeneric { $basetexture "{{texture}}" + $color2 "{ {{shadowCorrection}} {{shadowCorrection}} {{shadowCorrection}} }" } """; + } + else + { + return $$""" + LightmappedGeneric + { + $basetexture "{{texture}}" + } + """; + } } } } diff --git a/BSPConvert.Lib/Source/Utilities/ColorUtil.cs b/BSPConvert.Lib/Source/Utilities/ColorUtil.cs index ed8a9aa..b1da69d 100644 --- a/BSPConvert.Lib/Source/Utilities/ColorUtil.cs +++ b/BSPConvert.Lib/Source/Utilities/ColorUtil.cs @@ -2,13 +2,13 @@ { public static class ColorUtil { - public static ColorRGBExp32 ConvertQ3LightmapToColorRGBExp32(byte r, byte g, byte b) + public static ColorRGBExp32 ConvertQ3LightmapToColorRGBExp32(byte r, byte g, byte b, float brightness) { var color = new ColorRGBExp32(); - var rf = GammaToLinear(r) * 4f; // Multiply by 4 since Source expects lightmap values in 0-4 range - var gf = GammaToLinear(g) * 4f; - var bf = GammaToLinear(b) * 4f; + var rf = GammaToLinear(r) * brightness; + var gf = GammaToLinear(g) * brightness; + var bf = GammaToLinear(b) * brightness; var max = Math.Max(rf, Math.Max(gf, bf)); var exp = CalcExponent(max); @@ -24,7 +24,7 @@ public static ColorRGBExp32 ConvertQ3LightmapToColorRGBExp32(byte r, byte g, byt return color; } - private static float GammaToLinear(byte gamma) + public static float GammaToLinear(byte gamma) { return (float)(255.0 * Math.Pow(gamma / 255.0, 2.2)); }