private void InitAudioVisualizer() { totalTriangles = (int)sampleMesh.GetIndexCount(0) / 3; audioProfile.bandSize = totalTriangles; audioProcessor = new AudioProcessor(ref audioSource, ref audioProfile); MeshUtil.DeepCopyMesh(ref sampleMesh, out modifiedSampleMesh); audioVFX.SetMesh(VFXPropertyId.mesh_sampleMesh, modifiedSampleMesh); audioVFX.SetInt(VFXPropertyId.int_triangleCount, totalTriangles); modifiedSampleMesh.MarkDynamic(); meshFilter.mesh = modifiedSampleMesh; // transferring mesh data to native arrays to be processed parallely Mesh.MeshDataArray sampleMeshData = Mesh.AcquireReadOnlyMeshData(sampleMesh); normals = MeshUtil.NativeGetNormals(sampleMeshData[0], Allocator.Persistent); normals.AsReadOnly(); triangles = MeshUtil.NativeGetIndices(sampleMeshData[0], Allocator.Persistent); triangles.AsReadOnly(); vertices = MeshUtil.NativeGetVertices(sampleMeshData[0], Allocator.Persistent); // audio processing attributes samples = new NativeArray <float>(audioProfile.sampleSize, Allocator.Persistent); bandDistribution = new NativeArray <int>(audioProfile.bandSize + 1, Allocator.Persistent); bandDistribution.CopyFrom(audioProcessor.bandDistribution); bandDistribution.AsReadOnly(); prevBands = new NativeArray <float>(totalTriangles, Allocator.Persistent); prevBands.CopyFrom(prevBands); bandVelocities = new NativeArray <float>(totalTriangles, Allocator.Persistent); bandVelocities.CopyFrom(bandVelocities); sampleMeshData.Dispose(); if (seed != 0) { // if randomized is turned on int[] seqArray = MathUtil.GenerateSeqArray(totalTriangles); MathUtil.ShuffleArray <int>(ref seqArray, seed); // triangle indices NativeArray <int> trianglesCopy = new NativeArray <int>(triangles, Allocator.Temp); for (int s = 0; s < seqArray.Length; s++) { triangles[s * 3] = trianglesCopy[seqArray[s] * 3]; triangles[s * 3 + 1] = trianglesCopy[seqArray[s] * 3 + 1]; triangles[s * 3 + 2] = trianglesCopy[seqArray[s] * 3 + 2]; } trianglesCopy.Dispose(); } }
Initialize(Mesh sourceMesh, Transform transform) { using (var dataArray = Mesh.AcquireReadOnlyMeshData(sourceMesh)) { var data = dataArray[0]; // Vertex/index count var vtxCount = data.vertexCount; var idxCount = data.GetSubMesh(0).indexCount; // Triangle/voxel/fragment count var triCount = idxCount / 3; var vxlCount = (triCount + SourcePerVoxel - 1) / SourcePerVoxel; var frgCount = triCount - vxlCount; // Source index array Debug.Assert(data.indexFormat == IndexFormat.UInt32); var idx = data.GetIndexData <uint>(); // Read buffers allocation using (var vtx = MemoryUtil.TempJobArray <float3>(vtxCount)) using (var uvs = MemoryUtil.TempJobArray <float2>(vtxCount)) { // Retrieve vertex attribute arrays. data.GetVertices(vtx.Reinterpret <Vector3>()); data.GetUVs(0, uvs.Reinterpret <Vector2>()); // Output buffer var outVxl = MemoryUtil.Array <Element>(vxlCount); var outFrg = MemoryUtil.Array <Element>(frgCount); // Invoke and wait the initializer jobs. var xform = transform.localToWorldMatrix; new InitializationJob { Indices = idx, Vertices = vtx, UVs = uvs, Transform = xform, IsVoxel = true, Output = outVxl } .Schedule(vxlCount, 64).Complete(); new InitializationJob { Indices = idx, Vertices = vtx, UVs = uvs, Transform = xform, IsVoxel = false, Output = outFrg } .Schedule(frgCount, 64).Complete(); return(outVxl, outFrg); } } }
public void UpdateControlPoints() { count = controlPoints.Length; if (vertices == null || vertices.Length != _skinnedMeshRenderer.sharedMesh.vertexCount) { #if UNITY_2020_1_OR_NEWER using (var dataArray = Mesh.AcquireReadOnlyMeshData(_skinnedMeshRenderer.sharedMesh)) { var data = dataArray[0]; if (vertices != null && vertices.IsCreated) { vertices.Dispose(); } vertices = new NativeArray <Vector3>(skinnedMeshRenderer.sharedMesh.vertexCount, Allocator.Persistent); data.GetVertices(vertices); } #else if (vertices != null && vertices.IsCreated) { vertices.Dispose(); } vertices = new NativeArray <Vector3>(skinnedMeshRenderer.sharedMesh.vertices, Allocator.Persistent); #endif } updateControlPoints = false; for (c = 0; c < count; c++) { currentControlPoint = points.GetPoint(controlPoints[c]); if (vertices[c].x != currentControlPoint.x || vertices[c].y != currentControlPoint.y || vertices[c].z != currentControlPoint.z) { updateControlPoints = true; vertices[c] = currentControlPoint; } } if (updateControlPoints) { _skinnedMeshRenderer.sharedMesh.SetVertices(vertices); } }
/// <summary>\copydoc WireMesh(Mesh)</summary> public void WireMesh(Mesh mesh, Color color) { PushColor(color); #if UNITY_2020_1_OR_NEWER // Use a burst compiled function to draw the lines // This is significantly faster than pure C# (about 5x). var meshDataArray = Mesh.AcquireReadOnlyMeshData(mesh); var meshData = meshDataArray[0]; JobWireMesh.JobWireMeshFunctionPointer(ref meshData, ref this); meshDataArray.Dispose(); #else Debug.LogError("The WireMesh method is only suppored in Unity 2020.1 or later"); #endif PopColor(); }
// Start is called before the first frame update void Start() { var mesh = MeshFilter.mesh; using (var dataArray = Mesh.AcquireReadOnlyMeshData(mesh)) { var data = dataArray[0]; // prints "2" Debug.Log(data.vertexCount); var gotVertices = new NativeArray <Vector3>(mesh.vertexCount, Allocator.TempJob); data.GetVertices(gotVertices); // prints "(1.0, 1.0, 1.0)" and "(0.0, 0.0, 0.0)" foreach (var v in gotVertices) { Debug.Log(v); } gotVertices.Dispose(); } }
void OnValidate() { _pointCount = Mathf.Max(64, _pointCount); // We assume that someone changed the values/references in the // serialized fields, so let us dispose the internal objects to // re-initialize them with the new values/references. #BADCODE DisposeInternals(); using (var dataArray = Mesh.AcquireReadOnlyMeshData(_sources[0].sharedMesh)) { for (var i = 0; i < dataArray.Length; i++) { var data = dataArray[i]; Debug.Log(string.Format("{0}: {1}", i, dataArray[i].vertexCount)); } } }
void OnEnable() { // Only skin up to 2 bones, more than this messes up the skinning. if (skinnedMeshRenderer != null) { skinnedMeshRenderer.quality = SkinQuality.Bone2; #if UNITY_2020_1_OR_NEWER using (var dataArray = Mesh.AcquireReadOnlyMeshData(_skinnedMeshRenderer.sharedMesh)) { var data = dataArray[0]; vertices = new NativeArray <Vector3>(_skinnedMeshRenderer.sharedMesh.vertexCount, Allocator.Persistent); data.GetVertices(vertices); } #else vertices = new NativeArray <Vector3>(_skinnedMeshRenderer.sharedMesh.vertices, Allocator.Persistent); #endif } }
Build(Mesh source, Transform transform, Transform effector) { using (var dataArray = Mesh.AcquireReadOnlyMeshData(source)) { var data = dataArray[0]; // Vertex/index count var vcount = data.vertexCount; var icount = data.GetSubMesh(0).indexCount; // Source index array Debug.Assert(data.indexFormat == IndexFormat.UInt32); var src_idx = data.GetIndexData <uint>(); // Read buffer allocation using (var src_pos = MemoryUtil.TempJobArray <float3>(vcount)) using (var src_uv0 = MemoryUtil.TempJobArray <float2>(vcount)) { // Retrieve vertex attribute arrays. data.GetVertices(src_pos.Reinterpret <Vector3>()); data.GetUVs(0, src_uv0.Reinterpret <Vector2>()); // Output buffer var out_vtx = MemoryUtil.TempJobArray <Vertex>(icount); // Invoke and wait the array generator job. new VertexArrayJob { Idx = src_idx, Pos = src_pos, UV0 = src_uv0, Xfm = transform.localToWorldMatrix, Eff = effector.worldToLocalMatrix, Out = out_vtx.Reinterpret <Triangle>(Vertex.StructSize) } .Schedule(icount / 3, 64).Complete(); return(out_vtx); } } }
// This is for editing control points void OnSceneGUI() { if (Weightpainter.isPainting) { if (skin != null) { skin.editingPoints = false; } return; } // If we have a skin and control points then update them if (skin != null && skinnedMeshRenderer != null && skinnedMesh != null && skin.controlPoints != null && skin.controlPoints.Length > 0 && skin.points != null) { e = Event.current; EditorGUI.BeginChangeCheck(); r = HandleUtility.GUIPointToWorldRay(e.mousePosition); mousePos = r.origin; selectDistance = HandleUtility.GetHandleSize(mousePos) * baseSelectDistance; // Create the vertices here for the handle points if (!skin.editingPoints) { bakeMesh = new Mesh(); #if UNITY_2020_2_OR_NEWER skinnedMeshRenderer.BakeMesh(bakeMesh, true); #else skinnedMeshRenderer.BakeMesh(bakeMesh); #endif skinnedMesh = skinnedMeshRenderer.sharedMesh; // Always clear vertices before getting new ones #if UNITY_2020_1_OR_NEWER using (var dataArray = Mesh.AcquireReadOnlyMeshData(bakeMesh)) { var data = dataArray[0]; if (vertices == null || vertices != null && vertices.Length != bakeMesh.vertexCount) { if (vertices.IsCreated) { vertices.Dispose(); } vertices = new NativeArray <Vector3>(bakeMesh.vertexCount, Allocator.Persistent); } data.GetVertices(vertices); } #else if (vertices != null && vertices.IsCreated) { vertices.Dispose(); } vertices = new NativeArray <Vector3>(bakeMesh.vertices, Allocator.Persistent); #endif boneMatrices = new Matrix4x4[skinnedMeshRenderer.bones.Length]; weights = skinnedMesh.boneWeights; bindposes = skinnedMesh.bindposes; bones = skinnedMeshRenderer.bones; // First apply the scale, then transform it to World Space for (int i = 0; i < bakeMesh.vertexCount; i++) { #if !UNITY_2020_2_OR_NEWER vertices[i] = Vector3.Scale(bakeMesh.vertices[i], skinnedMeshRenderer.transform.lossyScale); #endif vertices[i] = skinnedMeshRenderer.transform.TransformPoint(vertices[i]); } // Always clear vertices before getting new ones #if UNITY_2020_1_OR_NEWER using (var dataArray = Mesh.AcquireReadOnlyMeshData(skinnedMesh)) { var data = dataArray[0]; if (newVertices == null || newVertices != null && newVertices.Length != skinnedMesh.vertexCount) { if (newVertices.IsCreated) { newVertices.Dispose(); } newVertices = new NativeArray <Vector3>(skinnedMesh.vertexCount, Allocator.Persistent); } data.GetVertices(newVertices); } #else if (newVertices != null && newVertices.IsCreated) { newVertices.Dispose(); } newVertices = new NativeArray <Vector3>(skinnedMesh.vertices, Allocator.Persistent); #endif // Debug.Log("Created new baked mesh."); } if (e.type == EventType.MouseDrag && e.button == 0 && e.isMouse) { /*if (!skin.editingPoints) { * Debug.Log("Started editing points"); * }*/ skin.editingPoints = true; } else if (e.type == EventType.MouseUp || vertices.Length != skinnedMeshRenderer.sharedMesh.vertexCount) { skin.editingPoints = false; // Debug.Log("Stopped editing points"); } #region Draw vertex handles Handles.color = handleColor; for (int i = 0; i < skin.controlPoints.Length; i++) { if (Handles.Button(vertices[i], Quaternion.identity, selectDistance, selectDistance, Handles.CircleHandleCap)) { selectedIndex = i; } if (selectedIndex == i) { EditorGUI.BeginChangeCheck(); // If we are editing points then the position handle drives the vertex if (skin.editingPoints) { vertices[i] = Handles.PositionHandle(vertices[i], Quaternion.identity); // Need to create matrices based on the skin's bones for (int b = 0; b < boneMatrices.Length; b++) { if (bones[b] != null) { boneMatrices[b] = bones[b].localToWorldMatrix * bindposes[b]; } } weight = weights[i]; vertexMatrix = new Matrix4x4(); // Since we are only using 2 bones for the Skin2D, only use the first 2 bones and weights for (int n = 0; n < 16; n++) { vertexMatrix[n] = boneMatrices[weight.boneIndex0][n] * weight.weight0 + boneMatrices[weight.boneIndex1][n] * weight.weight1; } // DEBUG HERE TO CHECK FOR DISCREPANCIES // /*Vector3 debugVert = vertexMatrix.MultiplyPoint(skinnedMesh.vertices[i]); * * Debug.Log("New Vertex: " + debugVert.x + ", " + debugVert.y + ", " + debugVert.z); * Debug.Log("Original Vertex: " + vertices[i].x + ", " + vertices[i].y + ", " + vertices[i].z); * * Handles.DotHandleCap( * i, * debugVert, * Quaternion.identity, * selectDistance, * EventType.Repaint * );*/ // Invert the matrix to get the local space position of the vertex newVert = vertexMatrix.inverse.MultiplyPoint(vertices[i]); skin.controlPoints[i].position = newVert; skin.points.SetPoint(skin.controlPoints[i]); newVertices[i] = skin.points.GetPoint(skin.controlPoints[i]); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(skin, "Changed Control Point"); Undo.RecordObject(skin.points, "Changed Control Point"); EditorUtility.SetDirty(this); } } else { currentControlPoint = skin.points.GetPoint(skin.controlPoints[i]); // If we are not editing points then just use the world space offset for the position handle offset = vertices[i] - skin.transform.TransformPoint(currentControlPoint); vertices[i] = Handles.PositionHandle(skin.transform.TransformPoint(currentControlPoint) + offset, Quaternion.identity); // Debug.Log("Not editing points."); } } } if (skin.editingPoints) { skinnedMeshRenderer.sharedMesh.SetVertices(newVertices); skin.UpdateControlPoints(); // Debug.Log("Set new vertices"); } #endregion } else { skin.editingPoints = false; } }
/// <summary> /// Applies Draco compression to a given mesh and returns the encoded result (one per submesh) /// The quantization paramters help to find a balance between encoded size and quality / precision. /// </summary> /// <param name="unityMesh">Input mesh</param> /// <param name="encodingSpeed">Encoding speed level. 0 means slow and small. 10 is fastest.</param> /// <param name="decodingSpeed">Decoding speed level. 0 means slow and small. 10 is fastest.</param> /// <param name="positionQuantization">Vertex position quantization</param> /// <param name="normalQuantization">Normal quantization</param> /// <param name="texCoordQuantization">Texture coordinate quantization</param> /// <param name="colorQuantization">Color quantization</param> /// <param name="genericQuantization">Generic quantization (e.g. blend weights and indices). unused at the moment</param> /// <returns></returns> public static unsafe EncodeResult[] EncodeMesh( Mesh unityMesh, int encodingSpeed = 0, int decodingSpeed = 4, int positionQuantization = 14, int normalQuantization = 10, int texCoordQuantization = 12, int colorQuantization = 8, int genericQuantization = 12 ) { #if UNITY_2020_1_OR_NEWER if (!unityMesh.isReadable) { Debug.LogError("Mesh is not readable"); return(null); } var mesh = unityMesh; var result = new EncodeResult[mesh.subMeshCount]; var vertexAttributes = mesh.GetVertexAttributes(); var strides = new int[DracoNative.maxStreamCount]; var attrDatas = new Dictionary <VertexAttribute, AttributeData>(); foreach (var attribute in vertexAttributes) { var attrData = new AttributeData { offset = strides[attribute.stream], stream = attribute.stream }; var size = attribute.dimension * GetAttributeSize(attribute.format); strides[attribute.stream] += size; attrDatas[attribute.attribute] = attrData; } var streamCount = 1; for (var stream = 0; stream < strides.Length; stream++) { var stride = strides[stream]; if (stride <= 0) { continue; } streamCount = stream + 1; } var dataArray = Mesh.AcquireReadOnlyMeshData(mesh); var data = dataArray[0]; var vData = new NativeArray <byte> [streamCount]; var vDataPtr = new IntPtr[streamCount]; for (var stream = 0; stream < streamCount; stream++) { vData[stream] = data.GetVertexData <byte>(stream); vDataPtr[stream] = (IntPtr)vData[stream].GetUnsafeReadOnlyPtr(); } for (int submeshIndex = 0; submeshIndex < mesh.subMeshCount; submeshIndex++) { var submesh = mesh.GetSubMesh(submeshIndex); if (submesh.topology != MeshTopology.Triangles) { Debug.LogError("Only triangles are supported"); return(null); } var indices = mesh.GetIndices(submeshIndex); var faceCount = indices.Length / 3; var dracoEncoder = dracoEncoderCreate(mesh.vertexCount); var attributeIds = new Dictionary <VertexAttribute, uint>(); foreach (var pair in attrDatas) { var attribute = pair.Key; var attrData = pair.Value; var format = mesh.GetVertexAttributeFormat(attribute); var dimension = mesh.GetVertexAttributeDimension(attribute); var stride = strides[attrData.stream]; var baseAddr = vDataPtr[attrData.stream] + attrData.offset; attributeIds[attribute] = dracoEncoderSetAttribute(dracoEncoder, (int)GetAttributeType(attribute), GetDataType(format), dimension, stride, baseAddr); } var indicesData = (IntPtr)UnsafeUtility.PinGCArrayAndGetDataAddress(indices, out var gcHandle); dracoEncoderSetIndices(dracoEncoder, DataType.DT_UINT32, (uint)indices.Length, indicesData); UnsafeUtility.ReleaseGCObject(gcHandle); // For both encoding and decoding (0 = slow and best compression; 10 = fast) dracoEncoderSetCompressionSpeed(dracoEncoder, Mathf.Clamp(encodingSpeed, 0, 10), Mathf.Clamp(decodingSpeed, 0, 10)); dracoEncoderSetQuantizationBits( dracoEncoder, Mathf.Clamp(positionQuantization, 4, 24), Mathf.Clamp(normalQuantization, 4, 24), Mathf.Clamp(texCoordQuantization, 4, 24), Mathf.Clamp(colorQuantization, 4, 24), Mathf.Clamp(genericQuantization, 4, 24) ); dracoEncoderEncode(dracoEncoder, false); var dracoDataSize = (int)dracoEncoderGetByteLength(dracoEncoder); var dracoData = new NativeArray <byte>(dracoDataSize, Allocator.Persistent); dracoEncoderCopy(dracoEncoder, dracoData.GetUnsafePtr()); result[submeshIndex] = new EncodeResult { indexCount = dracoEncoderGetEncodedIndexCount(dracoEncoder), vertexCount = dracoEncoderGetEncodedVertexCount(dracoEncoder), data = dracoData }; dracoEncoderRelease(dracoEncoder); } for (var stream = 0; stream < streamCount; stream++) { vData[stream].Dispose(); } dataArray.Dispose(); return(result); #else Debug.LogError("Draco Encoding only works on Unity 2020.1 or newer"); return(null); #endif }
/// <summary> /// Attempts to copy the data of the current <see cref="Mesh"/> to another one, as fast as possible, /// with minimal allocations (a few tens of bytes in scenarios with very large meshes). /// </summary> public static void CopyTo(this Mesh inMesh, ref Mesh outMesh) { if (inMesh == null) { return; } if (outMesh == null) { outMesh = new Mesh(); } else { outMesh.Clear(); } outMesh.name = inMesh.name; outMesh.bounds = inMesh.bounds; using (var readArray = Mesh.AcquireReadOnlyMeshData(inMesh)) { //------------------------------------------------------------- // INPUT INFO //------------------------------------------------------------- var readData = readArray[0]; // Formats var vertexFormat = inMesh.GetVertexAttributes(); var indexFormat = inMesh.indexFormat; var isIndexShort = indexFormat == IndexFormat.UInt16; // Counts var vertexCount = readData.vertexCount; var indexCount = isIndexShort ? readData.GetIndexData <ushort>().Length : readData.GetIndexData <uint>().Length; // Element Size in bytes var indexSize = isIndexShort ? SHORT_SIZE : INT_SIZE; var vertexSize = 0; for (var i = 0; i < vertexFormat.Length; i++) { // 4 bytes per component by default var size = FLOAT_SIZE; switch (vertexFormat[i].format) { case VertexAttributeFormat.Float16: case VertexAttributeFormat.UNorm16: case VertexAttributeFormat.SNorm16: case VertexAttributeFormat.UInt16: case VertexAttributeFormat.SInt16: size = 2; break; case VertexAttributeFormat.UNorm8: case VertexAttributeFormat.SNorm8: case VertexAttributeFormat.UInt8: case VertexAttributeFormat.SInt8: size = 1; break; } vertexSize += vertexFormat[i].dimension * size; } //------------------------------------------------------------- // OUTPUT SETUP //------------------------------------------------------------- var writeArray = Mesh.AllocateWritableMeshData(1); var writeData = writeArray[0]; writeData.SetVertexBufferParams(vertexCount, vertexFormat); writeData.SetIndexBufferParams(indexCount, indexFormat); //------------------------------------------------------------- // MEMORY COPYING //------------------------------------------------------------- NativeArray <byte> inData; NativeArray <byte> outData; // Vertices inData = readData.GetVertexData <byte>(); outData = writeData.GetVertexData <byte>(); #if USE_UNSAFE unsafe { UnityUnsafeUtility.MemCpy(outData.GetUnsafePtr(), inData.GetUnsafeReadOnlyPtr(), vertexCount * vertexSize); } #else inData.CopyTo(outData); #endif // Indices inData = readData.GetIndexData <byte>(); outData = writeData.GetIndexData <byte>(); #if USE_UNSAFE unsafe { UnityUnsafeUtility.MemCpy(outData.GetUnsafePtr(), inData.GetUnsafeReadOnlyPtr(), indexCount * indexSize); } #else inData.CopyTo(outData); #endif //------------------------------------------------------------- // FINALIZATION //------------------------------------------------------------- writeData.subMeshCount = inMesh.subMeshCount; // Set all sub-meshes for (var i = 0; i < inMesh.subMeshCount; i++) { writeData.SetSubMesh(i, new SubMeshDescriptor((int)inMesh.GetIndexStart(i), (int)inMesh.GetIndexCount(i))); } Mesh.ApplyAndDisposeWritableMeshData(writeArray, outMesh); } }
/// <summary> /// Combine transformed instances of a mesh. /// </summary> public static Mesh CopyReplicate(this Mesh mesh, NativeArray <float4x4> matrices) { using (var readArray = Mesh.AcquireReadOnlyMeshData(mesh)) { var m = new Mesh { subMeshCount = 1, indexFormat = IndexFormat.UInt32 }; //------------------------------------------------------------- // COLLECT ALL NECESSARY INPUT INFO //------------------------------------------------------------- // Source ----------------------------------------------------- var readData = readArray[0]; // Formats var sourceVertexSize = mesh.SizeOfVertex(); var sourceIndexFormat = mesh.indexFormat; // Counts var sourceVertexCount = readData.vertexCount; var sourceIndexCount = readData.GetIndexCount(); // Destination ----------------------------------------------------- var destIndexFormat = IndexFormat.UInt32; var destVertexFormat = mesh.CopyVertexFormat(0, 1); var destIndexCount = sourceIndexCount * matrices.Length; var destVertexCount = sourceVertexCount * matrices.Length; var hasStream1 = !mesh.IsVertexPositionOnly(); //------------------------------------------------------------- // OUTPUT SETUP //------------------------------------------------------------- var writeArray = Mesh.AllocateWritableMeshData(1); var writeData = writeArray[0]; writeData.SetVertexBufferParams(destVertexCount, destVertexFormat); writeData.SetIndexBufferParams(destIndexCount, destIndexFormat); //------------------------------------------------------------- // MEMORY COPYING //------------------------------------------------------------- // Replicate every other vertex attribute --------------------- // Essentially skip the first 12 bytes (= 3 floats) of every vertex, // because we know they represent position, and we handled this above. // Everything that is not VertexPosition will be written to stream 1 ! unsafe { if (hasStream1) { var inData = readData.GetVertexData <byte>(); var outData = writeData.GetVertexData <byte>(1); // Notice that we write to stream 1! var destElementSize = sourceVertexSize - FLOAT3_SIZE; var source = FLOAT3_SIZE + (byte *)inData.GetUnsafeReadOnlyPtr(); // Begin after the first vertex = first 12 bytes var copies = matrices.Length; var noPosition = new NativeArray <byte>(destElementSize * readData.vertexCount, Allocator.TempJob); // REMOVE POSITIONS FROM ORIGINAL MESH STREAM Unity.Collections.LowLevel.Unsafe.UnsafeUtility .MemCpyStride(destination: noPosition.GetUnsafePtr(), destinationStride: destElementSize, source: source, sourceStride: sourceVertexSize, elementSize: destElementSize, count: readData.vertexCount); // REPLICATE NORMALS,COLORS,UV ETC INTO THE MERGED MESH UnsafeUtility.MemCpyReplicate(destination: outData, source: noPosition, count: copies); noPosition.Dispose(); } // Transform Vertices ---------------------------------------- var inVertices = new NativeArray <Vector3>(sourceVertexCount, Allocator.TempJob); var outVertices = writeData.GetVertexData <float3>(0); readData.GetVertices(inVertices); new HelperJobs.TransformVerticesJob { inputVertices = inVertices.Reinterpret <float3>(), matrices = matrices, outputVertices = outVertices }.Schedule(destVertexCount, 128).Complete(); //Indices --------------------------------------------------- var inData2 = readData.GetIndexData <byte>().GetUnsafeReadOnlyPtr(); var outData2 = writeData.GetIndexData <int>(); if (sourceIndexFormat == IndexFormat.UInt16) { new HelperJobs.OffsetReplicateIndicesJob <ushort> { inputIndices = inData2, outputIndices = outData2, originalVertexCount = sourceVertexCount, originalIndexCount = sourceIndexCount }.Schedule(destIndexCount, 128).Complete(); } else { new HelperJobs.OffsetReplicateIndicesJob <uint> { inputIndices = inData2, outputIndices = outData2, originalVertexCount = sourceVertexCount, originalIndexCount = sourceIndexCount }.Schedule(destIndexCount, 128).Complete(); } inVertices.Dispose(); } writeData.subMeshCount = 1; writeData.SetSubMesh(0, new SubMeshDescriptor(0, destIndexCount, mesh.GetTopology(0))); Mesh.ApplyAndDisposeWritableMeshData(writeArray, m); m.RecalculateBounds(); return(m); }
public MeshCollection Finalize() { #if UNITY_2020_1_OR_NEWER Mesh.MeshDataArray data = Mesh.AcquireReadOnlyMeshData(meshData); var meshes = new NativeArray <RasterizationMesh>(this.meshes.Count, Allocator.Persistent); int meshBufferOffset = vertexBuffers.Count; UnityEngine.Profiling.Profiler.BeginSample("Copying vertices"); for (int i = 0; i < data.Length; i++) { var rawMeshData = data[i]; var verts = new NativeArray <Vector3>(rawMeshData.vertexCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); rawMeshData.GetVertices(verts); int totalIndices = 0; for (int subMeshIndex = 0; subMeshIndex < rawMeshData.subMeshCount; subMeshIndex++) { totalIndices += rawMeshData.GetSubMesh(subMeshIndex).indexCount; } var tris = new NativeArray <int>(totalIndices, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); int offset = 0; for (int subMeshIndex = 0; subMeshIndex < rawMeshData.subMeshCount; subMeshIndex++) { var submesh = rawMeshData.GetSubMesh(subMeshIndex); rawMeshData.GetIndices(tris.GetSubArray(offset, submesh.indexCount), subMeshIndex); offset += submesh.indexCount; } vertexBuffers.Add(verts); triangleBuffers.Add(tris); } UnityEngine.Profiling.Profiler.EndSample(); for (int i = 0; i < meshes.Length; i++) { var gatheredMesh = this.meshes[i]; int bufferIndex; if (gatheredMesh.meshDataIndex >= 0) { bufferIndex = meshBufferOffset + gatheredMesh.meshDataIndex; } else { bufferIndex = -(gatheredMesh.meshDataIndex + 1); } var bounds = gatheredMesh.bounds; var slice = vertexBuffers[bufferIndex].Reinterpret <float3>(); if (bounds == new Bounds()) { UnityEngine.Profiling.Profiler.BeginSample("CalculateBounds"); // Recalculate bounding box float4x4 m = gatheredMesh.matrix; unsafe { CalculateBoundsInvoke((float3 *)slice.GetUnsafeReadOnlyPtr(), slice.Length, ref m, out bounds); } UnityEngine.Profiling.Profiler.EndSample(); } var triangles = triangleBuffers[bufferIndex]; meshes[i] = new RasterizationMesh { vertices = new UnsafeSpan <float3>(slice), triangles = new UnsafeSpan <int>(triangles.Slice(0, gatheredMesh.indicesCount != -1 ? gatheredMesh.indicesCount : triangles.Length)), area = gatheredMesh.area, bounds = bounds, matrix = gatheredMesh.matrix, solid = gatheredMesh.solid, }; } cachedMeshes.Clear(); ObjectPoolSimple <Dictionary <MeshCacheItem, int> > .Release(ref cachedMeshes); ListPool <GatheredMesh> .Release(ref this.meshes); data.Dispose(); return(new MeshCollection(vertexBuffers, triangleBuffers, meshes)); #else throw new System.NotImplementedException("The burst version of recast is only supported in Unity 2020.1 or later"); #endif }
public static void CreateMesh_MeshDataApi() { var sw = Stopwatch.StartNew(); // Find all MeshFilter objects in the scene smp1.Begin(); var meshFilters = FindObjectsOfType <MeshFilter>(); smp1.End(); // Need to figure out how large the output mesh needs to be (in terms of vertex/index count), // as well as get transforms and vertex/index location offsets for each mesh. smp2.Begin(); var jobs = new ProcessMeshDataJob(); jobs.CreateInputArrays(meshFilters.Length); var inputMeshes = new List <Mesh>(meshFilters.Length); var vertexStart = 0; var indexStart = 0; var meshCount = 0; for (var i = 0; i < meshFilters.Length; ++i) { var mf = meshFilters[i]; var go = mf.gameObject; if (go.CompareTag("EditorOnly")) { DestroyImmediate(go); continue; } var mesh = mf.sharedMesh; inputMeshes.Add(mesh); jobs.vertexStart[meshCount] = vertexStart; jobs.indexStart[meshCount] = indexStart; jobs.xform[meshCount] = go.transform.localToWorldMatrix; vertexStart += mesh.vertexCount; indexStart += (int)mesh.GetIndexCount(0); jobs.bounds[meshCount] = new float3x2(new float3(Mathf.Infinity), new float3(Mathf.NegativeInfinity)); ++meshCount; } smp2.End(); // Acquire read-only data for input meshes jobs.meshData = Mesh.AcquireReadOnlyMeshData(inputMeshes); // Create and initialize writable data for the output mesh var outputMeshData = Mesh.AllocateWritableMeshData(1); jobs.outputMesh = outputMeshData[0]; jobs.outputMesh.SetIndexBufferParams(indexStart, IndexFormat.UInt32); jobs.outputMesh.SetVertexBufferParams(vertexStart, new VertexAttributeDescriptor(VertexAttribute.Position), new VertexAttributeDescriptor(VertexAttribute.Normal, stream: 1)); // Launch mesh processing jobs var handle = jobs.Schedule(meshCount, 4); // Create destination Mesh object smp3.Begin(); var newMesh = new Mesh(); newMesh.name = "CombinedMesh"; var sm = new SubMeshDescriptor(0, indexStart, MeshTopology.Triangles); sm.firstVertex = 0; sm.vertexCount = vertexStart; // Wait for jobs to finish, since we'll have to access the produced mesh/bounds data at this point handle.Complete(); // Final bounding box of the whole mesh is union of the bounds of individual transformed meshes var bounds = new float3x2(new float3(Mathf.Infinity), new float3(Mathf.NegativeInfinity)); for (var i = 0; i < meshCount; ++i) { var b = jobs.bounds[i]; bounds.c0 = math.min(bounds.c0, b.c0); bounds.c1 = math.max(bounds.c1, b.c1); } sm.bounds = new Bounds((bounds.c0 + bounds.c1) * 0.5f, bounds.c1 - bounds.c0); jobs.outputMesh.subMeshCount = 1; jobs.outputMesh.SetSubMesh(0, sm, MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers); Mesh.ApplyAndDisposeWritableMeshData(outputMeshData, new[] { newMesh }, MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers); newMesh.bounds = sm.bounds; smp3.End(); // Dispose of the read-only mesh data and temporary bounds array smp4.Begin(); jobs.meshData.Dispose(); jobs.bounds.Dispose(); smp4.End(); // Create new GameObject with the new mesh var newGo = new GameObject("CombinedMesh", typeof(MeshFilter), typeof(MeshRenderer)); newGo.tag = "EditorOnly"; var newMf = newGo.GetComponent <MeshFilter>(); var newMr = newGo.GetComponent <MeshRenderer>(); newMr.material = AssetDatabase.LoadAssetAtPath <Material>("Assets/CreateMeshFromAllSceneMeshes/MaterialForNewlyCreatedMesh.mat"); newMf.sharedMesh = newMesh; //newMesh.RecalculateNormals(); // faster to do normal xform in the job var dur = sw.ElapsedMilliseconds; Debug.Log($"Took {dur/1000.0:F2}sec for {meshCount} objects, total {vertexStart} verts"); Selection.activeObject = newGo; }