diff --git a/FinModelUtility/Bmd2Fbx/src/Exporter/ModelConverter.cs b/FinModelUtility/Bmd2Fbx/src/Exporter/ModelConverter.cs index 5e739db7f..689b9849c 100644 --- a/FinModelUtility/Bmd2Fbx/src/Exporter/ModelConverter.cs +++ b/FinModelUtility/Bmd2Fbx/src/Exporter/ModelConverter.cs @@ -33,7 +33,11 @@ public IModel Convert( new BmdMaterialManager(model, bmd, pathsAndBtis); var jointsAndBones = this.ConvertBones_(model, bmd); - this.ConvertAnimations_(model, bmd, pathsAndBcxs, frameRate, jointsAndBones); + this.ConvertAnimations_(model, + bmd, + pathsAndBcxs, + frameRate, + jointsAndBones); this.ConvertMesh_(model, bmd, jointsAndBones, materialManager); return model; @@ -235,22 +239,12 @@ private void ConvertMesh_( vertex.SetBones(weights); } - // TODO: Do the other colors need to be used too? - var indicesWithColor = - batch - .HasColors - .Select((hasColor, index) => (hasColor, index)) - .Where(hasColorAndIndex => hasColorAndIndex.hasColor) - .Select(hasColorAndIndex => hasColorAndIndex.index) - .ToList(); - Asserts.True(indicesWithColor.Count <= 1, - "More than one color at a vertex, unsupported!"); - if (indicesWithColor.Count == 1) { - var indexWithColor = indicesWithColor[0]; - var colorIndex = point.ColorIndex[indexWithColor]; - var color = vertexColors[indexWithColor][colorIndex]; - - vertex.SetColorBytes(color.R, color.G, color.B, color.A); + for (var c = 0; c < 2; ++c) { + if (batch.HasColors[c]) { + var colorIndex = point.ColorIndex[c]; + var color = vertexColors[c][colorIndex]; + vertex.SetColorBytes(c, color.R, color.G, color.B, color.A); + } } for (var i = 0; i < 8; ++i) { diff --git a/FinModelUtility/Bmd2Fbx/src/misc/GCN/BMD.cs b/FinModelUtility/Bmd2Fbx/src/misc/GCN/BMD.cs index 016f4f424..f24936855 100644 --- a/FinModelUtility/Bmd2Fbx/src/misc/GCN/BMD.cs +++ b/FinModelUtility/Bmd2Fbx/src/misc/GCN/BMD.cs @@ -2128,6 +2128,7 @@ public class MaterialEntry public ushort[] Unknown2; public ushort[] Indices2; + // https://github.com/LordNed/WindEditor/wiki/BMD-and-BDL-Model-Format#material-entry public MaterialEntry(EndianBinaryReader er) { this.Unknown1 = er.ReadBytes(8); this.Color1 = er.ReadUInt16s(2); @@ -2254,7 +2255,6 @@ public class TevStageProps { public TevStageProps(EndianBinaryReader er) { - er.ReadByte(); this.color_a = (GxCc) er.ReadByte(); this.color_b = (GxCc) er.ReadByte(); this.color_c = (GxCc) er.ReadByte(); @@ -2273,7 +2273,6 @@ public TevStageProps(EndianBinaryReader er) this.alpha_scale = (TevScale) er.ReadByte(); this.alpha_clamp = er.ReadByte() == (byte) 1; this.alpha_regid = er.ReadByte(); - er.ReadByte(); } public enum GxCc { diff --git a/FinModelUtility/Fin/src/exporter/assimp/indirect/AssimpIndirectUvFixer.cs b/FinModelUtility/Fin/src/exporter/assimp/indirect/AssimpIndirectUvFixer.cs index b4463b80b..0022913bb 100644 --- a/FinModelUtility/Fin/src/exporter/assimp/indirect/AssimpIndirectUvFixer.cs +++ b/FinModelUtility/Fin/src/exporter/assimp/indirect/AssimpIndirectUvFixer.cs @@ -45,23 +45,29 @@ public void Fix(IModel model, Scene sc) { } } - var assColors = assMesh.VertexColorChannels[0]; - assColors.Clear(); + var assColors = assMesh.VertexColorChannels; + for (var c = 0; c < 8; ++c) { + assColors[c].Clear(); + } + var hadColor = new bool[2]; foreach (var assColorIndexFloat in assColorIndices) { var assColorIndex = (int) Math.Round(assColorIndexFloat); var finVertex = assColorIndex != -1 ? finVertices[assColorIndex] : null; - var finColor = finVertex?.Color; - if (finColor != null) { - assColors.Add(new Color4D(finColor.Rf, - finColor.Gf, - finColor.Bf, - finColor.Af)); - } else { - assColors.Add(nullColor); - } + for (var c = 0; c < 2; ++c) { + var finColor = finVertex?.GetColor(c); + if (finColor != null) { + hadColor[c] = true; + assColors[c].Add(new Color4D(finColor.Rf, + finColor.Gf, + finColor.Bf, + finColor.Af)); + } else { + assColors[c].Add(nullColor); + } + } } @@ -75,6 +81,12 @@ public void Fix(IModel model, Scene sc) { assMesh.UVComponentCount[t] = 2; } } + + for (var c = 0; c < 2; ++c) { + if (!hadColor[c]) { + assColors[c].Clear(); + } + } } } } diff --git a/FinModelUtility/Fin/src/exporter/gltf/GltfMeshBuilder.cs b/FinModelUtility/Fin/src/exporter/gltf/GltfMeshBuilder.cs index e2bcb591c..52af95535 100644 --- a/FinModelUtility/Fin/src/exporter/gltf/GltfMeshBuilder.cs +++ b/FinModelUtility/Fin/src/exporter/gltf/GltfMeshBuilder.cs @@ -16,7 +16,7 @@ namespace fin.exporter.gltf { using VERTEX = - VertexBuilder; + VertexBuilder; public class GltfMeshBuilder { public bool UvIndices { get; set; } @@ -92,14 +92,24 @@ public Mesh BuildAndBindMesh( } // TODO: Include color - var finColor = point.Color; - var hasColor = finColor != null; - var assColor = hasColor - ? new Vector4(finColor.Rf, - finColor.Bf, - finColor.Gf, - finColor.Af) - : new Vector4(1, 1, 1, 1); + var finColor0 = point.GetColor(0); + var hasColor0 = finColor0 != null; + var assColor0 = hasColor0 + ? new Vector4(finColor0.Rf, + finColor0.Bf, + finColor0.Gf, + finColor0.Af) + : new Vector4(1, 1, 1, 1); + var finColor1 = point.GetColor(1); + var hasColor1 = finColor1 != null; + var assColor1 = hasColor1 + ? new Vector4(finColor1.Rf, + finColor1.Bf, + finColor1.Gf, + finColor1.Af) + : new Vector4(1, 1, 1, 1); + + var hasColor = hasColor0 || hasColor1; var uvs = point.Uvs; var hasUvs = (uvs?.Count ?? 0) > 0; @@ -107,10 +117,10 @@ public Mesh BuildAndBindMesh( if (hasUvs) { var uv = uvs[0]; vertexBuilder = - vertexBuilder.WithMaterial(assColor, + vertexBuilder.WithMaterial(assColor0, assColor1, new Vector2(uv.U, uv.V)); } else if (hasColor) { - vertexBuilder = vertexBuilder.WithMaterial(assColor); + vertexBuilder = vertexBuilder.WithMaterial(assColor0, assColor1); } } else { // Importing the color directly via Assimp doesn't work for some diff --git a/FinModelUtility/Fin/src/model/SkinInterfaces.cs b/FinModelUtility/Fin/src/model/SkinInterfaces.cs index d06dd72f8..67e6af48e 100644 --- a/FinModelUtility/Fin/src/model/SkinInterfaces.cs +++ b/FinModelUtility/Fin/src/model/SkinInterfaces.cs @@ -54,9 +54,13 @@ public interface IVertex { IVertex SetLocalTangent(ITangent? localNormal); IVertex SetLocalTangent(float x, float y, float z, float w); - IColor? Color { get; } + IReadOnlyDictionary? Colors { get; } IVertex SetColor(IColor? color); + IVertex SetColor(int colorIndex, IColor? color); IVertex SetColorBytes(byte r, byte g, byte b, byte a); + IVertex SetColorBytes(int colorIndex, byte r, byte g, byte b, byte a); + IColor? GetColor(); + IColor? GetColor(int colorIndex); IReadOnlyDictionary? Uvs { get; } IVertex SetUv(ITexCoord? uv); diff --git a/FinModelUtility/Fin/src/model/impl/SkinImpl.cs b/FinModelUtility/Fin/src/model/impl/SkinImpl.cs index ad13d5a00..e5c740009 100644 --- a/FinModelUtility/Fin/src/model/impl/SkinImpl.cs +++ b/FinModelUtility/Fin/src/model/impl/SkinImpl.cs @@ -90,6 +90,7 @@ public IPrimitive AddQuads(params IVertex[] vertices) { private class VertexImpl : IVertex { private IDictionary? uvs_; + private IDictionary? colors_; public VertexImpl(int index, IPosition position) { this.Index = index; @@ -146,18 +147,50 @@ public IVertex SetLocalTangent(ITangent localTangent) { } public IVertex SetLocalTangent(float x, float y, float z, float w) - => this.SetLocalTangent(new TangentImpl { X = x, Y = y, Z = z, W = w}); + => this.SetLocalTangent(new TangentImpl {X = x, Y = y, Z = z, W = w}); - public IColor? Color { get; private set; } + public IReadOnlyDictionary? Colors { get; private set; } + + public IVertex SetColor(IColor? color) => this.SetColor(0, color); + + public IVertex SetColor(int colorIndex, IColor? color) { + if (color != null) { + if (this.colors_ == null) { + this.colors_ = new Dictionary(); + this.Colors = new ReadOnlyDictionary(this.colors_); + } + + this.colors_[colorIndex] = color; + } else { + this.colors_?.Remove(colorIndex); + if (this.colors_?.Count == 0) { + this.colors_ = null; + this.Colors = null; + } + } - public IVertex SetColor(IColor? color) { - this.Color = color; return this; } public IVertex SetColorBytes(byte r, byte g, byte b, byte a) - => this.SetColor(ColorImpl.FromRgbaBytes(r, g, b, a)); + => this.SetColorBytes(0, r, g, b, a); + + public IVertex SetColorBytes( + int colorIndex, + byte r, + byte g, + byte b, + byte a) + => this.SetColor(colorIndex, ColorImpl.FromRgbaBytes(r, g, b, a)); + + public IColor? GetColor() => this.GetColor(0); + + public IColor? GetColor(int colorIndex) { + IColor? color = null; + this.colors_?.TryGetValue(colorIndex, out color); + return color; + } public IReadOnlyDictionary? Uvs { get; private set; } @@ -177,6 +210,7 @@ public IVertex SetUv(int uvIndex, ITexCoord? uv) { this.uvs_?.Remove(uvIndex); if (this.uvs_?.Count == 0) { this.uvs_ = null; + this.Uvs = null; } } @@ -190,7 +224,7 @@ public IVertex SetUv(int uvIndex, float u, float v) public ITexCoord? GetUv(int uvIndex) { ITexCoord? uv = null; - this.uvs_?.TryGetValue(uvIndex, out uv); + this.uvs_?.TryGetValue(uvIndex, out uv); return uv; } }