diff --git a/TombLib/TombLib/GeometryIO/IOMaterial.cs b/TombLib/TombLib/GeometryIO/IOMaterial.cs index 9305772a9d..e11c3b929d 100644 --- a/TombLib/TombLib/GeometryIO/IOMaterial.cs +++ b/TombLib/TombLib/GeometryIO/IOMaterial.cs @@ -6,7 +6,8 @@ public class IOMaterial { public string Name { get; private set; } public Texture Texture { get; set; } - public bool AdditiveBlending { get; set; } + public BlendMode BlendMode { get; set; } + public bool AdditiveBlending => BlendMode >= BlendMode.Additive; public bool DoubleSided { get; set; } public int Shininess { get; set; } @@ -20,11 +21,11 @@ public IOMaterial(string name) Name = name; } - public IOMaterial(string name, Texture texture, string texturePath,bool additiveBlending, bool doubleSided, int shininess, int page) + public IOMaterial(string name, Texture texture, string texturePath, BlendMode blendMode, bool doubleSided, int shininess, int page) { Name = name; Texture = texture; - AdditiveBlending = additiveBlending; + BlendMode = blendMode; DoubleSided = doubleSided; Shininess = shininess; Path = texturePath; diff --git a/TombLib/TombLib/GeometryIO/IOModel.cs b/TombLib/TombLib/GeometryIO/IOModel.cs index 679caa15aa..c85e1789d7 100644 --- a/TombLib/TombLib/GeometryIO/IOModel.cs +++ b/TombLib/TombLib/GeometryIO/IOModel.cs @@ -41,12 +41,12 @@ public BoundingSphere BoundingSphere } } - public IOMaterial GetMaterial(Texture texture, bool blending, int page, bool doubleSided, int shininess) + public IOMaterial GetMaterial(Texture texture, BlendMode blendMode, int page, bool doubleSided, int shininess) { foreach (var mat in Materials) if (mat.Page == page) if (mat.Texture.Equals(texture)) - if (mat.AdditiveBlending == blending) + if (mat.BlendMode == blendMode) if (mat.DoubleSided == doubleSided) if (mat.Shininess == shininess) return mat; diff --git a/TombLib/TombLib/GeometryIO/Importers/Assimp.cs b/TombLib/TombLib/GeometryIO/Importers/Assimp.cs index 774f40b52a..881ba5e3c2 100644 --- a/TombLib/TombLib/GeometryIO/Importers/Assimp.cs +++ b/TombLib/TombLib/GeometryIO/Importers/Assimp.cs @@ -146,12 +146,18 @@ public override IOModel ImportFromFile(string filename) // Create the new material material.Texture = textures[i]; - material.AdditiveBlending = (mat.HasBlendMode && mat.BlendMode == Assimp.BlendMode.Additive) || mat.Opacity < 1.0f - || mat.Name.StartsWith(Graphics.Material.Material_AdditiveBlending) - || mat.Name.StartsWith(Graphics.Material.Material_AdditiveBlendingDoubleSided); - material.DoubleSided = (mat.HasTwoSided && mat.IsTwoSided) - || mat.Name.StartsWith(Graphics.Material.Material_OpaqueDoubleSided) - || mat.Name.StartsWith(Graphics.Material.Material_AdditiveBlendingDoubleSided); + + if (mat.Name.StartsWith("Te")) + { + material.BlendMode = Graphics.Material.GetBlendModeFromName(mat.Name); + material.DoubleSided = Graphics.Material.GetDoubleSidedFromName(mat.Name); + } + else + { + material.BlendMode = (mat.HasBlendMode && mat.BlendMode == Assimp.BlendMode.Additive) || mat.Opacity < 1.0f + ? TombLib.Utils.BlendMode.Additive : TombLib.Utils.BlendMode.Normal; + material.DoubleSided = mat.HasTwoSided && mat.IsTwoSided; + } // HACK: Ass-imp uses different numbering for shininess in different formats! diff --git a/TombLib/TombLib/GeometryIO/Importers/Metasequoia.cs b/TombLib/TombLib/GeometryIO/Importers/Metasequoia.cs index d414eb251f..593a953df2 100644 --- a/TombLib/TombLib/GeometryIO/Importers/Metasequoia.cs +++ b/TombLib/TombLib/GeometryIO/Importers/Metasequoia.cs @@ -93,7 +93,8 @@ public override IOModel ImportFromFile(string filename) if (tokens.Length == 4 && float.TryParse(tokens[3], out alpha)) { - material.AdditiveBlending = (alpha < 1.0f); + if (alpha < 1.0f) + material.BlendMode = TombLib.Utils.BlendMode.Additive; } } diff --git a/TombLib/TombLib/GeometryIO/RoomGeometryExporter.cs b/TombLib/TombLib/GeometryIO/RoomGeometryExporter.cs index ea701e662e..d4d6c44661 100644 --- a/TombLib/TombLib/GeometryIO/RoomGeometryExporter.cs +++ b/TombLib/TombLib/GeometryIO/RoomGeometryExporter.cs @@ -133,15 +133,13 @@ public static RoomExportResult ExportRooms(IEnumerable roomsToExport, stri var page = new SplitPageReference(tex, y * numXPages + x, x, y, Path.Combine(Path.GetDirectoryName(filePath), textureFileName)); splitPages.Add(page); - var matOpaque = new IOMaterial(Material.Material_Opaque + "_" + j + "_" + page.Index, tex, page.Path, false, false, 0, page.Index); - var matOpaqueDoubleSided = new IOMaterial(Material.Material_OpaqueDoubleSided + "_" + j + "_" + page.Index, tex, page.Path, false, true, 0, page.Index); - var matAdditiveBlending = new IOMaterial(Material.Material_AdditiveBlending + "_" + j + "_" + page.Index, tex, page.Path, true, false, 0, page.Index); - var matAdditiveBlendingDoubleSided = new IOMaterial(Material.Material_AdditiveBlendingDoubleSided + "_" + j + "_" + page.Index, tex, page.Path, true, true, 0, page.Index); - - model.Materials.Add(matOpaque); - model.Materials.Add(matOpaqueDoubleSided); - model.Materials.Add(matAdditiveBlending); - model.Materials.Add(matAdditiveBlendingDoubleSided); + foreach (BlendMode mode in Enum.GetValues(typeof(BlendMode))) + { + var prefix = Material.GetPrefixForBlendMode(mode); + var suffix = j + "_" + page.Index; + model.Materials.Add(new IOMaterial(prefix + "_" + suffix, tex, page.Path, mode, false, 0, page.Index)); + model.Materials.Add(new IOMaterial(prefix + Material.DoubleSidedSuffix + "_" + suffix, tex, page.Path, mode, true, 0, page.Index)); + } } } } @@ -250,7 +248,7 @@ public static RoomExportResult ExportRooms(IEnumerable roomsToExport, stri } var mat = model.GetMaterial(textureArea1.Texture, - textureArea1.BlendMode >= BlendMode.Additive, + textureArea1.BlendMode, textureAreaPage, textureArea1.DoubleSided, 0); @@ -295,7 +293,7 @@ public static RoomExportResult ExportRooms(IEnumerable roomsToExport, stri mesh.Colors.Add(new Vector4(room.RoomGeometry.VertexColors[i + 2], 1.0f)); var mat = model.GetMaterial(textureArea.Texture, - textureArea.BlendMode >= BlendMode.Additive, + textureArea.BlendMode, textureAreaPage, textureArea.DoubleSided, 0); diff --git a/TombLib/TombLib/Graphics/Material.cs b/TombLib/TombLib/Graphics/Material.cs index 259d83ce23..b1fcd89101 100644 --- a/TombLib/TombLib/Graphics/Material.cs +++ b/TombLib/TombLib/Graphics/Material.cs @@ -4,10 +4,51 @@ namespace TombLib.Graphics { public class Material { + public const string DoubleSidedSuffix = "DS"; + + // Opaque (normal) prefix. public const string Material_Opaque = "TeOp"; public const string Material_OpaqueDoubleSided = "TeOpDS"; - public const string Material_AdditiveBlending = "TeBl"; + + // Blend mode prefixes. "TeBl" is kept for backward compatibility with Additive. + public const string Material_AdditiveBlending = "TeBl"; public const string Material_AdditiveBlendingDoubleSided = "TeBlDS"; + public const string Material_AlphaTest = "TeBlAT"; + public const string Material_AlphaTestDoubleSided = "TeBlATDS"; + public const string Material_Distortion = "TeBlDis"; + public const string Material_DistortionDoubleSided = "TeBlDisDS"; + public const string Material_NoZTest = "TeBlNZ"; + public const string Material_NoZTestDoubleSided = "TeBlNZDS"; + public const string Material_Subtract = "TeBlSub"; + public const string Material_SubtractDoubleSided = "TeBlSubDS"; + public const string Material_Wireframe = "TeBlWf"; + public const string Material_WireframeDoubleSided = "TeBlWfDS"; + public const string Material_Exclude = "TeBlExc"; + public const string Material_ExcludeDoubleSided = "TeBlExcDS"; + public const string Material_Screen = "TeBlScr"; + public const string Material_ScreenDoubleSided = "TeBlScrDS"; + public const string Material_Lighten = "TeBlLig"; + public const string Material_LightenDoubleSided = "TeBlLigDS"; + public const string Material_AlphaBlend = "TeBlAB"; + public const string Material_AlphaBlendDoubleSided = "TeBlABDS"; + + // Lookup from prefix to blend mode. + // Longer (more specific) entries must precede shorter generic ones to prevent + // partial matches (e.g., "TeBlSub" must come before "TeBl"). + private static readonly (string Prefix, BlendMode Mode)[] BlendModePrefixLookup = + { + (Material_AlphaTest, BlendMode.AlphaTest), + (Material_Distortion, BlendMode.Distortion), + (Material_NoZTest, BlendMode.NoZTest), + (Material_Subtract, BlendMode.Subtract), + (Material_Wireframe, BlendMode.Wireframe), + (Material_Exclude, BlendMode.Exclude), + (Material_Screen, BlendMode.Screen), + (Material_Lighten, BlendMode.Lighten), + (Material_AlphaBlend, BlendMode.AlphaBlend), + (Material_AdditiveBlending, BlendMode.Additive), + (Material_Opaque, BlendMode.Normal), + }; public string Name { get; private set; } public Texture Texture { get; set; } @@ -43,5 +84,45 @@ public void SetStates(SharpDX.Toolkit.Graphics.GraphicsDevice device, bool trans else device.SetRasterizerState(device.RasterizerStates.CullBack); } + + // Returns the material name prefix for the given blend mode. + public static string GetPrefixForBlendMode(BlendMode mode) + { + foreach (var (prefix, blendMode) in BlendModePrefixLookup) + if (blendMode == mode) + return prefix; + return Material_Opaque; + } + + // Parses the blend mode from a material name using the Te prefix convention. + public static BlendMode GetBlendModeFromName(string name) + { + foreach (var (prefix, mode) in BlendModePrefixLookup) + if (MatchesMaterialPrefix(name, prefix)) + return mode; + return BlendMode.Normal; + } + + // Returns true if the material name indicates a double-sided surface. + public static bool GetDoubleSidedFromName(string name) + { + foreach (var (prefix, _) in BlendModePrefixLookup) + if (MatchesMaterialPrefix(name, prefix + DoubleSidedSuffix)) + return true; + return false; + } + + // Returns true if name starts with prefix and is immediately followed by "DS", "_", or end of string. + private static bool MatchesMaterialPrefix(string name, string prefix) + { + if (!name.StartsWith(prefix)) + return false; + int after = prefix.Length; + if (after >= name.Length || name[after] == '_') + return true; + if (after + 1 < name.Length && name[after] == 'D' && name[after + 1] == 'S') + return true; + return false; + } } } diff --git a/TombLib/TombLib/Wad/WadMesh.cs b/TombLib/TombLib/Wad/WadMesh.cs index ca712fd5d8..a552c49796 100644 --- a/TombLib/TombLib/Wad/WadMesh.cs +++ b/TombLib/TombLib/Wad/WadMesh.cs @@ -369,15 +369,12 @@ public static IOModel PrepareForExport(string filePath, IOGeometrySettings setti var textureFileName = name + "_" + i + ".png"; var path = Path.Combine(Path.GetDirectoryName(filePath), textureFileName); - var matOpaque = new IOMaterial(Material.Material_Opaque + "_" + i, pages[i], path, false, false, 0, i); - var matOpaqueDoubleSided = new IOMaterial(Material.Material_OpaqueDoubleSided + "_" + i, pages[i], path, false, true, 0, i); - var matAdditiveBlending = new IOMaterial(Material.Material_AdditiveBlending + "_" + i, pages[i], path, true, false, 0, i); - var matAdditiveBlendingDoubleSided = new IOMaterial(Material.Material_AdditiveBlendingDoubleSided + "_" + i, pages[i], path, true, true, 0, i); - - model.Materials.Add(matOpaque); - model.Materials.Add(matOpaqueDoubleSided); - model.Materials.Add(matAdditiveBlending); - model.Materials.Add(matAdditiveBlendingDoubleSided); + foreach (BlendMode mode in Enum.GetValues(typeof(BlendMode))) + { + var prefix = Material.GetPrefixForBlendMode(mode); + model.Materials.Add(new IOMaterial(prefix + "_" + i, pages[i], path, mode, false, 0, i)); + model.Materials.Add(new IOMaterial(prefix + Material.DoubleSidedSuffix + "_" + i, pages[i], path, mode, true, 0, i)); + } } int lastIndex = 0; @@ -449,7 +446,7 @@ public static IOModel PrepareForExport(string filePath, IOGeometrySettings setti foreach (var mt in model.Materials) if ((mergeIntoPages && mt.Page == texture.Atlas) || (!mergeIntoPages && mt.Texture == p.Texture.Texture)) - if (mt.AdditiveBlending == (p.Texture.BlendMode >= BlendMode.Additive)) + if (mt.BlendMode == p.Texture.BlendMode) if (mt.DoubleSided == p.Texture.DoubleSided) if (mt.Shininess == 0) mat = mt; @@ -663,7 +660,7 @@ public static List ImportFromExternalModel(string fileName, IOGeometryS } area.DoubleSided = tmpSubmesh.Value.Material.DoubleSided; - area.BlendMode = tmpSubmesh.Value.Material.AdditiveBlending ? BlendMode.Additive : BlendMode.Normal; + area.BlendMode = tmpSubmesh.Value.Material.BlendMode; poly.Texture = area; poly.ShineStrength = (byte)Math.Min(Math.Round(tmpSubmesh.Value.Material.Shininess / 16.0f, MidpointRounding.ToEven), 63); diff --git a/WadTool/WadActions.cs b/WadTool/WadActions.cs index afedd1a567..fede959e97 100644 --- a/WadTool/WadActions.cs +++ b/WadTool/WadActions.cs @@ -406,15 +406,12 @@ private static void UpdateBoneAbsolutePositions(List bones) var textureFileName = "Texture_" + i + ".png"; var path = Path.Combine(Path.GetDirectoryName(filePath), textureFileName); - var matOpaque = new IOMaterial(Material.Material_Opaque + "_" + i, pages[i], path, false, false, 0, i); - var matOpaqueDoubleSided = new IOMaterial(Material.Material_OpaqueDoubleSided + "_" + i, pages[i], path, false, true, 0, i); - var matAdditiveBlending = new IOMaterial(Material.Material_AdditiveBlending + "_" + i, pages[i], path, true, false, 0, i); - var matAdditiveBlendingDoubleSided = new IOMaterial(Material.Material_AdditiveBlendingDoubleSided + "_" + i, pages[i], path, true, true, 0, i); - - model.Materials.Add(matOpaque); - model.Materials.Add(matOpaqueDoubleSided); - model.Materials.Add(matAdditiveBlending); - model.Materials.Add(matAdditiveBlendingDoubleSided); + foreach (BlendMode mode in Enum.GetValues(typeof(BlendMode))) + { + var prefix = Material.GetPrefixForBlendMode(mode); + model.Materials.Add(new IOMaterial(prefix + "_" + i, pages[i], path, mode, false, 0, i)); + model.Materials.Add(new IOMaterial(prefix + Material.DoubleSidedSuffix + "_" + i, pages[i], path, mode, true, 0, i)); + } } UpdateBoneAbsolutePositions(m.Bones); @@ -499,7 +496,7 @@ private static void UpdateBoneAbsolutePositions(List bones) var mat = model.Materials[0]; foreach (var mt in model.Materials) if (mt.Page == texture.Atlas) - if (mt.AdditiveBlending == (p.Texture.BlendMode >= BlendMode.Additive)) + if (mt.BlendMode == p.Texture.BlendMode) if (mt.DoubleSided == p.Texture.DoubleSided) if (mt.Shininess == 0) mat = mt;