/// <summary> /// Updates the mesh by setting its data given the last valid vertex index and the last /// valid triangle index. Be aware that by default the data is set starting from index 0. /// </summary> /// <param name="lastValidVertexIndex"></param> /// <param name="lastValidTriIndex"></param> protected void UpdateMesh(int lastValidVertexIndex, int lastValidTriIndex) { sampler.Begin(); // Note: this might be called in between job completions so it could be moved away. mesh.Clear(false); MeshUpdateFlags updateFlags = ~MeshUpdateFlags.Default; mesh.SetVertices(vertices, 0, lastValidVertexIndex, updateFlags); mesh.SetIndices(tris, 0, lastValidTriIndex, MeshTopology.Triangles, 0, false); if (UseNormals) { mesh.SetNormals(normals, 0, lastValidVertexIndex, updateFlags); } if (UseUvs) { mesh.SetUVs(0, uvs, 0, lastValidVertexIndex, updateFlags); } if (UseVertexColors) { mesh.SetColors(colors, 0, lastValidVertexIndex, updateFlags); } mesh.RecalculateBounds(); sampler.End(); }
void InitMeshFilter() { ReleaseMeshFilter(); int vertexCount = 100; int indexCount = 100; meshGrass = new Mesh(); // 宣告 vertex buffer 結構 VertexAttributeDescriptor[] layouts = new VertexAttributeDescriptor[] { new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3) }; meshGrass.SetVertexBufferParams(vertexCount, layouts); // 宣告 index buffer 結構 meshGrass.SetIndexBufferParams(indexCount, IndexFormat.UInt16); MeshUpdateFlags flag = MeshUpdateFlags.DontResetBoneBounds | MeshUpdateFlags.DontRecalculateBounds; meshGrass.SetVertexBufferData(getRandomPosition(vertexCount, 10f), 0, 0, vertexCount, 0, flag); meshGrass.SetIndexBufferData(getSequenceIndices(indexCount), 0, 0, indexCount, flag); // 設定 Mesh Topologiy SubMeshDescriptor desc = new SubMeshDescriptor(0, indexCount, MeshTopology.Points); meshGrass.SetSubMesh(0, desc, flag); meshGrass.bounds = new Bounds(Vector3.zero, new Vector3(1000f, 5f, 1000f)); this.GetComponent <MeshFilter>().sharedMesh = meshGrass; }
internal void ApplyToMeshAndDispose(Mesh mesh, MeshUpdateFlags flags) { if (!mesh.canAccess) { throw new InvalidOperationException($"Not allowed to access vertex data on mesh '{mesh.name}' (isReadable is false; Read/Write must be enabled in import settings)"); } ApplyToMeshImpl(mesh, m_Ptrs[0], flags); Dispose(); }
public static void SetVertexDataToMesh(Mesh mesh, NativeArray <Vertex> vertices) { MeshUpdateFlags flags = MeshUpdateFlags.DontResetBoneBounds | MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontNotifyMeshUsers; mesh.SetVertexBufferData(vertices, 0, 0, vertices.Length, 0, flags); }
PopulateMeshData() { Profiler.BeginSample("PopulateMeshData"); foreach (var map in attributes) { map.Dispose(); } attributes = null; Profiler.BeginSample("MeshAssign"); const MeshUpdateFlags flags = DracoMeshLoader.defaultMeshUpdateFlags; #if !DRACO_MESH_DATA for (var streamIndex = 0; streamIndex < streamCount; streamIndex++) { mesh.SetVertexBufferData(vData[streamIndex], 0, 0, vData[streamIndex].Length, streamIndex, flags); } mesh.SetIndexBufferData(indices, 0, 0, indices.Length); var indicesCount = indices.Length; #endif mesh.subMeshCount = 1; var submeshDescriptor = new SubMeshDescriptor(0, indicesCount) { firstVertex = 0, baseVertex = 0, vertexCount = mesh.vertexCount }; mesh.SetSubMesh(0, submeshDescriptor, flags); Profiler.EndSample(); // CreateUnityMesh.CreateMesh #if DRACO_MESH_DATA #else Profiler.BeginSample("Dispose"); indices.Dispose(); foreach (var nativeArray in vData) { nativeArray.Dispose(); } Profiler.EndSample(); #endif Profiler.EndSample(); #if DRACO_MESH_DATA return(true); #else return(mesh); #endif }
void CreateMeshForEachSubMesh( NativeArray <CombinedVertex> vertices, NativeArray <int> triangles, NativeArray <SubMesh> subMeshes) { const MeshUpdateFlags flags = MeshUpdateFlags.DontRecalculateBounds; foreach (var subMesh in subMeshes) { var vertexSlice = vertices.GetSubArray(subMesh.VertexStartIndex, subMesh.VertexCount); var trianglesSlice = triangles.GetSubArray(subMesh.TriangleStartIndex, subMesh.TriangleCount); var surfaceInfo = GetRoadSurfaceInfo(subMesh.Material); var meshObject = new GameObject(surfaceInfo.label) { isStatic = true }; meshObject.transform.parent = Parameters.parentObject.transform; var filter = meshObject.AddComponent <MeshFilter>(); var mesh = new Mesh(); var layout = new[] { new VertexAttributeDescriptor(VertexAttribute.Position), new VertexAttributeDescriptor(VertexAttribute.Normal), new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2), }; mesh.SetVertexBufferParams(vertexSlice.Length, layout); mesh.SetVertexBufferData(vertexSlice, 0, 0, vertexSlice.Length, 0, flags); mesh.SetIndexBufferParams(trianglesSlice.Length, IndexFormat.UInt32); mesh.SetIndexBufferData(trianglesSlice, 0, 0, trianglesSlice.Length, flags); mesh.SetSubMesh(0, new SubMeshDescriptor(0, subMesh.TriangleCount), flags); mesh.RecalculateBounds(); filter.sharedMesh = mesh; var renderer = meshObject.AddComponent <MeshRenderer>(); renderer.sharedMaterial = surfaceInfo.material; if (Parameters.addMeshCollider) { meshObject.AddComponent <MeshCollider>(); } #if PERCEPTION_PRESENT var label = meshObject.AddComponent <Labeling>(); label.labels.Add(surfaceInfo.label); #endif } }
internal void ApplyToMeshesAndDispose(Mesh[] meshes, MeshUpdateFlags flags) { for (int i = 0; i < m_Length; ++i) { Mesh m = meshes[i]; if (m == null) { throw new ArgumentNullException(nameof(meshes), $"Mesh at index {i} is null"); } if (!m.canAccess) { throw new InvalidOperationException($"Not allowed to access vertex data on mesh '{m.name}' at array index {i} (isReadable is false; Read/Write must be enabled in import settings)"); } } ApplyToMeshesImpl(meshes, m_Ptrs, m_Length, flags); Dispose(); }
void FillDiamond(ref Mesh diamond, ref DiamondData data) { _vertices[0].pos.x = data.pt1.x; _vertices[0].pos.y = data.pt1.y; _vertices[0].uv.x = data.uv1.x; _vertices[0].uv.y = data.uv1.y; _vertices[1].pos.x = data.pt2.x; _vertices[1].pos.y = data.pt2.y; _vertices[1].uv.x = data.uv2.x; _vertices[1].uv.y = data.uv2.y; _vertices[2].pos.x = data.pt3.x; _vertices[2].pos.y = data.pt3.y; _vertices[2].uv.x = data.uv3.x; _vertices[2].uv.y = data.uv3.y; _vertices[3].pos.x = data.pt4.x; _vertices[3].pos.y = data.pt4.y; _vertices[3].uv.x = data.uv4.x; _vertices[3].uv.y = data.uv4.y; _vertices[4].pos.x = data.pt5.x; _vertices[4].pos.y = data.pt5.y; _vertices[4].uv.x = data.uv5.x; _vertices[4].uv.y = data.uv5.y; MeshUpdateFlags flag = GetMeshUpdateFlags(); const int indexCount = 9; const int vertexCount = 5; // 設定 Mesh Topologiy SubMeshDescriptor desc = new SubMeshDescriptor(0, indexCount, MeshTopology.Triangles); diamond.SetSubMesh(0, desc, flag); // 宣告 index buffer 結構 diamond.SetIndexBufferParams(indexCount, IndexFormat.UInt16); // 宣告 vertex buffer 結構 diamond.SetVertexBufferParams(vertexCount, _layouts); diamond.SetVertexBufferData(_vertices, 0, 0, vertexCount, 0, flag); diamond.SetIndexBufferData(_indices, 0, 0, indexCount, flag); }
public void ApplyMeshData(ref Mesh mesh) { // 'is object' to bypass unity lifetime check for null if (!(mesh is object)) { throw new NullReferenceException(nameof(mesh)); } else { mesh.Clear(); } if ((_Vertices.Count == 0) || (_Triangles.Count == 0)) { return; } if ((int)((_Vertices.Count / 2f) * 1.5f) != _Triangles.Sum(triangles => triangles.Count)) { throw new ArgumentOutOfRangeException($"Sum of all {_Triangles} should be 1.5x as many vertices."); } const MeshUpdateFlags default_flags = MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontResetBoneBounds; mesh.SetVertexBufferParams(_Vertices.Count, _Layout); mesh.SetVertexBufferData(_Vertices, 0, 0, _Vertices.Count, 0, default_flags); mesh.subMeshCount = _Triangles.Count; foreach (List <int> triangles in _Triangles.Where(triangles => triangles.Count != 0)) { mesh.SetIndexBufferParams(triangles.Count, IndexFormat.UInt32); mesh.SetIndexBufferData(triangles, 0, 0, triangles.Count, default_flags); mesh.SetSubMesh(0, new SubMeshDescriptor(0, triangles.Count), default_flags); } mesh.bounds = new Bounds(new float3(GenerationConstants.CHUNK_SIZE / 2), new float3(GenerationConstants.CHUNK_SIZE)); }
public override void ApplyOnMesh(UnityEngine.Mesh msh, MeshUpdateFlags flags = MeshUpdateFlags.Default) { Profiler.BeginSample("ApplyOnMesh"); if (vad == null) { CreateDescriptors(); } Profiler.BeginSample("SetVertexBufferParams"); msh.SetVertexBufferParams(vData.Length, vad); Profiler.EndSample(); Profiler.BeginSample("SetVertexBufferData"); int stream = 0; msh.SetVertexBufferData(vData, 0, 0, vData.Length, stream, flags); stream++; Profiler.EndSample(); if (texCoords != null) { texCoords.ApplyOnMesh(msh, stream, flags); stream++; } if (colors != null) { colors.ApplyOnMesh(msh, stream, flags); stream++; } if (bones != null) { bones.ApplyOnMesh(msh, stream, flags); stream++; } Profiler.EndSample(); }
static private bool createMesh(ref LodVertex[] vertices, ref ushort[] indices, out Mesh mesh) { mesh = null; if (vertices.Length == 0 || indices.Length == 0) { return(false); } mesh = new Mesh(); // 宣告 vertex buffer 結構 VertexAttributeDescriptor[] layouts = new VertexAttributeDescriptor[] { new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3), new VertexAttributeDescriptor(VertexAttribute.Color, VertexAttributeFormat.UNorm8, 4), new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.UInt32, 1) }; mesh.SetVertexBufferParams(vertices.Length, layouts); // 宣告 index buffer 結構 mesh.SetIndexBufferParams(indices.Length, IndexFormat.UInt16); MeshUpdateFlags flag = MeshUpdateFlags.Default; mesh.SetVertexBufferData(vertices, 0, 0, vertices.Length, 0, flag); mesh.SetIndexBufferData(indices, 0, 0, indices.Length, flag); // 設定 Mesh Topologiy SubMeshDescriptor desc = new SubMeshDescriptor(0, indices.Length, MeshTopology.Triangles); mesh.SetSubMesh(0, desc, flag); return(true); }
void FillTriangle(ref Mesh triangle, ref TriangleData data) { _vertices[0].pos.x = data.pt1.x; _vertices[0].pos.y = data.pt1.y; _vertices[0].uv.x = data.uv1.x; _vertices[0].uv.y = data.uv1.y; _vertices[1].pos.x = data.pt2.x; _vertices[1].pos.y = data.pt2.y; _vertices[1].uv.x = data.uv2.x; _vertices[1].uv.y = data.uv2.y; _vertices[2].pos.x = data.pt3.x; _vertices[2].pos.y = data.pt3.y; _vertices[2].uv.x = data.uv3.x; _vertices[2].uv.y = data.uv3.y; MeshUpdateFlags flag = GetMeshUpdateFlags(); const int indexCount = 3; const int vertexCount = 3; // 設定 Mesh Topologiy SubMeshDescriptor desc = new SubMeshDescriptor(0, indexCount, MeshTopology.Triangles); triangle.SetSubMesh(0, desc, flag); // 宣告 index buffer 結構 triangle.SetIndexBufferParams(indexCount, IndexFormat.UInt16); // 宣告 vertex buffer 結構 triangle.SetVertexBufferParams(vertexCount, _layouts); triangle.SetVertexBufferData(_vertices, 0, 0, vertexCount, 0, flag); triangle.SetIndexBufferData(_indices, 0, 0, indexCount, flag); }
[NativeMethod(IsThreadSafe = true, ThrowsException = true)] static extern void SetSubMeshImpl(IntPtr self, int index, SubMeshDescriptor desc, MeshUpdateFlags flags);
public abstract void ApplyOnMesh(UnityEngine.Mesh msh, int stream, MeshUpdateFlags flags = PrimitiveCreateContextBase.defaultMeshUpdateFlags);
public override void ApplyOnMesh(UnityEngine.Mesh msh, int stream, MeshUpdateFlags flags = PrimitiveCreateContextBase.defaultMeshUpdateFlags) { Profiler.BeginSample("ApplyUVs"); msh.SetVertexBufferData(vData, 0, 0, vData.Length, stream, flags); Profiler.EndSample(); }
public abstract void ApplyOnMesh(UnityEngine.Mesh msh, MeshUpdateFlags flags = MeshUpdateFlags.Default);
public static bool Generate(float size, out Mesh _mesh) { const int VERTICES_COUNT = 8; float halfSize = size * 0.5f; // vertex CubeVertex[] vertices = new CubeVertex[VERTICES_COUNT]; vertices[0].pos = new Vector3(-halfSize, -halfSize, -halfSize); vertices[1].pos = new Vector3(-halfSize, halfSize, -halfSize); vertices[2].pos = new Vector3(halfSize, halfSize, -halfSize); vertices[3].pos = new Vector3(halfSize, -halfSize, -halfSize); vertices[4].pos = new Vector3(-halfSize, -halfSize, halfSize); vertices[5].pos = new Vector3(-halfSize, halfSize, halfSize); vertices[6].pos = new Vector3(halfSize, halfSize, halfSize); vertices[7].pos = new Vector3(halfSize, -halfSize, halfSize); for (int i = 0; i < VERTICES_COUNT; ++i) { vertices[i].normal = Vector3.Normalize(vertices[i].pos); } // index ushort[] indices = new ushort[] { 0, 1, 2, 2, 3, 0, 0, 4, 5, 5, 1, 0, 2, 6, 7, 7, 3, 2, 7, 6, 5, 5, 4, 7, 1, 5, 6, 6, 2, 1, 4, 0, 3, 3, 7, 4 }; MeshUpdateFlags flag = MeshUpdateFlags.Default; VertexAttributeDescriptor[] layouts = new VertexAttributeDescriptor[] { new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3), new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3), }; _mesh = new Mesh(); // 宣告 index buffer 結構 _mesh.SetIndexBufferParams(indices.Length, IndexFormat.UInt16); // 宣告 vertex buffer 結構 _mesh.SetVertexBufferParams(VERTICES_COUNT, layouts); _mesh.SetVertexBufferData(vertices, 0, 0, VERTICES_COUNT, 0, flag); _mesh.SetIndexBufferData(indices, 0, 0, indices.Length, flag); // 設定 Mesh Topologiy SubMeshDescriptor desc = new SubMeshDescriptor(0, indices.Length, MeshTopology.Triangles); _mesh.SetSubMesh(0, desc, flag); return(true); }
public override Primitive?CreatePrimitive() { Profiler.BeginSample("CreatePrimitive"); jobHandle.Complete(); var msh = new UnityEngine.Mesh(); msh.name = mesh.name; MeshUpdateFlags flags = MeshUpdateFlags.DontNotifyMeshUsers | MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontResetBoneBounds | MeshUpdateFlags.DontValidateIndices; vertexData.ApplyOnMesh(msh, flags); Profiler.BeginSample("SetIndices"); int indexCount = 0; for (int i = 0; i < indices.Length; i++) { indexCount += indices[i].Length; } Profiler.BeginSample("SetIndexBufferParams"); msh.SetIndexBufferParams(indexCount, IndexFormat.UInt32); //TODO: UInt16 maybe? Profiler.EndSample(); msh.subMeshCount = indices.Length; indexCount = 0; for (int i = 0; i < indices.Length; i++) { Profiler.BeginSample("SetIndexBufferData"); msh.SetIndexBufferData(indices[i], 0, indexCount, indices[i].Length, flags); Profiler.EndSample(); Profiler.BeginSample("SetSubMesh"); msh.SetSubMesh(i, new SubMeshDescriptor(indexCount, indices[i].Length, topology), flags); Profiler.EndSample(); indexCount += indices[i].Length; } Profiler.EndSample(); if (vertexData.calculateNormals) { Profiler.BeginSample("RecalculateNormals"); msh.RecalculateNormals(); Profiler.EndSample(); } if (vertexData.calculateTangents) { Profiler.BeginSample("RecalculateTangents"); msh.RecalculateTangents(); Profiler.EndSample(); } Profiler.BeginSample("RecalculateBounds"); msh.RecalculateBounds(); // TODO: make optional! maybe calculate bounds in Job. Profiler.EndSample(); #if GLTFAST_KEEP_MESH_DATA Profiler.BeginSample("UploadMeshData"); msh.UploadMeshData(false); Profiler.EndSample(); #else /// Don't upload explicitely. Unity takes care of upload on demand/deferred // Profiler.BeginSample("UploadMeshData"); // msh.UploadMeshData(true); // Profiler.EndSample(); #endif Profiler.BeginSample("Dispose"); Dispose(); Profiler.EndSample(); Profiler.EndSample(); return(new Primitive(msh, materials)); }
public void SetSubMesh(int index, SubMeshDescriptor desc, MeshUpdateFlags flags = MeshUpdateFlags.Default) { CheckWriteAccess(); SetSubMeshImpl(m_Ptr, index, desc, flags); }
[NativeThrows] static extern unsafe void ApplyToMeshesImpl([NotNull] Mesh[] meshes, IntPtr *datas, int count, MeshUpdateFlags flags);
// Display routine for 2d examples in the main program void display2() { // to push and pop location and angle Stack <float> positions = new Stack <float>(); Stack <float> angles = new Stack <float>(); // current angle and position float angle = 0f; float3 position = new float3(0, 0, 0); float posy = 0.0f; float posx = 0.0f; // positions to draw towards float3 newPosition; float2 rotated; // start at 0,0,0 // Apply the drawing rules to the string given to us for (int i = 0; i < lang.Count; i++) { byte buff = lang[i]; switch (buff) { case 0: // draw a line ending in a leaf posy += initial_length; newPosition = new float3(position.x, posy, 0); rotated = rotate(position, new float3(position.x, posy, 0), angle); newPosition = new float3(rotated.x, rotated.y, 0); addLineToMesh(lineMesh, position, new float3(rotated.x, rotated.y, 0), Color.green); //drawLSystemLine(position, new Vector3(rotated.x, rotated.y, 0), line, Color.red); // set up for the next draw position = newPosition; posx = newPosition.x; posy = newPosition.y; addCircleToMesh(lineMesh, 0.45f, 0.45f, position, Color.magenta); break; case 1: // draw a line posy += initial_length; newPosition = new float3(position.x, posy, 0); rotated = rotate(position, new float3(position.x, posy, 0), angle); newPosition = new float3(rotated.x, rotated.y, 0); //drawLSystemLine(position, newPosition, line, Color.green); addLineToMesh(lineMesh, position, newPosition, Color.green); // set up for the next draw position = newPosition; posx = newPosition.x; posy = newPosition.y; break; case 6: //[: push position and angle, turn left 45 degrees positions.Push(posy); positions.Push(posx); float currentAngle = angle; angles.Push(currentAngle); angle -= 45; break; case 9: //]: pop position and angle, turn right 45 degrees posx = positions.Pop(); posy = positions.Pop(); position = new float3(posx, posy, 0); angle = angles.Pop(); angle += 45; break; default: break; } // after we recreate the mesh we need to assign it to the original object MeshUpdateFlags flags = MeshUpdateFlags.DontNotifyMeshUsers & MeshUpdateFlags.DontRecalculateBounds & MeshUpdateFlags.DontResetBoneBounds & MeshUpdateFlags.DontValidateIndices; // set vertices var layout = new[] { new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3), new VertexAttributeDescriptor(VertexAttribute.Color, VertexAttributeFormat.UNorm8, 4) }; lineMesh.SetVertexBufferParams(vertices.Count, layout); lineMesh.SetVertexBufferData(vertices, 0, 0, vertices.Count, 0, flags); // set indices UnityEngine.Rendering.IndexFormat format = IndexFormat.UInt32; lineMesh.SetIndexBufferParams(indices.Count, format); lineMesh.SetIndexBufferData(indices, 0, 0, indices.Count, flags); // set submesh SubMeshDescriptor desc = new SubMeshDescriptor(0, indices.Count, MeshTopology.Lines); desc.bounds = new Bounds(); desc.baseVertex = 0; desc.firstVertex = 0; desc.vertexCount = vertices.Count; lineMesh.SetSubMesh(0, desc, flags); } }
public override void ApplyOnMesh(UnityEngine.Mesh msh, int stream, MeshUpdateFlags flags = MeshUpdateFlags.Default) { Profiler.BeginSample("ApplyBones"); msh.SetVertexBufferData(vData, 0, 0, vData.Length, stream, flags); Profiler.EndSample(); }
internal void Rebuild() { // no logic during play. if (Application.isPlaying) { return; } // create a new mesh or clear the existing one. Mesh mesh = ResetDecalMesh(); // update the flags we need to tell if we have to rebuild the decal mesh. UpdateDirtyFlags(); // find all mesh colliders that intersect with the decal projector box. List <MeshCollider> meshColliders = FindMeshColliders(); int meshCollidersCount = meshColliders.Count; if (meshCollidersCount == 0) { return; // early out. } List <Vector3> vertices = new List <Vector3>(6 * meshCollidersCount); List <int> triangles = new List <int>(6 * meshCollidersCount); List <Vector2> uvs = new List <Vector2>(6 * meshCollidersCount); // precalculate values that never change from this point on. MeshUpdateFlags meshUpdateFlags = MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontNotifyMeshUsers | MeshUpdateFlags.DontResetBoneBounds; Bounds projectorBounds = GetBounds(); projectorBounds.center = transform.position - projectorBounds.center; Vector3 r = transform.TransformVector(Vector3.right * 0.5f); Vector3 f = transform.TransformVector(Vector3.forward * 0.5f); Vector3 u = transform.TransformVector(Vector3.up * 0.5f); float uscale = transform.InverseTransformVector(transform.right).x *uvTiling.x; float vscale = transform.InverseTransformVector(transform.up).y *uvTiling.y; float uoffset = 0.5f * uvTiling.x - uvOffset.x; float voffset = 0.5f * uvTiling.y + uvOffset.y; Clipping clipping = new Clipping(new Plane[] { new Plane(-r, transform.position + r), new Plane(r, transform.position - r), new Plane(-f, transform.position + f), new Plane(f, transform.position - f), new Plane(-u, transform.position + u + f), new Plane(u, transform.position - u + f) }); // create a horizontal and vertical plane through the projector box. // the distance of the vertices to the planes are used to calculate UV coordinates. Plane hplane = new Plane(u, transform.position); Plane vplane = new Plane(r, transform.position); float maxAngleCos = Mathf.Cos(Mathf.Deg2Rad * (180.0f - maxAngle)); float zOffsetMultiplier = 0.001f + (zOffset * 0.001f); // optimization: recycle the same polygon list used for clipping. Vector3[] poly = new Vector3[8]; int polyCount = 0; for (int mc = 0; mc < meshCollidersCount; mc++) { var meshCollider = meshColliders[mc]; Mesh colliderMesh = meshCollider.sharedMesh; // make sure an octree of this mesh exists. if (!meshTriangleOctrees.ContainsKey(colliderMesh)) { meshTriangleOctrees.Add(colliderMesh, new BoundsOctree <BoundsOctreeTriangle>(8.0f, meshCollider.transform.position, 4.0f, 1.0f)); // get the collider vertices and triangles. Vector3[] colliderVertices = colliderMesh.vertices; int[] colliderTriangles = colliderMesh.GetTriangles(0); // build the octree. for (int i = 0; i < colliderTriangles.Length; i += 3) { // fetch a triangle from the collider. Vector3 colliderVertex1 = colliderVertices[colliderTriangles[i]]; Vector3 colliderVertex2 = colliderVertices[colliderTriangles[i + 1]]; Vector3 colliderVertex3 = colliderVertices[colliderTriangles[i + 2]]; // apply any modification from their transform. colliderVertex1 = meshCollider.transform.TransformPoint(colliderVertex1); colliderVertex2 = meshCollider.transform.TransformPoint(colliderVertex2); colliderVertex3 = meshCollider.transform.TransformPoint(colliderVertex3); // now the mesh vertices are in world space 1:1 to how they appear in the scene. // calculate the triangle bounds. Bounds triangleBounds = new Bounds(colliderVertex1, Vector3.zero); triangleBounds.Encapsulate(colliderVertex2); triangleBounds.Encapsulate(colliderVertex3); // add the triangle to the octree. meshTriangleOctrees[colliderMesh].Add(new BoundsOctreeTriangle() { vertex1 = colliderVertex1, vertex2 = colliderVertex2, vertex3 = colliderVertex3, normal = GetNormal(colliderVertex1, colliderVertex2, colliderVertex3) }, triangleBounds); } } // find all triangles inside of the projector bounds using the octree. List <BoundsOctreeTriangle> trianglesInsideProjector = new List <BoundsOctreeTriangle>(); meshTriangleOctrees[colliderMesh].GetColliding(trianglesInsideProjector, projectorBounds); int trianglesInsideProjectorCount = trianglesInsideProjector.Count; // optimization: ensure the decal mesh list capacities are large enough to contain all // of the triangles. By themselves the lists would reallocate their array many times. if (vertices.Capacity < trianglesInsideProjectorCount) { vertices.Capacity = trianglesInsideProjectorCount; triangles.Capacity = trianglesInsideProjectorCount; uvs.Capacity = trianglesInsideProjectorCount; } // iterate over all triangles inside of the projector bounds: for (int i = 0; i < trianglesInsideProjectorCount; i++) { // fetch a triangle from the octree. BoundsOctreeTriangle triangle = trianglesInsideProjector[i]; // the mesh vertices are in world space 1:1 to how they appear in the scene. // if the triangle exceeds the maximum angle we discard it. if (Vector3.Dot(transform.forward, triangle.normal) >= maxAngleCos) { continue; } // optimization?: if the triangle is wholly inside of the projector we don't have to clip it. // clip the triangle to fit inside of the projector box. poly[0] = triangle.vertex1; poly[1] = triangle.vertex2; poly[2] = triangle.vertex3; polyCount = clipping.ClipTriangle(poly); if (polyCount >= 3) { Vector3[] triangulated; int triangulatedCount; // only triangulate if required: if (polyCount > 3) { triangulated = Triangulate(poly, polyCount); triangulatedCount = triangulated.Length; } else { triangulated = poly; triangulatedCount = 3; } for (int tr = 0; tr < triangulatedCount; tr += 3) { Vector3 colliderVertex1 = triangulated[tr + 0]; Vector3 colliderVertex2 = triangulated[tr + 1]; Vector3 colliderVertex3 = triangulated[tr + 2]; // use the horizontal and vertical plane through the projector box. // the distance of the vertices to the planes are used to calculate UV coordinates. Vector2 uv1 = new Vector2((vplane.GetDistanceToPoint(colliderVertex1) * uscale) + uoffset, (hplane.GetDistanceToPoint(colliderVertex1) * vscale) + voffset); Vector2 uv2 = new Vector2((vplane.GetDistanceToPoint(colliderVertex2) * uscale) + uoffset, (hplane.GetDistanceToPoint(colliderVertex2) * vscale) + voffset); Vector2 uv3 = new Vector2((vplane.GetDistanceToPoint(colliderVertex3) * uscale) + uoffset, (hplane.GetDistanceToPoint(colliderVertex3) * vscale) + voffset); // calculate the z-offset. Vector3 normal = triangle.normal * zOffsetMultiplier; // undo our transformation so that the mesh looks correct in the scene. colliderVertex1 = transform.InverseTransformPoint(colliderVertex1 + normal); colliderVertex2 = transform.InverseTransformPoint(colliderVertex2 + normal); colliderVertex3 = transform.InverseTransformPoint(colliderVertex3 + normal); // add vertices to the decal mesh. vertices.Add(colliderVertex1); triangles.Add(vertices.Count - 1); uvs.Add(uv1); vertices.Add(colliderVertex2); triangles.Add(vertices.Count - 1); uvs.Add(uv2); vertices.Add(colliderVertex3); triangles.Add(vertices.Count - 1); uvs.Add(uv3); } } } } if (vertices.Count > 0) { mesh.SetVertices(vertices); mesh.SetTriangles(triangles, 0); mesh.SetUVs(0, uvs); mesh.RecalculateNormals(meshUpdateFlags); mesh.RecalculateTangents(meshUpdateFlags); } }
[NativeThrows] static extern void ApplyToMeshImpl([NotNull] Mesh mesh, IntPtr data, MeshUpdateFlags flags);
//struct PtrStruct { public IntPtr m_Ptr; } public static unsafe void ApplyAndDisposeWritableMeshData(this Mesh.MeshDataArray @this, Mesh[] meshes, int realLength, MeshUpdateFlags flags = MeshUpdateFlags.Default) { if (meshes == null) { throw new ArgumentNullException(nameof(meshes), "Mesh list is null"); } if (@this.Length < realLength) { throw new InvalidOperationException($"{nameof(Mesh.MeshDataArray)} length ({@this.Length}) cannot be less than destination meshes length ({realLength})"); } if (meshes.Length < realLength) { throw new InvalidOperationException($"{nameof(meshes)} length ({meshes.Length}) cannot be less than destination meshes length ({realLength})"); } if (realLength == 0) { @this.Dispose(); return; } for (int i = 0; i < realLength; ++i) { Mesh m = meshes[i]; if (m == null) { throw new ArgumentNullException(nameof(meshes), $"Mesh at index {i} is null"); } } // UNTESTED var ptrs = (IntPtr *)UnsafeUtility.AddressOf(ref @this); // First value of Mesh.MeshDataArray should be IntPtr* /* * var ptrs = stackalloc IntPtr[realLength]; * for (int i = 0; i < realLength; i++) * { * var meshData = @this[i]; * //ptrs[i] = ((PtrStruct*)&meshData)->m_Ptr; * ptrs[i] = *((IntPtr*)&meshData); * }*/ ApplyToMeshesImpl(meshes, ptrs, realLength, flags); @this.Dispose(); }