private static void extractTrianglesAndVerticies( List <vertex> verticies, List <triangle> triangles, int quota, meshData meshData, float maxArea) { quota = Mathf.Min(quota, meshData.Triangles.Count); var usedPositions = new HashSet <Vector3>(); var totalArea = 0f; var retries = quota / 2; var vd = new Dictionary <int, int>(); while (triangles.Count < quota && retries >= 0) { triangle t; if (!meshData.DequeueAdjacentTriangle(usedPositions, out t)) { retries--; continue; } var p0 = meshData.GetPosition(t.I0); var p1 = meshData.GetPosition(t.I1); var p2 = meshData.GetPosition(t.I2); var area = Vector3.Cross(p1 - p0, p2 - p0).magnitude / 2; if (area > maxArea) { meshData.Subdivide(t); return; } var i0 = meshData.NewVertex(verticies, vd, t.I0); var i1 = meshData.NewVertex(verticies, vd, t.I1); var i2 = meshData.NewVertex(verticies, vd, t.I2); verticies[i0].TriangleIndexes.Add(triangles.Count); verticies[i1].TriangleIndexes.Add(triangles.Count); verticies[i2].TriangleIndexes.Add(triangles.Count); triangles.Add(new triangle(i0, i1, i2)); if ((totalArea += area) > maxArea) { return; } } }
private void InitTerrain() { int meshGridSize = 16; int numTerrainMeshVertices = meshGridSize * meshGridSize; terrainMeshBuffer = new ComputeBuffer(numTerrainMeshVertices, sizeof(float) * (3 + 3 + 2 + 4)); int numStrokesPerVertX = 16; int numStrokesPerVertZ = 16; int numTerrainStrokes = meshGridSize * meshGridSize * numStrokesPerVertX * numStrokesPerVertZ; terrainStrokesBuffer = new ComputeBuffer(numTerrainStrokes, sizeof(float) * (3 + 3 + 3 + 3 + 3 + 2) + sizeof(int) * 1); //terrainGeneratorCompute = new ComputeShader(); int kernel_id = terrainGeneratorCompute.FindKernel("CSMain"); terrainGeneratorCompute.SetFloat("_GridSideLength", terrainSize); terrainGeneratorCompute.SetFloat("_NoiseFrequency", terrainNoiseFrequency); terrainGeneratorCompute.SetFloat("_NoiseAmplitude", terrainNoiseAmplitude); terrainGeneratorCompute.SetFloat("_GroundHeight", terrainAltitude); terrainGeneratorCompute.SetInt("_NumGroupsX", numStrokesPerVertX); terrainGeneratorCompute.SetInt("_NumGroupsZ", numStrokesPerVertZ); terrainGeneratorCompute.SetBuffer(kernel_id, "buf_StrokeData", terrainStrokesBuffer); terrainGeneratorCompute.SetBuffer(kernel_id, "buf_MeshData", terrainMeshBuffer); meshData[] meshDataArray = new meshData[numTerrainMeshVertices]; // memory to receive data from computeshader terrainGeneratorCompute.Dispatch(kernel_id, numStrokesPerVertX, 1, numStrokesPerVertZ); // fill buffers terrainMeshBuffer.GetData(meshDataArray); // download mesh Data // generate Mesh from data: //Construct mesh using received data // Why same number of tris as vertices? == // because all triangles have duplicate verts - no shared vertices? Vector3[] vertices = new Vector3[numTerrainMeshVertices]; Color[] colors = new Color[numTerrainMeshVertices]; int[] tris = new int[2 * (meshGridSize - 1) * (meshGridSize - 1) * 3]; Vector2[] uvs = new Vector2[numTerrainMeshVertices]; Vector3[] normals = new Vector3[numTerrainMeshVertices]; for(int i = 0; i < numTerrainMeshVertices; i++) { vertices[i] = meshDataArray[i].pos; normals[i] = meshDataArray[i].normal; uvs[i] = meshDataArray[i].uv; colors[i] = meshDataArray[i].color; } // Figure out triangles: int index = 0; int numSquares = meshGridSize - 1; for (int y = 0; y < numSquares; y++) { for(int x = 0; x < numSquares; x++) { // counterclockwise winding order: tris[index] = ((y + 1) * meshGridSize) + x; tris[index + 1] = (y * meshGridSize) + x + 1; tris[index + 2] = (y * meshGridSize) + x; tris[index + 3] = ((y + 1) * meshGridSize) + x; tris[index + 4] = ((y + 1) * meshGridSize) + x + 1; tris[index + 5] = (y * meshGridSize) + x + 1; index = index + 6; } } Mesh terrainMesh = new Mesh(); terrainMesh.vertices = vertices; terrainMesh.uv = uvs; //Unwrapping.GeneratePerTriangleUV(NewMesh); terrainMesh.triangles = tris; terrainMesh.normals = normals; //NewMesh.RecalculateNormals(); terrainMesh.colors = colors; terrainMesh.RecalculateNormals(); terrainMesh.RecalculateBounds(); trainerTerrainManager.GetComponent<MeshFilter>().sharedMesh = terrainMesh; trainerTerrainManager.GetComponent<MeshCollider>().sharedMesh = terrainMesh; terrainMeshBuffer.Release(); terrainMeshBuffer.Dispose(); }
private IEnumerator run( ScatterCommands commands, Action <Stats> whenDone) { var sw = Stopwatch.StartNew(); var stats = new Stats(); var objectCount = 0; foreach (var cmd in commands) { var gameObject = cmd.GameObject; // already scattered? if (gameObject == null || gameObject.name.StartsWith(FragmentNamePrefix) || gameObject.name.StartsWith(DebrisNamePrefix)) { continue; } var targetPartCount = cmd.TargetPartCount.GetValueOrDefault(TargetPartCount); var targetArea = cmd.TargetArea.GetValueOrDefault(TargetArea); var newThicknessMin = cmd.NewThicknessMin.GetValueOrDefault(NewThicknessMin); var newThicknessMax = cmd.NewThicknessMax.GetValueOrDefault(NewThicknessMax); Debug.Log(targetArea); var meshData = new meshData(cmd.Mesh, cmd.MeshScale); stats.SourceTriangles += meshData.TotalTriangleCount; for (var submeshIndex = 0; submeshIndex < cmd.Mesh.subMeshCount; submeshIndex++) { meshData.SelectSubmesh(submeshIndex); var maxTris = Mathf.Max(2, meshData.TotalTriangleCount / targetPartCount); while (meshData.Triangles.Any()) { var newTriangles = new List <triangle>(); var newVerticies = new List <vertex>(); var quota = Mathf.Min(meshData.Triangles.Count, Random.Range(maxTris - 1, maxTris + 2)); extractTrianglesAndVerticies(newVerticies, newTriangles, quota, meshData, targetArea); // at this point, we have a continous surface in newTriangles+newVerticies which // are a subset of the whole mesh // now let's create some SCAM! // first, calculate the average normal of the surface and the midpoint var frontNormal = Vector3.zero; var frontMidpoint = Vector3.zero; foreach (var t in newTriangles) { var p1 = newVerticies[t.I0].Pos; var p2 = newVerticies[t.I1].Pos; var p3 = newVerticies[t.I2].Pos; frontMidpoint += p1 + p2 + p3; frontNormal += Vector3.Cross(p2 - p1, p3 - p1); } var backVector = -frontNormal.normalized * Random.Range(newThicknessMin, newThicknessMax); var backMidpoint = frontMidpoint / (newTriangles.Count * 3) + backVector; // now we create a backside by creating a flipped backside, pushed backward by // the user's specified thickness. we also contract it somewhat, to avoid z-fighting // with orthogonal surfaces var vertLength = newVerticies.Count; for (var k = 0; k < vertLength; k++) { newVerticies.Add(new vertex { Pos = Vector3.Lerp(newVerticies[k].Pos + backVector, backMidpoint, 0.2f), Uv = Vector2.one - newVerticies[k].Uv, Normal = -newVerticies[k].Normal, Tangent = newVerticies[k].Tangent }); } // create all the new scam triangles var tlen = newTriangles.Count; for (var ti = 0; ti < tlen; ti++) { var t = newTriangles[ti]; // this is triangle for the backside (only verticies has been created this far) newTriangles.Add(new triangle(t.I0 + vertLength, t.I2 + vertLength, t.I1 + vertLength)); // for each side of the front triangle, investigate if it is an outer // side, and if so, create two triangles connecting the front with the back buildSideRect(newVerticies, vertLength, newTriangles, ti, t.I0, t.I1); buildSideRect(newVerticies, vertLength, newTriangles, ti, t.I1, t.I2); buildSideRect(newVerticies, vertLength, newTriangles, ti, t.I2, t.I0); // the new triangles created have completely wrong normals. fixing that would // slow things down. just sayin' } // a micro optimizition here would be to stop using LINQ var theNewMesh = new Mesh { vertices = newVerticies.Select(_ => _.Pos).ToArray(), normals = newVerticies.Select(_ => _.Normal).ToArray(), uv = newVerticies.Select(_ => _.Uv).ToArray(), tangents = newVerticies.Select(_ => _.Tangent).ToArray(), triangles = newTriangles.SelectMany(_ => new[] { _.I0, _.I1, _.I2 }).ToArray() }; var newFragment = new GameObject($"{FragmentNamePrefix}{++objectCount}"); newFragment.transform.parent = cmd.NewTransformParent; newFragment.transform.position = cmd.Renderer.transform.position; newFragment.transform.rotation = cmd.Renderer.transform.rotation; #if UNITY_EDITOR newFragment.AddComponent <MeshRenderer>().sharedMaterial = cmd.Renderer.sharedMaterials[submeshIndex % cmd.Renderer.sharedMaterials.Length]; #else newFragment.AddComponent <MeshRenderer>().material = cmd.Renderer.materials[submeshIndex % cmd.Renderer.materials.Length]; #endif newFragment.AddComponent <MeshFilter>().mesh = theNewMesh; newFragment.AddComponent <BoxCollider>(); stats.NewGameObjects++; stats.NewTriangles += newTriangles.Count; if (MaxTimeMs > 0 && sw.ElapsedMilliseconds > MaxTimeMs) { Debug.Log("Yield"); yield return(null); sw.Restart(); } } } if (cmd.DestroyMesh) { Object.Destroy(cmd.Mesh); } if (cmd.Destroy) { Object.Destroy(gameObject); } } whenDone?.Invoke(stats); }
private void InitTerrain() { int meshGridSize = 16; int numTerrainMeshVertices = meshGridSize * meshGridSize; terrainMeshBuffer = new ComputeBuffer(numTerrainMeshVertices, sizeof(float) * (3 + 3 + 2 + 4)); int numStrokesPerVertX = 16; int numStrokesPerVertZ = 16; int numTerrainStrokes = meshGridSize * meshGridSize * numStrokesPerVertX * numStrokesPerVertZ; terrainStrokesBuffer = new ComputeBuffer(numTerrainStrokes, sizeof(float) * (3 + 3 + 3 + 3 + 3 + 2) + sizeof(int) * 1); //terrainGeneratorCompute = new ComputeShader(); int kernel_id = terrainGeneratorCompute.FindKernel("CSMain"); terrainGeneratorCompute.SetFloat("_GridSideLength", terrainSize); terrainGeneratorCompute.SetFloat("_NoiseFrequency", terrainNoiseFrequency); terrainGeneratorCompute.SetFloat("_NoiseAmplitude", terrainNoiseAmplitude); terrainGeneratorCompute.SetFloat("_GroundHeight", terrainAltitude); terrainGeneratorCompute.SetInt("_NumGroupsX", numStrokesPerVertX); terrainGeneratorCompute.SetInt("_NumGroupsZ", numStrokesPerVertZ); terrainGeneratorCompute.SetBuffer(kernel_id, "buf_StrokeData", terrainStrokesBuffer); terrainGeneratorCompute.SetBuffer(kernel_id, "buf_MeshData", terrainMeshBuffer); meshData[] meshDataArray = new meshData[numTerrainMeshVertices]; // memory to receive data from computeshader terrainGeneratorCompute.Dispatch(kernel_id, numStrokesPerVertX, 1, numStrokesPerVertZ); // fill buffers terrainMeshBuffer.GetData(meshDataArray); // download mesh Data // generate Mesh from data: //Construct mesh using received data // Why same number of tris as vertices? == // because all triangles have duplicate verts - no shared vertices? Vector3[] vertices = new Vector3[numTerrainMeshVertices]; Color[] colors = new Color[numTerrainMeshVertices]; int[] tris = new int[2 * (meshGridSize - 1) * (meshGridSize - 1) * 3]; Vector2[] uvs = new Vector2[numTerrainMeshVertices]; Vector3[] normals = new Vector3[numTerrainMeshVertices]; for (int i = 0; i < numTerrainMeshVertices; i++) { vertices[i] = meshDataArray[i].pos; normals[i] = meshDataArray[i].normal; uvs[i] = meshDataArray[i].uv; colors[i] = meshDataArray[i].color; } // Figure out triangles: int index = 0; int numSquares = meshGridSize - 1; for (int y = 0; y < numSquares; y++) { for (int x = 0; x < numSquares; x++) { // counterclockwise winding order: tris[index] = ((y + 1) * meshGridSize) + x; tris[index + 1] = (y * meshGridSize) + x + 1; tris[index + 2] = (y * meshGridSize) + x; tris[index + 3] = ((y + 1) * meshGridSize) + x; tris[index + 4] = ((y + 1) * meshGridSize) + x + 1; tris[index + 5] = (y * meshGridSize) + x + 1; index = index + 6; } } Mesh terrainMesh = new Mesh(); terrainMesh.vertices = vertices; terrainMesh.uv = uvs; //Unwrapping.GeneratePerTriangleUV(NewMesh); terrainMesh.triangles = tris; terrainMesh.normals = normals; //NewMesh.RecalculateNormals(); terrainMesh.colors = colors; terrainMesh.RecalculateNormals(); terrainMesh.RecalculateBounds(); trainerTerrainManager.GetComponent <MeshFilter>().sharedMesh = terrainMesh; trainerTerrainManager.GetComponent <MeshCollider>().sharedMesh = terrainMesh; terrainMeshBuffer.Release(); terrainMeshBuffer.Dispose(); }