/// <summary> /// 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. /// We do this at OnElementEnd because it signals no more meshes or materials are /// coming for this element. /// </summary> /// <param name="elementId"></param> public void OnElementEnd(ElementId elementId) { Debug.WriteLine(" OnElementEnd"); if (_skipElementFlag) { // Duplicate element, skip. _skipElementFlag = false; return; } if (_currentVertices.List.Count == 0) { return; } Element e = _doc.GetElement(elementId); // create a new mesh for the node (we're assuming 1 mesh per node w/ multiple primatives on mesh) glTFMesh newMesh = new glTFMesh(); newMesh.primitives = new List <glTFMeshPrimitive>(); Meshes.AddOrUpdateCurrent(e.UniqueId, newMesh); // add the index of this mesh to the current node. Nodes.CurrentItem.mesh = Meshes.CurrentIndex; // Add vertex data to _currentGeometry for each geometry/material pairing foreach (KeyValuePair <string, VertexLookupInt> kvp in _currentVertices.Dict) { string vertex_key = kvp.Key; foreach (KeyValuePair <PointInt, int> p in kvp.Value) { _currentGeometry.GetElement(vertex_key).vertices.Add(p.Key.X); _currentGeometry.GetElement(vertex_key).vertices.Add(p.Key.Y); _currentGeometry.GetElement(vertex_key).vertices.Add(p.Key.Z); } } // Convert _currentGeometry objects into glTFMeshPrimitives foreach (KeyValuePair <string, GeometryData> kvp in _currentGeometry.Dict) { glTFBinaryData elementBinary = AddGeometryMeta(kvp.Value, kvp.Key); binaryFileData.Add(elementBinary); string material_key = kvp.Key.Split('_')[1]; glTFMeshPrimitive primative = new glTFMeshPrimitive(); primative.attributes.POSITION = elementBinary.vertexAccessorIndex; primative.indices = elementBinary.indexAccessorIndex; primative.material = Materials.GetIndexFromUUID(material_key); // TODO: Add normal here Meshes.CurrentItem.primitives.Add(primative); } }
/// <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); } }
public void CloseGeometry() { Debug.WriteLine(String.Format("{0} Closing Geometry", formatDebugHeirarchy)); // Create the new mesh and populate the primitives with GeometryData glTFMesh mesh = new glTFMesh(); mesh.primitives = new List <glTFMeshPrimitive>(); // transfer ordered vertices from vertex dictionary to vertices list foreach (KeyValuePair <string, GeometryData> key_geom in currentGeom) { string key = key_geom.Key; GeometryData geom = key_geom.Value; foreach (KeyValuePair <PointInt, int> point_index in geom.vertDictionary) { PointInt point = point_index.Key; geom.vertices.Add(point.X); geom.vertices.Add(point.Y); geom.vertices.Add(point.Z); } // convert GeometryData objects into glTFMeshPrimitive string material_key = key.Split('_')[1]; glTFBinaryData bufferMeta = processGeometry(geom, key); if (bufferMeta.hashcode != null) { binaryFileData.Add(bufferMeta); } glTFMeshPrimitive primative = new glTFMeshPrimitive(); primative.attributes.POSITION = bufferMeta.vertexAccessorIndex; primative.indices = bufferMeta.indexAccessorIndex; primative.material = materialDict.GetIndexFromUUID(material_key); // TODO: Add normal attribute accessor index here mesh.primitives.Add(primative); } // glTF entity can not be empty if (mesh.primitives.Count() > 0) { // Prevent mesh duplication by hash checking string meshHash = ManagerUtils.GenerateSHA256Hash(mesh); ManagerUtils.HashSearch hs = new ManagerUtils.HashSearch(meshHash); int idx = meshContainers.FindIndex(hs.EqualTo); if (idx != -1) { // set the current nodes mesh index to the already // created mesh location. nodeDict[currentNodeId].mesh = idx; } else { // create new mesh and add it's index to the current node. MeshContainer mc = new MeshContainer(); mc.hashcode = meshHash; mc.contents = mesh; meshContainers.Add(mc); nodeDict[currentNodeId].mesh = meshContainers.Count - 1; } } geometryStack.Pop(); return; }
/// <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); }