diff --git a/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/material/GlFixedFunctionMaterialShader.cs b/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/material/GlFixedFunctionMaterialShader.cs index 735ef7ca6..6125a49db 100644 --- a/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/material/GlFixedFunctionMaterialShader.cs +++ b/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/material/GlFixedFunctionMaterialShader.cs @@ -1,8 +1,11 @@ using System.Numerics; +using Assimp.Unmanaged; + using fin.language.equations.fixedFunction; using fin.math; using fin.model; +using fin.util.enumerables; namespace fin.ui.rendering.gl.material; @@ -48,6 +51,12 @@ protected override void Setup( this.SetUpTexture($"texture{i}", i, finTexture, glTexture); } + var normalTexture = material.NormalTexture; + if (normalTexture != null) { + var glTexture = GlTexture.FromTexture(normalTexture); + this.SetUpTexture("normalTexture", MaterialConstants.MAX_TEXTURES, normalTexture, glTexture); + } + var colorRegisterToUniform = new Dictionary>(); var scalarRegisterToUniform diff --git a/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/util/GlUtil_Texture.cs b/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/util/GlUtil_Texture.cs index f7c8ecb13..1a4d4eedd 100644 --- a/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/util/GlUtil_Texture.cs +++ b/FinModelUtility/Fin/Fin.Ui/src/rendering/gl/util/GlUtil_Texture.cs @@ -1,32 +1,36 @@ using System.Runtime.CompilerServices; +using fin.model; +using fin.shaders.glsl; + using OpenTK.Graphics.OpenGL; namespace fin.ui.rendering.gl; public partial class GlState { - public int ActiveTexture { get; set; }= -1; + public int ActiveTexture { get; set; } = -1; - public int[] CurrentTextureBindings { get; set; } = - [-1, -1, -1, -1, -1, -1, -1, -1]; + public int[] CurrentTextureBindings { get; } + = Enumerable.Repeat(-1, MaterialConstants.MAX_TEXTURES + 1).ToArray(); } public static partial class GlUtil { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void BindTexture(int textureIndex, int value) { - if (currentState_.CurrentTextureBindings[textureIndex] == value) { - return; - } - - if (currentState_.ActiveTexture != textureIndex) { - GL.ActiveTexture(TextureUnit.Texture0 + - (currentState_.ActiveTexture = textureIndex)); - } + if (currentState_.CurrentTextureBindings[textureIndex] == value) { + return; + } - GL.BindTexture(TextureTarget.Texture2D, - currentState_.CurrentTextureBindings[textureIndex] = value); + if (currentState_.ActiveTexture != textureIndex) { + GL.ActiveTexture(TextureUnit.Texture0 + + (currentState_.ActiveTexture = textureIndex)); } + GL.BindTexture(TextureTarget.Texture2D, + currentState_.CurrentTextureBindings[textureIndex] + = value); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UnbindTexture(int textureIndex) => BindTexture(textureIndex, -1); diff --git a/FinModelUtility/Fin/Fin/src/image/BumpMapUtils.cs b/FinModelUtility/Fin/Fin/src/image/BumpMapUtils.cs new file mode 100644 index 000000000..2c199f210 --- /dev/null +++ b/FinModelUtility/Fin/Fin/src/image/BumpMapUtils.cs @@ -0,0 +1,96 @@ +using fin.image.formats; + +using SixLabors.ImageSharp.PixelFormats; + +namespace fin.image; + +public static class BumpMapUtils { + /// + /// Shamelessly stolen from: + /// https://stackoverflow.com/questions/10652797/whats-the-logic-behind-creating-a-normal-map-from-a-texture + /// + public static unsafe Rgb24Image ConvertBumpMapImageToNormalImage( + IReadOnlyImage image) { + var normalImage = + new Rgb24Image(PixelFormat.RGB888, image.Width, image.Height); + using var normalImageLock = normalImage.UnsafeLock(); + var normalImageScan0 = normalImageLock.pixelScan0; + + image.Access(bumpGetHandler => { + for (var y = 0; y < image.Height; ++y) { + for (var x = 0; x < image.Width; ++x) { + bumpGetHandler( + x, + y, + out var centerIntensity, + out _, + out _, + out _); + + byte leftIntensity; + if (x > 0) { + bumpGetHandler( + x - 1, + y, + out leftIntensity, + out _, + out _, + out _); + } else { + leftIntensity = centerIntensity; + } + + byte rightIntensity; + if (x < image.Width - 1) { + bumpGetHandler( + x + 1, + y, + out rightIntensity, + out _, + out _, + out _); + } else { + rightIntensity = centerIntensity; + } + + byte upIntensity; + if (y > 0) { + bumpGetHandler( + x, + y - 1, + out upIntensity, + out _, + out _, + out _); + } else { + upIntensity = centerIntensity; + } + + byte downIntensity; + if (y < image.Height - 1) { + bumpGetHandler( + x, + y + 1, + out downIntensity, + out _, + out _, + out _); + } else { + downIntensity = centerIntensity; + } + + var xIntensity + = ((leftIntensity / 255f - rightIntensity / 255f + 1) * .5f) * + 255; + var yIntensity + = ((upIntensity / 255f - downIntensity / 255f + 1) * .5f) * 255; + + normalImageScan0[y * image.Width + x] + = new Rgb24((byte) xIntensity, (byte) yIntensity, 255); + } + } + }); + + return normalImage; + } +} \ No newline at end of file diff --git a/FinModelUtility/Fin/Fin/src/language/equations/fixedFunction/FixedFunctionEquationsGlslPrinter.cs b/FinModelUtility/Fin/Fin/src/language/equations/fixedFunction/FixedFunctionEquationsGlslPrinter.cs index 8b999c34a..cdd1c7c96 100644 --- a/FinModelUtility/Fin/Fin/src/language/equations/fixedFunction/FixedFunctionEquationsGlslPrinter.cs +++ b/FinModelUtility/Fin/Fin/src/language/equations/fixedFunction/FixedFunctionEquationsGlslPrinter.cs @@ -29,6 +29,9 @@ public void Print( var registers = material.Registers; var textures = material.TextureSources; + var normalTexture = material.NormalTexture; + var hasNormalTexture = normalTexture != null; + sb.AppendLine($"#version {GlslConstants.FRAGMENT_SHADER_VERSION}"); sb.AppendLine(GlslConstants.FLOAT_PRECISION); sb.AppendLine(); @@ -92,6 +95,12 @@ public void Print( } } + if (hasNormalTexture) { + sb.AppendLine( + $"uniform {GlslUtil.GetTypeOfTexture(normalTexture, this.animations_)} normalTexture;"); + hadUniform = true; + } + foreach (var colorRegister in registers.ColorRegisters) { if (equations.DoOutputsDependOn(colorRegister)) { hadUniform = true; @@ -133,6 +142,14 @@ public void Print( AppendLineBetweenUniformsAndIns(); sb.AppendLine("in vec3 vertexPosition;"); sb.AppendLine("in vec3 vertexNormal;"); + + if (hasNormalTexture && + shaderRequirements.TangentType == TangentType.DEFINED) { + sb.AppendLine(""" + in vec3 tangent; + in vec3 binormal; + """); + } } var usedColors = shaderRequirements.UsedColors; @@ -177,12 +194,41 @@ public void Print( // Calculate lighting if (dependsOnLights) { - sb.AppendLine( - $""" - // Have to renormalize because the vertex normals can become distorted when interpolated. - vec3 fragNormal = normalize(vertexNormal); + if (!hasNormalTexture) { + sb.AppendLine( + """ + // Have to renormalize because the vertex normals can become distorted when interpolated. + vec3 fragNormal = normalize(vertexNormal); + + """); + } else { + sb.AppendLine( + $""" + // Have to renormalize because the vertex normals can become distorted when interpolated. + vec3 fragNormal = normalize(vertexNormal); + vec3 textureNormal = {GlslUtil.ReadColorFromTexture("normalTexture", $"{GlslConstants.IN_UV_NAME}{normalTexture?.UvIndex ?? 0}", normalTexture, this.animations_)}.xyz * 2.0 - 1.0; + fragNormal = normalize(mat3(tangent, binormal, fragNormal) * textureNormal); + + """); + + if (shaderRequirements.TangentType is TangentType.CALCULATED) { + // Shamelessly stolen from: + // https://community.khronos.org/t/computing-the-tangent-space-in-the-fragment-shader/52861 + var texCoordName + = $"{GlslConstants.IN_UV_NAME}{normalTexture.UvIndex}"; + sb.AppendLine( + $""" + vec3 Q1 = dFdx(vertexPosition); + vec3 Q2 = dFdy(vertexPosition); + vec2 st1 = dFdx({texCoordName}); + vec2 st2 = dFdy({texCoordName}); + vec3 tangent = normalize(Q1*st2.t - Q2*st1.t); + vec3 binormal = normalize(-Q1*st2.s + Q2*st1.s); + + """); + } + } - """); // TODO: Optimize this if the shader depends on merged lighting as well as individual lights for some reason. if (dependsOnAnIndividualLight) { sb.AppendLine( @@ -209,7 +255,7 @@ public void Print( vec4 mergedLightDiffuseColor = vec4(0); vec4 mergedLightSpecularColor = vec4(0); getMergedLightColors(vertexPosition, fragNormal, {GlslConstants.UNIFORM_SHININESS_NAME}, mergedLightDiffuseColor, mergedLightSpecularColor); - + """); } } @@ -322,14 +368,20 @@ private string GetAlphaCompareText_( string alphaAccessorText, float reference) => alphaCompareType switch { - AlphaCompareType.Never => "false", - AlphaCompareType.Less => $"{alphaAccessorText} < {reference:0.0###########}", - AlphaCompareType.Equal => $"{alphaAccessorText} == {reference:0.0###########}", - AlphaCompareType.LEqual => $"{alphaAccessorText} <= {reference:0.0###########}", - AlphaCompareType.Greater => $"{alphaAccessorText} > {reference:0.0###########}", - AlphaCompareType.NEqual => $"{alphaAccessorText} != {reference:0.0###########}", - AlphaCompareType.GEqual => $"{alphaAccessorText} >= {reference:0.0###########}", - AlphaCompareType.Always => "true", + AlphaCompareType.Never => "false", + AlphaCompareType.Less => + $"{alphaAccessorText} < {reference:0.0###########}", + AlphaCompareType.Equal => + $"{alphaAccessorText} == {reference:0.0###########}", + AlphaCompareType.LEqual => + $"{alphaAccessorText} <= {reference:0.0###########}", + AlphaCompareType.Greater => + $"{alphaAccessorText} > {reference:0.0###########}", + AlphaCompareType.NEqual => + $"{alphaAccessorText} != {reference:0.0###########}", + AlphaCompareType.GEqual => + $"{alphaAccessorText} >= {reference:0.0###########}", + AlphaCompareType.Always => "true", _ => throw new ArgumentOutOfRangeException( nameof(alphaCompareType), alphaCompareType, diff --git a/FinModelUtility/Fin/Fin/src/model/MaterialInterfaces.cs b/FinModelUtility/Fin/Fin/src/model/MaterialInterfaces.cs index d542aa2e0..f304e4d34 100644 --- a/FinModelUtility/Fin/Fin/src/model/MaterialInterfaces.cs +++ b/FinModelUtility/Fin/Fin/src/model/MaterialInterfaces.cs @@ -117,6 +117,13 @@ IMaterial SetBlendingSeparate( LogicOp logicOp); } + +[GenerateReadOnly] +public partial interface IMaterialWithNormalTexture : IMaterial { + IReadOnlyTexture? NormalTexture { get; set; } +} + + [GenerateReadOnly] public partial interface INullMaterial : IMaterial; @@ -135,10 +142,9 @@ public partial interface IColorMaterial : IMaterial { } [GenerateReadOnly] -public partial interface IStandardMaterial : IMaterial { +public partial interface IStandardMaterial : IMaterialWithNormalTexture { IReadOnlyTexture? DiffuseTexture { get; set; } IReadOnlyTexture? AmbientOcclusionTexture { get; set; } - IReadOnlyTexture? NormalTexture { get; set; } IReadOnlyTexture? EmissiveTexture { get; set; } IReadOnlyTexture? SpecularTexture { get; set; } } @@ -310,7 +316,7 @@ public enum FixedFunctionSource { } [GenerateReadOnly] -public partial interface IFixedFunctionMaterial : IMaterial { +public partial interface IFixedFunctionMaterial : IMaterialWithNormalTexture { IFixedFunctionEquations Equations { get; } IFixedFunctionRegisters Registers { get; } diff --git a/FinModelUtility/Fin/Fin/src/model/impl/material/FixedFunctionMaterialImpl.cs b/FinModelUtility/Fin/Fin/src/model/impl/material/FixedFunctionMaterialImpl.cs index 79e48f456..aea6664ab 100644 --- a/FinModelUtility/Fin/Fin/src/model/impl/material/FixedFunctionMaterialImpl.cs +++ b/FinModelUtility/Fin/Fin/src/model/impl/material/FixedFunctionMaterialImpl.cs @@ -82,5 +82,17 @@ public IFixedFunctionMaterial SetAlphaCompare( AlphaCompareType.Always; public float AlphaReference1 { get; private set; } + + private IReadOnlyTexture? normalTexture_; + + public IReadOnlyTexture? NormalTexture { + get => this.normalTexture_; + set { + this.normalTexture_ = value; + if (value != null) { + this.textures_.Add(value); + } + } + } } } \ No newline at end of file diff --git a/FinModelUtility/Fin/Fin/src/model/io/exporters/gltf/GltfMaterialManager.cs b/FinModelUtility/Fin/Fin/src/model/io/exporters/gltf/GltfMaterialManager.cs index 43a8500cb..33e55e854 100644 --- a/FinModelUtility/Fin/Fin/src/model/io/exporters/gltf/GltfMaterialManager.cs +++ b/FinModelUtility/Fin/Fin/src/model/io/exporters/gltf/GltfMaterialManager.cs @@ -175,8 +175,7 @@ var usesDiffuse ]) .ToArray()); - KnownChannel - mainTextureChannel; + KnownChannel mainTextureChannel; if (usesSpecular) { // TODO: Get specular color gltfMaterialBuilder @@ -211,6 +210,14 @@ var texture .UseTexture(texture, gltfImageByFinImage[texture.Image]); } + var normalTexture = fixedFunctionMaterial.NormalTexture; + if (normalTexture != null) { + gltfMaterialBuilder + .UseChannel(KnownChannel.Normal) + .UseTexture(normalTexture, + gltfImageByFinImage[normalTexture.Image]); + } + break; } default: { diff --git a/FinModelUtility/Fin/Fin/src/shaders/glsl/GlslUtil.cs b/FinModelUtility/Fin/Fin/src/shaders/glsl/GlslUtil.cs index d88993f99..d9d36aa1a 100644 --- a/FinModelUtility/Fin/Fin/src/shaders/glsl/GlslUtil.cs +++ b/FinModelUtility/Fin/Fin/src/shaders/glsl/GlslUtil.cs @@ -80,8 +80,10 @@ public static string GetVertexSrc(IReadOnlyModel model, var modelRequirements = ModelRequirements.FromModel(model); var hasNormals = modelRequirements.HasNormals; + var hasTangents = modelRequirements.HasTangents; var hasBinormals = hasNormals && hasTangents; + var numBones = modelRequirements.NumBones; vertexSrc.AppendLine($$""" diff --git a/FinModelUtility/Fin/Fin/src/shaders/glsl/ShaderRequirements.cs b/FinModelUtility/Fin/Fin/src/shaders/glsl/ShaderRequirements.cs index 003c05166..73f03c8d6 100644 --- a/FinModelUtility/Fin/Fin/src/shaders/glsl/ShaderRequirements.cs +++ b/FinModelUtility/Fin/Fin/src/shaders/glsl/ShaderRequirements.cs @@ -6,12 +6,18 @@ namespace fin.shaders.glsl; +public enum TangentType { + NOT_PRESENT, + DEFINED, + CALCULATED, +} + public interface IShaderRequirements { public bool UsesSphericalReflectionMapping { get; } public bool UsesLinearReflectionMapping { get; } public bool HasNormals { get; } - public bool HasTangents { get; } + public TangentType TangentType { get; } public bool[] UsedUvs { get; } public bool[] UsedColors { get; } @@ -31,6 +37,7 @@ private ShaderRequirements(IReadOnlyModel model, this.UsesLinearReflectionMapping = material?.Textures.Any(t => t.UvType is UvType.LINEAR) ?? false; + this.TangentType = TangentType.NOT_PRESENT; foreach (var vertex in model.Skin.Meshes .SelectMany(mesh => mesh.Primitives) .Where(primitive @@ -41,7 +48,7 @@ private ShaderRequirements(IReadOnlyModel model, LocalNormal: not null, LocalTangent: not null }: { this.HasNormals = true; - this.HasTangents = true; + this.TangentType = TangentType.CALCULATED; break; } case IReadOnlyNormalVertex { LocalNormal: not null }: { @@ -51,6 +58,12 @@ private ShaderRequirements(IReadOnlyModel model, } } + if (this.TangentType is TangentType.NOT_PRESENT && + material is IFixedFunctionMaterial { NormalTexture: not null } + or IStandardMaterial { NormalTexture: not null }) { + this.TangentType = TangentType.CALCULATED; + } + this.UsedUvs = new bool[MaterialConstants.MAX_UVS]; if (material != null && material is not IFixedFunctionMaterial) { foreach (var texture in material.Textures) { @@ -105,7 +118,7 @@ or IStandardMaterial public bool UsesSphericalReflectionMapping { get; } public bool UsesLinearReflectionMapping { get; } public bool HasNormals { get; } - public bool HasTangents { get; } + public TangentType TangentType { get; } public bool[] UsedUvs { get; } public bool[] UsedColors { get; } } \ No newline at end of file diff --git a/FinModelUtility/Fin/Fin/src/shaders/glsl/StandardShaderSourceGlsl.cs b/FinModelUtility/Fin/Fin/src/shaders/glsl/StandardShaderSourceGlsl.cs index e3430b53e..a14694481 100644 --- a/FinModelUtility/Fin/Fin/src/shaders/glsl/StandardShaderSourceGlsl.cs +++ b/FinModelUtility/Fin/Fin/src/shaders/glsl/StandardShaderSourceGlsl.cs @@ -29,7 +29,7 @@ public StandardShaderSourceGlsl( var normalTexture = material.NormalTexture; var hasNormalTexture = normalTexture != null; var hasNormals = shaderRequirements.HasNormals; - var hasTangents = shaderRequirements.HasTangents; + var hasTangents = shaderRequirements.TangentType != TangentType.NOT_PRESENT; var hasBinormals = hasNormals && hasTangents; var ambientOcclusionTexture = material.AmbientOcclusionTexture; diff --git a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/IP2V_damaind3.png b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/IP2V_damaind3.png index 4260e4730..f6a6da632 100644 Binary files a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/IP2V_damaind3.png and b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/IP2V_damaind3.png differ diff --git a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/ashi_mat.fragment.glsl b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/ashi_mat.fragment.glsl index 87294330d..53a14c83e 100644 --- a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/ashi_mat.fragment.glsl +++ b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/ashi_mat.fragment.glsl @@ -39,6 +39,7 @@ struct Texture { uniform sampler2D texture0; uniform Texture texture2; uniform Texture texture3; +uniform Texture normalTexture; uniform vec3 color_GxMaterialColor0; uniform vec3 color_GxAmbientColor0; uniform float scalar_GxAlphaRegister0; @@ -121,6 +122,15 @@ void getIndividualLightColors(Light light, vec3 position, vec3 normal, float shi void main() { // Have to renormalize because the vertex normals can become distorted when interpolated. vec3 fragNormal = normalize(vertexNormal); + vec3 textureNormal = texture(normalTexture.sampler, normalTexture.transform2d * vec3((uv0).x, (uv0).y, 1)).xyz * 2.0 - 1.0; + fragNormal = normalize(mat3(tangent, binormal, fragNormal) * textureNormal); + + vec3 Q1 = dFdx(vertexPosition); + vec3 Q2 = dFdy(vertexPosition); + vec2 st1 = dFdx(uv0); + vec2 st2 = dFdy(uv0); + vec3 tangent = normalize(Q1*st2.t - Q2*st1.t); + vec3 binormal = normalize(-Q1*st2.s + Q2*st1.s); vec4 individualLightDiffuseColors[8]; vec4 individualLightSpecularColors[8]; diff --git a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/body_mat.fragment.glsl b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/body_mat.fragment.glsl index 87294330d..5759d9cdb 100644 --- a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/body_mat.fragment.glsl +++ b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/body_mat.fragment.glsl @@ -39,6 +39,7 @@ struct Texture { uniform sampler2D texture0; uniform Texture texture2; uniform Texture texture3; +uniform sampler2D normalTexture; uniform vec3 color_GxMaterialColor0; uniform vec3 color_GxAmbientColor0; uniform float scalar_GxAlphaRegister0; @@ -121,6 +122,15 @@ void getIndividualLightColors(Light light, vec3 position, vec3 normal, float shi void main() { // Have to renormalize because the vertex normals can become distorted when interpolated. vec3 fragNormal = normalize(vertexNormal); + vec3 textureNormal = texture(normalTexture, uv0).xyz * 2.0 - 1.0; + fragNormal = normalize(mat3(tangent, binormal, fragNormal) * textureNormal); + + vec3 Q1 = dFdx(vertexPosition); + vec3 Q2 = dFdy(vertexPosition); + vec2 st1 = dFdx(uv0); + vec2 st2 = dFdy(uv0); + vec3 tangent = normalize(Q1*st2.t - Q2*st1.t); + vec3 binormal = normalize(-Q1*st2.s + Q2*st1.s); vec4 individualLightDiffuseColors[8]; vec4 individualLightSpecularColors[8]; diff --git a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy.glb b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy.glb index fd7bf9331..47d64d955 100644 Binary files a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy.glb and b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy.glb differ diff --git a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_0.png b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_0.png index 1236dd3da..61c10af0e 100644 Binary files a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_0.png and b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_0.png differ diff --git a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_1.png b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_1.png index 898bc864f..1236dd3da 100644 Binary files a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_1.png and b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_1.png differ diff --git a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_2.png b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_2.png new file mode 100644 index 000000000..f6a6da632 Binary files /dev/null and b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_2.png differ diff --git a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_3.png b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_3.png new file mode 100644 index 000000000..898bc864f Binary files /dev/null and b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/enemy_3.png differ diff --git a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/ooashi_footind_h.png b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/ooashi_footind_h.png index 5d37e3120..61c10af0e 100644 Binary files a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/ooashi_footind_h.png and b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/ooashi_footind_h.png differ diff --git a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/tama_mat.fragment.glsl b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/tama_mat.fragment.glsl index c681dc1e0..4d8fd816a 100644 --- a/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/tama_mat.fragment.glsl +++ b/FinModelUtility/Formats/JSystem/JSystem Tests/goldens/pikmin_2_BigFoot/output/tama_mat.fragment.glsl @@ -39,6 +39,7 @@ struct Texture { uniform Texture texture0; uniform Texture texture2; uniform Texture texture3; +uniform Texture normalTexture; uniform vec3 color_GxMaterialColor0; uniform vec3 color_GxAmbientColor0; uniform float scalar_GxAlphaRegister0; @@ -121,6 +122,15 @@ void getIndividualLightColors(Light light, vec3 position, vec3 normal, float shi void main() { // Have to renormalize because the vertex normals can become distorted when interpolated. vec3 fragNormal = normalize(vertexNormal); + vec3 textureNormal = texture(normalTexture.sampler, normalTexture.transform2d * vec3((uv0).x, (uv0).y, 1)).xyz * 2.0 - 1.0; + fragNormal = normalize(mat3(tangent, binormal, fragNormal) * textureNormal); + + vec3 Q1 = dFdx(vertexPosition); + vec3 Q2 = dFdy(vertexPosition); + vec2 st1 = dFdx(uv0); + vec2 st2 = dFdy(uv0); + vec3 tangent = normalize(Q1*st2.t - Q2*st1.t); + vec3 binormal = normalize(-Q1*st2.s + Q2*st1.s); vec4 individualLightDiffuseColors[8]; vec4 individualLightSpecularColors[8]; diff --git a/FinModelUtility/Formats/JSystem/JSystem/src/misc/GCN/BMD_VTX1Section.cs b/FinModelUtility/Formats/JSystem/JSystem/src/misc/GCN/BMD_VTX1Section.cs index b3c15701f..1f8f2e286 100644 --- a/FinModelUtility/Formats/JSystem/JSystem/src/misc/GCN/BMD_VTX1Section.cs +++ b/FinModelUtility/Formats/JSystem/JSystem/src/misc/GCN/BMD_VTX1Section.cs @@ -28,6 +28,8 @@ public partial class VTX1Section { public ArrayFormat[] ArrayFormats; public Vector3[] Positions; public Vector3[] Normals; + public Vector3[] Tangents; + public Vector3[] Binormals; public VTX1Section(IBinaryReader br, out bool OK) { long position1 = br.Position; diff --git a/FinModelUtility/Platforms/Gx/src/impl/GxFixedFunctionMaterial.cs b/FinModelUtility/Platforms/Gx/src/impl/GxFixedFunctionMaterial.cs index 80c670fb1..ace90b5a6 100644 --- a/FinModelUtility/Platforms/Gx/src/impl/GxFixedFunctionMaterial.cs +++ b/FinModelUtility/Platforms/Gx/src/impl/GxFixedFunctionMaterial.cs @@ -87,7 +87,9 @@ var wrapModeOverrides = populatedMaterial.TextureWrapModeOverrides?[(int) textureIndex]; gxTexture = new GxTexture2d( gxTexture.Name, - gxTexture.MipmapImages, + gxTexture.MipmapImages + .Select(BumpMapUtils.ConvertBumpMapImageToNormalImage) + .ToArray(), wrapModeOverrides?.wrapModeS ?? gxTexture.WrapModeS, wrapModeOverrides?.wrapModeT ?? gxTexture.WrapModeT, gxTexture.MinTextureFilter, @@ -99,6 +101,8 @@ var wrapModeOverrides gxTexture, texCoordGen, texMatrix)]; + + material.NormalTexture = finIndirectTexture; } var colorConstants = new List();