/// <summary> /// Runs once at end of export. Serializes the gltf /// properties and wites out the *.gltf and *.bin files. /// </summary> public void Finish() { Debug.WriteLine("Finishing..."); glTFContainer container = manager.Finish(); if (_cfgs.IncludeNonStdElements) { // TODO: [RM] Standardize what non glTF spec elements will go into // this "BIM glTF superset" and write a spec for it. Gridlines below // are an example. // Add gridlines as gltf nodes in the format: // Origin {Vec3<double>}, Direction {Vec3<double>}, Length {double} FilteredElementCollector col = new FilteredElementCollector(_doc) .OfClass(typeof(Grid)); var grids = col.ToElements(); foreach (Grid g in grids) { Line l = g.Curve as Line; var origin = l.Origin; var direction = l.Direction; var length = l.Length; var xtras = new glTFExtras(); var grid = new GridParameters(); grid.origin = new List <double>() { origin.X, origin.Y, origin.Z }; grid.direction = new List <double>() { direction.X, direction.Y, direction.Z }; grid.length = length; xtras.GridParameters = grid; xtras.UniqueId = g.UniqueId; xtras.Properties = Util.GetElementProperties(g, true); var gridNode = new glTFNode(); gridNode.name = g.Name; gridNode.extras = xtras; container.glTF.nodes.Add(gridNode); container.glTF.nodes[0].children.Add(container.glTF.nodes.Count - 1); } } if (_cfgs.SingleBinary) { int bytePosition = 0; int currentBuffer = 0; foreach (var view in container.glTF.bufferViews) { if (view.buffer == 0) { bytePosition += view.byteLength; continue; } if (view.buffer != currentBuffer) { view.buffer = 0; view.byteOffset = bytePosition; bytePosition += view.byteLength; } } glTFBuffer buffer = new glTFBuffer(); buffer.uri = _filename + ".bin"; buffer.byteLength = bytePosition; container.glTF.buffers.Clear(); container.glTF.buffers.Add(buffer); using (FileStream f = File.Create(Path.Combine(_directory, buffer.uri))) { using (BinaryWriter writer = new BinaryWriter(f)) { foreach (var bin in container.binaries) { foreach (var coord in bin.contents.vertexBuffer) { writer.Write((float)coord); } // TODO: add writer for normals buffer foreach (var index in bin.contents.indexBuffer) { writer.Write((int)index); } } } } } else { // Write the *.bin files foreach (var bin in container.binaries) { using (FileStream f = File.Create(Path.Combine(_directory, bin.name))) { using (BinaryWriter writer = new BinaryWriter(f)) { foreach (var coord in bin.contents.vertexBuffer) { writer.Write((float)coord); } // TODO: add writer for normals buffer foreach (var index in bin.contents.indexBuffer) { writer.Write((int)index); } } } } } // Write the *.gltf file string serializedModel = JsonConvert.SerializeObject(container.glTF, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); File.WriteAllText(Path.Combine(_directory, _filename + ".gltf"), serializedModel); }
/// <summary> /// Takes the intermediate geometry data and performs the calculations /// to convert that into glTF buffers, views, and accessors. /// </summary> /// <param name="geomData"></param> /// <param name="name">Unique name for the .bin file that will be produced.</param> /// <returns></returns> private glTFBinaryData processGeometry(GeometryData geom, string name) { // TODO: rename this type to glTFBufferMeta ? glTFBinaryData bufferData = new glTFBinaryData(); glTFBinaryBufferContents bufferContents = new glTFBinaryBufferContents(); foreach (var coord in geom.vertices) { float vFloat = Convert.ToSingle(coord); bufferContents.vertexBuffer.Add(vFloat); } foreach (var index in geom.faces) { bufferContents.indexBuffer.Add(index); } // Prevent buffer duplication by hash checking string calculatedHash = ManagerUtils.GenerateSHA256Hash(bufferContents); ManagerUtils.HashSearch hs = new ManagerUtils.HashSearch(calculatedHash); var match = binaryFileData.Find(hs.EqualTo); if (match != null) { // return previously created buffer metadata bufferData.vertexAccessorIndex = match.vertexAccessorIndex; bufferData.indexAccessorIndex = match.indexAccessorIndex; return(bufferData); } else { // add a buffer glTFBuffer buffer = new glTFBuffer(); buffer.uri = name + ".bin"; buffers.Add(buffer); int bufferIdx = buffers.Count - 1; /** * Buffer Data **/ bufferData.name = buffer.uri; bufferData.contents = bufferContents; // TODO: Uncomment for normals //foreach (var normal in geomData.normals) //{ // bufferData.normalBuffer.Add((float)normal); //} // Get max and min for vertex data float[] vertexMinMax = Util.GetVec3MinMax(bufferContents.vertexBuffer); // Get max and min for index data int[] faceMinMax = Util.GetScalarMinMax(bufferContents.indexBuffer); // TODO: Uncomment for normals // Get max and min for normal data //float[] normalMinMax = getVec3MinMax(bufferData.normalBuffer); /** * BufferViews **/ // Add a vec3 buffer view int elementsPerVertex = 3; int bytesPerElement = 4; int bytesPerVertex = elementsPerVertex * bytesPerElement; int numVec3 = (geom.vertices.Count) / elementsPerVertex; int sizeOfVec3View = numVec3 * bytesPerVertex; glTFBufferView vec3View = new glTFBufferView(); vec3View.buffer = bufferIdx; vec3View.byteOffset = 0; vec3View.byteLength = sizeOfVec3View; vec3View.target = Targets.ARRAY_BUFFER; bufferViews.Add(vec3View); int vec3ViewIdx = bufferViews.Count - 1; // TODO: Add a normals (vec3) buffer view // Add a faces / indexes buffer view int elementsPerIndex = 1; int bytesPerIndexElement = 4; int bytesPerIndex = elementsPerIndex * bytesPerIndexElement; int numIndexes = geom.faces.Count; int sizeOfIndexView = numIndexes * bytesPerIndex; glTFBufferView facesView = new glTFBufferView(); facesView.buffer = bufferIdx; facesView.byteOffset = vec3View.byteLength; facesView.byteLength = sizeOfIndexView; facesView.target = Targets.ELEMENT_ARRAY_BUFFER; bufferViews.Add(facesView); int facesViewIdx = bufferViews.Count - 1; buffers[bufferIdx].byteLength = vec3View.byteLength + facesView.byteLength; /** * Accessors **/ // add a position accessor glTFAccessor positionAccessor = new glTFAccessor(); positionAccessor.bufferView = vec3ViewIdx; positionAccessor.byteOffset = 0; positionAccessor.componentType = ComponentType.FLOAT; positionAccessor.count = geom.vertices.Count / elementsPerVertex; positionAccessor.type = "VEC3"; positionAccessor.max = new List <float>() { vertexMinMax[1], vertexMinMax[3], vertexMinMax[5] }; positionAccessor.min = new List <float>() { vertexMinMax[0], vertexMinMax[2], vertexMinMax[4] }; accessors.Add(positionAccessor); bufferData.vertexAccessorIndex = accessors.Count - 1; // TODO: Uncomment for normals // add a normals accessor //glTFAccessor normalsAccessor = new glTFAccessor(); //normalsAccessor.bufferView = vec3ViewIdx; //normalsAccessor.byteOffset = (positionAccessor.count) * bytesPerVertex; //normalsAccessor.componentType = ComponentType.FLOAT; //normalsAccessor.count = geom.data.normals.Count / elementsPerVertex; //normalsAccessor.type = "VEC3"; //normalsAccessor.max = new List<float>() { normalMinMax[1], normalMinMax[3], normalMinMax[5] }; //normalsAccessor.min = new List<float>() { normalMinMax[0], normalMinMax[2], normalMinMax[4] }; //this.accessors.Add(normalsAccessor); //bufferData.normalsAccessorIndex = this.accessors.Count - 1; // add a face accessor glTFAccessor faceAccessor = new glTFAccessor(); faceAccessor.bufferView = facesViewIdx; faceAccessor.byteOffset = 0; faceAccessor.componentType = ComponentType.UNSIGNED_INT; faceAccessor.count = numIndexes; faceAccessor.type = "SCALAR"; faceAccessor.max = new List <float>() { faceMinMax[1] }; faceAccessor.min = new List <float>() { faceMinMax[0] }; accessors.Add(faceAccessor); bufferData.indexAccessorIndex = accessors.Count - 1; bufferData.hashcode = calculatedHash; return(bufferData); } }
/// <summary> /// Takes the intermediate geometry data and performs the calculations /// to convert that into glTF buffers, views, and accessors. /// </summary> /// <param name="geomData"></param> /// <param name="name">Unique name for the .bin file that will be produced.</param> /// <returns></returns> public glTFBinaryData AddGeometryMeta(GeometryData geomData, string name) { // add a buffer glTFBuffer buffer = new glTFBuffer(); buffer.uri = name + ".bin"; Buffers.Add(buffer); int bufferIdx = Buffers.Count - 1; /** * Buffer Data **/ glTFBinaryData bufferData = new glTFBinaryData(); bufferData.name = buffer.uri; foreach (var coord in geomData.vertices) { float vFloat = Convert.ToSingle(coord); bufferData.vertexBuffer.Add(vFloat); } foreach (var index in geomData.faces) { bufferData.indexBuffer.Add(index); } // TODO: Uncomment for normals //foreach (var normal in geomData.normals) //{ // bufferData.normalBuffer.Add((float)normal); //} // Get max and min for vertex data float[] vertexMinMax = Util.GetVec3MinMax(bufferData.vertexBuffer); // Get max and min for index data int[] faceMinMax = Util.GetScalarMinMax(bufferData.indexBuffer); // TODO: Uncomment for normals // Get max and min for normal data //float[] normalMinMax = getVec3MinMax(bufferData.normalBuffer); /** * BufferViews **/ // Add a vec3 buffer view int elementsPerVertex = 3; int bytesPerElement = 4; int bytesPerVertex = elementsPerVertex * bytesPerElement; int numVec3 = (geomData.vertices.Count) / elementsPerVertex; int sizeOfVec3View = numVec3 * bytesPerVertex; glTFBufferView vec3View = new glTFBufferView(); vec3View.buffer = bufferIdx; vec3View.byteOffset = 0; vec3View.byteLength = sizeOfVec3View; vec3View.target = Targets.ARRAY_BUFFER; BufferViews.Add(vec3View); int vec3ViewIdx = BufferViews.Count - 1; // TODO: Add a normals (vec3) buffer view // 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(); facesView.buffer = bufferIdx; facesView.byteOffset = vec3View.byteLength; facesView.byteLength = sizeOfIndexView; facesView.target = Targets.ELEMENT_ARRAY_BUFFER; BufferViews.Add(facesView); int facesViewIdx = BufferViews.Count - 1; Buffers[bufferIdx].byteLength = vec3View.byteLength + facesView.byteLength; /** * Accessors **/ // add a position accessor glTFAccessor positionAccessor = new glTFAccessor(); positionAccessor.bufferView = vec3ViewIdx; positionAccessor.byteOffset = 0; positionAccessor.componentType = ComponentType.FLOAT; positionAccessor.count = geomData.vertices.Count / elementsPerVertex; positionAccessor.type = "VEC3"; positionAccessor.max = new List <float>() { vertexMinMax[1], vertexMinMax[3], vertexMinMax[5] }; positionAccessor.min = new List <float>() { vertexMinMax[0], vertexMinMax[2], vertexMinMax[4] }; Accessors.Add(positionAccessor); bufferData.vertexAccessorIndex = Accessors.Count - 1; // TODO: Uncomment for normals // add a normals accessor //glTFAccessor normalsAccessor = new glTFAccessor(); //normalsAccessor.bufferView = vec3ViewIdx; //normalsAccessor.byteOffset = (positionAccessor.count) * bytesPerVertex; //normalsAccessor.componentType = ComponentType.FLOAT; //normalsAccessor.count = geom.data.normals.Count / elementsPerVertex; //normalsAccessor.type = "VEC3"; //normalsAccessor.max = new List<float>() { normalMinMax[1], normalMinMax[3], normalMinMax[5] }; //normalsAccessor.min = new List<float>() { normalMinMax[0], normalMinMax[2], normalMinMax[4] }; //this.accessors.Add(normalsAccessor); //bufferData.normalsAccessorIndex = this.accessors.Count - 1; // add a face accessor glTFAccessor faceAccessor = new glTFAccessor(); faceAccessor.bufferView = facesViewIdx; faceAccessor.byteOffset = 0; faceAccessor.componentType = ComponentType.UNSIGNED_INT; faceAccessor.count = numIndexes; faceAccessor.type = "SCALAR"; faceAccessor.max = new List <float>() { faceMinMax[1] }; faceAccessor.min = new List <float>() { faceMinMax[0] }; Accessors.Add(faceAccessor); bufferData.indexAccessorIndex = Accessors.Count - 1; return(bufferData); }
/// <summary> /// Runs once at end of export. Serializes the gltf /// properties and wites out the *.gltf and *.bin files. /// </summary> public void Finish() { Debug.WriteLine("Finishing..."); gltfContainer = manager.Finish(); if (_cfgs.IncludeNonStdElements) { // TODO: [RM] Standardize what non glTF spec elements will go into // this "BIM glTF superset" and write a spec for it. Gridlines below // are an example. // Add gridlines as gltf nodes in the format: // Origin {Vec3<double>}, Direction {Vec3<double>}, Length {double} FilteredElementCollector col = new FilteredElementCollector(_doc) .OfClass(typeof(Grid)); var grids = col.ToElements(); foreach (Grid g in grids) { Line l = g.Curve as Line; var origin = l.Origin; var direction = l.Direction; var length = l.Length; var xtras = new glTFExtras(); var grid = new GridParameters(); grid.origin = new List <double>() { origin.X, origin.Y, origin.Z }; grid.direction = new List <double>() { direction.X, direction.Y, direction.Z }; grid.length = length; xtras.GridParameters = grid; xtras.UniqueId = g.UniqueId; xtras.Properties = Util.GetElementProperties(g, true); var gridNode = new glTFNode(); gridNode.name = g.Name; gridNode.extras = xtras; gltfContainer.glTF.nodes.Add(gridNode); gltfContainer.glTF.nodes[0].children.Add(gltfContainer.glTF.nodes.Count - 1); } } if (_cfgs.SingleBinary) { int bytePosition = 0; int currentBuffer = 0; foreach (var view in gltfContainer.glTF.bufferViews) { if (view.buffer == 0) { bytePosition += view.byteLength; continue; } if (view.buffer != currentBuffer) { view.buffer = 0; view.byteOffset = bytePosition; bytePosition += view.byteLength; } } glTFBuffer buffer = new glTFBuffer(); buffer.uri = _filename + ".bin"; buffer.byteLength = bytePosition; gltfContainer.glTF.buffers.Clear(); gltfContainer.glTF.buffers.Add(buffer); using (FileStream f = File.Create(Path.Combine(_directory, buffer.uri))) { using (BinaryWriter writer = new BinaryWriter(f)) { foreach (var bin in gltfContainer.binaries) { foreach (var coord in bin.contents.vertexBuffer) { writer.Write((float)coord); } // TODO: add writer for normals buffer foreach (var index in bin.contents.indexBuffer) { writer.Write((int)index); } } } } } else { // Write the *.bin files foreach (var bin in gltfContainer.binaries) { using (FileStream f = File.Create(Path.Combine(_directory, bin.name))) { using (BinaryWriter writer = new BinaryWriter(f)) { foreach (var coord in bin.contents.vertexBuffer) { writer.Write((float)coord); } // TODO: add writer for normals buffer foreach (var index in bin.contents.indexBuffer) { writer.Write((int)index); } } } } } try { // Write the *.gltf file string pgltf = Path.Combine(_directory, _filename + ".gltf"); string serializedModel = JsonConvert.SerializeObject(gltfContainer.glTF, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); File.WriteAllText(pgltf, serializedModel); } catch (Exception ex) { MessageBox.Show(ex.GetType().Name + "\n" + ex.Message); } // FIXME convert to glb. // problem: can't load file or assembly System.Runtime.CompilerServices.Unsafe v4.0.6 // see possible cause here https://stackoverflow.com/a/62769681 // var mglb = SharpGLTF.Schema2.ModelRoot.Load(pgltf); // mglb.SaveGLB(Path.Combine(_directory, _filename + ".glb")); }
/// <summary> /// Runs once at end of export. Serializes the gltf /// properties and wites out the *.gltf and *.bin files. /// </summary> public void Finish() { Debug.WriteLine("Finishing..."); // TODO: [RM] Standardize what non glTF spec elements will go into // this "BIM glTF superset" and write a spec for it. Gridlines below // are an example. // Add gridlines as gltf nodes in the format: // Origin {Vec3<double>}, Direction {Vec3<double>}, Length {double} FilteredElementCollector col = new FilteredElementCollector(_doc) .OfClass(typeof(Grid)); var grids = col.ToElements(); foreach (Grid g in grids) { Line l = g.Curve as Line; var origin = l.Origin; var direction = l.Direction; var length = l.Length; var xtras = new glTFExtras(); var grid = new GridParameters(); grid.origin = new List <double>() { origin.X, origin.Y, origin.Z }; grid.direction = new List <double>() { direction.X, direction.Y, direction.Z }; grid.length = length; xtras.GridParameters = grid; xtras.UniqueId = g.UniqueId; xtras.Properties = Util.GetElementProperties(g, true); var gridNode = new glTFNode(); gridNode.name = g.Name; gridNode.extras = xtras; Nodes.AddOrUpdateCurrent(g.UniqueId, gridNode); rootNode.children.Add(Nodes.CurrentIndex); } if (_singleBinary) { int bytePosition = 0; int currentBuffer = 0; foreach (var view in BufferViews) { if (view.buffer == 0) { bytePosition += view.byteLength; continue; } if (view.buffer != currentBuffer) { view.buffer = 0; view.byteOffset = bytePosition; bytePosition += view.byteLength; } } glTFBuffer buffer = new glTFBuffer(); buffer.uri = "monobuffer.bin"; buffer.byteLength = bytePosition; Buffers.Clear(); Buffers.Add(buffer); using (FileStream f = File.Create(_directory + "monobuffer.bin")) { using (BinaryWriter writer = new BinaryWriter(f)) { foreach (var bin in binaryFileData) { foreach (var coord in bin.vertexBuffer) { writer.Write((float)coord); } // TODO: add writer for normals buffer foreach (var index in bin.indexBuffer) { writer.Write((int)index); } } } } } // Package the properties into a serializable container glTF model = new glTF(); model.asset = new glTFVersion(); model.scenes = Scenes; model.nodes = Nodes.List; model.meshes = Meshes.List; model.materials = Materials.List; model.buffers = Buffers; model.bufferViews = BufferViews; model.accessors = Accessors; // Write the *.gltf file string serializedModel = JsonConvert.SerializeObject(model, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); File.WriteAllText(_filename, serializedModel); if (!_singleBinary) { // Write the *.bin files foreach (var bin in binaryFileData) { using (FileStream f = File.Create(_directory + bin.name)) { using (BinaryWriter writer = new BinaryWriter(f)) { foreach (var coord in bin.vertexBuffer) { writer.Write((float)coord); } // TODO: add writer for normals buffer foreach (var index in bin.indexBuffer) { writer.Write((int)index); } } } } } }