// 1) Raycast against the mirror plane:
var ray = new Ray(observer.position, observer.forward);
if (!Physics.Raycast(ray, out var hit, 1000f, 1 << mirrorPlane.gameObject.layer))
Vector3 hp = hit.point; // hit position on the plane
Vector3 normal = hit.normal; // plane normal at the hit
// 2) Acquire the original mesh data
var rdArray = Mesh.AcquireReadOnlyMeshData(meshToReflect.sharedMesh);
int vertexCount = readMd.vertexCount;
var inInds = readMd.GetIndexData<int>();
int indexCount = inInds.Length;
// 3) Allocate exactly one writable MeshData slot
var writeMdArray = Mesh.AllocateWritableMeshData(1);
var writeMd = writeMdArray[0];
// 4) Clear the existing reflected mesh (start fresh)
reflectedMeshData.Clear();
// 5) Copy submesh count, then set each descriptor
int subMeshCount = readMd.subMeshCount;
writeMd.subMeshCount = subMeshCount;
for (int s = 0; s < subMeshCount; ++s)
// Copy each submesh descriptor verbatim:
var desc = readMd.GetSubMesh(s);
writeMd.SetSubMesh(s, new SubMeshDescriptor(
// 6) Set up vertex‐ and index‐buffers on the writable MeshData
writeMd.SetVertexBufferParams(
new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3),
new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3),
new VertexAttributeDescriptor(VertexAttribute.Tangent, VertexAttributeFormat.Float32, 4),
new VertexAttributeDescriptor(VertexAttribute.TexCoord0,VertexAttributeFormat.Float32, 2),
new VertexAttributeDescriptor(VertexAttribute.Color, VertexAttributeFormat.Float32, 4)
// (add more descriptors if your mesh has UV2, UV3, bone weights, etc.)
writeMd.SetIndexBufferParams(
// 7) Allocate and fill NativeArrays for each stream:
var inVerts = new NativeArray<Vector3>(vertexCount, Allocator.TempJob);
var reflectedPts = new NativeArray<Vector3>(vertexCount, Allocator.TempJob);
readMd.GetVertices(inVerts);
var inNormals = new NativeArray<Vector3>(vertexCount, Allocator.TempJob);
var reflectedNrm = new NativeArray<Vector3>(vertexCount, Allocator.TempJob);
readMd.GetNormals(inNormals);
var inTngs = new NativeArray<Vector4>(vertexCount, Allocator.TempJob);
var reflectedTng = new NativeArray<Vector4>(vertexCount, Allocator.TempJob);
readMd.GetTangents(inTngs);
var inUVs = new NativeArray<Vector2>(vertexCount, Allocator.TempJob);
var reflectedUVs = new NativeArray<Vector2>(vertexCount, Allocator.TempJob);
var inCols = new NativeArray<Color>(vertexCount, Allocator.TempJob);
var reflectedCl = new NativeArray<Color>(vertexCount, Allocator.TempJob);
readMd.GetColors(inCols);
// (If you have bone weights, blend shapes, etc., do them here.)
// 8) Schedule your reflect‐job, passing all streams:
points = inVerts .Reinterpret<float3>(),
normals = inNormals .Reinterpret<float3>(),
tangents = inTngs .Reinterpret<float4>(),
uvs = inUVs .Reinterpret<float2>(),
colors = inCols .Reinterpret<float4>(),
outPoints = reflectedPts .Reinterpret<float3>(),
outNormals = reflectedNrm .Reinterpret<float3>(),
outTangents = reflectedTng .Reinterpret<float4>(),
outUVs = reflectedUVs .Reinterpret<float2>(),
outColors = reflectedCl .Reinterpret<float4>(),
.Schedule(vertexCount, 128)
// 9) Write back each stream into the writable MeshData
writeMd.SetVertices (reflectedPts);
writeMd.SetNormals (reflectedNrm);
writeMd.SetTangents (reflectedTng);
writeMd.SetUVs (0, reflectedUVs);
writeMd.SetColors (reflectedCl);
// 10) Take care of index buffer with reversed winding:
var outInds = writeMd.GetIndexData<int>();
// First, copy raw indices so we have the same array length:
outInds.CopyFrom(inInds);
// Then reverse‐winding per‐submesh:
for (int s = 0; s < subMeshCount; ++s)
var desc = readMd.GetSubMesh(s);
int start = desc.indexStart;
int count = desc.indexCount; // must be divisible by 3
for (int t = start; t < start + count; t += 3)
// 11) Apply the MeshData to the target mesh and release resources
Mesh.ApplyAndDisposeWritableMeshData(writeMdArray, new[] { reflectedMeshData });
// 12) Dispose all temp jobs
// 13) Optionally recalc bounds if you want Unity to update them automatically
reflectedMeshData.RecalculateBounds();
// (No need to RecalculateNormals(), since we provided mirrored normals explicitly.)