diff --git a/glTF-BinExporter/Constants.cs b/glTF-BinExporter/Constants.cs index 02e8fbe..347839d 100644 --- a/glTF-BinExporter/Constants.cs +++ b/glTF-BinExporter/Constants.cs @@ -27,6 +27,7 @@ public static class Constants public const string PositionAttributeTag = "POSITION"; public const string NormalAttributeTag = "NORMAL"; public const string TexCoord0AttributeTag = "TEXCOORD_0"; + public const string TexCoord1AttributeTag = "TEXCOORD_1"; public const string VertexColorAttributeTag = "COLOR_0"; public const string DracoMeshCompressionExtensionTag = "KHR_draco_mesh_compression"; diff --git a/glTF-BinExporter/GlTFExporterCommand.cs b/glTF-BinExporter/GlTFExporterCommand.cs index 650ca0b..b8c854c 100644 --- a/glTF-BinExporter/GlTFExporterCommand.cs +++ b/glTF-BinExporter/GlTFExporterCommand.cs @@ -47,7 +47,7 @@ protected override Result RunCommand(Rhino.RhinoDoc doc, RunMode mode) Rhino.DocObjects.RhinoObject[] rhinoObjects = go.Objects().Select(o => o.Object()).ToArray(); - if(!DoExport(dialog.FileName, opts, binary, rhinoObjects, doc.RenderSettings.LinearWorkflow)) + if(!DoExport(dialog.FileName, opts, binary, doc, rhinoObjects, doc.RenderSettings.LinearWorkflow)) { return Result.Failure; } @@ -132,9 +132,9 @@ private SaveFileDialog GetSaveFileDialog() }; } - public static bool DoExport(string fileName, glTFExportOptions options, bool binary, IEnumerable rhinoObjects, Rhino.Render.LinearWorkflow workflow) + public static bool DoExport(string fileName, glTFExportOptions options, bool binary, RhinoDoc doc, IEnumerable rhinoObjects, Rhino.Render.LinearWorkflow workflow) { - RhinoDocGltfConverter converter = new RhinoDocGltfConverter(options, binary, rhinoObjects, workflow); + RhinoDocGltfConverter converter = new RhinoDocGltfConverter(options, binary, doc, rhinoObjects, workflow); glTFLoader.Schema.Gltf gltf = converter.ConvertToGltf(); if (binary) diff --git a/glTF-BinExporter/RhinoDocGltfConverter.cs b/glTF-BinExporter/RhinoDocGltfConverter.cs index 0ca9ab1..81e15e0 100644 --- a/glTF-BinExporter/RhinoDocGltfConverter.cs +++ b/glTF-BinExporter/RhinoDocGltfConverter.cs @@ -1,13 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Rhino.DocObjects; +using glTFLoader.Schema; using Rhino; -using glTFLoader.Schema; -using Rhino.Render; using Rhino.Display; +using Rhino.DocObjects; +using Rhino.Geometry; +using Rhino.Render; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; namespace glTF_BinExporter { @@ -15,13 +15,16 @@ public struct ObjectExportData { public Rhino.Geometry.Mesh[] Meshes; public RenderMaterial[] RenderMaterials; + public TextureMapping[] TextureMappings; + public Transform ObjectTransform; public RhinoObject Object; } class RhinoDocGltfConverter { - public RhinoDocGltfConverter(glTFExportOptions options, bool binary, IEnumerable objects, LinearWorkflow workflow) + public RhinoDocGltfConverter(glTFExportOptions options, bool binary, RhinoDoc doc, IEnumerable objects, LinearWorkflow workflow) { + this.doc = doc; this.options = options; this.binary = binary; this.objects = objects; @@ -30,12 +33,15 @@ public RhinoDocGltfConverter(glTFExportOptions options, bool binary, IEnumerable public RhinoDocGltfConverter(glTFExportOptions options, bool binary, RhinoDoc doc, LinearWorkflow workflow) { + this.doc = doc; this.options = options; this.binary = binary; this.objects = doc.Objects; this.workflow = null; } + private RhinoDoc doc = null; + private IEnumerable objects = null; private bool binary = false; @@ -50,6 +56,12 @@ public RhinoDocGltfConverter(glTFExportOptions options, bool binary, RhinoDoc do private Dictionary layers = new Dictionary(); + private Dictionary layerMaterialIndices = new Dictionary(); + + private Dictionary colorMaterials = new Dictionary(); + + private int? defaultMaterialIndex = null; + public Gltf ConvertToGltf() { dummy.Scene = 0; @@ -68,7 +80,7 @@ public Gltf ConvertToGltf() WrapT = Sampler.WrapTEnum.REPEAT, }); - if(options.UseDracoCompression) + if (options.UseDracoCompression) { dummy.ExtensionsUsed.Add(Constants.DracoMeshCompressionExtensionTag); dummy.ExtensionsRequired.Add(Constants.DracoMeshCompressionExtensionTag); @@ -79,9 +91,14 @@ public Gltf ConvertToGltf() var sanitized = SanitizeRhinoObjects(objects); - foreach(ObjectExportData exportData in sanitized) + foreach (ObjectExportData exportData in sanitized) { - int[] materialIndices = GetMaterials(exportData.RenderMaterials, exportData.Object); + int[] materialIndices = null; + + if (options.ExportMaterials) + { + materialIndices = GetMaterials(exportData.RenderMaterials, exportData.Object); + } RhinoMeshGltfConverter meshConverter = new RhinoMeshGltfConverter(exportData, materialIndices, options, binary, dummy, binaryBuffer); int meshIndex = meshConverter.AddMesh(); @@ -94,9 +111,9 @@ public Gltf ConvertToGltf() int nodeIndex = dummy.Nodes.AddAndReturnIndex(node); - if(options.ExportLayers) + if (options.ExportLayers) { - AddToLayer(RhinoDoc.ActiveDoc.Layers[exportData.Object.Attributes.LayerIndex], nodeIndex); + AddToLayer(doc.Layers[exportData.Object.Attributes.LayerIndex], nodeIndex); } else { @@ -104,7 +121,7 @@ public Gltf ConvertToGltf() } } - if(binary && binaryBuffer.Count > 0) + if (binary && binaryBuffer.Count > 0) { //have to add the empty buffer for the binary file header dummy.Buffers.Add(new glTFLoader.Schema.Buffer() @@ -119,7 +136,7 @@ public Gltf ConvertToGltf() private void AddToLayer(Layer layer, int child) { - if(layers.TryGetValue(layer.Index, out Node node)) + if (layers.TryGetValue(layer.Index, out Node node)) { if (node.Children == null) { @@ -140,7 +157,7 @@ private void AddToLayer(Layer layer, int child) layers.Add(layer.Index, node); int nodeIndex = dummy.Nodes.AddAndReturnIndex(node); - Layer parentLayer = RhinoDoc.ActiveDoc.Layers.FindId(layer.ParentLayerId); + Layer parentLayer = doc.Layers.FindId(layer.ParentLayerId); if (parentLayer == null) { @@ -155,7 +172,7 @@ private void AddToLayer(Layer layer, int child) public string GetObjectName(RhinoObject rhinoObject) { - return string.IsNullOrEmpty(rhinoObject.Name) ? null : rhinoObject.Name; + return string.IsNullOrEmpty(rhinoObject.Name) ? rhinoObject.Id.ToString() : rhinoObject.Name; } public byte[] GetBinaryBuffer() @@ -165,41 +182,102 @@ public byte[] GetBinaryBuffer() int[] GetMaterials(RenderMaterial[] materials, RhinoObject rhinoObject) { + RhinoObject[] subObjects = rhinoObject.GetSubObjects(); + + if (subObjects.Length == 0) + { + subObjects = new RhinoObject[1] { rhinoObject }; + } + int[] materialIndices = new int[materials.Length]; for (int i = 0; i < materials.Length; i++) { var material = materials[i]; - if (!options.ExportMaterials) + if (material == null) { - return null; - } - - if (material == null && options.UseDisplayColorForUnsetMaterials) - { - Color4f objectColor = GetObjectColor(rhinoObject); - materialIndices[i] = CreateSolidColorMaterial(objectColor, GetObjectName(rhinoObject)); - continue; + if (options.UseDisplayColorForUnsetMaterials) + { + if (subObjects[i].Attributes.ColorSource == ObjectColorSource.ColorFromLayer) + { + materialIndices[i] = GetLayerMaterial(rhinoObject); + } + else + { + materialIndices[i] = GetColorMaterial(subObjects[i]); + } + } + else + { + materialIndices[i] = GetDefaultMaterial(); + } } - else if (material == null) + else { - material = Rhino.DocObjects.Material.DefaultMaterial.RenderMaterial; + Guid materialId = material.Id; + if (!materialsMap.TryGetValue(materialId, out int materialIndex)) + { + RhinoMaterialGltfConverter materialConverter = new RhinoMaterialGltfConverter(options, binary, dummy, binaryBuffer, material, workflow); + materialIndex = materialConverter.AddMaterial(); + materialsMap.Add(materialId, materialIndex); + } + materialIndices[i] = materialIndex; } + } - Guid materialId = material.Id; + return materialIndices; + } - if (!materialsMap.TryGetValue(materialId, out int materialIndex)) - { - RhinoMaterialGltfConverter materialConverter = new RhinoMaterialGltfConverter(options, binary, dummy, binaryBuffer, material, workflow); - materialIndex = materialConverter.AddMaterial(); - materialsMap.Add(materialId, materialIndex); - } + private int GetColorMaterial(RhinoObject rhinoObject) + { + Color objectColor = GetObjectColor(rhinoObject); + + int colorIndex = objectColor.ToArgb(); - materialIndices[i] = materialIndex; + if (!colorMaterials.TryGetValue(colorIndex, out int colorMaterialIndex)) + { + colorMaterialIndex = CreateSolidColorMaterial(new Color4f(objectColor), GetColorName(objectColor)); + colorMaterials.Add(colorIndex, colorMaterialIndex); } - return materialIndices; + return colorMaterialIndex; + } + + private string GetColorName(Color color) + { + if (color.IsNamedColor) + { + return color.Name; + } + else + { + return color.ToString(); + } + } + + private int GetDefaultMaterial() + { + if (defaultMaterialIndex == null) + { + RenderMaterial material = Rhino.DocObjects.Material.DefaultMaterial.RenderMaterial; + material.Name = "DefaultMaterial"; + RhinoMaterialGltfConverter materialConverter = new RhinoMaterialGltfConverter(options, binary, dummy, binaryBuffer, material, workflow); + defaultMaterialIndex = materialConverter.AddMaterial(); + } + + return defaultMaterialIndex.Value; + } + + private int GetLayerMaterial(RhinoObject rhinoObject) + { + if (!layerMaterialIndices.TryGetValue(rhinoObject.Attributes.LayerIndex, out int layerMaterialIndex)) + { + Color4f objectColor = new Color4f(GetLayerColor(rhinoObject)); + layerMaterialIndex = CreateSolidColorMaterial(objectColor, doc.Layers[rhinoObject.Attributes.LayerIndex].Name); + layerMaterialIndices.Add(rhinoObject.Attributes.LayerIndex, layerMaterialIndex); + } + return layerMaterialIndex; } int CreateSolidColorMaterial(Color4f color, string name) @@ -216,20 +294,24 @@ int CreateSolidColorMaterial(Color4f color, string name) return dummy.Materials.AddAndReturnIndex(material); } - Color4f GetObjectColor(RhinoObject rhinoObject) + Color GetObjectColor(RhinoObject rhinoObject) { - if(rhinoObject.Attributes.ColorSource == ObjectColorSource.ColorFromLayer) + if (rhinoObject.Attributes.ColorSource == ObjectColorSource.ColorFromLayer) { - int layerIndex = rhinoObject.Attributes.LayerIndex; - - return new Color4f(rhinoObject.Document.Layers[layerIndex].Color); + return GetLayerColor(rhinoObject); } else { - return new Color4f(rhinoObject.Attributes.ObjectColor); + return rhinoObject.Attributes.ObjectColor; } } + Color GetLayerColor(RhinoObject rhinoObject) + { + int layerIndex = rhinoObject.Attributes.LayerIndex; + return doc.Layers[layerIndex].Color; + } + public Rhino.Geometry.Mesh[] GetMeshes(RhinoObject rhinoObject) { @@ -239,7 +321,7 @@ public Rhino.Geometry.Mesh[] GetMeshes(RhinoObject rhinoObject) return new Rhino.Geometry.Mesh[] { meshObj.MeshGeometry }; } - else if(rhinoObject.ObjectType == ObjectType.SubD) + else if (rhinoObject.ObjectType == ObjectType.SubD) { SubDObject subdObject = rhinoObject as SubDObject; @@ -263,10 +345,46 @@ public Rhino.Geometry.Mesh[] GetMeshes(RhinoObject rhinoObject) // Need to get a Mesh from the None-mesh object. Using the FastRenderMesh here. Could be made configurable. // First make sure the internal rhino mesh has been created - //rhinoObject.CreateMeshes(Rhino.Geometry.MeshType.Preview, Rhino.Geometry.MeshingParameters.FastRenderMesh, true); + rhinoObject.CreateMeshes(MeshType.Preview, MeshingParameters.FastRenderMesh, true); // Then get the internal rhino meshes - Rhino.Geometry.Mesh[] meshes = rhinoObject.GetMeshes(Rhino.Geometry.MeshType.Preview); + Rhino.Geometry.Mesh[] meshes;// = rhinoObject.GetMeshes(Rhino.Geometry.MeshType.Preview); + int count = 0; + + var subObjects = rhinoObject.GetSubObjects(); + if (subObjects.Length == 0) + { + meshes = rhinoObject.GetMeshes(MeshType.Preview); + } + else + { + meshes = new Rhino.Geometry.Mesh[subObjects.Length]; + var jointMeshes = rhinoObject.GetMeshes(MeshType.Preview); + jointMeshes = jointMeshes[0].ExplodeAtUnweldedEdges(); + + for (int i = 0; i < subObjects.Length; i++) + { + var subMeshes = subObjects[i].GetMeshes(MeshType.Preview); + + if (subMeshes.Length == 0) + { + meshes[i] = jointMeshes[i]; + //subObjects[i].CreateMeshes(MeshType.Preview, MeshingParameters.FastRenderMesh, true); + //subMeshes = subObjects[i].GetMeshes(MeshType.Preview); + count++; + RhinoApp.WriteLine("Subobject didn't have a mesh! RhinoDocGltfConverter.cs line:370"); + } + + if (subMeshes.Length == 1) + { + meshes[i] = subMeshes[0]; + } + else if (subMeshes.Length > 1) + { + RhinoApp.WriteLine("This shouldn't have happened: An sub object created more than one mesh! RhinoDocGltfConverter.cs line:379"); + } + } + } List validMeshes = new List(); @@ -303,7 +421,7 @@ public bool MeshIsValidForExport(Rhino.Geometry.Mesh mesh) return false; } - if(!options.ExportOpenMeshes && !mesh.IsClosed) + if (!options.ExportOpenMeshes && !mesh.IsClosed) { return false; } @@ -311,16 +429,6 @@ public bool MeshIsValidForExport(Rhino.Geometry.Mesh mesh) return true; } - private string GetDebugName(RhinoObject rhinoObject) - { - if (string.IsNullOrEmpty(rhinoObject.Name)) - { - return "(Unnamed)"; - } - - return rhinoObject.Name; - } - public List SanitizeRhinoObjects(IEnumerable rhinoObjects) { var rhinoObjectsRes = new List(); @@ -329,36 +437,25 @@ public List SanitizeRhinoObjects(IEnumerable rhin { if (!rhinoObject.IsMeshable(Rhino.Geometry.MeshType.Any)) { - RhinoApp.WriteLine("Skipping " + GetDebugName(rhinoObject) + ", object is not meshable. Object is a " + rhinoObject.ObjectType.ToString()); + RhinoApp.WriteLine("Skipping " + GetObjectName(rhinoObject) + ", object is not meshable. Object is a " + rhinoObject.ObjectType.ToString()); continue; } // Need to get a Mesh from the None-mesh object. Using the FastRenderMesh here. Could be made configurable. // First make sure the internal rhino mesh has been created - rhinoObject.CreateMeshes(Rhino.Geometry.MeshType.Preview, Rhino.Geometry.MeshingParameters.FastRenderMesh, true); - - var mats = new RenderMaterial[rhinoObject.MeshCount(Rhino.Geometry.MeshType.Preview, Rhino.Geometry.MeshingParameters.FastRenderMesh)]; - for (int i = 0; i < mats.Length; i++) - { - foreach (var component in rhinoObject.SubobjectMaterialComponents) - { - if (component.Index == i) - { - mats[i] = rhinoObject.GetRenderMaterial(component); - break; - } - } - - if (mats[i] == null) - { - mats[i] = rhinoObject.RenderMaterial; - } - } + //rhinoObject.CreateMeshes(Rhino.Geometry.MeshType.Preview, Rhino.Geometry.MeshingParameters.FastRenderMesh, true); var isValidGeometry = Constants.ValidObjectTypes.Contains(rhinoObject.ObjectType); if (isValidGeometry && rhinoObject.ObjectType != ObjectType.InstanceReference) { + Transform objectTransform; + TextureMapping[] textureMappings = GetTextureMappings(rhinoObject, out objectTransform); + + + + var mats = GetRenderMaterials(rhinoObject); + var meshes = GetMeshes(rhinoObject); if (meshes.Length > 0) //Objects need a mesh to export @@ -367,6 +464,8 @@ public List SanitizeRhinoObjects(IEnumerable rhin { Meshes = meshes, RenderMaterials = mats, + TextureMappings = textureMappings, + ObjectTransform = objectTransform, Object = rhinoObject, }); } @@ -383,6 +482,18 @@ public List SanitizeRhinoObjects(IEnumerable rhin // Transform the exploded geo into its correct place foreach (var item in objects.Zip(transforms, (rObj, trans) => (rhinoObject: rObj, trans))) { + if (!doc.Layers[item.rhinoObject.Attributes.LayerIndex].IsVisible) continue; + + if (!item.rhinoObject.IsMeshable(Rhino.Geometry.MeshType.Any)) + { + RhinoApp.WriteLine("Skipping " + GetObjectName(item.rhinoObject) + ", object is not meshable. Object is a " + item.rhinoObject.ObjectType.ToString()); + continue; + } + + Transform objectTransform; + var textureMappings = GetTextureMappings(item.rhinoObject, out objectTransform); + + var mats = GetRenderMaterials(item.rhinoObject); var meshes = GetMeshes(item.rhinoObject); foreach (var mesh in meshes) @@ -396,6 +507,8 @@ public List SanitizeRhinoObjects(IEnumerable rhin { Meshes = meshes, RenderMaterials = mats, + TextureMappings = textureMappings, + ObjectTransform = objectTransform, Object = item.rhinoObject, }); } @@ -410,6 +523,84 @@ public List SanitizeRhinoObjects(IEnumerable rhin return rhinoObjectsRes; } + private RenderMaterial[] GetRenderMaterials(RhinoObject rhinoObject) + { + var subObjects = rhinoObject.GetSubObjects(); + + if (subObjects.Length == 0) + { + subObjects = new RhinoObject[1] { rhinoObject }; + } + + var mats = new RenderMaterial[subObjects.Length]; + + var components = rhinoObject.SubobjectMaterialComponents; + + for (int i = 0; i < subObjects.Length; i++) + { + mats[i] = rhinoObject.RenderMaterial; + + foreach (var component in components) + { + if (component.Index == i) + { + mats[i] = rhinoObject.GetRenderMaterial(component); + } + } + + if (mats[i] == null) + { + mats[i] = doc.Layers[rhinoObject.Attributes.LayerIndex].RenderMaterial; + } + } + + return mats; + } + + private TextureMapping[] GetTextureMappings(RhinoObject rhinoObject, out Transform objectTransform) + { + objectTransform = Transform.Unset; + + var textureMappingIndeces = rhinoObject.GetTextureChannels(); + Array.Sort(textureMappingIndeces); + if (textureMappingIndeces.Length == 0) + { + return new TextureMapping[0]; + } + + TextureMapping[] textureMappings; + if (options.ExportAllTextureCoordinates) + { + textureMappings = new TextureMapping[textureMappingIndeces.Max() + 1]; + int j = 0; + for (int i = 0; i < textureMappings.Length; i++) + { + if (textureMappingIndeces[j] == i) + { + textureMappings[i] = rhinoObject.GetTextureMapping(textureMappingIndeces[j], out objectTransform); + j++; + } + } + } + else + { + textureMappings = new TextureMapping[2]; + + if (textureMappingIndeces.Contains(options.UV0)) + { + textureMappings[0] = rhinoObject.GetTextureMapping(options.UV0, out objectTransform); + } + if (options.UV0 != options.UV1) + { + if (textureMappingIndeces.Contains(options.UV1)) + { + textureMappings[1] = rhinoObject.GetTextureMapping(options.UV1, out objectTransform); + } + } + } + return textureMappings; + } + private void ExplodeRecursive(InstanceObject instanceObject, Rhino.Geometry.Transform instanceTransform, List pieces, List transforms) { for (int i = 0; i < instanceObject.InstanceDefinition.ObjectCount; i++) diff --git a/glTF-BinExporter/RhinoMaterialGltfConverter.cs b/glTF-BinExporter/RhinoMaterialGltfConverter.cs index d236de3..7ec2d24 100644 --- a/glTF-BinExporter/RhinoMaterialGltfConverter.cs +++ b/glTF-BinExporter/RhinoMaterialGltfConverter.cs @@ -83,12 +83,12 @@ public int AddMaterial() if (occlusionTexture != null && occlusionTexture.Enabled) { - material.OcclusionTexture = AddTextureOcclusion(occlusionTexture.FileReference.FullPath); + material.OcclusionTexture = AddTextureOcclusion(occlusionTexture.FileReference.FullPath, occlusionTexture.MappingChannelId); } if (emissiveTexture != null && emissiveTexture.Enabled) { - material.EmissiveTexture = AddTexture(emissiveTexture.FileReference.FullPath); + material.EmissiveTexture = AddTexture(emissiveTexture.FileReference.FullPath, emissiveTexture.MappingChannelId); float emissionMultiplier = 1.0f; @@ -142,7 +142,7 @@ public int AddMaterial() if(clearcoatTexture != null && clearcoatTexture.Enabled) { - clearcoat.ClearcoatTexture = AddTexture(clearcoatTexture.FileReference.FullPath); + clearcoat.ClearcoatTexture = AddTexture(clearcoatTexture.FileReference.FullPath, clearcoatTexture.MappingChannelId); clearcoat.ClearcoatFactor = 1.0f; } else @@ -152,7 +152,7 @@ public int AddMaterial() if(clearcoatRoughessTexture != null && clearcoatRoughessTexture.Enabled) { - clearcoat.ClearcoatRoughnessTexture = AddTexture(clearcoatRoughessTexture.FileReference.FullPath); + clearcoat.ClearcoatRoughnessTexture = AddTexture(clearcoatRoughessTexture.FileReference.FullPath, clearcoatRoughessTexture.MappingChannelId); clearcoat.ClearcoatRoughnessFactor = 1.0f; } else @@ -210,7 +210,7 @@ glTFLoader.Schema.TextureInfo CreateOpacityTexture(Rhino.DocObjects.Texture text glTFLoader.Schema.TextureInfo textureInfo = new glTFLoader.Schema.TextureInfo() { Index = textureIndex, - TexCoord = 0, + TexCoord = GetMappingChannel(texture.MappingChannelId), }; return textureInfo; @@ -385,7 +385,7 @@ glTFLoader.Schema.TextureInfo CombineBaseColorAndAlphaTexture(RenderTexture base //Testing //bitmap.Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "out.png")); - return GetTextureInfoFromBitmap(bitmap); + return GetTextureInfoFromBitmap(bitmap, baseColorTexture == null?(alphaTexture == null? 0:alphaTexture.GetMappingChannel()):baseColorTexture.GetMappingChannel()); } private int AddTextureToBuffers(string texturePath) @@ -470,11 +470,27 @@ private byte[] GetImageBytesFromFile(string fileName) return GetImageBytes(bmp); } - private glTFLoader.Schema.TextureInfo AddTexture(string texturePath) + private glTFLoader.Schema.TextureInfo AddTexture(string texturePath, int mappingChannel) { int textureIdx = AddTextureToBuffers(texturePath); - return new glTFLoader.Schema.TextureInfo() { Index = textureIdx, TexCoord = 0 }; + return new glTFLoader.Schema.TextureInfo() { Index = textureIdx, TexCoord = GetMappingChannel(mappingChannel) }; + } + + private int GetMappingChannel(int mappingChannel) + { + if (options.ExportAllTextureCoordinates) return mappingChannel; + else + { + if (mappingChannel == options.UV0) return 0; + else if (mappingChannel == options.UV1) return 1; + else + { + if (options.UV0 == 0) return 0; + else if (options.UV1 == 0) return 0; + else return 0; + } + } } private glTFLoader.Schema.MaterialNormalTextureInfo AddTextureNormal(Rhino.DocObjects.Texture normalTexture) @@ -486,7 +502,7 @@ private glTFLoader.Schema.MaterialNormalTextureInfo AddTextureNormal(Rhino.DocOb return new glTFLoader.Schema.MaterialNormalTextureInfo() { Index = textureIdx, - TexCoord = 0, + TexCoord = GetMappingChannel(normalTexture.MappingChannelId), Scale = (float)constant, }; } @@ -503,14 +519,14 @@ private int AddNormalTexture(Rhino.DocObjects.Texture normalTexture) return GetTextureFromBitmap(bmp); } - private glTFLoader.Schema.MaterialOcclusionTextureInfo AddTextureOcclusion(string texturePath) + private glTFLoader.Schema.MaterialOcclusionTextureInfo AddTextureOcclusion(string texturePath, int mappingChannel) { int textureIdx = AddTextureToBuffers(texturePath); return new glTFLoader.Schema.MaterialOcclusionTextureInfo() { Index = textureIdx, - TexCoord = 0, + TexCoord = GetMappingChannel(mappingChannel), Strength = 0.9f }; } @@ -604,7 +620,7 @@ public glTFLoader.Schema.TextureInfo AddMetallicRoughnessTexture(Rhino.DocObject } } - return GetTextureInfoFromBitmap(bitmap); + return GetTextureInfoFromBitmap(bitmap, renderTextureMetal == null? (renderTextureRoughness == null? 0 : renderTextureRoughness.GetMappingChannel()) : renderTextureMetal.GetMappingChannel()); } private int GetTextureFromBitmap(Bitmap bitmap) @@ -622,14 +638,14 @@ private int GetTextureFromBitmap(Bitmap bitmap) return dummy.Textures.AddAndReturnIndex(texture); } - private glTFLoader.Schema.TextureInfo GetTextureInfoFromBitmap(Bitmap bitmap) + private glTFLoader.Schema.TextureInfo GetTextureInfoFromBitmap(Bitmap bitmap, int mappingChannel) { int textureIdx = GetTextureFromBitmap(bitmap); return new glTFLoader.Schema.TextureInfo() { Index = textureIdx, - TexCoord = 0 + TexCoord = GetMappingChannel(mappingChannel) }; } diff --git a/glTF-BinExporter/RhinoMeshGltfConverter.cs b/glTF-BinExporter/RhinoMeshGltfConverter.cs index da90aff..d2db450 100644 --- a/glTF-BinExporter/RhinoMeshGltfConverter.cs +++ b/glTF-BinExporter/RhinoMeshGltfConverter.cs @@ -89,6 +89,7 @@ private void PreprocessMesh(Mesh rhinoMesh) } var rhinoMesh = exportData.Meshes[i]; + var textureMappings = exportData.TextureMappings; PreprocessMesh(rhinoMesh); @@ -104,6 +105,15 @@ private void PreprocessMesh(Mesh rhinoMesh) bool exportTextureCoordinates = ExportTextureCoordinates(rhinoMesh); bool exportVertexColors = ExportVertexColors(rhinoMesh); + if(exportTextureCoordinates) + { + foreach (var textureMapping in textureMappings) + { + if (textureMapping == null) continue; + rhinoMesh.SetCachedTextureCoordinates(textureMapping, ref exportData.ObjectTransform); + } + } + glTFLoader.Schema.MeshPrimitive primitive = new glTFLoader.Schema.MeshPrimitive() { Attributes = new Dictionary(), @@ -126,9 +136,57 @@ private void PreprocessMesh(Mesh rhinoMesh) if (exportTextureCoordinates) { - int textureCoordinatesAccessorIdx = GetTextureCoordinatesAccessor(rhinoMesh.TextureCoordinates); - - primitive.Attributes.Add(Constants.TexCoord0AttributeTag, textureCoordinatesAccessorIdx); + if (options.ExportAllTextureCoordinates) + { + int textureCoordinatesAccessorIdx = GetTextureCoordinatesAccessor(rhinoMesh.TextureCoordinates); + primitive.Attributes.Add(Constants.TexCoord0AttributeTag, textureCoordinatesAccessorIdx); + + for (int j = 1; j < textureMappings.Length; j++) + { + if (textureMappings[j] == null) primitive.Attributes.Add("TEXCOORD_" + j, textureCoordinatesAccessorIdx); + else + { + rhinoMesh.SetTextureCoordinates(textureMappings[j], exportData.ObjectTransform, false); + int newTextureCoordinatesAccessorIdx = GetTextureCoordinatesAccessor(rhinoMesh.TextureCoordinates); + primitive.Attributes.Add("TEXCOORD_" + j, newTextureCoordinatesAccessorIdx); + } + } + } + else + { + var defaultTextureCoordinates = rhinoMesh.TextureCoordinates; + int textureCoordinatesAccessorIdx = -1; + + if (textureMappings.Length == 0 || textureMappings[0] == null) + { + textureCoordinatesAccessorIdx = GetTextureCoordinatesAccessor(defaultTextureCoordinates); + primitive.Attributes.Add(Constants.TexCoord0AttributeTag, textureCoordinatesAccessorIdx); + } + else + { + rhinoMesh.SetTextureCoordinates(textureMappings[0], exportData.ObjectTransform, false); + int newTextureCoordinatesAccessorIdx = GetTextureCoordinatesAccessor(rhinoMesh.TextureCoordinates); + primitive.Attributes.Add(Constants.TexCoord0AttributeTag, newTextureCoordinatesAccessorIdx); + } + if ((textureMappings.Length < 2 || textureMappings[1] == null) && options.UV1 == 0 && options.UV0 != options.UV1) + { + if (textureCoordinatesAccessorIdx == -1) + { + textureCoordinatesAccessorIdx = GetTextureCoordinatesAccessor(defaultTextureCoordinates); + primitive.Attributes.Add(Constants.TexCoord1AttributeTag, textureCoordinatesAccessorIdx); + } + else + { + primitive.Attributes.Add(Constants.TexCoord1AttributeTag, textureCoordinatesAccessorIdx); + } + } + else if(textureMappings.Length > 1 && textureMappings[1] !=null && options.UV0 != options.UV1) + { + rhinoMesh.SetTextureCoordinates(textureMappings[1], exportData.ObjectTransform, false); + int newTextureCoordinatesAccessorIdx = GetTextureCoordinatesAccessor(rhinoMesh.TextureCoordinates); + primitive.Attributes.Add(Constants.TexCoord1AttributeTag, newTextureCoordinatesAccessorIdx); + } + } } if (exportVertexColors) @@ -154,6 +212,11 @@ private void PreprocessMesh(Mesh rhinoMesh) if(exportTextureCoordinates) { dracoCompressionObject.Attributes.Add(Constants.TexCoord0AttributeTag, currentGeometryInfo.TextureCoordinatesAttributePosition); + + for (int j = 1; j < textureMappings.Length; j++) + { + dracoCompressionObject.Attributes.Add("TEXCOORD_" + j, currentGeometryInfo.TextureCoordinatesAttributePosition); + } } if(exportVertexColors) diff --git a/glTF-BinExporter/glTFBinExporterPlugin.cs b/glTF-BinExporter/glTFBinExporterPlugin.cs index 20995fd..428ed7c 100644 --- a/glTF-BinExporter/glTFBinExporterPlugin.cs +++ b/glTF-BinExporter/glTFBinExporterPlugin.cs @@ -54,7 +54,7 @@ protected override WriteFileResult WriteFile(string filename, int index, RhinoDo IEnumerable objects = GetObjectsToExport(doc, options); - if(!GlTFExporterCommand.DoExport(filename, exportOptions, binary, objects, doc.RenderSettings.LinearWorkflow)) + if(!GlTFExporterCommand.DoExport(filename, exportOptions, binary, doc, objects, doc.RenderSettings.LinearWorkflow)) { return WriteFileResult.Failure; } @@ -147,6 +147,33 @@ public static bool ExportTextureCoordinates set => Instance.Settings.SetBool(ExportTextureCoordinatesKey, value); } + private const string UV0Key = "UV0"; + public const int UV0Default = 0; + + public static int UV0 + { + get => Instance.Settings.GetInteger(UV0Key, UV0Default); + set => Instance.Settings.SetInteger(UV0Key, value); + } + + private const string UV1Key = "UV1"; + public const int UV1Default = 0; + + public static int UV1 + { + get => Instance.Settings.GetInteger(UV1Key, UV1Default); + set => Instance.Settings.SetInteger(UV1Key, value); + } + + public const string ExportAllTextureCoordinatesKey = "ExportAllTextureCoordinates"; + public const bool ExportAllTextureCoordinatesDefault = false; + + public static bool ExportAllTextureCoordinates + { + get => Instance.Settings.GetBool(ExportAllTextureCoordinatesKey, ExportAllTextureCoordinatesDefault); + set => Instance.Settings.SetBool(ExportAllTextureCoordinatesKey, value); + } + public const string ExportVertexNormalsKey = "ExportVertexNormals"; public const bool ExportVertexNormalsDefault = true; @@ -240,6 +267,10 @@ public static glTFExportOptions GetSavedOptions() SubDLevel = SubDLevel, ExportTextureCoordinates = ExportTextureCoordinates, + UV0 = UV0, + UV1 = UV1, + ExportAllTextureCoordinates = ExportAllTextureCoordinates, + ExportVertexNormals = ExportVertexNormals, ExportOpenMeshes = ExportOpenMeshes, ExportVertexColors = ExportVertexColors, diff --git a/glTF-BinExporter/glTFExportOptions.cs b/glTF-BinExporter/glTFExportOptions.cs index 94b0f7c..7d4352a 100644 --- a/glTF-BinExporter/glTFExportOptions.cs +++ b/glTF-BinExporter/glTFExportOptions.cs @@ -17,6 +17,10 @@ public class glTFExportOptions public int SubDLevel = glTFBinExporterPlugin.SubDLevelDefault; public bool ExportTextureCoordinates = glTFBinExporterPlugin.ExportTextureCoordinatesDefault; + public int UV0 = glTFBinExporterPlugin.UV0Default; + public int UV1 = glTFBinExporterPlugin.UV1Default; + public bool ExportAllTextureCoordinates = glTFBinExporterPlugin.ExportAllTextureCoordinatesDefault; + public bool ExportVertexNormals = glTFBinExporterPlugin.ExportVertexNormalsDefault; public bool ExportOpenMeshes = glTFBinExporterPlugin.ExportOpenMeshesDefault; public bool ExportVertexColors = glTFBinExporterPlugin.ExportVertexColorsDefault; diff --git a/glTF-BinExporter/glTFExportOptionsDialog.cs b/glTF-BinExporter/glTFExportOptionsDialog.cs index ed62139..03b7091 100644 --- a/glTF-BinExporter/glTFExportOptionsDialog.cs +++ b/glTF-BinExporter/glTFExportOptionsDialog.cs @@ -23,6 +23,14 @@ class ExportOptionsDialog : Dialog private Slider subdLevel = new Slider(); private CheckBox exportTextureCoordinates = new CheckBox(); + private GroupBox textureChannelsBox = new GroupBox(); + private Label uv0Label = new Label(); + private NumericStepper uv0 = new NumericStepper(); + private Label uv1Label = new Label(); + private NumericStepper uv1 = new NumericStepper(); + private CheckBox exportAllTextureChannels = new CheckBox(); + private Label uvWarningLabel = new Label(); + private CheckBox exportVertexNormals = new CheckBox(); private CheckBox exportOpenMeshes = new CheckBox(); private CheckBox exportVertexColors = new CheckBox(); @@ -78,8 +86,40 @@ public ExportOptionsDialog() } }; + textureChannelsBox.Text = "Texture Channels"; + exportTextureCoordinates.Text = "Export texture coordinates"; + uv0Label.Text = "UV0 Channel"; + + uv0.ToolTip = "0 = Default"; + uv0.DecimalPlaces = 0; + uv0.MinValue = 0; + + uv1Label.Text = "UV1 Channel"; + + uv1.ToolTip = "0 = Default"; + uv1.DecimalPlaces = 0; + uv1.MinValue = 0; + + exportAllTextureChannels.Text = "Export all texture channels (unsupported)"; + + textureChannelsBox.Content = new TableLayout() + { + Padding = DefaultPadding, + Spacing = DefaultSpacing, + Rows = + { + new TableRow(uv0Label, uv0), + new TableRow(uv1Label, uv1), + new TableRow(exportAllTextureChannels) + } + }; + + uvWarningLabel.Text = "Warning! These textures are getting merged:\n" + + "- Color and alpha\n" + + "- Metalness and Roughness"; + exportVertexNormals.Text = "Export vertex normals"; exportOpenMeshes.Text = "Export open meshes"; @@ -106,6 +146,9 @@ public ExportOptionsDialog() useDracoCompressionCheck.CheckedChanged += UseDracoCompressionCheck_CheckedChanged; exportMaterials.CheckedChanged += ExportMaterials_CheckedChanged; + exportTextureCoordinates.CheckedChanged += ExportTextureCoordinates_CheckedChanged; + exportAllTextureChannels.CheckedChanged += ExportAllTextureChannels_CheckedChanged; + useSubdControlNet.CheckedChanged += UseSubdControlNet_CheckedChanged; cancelButton.Click += CancelButton_Click; @@ -173,6 +216,24 @@ public ExportOptionsDialog() tabControl.Pages.Add(meshPage); + TabPage textureCoordinatesPage = new TabPage() + { + Text = "UVs", + Content = new TableLayout() + { + Padding = DefaultPadding, + Spacing = DefaultSpacing, + Rows = + { + new TableRow(exportTextureCoordinates), + new TableRow(textureChannelsBox), + new TableRow(uvWarningLabel) + }, + }, + }; + + tabControl.Pages.Add(textureCoordinatesPage); + TabPage compressionPage = new TabPage() { Text = "Compression", @@ -203,6 +264,7 @@ public ExportOptionsDialog() }; } + private void OptionsToDialog() { useSettingsDontShowDialogCheck.Checked = glTFBinExporterPlugin.UseSavedSettingsDontShowDialog; @@ -221,6 +283,12 @@ private void OptionsToDialog() subdLevel.Value = glTFBinExporterPlugin.SubDLevel; exportTextureCoordinates.Checked = glTFBinExporterPlugin.ExportTextureCoordinates; + EnableDisableTextureChannelControls(glTFBinExporterPlugin.ExportTextureCoordinates); + uv0.Value = glTFBinExporterPlugin.UV0; + uv1.Value = glTFBinExporterPlugin.UV1; + exportAllTextureChannels.Checked = glTFBinExporterPlugin.ExportAllTextureCoordinates; + EnableDisableUVControls(glTFBinExporterPlugin.ExportAllTextureCoordinates); + exportVertexNormals.Checked = glTFBinExporterPlugin.ExportVertexNormals; exportOpenMeshes.Checked = glTFBinExporterPlugin.ExportOpenMeshes; exportVertexColors.Checked = glTFBinExporterPlugin.ExportVertexColors; @@ -249,6 +317,10 @@ private void DialogToOptions() glTFBinExporterPlugin.SubDLevel = subdLevel.Value; glTFBinExporterPlugin.ExportTextureCoordinates = GetCheckboxValue(exportTextureCoordinates); + glTFBinExporterPlugin.UV0 = (int)uv0.Value; + glTFBinExporterPlugin.UV1 = (int)uv1.Value; + glTFBinExporterPlugin.ExportAllTextureCoordinates = GetCheckboxValue(exportAllTextureChannels); + glTFBinExporterPlugin.ExportVertexNormals = GetCheckboxValue(exportVertexNormals); glTFBinExporterPlugin.ExportOpenMeshes = GetCheckboxValue(exportOpenMeshes); glTFBinExporterPlugin.ExportVertexColors = GetCheckboxValue(exportVertexColors); @@ -296,6 +368,30 @@ private void EnableDisableMaterialControls(bool enabled) { useDisplayColorForUnsetMaterial.Enabled = enabled; } + private void ExportTextureCoordinates_CheckedChanged(object sender, EventArgs e) + { + bool enabled = GetCheckboxValue(exportTextureCoordinates); + + EnableDisableTextureChannelControls(enabled); + } + + private void EnableDisableTextureChannelControls(bool enabled) + { + textureChannelsBox.Enabled = enabled; + } + + private void ExportAllTextureChannels_CheckedChanged(object sender, EventArgs e) + { + bool enabled = GetCheckboxValue(exportAllTextureChannels); + + EnableDisableUVControls(enabled); + } + + private void EnableDisableUVControls(bool enabled) + { + uv0.Enabled = !enabled; + uv1.Enabled = !enabled; + } private void UseSubdControlNet_CheckedChanged(object sender, EventArgs e) {