diff --git a/Common_glTF_Exporter/Common_glTF_Exporter.projitems b/Common_glTF_Exporter/Common_glTF_Exporter.projitems index 8005c04..386632e 100644 --- a/Common_glTF_Exporter/Common_glTF_Exporter.projitems +++ b/Common_glTF_Exporter/Common_glTF_Exporter.projitems @@ -9,24 +9,9 @@ Common_glTF_Exporter - - - - - - - - - - - - - - - - + + - @@ -54,9 +39,7 @@ - - diff --git a/Common_glTF_Exporter/Core/GLTFAccessor.cs b/Common_glTF_Exporter/Core/GLTFAccessor.cs deleted file mode 100644 index ed37c9e..0000000 --- a/Common_glTF_Exporter/Core/GLTFAccessor.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using System.Collections.Generic; - using Revit_glTF_Exporter; - - /// - /// A reference to a subsection of a BufferView containing a particular data type - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessors. - /// - public class GLTFAccessor - { - public GLTFAccessor(int bufferView, int byteOffset, ComponentType componentType, int count, string type, List max, List min, string name) - { - this.bufferView = bufferView; - this.byteOffset = byteOffset; - this.componentType = componentType; - this.count = count; - this.type = type; - this.max = max; - this.min = min; - this.name = name; - } - - /// - /// Gets or sets the index of the bufferView. - /// - public int bufferView { get; set; } - - /// - /// Gets or sets the offset relative to the start of the bufferView in bytes. - /// - public int byteOffset { get; set; } - - /// - /// Gets or sets the datatype of the components in the attribute. - /// - public ComponentType componentType { get; set; } - - /// - /// Gets or sets the number of attributes referenced by this accessor. - /// - public int count { get; set; } - - /// - /// Gets or sets the specifies if the attribute is a scala, vector, or matrix. - /// - public string type { get; set; } - - /// - /// Gets or sets the maximum value of each component in this attribute. - /// - public List max { get; set; } - - /// - /// Gets or sets the minimum value of each component in this attribute. - /// - public List min { get; set; } - - /// - /// Gets or sets a user defined name for this accessor. - /// - public string name { get; set; } - } -} diff --git a/Common_glTF_Exporter/Core/GLTFAttribute.cs b/Common_glTF_Exporter/Core/GLTFAttribute.cs deleted file mode 100644 index b1c4a57..0000000 --- a/Common_glTF_Exporter/Core/GLTFAttribute.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.Text; - - /// - /// The list of accessors available to the renderer for a particular mesh - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#meshes. - /// - public class GLTFAttribute - { - /// - /// Gets or sets the index of the accessor for position data. - /// - public int POSITION { get; set; } - - /// - /// Gets or sets the index of the accessor for normal data. - /// - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int? NORMAL { get; set; } - - /// - /// Gets or sets the index of the accessor for batchId data. - /// - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int? _BATCHID { get; set; } - - /// - /// Gets or sets the index of the UV Coordinates of the material's textures. - /// - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int? TEXCOORD_0 { get; set; } - } -} diff --git a/Common_glTF_Exporter/Core/GLTFBinaryData.cs b/Common_glTF_Exporter/Core/GLTFBinaryData.cs deleted file mode 100644 index 7908002..0000000 --- a/Common_glTF_Exporter/Core/GLTFBinaryData.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using System; - using System.Collections.Generic; - using System.Text; - - /// - /// A binary data store serialized to a *.bin file - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#binary-data-storage. - /// - public class GLTFBinaryData - { - public List vertexBuffer { get; set; } = new List(); - - public List normalBuffer { get; set; } = new List(); - - public List indexBuffer { get; set; } = new List(); - - public List batchIdBuffer { get; set; } = new List(); - - public int vertexAccessorIndex { get; set; } - - public int normalsAccessorIndex { get; set; } - - public int indexAccessorIndex { get; set; } - - public int batchIdAccessorIndex { get; set; } - - public string name { get; set; } - - public List uvBuffer { get; set; } = new List(); - public int uvAccessorIndex { get; set; } = -1; - - public byte[] byteData { get; set; } = null; - } -} diff --git a/Common_glTF_Exporter/Core/GLTFBuffer.cs b/Common_glTF_Exporter/Core/GLTFBuffer.cs deleted file mode 100644 index a973d3a..0000000 --- a/Common_glTF_Exporter/Core/GLTFBuffer.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using System; - using System.Collections.Generic; - using System.Text; - - /// - /// A reference to the location and size of binary data - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#buffers-and-buffer-views. - /// - public class GLTFBuffer - { - /// - /// Gets or sets the uri of the buffer. - /// - public string uri { get; set; } - - /// - /// Gets or sets the total byte length of the buffer. - /// - public int byteLength { get; set; } - } -} diff --git a/Common_glTF_Exporter/Core/GLTFBufferView.cs b/Common_glTF_Exporter/Core/GLTFBufferView.cs deleted file mode 100644 index 11e3306..0000000 --- a/Common_glTF_Exporter/Core/GLTFBufferView.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using Newtonsoft.Json; - using Revit_glTF_Exporter; - - /// - /// A reference to a subsection of a buffer containing either vector or scalar data. - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#buffers-and-buffer-views - /// - public class GLTFBufferView - { - public GLTFBufferView(int buffer, int byteOffset, int byteLength, Targets target, string name) - { - this.buffer = buffer; - this.byteOffset = byteOffset; - this.byteLength = byteLength; - this.target = target; - this.name = name; - } - - [JsonProperty("buffer")] - public int buffer { get; set; } - - [JsonProperty("byteOffset")] - public int byteOffset { get; set; } - - [JsonProperty("byteLength")] - public int byteLength { get; set; } - - [JsonIgnore] // Ignore the raw enum field so we can customize serialization - public Targets target { get; set; } - - [JsonProperty("target")] - public int? TargetValue - { - get - { - if (target == Targets.ARRAY_BUFFER) - return 34962; - else if (target == Targets.ELEMENT_ARRAY_BUFFER) - return 34963; - else - return null; - } - } - - public bool ShouldSerializeTargetValue() - { - return TargetValue != null; - } - - [JsonProperty("name")] - public string name { get; set; } - } -} diff --git a/Common_glTF_Exporter/Core/GLTFExtras.cs b/Common_glTF_Exporter/Core/GLTFExtras.cs deleted file mode 100644 index c5aa8ef..0000000 --- a/Common_glTF_Exporter/Core/GLTFExtras.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using System.Collections.Generic; - using Common_glTF_Exporter.Export; - using Revit_glTF_Exporter; - - public class GLTFExtras - { - /// - /// Gets or sets the Revit created UniqueId for this object. - /// - public string uniqueId { get; set; } - - public RevitGridParametersObject gridParameters { get; set; } - - public Dictionary parameters { get; set; } - - public long elementId { get; set; } - - public string elementCategory { get; set; } - } -} diff --git a/Common_glTF_Exporter/Core/GLTFImage.cs b/Common_glTF_Exporter/Core/GLTFImage.cs deleted file mode 100644 index e47bb30..0000000 --- a/Common_glTF_Exporter/Core/GLTFImage.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Newtonsoft.Json; - -namespace Common_glTF_Exporter.Core -{ - /// - /// An image used by a texture - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#images - /// - public class GLTFImage - { - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string uri { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int? bufferView { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string mimeType { get; set; } - } -} \ No newline at end of file diff --git a/Common_glTF_Exporter/Core/GLTFMaterial.cs b/Common_glTF_Exporter/Core/GLTFMaterial.cs deleted file mode 100644 index 842338b..0000000 --- a/Common_glTF_Exporter/Core/GLTFMaterial.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using System; - using System.Collections.Generic; - using System.Drawing; - using System.Text; - using Newtonsoft.Json; - using Revit_glTF_Exporter; - - /// - /// The glTF PBR Material format - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#materials. - /// - public class GLTFMaterial - { - public string alphaMode { get; set; } - - public float? alphaCutoff { get; set; } - - public string name { get; set; } - - public GLTFPBR pbrMetallicRoughness { get; set; } - - public bool doubleSided { get; set; } - - public GLTFTexture normalTexture { get; set; } - public GLTFTexture occlusionTexture { get; set; } - public GLTFTexture emissiveTexture { get; set; } - public List emissiveFactor { get; set; } - - [JsonIgnore] - public string EmbeddedTexturePath { get; set; } = null; - [JsonIgnore] - public double Fadevalue { get; set; } = 1; - - [JsonIgnore] - public Autodesk.Revit.DB.Color TintColour { get; set; } - - [JsonIgnore] - public Autodesk.Revit.DB.Color BaseColor { get; set; } - - [JsonIgnore] - public string UniqueId { get; set; } = null; - } -} diff --git a/Common_glTF_Exporter/Core/GLTFMesh.cs b/Common_glTF_Exporter/Core/GLTFMesh.cs deleted file mode 100644 index ebc1238..0000000 --- a/Common_glTF_Exporter/Core/GLTFMesh.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using System; - using System.Collections.Generic; - using System.Text; - using Revit_glTF_Exporter; - - /// - /// The array of primitives defining the mesh of an object - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#meshes. - /// - public class GLTFMesh - { - public List primitives { get; set; } - - public string name { get; set; } - } -} diff --git a/Common_glTF_Exporter/Core/GLTFMeshPrimitive.cs b/Common_glTF_Exporter/Core/GLTFMeshPrimitive.cs deleted file mode 100644 index f3eed35..0000000 --- a/Common_glTF_Exporter/Core/GLTFMeshPrimitive.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using System; - using System.Collections.Generic; - using System.Text; - using static Revit_glTF_Exporter.GLTF; - - /// - /// Properties defining where the GPU should look to find the mesh and material data - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#meshes. - /// - public class GLTFMeshPrimitive - { - public GLTFAttribute attributes { get; set; } = new GLTFAttribute(); - - public int indices { get; set; } - - public int? material { get; set; } = null; - - public int mode { get; set; } = 4; // 4 is triangles - } -} diff --git a/Common_glTF_Exporter/Core/GLTFNode.cs b/Common_glTF_Exporter/Core/GLTFNode.cs deleted file mode 100644 index d0d9d29..0000000 --- a/Common_glTF_Exporter/Core/GLTFNode.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using System; - using System.Collections.Generic; - using System.Text; - using Autodesk.Revit.DB; - using Revit_glTF_Exporter; - - /// - /// The nodes defining individual (or nested) elements in the scene - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy. - /// - public class GLTFNode - { - /// - /// Gets or sets the user-defined name of this object. - /// - public string name { get; set; } - - /// - /// Gets or sets the index of the mesh in this node. - /// - public int? mesh { get; set; } = null; - - /// - /// Gets or sets a floating-point 4x4 transformation matrix stored in column major order. - /// - public List matrix { get; set; } - - /// - /// Gets or sets the indices of this node's children. - /// - public List children { get; set; } - - /// - /// Gets or sets the extras describing this node. - /// - public GLTFExtras extras { get; set; } - - /// - /// Gets or sets rotation of the node. - /// - public List rotation { get; set; } - - /// - /// Gets or sets translation of the node. - /// - public List translation { get; set; } - - /// - /// Gets or sets scale of the node. - /// - public List scale { get; set; } - } -} diff --git a/Common_glTF_Exporter/Core/GLTFScene.cs b/Common_glTF_Exporter/Core/GLTFScene.cs deleted file mode 100644 index 1c30c67..0000000 --- a/Common_glTF_Exporter/Core/GLTFScene.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using System.Collections.Generic; - - /// - /// The scenes available to render - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes. - /// - public class GLTFScene - { - public List nodes = new List(); - } -} diff --git a/Common_glTF_Exporter/Core/GLTFTextureInfo.cs b/Common_glTF_Exporter/Core/GLTFTextureInfo.cs deleted file mode 100644 index daa9480..0000000 --- a/Common_glTF_Exporter/Core/GLTFTextureInfo.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - - /// - /// Texture information for a material property - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#texture-info - /// - public class GLTFTexture - { - public int source { get; set; } - } - - public class GLTFTextureInfo - { - public int index { get; set; } - public int texCoord { get; set; } = 0; - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public GLTFTextureExtensions extensions { get; set; } - } - - public class GLTFTextureTransform - { - public float[] offset { get; set; } = new float[] { 0.0f, 0.0f }; - public float[] scale { get; set; } = new float[] { 1.0f, 1.0f }; - public float rotation { get; set; } = 0.0f; - public int texCoord { get; set; } = 0; - } - - public class GLTFTextureExtensions - { - [JsonProperty("KHR_texture_transform")] - public GLTFTextureTransform TextureTransform { get; set; } - } -} \ No newline at end of file diff --git a/Common_glTF_Exporter/Core/GLTFVersion.cs b/Common_glTF_Exporter/Core/GLTFVersion.cs deleted file mode 100644 index 192cc9d..0000000 --- a/Common_glTF_Exporter/Core/GLTFVersion.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using Common_glTF_Exporter.Utils; - using System; - using System.Collections.Generic; - using System.Text; - - /// - /// Required glTF asset information - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#asset. - /// - public class GLTFVersion - { - public string version = "2.0"; - public string generator = string.Concat("e-verse custom generator ", SettingsConfig.currentVersion); - public string copyright = "free tool created by e-verse"; - } -} diff --git a/Common_glTF_Exporter/Core/GlTFExportContext.cs b/Common_glTF_Exporter/Core/GlTFExportContext.cs index 3cde758..731921e 100644 --- a/Common_glTF_Exporter/Core/GlTFExportContext.cs +++ b/Common_glTF_Exporter/Core/GlTFExportContext.cs @@ -1,16 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Autodesk.Revit.DB; +using Autodesk.Revit.DB; +using Common_glTF_Exporter.EportUtils; using Common_glTF_Exporter.Export; using Common_glTF_Exporter.Model; using Common_glTF_Exporter.Transform; using Common_glTF_Exporter.Utils; using Common_glTF_Exporter.Windows.MainWindow; +using glTF.Manipulator.GenericSchema; +using glTF.Manipulator.Schema; +using glTF.Manipulator.Utils; using Revit_glTF_Exporter; -using Common_glTF_Exporter.EportUtils; -using System.Windows.Media.Media3D; -using System.Windows.Controls; +using System.Collections.Generic; +using System.Linq; +using Buffer = glTF.Manipulator.Schema.Buffer; namespace Common_glTF_Exporter.Core { @@ -31,10 +32,8 @@ public class GLTFExportContext : IExportContext private Document currentDocument; private Autodesk.Revit.DB.View currentView; - private GLTFNode currentNode; private Element currentElement; - private Face currentFace; - private GLTFMaterial currentMaterial; + private BaseMaterial currentMaterial; private Stack transformStack = new Stack(); /// @@ -52,20 +51,22 @@ public class GLTFExportContext : IExportContext /// /// Reference to the rootNode to add children. /// - private GLTFNode rootNode; - - public IndexedDictionary nodes = new IndexedDictionary(); - - public IndexedDictionary materials = new IndexedDictionary(); - public List textures { get; } = new List(); - public List images { get; } = new List(); - public List scenes { get; } = new List(); - public IndexedDictionary meshes { get; } = new IndexedDictionary(); - public List buffers { get; } = new List(); - public List bufferViews { get; } = new List(); - public List accessors { get; } = new List(); + private Node rootNode; + + public IndexedDictionary nodes = new IndexedDictionary(); + + public IndexedDictionary materials = new IndexedDictionary(); + public List textures { get; } = new List(); + public List images { get; } = new List(); + public List scenes { get; } = new List(); + public IndexedDictionary meshes { get; } = new IndexedDictionary(); + public List buffers { get; } = new List(); + public List bufferViews { get; } = new List(); + public List accessors { get; } = new List(); public List binaryFileData { get; } = new List(); + GLTFBinaryData globalBuffer = new GLTFBinaryData(); + private Autodesk.Revit.DB.Transform CurrentTransform { get @@ -93,7 +94,7 @@ public bool Start() transformStack.Push(Autodesk.Revit.DB.Transform.Identity); // Creation Root Node - rootNode = new GLTFNode(); + rootNode = new Node(); rootNode.name = "rootNode"; rootNode.rotation = ModelRotation.Get(preferences.flipAxis); rootNode.scale = ModelScale.Get(preferences); @@ -104,7 +105,7 @@ public bool Start() nodes.AddOrUpdateCurrent("rootNode", rootNode); // Creation default scene - GLTFScene defaultScene = new GLTFScene(); + Scene defaultScene = new Scene(); defaultScene.nodes.Add(0); scenes.Add(defaultScene); @@ -112,7 +113,9 @@ public bool Start() currentVertices = new IndexedDictionary(); currentMaterial = GLTFExportUtils.CreateDefaultGLTFMaterial(1, true); - materials.AddOrUpdateCurrentMaterial(currentMaterial.UniqueId, currentMaterial, false); + + materials.AddOrUpdateCurrentMaterial(currentMaterial.uuid, currentMaterial, false); + return true; } @@ -127,14 +130,27 @@ public void Finish() return; } + Buffer buffer = new Buffer(); + buffer.uri = string.Concat(preferences.fileName, ".bin"); + + if (preferences.materials == MaterialsEnum.textures) + { + GLTFBinaryDataUtils.ExportImageBuffer(images, bufferViews, globalBuffer); + } + if (preferences.grids) { RevitGrids.Export(currentDocument, ref nodes, ref rootNode, preferences); } + buffer.byteLength = globalBuffer.GetCurrentByteOffset(); + buffers.Add(buffer); + if (bufferViews.Count != 0) { - FileExport.Run(preferences, bufferViews, buffers, binaryFileData, + ProgressBarWindow.ViewModel.Message = "Saving to File"; + + FileExport.Run(preferences, bufferViews, buffers, globalBuffer, scenes, nodes, meshes, materials, accessors, textures, images); Compression.Run(preferences, ProgressBarWindow.ViewModel); } @@ -174,7 +190,6 @@ public RenderNodeAction OnElementBegin(ElementId elementId) } } - currentNode = GLTFNodeActions.CreateGLTFNodeFromElement(currentElement, preferences); currentGeometry.Reset(); currentVertices.Reset(); @@ -182,8 +197,6 @@ public RenderNodeAction OnElementBegin(ElementId elementId) return RenderNodeAction.Proceed; } - const char UNDERSCORE = '_'; - /// /// Runs at the end of an element being processed, after all other calls for that element. /// Here we compile all the "_current" variables (geometry and vertices) onto glTF buffers. @@ -199,76 +212,11 @@ public void OnElementEnd(ElementId elementId) return; } - nodes.AddOrUpdateCurrent(currentElement.UniqueId, currentNode); - rootNode.children.Add(nodes.CurrentIndex); - - GLTFMesh newMesh = new GLTFMesh - { - name = currentElement.Name, - primitives = new List() - }; - meshes.AddOrUpdateCurrent(currentElement.UniqueId, newMesh); - nodes.CurrentItem.mesh = meshes.CurrentIndex; - - // Convert _currentGeometry objects into glTFMeshPrimitives - foreach (KeyValuePair kvp in currentGeometry.Dict) - { - string material_key = kvp.Key.Split(UNDERSCORE)[1]; - GLTFMaterial mat = materials.GetElement(material_key); - - GLTFBinaryData elementBinary = GLTFExportUtils.AddGeometryMeta( - buffers, - accessors, - bufferViews, - kvp.Value, - kvp.Key, -#if REVIT2024 || REVIT2025 || REVIT2026 - elementId.Value, -#else - elementId.IntegerValue, -#endif - preferences, - mat, - images, - textures); - - binaryFileData.Add(elementBinary); - + ProcessGeometry.ProcessNode(nodes, rootNode, meshes, preferences, + globalBuffer, bufferViews, accessors, materials, currentGeometry, currentElement); - GLTFMeshPrimitive primitive = new GLTFMeshPrimitive(); - - primitive.attributes.POSITION = elementBinary.vertexAccessorIndex; - - if (preferences.normals) - { - primitive.attributes.NORMAL = elementBinary.normalsAccessorIndex; - } - - if (preferences.batchId) - { - primitive.attributes._BATCHID = elementBinary.batchIdAccessorIndex; - } - - if (elementBinary.uvAccessorIndex != -1 && - preferences.materials == MaterialsEnum.textures && - mat.EmbeddedTexturePath != null) - { - primitive.attributes.TEXCOORD_0 = elementBinary.uvAccessorIndex; - } - - primitive.indices = elementBinary.indexAccessorIndex; - - if (preferences.materials == MaterialsEnum.materials || preferences.materials == MaterialsEnum.textures) - { - if (materials.Contains(material_key)) - { - primitive.material = materials.GetIndexFromUUID(material_key); - } - } - - meshes.CurrentItem.primitives.Add(primitive); - meshes.CurrentItem.name = currentElement.Name; - } + currentGeometry.Reset(); + currentVertices.Reset(); } /// @@ -280,23 +228,14 @@ public void OnElementEnd(ElementId elementId) public void OnMaterial(MaterialNode node) { if (preferences.materials == MaterialsEnum.materials || preferences.materials == MaterialsEnum.textures) - { + { if (node.MaterialId == ElementId.InvalidElementId) { - currentMaterial = GLTFExportUtils.GetGLTFMaterial(materials, node.Transparency, false); + currentMaterial = GLTFExportUtils.GetGLTFMaterial(materials); } else { - string materialId = node.MaterialId.ToString(); - if (materials.Contains(materialId)) - { - currentMaterial = materials.GetElement(materialId); - } - else - { - currentMaterial = RevitMaterials.Export(node, preferences, currentDocument); - } - materials.AddOrUpdateCurrentMaterial(materialId, currentMaterial, false); + currentMaterial = RevitMaterials.ProcessMaterial(node, preferences, currentDocument, materials, textures, images); } } } @@ -332,10 +271,14 @@ public void OnPolymesh(PolymeshTopology polymesh) new PointIntObject(vertex), geomItem.Vertices); geomItem.Faces.Add(vertexIndex); - if (preferences.materials == MaterialsEnum.textures && currentMaterial?.EmbeddedTexturePath != null) + if (preferences.materials == MaterialsEnum.textures && currentMaterial.hasTexture) { UV uv = uvs[index]; - UV flippedUV = new UV(uv.U, 1.0 - uv.V); + GltfUV flippedUV = new GltfUV + { + U = (float)uv.U, + V = (float)(1.0 - uv.V) + }; geomItem.Uvs.Add(flippedUV); } } @@ -440,7 +383,7 @@ public void OnRPC(RPCNode node) if (preferences.materials == MaterialsEnum.materials || preferences.materials == MaterialsEnum.textures) { currentMaterial = MaterialUtils.GetGltfMeshMaterial(currentDocument, preferences, mesh, materials, true); - materials.AddOrUpdateCurrentMaterial(currentMaterial.UniqueId, currentMaterial, true); + materials.AddOrUpdateCurrentMaterial(currentMaterial.name, currentMaterial, true); } GLTFExportUtils.AddOrUpdateCurrentItem(currentElement, currentGeometry, currentVertices, currentMaterial); @@ -486,12 +429,10 @@ public void OnLight(LightNode node) public RenderNodeAction OnFaceBegin(FaceNode node) { - currentFace = node.GetFace(); return RenderNodeAction.Proceed; } public void OnFaceEnd(FaceNode node) { - currentFace = null; } } } \ No newline at end of file diff --git a/Common_glTF_Exporter/Core/IndexedDictionary.cs b/Common_glTF_Exporter/Core/IndexedDictionary.cs deleted file mode 100644 index 3230c82..0000000 --- a/Common_glTF_Exporter/Core/IndexedDictionary.cs +++ /dev/null @@ -1,163 +0,0 @@ -namespace Revit_glTF_Exporter -{ - using System; - using System.Collections.Generic; - using Common_glTF_Exporter.Core; - - /// - /// Container for holding a strict set of items - /// that is also addressable by a unique ID. - /// - /// The type of item contained. - public class IndexedDictionary - { - private Dictionary dict = new Dictionary(); - - /// - /// Gets all the generic elements inside the IndexedDictionary. - /// - public List List { get; } = new List(); - - /// - /// Gets the current key from actual element. - /// - public string CurrentKey { get; private set; } - - /// - /// Temp output used in 'this.Dict' - /// - Dictionary output = new Dictionary(); - - /// - /// Gets the dictionary. - /// - public Dictionary Dict - { - get - { - output.Clear(); - foreach (var kvp in this.dict) - { - output.Add(kvp.Key, this.List[kvp.Value]); - } - - return output; - } - } - - /// - /// Gets the most recently accessed item (not effected by GetElement()). - /// - public T CurrentItem - { - get { return this.List[this.dict[this.CurrentKey]]; } - } - - /// - /// Gets the index of the most recently accessed item (not effected by GetElement()). - /// - public int CurrentIndex - { - get { return this.dict[this.CurrentKey]; } - } - - /// - /// Add a new item to the list, if it already exists then the current item will be set to this item. - /// - /// Unique identifier for the item. - /// The item to add. - /// true if item did not already exist. - public bool AddOrUpdateCurrent(string uuid, T elem) - { - if (!this.dict.ContainsKey(uuid)) - { - this.List.Add(elem); - this.dict.Add(uuid, this.List.Count - 1); - this.CurrentKey = uuid; - return true; - } - - this.CurrentKey = uuid; - - return false; - } - - /// - /// Add a new gltfMaterial to the list, if it already exists then the current item will be set to this item. - /// - /// Unique identifier for the item. - /// The item to add. - /// Identify if the material is double sided. - /// true if item did not already exist. - public bool AddOrUpdateCurrentMaterial(string uuid, T elem, bool doubleSided) - { - if (!this.dict.ContainsKey(uuid)) - { - this.List.Add(elem); - this.dict.Add(uuid, this.List.Count - 1); - this.CurrentKey = uuid; - return true; - } - - this.CurrentKey = uuid; - - if (elem is GLTFMaterial) - { - var mat = this.GetElement(uuid) as GLTFMaterial; - mat.doubleSided = doubleSided; - } - - return false; - } - - /// - /// Check if the container already has an item with this key. - /// - /// Unique identifier for the item. - /// Returns TRUE if the dictionary contains the given element, otherwise, returns FALSE. - public bool Contains(string uuid) - { - return this.dict.ContainsKey(uuid); - } - - /// - /// Returns the index for an item given it's unique identifier. - /// - /// Unique identifier for the item. - /// index of item or -1. - public int GetIndexFromUUID(string uuid) - { - try - { - return this.dict[uuid]; - } - catch (KeyNotFoundException) - { - throw new Exception("Specified item could not be found."); - } - catch (Exception ex) - { - throw new Exception($"Error getting the specified item {ex.Message}"); - } - } - - /// - /// Returns an item given it's unique identifier. - /// - /// Unique identifier for the item. - /// Element. - public T GetElement(string uuid) - { - int index = this.GetIndexFromUUID(uuid); - return this.List[index]; - } - - public void Reset() - { - this.dict.Clear(); - this.List.Clear(); - this.Dict.Clear(); - this.CurrentKey = string.Empty; - } - } -} diff --git a/Common_glTF_Exporter/Core/ProcessGeometry.cs b/Common_glTF_Exporter/Core/ProcessGeometry.cs new file mode 100644 index 0000000..fe108a9 --- /dev/null +++ b/Common_glTF_Exporter/Core/ProcessGeometry.cs @@ -0,0 +1,106 @@ +using Autodesk.Revit.DB; +using Common_glTF_Exporter.Model; +using Common_glTF_Exporter.Utils; +using Common_glTF_Exporter.Windows.MainWindow; +using glTF.Manipulator.GenericSchema; +using glTF.Manipulator.Schema; +using glTF.Manipulator.Utils; +using Revit_glTF_Exporter; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Xml.Linq; + +namespace Common_glTF_Exporter.Core +{ + public static class ProcessGeometry + { + public static void ProcessNode(IndexedDictionary nodes, Node rootNode, IndexedDictionary meshes, + Preferences preferences, GLTFBinaryData globalBuffer, List bufferViews, List accessors, IndexedDictionary materials, + IndexedDictionary dataObject, Element currentElement) + { + + Extras extras = new Extras(); + + if (preferences.properties) + { + var parameters = Util.GetElementParameters(currentElement, true); + parameters["UniqueId"] = currentElement.UniqueId; + + if (currentElement.Category != null) + parameters["Category"] = currentElement.Category.Name; + + extras.parameters = parameters; + } + + Node newNode = new Node() + { + name = Util.ElementDescription(currentElement), + extras = extras + }; + + long elmId; + + #if REVIT2024 || REVIT2025 || REVIT2026 + elmId = currentElement.Id.Value; + #else + elmId = currentElement.Id.IntegerValue; + #endif + + nodes.AddOrUpdateCurrent(currentElement.UniqueId, newNode); + rootNode.children.Add(nodes.CurrentIndex); + + glTF.Manipulator.Schema.Mesh newMesh = new glTF.Manipulator.Schema.Mesh + { + name = currentElement.Name, + primitives = new List() + }; + + meshes.AddOrUpdateCurrent(currentElement.UniqueId, newMesh); + nodes.CurrentItem.mesh = meshes.CurrentIndex; + + foreach (KeyValuePair kvp in dataObject.Dict) + { + MeshPrimitive primitive = new MeshPrimitive(); + GLTFBinaryData gLTFBinaryDataElement = globalBuffer; + + if (preferences.materials == MaterialsEnum.textures) + { + GLTFBinaryDataUtils.ExportUVs(kvp.Value, gLTFBinaryDataElement, bufferViews, accessors, primitive); + } + + GLTFBinaryDataUtils.ExportVertices(kvp.Value, gLTFBinaryDataElement, bufferViews, accessors, primitive); + + if (preferences.normals) + { + GLTFBinaryDataUtils.ExportNormals(kvp.Value, gLTFBinaryDataElement, bufferViews, accessors, primitive); + } + + if (preferences.batchId) + { + GLTFBinaryDataUtils.ExportBatchId(elmId, kvp.Value, gLTFBinaryDataElement, bufferViews, accessors, primitive); + } + + int indexAcessor = GLTFBinaryDataUtils.ExportFaces(kvp.Value, gLTFBinaryDataElement, bufferViews, accessors); + + primitive.indices = indexAcessor; + + if (preferences.materials != MaterialsEnum.nonematerials) + { + primitive.material = materials.GetIndexFromUUID(kvp.Value.MaterialInfo.uuid); + } + + meshes.CurrentItem.primitives.Add(primitive); + meshes.CurrentItem.name = currentElement.Name; + + var g = kvp.Value; + g.Vertices.Clear(); + g.Normals.Clear(); + g.Uvs.Clear(); + g.Faces.Clear(); + } + } + } +} diff --git a/Common_glTF_Exporter/Core/glTF.cs b/Common_glTF_Exporter/Core/glTF.cs deleted file mode 100644 index 9b04486..0000000 --- a/Common_glTF_Exporter/Core/glTF.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Revit_glTF_Exporter -{ - using System.Collections.Generic; - using Common_glTF_Exporter.Core; - - /// - /// Magic numbers to differentiate scalar and vector array buffers - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#buffers-and-buffer-views. - /// - public enum Targets - { - NONE = 0, - ARRAY_BUFFER = 34962, // signals vertex data - ELEMENT_ARRAY_BUFFER = 34963, // signals index or face data - } - - /// - /// Magic numbers to differentiate array buffer component types - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessor-element-size. - /// - public enum ComponentType - { - BYTE = 5120, - UNSIGNED_BYTE = 5121, - SHORT = 5122, - UNSIGNED_SHORT = 5123, - UNSIGNED_INT = 5125, - FLOAT = 5126, - } - - /// - /// The json serializable glTF file format - /// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0. - /// - public struct GLTF - { - public GLTFVersion asset; - public List extensionsUsed; - public List scenes; - public List nodes; - public List meshes; - public List buffers; - public List bufferViews; - public List accessors; - public List materials; - public List textures; - public List images; - } -} diff --git a/Common_glTF_Exporter/Core/glTFPBR.cs b/Common_glTF_Exporter/Core/glTFPBR.cs deleted file mode 100644 index d7606e9..0000000 --- a/Common_glTF_Exporter/Core/glTFPBR.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Common_glTF_Exporter.Core -{ - using System.Collections.Generic; - - public class GLTFPBR - { - public List baseColorFactor { get; set; } - - public float metallicFactor { get; set; } - - public float roughnessFactor { get; set; } - - // Texture properties - public GLTFTextureInfo baseColorTexture { get; set; } - public GLTFTexture metallicRoughnessTexture { get; set; } - } -} diff --git a/Common_glTF_Exporter/EportUtils/ElementValidations.cs b/Common_glTF_Exporter/EportUtils/ElementValidations.cs index b12dbe8..15e2b4a 100644 --- a/Common_glTF_Exporter/EportUtils/ElementValidations.cs +++ b/Common_glTF_Exporter/EportUtils/ElementValidations.cs @@ -1,24 +1,26 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Linq; -using Autodesk.Revit.DB; +using Autodesk.Revit.DB; +using Common_glTF_Exporter.Core; +using Common_glTF_Exporter.EportUtils; using Common_glTF_Exporter.Export; using Common_glTF_Exporter.Model; using Common_glTF_Exporter.Transform; using Common_glTF_Exporter.Utils; using Common_glTF_Exporter.Windows.MainWindow; +using glTF.Manipulator.Schema; +using glTF.Manipulator.Utils; using Revit_glTF_Exporter; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; using Transform = Autodesk.Revit.DB.Transform; -using Common_glTF_Exporter.EportUtils; -using Common_glTF_Exporter.Core; namespace Common_glTF_Exporter.EportUtils { public static class ElementValidations { public static bool ShouldSkipElement(Element currentElement, Autodesk.Revit.DB.View currentView, - Document currentDocument, Preferences preferences, IndexedDictionary nodes) + Document currentDocument, Preferences preferences, IndexedDictionary nodes) { if (currentElement == null) { diff --git a/Common_glTF_Exporter/EportUtils/GLTFNodeActions.cs b/Common_glTF_Exporter/EportUtils/GLTFNodeActions.cs deleted file mode 100644 index 28f2586..0000000 --- a/Common_glTF_Exporter/EportUtils/GLTFNodeActions.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Autodesk.Revit.DB; -using Revit_glTF_Exporter; -using Common_glTF_Exporter.Core; -using Common_glTF_Exporter.Windows.MainWindow; - -namespace Common_glTF_Exporter.EportUtils -{ - public static class GLTFNodeActions - { - public static GLTFNode CreateGLTFNodeFromElement(Element currentElement, Preferences preferences) - { - // create a new node for the element - GLTFNode newNode = new GLTFNode(); - newNode.name = Util.ElementDescription(currentElement); - - if (preferences.properties) - { - // get the extras for this element - GLTFExtras extras = new GLTFExtras - { - uniqueId = currentElement.UniqueId, - parameters = Util.GetElementParameters(currentElement, true) - }; - - if (currentElement.Category != null) - { - extras.elementCategory = currentElement.Category.Name; - } - -#if REVIT2024 || REVIT2025 || REVIT2026 - extras.elementId = currentElement.Id.Value; -#else - extras.elementId = currentElement.Id.IntegerValue; -#endif - - newNode.extras = extras; - } - - return newNode; - } - } -} diff --git a/Common_glTF_Exporter/Export/BinFile.cs b/Common_glTF_Exporter/Export/BinFile.cs index 9c242a8..904d89c 100644 --- a/Common_glTF_Exporter/Export/BinFile.cs +++ b/Common_glTF_Exporter/Export/BinFile.cs @@ -1,74 +1,21 @@ -namespace Common_glTF_Exporter.Export -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using Common_glTF_Exporter.Core; - using Common_glTF_Exporter.Windows.MainWindow; +using System.IO; +using glTF.Manipulator.GenericSchema; - public static class BinFile +public static class BinFile +{ + /// + /// Create a new .bin file using the optimized GLTFBinaryData. + /// + public static void Create(string filename, GLTFBinaryData globalBuffer) { - /// - /// Create a new .bin file. - /// - /// .bin file name. - /// binary file data. - /// export normals. - /// export BatchId. - public static void Create(string filename, List binaryFileData, - Preferences preferences) - { - using (FileStream f = File.Create(filename)) - using (var writer = new BinaryWriter(new BufferedStream(f), Encoding.UTF8)) - { - foreach (var bin in binaryFileData) - { - for (int i = 0; i < bin.vertexBuffer.Count; i++) - { - writer.Write((float)bin.vertexBuffer[i]); - } - - if (preferences.normals) - { - for (int i = 0; i < bin.normalBuffer.Count; i++) - { - writer.Write((float)bin.normalBuffer[i]); - } - } - - if (preferences.materials == MaterialsEnum.textures) - { - if (bin.byteData != null) - { - writer.Write((byte[])bin.byteData); - } + // Obtiene los bytes finales del MemoryStream + byte[] data = globalBuffer.ToArray(); - if (bin.uvBuffer != null && bin.uvBuffer.Count > 0) - { - for (int i = 0; i < bin.uvBuffer.Count; i++) - { - writer.Write((float)bin.uvBuffer[i]); - } - } - } - - if (preferences.batchId) - { - for (int i = 0; i < bin.batchIdBuffer.Count; i++) - { - writer.Write((float)bin.batchIdBuffer[i]); - } - } - - for (int i = 0; i < bin.indexBuffer.Count; i++) - { - writer.Write((int)bin.indexBuffer[i]); - } - } - - writer.Flush(); - } + using (FileStream f = File.Create(filename)) + using (var writer = new BinaryWriter(new BufferedStream(f))) + { + writer.Write(data); + writer.Flush(); } } } diff --git a/Common_glTF_Exporter/Export/BufferConfig.cs b/Common_glTF_Exporter/Export/BufferConfig.cs index 403d412..9680694 100644 --- a/Common_glTF_Exporter/Export/BufferConfig.cs +++ b/Common_glTF_Exporter/Export/BufferConfig.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; -using System.Text; using Common_glTF_Exporter.Core; using Common_glTF_Exporter.Windows.MainWindow; +using glTF.Manipulator.Schema; +using Buffer = glTF.Manipulator.Schema.Buffer; namespace Common_glTF_Exporter.Export { @@ -10,7 +11,7 @@ public static class BufferConfig { const string BIN = ".bin"; - public static void Run(List bufferViews, List buffers, + public static void Run(List bufferViews, List buffers, Preferences preferences) { int bytePosition = 0; @@ -32,7 +33,7 @@ public static void Run(List bufferViews, List buffer } } - GLTFBuffer buffer = new GLTFBuffer(); + Buffer buffer = new Buffer(); if (preferences.format == FormatEnum.gltf) { diff --git a/Common_glTF_Exporter/Export/Draco.cs b/Common_glTF_Exporter/Export/Draco.cs index acebdd9..e3a855e 100644 --- a/Common_glTF_Exporter/Export/Draco.cs +++ b/Common_glTF_Exporter/Export/Draco.cs @@ -1,14 +1,444 @@ -using System; +using Common_glTF_Exporter.Model; +using Common_glTF_Exporter.Windows.MainWindow; +using dracowrapper; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; using System.IO; using System.Reflection; -using System.Windows; -using Common_glTF_Exporter.Model; -using Common_glTF_Exporter.Windows.MainWindow; -using dracowrapper; namespace Common_glTF_Exporter.Export { + internal sealed class GlbData + { + public string Json; + public byte[] Bin; + + public GlbData(string json, byte[] bin) + { + Json = json; + Bin = bin ?? new byte[0]; + } + } + + internal static class GltfExtrasPatcher + { + private static JToken DeepClone(JToken token) + { + if (token == null) return null; + return token.DeepClone(); + } + + public static void PatchExtras(string originalPath, string tempPath) + { + if (string.IsNullOrEmpty(originalPath) || string.IsNullOrEmpty(tempPath)) + throw new ArgumentNullException(); + + string ext = (Path.GetExtension(originalPath) ?? "").ToLowerInvariant(); + if (ext == ".gltf") + { + PatchExtrasGltf(originalPath, tempPath); + } + else if (ext == ".glb") + { + PatchExtrasGlb(originalPath, tempPath); + } + } + + private static void PatchExtrasGltf(string originalGltf, string tempGltf) + { + JObject src = JObject.Parse(File.ReadAllText(originalGltf)); + JObject dst = JObject.Parse(File.ReadAllText(tempGltf)); + + PatchArrayByIndex(src, dst, "nodes", PatchNodeLike); + + MergeExtensionsUsedAndRequired(src, dst); + + string baseDir = Path.GetDirectoryName(tempGltf); + string tempBinFileName = Path.GetFileName(tempGltf).Replace(".gltf", ".bin"); + + byte[] binBytes = null; + InlineExternalImagesIntoBin(dst, baseDir, tempBinFileName, ref binBytes, false, true); + + if (binBytes != null) + { + string binPath = Path.Combine(baseDir, tempBinFileName); + File.WriteAllBytes(binPath, binBytes); + } + + string jsonOut = dst.ToString(Formatting.None); + File.WriteAllText(tempGltf, jsonOut); + } + + private static void PatchExtrasGlb(string originalGlb, string tempGlb) + { + GlbData srcGlb = ReadGlb(originalGlb); + GlbData dstGlb = ReadGlb(tempGlb); + + JObject src = JObject.Parse(srcGlb.Json); + JObject dst = JObject.Parse(dstGlb.Json); + + PatchArrayByIndex(src, dst, "nodes", PatchNodeLike); + + MergeExtensionsUsedAndRequired(src, dst); + + byte[] glbBin = dstGlb.Bin; + string baseDir = Path.GetDirectoryName(tempGlb); + InlineExternalImagesIntoBin(dst, baseDir, null, ref glbBin, true, true); + + string newJson = dst.ToString(Formatting.None); + WriteGlb(tempGlb, newJson, glbBin); + } + + private static void PatchNodeLike(JObject srcNode, JObject dstNode) + { + CopyExtras(srcNode, dstNode); + CopyUnknownExtensions(srcNode, dstNode, new[] { "KHR_draco_mesh_compression" }); + } + + private static void PatchArrayByIndex(JObject src, JObject dst, string name, Action patchItem) + { + JArray sa = src[name] as JArray; + JArray da = dst[name] as JArray; + + if (sa == null || da == null) return; + + int count = Math.Min(sa.Count, da.Count); + for (int i = 0; i < count; i++) + { + JObject sItem = sa[i] as JObject; + JObject dItem = da[i] as JObject; + if (sItem == null || dItem == null) continue; + + patchItem(sItem, dItem); + } + } + + private static void CopyExtras(JObject src, JObject dst) + { + JToken extras = src["extras"]; + if (extras != null) + { + dst["extras"] = DeepClone(extras); + } + } + + private static void CopyUnknownExtensions(JObject src, JObject dst, IEnumerable keepKnown) + { + JObject sExt = src["extensions"] as JObject; + if (sExt == null) return; + + JObject dExt = dst["extensions"] as JObject; + if (dExt == null) dExt = new JObject(); + + HashSet known = new HashSet(keepKnown, StringComparer.Ordinal); + + foreach (var kvp in sExt) + { + string name = kvp.Key; + JToken value = kvp.Value; + if (value == null) continue; + + if (known.Contains(name) && dExt.ContainsKey(name)) continue; + + dExt[name] = DeepClone(value); + } + + if (dExt.Count > 0) + dst["extensions"] = dExt; + } + + private static void MergeExtensionsUsedAndRequired(JObject src, JObject dst) + { + MergeTokenArray(src, dst, "extensionsUsed"); + MergeTokenArray(src, dst, "extensionsRequired"); + + EnsureExtInArray(dst, "extensionsUsed", "KHR_draco_mesh_compression"); + } + + private static void MergeTokenArray(JObject src, JObject dst, string prop) + { + JArray srcArr = src[prop] as JArray; + + if (srcArr == null || srcArr.Count == 0) + return; + + JArray dstArr = dst[prop] as JArray; + if (dstArr == null) + { + dstArr = new JArray(); + dst[prop] = dstArr; + } + + HashSet existing = new HashSet(StringComparer.Ordinal); + foreach (JToken t in dstArr) + { + if (t == null) continue; + existing.Add((string)t); + } + + foreach (JToken t in srcArr) + { + if (t == null) continue; + string name = (string)t; + if (!existing.Contains(name)) + { + dstArr.Add(name); + existing.Add(name); + } + } + } + + private static void EnsureExtInArray(JObject dst, string arrayName, string ext) + { + JArray arr = dst[arrayName] as JArray; + if (arr == null) + { + arr = new JArray(); + dst[arrayName] = arr; + } + + foreach (JToken t in arr) + { + if (t == null) continue; + if (string.Equals((string)t, ext, StringComparison.Ordinal)) + return; + } + + arr.Add(ext); + } + + private static void InlineExternalImagesIntoBin( + JObject model, + string baseDir, + string desiredBinFileName, + ref byte[] binBytes, + bool isGlb, + bool removeExternalImageFiles) + { + JArray buffers = model["buffers"] as JArray; + if (buffers == null) + { + buffers = new JArray(); + model["buffers"] = buffers; + } + if (buffers.Count == 0) + { + JObject bufObj = new JObject(); + bufObj["byteLength"] = 0; + buffers.Add(bufObj); + } + + JArray bufferViews = model["bufferViews"] as JArray; + if (bufferViews == null) + { + bufferViews = new JArray(); + model["bufferViews"] = bufferViews; + } + + JObject buf0 = buffers[0] as JObject; + + if (!isGlb) + { + if (string.IsNullOrEmpty(desiredBinFileName)) + desiredBinFileName = "sceneTemp.bin"; + + buf0["uri"] = desiredBinFileName; + + string desiredBinPath = Path.Combine(baseDir, desiredBinFileName); + if ((binBytes == null || binBytes.Length == 0) && File.Exists(desiredBinPath)) + { + binBytes = File.ReadAllBytes(desiredBinPath); + } + } + + int appendOffset = (binBytes != null) ? binBytes.Length : 0; + + JArray images = model["images"] as JArray; + HashSet consumedFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + + if (images != null) + { + for (int i = 0; i < images.Count; i++) + { + JObject img = images[i] as JObject; + if (img == null) continue; + + if (img["bufferView"] != null) continue; + + JToken uriNode = img["uri"]; + string uri = (uriNode != null) ? (string)uriNode : null; + if (string.IsNullOrEmpty(uri)) continue; + if (uri.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) continue; + + string imgPath = Path.Combine(baseDir, uri); + if (!File.Exists(imgPath)) continue; + + byte[] imgBytes = File.ReadAllBytes(imgPath); + string mime = MimeFromExtension(Path.GetExtension(uri)); + + int thisOffset = appendOffset; + binBytes = AppendBytes(binBytes, imgBytes, true, 0x00); + int thisLength = imgBytes.Length; + appendOffset = binBytes.Length; + + int bvIndex = bufferViews.Count; + JObject bv = new JObject(); + bv["buffer"] = 0; + bv["byteOffset"] = thisOffset; + bv["byteLength"] = thisLength; + bufferViews.Add(bv); + + img.Remove("uri"); + img["bufferView"] = bvIndex; + + if (!string.IsNullOrEmpty(mime)) + img["mimeType"] = mime; + + if (removeExternalImageFiles) + consumedFiles.Add(imgPath); + } + } + + buf0["byteLength"] = binBytes != null ? binBytes.Length : 0; + + if (removeExternalImageFiles && consumedFiles.Count > 0) + { + foreach (string p in consumedFiles) + { + try + { + if (File.Exists(p)) File.Delete(p); + } + catch { } + } + } + } + + private static string MimeFromExtension(string ext) + { + if (ext == null) ext = ""; + ext = ext.ToLowerInvariant(); + + if (ext == ".png") return "image/png"; + if (ext == ".jpg" || ext == ".jpeg") return "image/jpeg"; + if (ext == ".ktx2") return "image/ktx2"; + return null; + } + + private static byte[] AppendBytes(byte[] bin, byte[] add, bool padTo4, byte padByte) + { + if (bin == null) bin = new byte[0]; + if (add == null) add = new byte[0]; + + int oldLen = bin.Length; + int newLen = oldLen + add.Length; + + byte[] outArr = new byte[newLen]; + Buffer.BlockCopy(bin, 0, outArr, 0, oldLen); + Buffer.BlockCopy(add, 0, outArr, oldLen, add.Length); + + if (padTo4) + { + int mod = outArr.Length % 4; + if (mod != 0) + { + int pad = 4 - mod; + byte[] padded = new byte[outArr.Length + pad]; + Buffer.BlockCopy(outArr, 0, padded, 0, outArr.Length); + for (int i = 0; i < pad; i++) + padded[outArr.Length + i] = padByte; + return padded; + } + } + + return outArr; + } + + private static GlbData ReadGlb(string path) + { + using (FileStream fs = File.OpenRead(path)) + using (BinaryReader br = new BinaryReader(fs)) + { + uint magic = br.ReadUInt32(); + uint version = br.ReadUInt32(); + uint length = br.ReadUInt32(); + + uint chunkLen0 = br.ReadUInt32(); + uint chunkType0 = br.ReadUInt32(); + byte[] jsonBytes = br.ReadBytes((int)chunkLen0); + string json = System.Text.Encoding.UTF8.GetString(jsonBytes); + + byte[] bin = new byte[0]; + if (fs.Position + 8 <= fs.Length) + { + uint chunkLen1 = br.ReadUInt32(); + uint chunkType1 = br.ReadUInt32(); + bin = br.ReadBytes((int)chunkLen1); + } + + return new GlbData(json, bin); + } + } + + private static void WriteGlb(string path, string json, byte[] bin) + { + byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json); + jsonBytes = PadTo4(jsonBytes, 0x20); + + byte[] binBytes = bin ?? new byte[0]; + binBytes = PadTo4(binBytes, 0x00); + + using (FileStream fs = File.Create(path)) + using (BinaryWriter bw = new BinaryWriter(fs)) + { + uint magic = 0x46546C67; // 'glTF' + uint version = 2; + + uint length = 12 + + 8 + (uint)jsonBytes.Length + + (binBytes.Length > 0 ? (8 + (uint)binBytes.Length) : 0); + + bw.Write(magic); + bw.Write(version); + bw.Write(length); + + bw.Write((uint)jsonBytes.Length); + bw.Write(0x4E4F534A); + bw.Write(jsonBytes); + + if (binBytes.Length > 0) + { + bw.Write((uint)binBytes.Length); + bw.Write(0x004E4942); + bw.Write(binBytes); + } + } + } + + private static byte[] PadTo4(byte[] data, byte padByte) + { + int mod = data.Length % 4; + if (mod == 0) return data; + + int pad = 4 - mod; + byte[] outArr = new byte[data.Length + pad]; + Buffer.BlockCopy(data, 0, outArr, 0, data.Length); + for (int i = 0; i < pad; i++) + outArr[data.Length + i] = padByte; + + return outArr; + } + } + + + // + // -------------------------------------------------------------------------------------- + // DRACO COMPRESSION (unchanged) + // -------------------------------------------------------------------------------------- + // + public static class Draco { public static void Compress(Preferences preferences) @@ -19,74 +449,105 @@ public static void Compress(Preferences preferences) if (preferences.format == FormatEnum.gltf) { - fileToCompress = string.Concat(preferences.path, ".gltf"); - fileToCompressTemp = string.Concat(preferences.path, "Temp.gltf"); + fileToCompress = preferences.path + ".gltf"; + fileToCompressTemp = preferences.path + "Temp.gltf"; - files.Add(string.Concat(preferences.path, ".bin")); + files.Add(preferences.path + ".bin"); files.Add(fileToCompress); } else { - fileToCompress = string.Concat(preferences.path, ".glb"); - fileToCompressTemp = string.Concat(preferences.path, "Temp.glb"); + fileToCompress = preferences.path + ".glb"; + fileToCompressTemp = preferences.path + "Temp.glb"; files.Add(fileToCompress); } #if REVIT2025 || REVIT2026 - var loadContext = new NonCollectibleAssemblyLoadContext(); - string programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); string assemblyPath = Path.Combine(programDataPath, "Autodesk", "ApplicationPlugins", "leia.bundle", "Contents", "2025", "DracoWrapper.dll"); - Assembly mixedModeAssembly = loadContext.LoadFromAssemblyPath(assemblyPath); var gltfDecoderType = mixedModeAssembly.GetType("dracowrapper.GltfDecoder"); var gltfDecoderInstance = Activator.CreateInstance(gltfDecoderType); - var decodeFromFileToSceneMethod = gltfDecoderType.GetMethod("DecodeFromFileToScene"); - var res = decodeFromFileToSceneMethod.Invoke(gltfDecoderInstance, new object[] { fileToCompress }); + var decodeMethod = gltfDecoderType.GetMethod("DecodeFromFileToScene"); + var res = decodeMethod.Invoke(gltfDecoderInstance, new object[] { fileToCompress }); var resType = res.GetType(); var valueMethod = resType.GetMethod("Value"); var scene = valueMethod.Invoke(res, null); - var dracoCompressionOptionsType = mixedModeAssembly.GetType("dracowrapper.DracoCompressionOptions"); - var dracoCompressionOptionsInstance = Activator.CreateInstance(dracoCompressionOptionsType); + var optionsType = mixedModeAssembly.GetType("dracowrapper.DracoCompressionOptions"); + var optionsInstance = Activator.CreateInstance(optionsType); var sceneUtilsType = mixedModeAssembly.GetType("dracowrapper.SceneUtils"); - var setDracoCompressionOptionsMethod = sceneUtilsType.GetMethod("SetDracoCompressionOptions"); - setDracoCompressionOptionsMethod.Invoke(null, new object[] { dracoCompressionOptionsInstance, scene }); + var setMethod = sceneUtilsType.GetMethod("SetDracoCompressionOptions"); + setMethod.Invoke(null, new object[] { optionsInstance, scene }); - var gltfEncoderType = mixedModeAssembly.GetType("dracowrapper.GltfEncoder"); - var gltfEncoderInstance = Activator.CreateInstance(gltfEncoderType); - var encodeSceneToFileMethod = gltfEncoderType.GetMethod("EncodeSceneToFile"); - encodeSceneToFileMethod.Invoke(gltfEncoderInstance, new object[] { scene, fileToCompressTemp }); + var encoderType = mixedModeAssembly.GetType("dracowrapper.GltfEncoder"); + var encoderInstance = Activator.CreateInstance(encoderType); + var encodeMethod = encoderType.GetMethod("EncodeSceneToFile"); + encodeMethod.Invoke(encoderInstance, new object[] { scene, fileToCompressTemp }); #else - var decoder = new GltfDecoder(); var res = decoder.DecodeFromFileToScene(fileToCompress); var scene = res.Value(); + DracoCompressionOptions options = new DracoCompressionOptions(); SceneUtils.SetDracoCompressionOptions(options, scene); + var encoder = new GltfEncoder(); encoder.EncodeSceneToFile(scene, fileToCompressTemp); +#endif + + GltfExtrasPatcher.PatchExtras(fileToCompress, fileToCompressTemp); - #endif + foreach (string x in files) + { + try + { + if (File.Exists(x)) File.Delete(x); + } + catch + { + } + } - files.ForEach(x => File.Delete(x)); - File.Move(fileToCompressTemp, fileToCompress); + File_MoveOverwrite(fileToCompressTemp, fileToCompress); if (preferences.format == FormatEnum.gltf) { - string binToReplace = fileToCompressTemp.Replace(".gltf", ".bin"); - string binFinalName = fileToCompressTemp.Replace("Temp.gltf", ".bin"); - File.Move(binToReplace, binFinalName); + string binTemp = fileToCompressTemp.Replace(".gltf", ".bin"); + string binFinal = preferences.path + ".bin"; + + if (File.Exists(binTemp)) + { + File_MoveOverwrite(binTemp, binFinal); + } - string text = File.ReadAllText(fileToCompress); - text = text.Replace(Path.GetFileName(binToReplace), Path.GetFileName(binFinalName)); - File.WriteAllText(fileToCompress, text); + if (File.Exists(fileToCompress)) + { + string text = File.ReadAllText(fileToCompress); + string binTempName = Path.GetFileName(binTemp); + string binFinalName = Path.GetFileName(binFinal); + if (!string.IsNullOrEmpty(binTempName) && !string.IsNullOrEmpty(binFinalName)) + { + text = text.Replace(binTempName, binFinalName); + File.WriteAllText(fileToCompress, text); + } + } + } + } + + private static void File_MoveOverwrite(string src, string dst) + { + if (File.Exists(dst)) + { + try { File.Delete(dst); } + catch { } } + File.Move(src, dst); } } } diff --git a/Common_glTF_Exporter/Export/FileExport.cs b/Common_glTF_Exporter/Export/FileExport.cs index 522d7d0..a239063 100644 --- a/Common_glTF_Exporter/Export/FileExport.cs +++ b/Common_glTF_Exporter/Export/FileExport.cs @@ -1,13 +1,16 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using Autodesk.Revit.DB; +using Autodesk.Revit.DB; using Common_glTF_Exporter.Core; +using Common_glTF_Exporter.Model; using Common_glTF_Exporter.Windows.MainWindow; -using Newtonsoft.Json; +using glTF.Manipulator.GenericSchema; +using glTF.Manipulator.Schema; +using glTF.Manipulator.Utils; using Revit_glTF_Exporter; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Buffer = glTF.Manipulator.Schema.Buffer; +using Material = glTF.Manipulator.Schema.Material; namespace Common_glTF_Exporter.Export { @@ -18,21 +21,22 @@ public static class FileExport public static void Run( Preferences preferences, - List bufferViews, - List buffers, - List binaryFileData, List scenes, - IndexedDictionary nodes, - IndexedDictionary meshes, - IndexedDictionary materials, - List accessors, - List textures, - List images) + List bufferViews, + List buffers, + GLTFBinaryData binaryFileData, + List scenes, + IndexedDictionary nodes, + IndexedDictionary meshes, + IndexedDictionary materials, + List accessors, + List textures, + List images) { if (preferences.format == FormatEnum.gltf) { BufferConfig.Run(bufferViews, buffers, preferences); string fileDirectory = string.Concat(preferences.path, BIN); - BinFile.Create(fileDirectory, binaryFileData, preferences); + BinFile.Create(fileDirectory, binaryFileData); string gltfJson = GltfJson.Get(scenes, nodes.List, meshes.List, materials.List, buffers, bufferViews, accessors, textures, images, preferences); diff --git a/Common_glTF_Exporter/Export/GlbBinInfo.cs b/Common_glTF_Exporter/Export/GlbBinInfo.cs index 33a469f..2dffdcc 100644 --- a/Common_glTF_Exporter/Export/GlbBinInfo.cs +++ b/Common_glTF_Exporter/Export/GlbBinInfo.cs @@ -1,89 +1,41 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Autodesk.Revit.DB.Visual; -using Common_glTF_Exporter.Core; using Common_glTF_Exporter.Model; -using Common_glTF_Exporter.Windows.MainWindow; +using glTF.Manipulator.GenericSchema; namespace Common_glTF_Exporter.Export { public static class GlbBinInfo { - public static byte[] Get(List binaryFileData, Preferences preferences) + public static byte[] Get(GLTFBinaryData globalBuffer) { - List binData = new List(); - - foreach (var bin in binaryFileData) - { - foreach (var coord in bin.vertexBuffer) - { - List vertex = BitConverter.GetBytes((float)coord).ToList(); - binData.AddRange(vertex); - } - - if (preferences.normals) - { - foreach (var normal in bin.normalBuffer) - { - List normalBuffer = BitConverter.GetBytes((float)normal).ToList(); - binData.AddRange(normalBuffer); - } - } + GlbBin glbBin = new GlbBin(); - if (preferences.materials == MaterialsEnum.textures) - { - if (bin.byteData != null) - { - binData.AddRange(bin.byteData); - } + // Usar MemoryStream → ToArray() + glbBin.ChunkData = globalBuffer.ToArray(); - if (bin.uvBuffer != null && bin.uvBuffer.Count > 0) - { - foreach (var uv in bin.uvBuffer) - { - List uvBytes = BitConverter.GetBytes((float)uv).ToList(); - binData.AddRange(uvBytes); - } - } - } + // Length del chunk BIN + glbBin.Length = BitConverter.GetBytes((uint)glbBin.ChunkData.Length); - if (preferences.batchId) - { - foreach (var batchId in bin.batchIdBuffer) - { - List batchIdBuffer = BitConverter.GetBytes((float)batchId).ToList(); - binData.AddRange(batchIdBuffer); - } - } + // 📌 Construcción del chunk final: + // [byteLength][CHUNK_TYPE][binary data] + byte[] result = new byte[ + glbBin.Length.Length + + glbBin.ChunkType().Length + + glbBin.ChunkData.Length]; - foreach (var index in bin.indexBuffer) - { - List indexIdBuffer = BitConverter.GetBytes((int)index).ToList(); - binData.AddRange(indexIdBuffer); - } - } + int offset = 0; - if (binData.Count % 4 != 0) - { - int missingNumbers = 4 - (binData.Count % 4); - for (int i = 0; i < missingNumbers; i++) - { - byte emptyByte = (byte)00; - List zeros = new List { emptyByte }; - binData.AddRange(zeros); - } - } + // Copiar chunk length + Buffer.BlockCopy(glbBin.Length, 0, result, offset, glbBin.Length.Length); + offset += glbBin.Length.Length; - GlbBin glbBin = new GlbBin(); - glbBin.ChunkData = binData.ToArray(); - glbBin.Length = BitConverter.GetBytes(Convert.ToUInt32(glbBin.ChunkData.Length)); + // Copiar chunk type + byte[] type = glbBin.ChunkType(); + Buffer.BlockCopy(type, 0, result, offset, type.Length); + offset += type.Length; - byte[] result = new byte[] { }; - result = result.Concat(glbBin.Length).ToArray(); - result = result.Concat(glbBin.ChunkType()).ToArray(); - result = result.Concat(glbBin.ChunkData).ToArray(); + // Copiar datos binarios + Buffer.BlockCopy(glbBin.ChunkData, 0, result, offset, glbBin.ChunkData.Length); return result; } diff --git a/Common_glTF_Exporter/Export/GlbFile.cs b/Common_glTF_Exporter/Export/GlbFile.cs index b86d25e..7ad56b9 100644 --- a/Common_glTF_Exporter/Export/GlbFile.cs +++ b/Common_glTF_Exporter/Export/GlbFile.cs @@ -6,16 +6,17 @@ using System.Windows.Forms; using Common_glTF_Exporter.Core; using Common_glTF_Exporter.Windows.MainWindow; +using glTF.Manipulator.GenericSchema; namespace Common_glTF_Exporter.Export { internal class GlbFile { - public static void Create(Preferences preferences, List binaryFileData, string json) + public static void Create(Preferences preferences, GLTFBinaryData binaryFileData, string json) { byte[] jsonChunk = GlbJsonInfo.Get(json); int lenggg = jsonChunk.Length; - byte[] binChunk = GlbBinInfo.Get(binaryFileData, preferences); + byte[] binChunk = GlbBinInfo.Get(binaryFileData); byte[] headerChunk = GlbHeaderInfo.Get(jsonChunk, binChunk); string fileDirectory = string.Concat(preferences.path, ".glb"); diff --git a/Common_glTF_Exporter/Export/GltfJson.cs b/Common_glTF_Exporter/Export/GltfJson.cs index 4fb8230..a420781 100644 --- a/Common_glTF_Exporter/Export/GltfJson.cs +++ b/Common_glTF_Exporter/Export/GltfJson.cs @@ -1,33 +1,39 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Autodesk.Revit.DB; -using Common_glTF_Exporter.Core; +using Common_glTF_Exporter.Utils; using Common_glTF_Exporter.Windows.MainWindow; +using glTF.Manipulator.GenericSchema; +using glTF.Manipulator.Schema; using Newtonsoft.Json; -using Revit_glTF_Exporter; +using System.Collections.Generic; +using System.Linq; +using Buffer = glTF.Manipulator.Schema.Buffer; namespace Common_glTF_Exporter.Export { public static class GltfJson { public static string Get( - List scenes, - List nodes, - List meshes, - List materials, - List buffers, - List bufferViews, - List accessors, - List textures, - List images, + List scenes, + List nodes, + List meshes, + List materials, + List buffers, + List bufferViews, + List accessors, + List textures, + List images, Preferences preferences) { + glTF.Manipulator.Schema.Version version = new glTF.Manipulator.Schema.Version + { + version = "2.0", + generator = string.Concat("e-verse custom generator ", SettingsConfig.currentVersion), + copyright = "free tool created by e-verse" + }; + GLTF model = new GLTF { - asset = new GLTFVersion(), + asset = version, scenes = scenes, nodes = nodes, meshes = meshes, @@ -38,9 +44,9 @@ public static string Get( model.extensionsUsed = new List { "KHR_texture_transform" }; } - if (materials.Any()) + if (materials.Any() && preferences.materials != MaterialsEnum.nonematerials) { - model.materials = materials; + model.materials = transformMaterials(materials); } if (preferences.materials == MaterialsEnum.textures) @@ -52,7 +58,7 @@ public static string Get( if (images.Any()) { - model.images = images; + model.images = cleanImages(images); } } @@ -60,12 +66,76 @@ public static string Get( model.bufferViews = bufferViews; model.accessors = accessors; - // Write the *.gltf file - string serializedModel = JsonConvert.SerializeObject( - model, - new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + // --------------------------- + // NEWTONSOFT JSON SETTINGS + // --------------------------- + var settings = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + Formatting = Newtonsoft.Json.Formatting.None + }; + string serializedModel = JsonConvert.SerializeObject(model, settings); return serializedModel; } + + public static List cleanImages(List images) + { + List resultImages = new List(); + foreach (glTFImage img in images) + { + glTFImage newImg = new glTFImage + { + bufferView = img.bufferView, + mimeType = img.mimeType + }; + + resultImages.Add(newImg); + } + + return resultImages; + } + + public static List transformMaterials(List baseMaterials) + { + List materials = new List(); + + foreach (BaseMaterial baseMat in baseMaterials) + { + glTF.Manipulator.Schema.Material mat = new glTF.Manipulator.Schema.Material(); + mat.alphaMode = baseMat.alphaMode; + mat.alphaCutoff = baseMat.alphaCutoff; + mat.name = baseMat.name; + mat.doubleSided = baseMat.doubleSided; + + PBR pbrMetallicRoughness = new PBR(); + pbrMetallicRoughness.metallicFactor = baseMat.metallicFactor; + pbrMetallicRoughness.baseColorFactor = baseMat.baseColorFactor; + pbrMetallicRoughness.roughnessFactor = baseMat.roughnessFactor; + + if (baseMat.hasTexture) + { + TextureInfo baseColorTexture = new TextureInfo(); + baseColorTexture.index = baseMat.textureIndex; + + KHR_texture_transform kHR_Texture_Transform = new KHR_texture_transform(); + kHR_Texture_Transform.rotation = baseMat.rotation; + kHR_Texture_Transform.scale = baseMat.scale; + kHR_Texture_Transform.offset = baseMat.offset; + + TextureExtensions extensions = new TextureExtensions(); + extensions.KHR_texture_transform = kHR_Texture_Transform; + + baseColorTexture.extensions = extensions; + pbrMetallicRoughness.baseColorTexture = baseColorTexture; + } + + mat.pbrMetallicRoughness = pbrMetallicRoughness; + + materials.Add(mat); + } + + return materials; + } } } diff --git a/Common_glTF_Exporter/Export/RevitGrids.cs b/Common_glTF_Exporter/Export/RevitGrids.cs index 57bd704..9b3a2c1 100644 --- a/Common_glTF_Exporter/Export/RevitGrids.cs +++ b/Common_glTF_Exporter/Export/RevitGrids.cs @@ -1,11 +1,13 @@ -namespace Common_glTF_Exporter.Export -{ - using System.Collections.Generic; - using Autodesk.Revit.DB; - using Common_glTF_Exporter.Core; - using Common_glTF_Exporter.Windows.MainWindow; - using Revit_glTF_Exporter; +using Autodesk.Revit.DB; +using Common_glTF_Exporter.Windows.MainWindow; +using glTF.Manipulator.Schema; +using glTF.Manipulator.Utils; +using Revit_glTF_Exporter; +using System.Collections.Generic; +using System.Globalization; +namespace Common_glTF_Exporter.Export +{ /// /// Revit grids. /// @@ -18,7 +20,7 @@ public static class RevitGrids /// Nodes. /// root node. /// preferences. - public static void Export(Document doc, ref IndexedDictionary nodes, ref GLTFNode rootNode, Preferences preferences) + public static void Export(Document doc, ref IndexedDictionary nodes, ref Node rootNode, Preferences preferences) { using (FilteredElementCollector col = new FilteredElementCollector(doc).OfClass(typeof(Grid))) { @@ -38,25 +40,33 @@ public static void Export(Document doc, ref IndexedDictionary nodes, r var direction = l.Direction; var length = l.Length; - var xtras = new GLTFExtras(); - var grid = new RevitGridParametersObject(); + Extras xtras = new Extras(); - grid.origin = new List() { origin.X, origin.Y, origin.Z }; + Dictionary parameters = preferences.properties + ? Util.GetElementParameters(g, true) ?? new Dictionary() + : new Dictionary(); - grid.direction = new List() { direction.X, direction.Y, direction.Z, }; - grid.length = length; + parameters["UniqueId"] = g.UniqueId; - xtras.gridParameters = grid; - xtras.uniqueId = g.UniqueId; + parameters["GridOrigin"] = string.Format( + CultureInfo.InvariantCulture, + "{0},{1},{2}", + origin.X, origin.Y, origin.Z); - if (preferences.properties) - { - xtras.parameters = Util.GetElementParameters(g, true); - } + parameters["GridDirection"] = string.Format( + CultureInfo.InvariantCulture, + "{0},{1},{2}", + direction.X, direction.Y, direction.Z); - var gridNode = new GLTFNode(); - gridNode.name = g.Name; - gridNode.extras = xtras; + parameters["GridLength"] = length.ToString(CultureInfo.InvariantCulture); + + xtras.parameters = parameters; + + Node gridNode = new Node + { + name = g.Name, + extras = xtras + }; nodes.AddOrUpdateCurrent(g.UniqueId, gridNode); rootNode.children.Add(nodes.CurrentIndex); diff --git a/Common_glTF_Exporter/ExternalApplication.cs b/Common_glTF_Exporter/ExternalApplication.cs index 0d1a130..61771a0 100644 --- a/Common_glTF_Exporter/ExternalApplication.cs +++ b/Common_glTF_Exporter/ExternalApplication.cs @@ -5,6 +5,7 @@ using Autodesk.Windows; using Common_glTF_Exporter; using Common_glTF_Exporter.Service; + using Common_glTF_Exporter.Version; using System; using System.IO; using System.Linq; @@ -25,6 +26,7 @@ public class ExternalApplication : IExternalApplication private static string pushButtonText = "Leia"; private static string addInPath = typeof(ExternalApplication).Assembly.Location; private static string buttonIconsFolder = Path.GetDirectoryName(addInPath) + "\\Images\\"; + public static string InstallerRoute; internal Document Document { get; set; } public static UIApplication UiApp { get; private set; } @@ -57,6 +59,10 @@ public static void CreateRibbonTab(UIControlledApplication application, string r /// Result. public Result OnShutdown(UIControlledApplication application) { + if (!string.IsNullOrEmpty(InstallerRoute)) + { + RunLocalFile.Action(InstallerRoute); + } return Result.Succeeded; } diff --git a/Common_glTF_Exporter/Materials/BitmapsUtils.cs b/Common_glTF_Exporter/Materials/BitmapsUtils.cs index b470f64..1d6f8ec 100644 --- a/Common_glTF_Exporter/Materials/BitmapsUtils.cs +++ b/Common_glTF_Exporter/Materials/BitmapsUtils.cs @@ -30,6 +30,28 @@ public static (string, ImageFormat) GetMimeType(string path) } } + /// + /// Removes non-default gamma or ICC profile metadata from a PNG/JPG file + /// by re-encoding it to a clean sRGB version in memory. + /// + /// Path to the source image + /// Byte array of the cleaned image (PNG) + public static byte[] CleanGamma(string path, ImageFormat imageFormat) + { + using (var original = new Bitmap(path)) + using (var ms = new MemoryStream()) + { + // Convert to standard sRGB (System.Drawing assumes sRGB by default) + using (var converted = new Bitmap(original.Width, original.Height, PixelFormat.Format24bppRgb)) + using (var g = Graphics.FromImage(converted)) + { + g.DrawImage(original, 0, 0, original.Width, original.Height); + converted.Save(ms, imageFormat); + } + return ms.ToArray(); + } + } + public static byte[] BlendImageWithColor( byte[] imageBytes, double fade, diff --git a/Common_glTF_Exporter/Materials/MaterialProperties.cs b/Common_glTF_Exporter/Materials/MaterialProperties.cs index b5de6fd..f74d389 100644 --- a/Common_glTF_Exporter/Materials/MaterialProperties.cs +++ b/Common_glTF_Exporter/Materials/MaterialProperties.cs @@ -1,9 +1,13 @@ using System; using System.Collections.Generic; using System.IO.Ports; +using System.Linq; using System.Xml.Linq; using Autodesk.Revit.DB; using Common_glTF_Exporter.Core; +using Common_glTF_Exporter.Model; +using glTF.Manipulator.GenericSchema; +using glTF.Manipulator.Schema; namespace Common_glTF_Exporter.Materials { @@ -11,52 +15,43 @@ public static class MaterialProperties { private const string BLEND = "BLEND"; private const string OPAQUE = "OPAQUE"; - public static void SetProperties(MaterialNode node, float opacity, ref GLTFPBR pbr, ref GLTFMaterial gl_mat) + public static void SetProperties(MaterialNode node, float opacity, ref BaseMaterial material) { - pbr.metallicFactor = 0f; - pbr.roughnessFactor = opacity != 1 ? 0.5f : 1f; - gl_mat.pbrMetallicRoughness = pbr; - - gl_mat.alphaMode = opacity != 1 ? BLEND : OPAQUE; - gl_mat.alphaCutoff = null; + material.metallicFactor = 0f; + material.roughnessFactor = opacity != 1 ? 0.5f : 1f; + material.alphaMode = opacity != 1 ? BLEND : OPAQUE; + material.alphaCutoff = null; } - public static void SetMaterialColour(MaterialNode node, - float opacity, ref GLTFPBR pbr, ref GLTFMaterial gl_mat) + public static List SetMaterialColour(MaterialNode node, + float opacity, Autodesk.Revit.DB.Color baseColor, Autodesk.Revit.DB.Color tintColor) { - if (gl_mat.EmbeddedTexturePath == null) - { + (float, float, float) baseColours; - if (gl_mat.BaseColor == null) + if (baseColor == null) { baseColours = RgbToUnit(node.Color); } else { - baseColours = RgbToUnit(gl_mat.BaseColor); + baseColours = RgbToUnit(baseColor); } - if (gl_mat.TintColour == null) + if (tintColor == null) { - pbr.baseColorFactor = GetLinearColour(baseColours, opacity); + return GetLinearColour(baseColours, opacity); } else { - (float, float, float) baseTintColour = RgbToUnit(gl_mat.TintColour); + (float, float, float) baseTintColour = RgbToUnit(tintColor); (float, float, float) blendColour = BlendColour(baseColours, baseTintColour); - pbr.baseColorFactor = GetLinearColour(blendColour, opacity); + return GetLinearColour(blendColour, opacity); } - } - else - { - gl_mat.pbrMetallicRoughness.baseColorFactor = GetDefaultColour(opacity); - } - - gl_mat.pbrMetallicRoughness = pbr; } + public static List GetDefaultColour(float opacity) { return new List(4) { 1, 1, 1, opacity }; diff --git a/Common_glTF_Exporter/Materials/MaterialTexture.cs b/Common_glTF_Exporter/Materials/MaterialTexture.cs new file mode 100644 index 0000000..cc9ec91 --- /dev/null +++ b/Common_glTF_Exporter/Materials/MaterialTexture.cs @@ -0,0 +1,12 @@ +namespace Common_glTF_Exporter.Core +{ + public class MaterialTexture + { + public string EmbeddedTexturePath { get; set; } = null; + public double Fadevalue { get; set; } = 1; + + public Autodesk.Revit.DB.Color TintColour { get; set; } + + public Autodesk.Revit.DB.Color BaseColor { get; set; } + } +} diff --git a/Common_glTF_Exporter/Materials/MaterialTextures.cs b/Common_glTF_Exporter/Materials/MaterialTextures.cs index 74d0455..c162a6b 100644 --- a/Common_glTF_Exporter/Materials/MaterialTextures.cs +++ b/Common_glTF_Exporter/Materials/MaterialTextures.cs @@ -1,66 +1,71 @@ -using System.Collections.Generic; -using System.IO; -using Autodesk.Revit.DB; +using Autodesk.Revit.DB; using Autodesk.Revit.DB.Visual; using Common_glTF_Exporter.Core; -using Common_glTF_Exporter.Windows.MainWindow; -using Revit_glTF_Exporter; -using Common_glTF_Exporter.Materials; using Common_glTF_Exporter.Model; -using System.IO.Ports; -using System.Windows.Controls; -using System.Windows.Media.Media3D; +using glTF.Manipulator.GenericSchema; +using glTF.Manipulator.Schema; +using Revit_glTF_Exporter; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Windows.Interop; using Material = Autodesk.Revit.DB.Material; -using System; -using Common_glTF_Exporter.Utils; -using System.Diagnostics; namespace Common_glTF_Exporter.Materials { public static class MaterialTextures { - public static GLTFMaterial SetMaterialTextures(Material material, GLTFMaterial gl_mat, - Document doc, float opacity) + public static (Autodesk.Revit.DB.Color, Autodesk.Revit.DB.Color) SetMaterialTextures(Material revitMaterial, BaseMaterial material, + Document doc, float opacity, List textures, List images) { - ElementId appearanceId = material.AppearanceAssetId; + ElementId appearanceId = revitMaterial.AppearanceAssetId; if (appearanceId == ElementId.InvalidElementId) { - return gl_mat; + return (null,null); } var appearanceElem = doc.GetElement(appearanceId) as AppearanceAssetElement; if (appearanceElem == null) { - return gl_mat; + return (null, null); } Asset theAsset = appearanceElem.GetRenderingAsset(); AssetPropertyString baseSchema = theAsset.FindByName("BaseSchema") as AssetPropertyString; if (baseSchema == null) { - return gl_mat; + return (null, null); } string schemaName = baseSchema.Value; Asset connectedAsset = AssetPropertiesUtils.GetDiffuseBitmap(theAsset, schemaName); string texturePath = AssetPropertiesUtils.GetTexturePath(connectedAsset); - gl_mat.TintColour = AssetPropertiesUtils.GetTint(theAsset); - gl_mat.BaseColor = AssetPropertiesUtils.GetAppearanceColor(theAsset, schemaName); + Autodesk.Revit.DB.Color tintColour = AssetPropertiesUtils.GetTint(theAsset); + Autodesk.Revit.DB.Color baseColor = AssetPropertiesUtils.GetAppearanceColor(theAsset, schemaName); + double Fadevalue = AssetPropertiesUtils.GetFade(theAsset); - if (!string.IsNullOrEmpty(texturePath) && File.Exists(texturePath)) + if (!string.IsNullOrEmpty(texturePath)) { - SetTextureProperties(gl_mat, texturePath, connectedAsset, theAsset, opacity); + int indexImage = createOrGetBaseImage(tintColour, baseColor, Fadevalue, texturePath, images); + + Texture baseTexture = createTexture(material, texturePath, connectedAsset, theAsset, opacity, indexImage); + textures.Add(baseTexture); + + material.hasTexture = true; + material.textureIndex = textures.Count - 1; } - return gl_mat; + return (baseColor, tintColour); } - private static void SetTextureProperties(GLTFMaterial gl_mat, string texturePath, Asset connectedAsset, - Asset theAsset, float opacity) + private static Texture createTexture(BaseMaterial material, string texturePath, Asset connectedAsset, + Asset theAsset, float opacity, int imageIndex) { - gl_mat.EmbeddedTexturePath = texturePath; + + Texture texture = new Texture(); float scaleX = AssetPropertiesUtils.GetScale(connectedAsset, UnifiedBitmap.TextureRealWorldScaleX); float scaleY = AssetPropertiesUtils.GetScale(connectedAsset, UnifiedBitmap.TextureRealWorldScaleY); @@ -68,8 +73,6 @@ private static void SetTextureProperties(GLTFMaterial gl_mat, string texturePath float offsetY = AssetPropertiesUtils.GetOffset(connectedAsset, UnifiedBitmap.TextureRealWorldOffsetY); float rotation = AssetPropertiesUtils.GetRotationRadians(connectedAsset); - gl_mat.Fadevalue = AssetPropertiesUtils.GetFade(theAsset); - float[] gltfScale = new float[] { 1f / scaleX, 1f / scaleY }; float[] gltfOffset = new float[] { @@ -77,19 +80,40 @@ private static void SetTextureProperties(GLTFMaterial gl_mat, string texturePath offsetY / scaleY - gltfScale[1] }; - gl_mat.pbrMetallicRoughness.baseColorTexture = new GLTFTextureInfo + material.offset = gltfOffset; + material.rotation = rotation; + material.scale = gltfScale; + texture.source = imageIndex; + + return texture; + } + + private static int createOrGetBaseImage(Autodesk.Revit.DB.Color TintColour, Autodesk.Revit.DB.Color BaseColor, double Fadevalue, string texturePath, List images) + { + + bool checkIfImageExists = images.Any(x => x.uuid == texturePath); + + if (checkIfImageExists) { - index = -1, - extensions = new GLTFTextureExtensions - { - TextureTransform = new GLTFTextureTransform - { - offset = gltfOffset, - scale = gltfScale, - rotation = rotation - } - } - }; + int index = images.FindIndex(x => x.uuid == texturePath); + + return index; + } + else + { + glTFImage Image = new glTFImage(); + Image.uuid = texturePath; + (string, ImageFormat) mimeType = BitmapsUtils.GetMimeType(texturePath); + Image.mimeType = mimeType.Item1; + byte[] imageBytes = BitmapsUtils.CleanGamma(texturePath, mimeType.Item2); + byte[] blendedBytes = BitmapsUtils.BlendImageWithColor(imageBytes, Fadevalue, + BaseColor, mimeType.Item2, TintColour); + Image.imageData = blendedBytes; + images.Add(Image); + int index = images.Count - 1; + + return index; + } } } } diff --git a/Common_glTF_Exporter/Materials/RevitMaterials.cs b/Common_glTF_Exporter/Materials/RevitMaterials.cs index 23e20a5..f1e9208 100644 --- a/Common_glTF_Exporter/Materials/RevitMaterials.cs +++ b/Common_glTF_Exporter/Materials/RevitMaterials.cs @@ -11,6 +11,10 @@ using System.Windows.Controls; using System.Windows.Media.Media3D; using Material = Autodesk.Revit.DB.Material; +using Common_glTF_Exporter.Utils; +using glTF.Manipulator.Schema; +using glTF.Manipulator.GenericSchema; +using glTF.Manipulator.Utils; namespace Common_glTF_Exporter.Export @@ -19,37 +23,69 @@ public static class RevitMaterials { const int ONEINTVALUE = 1; - /// - /// Export Revit materials. - /// - public static GLTFMaterial Export(MaterialNode node, - Preferences preferences, Document doc) + public static BaseMaterial ProcessMaterial(MaterialNode node, + Preferences preferences, Document doc, IndexedDictionary materials, + List textures, List images) { - GLTFMaterial gl_mat = new GLTFMaterial(); - float opacity = ONEINTVALUE - (float)node.Transparency; + BaseMaterial material = new BaseMaterial(); + string materialId = node.MaterialId.ToString(); + material.uuid = materialId; - Material material = doc.GetElement(node.MaterialId) as Material; + if (materials.Contains(materialId)) + { + material = materials.GetElement(materialId); + } + else + { + Autodesk.Revit.DB.Material revitMaterial = doc.GetElement(node.MaterialId) as Autodesk.Revit.DB.Material; - if (material == null) + if (revitMaterial == null) + { + material = GLTFExportUtils.GetGLTFMaterial(materials); + } + else { - return gl_mat; + material = RevitMaterials.Export(node, preferences, doc, revitMaterial, textures, images, material); } + } + materials.AddOrUpdateCurrentMaterial(material.uuid, material, false); - gl_mat.name = material.Name; - gl_mat.UniqueId = node.MaterialId.ToString(); + return material; + } - GLTFPBR pbr = new GLTFPBR(); - MaterialProperties.SetProperties(node, opacity, ref pbr, ref gl_mat); - if (material != null && preferences.materials == MaterialsEnum.textures) + /// + /// Export Revit materials. + /// + public static BaseMaterial Export(MaterialNode node, + Preferences preferences, Document doc, + Material revitMaterial, List textures, + List images, BaseMaterial material) + { + + float opacity = ONEINTVALUE - (float)node.Transparency; + + material.name = revitMaterial.Name; + MaterialProperties.SetProperties(node, opacity, ref material); + + (Autodesk.Revit.DB.Color, Autodesk.Revit.DB.Color) baseNTintColour = (null, null); + + if (revitMaterial != null && preferences.materials == MaterialsEnum.textures) { - MaterialTextures.SetMaterialTextures(material, gl_mat, doc, opacity); + baseNTintColour = MaterialTextures.SetMaterialTextures(revitMaterial, material, doc, opacity, textures, images); + material.baseColorFactor = MaterialProperties.GetDefaultColour(opacity); } - MaterialProperties.SetMaterialColour(node, opacity, ref pbr, ref gl_mat); - + if (material.hasTexture) + { + material.baseColorFactor = MaterialProperties.GetDefaultColour(opacity); + } + else + { + material.baseColorFactor = MaterialProperties.SetMaterialColour(node, opacity, baseNTintColour.Item1, baseNTintColour.Item2); + } - return gl_mat; + return material; } } } diff --git a/Common_glTF_Exporter/Model/GeometryDataObject.cs b/Common_glTF_Exporter/Model/GeometryDataObject.cs deleted file mode 100644 index 2d0158a..0000000 --- a/Common_glTF_Exporter/Model/GeometryDataObject.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Common_glTF_Exporter.Model -{ - using Autodesk.Revit.DB; - using System.Collections.Generic; - - /// - /// Intermediate data format for converting between Revit Polymesh and glTF buffers. - /// - public class GeometryDataObject - { - public List Vertices { get; set; } = new List(); - - public List Normals { get; set; } = new List(); - - public List Uvs { get; set; } = new List(); - - public List Faces { get; set; } = new List(); - - } -} diff --git a/Common_glTF_Exporter/Utils/GLTFBinaryDataUtils.cs b/Common_glTF_Exporter/Utils/GLTFBinaryDataUtils.cs index 0039ad4..f6b283a 100644 --- a/Common_glTF_Exporter/Utils/GLTFBinaryDataUtils.cs +++ b/Common_glTF_Exporter/Utils/GLTFBinaryDataUtils.cs @@ -1,275 +1,345 @@ -using System.Drawing.Imaging; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Common_glTF_Exporter.Core; +using Common_glTF_Exporter.Core; using Common_glTF_Exporter.Model; +using glTF.Manipulator.GenericSchema; +using glTF.Manipulator.Schema; using Revit_glTF_Exporter; -using Common_glTF_Exporter.Materials; +using System.Collections.Generic; +using System.Linq; namespace Common_glTF_Exporter.Utils { public class GLTFBinaryDataUtils { - const string SCALAR_STR = "SCALAR"; - const string FACE_STR = "FACE"; - const string BATCH_ID_STR = "BATCH_ID"; - - public static int ExportFaces(int bufferIdx, int byteOffset, GeometryDataObject geomData, GLTFBinaryData bufferData, List bufferViews, List accessors) + public static int ExportFaces( + GeometryDataObject geomData, + GLTFBinaryData bufferData, + List bufferViews, + List accessors) { - foreach (var index in geomData.Faces) - { - bufferData.indexBuffer.Add(index); - } - - // Get max and min for index data - int[] faceMinMax = Util.GetScalarMinMax(bufferData.indexBuffer); - - // Add a faces / indexes buffer view - int elementsPerIndex = 1; - int bytesPerIndexElement = 4; - int bytesPerIndex = elementsPerIndex * bytesPerIndexElement; - int numIndexes = geomData.Faces.Count; - int sizeOfIndexView = numIndexes * bytesPerIndex; - GLTFBufferView facesView = new GLTFBufferView(bufferIdx, byteOffset, sizeOfIndexView, Targets.ELEMENT_ARRAY_BUFFER, string.Empty); - bufferViews.Add(facesView); - int facesViewIdx = bufferViews.Count - 1; - - // add a face accessor - var count = geomData.Faces.Count / elementsPerIndex; - var max = new List(1) { faceMinMax[1] }; - var min = new List(1) { faceMinMax[0] }; - GLTFAccessor faceAccessor = new GLTFAccessor(facesViewIdx, 0, ComponentType.UNSIGNED_INT, count, SCALAR_STR, max, min, FACE_STR); - accessors.Add(faceAccessor); - bufferData.indexAccessorIndex = accessors.Count - 1; - return byteOffset + facesView.byteLength; + const string SCALAR_STR = "SCALAR"; + const string FACE_STR = "FACE"; + + int indexCount = geomData.Faces.Count; + if (indexCount == 0) + return -1; + + int[] indices = geomData.Faces.ToArray(); + + List listIndicess = new List(indices); + int[] minMax = Util.GetScalarMinMax(listIndicess); + + var max = new List { minMax[1] }; + var min = new List { minMax[0] }; + + int byteOffset = bufferData.AppendIntArray(indices); + int byteLength = indices.Length * sizeof(int); + + BufferView view = new BufferView( + buffer: 0, + byteOffset: byteOffset, + byteLength: byteLength, + Targets.ELEMENT_ARRAY_BUFFER, + name: "" + ); + + bufferViews.Add(view); + int viewIdx = bufferViews.Count - 1; + + Accessor accessor = new Accessor( + bufferView: viewIdx, + byteOffset: 0, + componentType: ComponentType.UNSIGNED_INT, // 5125 + count: indexCount, + type: SCALAR_STR, + max: max, + min: min, + name: FACE_STR + ); + + accessors.Add(accessor); + int accessorIdx = accessors.Count - 1; + + return accessorIdx; } + const string VEC3_STR = "VEC3"; const string POSITION_STR = "POSITION"; - public static int ExportVertices(int bufferIdx, int byteOffset, GeometryDataObject geomData, GLTFBinaryData bufferData, List bufferViews, List accessors, out int sizeOfVec3View, out int elementsPerVertex) + public static int ExportVertices( + GeometryDataObject geomData, + GLTFBinaryData bufferData, + List bufferViews, + List accessors, + MeshPrimitive primitive) { + const string VEC3_STR = "VEC3"; + const string POSITION_STR = "POSITION"; - for (int i = 0; i < geomData.Vertices.Count; i++) - { - bufferData.vertexBuffer.Add(Convert.ToSingle(geomData.Vertices[i])); - } + int vertexCount = geomData.Vertices.Count / 3; + if (vertexCount == 0) + return -1; - // Get max and min for vertex data - float[] vertexMinMax = Util.GetVec3MinMax(bufferData.vertexBuffer); - - // Add a vec3 buffer view - elementsPerVertex = 3; - int bytesPerElement = 4; - int bytesPerVertex = elementsPerVertex * bytesPerElement; - int numVec3 = geomData.Vertices.Count / elementsPerVertex; - sizeOfVec3View = numVec3 * bytesPerVertex; - - GLTFBufferView vec3View = new GLTFBufferView(bufferIdx, byteOffset, sizeOfVec3View, Targets.ARRAY_BUFFER, string.Empty); - bufferViews.Add(vec3View); - int vec3ViewIdx = bufferViews.Count - 1; - - // add a position accessor - int count = geomData.Vertices.Count / elementsPerVertex; - var max = new List(3) { vertexMinMax[1], vertexMinMax[3], vertexMinMax[5] }; - var min = new List(3) { vertexMinMax[0], vertexMinMax[2], vertexMinMax[4] }; - - GLTFAccessor positionAccessor = new GLTFAccessor(vec3ViewIdx, 0, ComponentType.FLOAT, count, VEC3_STR, max, min, POSITION_STR); - accessors.Add(positionAccessor); - bufferData.vertexAccessorIndex = accessors.Count - 1; - return byteOffset + vec3View.byteLength; + // Convert to float array + float[] vertexFloats = new float[geomData.Vertices.Count]; + for (int i = 0; i < geomData.Vertices.Count; i++) + vertexFloats[i] = (float)geomData.Vertices[i]; + + // Compute min/max + float[] minMax = Util.GetVec3MinMax(vertexFloats); + var max = new List { minMax[1], minMax[3], minMax[5] }; + var min = new List { minMax[0], minMax[2], minMax[4] }; + + // Write to buffer (correct padding applied) + int byteOffset = bufferData.AppendFloatArray(vertexFloats); + int byteLength = vertexFloats.Length * sizeof(float); + + // Create bufferView + var view = new BufferView( + buffer: 0, + byteOffset: byteOffset, + byteLength: byteLength, + Targets.ARRAY_BUFFER, + name: "" + ); + + bufferViews.Add(view); + int bufferViewIndex = bufferViews.Count - 1; + + // Create accessor + var accessor = new Accessor( + bufferView: bufferViewIndex, + byteOffset: 0, + componentType: ComponentType.FLOAT, + count: vertexCount, + type: VEC3_STR, + max: max, + min: min, + name: POSITION_STR + ); + + accessors.Add(accessor); + int accessorIndex = accessors.Count - 1; + + // Assign accessor to primitive + primitive.attributes.POSITION = accessorIndex; + + return accessorIndex; } + + const string NORMAL_STR = "NORMALS"; - public static int ExportNormals(int bufferIdx, int byteOffset, GeometryDataObject geomData, GLTFBinaryData bufferData, List bufferViews, List accessors) + public static int ExportNormals( + GeometryDataObject geomData, + GLTFBinaryData bufferData, + List bufferViews, + List accessors, + MeshPrimitive primitive) { - for (int i = 0; i < geomData.Normals.Count; i++) - { - bufferData.normalBuffer.Add(Convert.ToSingle(geomData.Normals[i])); - } - - // Get max and min for normal data - float[] normalMinMax = Util.GetVec3MinMax(bufferData.normalBuffer); - - // Add a normals (vec3) buffer view - int elementsPerNormal = 3; - int bytesPerNormalElement = 4; - int bytesPerNormal = elementsPerNormal * bytesPerNormalElement; - var normalsCount = geomData.Normals.Count; - int numVec3Normals = normalsCount / elementsPerNormal; - int sizeOfVec3ViewNormals = numVec3Normals * bytesPerNormal; - GLTFBufferView vec3ViewNormals = new GLTFBufferView(bufferIdx, byteOffset, sizeOfVec3ViewNormals, Targets.ARRAY_BUFFER, string.Empty); - bufferViews.Add(vec3ViewNormals); - int vec3ViewNormalsIdx = bufferViews.Count - 1; - - // add a normals accessor - var count = normalsCount / elementsPerNormal; - var max = new List(3) { normalMinMax[1], normalMinMax[3], normalMinMax[5] }; - var min = new List(3) { normalMinMax[0], normalMinMax[2], normalMinMax[4] }; - - GLTFAccessor normalsAccessor = new GLTFAccessor(vec3ViewNormalsIdx, 0, ComponentType.FLOAT, count, VEC3_STR, max, min, NORMAL_STR); - accessors.Add(normalsAccessor); - bufferData.normalsAccessorIndex = accessors.Count - 1; - return byteOffset + vec3ViewNormals.byteLength; + const string VEC3_STR = "VEC3"; + const string NORMAL_STR = "NORMAL"; + + int normalCount = geomData.Normals.Count; + if (normalCount == 0) + return -1; + + // Convert normals to float[] + float[] normals = geomData.Normals + .Select(n => (float)n) + .ToArray(); + + // Min/Max SOLO para estas normales + float[] minMax = Util.GetVec3MinMax(normals); + + var max = new List { minMax[1], minMax[3], minMax[5] }; + var min = new List { minMax[0], minMax[2], minMax[4] }; + + // Append normals al buffer global (float32) + int byteOffset = bufferData.AppendFloatArray(normals); + int byteLength = normals.Length * sizeof(float); + + // Crear bufferView + BufferView view = new BufferView( + buffer: 0, + byteOffset: byteOffset, + byteLength: byteLength, + Targets.ARRAY_BUFFER, + name: "" + ); + + bufferViews.Add(view); + int viewIdx = bufferViews.Count - 1; + + // Crear accessor + int count = normalCount / 3; + + Accessor accessor = new Accessor( + bufferView: viewIdx, + byteOffset: 0, + componentType: ComponentType.FLOAT, + count: count, + type: VEC3_STR, + max: max, + min: min, + name: NORMAL_STR + ); + + accessors.Add(accessor); + int accessorIdx = accessors.Count - 1; + + // Asignar al primitive + primitive.attributes.NORMAL = accessorIdx; + + return accessorIdx; } - public static int ExportBatchId(int bufferIdx, int byteOffset, int sizeOfVec3View, int elementsPerVertex, long elementId, GeometryDataObject geomData, GLTFBinaryData bufferData, List bufferViews, List accessors) + public static int ExportBatchId( + long elementId, + GeometryDataObject geomData, + GLTFBinaryData bufferData, + List bufferViews, + List accessors, + MeshPrimitive primitive) { - for (int i = 0; i < geomData.Vertices.Count; i++) - { - bufferData.batchIdBuffer.Add(elementId); - } - - // Get max and min for batchId data - float[] batchIdMinMax = Util.GetVec3MinMax(bufferData.batchIdBuffer); - - // Add a batchId buffer view - GLTFBufferView batchIdsView = new GLTFBufferView(bufferIdx, byteOffset, sizeOfVec3View, Targets.ARRAY_BUFFER, string.Empty); - bufferViews.Add(batchIdsView); - int batchIdsViewIdx = bufferViews.Count - 1; - - // add a batchId accessor - var count = geomData.Vertices.Count / elementsPerVertex; - var max = new List(3) { batchIdMinMax[1], batchIdMinMax[3], batchIdMinMax[5] }; - var min = new List(3) { batchIdMinMax[0], batchIdMinMax[2], batchIdMinMax[4] }; - GLTFAccessor batchIdAccessor = new GLTFAccessor(batchIdsViewIdx, 0, ComponentType.FLOAT, count, VEC3_STR, max, min, BATCH_ID_STR); - accessors.Add(batchIdAccessor); - bufferData.batchIdAccessorIndex = accessors.Count - 1; - return byteOffset + batchIdsView.byteLength; + const string SCALAR_STR = "SCALAR"; + const string BATCH_ID_STR = "_BATCHID"; + + int vertexCount = geomData.Vertices.Count / 3; + if (vertexCount == 0) + return -1; + + float idValue = (float)elementId; + float[] batchIds = Enumerable.Repeat(idValue, vertexCount).ToArray(); + + var min = new List { idValue }; + var max = new List { idValue }; + + int byteOffset = bufferData.AppendFloatArray(batchIds); + int byteLength = batchIds.Length * sizeof(float); + + BufferView view = new BufferView( + buffer: 0, + byteOffset: byteOffset, + byteLength: byteLength, + Targets.ARRAY_BUFFER, + name: "" + ); + + bufferViews.Add(view); + int viewIdx = bufferViews.Count - 1; + + Accessor accessor = new Accessor( + bufferView: viewIdx, + byteOffset: 0, + componentType: ComponentType.FLOAT, + count: vertexCount, + type: SCALAR_STR, + max: max, + min: min, + name: BATCH_ID_STR + ); + + accessors.Add(accessor); + int accessorIdx = accessors.Count - 1; + + primitive.attributes._BATCHID = accessorIdx; + + return accessorIdx; } + public static int ExportUVs( - int bufferIdx, - int byteOffset, - GeometryDataObject geomData, - GLTFBinaryData bufferData, - List bufferViews, - List accessors) + GeometryDataObject geomData, + GLTFBinaryData bufferData, + List bufferViews, + List accessors, + MeshPrimitive primitive) { const string VEC2_STR = "VEC2"; const string TEXCOORD_STR = "TEXCOORD_0"; int uvCount = geomData.Uvs.Count; + if (uvCount == 0) + return -1; + + float[] uvFloats = new float[uvCount * 2]; + int ptr = 0; - // Convert UVs to float buffer (U, V per entry) foreach (var uv in geomData.Uvs) { - bufferData.uvBuffer.Add((float)uv.U); - bufferData.uvBuffer.Add((float)uv.V); + uvFloats[ptr++] = uv.U; + uvFloats[ptr++] = uv.V; } - int elementsPerUV = 2; - int bytesPerUVElement = 4; - int bytesPerUV = elementsPerUV * bytesPerUVElement; + List listUvFloats = new List(uvFloats); + float[] uvMinMax = Util.GetVec2MinMax(listUvFloats); + + var max = new List { uvMinMax[1], uvMinMax[3] }; + var min = new List { uvMinMax[0], uvMinMax[2] }; + + int byteOffset = bufferData.AppendFloatArray(uvFloats); + int byteLength = uvFloats.Length * sizeof(float); - int sizeOfUVView = uvCount * bytesPerUV; + BufferView uvBufferView = new BufferView( + buffer: 0, + byteOffset: byteOffset, + byteLength: byteLength, + Targets.ARRAY_BUFFER, + name: "" + ); - // Create UV buffer view - GLTFBufferView uvBufferView = new GLTFBufferView(bufferIdx, byteOffset, sizeOfUVView, Targets.ARRAY_BUFFER, string.Empty); bufferViews.Add(uvBufferView); - int uvBufferViewIdx = bufferViews.Count - 1; + int viewIdx = bufferViews.Count - 1; - // Min/max for VEC2 accessors (optional, but good practice) - float[] uvMinMax = Util.GetVec2MinMax(bufferData.uvBuffer); - var max = new List { uvMinMax[1], uvMinMax[3] }; - var min = new List { uvMinMax[0], uvMinMax[2] }; + Accessor accessor = new Accessor( + bufferView: viewIdx, + byteOffset: 0, + componentType: ComponentType.FLOAT, + count: uvCount, + type: VEC2_STR, + max: max, + min: min, + name: TEXCOORD_STR + ); + + accessors.Add(accessor); + int accessorIdx = accessors.Count - 1; - // Create UV accessor - GLTFAccessor uvAccessor = new GLTFAccessor( - uvBufferViewIdx, - 0, - ComponentType.FLOAT, - uvCount, - VEC2_STR, - max, - min, - TEXCOORD_STR); - - accessors.Add(uvAccessor); - bufferData.uvAccessorIndex = accessors.Count - 1; - - return byteOffset + uvBufferView.byteLength; + primitive.attributes.TEXCOORD_0 = accessorIdx; + + return accessorIdx; } - public static int ExportImageBuffer( - int bufferIdx, - int byteOffset, - GLTFMaterial material, - List images, - List textures, - GLTFBinaryData bufferData, - List bufferViews) + public static void ExportImageBuffer( + List images, + List bufferViews, + GLTFBinaryData bufferData) { - if (material.EmbeddedTexturePath == null) - { - return byteOffset; - } - - if (material.pbrMetallicRoughness.baseColorTexture.index == -1) - { - byte[] imageBytes = File.ReadAllBytes(material.EmbeddedTexturePath); - (string , ImageFormat) mimeType = BitmapsUtils.GetMimeType(material.EmbeddedTexturePath); - - byte[] blendedBytes = BitmapsUtils.BlendImageWithColor(imageBytes, material.Fadevalue, - material.BaseColor, mimeType.Item2, material.TintColour); - - if (blendedBytes != null) - { - if (bufferData.byteData == null) - { - bufferData.byteData = blendedBytes; - } - else - { - byte[] combined = new byte[bufferData.byteData.Length + blendedBytes.Length]; - Buffer.BlockCopy(bufferData.byteData, 0, combined, 0, bufferData.byteData.Length); - Buffer.BlockCopy(blendedBytes, 0, combined, bufferData.byteData.Length, blendedBytes.Length); - bufferData.byteData = combined; - } - } - - int currentLenght = blendedBytes.Length; - int alignment = 4; - int padding = (alignment - (currentLenght % alignment)) % alignment; - - if (padding != 0) - { - currentLenght = currentLenght + padding; - - byte[] newArray = bufferData.byteData.Concat(new byte[padding]).ToArray(); - bufferData.byteData = newArray; - } - - GLTFBufferView ImageBufferView = new GLTFBufferView(bufferIdx, byteOffset, currentLenght, Targets.NONE, string.Empty); - - bufferViews.Add(ImageBufferView); - int bufferViewIndex = bufferViews.Count - 1; - - var image = new GLTFImage - { - bufferView = bufferViewIndex, - mimeType = mimeType.Item1 - }; - - images.Add(image); - int imageIndex = images.Count - 1; - - var texture = new GLTFTexture - { - source = imageIndex - }; - textures.Add(texture); - int textureIndex = textures.Count - 1; - material.pbrMetallicRoughness.baseColorTexture.index = textureIndex; - return byteOffset + ImageBufferView.byteLength; - } - else + foreach (var baseImage in images) { - return byteOffset; + byte[] imgBytes = baseImage.imageData; + if (imgBytes == null || imgBytes.Length == 0) + continue; + + int byteOffset = bufferData.GetCurrentByteOffset(); + int byteLength = bufferData.Append(imgBytes); + + BufferView imgView = new BufferView( + buffer: 0, + byteOffset: byteOffset, + byteLength: byteLength, + Targets.NONE, + name: "" + ); + + bufferViews.Add(imgView); + int viewIdx = bufferViews.Count - 1; + + baseImage.bufferView = viewIdx; + baseImage.uri = null; } } } diff --git a/Common_glTF_Exporter/Utils/MaterialUtils.cs b/Common_glTF_Exporter/Utils/MaterialUtils.cs index 0a8e60a..d559e4c 100644 --- a/Common_glTF_Exporter/Utils/MaterialUtils.cs +++ b/Common_glTF_Exporter/Utils/MaterialUtils.cs @@ -2,19 +2,23 @@ { using Autodesk.Revit.DB; using Common_glTF_Exporter.Core; + using Common_glTF_Exporter.Model; using Common_glTF_Exporter.Windows.MainWindow; + using glTF.Manipulator.GenericSchema; + using glTF.Manipulator.Schema; + using glTF.Manipulator.Utils; using Revit_glTF_Exporter; using System.Collections.Generic; public class MaterialUtils { - public static Material GetMeshMaterial(Document doc, Mesh mesh) + public static Autodesk.Revit.DB.Material GetMeshMaterial(Document doc, Autodesk.Revit.DB.Mesh mesh) { ElementId materialId = mesh.MaterialElementId; if (materialId != null) { - return doc.GetElement(materialId) as Material; + return doc.GetElement(materialId) as Autodesk.Revit.DB.Material; } else { @@ -22,29 +26,27 @@ public static Material GetMeshMaterial(Document doc, Mesh mesh) } } - public static GLTFMaterial GetGltfMeshMaterial(Document doc, Preferences preferences, Mesh mesh, IndexedDictionary materials, bool doubleSided) + public static BaseMaterial GetGltfMeshMaterial(Document doc, Preferences preferences, Autodesk.Revit.DB.Mesh mesh, IndexedDictionary materials, bool doubleSided) { - GLTFMaterial gl_mat = new GLTFMaterial(); + BaseMaterial gl_mat = new BaseMaterial(); - Material material = GetMeshMaterial(doc, mesh); + Autodesk.Revit.DB.Material material = GetMeshMaterial(doc, mesh); if (preferences.materials == MaterialsEnum.materials || preferences.materials == MaterialsEnum.textures) { if (material == null) { - gl_mat = GLTFExportUtils.GetGLTFMaterial(materials, 1, doubleSided); + gl_mat = GLTFExportUtils.GetGLTFMaterial(materials); } else { gl_mat.doubleSided = doubleSided; float opacity = 1 - (float)material.Transparency; gl_mat.name = material.Name; - GLTFPBR pbr = new GLTFPBR(); - pbr.baseColorFactor = new List(4) { material.Color.Red / 255f, material.Color.Green / 255f, material.Color.Blue / 255f, opacity }; - pbr.metallicFactor = 0f; - pbr.roughnessFactor = 1f; - gl_mat.pbrMetallicRoughness = pbr; - gl_mat.UniqueId = material.UniqueId; + + gl_mat.baseColorFactor = new List(4) { material.Color.Red / 255f, material.Color.Green / 255f, material.Color.Blue / 255f, opacity }; + gl_mat.metallicFactor = 0f; + gl_mat.roughnessFactor = 1f; } } diff --git a/Common_glTF_Exporter/Utils/glTFExportUtils.cs b/Common_glTF_Exporter/Utils/glTFExportUtils.cs index 476bcf8..e170f4e 100644 --- a/Common_glTF_Exporter/Utils/glTFExportUtils.cs +++ b/Common_glTF_Exporter/Utils/glTFExportUtils.cs @@ -1,47 +1,40 @@ - namespace Common_glTF_Exporter.Utils - { - using System; - using System.Collections.Generic; - using System.Linq; - using Autodesk.Revit.DB; - using Common_glTF_Exporter.Core; - using Common_glTF_Exporter.Model; - using Common_glTF_Exporter.Windows.MainWindow; - using Revit_glTF_Exporter; - - public class GLTFExportUtils +namespace Common_glTF_Exporter.Utils +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Autodesk.Revit.DB; + using Common_glTF_Exporter.Core; + using Common_glTF_Exporter.Model; + using Common_glTF_Exporter.Windows.MainWindow; + using glTF.Manipulator.GenericSchema; + using glTF.Manipulator.Schema; + using glTF.Manipulator.Utils; + using Revit_glTF_Exporter; + using Buffer = glTF.Manipulator.Schema.Buffer; + public class GLTFExportUtils { const int DEF_COLOR = 250; const string DEF_MATERIAL_NAME = "default"; - const string DEF_UNIQUEL_ID = "8a3c94b3-d9e2-4e57-9189-f9bb6a9a54a4"; + public const string DEF_UNIQUEL_ID = "8a3c94b3-d9e2-4e57-9189-f9bb6a9a54a4"; - public static GLTFMaterial GetGLTFMaterial(IndexedDictionary gltfMaterials, double opacity, bool doubleSided) + public static BaseMaterial GetGLTFMaterial(IndexedDictionary Materials) { - - if (gltfMaterials.Dict.ContainsKey(DEF_UNIQUEL_ID)) - { - return gltfMaterials.GetElement(DEF_UNIQUEL_ID); - } - else - { - return (CreateDefaultGLTFMaterial((int)opacity, doubleSided)); - } + return Materials.GetElement(DEF_UNIQUEL_ID); } - public static GLTFMaterial CreateDefaultGLTFMaterial(int materialOpacity, bool doubleSided) + public static BaseMaterial CreateDefaultGLTFMaterial(int materialOpacity, bool doubleSided) { - GLTFMaterial gl_mat = new GLTFMaterial(); - gl_mat.doubleSided = doubleSided; + BaseMaterial baseMaterial = new BaseMaterial(); + baseMaterial.doubleSided = doubleSided; float opacity = 1 - (float)materialOpacity; - gl_mat.name = DEF_MATERIAL_NAME; - GLTFPBR pbr = new GLTFPBR(); - pbr.baseColorFactor = new List(4) { 1f, 1f, 1f, opacity }; - pbr.metallicFactor = 0f; - pbr.roughnessFactor = 1f; - gl_mat.pbrMetallicRoughness = pbr; - gl_mat.UniqueId = DEF_UNIQUEL_ID; - - return gl_mat; + baseMaterial.name = DEF_MATERIAL_NAME; + baseMaterial.baseColorFactor = new List(4) { 1f, 1f, 1f, opacity }; + baseMaterial.metallicFactor = 0f; + baseMaterial.roughnessFactor = 1f; + baseMaterial.uuid = DEF_UNIQUEL_ID; + + return baseMaterial; } public static void AddVerticesAndFaces( @@ -63,12 +56,14 @@ public static void AddOrUpdateCurrentItem( Element element, IndexedDictionary geomDataObj, IndexedDictionary vertexIntObj, - GLTFMaterial material) + BaseMaterial material) { - - // Add new "_current" entries if vertex_key is unique - string vertex_key = string.Concat(element.UniqueId, UNDERSCORE, material.UniqueId); + string vertex_key = element.Id.ToString() + "_" + material.uuid; geomDataObj.AddOrUpdateCurrent(vertex_key, new GeometryDataObject()); + geomDataObj.CurrentItem.MaterialInfo = new MaterialInfo + { + uuid = material.uuid + }; vertexIntObj.AddOrUpdateCurrent(vertex_key, new VertexLookupIntObject()); } @@ -84,72 +79,6 @@ public static void AddRPCNormals(Preferences preferences, MeshTriangle triangle, } } - const string BIN = ".bin"; - - /// - /// Takes the intermediate geometry data and performs the calculations - /// to convert that into glTF buffers, views, and accessors. - /// - /// buffers. - /// accessors. - /// bufferViews. - /// geomData. - /// Unique name for the .bin file that will be produced. - /// Revit element's Element ID that will be used as the batchId value. - /// exportBatchId. - /// exportNormals. - /// Returns the GLTFBinaryData object. - public static GLTFBinaryData AddGeometryMeta( - List buffers, - List accessors, - List bufferViews, - GeometryDataObject geomData, - string name, - long elementId, - Preferences preferences, - GLTFMaterial material, - List images, - List textures) - { - - int byteOffset = 0; - - // add a buffer - GLTFBuffer buffer = new GLTFBuffer(); - buffer.uri = string.Concat(name, BIN); - buffers.Add(buffer); - int bufferIdx = buffers.Count - 1; - GLTFBinaryData bufferData = new GLTFBinaryData(); - bufferData.name = buffer.uri; - - - byteOffset = GLTFBinaryDataUtils.ExportVertices(bufferIdx, byteOffset, geomData, bufferData, bufferViews, accessors, out int sizeOfVec3View, out int elementsPerVertex); - - if (preferences.normals) - { - byteOffset = GLTFBinaryDataUtils.ExportNormals(bufferIdx, byteOffset, geomData, bufferData, bufferViews, accessors); - } - - if (preferences.materials == MaterialsEnum.textures && - material.pbrMetallicRoughness?.baseColorTexture != null && geomData.Uvs.Count != 0) - { - byteOffset = GLTFBinaryDataUtils.ExportImageBuffer(bufferIdx, byteOffset, material, images, textures, bufferData, bufferViews); - byteOffset = GLTFBinaryDataUtils.ExportUVs(bufferIdx, byteOffset, geomData, bufferData, bufferViews, accessors); - - } - - if (preferences.batchId) - { - byteOffset = GLTFBinaryDataUtils.ExportBatchId(bufferIdx, byteOffset, sizeOfVec3View, elementsPerVertex, elementId, geomData, bufferData, bufferViews, accessors); - } - - byteOffset = GLTFBinaryDataUtils.ExportFaces(bufferIdx, byteOffset, geomData, bufferData, bufferViews, accessors); - - buffers[bufferIdx].byteLength = byteOffset; - - return bufferData; - } - public static void AddNormals(Transform transform, PolymeshTopology polymesh, List normals) { diff --git a/Common_glTF_Exporter/Version/LatestVersion.cs b/Common_glTF_Exporter/Version/LatestVersion.cs index 2267dfb..7cd0cd3 100644 --- a/Common_glTF_Exporter/Version/LatestVersion.cs +++ b/Common_glTF_Exporter/Version/LatestVersion.cs @@ -1,12 +1,10 @@ -using Autodesk.Revit.UI; -using Common_glTF_Exporter.Model; +using Common_glTF_Exporter.Model; using Common_glTF_Exporter.Utils; -using Revit_glTF_Exporter; +using Newtonsoft.Json; using System; using System.Net.Http; using System.Threading.Tasks; - namespace Revit_glTF_Exporter { public static class LatestVersion @@ -20,16 +18,17 @@ public static async Task Get() string urlParameters = "?inputVersion=" + version + "&&folderName=" + "e-verse/LeiaGltfExporter"; - try { + try + { HttpResponseMessage result = client.GetAsync(urlParameters, HttpCompletionOption.ResponseHeadersRead).Result; if (result.IsSuccessStatusCode) { - HttpContent content = result.Content; string myContent = await content.ReadAsStringAsync(); - Payload payload = Newtonsoft.Json.JsonConvert.DeserializeObject(myContent); + // Newtonsoft.Json deserialization + Payload payload = JsonConvert.DeserializeObject(myContent); if (!payload.Update) { diff --git a/Common_glTF_Exporter/ViewModel/ProgressBarWindowViewModel.cs b/Common_glTF_Exporter/ViewModel/ProgressBarWindowViewModel.cs index 8e9795f..00d33f1 100644 --- a/Common_glTF_Exporter/ViewModel/ProgressBarWindowViewModel.cs +++ b/Common_glTF_Exporter/ViewModel/ProgressBarWindowViewModel.cs @@ -60,9 +60,17 @@ public double ProgressBarValue } this.progressBarValue = value; - ProgressBarPercentage = (value / ProgressBarMax) * 100; - if (ProgressBarPercentage > 100) + + double percentageProgress = (value / ProgressBarMax) * 100; + if (percentageProgress > 99) + { ProgressBarPercentage = 100; + } + else + { + ProgressBarPercentage = (value / ProgressBarMax) * 100; + } + this.OnPropertyChanged(); } } diff --git a/Common_glTF_Exporter/Windows/MainWindow.xaml.cs b/Common_glTF_Exporter/Windows/MainWindow.xaml.cs index 58203a0..c47d3df 100644 --- a/Common_glTF_Exporter/Windows/MainWindow.xaml.cs +++ b/Common_glTF_Exporter/Windows/MainWindow.xaml.cs @@ -91,9 +91,10 @@ private void OnExportView(object sender, RoutedEventArgs e) int incrementRun = numberRuns + 1; SettingsConfig.SetValue("runs", incrementRun.ToString()); - ExportLog.Write($"{elementsInView.Count} elements will be exported"); + int elemInView = elementsInView.Count; + ExportLog.Write($"{elemInView} elements will be exported"); ProgressBarWindow progressBar = - ProgressBarWindow.Create(elementsInView.Count + 1, 0, "Converting elements...", this); + ProgressBarWindow.Create(elemInView + 1, 0, "Converting elements...", this); // Use our custom implementation of IExportContext as the exporter context. GLTFExportContext ctx = new GLTFExportContext(doc); @@ -110,8 +111,8 @@ private void OnExportView(object sender, RoutedEventArgs e) Analytics.Send("exported", SettingsConfig.GetValue("format")).GetAwaiter(); Thread.Sleep(500); - ProgressBarWindow.ViewModel.ProgressBarValue = elementsInView.Count + 1; - ProgressBarWindow.ViewModel.ProgressBarPercentage = 100; + + ProgressBarWindow.ViewModel.ProgressBarValue = elemInView; ProgressBarWindow.ViewModel.Message = "Export completed!"; ExportLog.EndLog(); ProgressBarWindow.ViewModel.Action = "Accept"; diff --git a/Common_glTF_Exporter/Windows/ProgressBarWindow.xaml.cs b/Common_glTF_Exporter/Windows/ProgressBarWindow.xaml.cs index 0752852..36634af 100644 --- a/Common_glTF_Exporter/Windows/ProgressBarWindow.xaml.cs +++ b/Common_glTF_Exporter/Windows/ProgressBarWindow.xaml.cs @@ -31,11 +31,12 @@ public static ProgressBarWindow Create(double maxValue, double currentValue, { mainWin = mainWindow; var progressBar = new ProgressBarWindow(); - ProgressBarWindow.ViewModel.ProgressBarGraphicValue = maxValue * 0.07; + double maxValueDuplicated = maxValue; + ProgressBarWindow.ViewModel.ProgressBarGraphicValue = maxValueDuplicated * 0.07; ProgressBarWindow.ViewModel.ProgressBarValue = currentValue; ProgressBarWindow.ViewModel.Message = message; ProgressBarWindow.ViewModel.Action = "Cancel"; - ProgressBarWindow.ViewModel.ProgressBarMax = maxValue; + ProgressBarWindow.ViewModel.ProgressBarMax = maxValueDuplicated; ProgressBarWindow.ViewModel.ProgressBarPercentage = 0; progressBar.Show(); ProgressBarWindow.MainView.Topmost = true; diff --git a/Common_glTF_Exporter/Windows/VersionWindow.xaml b/Common_glTF_Exporter/Windows/VersionWindow.xaml index a92cabb..01584ff 100644 --- a/Common_glTF_Exporter/Windows/VersionWindow.xaml +++ b/Common_glTF_Exporter/Windows/VersionWindow.xaml @@ -111,12 +111,31 @@ -