From a1c46d2e6e599ba769eca7b1171bbbac59759e5e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 11 Jan 2025 22:40:20 +0100 Subject: [PATCH] Add support for up to 4 texture channels when importing meshes #2667 --- .../Editor/MaterialTemplates/Particle.shader | 2 +- .../Editor/MaterialTemplates/Surface.shader | 35 +++-- Source/Editor/Surface/Archetypes/Textures.cs | 8 +- .../Editor/Windows/Assets/ModelBaseWindow.cs | 134 ++++++++++++++++-- Source/Editor/Windows/Assets/ModelWindow.cs | 87 +----------- .../Windows/Assets/SkinnedModelWindow.cs | 81 +---------- Source/Engine/CSG/CSGData.cpp | 11 +- Source/Engine/CSG/Types.h | 2 + Source/Engine/Content/Assets/ModelBase.cpp | 43 +++--- Source/Engine/Content/Assets/SkinnedModel.cpp | 6 +- .../Engine/ContentImporters/ImportModel.cpp | 5 +- .../Graphics/Materials/MaterialShader.h | 2 +- Source/Engine/Graphics/Models/Mesh.cpp | 27 +++- Source/Engine/Graphics/Models/Mesh.h | 2 +- Source/Engine/Graphics/Models/MeshBase.cpp | 2 +- Source/Engine/Graphics/Models/MeshBase.h | 2 +- .../Engine/Graphics/Models/ModelData.Tool.cpp | 101 +++++++++---- Source/Engine/Graphics/Models/ModelData.cpp | 61 ++++---- Source/Engine/Graphics/Models/ModelData.h | 12 +- .../Engine/Graphics/Shaders/VertexElement.h | 2 + .../GraphicsDevice/DirectX/RenderToolsDX.cpp | 2 + .../ShadersCompilation/ShaderCompiler.cpp | 2 + .../MaterialGenerator.Textures.cpp | 14 +- .../Tools/MaterialGenerator/MaterialLayer.cpp | 1 - .../Tools/ModelTool/ModelTool.Assimp.cpp | 64 ++------- .../ModelTool/ModelTool.AutodeskFbxSdk.cpp | 65 ++------- .../Tools/ModelTool/ModelTool.OpenFBX.cpp | 68 ++------- Source/Engine/Tools/ModelTool/ModelTool.cpp | 7 +- Source/Shaders/BakeLightmap.shader | 3 +- .../Shaders/Editor/LightmapUVsDensity.shader | 20 ++- Source/Shaders/MaterialCommon.hlsl | 31 +++- 31 files changed, 427 insertions(+), 475 deletions(-) diff --git a/Content/Editor/MaterialTemplates/Particle.shader b/Content/Editor/MaterialTemplates/Particle.shader index 3623e5115..6f0be21e0 100644 --- a/Content/Editor/MaterialTemplates/Particle.shader +++ b/Content/Editor/MaterialTemplates/Particle.shader @@ -514,7 +514,7 @@ VertexOutput VS_Model(ModelInput input, uint particleIndex : SV_InstanceID) output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix); // Pass vertex attributes - output.TexCoord = input.TexCoord; + output.TexCoord = input.TexCoord0; output.ParticleIndex = particleIndex; #if USE_VERTEX_COLOR output.VertexColor = input.Color; diff --git a/Content/Editor/MaterialTemplates/Surface.shader b/Content/Editor/MaterialTemplates/Surface.shader index 1c142f463..b0465d2ff 100644 --- a/Content/Editor/MaterialTemplates/Surface.shader +++ b/Content/Editor/MaterialTemplates/Surface.shader @@ -2,6 +2,7 @@ // Version: @0 #define MATERIAL 1 +#define MATERIAL_TEXCOORDS 4 #define USE_PER_VIEW_CONSTANTS 1 #define USE_PER_DRAW_CONSTANTS 1 @3 @@ -24,17 +25,19 @@ Buffer BoneMatrices : register(t1); Buffer PrevBoneMatrices : register(t2); #endif #endif + // Geometry data passed though the graphics rendering stages up to the pixel shader struct GeometryData { float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; + float4 TexCoords01 : TEXCOORD1; + float4 TexCoords23 : TEXCOORD2; + float2 LightmapUV : TEXCOORD3; #if USE_VERTEX_COLOR half4 VertexColor : COLOR; #endif - float3 WorldNormal : TEXCOORD3; - float4 WorldTangent : TEXCOORD4; + float3 WorldNormal : TEXCOORD4; + float4 WorldTangent : TEXCOORD5; float3 PrevWorldPosition : TEXCOORD7; nointerpolation uint ObjectIndex : TEXCOORD8; }; @@ -68,7 +71,7 @@ struct MaterialInput { float3 WorldPosition; float TwoSidedSign; - float2 TexCoord; + float2 TexCoords[MATERIAL_TEXCOORDS]; #if USE_LIGHTMAP float2 LightmapUV; #endif @@ -86,12 +89,18 @@ struct MaterialInput #endif }; +// Map access to the main texure coordinate channel as UV0 +#define TexCoord TexCoords[0] + // Extracts geometry data to the material input MaterialInput GetGeometryMaterialInput(GeometryData geometry) { MaterialInput output = (MaterialInput)0; output.WorldPosition = geometry.WorldPosition; - output.TexCoord = geometry.TexCoord; + output.TexCoords[0] = geometry.TexCoords01.xy; + output.TexCoords[1] = geometry.TexCoords01.zw; + output.TexCoords[2] = geometry.TexCoords23.xy; + output.TexCoords[3] = geometry.TexCoords23.zw; #if USE_LIGHTMAP output.LightmapUV = geometry.LightmapUV; #endif @@ -126,8 +135,8 @@ MaterialInput GetGeometryMaterialInput(GeometryData geometry) GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, float w1, GeometryData p2, float w2) { GeometryData output = (GeometryData)0; - output.TexCoord = p0.TexCoord * w0 + p1.TexCoord * w1 + p2.TexCoord * w2; - output.LightmapUV = p0.LightmapUV * w0 + p1.LightmapUV * w1 + p2.LightmapUV * w2; + output.TexCoords01 = p0.TexCoords01 * w0 + p1.TexCoords01 * w1 + p2.TexCoords01 * w2; + output.TexCoords23 = p0.TexCoords23 * w0 + p1.TexCoords23 * w1 + p2.TexCoords23 * w2; #if USE_VERTEX_COLOR output.VertexColor = p0.VertexColor * w0 + p1.VertexColor * w1 + p2.VertexColor * w2; #endif @@ -312,14 +321,15 @@ VertexOutput VS(ModelInput input) output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); // Pass vertex attributes - output.Geometry.TexCoord = input.TexCoord; + output.Geometry.TexCoords01 = float4(input.TexCoord0, input.TexCoord1); + output.Geometry.TexCoords23 = float4(input.TexCoord2, input.TexCoord3); #if USE_VERTEX_COLOR output.Geometry.VertexColor = input.Color; #endif #if CAN_USE_LIGHTMAP output.Geometry.LightmapUV = input.LightmapUV * object.LightmapArea.zw + object.LightmapArea.xy; #else - output.Geometry.LightmapUV = input.LightmapUV; + output.Geometry.LightmapUV = float2(0, 0); #endif // Calculate tanget space to world space transformation matrix for unit vectors @@ -486,9 +496,10 @@ VertexOutput VS_Skinned(ModelInput_Skinned input) output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); // Pass vertex attributes - output.Geometry.TexCoord = input.TexCoord; + output.Geometry.TexCoords01 = float4(input.TexCoord0, input.TexCoord1); + output.Geometry.TexCoords23 = float4(input.TexCoord2, input.TexCoord3); #if USE_VERTEX_COLOR - output.Geometry.VertexColor = float4(0, 0, 0, 1); + output.Geometry.VertexColor = input.Color; #endif output.Geometry.LightmapUV = float2(0, 0); diff --git a/Source/Editor/Surface/Archetypes/Textures.cs b/Source/Editor/Surface/Archetypes/Textures.cs index be310c9cd..d941da939 100644 --- a/Source/Editor/Surface/Archetypes/Textures.cs +++ b/Source/Editor/Surface/Archetypes/Textures.cs @@ -123,9 +123,15 @@ namespace FlaxEditor.Surface.Archetypes AlternativeTitles = new string[] { "UV", "UVs" }, Description = "Texture coordinates", Flags = NodeFlags.MaterialGraph, - Size = new Float2(110, 30), + Size = new Float2(150, 30), + DefaultValues = new object[] + { + 0u + }, Elements = new[] { + NodeElementArchetype.Factory.Text(0, 1, "Channel:"), + NodeElementArchetype.Factory.UnsignedInteger(50, 0, 0, -1, 0, 3), NodeElementArchetype.Factory.Output(0, "UVs", typeof(Float2), 0) } }, diff --git a/Source/Editor/Windows/Assets/ModelBaseWindow.cs b/Source/Editor/Windows/Assets/ModelBaseWindow.cs index ead4b7bd9..b1b531e5c 100644 --- a/Source/Editor/Windows/Assets/ModelBaseWindow.cs +++ b/Source/Editor/Windows/Assets/ModelBaseWindow.cs @@ -93,6 +93,118 @@ namespace FlaxEditor.Windows.Assets } } + protected class UVsPropertiesProxyBase : PropertiesProxyBase + { + public enum UVChannel + { + None, + TexCoord0, + TexCoord1, + TexCoord2, + TexCoord3, + LightmapUVs, + }; + + private UVChannel _uvChannel = UVChannel.None; + + [EditorOrder(0), EditorDisplay(null, "Preview UV Channel"), EnumDisplay(EnumDisplayAttribute.FormatMode.None)] + [Tooltip("Set UV channel to preview.")] + public UVChannel Channel + { + get => _uvChannel; + set + { + if (_uvChannel == value) + return; + _uvChannel = value; + Window._meshData?.RequestMeshData(Window._asset); + } + } + + [EditorOrder(1), EditorDisplay(null, "LOD"), Limit(0, Model.MaxLODs), VisibleIf("ShowUVs")] + [Tooltip("Level Of Detail index to preview UVs layout.")] + public int LOD = 0; + + [EditorOrder(2), EditorDisplay(null, "Mesh"), Limit(-1, 1000000), VisibleIf("ShowUVs")] + [Tooltip("Mesh index to preview UVs layout. Use -1 for all meshes")] + public int Mesh = -1; + + private bool ShowUVs => _uvChannel != UVChannel.None; + + /// + public override void OnClean() + { + Channel = UVChannel.None; + + base.OnClean(); + } + + protected class ProxyEditor : ProxyEditorBase + { + private UVsLayoutPreviewControl _uvsPreview; + + public override void Initialize(LayoutElementsContainer layout) + { + var proxy = (UVsPropertiesProxyBase)Values[0]; + if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset)) + return; + + base.Initialize(layout); + + _uvsPreview = layout.Custom().CustomControl; + _uvsPreview.Window = proxy.Window; + } + + /// + public override void Refresh() + { + base.Refresh(); + + if (_uvsPreview != null) + { + var proxy = (UVsPropertiesProxyBase)Values[0]; + switch (proxy._uvChannel) + { + case UVChannel.TexCoord0: _uvsPreview.Channel = 0; break; + case UVChannel.TexCoord1: _uvsPreview.Channel = 1; break; + case UVChannel.TexCoord2: _uvsPreview.Channel = 2; break; + case UVChannel.TexCoord3: _uvsPreview.Channel = 3; break; + case UVChannel.LightmapUVs: + { + _uvsPreview.Channel = -1; + if (proxy.Window.Asset && proxy.Window.Asset.IsLoaded) + { + // Pick UVs channel index from the first mesh + proxy.Window.Asset.GetMeshes(out var meshes); + foreach (var mesh in meshes) + { + if (mesh is Mesh m && m.HasLightmapUVs) + { + _uvsPreview.Channel = m.LightmapUVsIndex; + break; + } + } + } + break; + } + default: _uvsPreview.Channel = -1; break; + } + _uvsPreview.LOD = proxy.LOD; + _uvsPreview.Mesh = proxy.Mesh; + _uvsPreview.HighlightIndex = proxy.Window._highlightIndex; + _uvsPreview.IsolateIndex = proxy.Window._isolateIndex; + } + } + + protected override void Deinitialize() + { + _uvsPreview = null; + + base.Deinitialize(); + } + } + } + protected sealed class UVsLayoutPreviewControl : RenderToTextureControl { private int _channel = -1; @@ -168,15 +280,21 @@ namespace FlaxEditor.Windows.Assets } } - private void DrawMeshUVs(int meshIndex, ref MeshDataCache.MeshData meshData) + private void DrawMeshUVs(int meshIndex, ref MeshDataCache.MeshData meshData, ref Rectangle bounds) { - var uvScale = Size; if (meshData.IndexBuffer == null || meshData.VertexAccessor == null) + { + Render2D.DrawText(Style.Current.FontMedium, "Missing mesh data", bounds, Color.Red, TextAlignment.Center, TextAlignment.Center); return; + } var linesColor = _highlightIndex != -1 && _highlightIndex == meshIndex ? Style.Current.BackgroundSelected : Color.White; var texCoordStream = meshData.VertexAccessor.TexCoord(_channel); if (!texCoordStream.IsValid) + { + Render2D.DrawText(Style.Current.FontMedium, "Missing texcoords channel", bounds, Color.Yellow, TextAlignment.Center, TextAlignment.Center); return; + } + var uvScale = bounds.Size; for (int i = 0; i < meshData.IndexBuffer.Length; i += 3) { // Cache triangle indices @@ -206,19 +324,19 @@ namespace FlaxEditor.Windows.Assets { base.DrawSelf(); - var size = Size; - if (_channel < 0 || size.MaxValue < 5.0f) + var bounds = new Rectangle(Float2.Zero, Size); + if (_channel < 0 || bounds.Size.MaxValue < 5.0f) return; if (Window._meshData == null) Window._meshData = new MeshDataCache(); if (!Window._meshData.RequestMeshData(Window._asset)) { Invalidate(); - Render2D.DrawText(Style.Current.FontMedium, "Loading...", new Rectangle(Float2.Zero, size), Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(Style.Current.FontMedium, "Loading...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); return; } - Render2D.PushClip(new Rectangle(Float2.Zero, size)); + Render2D.PushClip(bounds); var meshDatas = Window._meshData.MeshDatas; var lodIndex = Mathf.Clamp(_lod, 0, meshDatas.Length - 1); @@ -230,12 +348,12 @@ namespace FlaxEditor.Windows.Assets { if (_isolateIndex != -1 && _isolateIndex != meshIndex) continue; - DrawMeshUVs(meshIndex, ref lod[meshIndex]); + DrawMeshUVs(meshIndex, ref lod[meshIndex], ref bounds); } } else { - DrawMeshUVs(mesh, ref lod[mesh]); + DrawMeshUVs(mesh, ref lod[mesh], ref bounds); } Render2D.PopClip(); diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs index 16826b508..dd4a3ec48 100644 --- a/Source/Editor/Windows/Assets/ModelWindow.cs +++ b/Source/Editor/Windows/Assets/ModelWindow.cs @@ -438,93 +438,8 @@ namespace FlaxEditor.Windows.Assets } [CustomEditor(typeof(ProxyEditor))] - private sealed class UVsPropertiesProxy : PropertiesProxyBase + private sealed class UVsPropertiesProxy : UVsPropertiesProxyBase { - public enum UVChannel - { - None, - TexCoord, - LightmapUVs, - }; - - private UVChannel _uvChannel = UVChannel.None; - - [EditorOrder(0), EditorDisplay(null, "Preview UV Channel"), EnumDisplay(EnumDisplayAttribute.FormatMode.None)] - [Tooltip("Set UV channel to preview.")] - public UVChannel Channel - { - get => _uvChannel; - set - { - if (_uvChannel == value) - return; - _uvChannel = value; - Window._meshData?.RequestMeshData(Window._asset); - } - } - - [EditorOrder(1), EditorDisplay(null, "LOD"), Limit(0, Model.MaxLODs), VisibleIf("ShowUVs")] - [Tooltip("Level Of Detail index to preview UVs layout.")] - public int LOD = 0; - - [EditorOrder(2), EditorDisplay(null, "Mesh"), Limit(-1, 1000000), VisibleIf("ShowUVs")] - [Tooltip("Mesh index to preview UVs layout. Use -1 for all meshes")] - public int Mesh = -1; - - private bool ShowUVs => _uvChannel != UVChannel.None; - - /// - public override void OnClean() - { - Channel = UVChannel.None; - - base.OnClean(); - } - - private class ProxyEditor : ProxyEditorBase - { - private UVsLayoutPreviewControl _uvsPreview; - - public override void Initialize(LayoutElementsContainer layout) - { - var proxy = (UVsPropertiesProxy)Values[0]; - if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset)) - return; - - base.Initialize(layout); - - _uvsPreview = layout.Custom().CustomControl; - _uvsPreview.Window = proxy.Window; - } - - /// - public override void Refresh() - { - base.Refresh(); - - if (_uvsPreview != null) - { - var proxy = (UVsPropertiesProxy)Values[0]; - switch (proxy._uvChannel) - { - case UVChannel.TexCoord: _uvsPreview.Channel = 0; break; - case UVChannel.LightmapUVs: _uvsPreview.Channel = 1; break; - default: _uvsPreview.Channel = -1; break; - } - _uvsPreview.LOD = proxy.LOD; - _uvsPreview.Mesh = proxy.Mesh; - _uvsPreview.HighlightIndex = proxy.Window._highlightIndex; - _uvsPreview.IsolateIndex = proxy.Window._isolateIndex; - } - } - - protected override void Deinitialize() - { - _uvsPreview = null; - - base.Deinitialize(); - } - } } [CustomEditor(typeof(ProxyEditor))] diff --git a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs index d26a2153f..21f5ee209 100644 --- a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs +++ b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs @@ -497,87 +497,8 @@ namespace FlaxEditor.Windows.Assets } [CustomEditor(typeof(ProxyEditor))] - private sealed class UVsPropertiesProxy : PropertiesProxyBase + private sealed class UVsPropertiesProxy : UVsPropertiesProxyBase { - public enum UVChannel - { - None, - TexCoord, - }; - - private UVChannel _uvChannel = UVChannel.None; - - [EditorOrder(0), EditorDisplay(null, "Preview UV Channel"), EnumDisplay(EnumDisplayAttribute.FormatMode.None)] - [Tooltip("Set UV channel to preview.")] - public UVChannel Channel - { - get => _uvChannel; - set - { - if (_uvChannel == value) - return; - _uvChannel = value; - Window._meshData?.RequestMeshData(Window._asset); - } - } - - [EditorOrder(1), EditorDisplay(null, "LOD"), Limit(0, Model.MaxLODs), VisibleIf("ShowUVs")] - [Tooltip("Level Of Detail index to preview UVs layout.")] - public int LOD = 0; - - [EditorOrder(2), EditorDisplay(null, "Mesh"), Limit(-1, 1000000), VisibleIf("ShowUVs")] - [Tooltip("Mesh index to preview UVs layout. Use -1 for all meshes")] - public int Mesh = -1; - - private bool ShowUVs => _uvChannel != UVChannel.None; - - /// - public override void OnClean() - { - Channel = UVChannel.None; - - base.OnClean(); - } - - private class ProxyEditor : ProxyEditorBase - { - private UVsLayoutPreviewControl _uvsPreview; - - public override void Initialize(LayoutElementsContainer layout) - { - var proxy = (UVsPropertiesProxy)Values[0]; - if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset)) - return; - - base.Initialize(layout); - - _uvsPreview = layout.Custom().CustomControl; - _uvsPreview.Window = proxy.Window; - } - - /// - public override void Refresh() - { - base.Refresh(); - - if (_uvsPreview != null) - { - var proxy = (UVsPropertiesProxy)Values[0]; - _uvsPreview.Channel = proxy._uvChannel == UVChannel.TexCoord ? 0 : -1; - _uvsPreview.LOD = proxy.LOD; - _uvsPreview.Mesh = proxy.Mesh; - _uvsPreview.HighlightIndex = proxy.Window._highlightIndex; - _uvsPreview.IsolateIndex = proxy.Window._isolateIndex; - } - } - - protected override void Deinitialize() - { - _uvsPreview = null; - - base.Deinitialize(); - } - } } [CustomEditor(typeof(ProxyEditor))] diff --git a/Source/Engine/CSG/CSGData.cpp b/Source/Engine/CSG/CSGData.cpp index 96c768d0c..db59466f9 100644 --- a/Source/Engine/CSG/CSGData.cpp +++ b/Source/Engine/CSG/CSGData.cpp @@ -167,23 +167,22 @@ void RawData::ToModelData(ModelData& modelData) const auto& surface = slot->Surfaces[i]; vertexCount += surface.Vertices.Count(); } - mesh->EnsureCapacity(vertexCount, vertexCount, false, false); + mesh->EnsureCapacity(vertexCount, vertexCount, false, false, false, 2); // Write surfaces into vertex and index buffers int32 index = 0; for (int32 i = 0; i < slot->Surfaces.Count(); i++) { - auto& surface = slot->Surfaces[i]; - + auto& surface = slot->Surfaces.Get()[i]; for (int32 vIndex = 0; vIndex < surface.Vertices.Count(); vIndex++) { - auto& v = surface.Vertices[vIndex]; + auto& v = surface.Vertices.Get()[vIndex]; mesh->Positions.Add(v.Position); - mesh->UVs.Add(v.TexCoord); + mesh->UVs.Get()[0].Add(v.TexCoord); + mesh->UVs.Get()[1].Add(v.LightmapUVs * surface.UVsArea.Size + surface.UVsArea.Location); mesh->Normals.Add(v.Normal); mesh->Tangents.Add(v.Tangent); - mesh->LightmapUVs.Add(v.LightmapUVs * surface.UVsArea.Size + surface.UVsArea.Location); mesh->Indices.Add(index++); } diff --git a/Source/Engine/CSG/Types.h b/Source/Engine/CSG/Types.h index 7100ef694..16e399822 100644 --- a/Source/Engine/CSG/Types.h +++ b/Source/Engine/CSG/Types.h @@ -3,6 +3,8 @@ #pragma once #include "Engine/Core/Config.h" +#include "Engine/Core/Math/Vector2.h" +#include "Engine/Core/Math/Vector3.h" #include "Engine/Level/Actors/BrushMode.h" namespace CSG diff --git a/Source/Engine/Content/Assets/ModelBase.cpp b/Source/Engine/Content/Assets/ModelBase.cpp index 747e7d2f9..cf7969e5d 100644 --- a/Source/Engine/Content/Assets/ModelBase.cpp +++ b/Source/Engine/Content/Assets/ModelBase.cpp @@ -562,11 +562,13 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l LOG(Warning, "Cannot save model with empty meshes."); return true; } - bool hasUVs = mesh.UVs.HasItems(); - if (hasUVs && (uint32)mesh.UVs.Count() != vertices) + for (auto& channel : mesh.UVs) { - LOG(Error, "Invalid size of {0} stream.", TEXT("UVs")); - return true; + if ((uint32)channel.Count() != vertices) + { + LOG(Error, "Invalid size of {0} stream.", TEXT("UVs")); + return true; + } } bool hasNormals = mesh.Normals.HasItems(); if (hasNormals && (uint32)mesh.Normals.Count() != vertices) @@ -586,12 +588,6 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l LOG(Error, "Invalid size of {0} stream.", TEXT("BitangentSigns")); return true; } - bool hasLightmapUVs = mesh.LightmapUVs.HasItems(); - if (hasLightmapUVs && (uint32)mesh.LightmapUVs.Count() != vertices) - { - LOG(Error, "Invalid size of {0} stream.", TEXT("LightmapUVs")); - return true; - } bool hasColors = mesh.Colors.HasItems(); if (hasColors && (uint32)mesh.Colors.Count() != vertices) { @@ -626,7 +622,6 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l byte vbIndex = 0; // TODO: add option to quantize vertex positions (eg. 16-bit) // TODO: add option to quantize vertex attributes (eg. 8-bit blend weights, 8-bit texcoords) - // TODO: add support for 16-bit blend indices (up to 65535 bones) // Position if (useSeparatePositions) @@ -641,10 +636,14 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l auto& vb = vbElements.AddOne(); if (!useSeparatePositions) vb.Add({ VertexElement::Types::Position, vbIndex, 0, 0, PixelFormat::R32G32B32_Float }); - if (hasUVs) - vb.Add({ VertexElement::Types::TexCoord, vbIndex, 0, 0, PixelFormat::R16G16_Float }); - if (hasLightmapUVs) - vb.Add({ VertexElement::Types::TexCoord1, vbIndex, 0, 0, PixelFormat::R16G16_Float }); + for (int32 channelIdx = 0; channelIdx < mesh.UVs.Count(); channelIdx++) + { + auto& channel = mesh.UVs.Get()[channelIdx]; + if (channel.HasItems()) + { + vb.Add({ (VertexElement::Types)((int32)VertexElement::Types::TexCoord0 + channelIdx), vbIndex, 0, 0, PixelFormat::R16G16_Float }); + } + } vb.Add({ VertexElement::Types::Normal, vbIndex, 0, 0, PixelFormat::R10G10B10A2_UNorm }); vb.Add({ VertexElement::Types::Tangent, vbIndex, 0, 0, PixelFormat::R10G10B10A2_UNorm }); if (isSkinned) @@ -745,20 +744,16 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l break; } case VertexElement::Types::TexCoord0: + case VertexElement::Types::TexCoord1: + case VertexElement::Types::TexCoord2: + case VertexElement::Types::TexCoord3: { - const Float2 uv = hasUVs ? mesh.UVs.Get()[vertex] : Float2::Zero; + const int32 channelIdx = (int32)element.Type - (int32)VertexElement::Types::TexCoord0; + const Float2 uv = mesh.UVs.Get()[channelIdx].Get()[vertex]; const Half2 uvEnc(uv); stream.Write(uvEnc); break; } - case VertexElement::Types::TexCoord1: - { - // TODO: refactor LightmapUVs into a generic TexCoord channel and support up to 4 UVs - const Float2 lightmapUV = hasLightmapUVs ? mesh.LightmapUVs.Get()[vertex] : Float2::Zero; - const Half2 lightmapUVEnc(lightmapUV); - stream.Write(lightmapUVEnc); - break; - } default: LOG(Error, "Unsupported vertex element: {}", element.ToString()); return true; diff --git a/Source/Engine/Content/Assets/SkinnedModel.cpp b/Source/Engine/Content/Assets/SkinnedModel.cpp index acfa53a09..0c866de37 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.cpp +++ b/Source/Engine/Content/Assets/SkinnedModel.cpp @@ -6,17 +6,17 @@ #include "Engine/Engine/Engine.h" #include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Streaming/StreamingGroup.h" -#include "Engine/Threading/ThreadPoolTask.h" #include "Engine/Threading/Threading.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/RenderTask.h" -#include "Engine/Graphics/Models/ModelInstanceEntry.h" #include "Engine/Graphics/Models/Config.h" +#include "Engine/Graphics/Models/MeshDeformation.h" +#include "Engine/Graphics/Models/ModelInstanceEntry.h" +#include "Engine/Graphics/Shaders/GPUVertexLayout.h" #include "Engine/Content/Content.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Upgraders/SkinnedModelAssetUpgrader.h" #include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h" -#include "Engine/Graphics/Models/MeshDeformation.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Renderer/DrawCall.h" #if USE_EDITOR diff --git a/Source/Engine/ContentImporters/ImportModel.cpp b/Source/Engine/ContentImporters/ImportModel.cpp index c33ab3853..3d93842c7 100644 --- a/Source/Engine/ContentImporters/ImportModel.cpp +++ b/Source/Engine/ContentImporters/ImportModel.cpp @@ -132,7 +132,10 @@ void RepackMeshLightmapUVs(ModelData& data) { Float2 uvOffset(entry.Slot->X * atlasSizeInv, entry.Slot->Y * atlasSizeInv); Float2 uvScale(entry.Slot->Width * atlasSizeInv, entry.Slot->Height * atlasSizeInv); - for (auto& uv : entry.Mesh->LightmapUVs) + if (entry.Mesh->LightmapUVsIndex == -1) + continue; + auto& lightmapUVs = entry.Mesh->UVs[entry.Mesh->LightmapUVsIndex]; + for (auto& uv : lightmapUVs) { uv = uv * uvScale + uvOffset; } diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index 8170c8e5a..0ca7a0c4f 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -10,7 +10,7 @@ /// /// Current materials shader version. /// -#define MATERIAL_GRAPH_VERSION 170 +#define MATERIAL_GRAPH_VERSION 171 class Material; class GPUShader; diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 370d56931..f2736e18a 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -385,8 +385,33 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in renderContextBatch.GetMainContext().List->AddDrawCall(renderContextBatch, drawModes, info.Flags, shadowsMode, info.Bounds, drawCall, entry.ReceiveDecals, info.SortOrder); } -bool Mesh::Init(uint32 vertices, uint32 triangles, const Array>& vbData, const void* ibData, bool use16BitIndexBuffer, const Array>& vbLayout) +bool Mesh::Init(uint32 vertices, uint32 triangles, const Array>& vbData, const void* ibData, bool use16BitIndexBuffer, Array> vbLayout) { + // Inject lightmap uv coordinate index into the vertex layout of one of the buffers + if (LightmapUVsIndex != -1) + { + const auto vertexElementType = (VertexElement::Types)((int32)VertexElement::Types::TexCoord0 + LightmapUVsIndex); + for (int32 vbIndex = 0; vbIndex < vbLayout.Count(); vbIndex++) + { + // Check if layout contains lightmap uvs texcoords channel + GPUVertexLayout* layout = vbLayout[vbIndex]; + VertexElement element = layout->FindElement(vertexElementType); + if (element.Type == vertexElementType) + { + // Ensure element doesn't exist in this layout + if (layout->FindElement(VertexElement::Types::Lightmap).Format == PixelFormat::Unknown) + { + GPUVertexLayout::Elements elements = layout->GetElements(); + element.Type = VertexElement::Types::Lightmap; + elements.Add(element); + layout = GPUVertexLayout::Get(elements, true); + vbLayout[vbIndex] = layout; + } + break; + } + } + } + if (MeshBase::Init(vertices, triangles, vbData, ibData, use16BitIndexBuffer, vbLayout)) return true; diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index 9d8f532c1..7ca50a9c2 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -172,7 +172,7 @@ public: public: // [MeshBase] - bool Init(uint32 vertices, uint32 triangles, const Array>& vbData, const void* ibData, bool use16BitIndexBuffer, const Array>& vbLayout) override; + bool Init(uint32 vertices, uint32 triangles, const Array>& vbData, const void* ibData, bool use16BitIndexBuffer, Array> vbLayout) override; void Release() override; private: diff --git a/Source/Engine/Graphics/Models/MeshBase.cpp b/Source/Engine/Graphics/Models/MeshBase.cpp index a845bdf4b..7df3670cc 100644 --- a/Source/Engine/Graphics/Models/MeshBase.cpp +++ b/Source/Engine/Graphics/Models/MeshBase.cpp @@ -330,7 +330,7 @@ GPUVertexLayout* MeshBase::GetVertexLayout() const return GPUVertexLayout::Get(Span(_vertexBuffers, MODEL_MAX_VB)); } -bool MeshBase::Init(uint32 vertices, uint32 triangles, const Array>& vbData, const void* ibData, bool use16BitIndexBuffer, const Array>& vbLayout) +bool MeshBase::Init(uint32 vertices, uint32 triangles, const Array>& vbData, const void* ibData, bool use16BitIndexBuffer, Array> vbLayout) { CHECK_RETURN(vbData.HasItems() && vertices, true); CHECK_RETURN(ibData, true); diff --git a/Source/Engine/Graphics/Models/MeshBase.h b/Source/Engine/Graphics/Models/MeshBase.h index e256b4826..1b80edb1b 100644 --- a/Source/Engine/Graphics/Models/MeshBase.h +++ b/Source/Engine/Graphics/Models/MeshBase.h @@ -221,7 +221,7 @@ public: /// True to use 16-bit indices for the index buffer (true: uint16, false: uint32). /// Layout descriptors for the vertex buffers attributes (one for each vertex buffer). /// True if failed, otherwise false. - API_FUNCTION(Sealed) virtual bool Init(uint32 vertices, uint32 triangles, const Array>& vbData, const void* ibData, bool use16BitIndexBuffer, const Array>& vbLayout); + API_FUNCTION(Sealed) virtual bool Init(uint32 vertices, uint32 triangles, const Array>& vbData, const void* ibData, bool use16BitIndexBuffer, Array> vbLayout); /// /// Releases the mesh data (GPU buffers and local cache). diff --git a/Source/Engine/Graphics/Models/ModelData.Tool.cpp b/Source/Engine/Graphics/Models/ModelData.Tool.cpp index a62fa6bae..cce73061d 100644 --- a/Source/Engine/Graphics/Models/ModelData.Tool.cpp +++ b/Source/Engine/Graphics/Models/ModelData.Tool.cpp @@ -6,7 +6,7 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Utilities.h" #include "Engine/Core/Types/DateTime.h" -#include "Engine/Core/Types/TimeSpan.h" +#include "Engine/Core/Types/Stopwatch.h" #include "Engine/Core/Collections/BitArray.h" #include "Engine/Tools/ModelTool/ModelTool.h" #include "Engine/Tools/ModelTool/VertexTriangleAdjacency.h" @@ -77,8 +77,39 @@ void RemapArrayHelper(Array& target, const std::vector& remap) } } +void MeshData::SetLightmapUVsSource(ModelLightmapUVsSource source) +{ + if (source == ModelLightmapUVsSource::Disable) + { + // No lightmap UVs + } + else if (source == ModelLightmapUVsSource::Generate) + { + // Generate lightmap UVs + if (GenerateLightmapUVs()) + { + LOG(Error, "Failed to generate lightmap uvs"); + } + } + else + { + // Select input channel index + const int32 inputChannelIndex = (int32)source - (int32)ModelLightmapUVsSource::Channel0; + if (inputChannelIndex >= 0 && inputChannelIndex < UVs.Count() && UVs[inputChannelIndex].HasItems()) + { + LightmapUVsIndex = inputChannelIndex; + } + else + { + LOG(Warning, "Cannot import result lightmap uvs. Missing texcoords channel {0}.", inputChannelIndex); + } + } +} + bool MeshData::GenerateLightmapUVs() { + if (Positions.IsEmpty() || Indices.IsEmpty()) + return true; PROFILE_CPU(); #if PLATFORM_WINDOWS // Prepare @@ -87,8 +118,7 @@ bool MeshData::GenerateLightmapUVs() int32 facesCount = Indices.Count() / 3; DirectX::XMFLOAT3* positions = (DirectX::XMFLOAT3*)Positions.Get(); LOG(Info, "Generating lightmaps UVs ({0} vertices, {1} triangles)...", verticesCount, facesCount); - - DateTime startTime = DateTime::Now(); + Stopwatch stopwatch; // Generate adjacency data const float adjacencyEpsilon = 0.001f; @@ -126,27 +156,30 @@ bool MeshData::GenerateLightmapUVs() return true; } - const DateTime endTime = DateTime::Now(); - // Log info - const int32 nTotalVerts = (int32)vb.size(); - const int32 msTime = Math::CeilToInt((float)(endTime - startTime).GetTotalMilliseconds()); - LOG(Info, "Lightmap UVs generated! Charts: {0}, stretching: {1}, {2} vertices. Time: {3}ms", outCharts, outStretch, nTotalVerts, msTime); + stopwatch.Stop(); + LOG(Info, "Lightmap UVs generated! Charts: {0}, stretching: {1}, {2} vertices. Time: {3}ms", outCharts, outStretch, (int32)vb.size(), stopwatch.GetMilliseconds()); // Update mesh data (remap vertices due to vertex buffer and index buffer change) RemapArrayHelper(Positions, vertexRemapArray); - RemapArrayHelper(UVs, vertexRemapArray); + LightmapUVsIndex = Math::Min(UVs.Count(), MODEL_MAX_UV - 1); + for (int32 channel = 0; channel < LightmapUVsIndex; channel++) + RemapArrayHelper(UVs[channel], vertexRemapArray); RemapArrayHelper(Normals, vertexRemapArray); RemapArrayHelper(Tangents, vertexRemapArray); RemapArrayHelper(Colors, vertexRemapArray); RemapArrayHelper(BlendIndices, vertexRemapArray); RemapArrayHelper(BlendWeights, vertexRemapArray); - LightmapUVs.Resize(nTotalVerts, false); - for (int32 i = 0; i < nTotalVerts; i++) - LightmapUVs[i] = *(Float2*)&vb[i].uv; uint32* ibP = (uint32*)ib.data(); for (int32 i = 0; i < Indices.Count(); i++) Indices[i] = *ibP++; + + // Add generated data + UVs.Resize(LightmapUVsIndex + 1); + auto& lightmapChannel = UVs[LightmapUVsIndex]; + lightmapChannel.Resize((int32)vb.size(), false); + for (int32 i = 0; i < (int32)vb.size(); i++) + lightmapChannel.Get()[i] = *(Float2*)&vb[i].uv; #else LOG(Error, "Model lightmap UVs generation is not supported on this platform."); #endif @@ -162,6 +195,10 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3 ) { const float uvEpsSqr = (1.0f / 250.0f) * (1.0f / 250.0f); + const Float2* uv0 = mesh.UVs.Count() > 0 && mesh.UVs[0].HasItems() ? mesh.UVs[0].Get() : nullptr; + const Float2* uv1 = mesh.UVs.Count() > 1 && mesh.UVs[1].HasItems() ? mesh.UVs[1].Get() : nullptr; + const Float2* uv2 = mesh.UVs.Count() > 2 && mesh.UVs[2].HasItems() ? mesh.UVs[2].Get() : nullptr; + const Float2* uv3 = mesh.UVs.Count() > 3 && mesh.UVs[3].HasItems() ? mesh.UVs[3].Get() : nullptr; #if USE_SPATIAL_SORT const Float3 vPosition = mesh.Positions[vertexIndex]; @@ -169,10 +206,12 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3 if (spatialSortCache.empty()) return INVALID_INDEX; - const Float2 vUV = mesh.UVs.HasItems() ? mesh.UVs[vertexIndex] : Float2::Zero; + const Float2 vUV0 = uv0 ? uv0[vertexIndex] : Float2::Zero; + const Float2 vUV1 = uv1 ? uv1[vertexIndex] : Float2::Zero; + const Float2 vUV2 = uv2 ? uv2[vertexIndex] : Float2::Zero; + const Float2 vUV3 = uv3 ? uv3[vertexIndex] : Float2::Zero; const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero; const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero; - const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero; const Color vColor = mesh.Colors.HasItems() ? mesh.Colors[vertexIndex] : Color::Black; // Assuming Color::Black as a default color const int32 end = startIndex + searchRange; @@ -184,10 +223,12 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3 continue; #else const Float3 vPosition = mesh.Positions[vertexIndex]; - const Float2 vUV = mesh.UVs.HasItems() ? mesh.UVs[vertexIndex] : Float2::Zero; + const Float2 vUV0 = uv0 ? uv0[vertexIndex] : Float2::Zero; + const Float2 vUV1 = uv1 ? uv1[vertexIndex] : Float2::Zero; + const Float2 vUV2 = uv2 ? uv2[vertexIndex] : Float2::Zero; + const Float2 vUV3 = uv3 ? uv3[vertexIndex] : Float2::Zero; const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero; const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero; - const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero; const Color vColor = mesh.Colors.HasItems() ? mesh.Colors[vertexIndex] : Color::Black; // Assuming Color::Black as a default color const int32 end = startIndex + searchRange; @@ -199,17 +240,20 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3 #endif if (mapping[v] == INVALID_INDEX) continue; - if (mesh.UVs.HasItems() && (vUV - mesh.UVs[v]).LengthSquared() > uvEpsSqr) + if (uv0 && (vUV0 - uv0[v]).LengthSquared() > uvEpsSqr) + continue; + if (uv1 && (vUV1 - uv1[v]).LengthSquared() > uvEpsSqr) + continue; + if (uv2 && (vUV2 - uv2[v]).LengthSquared() > uvEpsSqr) + continue; + if (uv3 && (vUV3 - uv3[v]).LengthSquared() > uvEpsSqr) continue; if (mesh.Normals.HasItems() && Float3::Dot(vNormal, mesh.Normals[v]) < 0.98f) continue; if (mesh.Tangents.HasItems() && Float3::Dot(vTangent, mesh.Tangents[v]) < 0.98f) continue; - if (mesh.LightmapUVs.HasItems() && (vLightmapUV - mesh.LightmapUVs[v]).LengthSquared() > uvEpsSqr) - continue; if (mesh.Colors.HasItems() && vColor != mesh.Colors[v]) continue; - // TODO: check more components? return v; } @@ -238,7 +282,7 @@ void RemapBuffer(Array& src, Array& dst, const Array& mapping, int3 void MeshData::BuildIndexBuffer() { PROFILE_CPU(); - const auto startTime = Platform::GetTimeSeconds(); + Stopwatch stopwatch; const int32 vertexCount = Positions.Count(); MeshData newMesh; @@ -287,14 +331,15 @@ void MeshData::BuildIndexBuffer() newMesh.SwapBuffers(*this); #define REMAP_BUFFER(name) RemapBuffer(newMesh.name, name, mapping, newVertexCounter) REMAP_BUFFER(Positions); - REMAP_BUFFER(UVs); REMAP_BUFFER(Normals); REMAP_BUFFER(Tangents); REMAP_BUFFER(BitangentSigns); - REMAP_BUFFER(LightmapUVs); REMAP_BUFFER(Colors); REMAP_BUFFER(BlendIndices); REMAP_BUFFER(BlendWeights); + UVs.Resize(newMesh.UVs.Count()); + for (int32 channelIdx = 0; channelIdx < UVs.Count(); channelIdx++) + REMAP_BUFFER(UVs[channelIdx]); #undef REMAP_BUFFER BlendShapes.Resize(newMesh.BlendShapes.Count()); for (int32 blendShapeIndex = 0; blendShapeIndex < newMesh.BlendShapes.Count(); blendShapeIndex++) @@ -317,8 +362,8 @@ void MeshData::BuildIndexBuffer() } } - const auto endTime = Platform::GetTimeSeconds(); - const double time = Utilities::RoundTo2DecimalPlaces(endTime - startTime); + stopwatch.Stop(); + const double time = Utilities::RoundTo2DecimalPlaces(stopwatch.GetTotalSeconds()); if (time > 0.5f) // Don't log if generation was fast enough LOG(Info, "Generated {3} for mesh in {0}s ({1} vertices, {2} indices)", time, vertexCount, Indices.Count(), TEXT("indices")); } @@ -494,7 +539,7 @@ namespace void GetTexCoord(const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert) { const auto meshData = (MeshData*)pContext->m_pUserData; - const auto e = meshData->UVs[meshData->Indices[iFace * 3 + iVert]]; + const auto e = meshData->UVs[0][meshData->Indices[iFace * 3 + iVert]]; fvTexcOut[0] = e.X; fvTexcOut[1] = e.Y; } @@ -518,7 +563,7 @@ bool MeshData::GenerateTangents(float smoothingAngle) } if (Normals.IsEmpty() || UVs.IsEmpty()) { - LOG(Warning, "Missing normals or texcoors data to generate tangents."); + LOG(Warning, "Missing normals or texcoords data to generate tangents."); return true; } PROFILE_CPU(); @@ -549,7 +594,7 @@ bool MeshData::GenerateTangents(float smoothingAngle) vertexDone.SetAll(false); const Float3* meshNorm = Normals.Get(); - const Float2* meshTex = UVs.Get(); + const Float2* meshTex = UVs[0].Get(); Float3* meshTang = Tangents.Get(); // Calculate the tangent per-triangle diff --git a/Source/Engine/Graphics/Models/ModelData.cpp b/Source/Engine/Graphics/Models/ModelData.cpp index fddde25ce..be9e33b38 100644 --- a/Source/Engine/Graphics/Models/ModelData.cpp +++ b/Source/Engine/Graphics/Models/ModelData.cpp @@ -17,21 +17,21 @@ void MeshData::Clear() Normals.Clear(); Tangents.Clear(); BitangentSigns.Clear(); - LightmapUVs.Clear(); Colors.Clear(); BlendIndices.Clear(); BlendWeights.Clear(); BlendShapes.Clear(); } -void MeshData::EnsureCapacity(int32 vertices, int32 indices, bool preserveContents, bool withColors, bool withSkin) +void MeshData::EnsureCapacity(int32 vertices, int32 indices, bool preserveContents, bool withColors, bool withSkin, int32 texcoords) { Positions.EnsureCapacity(vertices, preserveContents); Indices.EnsureCapacity(indices, preserveContents); - UVs.EnsureCapacity(vertices, preserveContents); + UVs.Resize(texcoords); + for (auto& channel : UVs) + channel.EnsureCapacity(vertices, preserveContents); Normals.EnsureCapacity(vertices, preserveContents); Tangents.EnsureCapacity(vertices, preserveContents); - LightmapUVs.EnsureCapacity(vertices, preserveContents); Colors.EnsureCapacity(withColors ? vertices : 0, preserveContents); BlendIndices.EnsureCapacity(withSkin ? vertices : 0, preserveContents); BlendWeights.EnsureCapacity(withSkin ? vertices : 0, preserveContents); @@ -45,7 +45,6 @@ void MeshData::SwapBuffers(MeshData& other) Normals.Swap(other.Normals); Tangents.Swap(other.Tangents); BitangentSigns.Swap(other.BitangentSigns); - LightmapUVs.Swap(other.LightmapUVs); Colors.Swap(other.Colors); BlendIndices.Swap(other.BlendIndices); BlendWeights.Swap(other.BlendWeights); @@ -61,7 +60,6 @@ void MeshData::Release() Normals.Resize(0); Tangents.Resize(0); BitangentSigns.Resize(0); - LightmapUVs.Resize(0); Colors.Resize(0); BlendIndices.Resize(0); BlendWeights.Resize(0); @@ -72,11 +70,12 @@ PRAGMA_DISABLE_DEPRECATION_WARNINGS void MeshData::InitFromModelVertices(ModelVertex19* vertices, uint32 verticesCount) { Positions.Resize(verticesCount, false); - UVs.Resize(verticesCount, false); + UVs.Resize(2); + UVs[0].Resize(verticesCount, false); + UVs[1].Resize(verticesCount, false); Normals.Resize(verticesCount, false); Tangents.Resize(verticesCount, false); BitangentSigns.Resize(0); - LightmapUVs.Resize(verticesCount, false); Colors.Resize(0); BlendIndices.Resize(0); BlendWeights.Resize(0); @@ -85,10 +84,10 @@ void MeshData::InitFromModelVertices(ModelVertex19* vertices, uint32 verticesCou for (uint32 i = 0; i < verticesCount; i++) { Positions[i] = vertices->Position; - UVs[i] = vertices->TexCoord.ToFloat2(); + UVs[0][i] = vertices->TexCoord.ToFloat2(); + UVs[1][i] = vertices->LightmapUVs.ToFloat2(); Normals[i] = vertices->Normal.ToFloat3() * 2.0f - 1.0f; Tangents[i] = vertices->Tangent.ToFloat3() * 2.0f - 1.0f; - LightmapUVs[i] = vertices->LightmapUVs.ToFloat2(); Colors[i] = Color(vertices->Color); vertices++; @@ -98,11 +97,12 @@ void MeshData::InitFromModelVertices(ModelVertex19* vertices, uint32 verticesCou void MeshData::InitFromModelVertices(VB0ElementType18* vb0, VB1ElementType18* vb1, uint32 verticesCount) { Positions.Resize(verticesCount, false); - UVs.Resize(verticesCount, false); + UVs.Resize(2); + UVs[0].Resize(verticesCount, false); + UVs[1].Resize(verticesCount, false); Normals.Resize(verticesCount, false); Tangents.Resize(verticesCount, false); BitangentSigns.Resize(0); - LightmapUVs.Resize(verticesCount, false); Colors.Resize(0); BlendIndices.Resize(0); BlendWeights.Resize(0); @@ -111,10 +111,10 @@ void MeshData::InitFromModelVertices(VB0ElementType18* vb0, VB1ElementType18* vb for (uint32 i = 0; i < verticesCount; i++) { Positions[i] = vb0->Position; - UVs[i] = vb1->TexCoord.ToFloat2(); + UVs[0][i] = vb1->TexCoord.ToFloat2(); + UVs[1][i] = vb1->LightmapUVs.ToFloat2(); Normals[i] = vb1->Normal.ToFloat3() * 2.0f - 1.0f; Tangents[i] = vb1->Tangent.ToFloat3() * 2.0f - 1.0f; - LightmapUVs[i] = vb1->LightmapUVs.ToFloat2(); vb0++; vb1++; @@ -124,19 +124,16 @@ void MeshData::InitFromModelVertices(VB0ElementType18* vb0, VB1ElementType18* vb void MeshData::InitFromModelVertices(VB0ElementType18* vb0, VB1ElementType18* vb1, VB2ElementType18* vb2, uint32 verticesCount) { Positions.Resize(verticesCount, false); - UVs.Resize(verticesCount, false); + UVs.Resize(2); + UVs[0].Resize(verticesCount, false); + UVs[1].Resize(verticesCount, false); Normals.Resize(verticesCount, false); Tangents.Resize(verticesCount, false); BitangentSigns.Resize(0); - LightmapUVs.Resize(verticesCount, false); if (vb2) - { Colors.Resize(verticesCount, false); - } else - { Colors.Resize(0); - } BlendIndices.Resize(0); BlendWeights.Resize(0); BlendShapes.Resize(0); @@ -144,10 +141,10 @@ void MeshData::InitFromModelVertices(VB0ElementType18* vb0, VB1ElementType18* vb for (uint32 i = 0; i < verticesCount; i++) { Positions[i] = vb0->Position; - UVs[i] = vb1->TexCoord.ToFloat2(); + UVs[0][i] = vb1->TexCoord.ToFloat2(); + UVs[1][i] = vb1->LightmapUVs.ToFloat2(); Normals[i] = vb1->Normal.ToFloat3() * 2.0f - 1.0f; Tangents[i] = vb1->Tangent.ToFloat3() * 2.0f - 1.0f; - LightmapUVs[i] = vb1->LightmapUVs.ToFloat2(); if (vb2) { Colors[i] = Color(vb2->Color); @@ -310,22 +307,32 @@ void MeshData::Merge(MeshData& other) } // Merge vertex buffer -#define MERGE(item, defautValue) \ +#define MERGE(item, defaultValue) \ if (item.HasItems() && other.item.HasItems()) \ item.Add(other.item); \ else if (item.HasItems() && !other.item.HasItems()) \ - for (int32 i = 0; i < other.Positions.Count(); i++) item.Add(defautValue); \ + for (int32 i = 0; i < other.Positions.Count(); i++) item.Add(defaultValue); \ else if (!item.HasItems() && other.item.HasItems()) \ - for (int32 i = 0; i < Positions.Count(); i++) item.Add(defautValue) + for (int32 i = 0; i < Positions.Count(); i++) item.Add(defaultValue) MERGE(Positions, Float3::Zero); - MERGE(UVs, Float2::Zero); MERGE(Normals, Float3::Forward); MERGE(Tangents, Float3::Right); MERGE(BitangentSigns, 1.0f); - MERGE(LightmapUVs, Float2::Zero); MERGE(Colors, Color::Black); MERGE(BlendIndices, Int4::Zero); MERGE(BlendWeights, Float4::Zero); + if (other.UVs.Count() > UVs.Count()) + UVs.Resize(other.UVs.Count()); + for (int32 channelIdx = 0; channelIdx < UVs.Count(); channelIdx++) + { + if (other.UVs.Count() <= channelIdx) + { + for (int32 i = 0; i < other.Positions.Count(); i++) + UVs[channelIdx].Add(Float2::Zero); + continue; + } + MERGE(UVs[channelIdx], Float2::Zero); + } #undef MERGE // Merge blend shapes diff --git a/Source/Engine/Graphics/Models/ModelData.h b/Source/Engine/Graphics/Models/ModelData.h index 615a3d31a..fba857ff3 100644 --- a/Source/Engine/Graphics/Models/ModelData.h +++ b/Source/Engine/Graphics/Models/ModelData.h @@ -41,9 +41,7 @@ public: /// /// Texture coordinates (list of channels) /// - // TODO: multiple UVs - Array UVs; - Array LightmapUVs; // TODO: remove this and move to UVs + Array, FixedAllocation> UVs; /// /// Normals vector @@ -121,7 +119,8 @@ public: /// Failed if clear data otherwise will try to preserve the buffers contents. /// True if use vertex colors buffer. /// True if use vertex blend indices and weights buffer. - void EnsureCapacity(int32 vertices, int32 indices, bool preserveContents = false, bool withColors = true, bool withSkin = true); + /// Amount of texture coordinate channels to use. + void EnsureCapacity(int32 vertices, int32 indices, bool preserveContents = false, bool withColors = true, bool withSkin = true, int32 texcoords = 1); /// /// Swaps the vertex and index buffers contents (without a data copy) with the other mesh. @@ -189,6 +188,11 @@ public: void CalculateBounds(BoundingBox& box, BoundingSphere& sphere) const; #if COMPILE_WITH_MODEL_TOOL + /// + /// Setups Lightmap UVs based on the option. + /// + void SetLightmapUVsSource(ModelLightmapUVsSource source); + /// /// Generate lightmap uvs for the mesh entry /// diff --git a/Source/Engine/Graphics/Shaders/VertexElement.h b/Source/Engine/Graphics/Shaders/VertexElement.h index 3744c2f6a..8fbe75473 100644 --- a/Source/Engine/Graphics/Shaders/VertexElement.h +++ b/Source/Engine/Graphics/Shaders/VertexElement.h @@ -55,6 +55,8 @@ PACK_BEGIN() struct FLAXENGINE_API VertexElement Attribute2 = 17, // General purpose attribute (at index 3). Maps to 'ATTRIBUTE3' semantic in the shader. Attribute3 = 18, + // Lightmap UVs that usually map one of the texture coordinate channels. Maps to 'LIGHTMAP' semantic in the shader. + Lightmap = 30, // Texture coordinate. Maps to 'TEXCOORD' semantic in the shader. TexCoord = TexCoord0, // General purpose attribute. Maps to 'ATTRIBUTE0' semantic in the shader. diff --git a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp index 94e2513d5..d64df3857 100644 --- a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.cpp @@ -325,6 +325,8 @@ LPCSTR RenderToolsDX::GetVertexInputSemantic(VertexElement::Types type, UINT& se case VertexElement::Types::Attribute3: semanticIndex = 3; return "ATTRIBUTE"; + case VertexElement::Types::Lightmap: + return "LIGHTMAP"; default: LOG(Fatal, "Invalid vertex shader element semantic type"); return ""; diff --git a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp index 8e3ca2abf..d931ee5b0 100644 --- a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp +++ b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp @@ -488,6 +488,8 @@ VertexElement::Types ShaderCompiler::ParseVertexElementType(StringAnsiView seman return VertexElement::Types::Tangent; if (semantic == "BLENDINDICES") return VertexElement::Types::BlendIndices; + if (semantic == "LIGHTMAP") + return VertexElement::Types::Lightmap; if (semantic == "BLENDWEIGHTS" || semantic == "BLENDWEIGHT") // [Deprecated in v1.10] return VertexElement::Types::BlendWeights; diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp index 58448f5ea..691924507 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp @@ -189,8 +189,20 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) } // TexCoord case 2: - value = getUVs; + { + const auto layer = GetRootLayer(); + if (layer && layer->Domain == MaterialDomain::Surface) + { + const uint32 channel = node->Values.HasItems() ? Math::Min((uint32)node->Values[0], 3u) : 0u; + value = Value(VariantType::Float2, String::Format(TEXT("input.TexCoords[{}]"), channel)); + } + else + { + // TODO: migrate all material domain templates to TexCoords array (of size MATERIAL_TEXCOORDS=1) + value = getUVs; + } break; + } // Cube Texture case 3: { diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp index 3e54f14be..f6024ce8e 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp @@ -15,7 +15,6 @@ MaterialLayer::MaterialLayer(const Guid& id) , ShadingModel(MaterialShadingModel::Lit) , MaskThreshold(0.3f) , OpacityThreshold(0.12f) - , ParamIdsMappings(8) { ASSERT(ID.IsValid()); } diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp index ff8dc31fe..df6a5e90f 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp @@ -246,13 +246,15 @@ bool ProcessMesh(ModelData& result, AssimpImporterData& data, const aiMesh* aMes mesh.Positions.Set((const Float3*)aMesh->mVertices, aMesh->mNumVertices); // Texture coordinates - if (aMesh->mTextureCoords[0]) + for (int32 channelIndex = 0; channelIndex < MODEL_MAX_UV && aMesh->mTextureCoords[channelIndex]; channelIndex++) { - mesh.UVs.Resize(aMesh->mNumVertices, false); - aiVector3D* a = aMesh->mTextureCoords[0]; + mesh.UVs.Resize(channelIndex + 1); + auto& channel = mesh.UVs[channelIndex]; + channel.Resize(aMesh->mNumVertices, false); + aiVector3D* a = aMesh->mTextureCoords[channelIndex]; for (uint32 v = 0; v < aMesh->mNumVertices; v++) { - mesh.UVs[v] = *(Float2*)a; + channel.Get()[v] = *(Float2*)a; a++; } } @@ -265,7 +267,7 @@ bool ProcessMesh(ModelData& result, AssimpImporterData& data, const aiMesh* aMes const auto face = &aMesh->mFaces[faceIndex]; if (face->mNumIndices != 3) { - errorMsg = TEXT("All faces in a mesh must be trangles!"); + errorMsg = TEXT("All faces in a mesh must be triangles!"); return true; } @@ -296,57 +298,7 @@ bool ProcessMesh(ModelData& result, AssimpImporterData& data, const aiMesh* aMes } // Lightmap UVs - if (data.Options.LightmapUVsSource == ModelLightmapUVsSource::Disable) - { - // No lightmap UVs - } - else if (data.Options.LightmapUVsSource == ModelLightmapUVsSource::Generate) - { - // Generate lightmap UVs - if (mesh.GenerateLightmapUVs()) - { - LOG(Error, "Failed to generate lightmap uvs"); - } - } - else - { - // Select input channel index - int32 inputChannelIndex; - switch (data.Options.LightmapUVsSource) - { - case ModelLightmapUVsSource::Channel0: - inputChannelIndex = 0; - break; - case ModelLightmapUVsSource::Channel1: - inputChannelIndex = 1; - break; - case ModelLightmapUVsSource::Channel2: - inputChannelIndex = 2; - break; - case ModelLightmapUVsSource::Channel3: - inputChannelIndex = 3; - break; - default: - inputChannelIndex = INVALID_INDEX; - break; - } - - // Check if has that channel texcoords - if (inputChannelIndex >= 0 && inputChannelIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS && aMesh->mTextureCoords[inputChannelIndex]) - { - mesh.LightmapUVs.Resize(aMesh->mNumVertices, false); - aiVector3D* a = aMesh->mTextureCoords[inputChannelIndex]; - for (uint32 v = 0; v < aMesh->mNumVertices; v++) - { - mesh.LightmapUVs[v] = *(Float2*)a; - a++; - } - } - else - { - LOG(Warning, "Cannot import result lightmap uvs. Missing texcoords channel {0}.", inputChannelIndex); - } - } + mesh.SetLightmapUVsSource(data.Options.LightmapUVsSource); // Vertex Colors if (data.Options.ImportVertexColors && aMesh->mColors[0]) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.AutodeskFbxSdk.cpp b/Source/Engine/Tools/ModelTool/ModelTool.AutodeskFbxSdk.cpp index 1ea6a22ad..03eda839c 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.AutodeskFbxSdk.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.AutodeskFbxSdk.cpp @@ -386,10 +386,12 @@ bool ProcessMesh(ImporterData& data, FbxMesh* fbxMesh, MeshData& mesh, String& e } // Texture coordinates - FbxGeometryElementUV* texcoords = fbxMesh->GetElementUV(0); - if (texcoords) + for (int32 channelIndex = 0; channelIndex < MODEL_MAX_UV && fbxMesh->GetElementUV(channelIndex); channelIndex++) { - ReadLayerData(fbxMesh, *texcoords, mesh.UVs); + FbxGeometryElementUV* texcoords = fbxMesh->GetElementUV(0); + mesh.UVs.Resize(channelIndex + 1); + auto& channel = mesh.UVs[channelIndex]; + ReadLayerData(fbxMesh, *texcoords, channel); } // Normals @@ -405,53 +407,7 @@ bool ProcessMesh(ImporterData& data, FbxMesh* fbxMesh, MeshData& mesh, String& e } // Lightmap UVs - if (data.Options.LightmapUVsSource == ModelLightmapUVsSource::Disable) - { - // No lightmap UVs - } - else if (data.Options.LightmapUVsSource == ModelLightmapUVsSource::Generate) - { - // Generate lightmap UVs - if (mesh.GenerateLightmapUVs()) - { - // TODO: we could propagate this message to Debug Console in editor? or create interface to gather some msgs from importing service - LOG(Warning, "Failed to generate lightmap uvs"); - } - } - else - { - // Select input channel index - int32 inputChannelIndex; - switch (data.Options.LightmapUVsSource) - { - case ModelLightmapUVsSource::Channel0: - inputChannelIndex = 0; - break; - case ModelLightmapUVsSource::Channel1: - inputChannelIndex = 1; - break; - case ModelLightmapUVsSource::Channel2: - inputChannelIndex = 2; - break; - case ModelLightmapUVsSource::Channel3: - inputChannelIndex = 3; - break; - default: - inputChannelIndex = INVALID_INDEX; - break; - } - - // Check if has that channel texcoords - if (inputChannelIndex >= 0 && inputChannelIndex < fbxMesh->GetElementUVCount() && fbxMesh->GetElementUV(inputChannelIndex)) - { - ReadLayerData(fbxMesh, *fbxMesh->GetElementUV(inputChannelIndex), mesh.LightmapUVs); - } - else - { - // TODO: we could propagate this message to Debug Console in editor? or create interface to gather some msgs from importing service - LOG(Warning, "Cannot import model lightmap uvs. Missing texcoords channel {0}.", inputChannelIndex); - } - } + mesh.SetLightmapUVsSource(data.Options.LightmapUVsSource); // Vertex Colors if (data.Options.ImportVertexColors && fbxMesh->GetElementVertexColorCount() > 0) @@ -609,10 +565,11 @@ bool ProcessMesh(ImporterData& data, FbxMesh* fbxMesh, MeshData& mesh, String& e } // Flip the Y in texcoords - for (int32 i = 0; i < mesh.UVs.Count(); i++) - mesh.UVs[i].Y = 1.0f - mesh.UVs[i].Y; - for (int32 i = 0; i < mesh.LightmapUVs.Count(); i++) - mesh.LightmapUVs[i].Y = 1.0f - mesh.LightmapUVs[i].Y; + for (auto& channel : mesh.UVs) + { + for (int32 i = 0; i < channel.Count(); i++) + channel[i].Y = 1.0f - channel[i].Y; + } // Handle missing material case (could never happen but it's better to be sure it will work) if (mesh.MaterialSlotIndex == -1) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index dcb0b6c5f..750b0e059 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -690,7 +690,6 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* const ofbx::GeometryPartition& partition = geometryData.getPartition(partitionIndex); const int vertexCount = partition.triangles_count * 3; const ofbx::Vec3Attributes& positions = geometryData.getPositions(); - const ofbx::Vec2Attributes& uvs = geometryData.getUVs(); const ofbx::Vec3Attributes& normals = geometryData.getNormals(); const ofbx::Vec3Attributes& tangents = geometryData.getTangents(); const ofbx::Vec4Attributes& colors = geometryData.getColors(); @@ -723,15 +722,18 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* mesh.Indices.Get()[i] = i; // Texture coordinates - if (uvs.values) + for (int32 channelIndex = 0; channelIndex < MODEL_MAX_UV && geometryData.getUVs(channelIndex).values; channelIndex++) { - mesh.UVs.Resize(vertexCount, false); + const ofbx::Vec2Attributes& uvs = geometryData.getUVs(channelIndex); + mesh.UVs.Resize(channelIndex + 1); + auto& channel = mesh.UVs[channelIndex]; + channel.Resize(vertexCount, false); for (int i = 0; i < vertexCount; i++) - mesh.UVs.Get()[i] = ToFloat2(uvs.get(triangulatedIndices[i])); + channel.Get()[i] = ToFloat2(uvs.get(triangulatedIndices[i])); if (data.ConvertRH) { - for (int32 v = 0; v < vertexCount; v++) - mesh.UVs[v].Y = 1.0f - mesh.UVs[v].Y; + for (int v = 0; v < vertexCount; v++) + channel.Get()[v].Y = 1.0f - channel.Get()[v].Y; } } @@ -776,59 +778,7 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* } // Lightmap UVs - if (data.Options.LightmapUVsSource == ModelLightmapUVsSource::Disable) - { - // No lightmap UVs - } - else if (data.Options.LightmapUVsSource == ModelLightmapUVsSource::Generate) - { - // Generate lightmap UVs - if (mesh.GenerateLightmapUVs()) - { - LOG(Error, "Failed to generate lightmap uvs"); - } - } - else - { - // Select input channel index - int32 inputChannelIndex; - switch (data.Options.LightmapUVsSource) - { - case ModelLightmapUVsSource::Channel0: - inputChannelIndex = 0; - break; - case ModelLightmapUVsSource::Channel1: - inputChannelIndex = 1; - break; - case ModelLightmapUVsSource::Channel2: - inputChannelIndex = 2; - break; - case ModelLightmapUVsSource::Channel3: - inputChannelIndex = 3; - break; - default: - inputChannelIndex = INVALID_INDEX; - break; - } - - // Check if has that channel texcoords - const auto lightmapUVs = geometryData.getUVs(inputChannelIndex); - if (lightmapUVs.values) - { - mesh.LightmapUVs.Resize(vertexCount, false); - for (int i = 0; i < vertexCount; i++) - mesh.LightmapUVs.Get()[i] = ToFloat2(lightmapUVs.get(triangulatedIndices[i])); - if (data.ConvertRH) - { - for (int32 v = 0; v < vertexCount; v++) - mesh.LightmapUVs[v].Y = 1.0f - mesh.LightmapUVs[v].Y; - } - } - else - { - LOG(Warning, "Cannot import model lightmap uvs. Missing texcoords channel {0}.", inputChannelIndex); - } - } + mesh.SetLightmapUVsSource(data.Options.LightmapUVsSource); // Vertex Colors if (data.Options.ImportVertexColors && colors.values) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 9a1721569..874017833 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -2027,11 +2027,14 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option meshopt_remapVertexBuffer(dstMesh->name.Get(), srcMesh->name.Get(), srcMeshVertexCount, sizeof(type), remap.Get()); \ } REMAP_VERTEX_BUFFER(Positions, Float3); - REMAP_VERTEX_BUFFER(UVs, Float2); + dstMesh->UVs.Resize(srcMesh->UVs.Count()); + for (int32 channelIdx = 0; channelIdx < srcMesh->UVs.Count(); channelIdx++) + { + REMAP_VERTEX_BUFFER(UVs[channelIdx], Float2); + } REMAP_VERTEX_BUFFER(Normals, Float3); REMAP_VERTEX_BUFFER(Tangents, Float3); REMAP_VERTEX_BUFFER(Tangents, Float3); - REMAP_VERTEX_BUFFER(LightmapUVs, Float2); REMAP_VERTEX_BUFFER(Colors, Color); REMAP_VERTEX_BUFFER(BlendIndices, Int4); REMAP_VERTEX_BUFFER(BlendWeights, Float4); diff --git a/Source/Shaders/BakeLightmap.shader b/Source/Shaders/BakeLightmap.shader index b70ced7af..292b8db44 100644 --- a/Source/Shaders/BakeLightmap.shader +++ b/Source/Shaders/BakeLightmap.shader @@ -126,8 +126,7 @@ RenderCacheVSOutput VS_RenderCacheTerrain(TerrainVertexInput input) output.WorldNormal = tangentToWorld[2]; // Transform lightmap UV to clip-space - float2 texCoord = input.TexCoord; - float2 lightmapUV = texCoord * LightmapArea.zw + LightmapArea.xy; + float2 lightmapUV = input.TexCoord * LightmapArea.zw + LightmapArea.xy; lightmapUV.y = 1.0 - lightmapUV.y; lightmapUV.xy = lightmapUV.xy * 2.0 - 1.0; output.Position = float4(lightmapUV, 0, 1); diff --git a/Source/Shaders/Editor/LightmapUVsDensity.shader b/Source/Shaders/Editor/LightmapUVsDensity.shader index 588332a00..b9763846e 100644 --- a/Source/Shaders/Editor/LightmapUVsDensity.shader +++ b/Source/Shaders/Editor/LightmapUVsDensity.shader @@ -1,6 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #define MATERIAL 1 +#define MATERIAL_TEXCOORDS 4 #include "./Flax/Common.hlsl" #include "./Flax/MaterialCommon.hlsl" @@ -19,20 +20,18 @@ Texture2D GridTexture : register(t0); struct VertexOutput { - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; - float3 WorldNormal : TEXCOORD3; + float4 Position : SV_Position; + float3 WorldPosition : TEXCOORD0; + float2 LightmapUV : TEXCOORD1; + float3 WorldNormal : TEXCOORD2; }; struct PixelInput { - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; - float3 WorldNormal : TEXCOORD3; + float4 Position : SV_Position; + float3 WorldPosition : TEXCOORD0; + float2 LightmapUV : TEXCOORD1; + float3 WorldNormal : TEXCOORD2; }; float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld) @@ -67,7 +66,6 @@ VertexOutput VS(ModelInput input) VertexOutput output; output.WorldPosition = mul(float4(input.Position.xyz, 1), WorldMatrix).xyz; output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); - output.TexCoord = input.TexCoord; output.LightmapUV = input.LightmapUV * LightmapArea.zw + LightmapArea.xy; output.WorldNormal = tangentToWorld[2]; return output; diff --git a/Source/Shaders/MaterialCommon.hlsl b/Source/Shaders/MaterialCommon.hlsl index 6b20e2d71..c7a46c171 100644 --- a/Source/Shaders/MaterialCommon.hlsl +++ b/Source/Shaders/MaterialCommon.hlsl @@ -31,6 +31,9 @@ #ifndef MATERIAL_SHADING_MODEL #define MATERIAL_SHADING_MODEL SHADING_MODEL_LIT #endif +#ifndef MATERIAL_TEXCOORDS +#define MATERIAL_TEXCOORDS 1 +#endif #ifndef USE_INSTANCING #define USE_INSTANCING 0 #endif @@ -186,10 +189,21 @@ cbuffer DrawData : register(b2) struct ModelInput { float3 Position : POSITION; - float2 TexCoord : TEXCOORD0; +#if MATERIAL_TEXCOORDS > 0 + float2 TexCoord0 : TEXCOORD0; +#endif +#if MATERIAL_TEXCOORDS > 1 + float2 TexCoord1 : TEXCOORD1; +#endif +#if MATERIAL_TEXCOORDS > 2 + float2 TexCoord2 : TEXCOORD2; +#endif +#if MATERIAL_TEXCOORDS > 3 + float2 TexCoord3 : TEXCOORD3; +#endif + float2 LightmapUV : LIGHTMAP; float4 Normal : NORMAL; float4 Tangent : TANGENT; - float2 LightmapUV : TEXCOORD1; #if USE_VERTEX_COLOR half4 Color : COLOR; #endif @@ -209,7 +223,18 @@ struct ModelInput_PosOnly struct ModelInput_Skinned { float3 Position : POSITION; - float2 TexCoord : TEXCOORD0; +#if MATERIAL_TEXCOORDS > 0 + float2 TexCoord0 : TEXCOORD0; +#endif +#if MATERIAL_TEXCOORDS > 1 + float2 TexCoord1 : TEXCOORD1; +#endif +#if MATERIAL_TEXCOORDS > 2 + float2 TexCoord2 : TEXCOORD2; +#endif +#if MATERIAL_TEXCOORDS > 3 + float2 TexCoord3 : TEXCOORD3; +#endif float4 Normal : NORMAL; float4 Tangent : TANGENT; uint4 BlendIndices : BLENDINDICES;