From b52684070b9119b9f7fd6607f8fa9db1c49133ba Mon Sep 17 00:00:00 2001 From: Andreas Atteneder Date: Wed, 23 Feb 2022 19:09:10 +0100 Subject: [PATCH] feat: glTF Export for Unity versions older than 2020.2 --- ChangeLog.md | 1 + Runtime/Scripts/Export/GltfWriter.cs | 347 ++++++++++++++++++++++++++- 2 files changed, 347 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 0f17482f..4581b2b4 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - (Export) Runtime glTF export to files +- (Export) Export for Unity versions older than 2020.2 - (Export) Save to `System.IO.Stream` - (Export) Occlusion map support - (Export) Metallic-gloss map support (converted to roughness-metallic) diff --git a/Runtime/Scripts/Export/GltfWriter.cs b/Runtime/Scripts/Export/GltfWriter.cs index dff126df..08cb5f0b 100644 --- a/Runtime/Scripts/Export/GltfWriter.cs +++ b/Runtime/Scripts/Export/GltfWriter.cs @@ -498,7 +498,7 @@ async Task Bake(string bufferPath, string directory) { #if GLTFAST_MESH_DATA await BakeMeshes(); #else - throw new NotImplementedException("glTF export (containing meshes) is currently not supported on Unity 2020.1 and older"); + await BakeMeshesLegacy(); #endif } @@ -924,7 +924,352 @@ int AddAccessor(Accessor accessor) { m_Accessors.Add(accessor); return accessorId; } +#else + + async Task BakeMeshesLegacy() { + Profiler.BeginSample("BakeMeshesLegacy"); + for (var meshId = 0; meshId < m_Meshes.Count; meshId++) { + BakeMeshLegacy(meshId); + await m_DeferAgent.BreakPoint(); + } + Profiler.EndSample(); + } + + void BakeMeshLegacy(int meshId) { + + Profiler.BeginSample("BakeMeshLegacy"); + + var mesh = m_Meshes[meshId]; + var uMesh = m_UnityMeshes[meshId]; + + var attributes = new Attributes(); + var vertexAttributes = uMesh.GetVertexAttributes(); + var attrDataDict = new Dictionary(); + + for (var streamId = 0; streamId((int)totalFaceCount,Allocator.TempJob); + var offset = 0; + for (var subMeshIndex = 0; subMeshIndex < uMesh.subMeshCount; subMeshIndex++) { + var indexData16 = uMesh.GetIndices(subMeshIndex); + switch (topology) { + case MeshTopology.Triangles: { + var triCount = indexData16.Length / 3; + for (var i = 0; i < triCount; i++) { + destIndices[offset+i*3] = (ushort) indexData16[i*3]; + destIndices[offset+i*3+1] = (ushort) indexData16[i*3+2]; + destIndices[offset+i*3+2] = (ushort) indexData16[i*3+1]; + } + offset += indexData16.Length; + break; + } + case MeshTopology.Quads: { + var quadCount = indexData16.Length / 4; + for (var i = 0; i < quadCount; i++) { + destIndices[offset+i*6+0] = (ushort) indexData16[i*4+0]; + destIndices[offset+i*6+1] = (ushort) indexData16[i*4+2]; + destIndices[offset+i*6+2] = (ushort) indexData16[i*4+1]; + destIndices[offset+i*6+3] = (ushort) indexData16[i*4+2]; + destIndices[offset+i*6+4] = (ushort) indexData16[i*4+0]; + destIndices[offset+i*6+5] = (ushort) indexData16[i*4+3]; + } + offset += quadCount*6; + break; + } + default: { + for (var i = 0; i < indexData16.Length; i++) { + destIndices[offset+i] = (ushort) indexData16[i]; + } + offset += indexData16.Length; + break; + } + } + } + indexBufferViewId = WriteBufferViewToBuffer( + destIndices.Reinterpret(sizeof(ushort)), + byteAlignment:sizeof(ushort) + ); + destIndices.Dispose(); + } else { + var destIndices = new NativeArray((int)totalFaceCount,Allocator.TempJob); + var offset = 0; + for (var subMeshIndex = 0; subMeshIndex < uMesh.subMeshCount; subMeshIndex++) { + var indexData16 = uMesh.GetIndices(subMeshIndex); + switch (topology) { + case MeshTopology.Triangles: { + var triCount = indexData16.Length / 3; + for (var i = 0; i < triCount; i++) { + destIndices[offset+i*3] = (uint) indexData16[i*3]; + destIndices[offset+i*3+1] = (uint) indexData16[i*3+2]; + destIndices[offset+i*3+2] = (uint) indexData16[i*3+1]; + } + offset += indexData16.Length; + break; + } + case MeshTopology.Quads:{ + var quadCount = indexData16.Length / 4; + for (var i = 0; i < quadCount; i++) { + destIndices[offset+i*6+0] = (uint) indexData16[i*4+0]; + destIndices[offset+i*6+1] = (uint) indexData16[i*4+2]; + destIndices[offset+i*6+2] = (uint) indexData16[i*4+1]; + destIndices[offset+i*6+3] = (uint) indexData16[i*4+2]; + destIndices[offset+i*6+4] = (uint) indexData16[i*4+0]; + destIndices[offset+i*6+5] = (uint) indexData16[i*4+3]; + } + offset += quadCount*6; + break; + } + default: { + for (var i = 0; i < indexData16.Length; i++) { + destIndices[offset+i] = (uint) indexData16[i]; + } + offset += indexData16.Length; + break; + } + } + } + indexBufferViewId = WriteBufferViewToBuffer( + destIndices.Reinterpret(sizeof(uint)), + byteAlignment:sizeof(uint) + ); + destIndices.Dispose(); + } + Profiler.EndSample(); + + foreach (var accessor in indexAccessors) { + accessor.bufferView = indexBufferViewId; + } + + Profiler.BeginSample("ExportVertexAttributes"); + foreach (var pair in attrDataDict) { + var vertexAttribute = pair.Key; + var attrData = pair.Value; + var bufferViewId = -1; + switch (vertexAttribute) { + case VertexAttribute.Position: { + var vertices = new List(); + uMesh.GetVertices(vertices); + var outStream = new NativeArray(vertices.Count, Allocator.TempJob); + for (var i = 0; i < vertices.Count; i++) { + outStream[i] = new Vector3(-vertices[i].x,vertices[i].y,vertices[i].z); + } + bufferViewId = WriteBufferViewToBuffer( + outStream.Reinterpret(12), + 12 + ); + outStream.Dispose(); + break; + } + case VertexAttribute.Normal: { + var normals = new List(); + uMesh.GetNormals(normals); + var outStream = new NativeArray(normals.Count, Allocator.TempJob); + for (var i = 0; i < normals.Count; i++) { + outStream[i] = new Vector3(-normals[i].x,normals[i].y,normals[i].z); + } + bufferViewId = WriteBufferViewToBuffer( + outStream.Reinterpret(12), + 12 + ); + outStream.Dispose(); + break; + } + case VertexAttribute.Tangent: { + var tangents = new List(); + uMesh.GetTangents(tangents); + var outStream = new NativeArray(tangents.Count, Allocator.TempJob); + for (var i = 0; i < tangents.Count; i++) { + outStream[i] = new Vector4(tangents[i].x,tangents[i].y,-tangents[i].z,tangents[i].w); + } + bufferViewId = WriteBufferViewToBuffer( + outStream.Reinterpret(16), + 16 + ); + outStream.Dispose(); + break; + } + case VertexAttribute.Color: + break; + case VertexAttribute.TexCoord0: + case VertexAttribute.TexCoord1: + case VertexAttribute.TexCoord2: + case VertexAttribute.TexCoord3: + case VertexAttribute.TexCoord4: + case VertexAttribute.TexCoord5: + case VertexAttribute.TexCoord6: + case VertexAttribute.TexCoord7: { + var uvs = new List(); + var channel = (int)vertexAttribute - (int)VertexAttribute.TexCoord0; + uMesh.GetUVs( channel, uvs); + var outStream = new NativeArray(uvs.Count, Allocator.TempJob); + for (var i = 0; i < uvs.Count; i++) { + outStream[i] = uvs[i]; + } + bufferViewId = WriteBufferViewToBuffer( + outStream.Reinterpret(8), + 8 + ); + outStream.Dispose(); + break; + } + case VertexAttribute.BlendWeight: + break; + case VertexAttribute.BlendIndices: + break; + } + m_Accessors[attrData.accessorId].bufferView = bufferViewId; + } + Profiler.EndSample(); + Profiler.EndSample(); + } + + int AddAccessor(Accessor accessor) { + m_Accessors = m_Accessors ?? new List(); + var accessorId = m_Accessors.Count; + m_Accessors.Add(accessor); + return accessorId; + } #endif // #if GLTFAST_MESH_DATA async Task BakeImages(string directory) {