private void CombineContours(MeshOutput o) { for (int i = o.contours.Count - 1; i > 0; i--) { for (int k = i - 1; k >= 0; k--) { bool merged = false; Vector2 ifirst = o.contours[i][0]; Vector2 ilast = o.contours[i][o.contours[i].Count - 1]; Vector2 kfirst = o.contours[k][0]; Vector2 klast = o.contours[k][o.contours[k].Count - 1]; if (Mathf.Approximately(klast.x, ifirst.x) && Mathf.Approximately(klast.y, ifirst.y)) { o.contours[k].RemoveAt(o.contours[k].Count - 1); o.contours[k].AddRange(o.contours[i]); merged = true; } else if (Mathf.Approximately(kfirst.x, ilast.x) && Mathf.Approximately(kfirst.y, ilast.y)) { o.contours[i].RemoveAt(o.contours[i].Count - 1); o.contours[i].AddRange(o.contours[k]); o.contours[k] = o.contours[i]; merged = true; } if (merged == true) { o.contours.RemoveAt(i); break; } } } }
private bool AppendContour(Vector2 from, Vector2 to, MeshOutput o) { for (int i = 0; i < o.contours.Count; i++) { if (o.contours[i].Count > 1) { Vector2 first = o.contours[i][0]; Vector2 second = o.contours[i][1]; Vector2 last = o.contours[i][o.contours[i].Count - 1]; Vector2 beforeLast = o.contours[i][o.contours[i].Count - 2]; if (Mathf.Approximately(last.x, from.x) && Mathf.Approximately(last.y, from.y)) { Vector2 ab = to - from; Vector2 cd = last - beforeLast; float a = Vector2.Angle(ab, cd); if (Mathf.Approximately(a, 0.0f)) { o.contours[i][o.contours[i].Count - 1] = to; } else { o.contours[i].Add(to); } return(true); } else if (Mathf.Approximately(first.x, to.x) && Mathf.Approximately(first.y, to.y)) { Vector2 ab = to - from; Vector2 cd = second - first; float a = Vector2.Angle(ab, cd); if (Mathf.Approximately(a, 0.0f)) { o.contours[i][0] = from; } else { o.contours[i].Insert(0, from); } return(true); } } } List <Vector2> ctr = new List <Vector2>(); ctr.Add(from); ctr.Add(to); o.contours.Add(ctr); return(false); }
private void BuildCollision(MeshOutput o) { float extent = m_settings.collisionExtrudeExtent; int verts = 0; for (int i = 0; i < o.contours.Count; i++) { List <Vector2> ctr = o.contours[i]; Vector2 prev = ctr[0]; for (int p = 1; p < ctr.Count; p++) { Vector2 next = ctr[p]; Vector3 afar = new Vector3(prev.x, prev.y, extent); Vector3 anear = new Vector3(prev.x, prev.y, -extent); Vector3 bfar = new Vector3(next.x, next.y, extent); Vector3 bnear = new Vector3(next.x, next.y, -extent); o.collisionVerts.Add(anear); o.collisionVerts.Add(afar); o.collisionVerts.Add(bnear); o.collisionVerts.Add(bfar); o.collisionTris.Add(verts); o.collisionTris.Add(verts + 1); o.collisionTris.Add(verts + 2); o.collisionTris.Add(verts + 2); o.collisionTris.Add(verts + 1); o.collisionTris.Add(verts + 3); verts += 4; prev = next; } } }
/// <summary> /// This is the main workhorse method which runs in another thread. /// </summary> /// <remarks> /// Takes the MeshInput(s) and converts them to TransitionMeshes in parallel. It then creates optimized /// MeshOutput(s) for use later or through the callback. /// </remarks> protected sealed override void ThreadedFunction() { // Empty out preexisting parsed data _transitionMeshes.Clear(); _meshOutputs.Clear(); // Clever forker solution since we don't have Parallel.ForEach support available. var parallelTasks = new Forker(); foreach (var meshInput in _meshInputs) { var tempInput = meshInput; parallelTasks.Fork(delegate { CreateTransitionMesh(tempInput); }); } parallelTasks.Join(); // Sort the meshes in order _transitionMeshes.Sort(new TransitionMeshSorter()); var meshOutput = new MeshOutput(); int bitmask = _transitionMeshes [0].GetBitMask(); foreach (var transitionMesh in _transitionMeshes) { if (transitionMesh.GetBitMask() != bitmask || (transitionMesh.VertexCount + meshOutput.VertexCount) > Mesh.VerticesArrayLimit) { _meshOutputs.Add(meshOutput); meshOutput = new MeshOutput(); } var baseIndex = meshOutput.VertexCount; meshOutput.VertexCount += transitionMesh.VertexCount; meshOutput.SortedSources.Add(transitionMesh); meshOutput.Vertices.AddRange(transitionMesh.Vertices); if (transitionMesh.Normals != null) { meshOutput.Normals.AddRange(transitionMesh.Normals); } if (transitionMesh.Colors != null) { meshOutput.Colors.AddRange(transitionMesh.Colors); } if (transitionMesh.Tangents != null) { meshOutput.Tangents.AddRange(transitionMesh.Tangents); } if (transitionMesh.UV != null) { meshOutput.UV.AddRange(transitionMesh.UV); } if (transitionMesh.UV1 != null) { meshOutput.UV1.AddRange(transitionMesh.UV1); } if (transitionMesh.UV2 != null) { meshOutput.UV2.AddRange(transitionMesh.UV2); } var indexes = meshOutput.GetSubMesh(transitionMesh.Material); indexes.Capacity = indexes.Count + transitionMesh.IndexCount; for (var i = 0; i < transitionMesh.IndexCount; i++) { indexes.Add(baseIndex + transitionMesh.Indexes [i]); } bitmask = transitionMesh.GetBitMask(); } _meshOutputs.Add(meshOutput); }
/// <summary> /// Creates a MeshObject from the passed MeshOutput. /// </summary> /// <returns>The created MeshObject.</returns> /// <param name="meshOutput">The source MeshOutput to use in creating the MeshObject.</param> /// <param name="instanceMaterials">If set to <c>true</c> materials will be instanced.</param> public MeshObject CreateMeshObject(MeshOutput meshOutput, bool instanceMaterials) { var meshObject = new MeshObject(); meshObject.Materials = instanceMaterials ? GetMaterialInstances(meshOutput.Materials.ToArray()) : GetMaterials(meshOutput.Materials.ToArray()); meshObject.Mesh = new UnityEngine.Mesh(); meshObject.Mesh.vertices = meshOutput.Vertices.ToArray(); meshObject.Mesh.name = "Combined Mesh (" + meshObject.Mesh.vertices.GetHashCode() + ")"; // If there are normals we need to assign them to the mesh. if (meshOutput.Normals != null) { meshObject.Mesh.normals = meshOutput.Normals.ToArray(); } // Much like normals, if we've got tangents lets throw them on there too. if (meshOutput.Tangents != null) { meshObject.Mesh.tangents = meshOutput.Tangents.ToArray(); } // How about some vertex color data? Sounds like a good idea to add that too. if (meshOutput.Colors != null) { meshObject.Mesh.colors = meshOutput.Colors.ToArray(); } // Better make those textures work too while were at it. if (meshOutput.UV != null) { meshObject.Mesh.uv = meshOutput.UV.ToArray(); } // How about some more UV's? if (meshOutput.UV1 != null) { meshObject.Mesh.uv1 = meshOutput.UV1.ToArray(); } // Lightmapping UV's anyone? if (meshOutput.UV2 != null) { meshObject.Mesh.uv2 = meshOutput.UV2.ToArray(); } meshObject.Mesh.subMeshCount = meshOutput.Indexes.Count; for (int i = 0; i < meshOutput.Indexes.Count; i++) { meshObject.Mesh.SetIndices(meshOutput.Indexes [i].ToArray(), MeshTopology.Triangles, i); } // Recalculate mesh's bounds for fun. meshObject.Mesh.RecalculateBounds(); // Return our processed object. return(meshObject); }
/// <summary> /// Creates a MeshObject from the MeshOutput (Instanced Materials). /// </summary> /// <returns>The created MeshObject.</returns> /// <param name="meshOutput">The source MeshOutput to use in creating the MeshObject.</param> public MeshObject CreateMeshObject(MeshOutput meshOutput) { return(CreateMeshObject(meshOutput, true)); }
private void GenerateGreedyQuad(IReadableDataset <VoxelData> data, IntRect region, int x, int y, MeshOutput o) { float voxelSize = m_settings.voxelSize; int startX = x; int startY = y; int endX = x; int endY = y; VoxelData compare = data.Sample(region.origin.x + x, region.origin.y + y); for (int xx = x + 1; xx < region.size.x - 1; xx++) { if (o.batched[y * m_maxWidth + xx] == true) { break; } // Encountered another batch, stop expanding VoxelData a = data.Sample(region.origin.x + xx + 1, region.origin.y + y); VoxelData b = data.Sample(region.origin.x + xx + 1, region.origin.y + y + 1); if (compare.CompareWithoutExtent(a) && compare.CompareWithoutExtent(b)) { endX = xx; } else { break; } // Change in voxel data detected, stop extending } bool validUp = true; for (int yy = y + 1; yy < region.size.y - 1 && validUp == true; yy++) { for (int xx = startX; xx <= endX && validUp == true; xx++) { if (o.batched[yy * m_maxWidth + xx] == true) { validUp = false; break; } // Encountered another batch, stop expanding VoxelData a = data.Sample(region.origin.x + xx + 1, region.origin.y + yy + 1); VoxelData b = data.Sample(region.origin.x + xx, region.origin.y + yy + 1); if (compare.CompareWithoutExtent(a) == false || compare.CompareWithoutExtent(b) == false) { validUp = false; break; } } if (validUp == true) // Collect line { endY = yy; } } // Mark batch for (int yy = startY; yy <= endY; yy++) { for (int xx = startX; xx <= endX; xx++) { o.batched[yy * m_maxWidth + xx] = true; } } // Build quad mesh data float x0 = startX * voxelSize; float x1 = (endX + 1) * voxelSize; float y0 = startY * voxelSize; float y1 = (endY + 1) * voxelSize; Vector3 v0 = new Vector3(x0, y0); Vector3 v1 = new Vector3(x0, y1); Vector3 v2 = new Vector3(x1, y1); Vector3 v3 = new Vector3(x1, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.tris.Add(vcount); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); }
public void Initialize(GeneratorSettings settings, int maxWidth, int maxHeight) { nextPosition = transform.position; m_maxWidth = maxWidth; m_maxHeight = maxHeight; m_settings = settings; // Initialize mesh m_mesh = new Mesh(); m_mesh.name = name + "_Mesh"; m_mesh.MarkDynamic(); MeshFilter filter = gameObject.AddComponent <MeshFilter>(); m_renderer = gameObject.AddComponent <MeshRenderer>(); filter.sharedMesh = m_mesh; m_renderer.sharedMaterial = m_settings.fillMaterial; if (m_settings.generateCollision) { m_meshCollision = new Mesh(); m_meshCollision.name = name + "_CollisionMesh"; m_meshCollision.MarkDynamic(); m_meshCollision.bounds = m_mesh.bounds; m_collider = gameObject.AddComponent <MeshCollider>(); m_collider.sharedMesh = m_meshCollision; m_collider.convex = false; m_collider.cookingOptions = m_settings.colliderCookingOptions; } if (m_settings.meshContour) { GameObject contour = new GameObject("Contour"); contour.transform.parent = transform; contour.transform.localPosition = Vector3.zero; contour.transform.localScale = Vector3.one; contour.transform.localRotation = Quaternion.identity; m_meshContour = new Mesh(); m_meshContour.name = name + "_MeshContour"; m_meshContour.MarkDynamic(); m_meshContour.bounds = m_mesh.bounds; MeshFilter contourFilter = contour.AddComponent <MeshFilter>(); contourFilter.sharedMesh = m_meshContour; m_rendererContour = contour.AddComponent <MeshRenderer>(); m_rendererContour.sharedMaterial = settings.outlineMaterial; } // Initialize chunk data m_meshOut = new MeshOutput(); m_meshOut.verts = new List <Vector3>(); m_meshOut.tris = new List <int>(); m_meshOut.uvs = new List <Vector2>(); if (m_settings.generateNormals) { m_meshOut.normals = new List <Vector3>(); } // Initialize temp buffers m_meshOut.batched = new bool[m_maxWidth * m_maxHeight]; m_meshOut.contours = new List <List <Vector2> >(); // Contour buffers if (m_settings.meshContour) { m_meshOut.contourVerts = new List <Vector3>(); m_meshOut.contourTris = new List <int>(); m_meshOut.contourUVs = new List <Vector2>(); if (m_settings.generateNormals) { m_meshOut.contourNormals = new List <Vector3>(); } } // Collision buffers if (m_settings.generateCollision) { m_meshOut.collisionVerts = new List <Vector3>(); m_meshOut.collisionTris = new List <int>(); } }
private void GenerateMesh(IReadableDataset <VoxelData> data, IntRect region, MeshOutput o) { float voxelSize = m_settings.voxelSize; for (int y = 0; y < region.size.y - 1; y++) { for (int x = 0; x < region.size.x - 1; x++) { if (o.batched[y * m_maxWidth + x] == true) { continue; } // Cell is already a part of existing batch, skip it... VoxelData bottomLeft = data.Sample(region.origin.x + x, region.origin.y + y); // v = 1 VoxelData bottomRight = data.Sample(region.origin.x + x + 1, region.origin.y + y); // v = 2 VoxelData topRight = data.Sample(region.origin.x + x + 1, region.origin.y + y + 1); // v = 4 VoxelData topLeft = data.Sample(region.origin.x + x, region.origin.y + y + 1); // v = 8 byte index = 0; if ((bottomLeft.cell & VoxelData.CELL_MASK_SOLID) > 0) { index |= (1 << 0); } if ((bottomRight.cell & VoxelData.CELL_MASK_SOLID) > 0) { index |= (1 << 1); } if ((topRight.cell & VoxelData.CELL_MASK_SOLID) > 0) { index |= (1 << 2); } if ((topLeft.cell & VoxelData.CELL_MASK_SOLID) > 0) { index |= (1 << 3); } float x0 = x * voxelSize; float x1 = (x + 1) * voxelSize; float y0 = y * voxelSize; float y1 = (y + 1) * voxelSize; switch (index) { case 0: break; // Empty cell case 1: { float xstep = bottomLeft.GetExtentRightNormalized() * voxelSize; float ystep = bottomLeft.GetExtentTopNormalized() * voxelSize; Vector3 v0 = new Vector3(x0, y0); Vector3 v1 = new Vector3(x0, y0 + ystep); Vector3 v2 = new Vector3(x0 + xstep, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); AppendContour(v1, v2, o); } break; case 2: { float xstep = bottomRight.GetExtentLeftNormalized() * voxelSize; float ystep = bottomRight.GetExtentTopNormalized() * voxelSize; Vector3 v0 = new Vector3(x1 - xstep, y0); Vector3 v1 = new Vector3(x1, y0 + ystep); Vector3 v2 = new Vector3(x1, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); AppendContour(v0, v1, o); } break; case 3: { float ystep = bottomLeft.GetExtentTopNormalized() * voxelSize; float ystep2 = bottomRight.GetExtentTopNormalized() * voxelSize; Vector3 v0 = new Vector3(x0, y0); Vector3 v1 = new Vector3(x0, y0 + ystep); Vector3 v2 = new Vector3(x1, y0 + ystep2); Vector3 v3 = new Vector3(x1, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.tris.Add(vcount); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); AppendContour(v1, v2, o); } break; case 4: { float xstep = topRight.GetExtentLeftNormalized() * voxelSize; float ystep = topRight.GetExtentBottomNormalized() * voxelSize; Vector3 v0 = new Vector3(x1, y1); Vector3 v1 = new Vector3(x1, y1 - ystep); Vector3 v2 = new Vector3(x1 - xstep, y1); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); AppendContour(v1, v2, o); } break; case 5: { float xstep = bottomLeft.GetExtentRightNormalized() * voxelSize; float ystep = bottomLeft.GetExtentTopNormalized() * voxelSize; float xstep2 = topRight.GetExtentLeftNormalized() * voxelSize; float ystep2 = topRight.GetExtentBottomNormalized() * voxelSize; Vector3 v0 = new Vector3(x0, y0); Vector3 v1 = new Vector3(x0, y0 + ystep); Vector3 v2 = new Vector3(x0 + xstep, y0); Vector3 v3 = new Vector3(x1 - xstep2, y1); Vector3 v4 = new Vector3(x1, y1); Vector3 v5 = new Vector3(x1, y1 - ystep2); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.verts.Add(v4); o.verts.Add(v5); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.tris.Add(vcount + 4); o.tris.Add(vcount + 5); o.tris.Add(vcount + 1); o.tris.Add(vcount + 3); o.tris.Add(vcount + 2); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.tris.Add(vcount + 5); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v4) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v5) / m_settings.voxelSize); AppendContour(v1, v3, o); AppendContour(v5, v2, o); } break; case 6: { float xstep = topRight.GetExtentLeftNormalized() * voxelSize; float xstep2 = bottomRight.GetExtentLeftNormalized() * voxelSize; Vector3 v0 = new Vector3(x1 - xstep2, y0); Vector3 v1 = new Vector3(x1 - xstep, y1); Vector3 v2 = new Vector3(x1, y1); Vector3 v3 = new Vector3(x1, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.tris.Add(vcount); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); AppendContour(v0, v1, o); } break; case 7: { float xstep = topRight.GetExtentLeftNormalized() * voxelSize; float ystep = bottomLeft.GetExtentTopNormalized() * voxelSize; Vector3 v0 = new Vector3(x0, y0); Vector3 v1 = new Vector3(x0, y0 + ystep); Vector3 v2 = new Vector3(x1 - xstep, y1); Vector3 v3 = new Vector3(x1, y1); Vector3 v4 = new Vector3(x1, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.verts.Add(v4); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 4); o.tris.Add(vcount + 4); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.tris.Add(vcount + 4); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v4) / m_settings.voxelSize); AppendContour(v1, v2, o); } break; case 8: { float xstep = topLeft.GetExtentRightNormalized() * voxelSize; float ystep = topLeft.GetExtentBottomNormalized() * voxelSize; Vector3 v0 = new Vector3(x0, y1 - ystep); Vector3 v1 = new Vector3(x0, y1); Vector3 v2 = new Vector3(x0 + xstep, y1); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); AppendContour(v2, v0, o); } break; case 9: { float xstep = topLeft.GetExtentRightNormalized() * voxelSize; float xstep2 = bottomLeft.GetExtentRightNormalized() * voxelSize; Vector3 v0 = new Vector3(x0, y0); Vector3 v1 = new Vector3(x0, y1); Vector3 v2 = new Vector3(x0 + xstep, y1); Vector3 v3 = new Vector3(x0 + xstep2, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.tris.Add(vcount); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); AppendContour(v2, v3, o); } break; case 10: { float xstep = topLeft.GetExtentRightNormalized() * voxelSize; float ystep = topLeft.GetExtentBottomNormalized() * voxelSize; float xstep2 = bottomRight.GetExtentLeftNormalized() * voxelSize; float ystep2 = bottomRight.GetExtentTopNormalized() * voxelSize; Vector3 v0 = new Vector3(x0, y1 - ystep); Vector3 v1 = new Vector3(x0, y1); Vector3 v2 = new Vector3(x0 + xstep, y1); Vector3 v3 = new Vector3(x1 - xstep2, y0); Vector3 v4 = new Vector3(x1, y0 + ystep2); Vector3 v5 = new Vector3(x1, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.verts.Add(v4); o.verts.Add(v5); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.tris.Add(vcount + 4); o.tris.Add(vcount + 5); o.tris.Add(vcount + 3); o.tris.Add(vcount + 0); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.tris.Add(vcount + 2); o.tris.Add(vcount + 4); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v4) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v5) / m_settings.voxelSize); AppendContour(v3, v0, o); AppendContour(v2, v4, o); } break; case 11: { float xstep = topLeft.GetExtentRightNormalized() * voxelSize; float ystep = bottomRight.GetExtentTopNormalized() * voxelSize; Vector3 v0 = new Vector3(x0, y0); Vector3 v1 = new Vector3(x0, y1); Vector3 v2 = new Vector3(x0 + xstep, y1); Vector3 v3 = new Vector3(x1, y0 + ystep); Vector3 v4 = new Vector3(x1, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.verts.Add(v4); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.tris.Add(vcount); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.tris.Add(vcount); o.tris.Add(vcount + 3); o.tris.Add(vcount + 4); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v4) / m_settings.voxelSize); AppendContour(v2, v3, o); } break; case 12: { float ystep = topLeft.GetExtentBottomNormalized() * voxelSize; float ystep2 = topRight.GetExtentBottomNormalized() * voxelSize; Vector3 v0 = new Vector3(x0, y1 - ystep); Vector3 v1 = new Vector3(x0, y1); Vector3 v2 = new Vector3(x1, y1); Vector3 v3 = new Vector3(x1, y1 - ystep2); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.tris.Add(vcount); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); AppendContour(v3, v0, o); } break; case 13: { float xstep = bottomLeft.GetExtentRightNormalized() * voxelSize; float ystep = topRight.GetExtentBottomNormalized() * voxelSize; Vector3 v0 = new Vector3(x0, y0); Vector3 v1 = new Vector3(x0, y1); Vector3 v2 = new Vector3(x1, y1); Vector3 v3 = new Vector3(x1, y1 - ystep); Vector3 v4 = new Vector3(x0 + xstep, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.verts.Add(v4); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 4); o.tris.Add(vcount + 4); o.tris.Add(vcount + 1); o.tris.Add(vcount + 3); o.tris.Add(vcount + 3); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v4) / m_settings.voxelSize); AppendContour(v3, v4, o); } break; case 14: { float xstep = bottomRight.GetExtentLeftNormalized() * voxelSize; float ystep = topLeft.GetExtentBottomNormalized() * voxelSize; Vector3 v0 = new Vector3(x0, y1 - ystep); Vector3 v1 = new Vector3(x0, y1); Vector3 v2 = new Vector3(x1, y1); Vector3 v3 = new Vector3(x1, y0); Vector3 v4 = new Vector3(x1 - xstep, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.verts.Add(v4); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.tris.Add(vcount); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.tris.Add(vcount); o.tris.Add(vcount + 3); o.tris.Add(vcount + 4); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v4) / m_settings.voxelSize); AppendContour(v4, v0, o); } break; case 15: { if (topLeft.CompareWithoutExtent(bottomLeft) && topRight.CompareWithoutExtent(bottomRight) && bottomLeft.CompareWithoutExtent(bottomRight)) { GenerateGreedyQuad(data, region, x, y, o); } else { Vector3 v0 = new Vector3(x0, y0); Vector3 v1 = new Vector3(x0, y1); Vector3 v2 = new Vector3(x1, y1); Vector3 v3 = new Vector3(x1, y0); int vcount = o.verts.Count; o.verts.Add(v0); o.verts.Add(v1); o.verts.Add(v2); o.verts.Add(v3); o.tris.Add(vcount); o.tris.Add(vcount + 1); o.tris.Add(vcount + 2); o.tris.Add(vcount); o.tris.Add(vcount + 2); o.tris.Add(vcount + 3); o.uvs.Add((m_worldPos + v0) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v1) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v2) / m_settings.voxelSize); o.uvs.Add((m_worldPos + v3) / m_settings.voxelSize); } } break; } } } CombineContours(o); if (m_settings.generateCollision) { BuildCollision(o); } if (m_settings.meshContour) { BuildContourMesh(o); } if (m_settings.generateNormals) { for (int v = 0; v < o.verts.Count; v++) { o.normals.Add(Vector3.back); } } }
private void BuildContourMesh(MeshOutput o) { float inset = m_settings.contourInset; float outset = m_settings.contourOutset; float zbias = m_settings.contourZbias; int verts = 0; Vector2 textureCutoff = (Vector2.left + Vector2.up).normalized; for (int i = 0; i < o.contours.Count; i++) { List <Vector2> contour = o.contours[i]; if (contour.Count < 2) { continue; } int vstart = verts; // Repeat segments for (int p = 0; p < contour.Count - 1; p++) { Vector2 a = contour[p]; Vector2 b = contour[p + 1]; Vector2 ab = b - a; Vector2 n = new Vector2(-ab.y, ab.x).normalized; Vector3 p1 = a - n * inset; Vector3 p2 = a + n * outset; Vector3 p3 = b - n * inset; Vector3 p4 = b + n * outset; p1.z = zbias; p2.z = zbias; p3.z = zbias; p4.z = zbias; if (p > 0) // Instead of proper mitering smooth joints by averaging vertices { p1 = (o.contourVerts[verts - 2] + p1) / 2.0f; p2 = (o.contourVerts[verts - 1] + p2) / 2.0f; o.contourVerts[verts - 2] = p1; o.contourVerts[verts - 1] = p2; } o.contourVerts.Add(p1); o.contourVerts.Add(p2); o.contourVerts.Add(p3); o.contourVerts.Add(p4); o.contourTris.Add(verts); o.contourTris.Add(verts + 1); o.contourTris.Add(verts + 2); o.contourTris.Add(verts + 2); o.contourTris.Add(verts + 1); o.contourTris.Add(verts + 3); verts += 4; float angle = Vector2.SignedAngle(textureCutoff, ab) + 180.0f; if (angle >= 360.0f) { angle -= 360.0f; } int face = Mathf.FloorToInt(angle / 90); float u0 = (face == 0 || face == 2) ? a.x / m_settings.voxelSize : a.y / m_settings.voxelSize; float u1 = (face == 0 || face == 2) ? b.x / m_settings.voxelSize : b.y / m_settings.voxelSize; float v0 = face * 0.25f; float v1 = (face + 1) * 0.25f; o.contourUVs.Add(new Vector2(u0, v0)); o.contourUVs.Add(new Vector2(u0, v1)); o.contourUVs.Add(new Vector2(u1, v0)); o.contourUVs.Add(new Vector2(u1, v1)); } Vector2 last = contour[contour.Count - 1]; Vector2 first = contour[0]; // If contour line loops average start/end vertices if (Mathf.Approximately(first.x, last.x) && Mathf.Approximately(first.y, last.y)) { Vector3 avg1 = (o.contourVerts[vstart] + o.contourVerts[verts - 2]) / 2.0f; Vector3 avg2 = (o.contourVerts[vstart + 1] + o.contourVerts[verts - 1]) / 2.0f; o.contourVerts[vstart] = avg1; o.contourVerts[verts - 2] = avg1; o.contourVerts[vstart + 1] = avg2; o.contourVerts[verts - 1] = avg2; } } if (m_settings.generateNormals) { for (int v = 0; v < o.contourVerts.Count; v++) { o.contourNormals.Add(Vector3.back); } } }