public static void flatShade(this MeshObjectData mesh) { Vector3[] oldVerts = mesh.vertices; int[] triangles = mesh.triangles; Vector3[] vertices = new Vector3[triangles.Length]; for (int i = 0; i < triangles.Length; i++) { vertices[i] = oldVerts[triangles[i]]; triangles[i] = i; } mesh.vertices = vertices; mesh.triangles = triangles; }
private void BuildTree(TreeThreadReturnData ReturnData) { MeshObjectData TreeBuildData = ReturnData.TreeBuildData; Mesh TreeMesh = TreeBuildData.ToMesh(); TreeMesh.RecalculateBounds(); TreeMesh.RecalculateNormals(); string ObjectName = ReturnData.TreeData.TreeName != null && ReturnData.TreeData.TreeName != "" ? ReturnData.TreeData.TreeName : "Woah!, Some kind of random Tree"; GameObject treeObject = new GameObject(ObjectName); treeObject.AddComponent <MeshFilter>().mesh = TreeMesh; treeObject.AddComponent <MeshRenderer>(); Material mat = new Material(vertexShader); mat.SetFloat("_Smoothness", 0f); treeObject.GetComponent <Renderer>().material = mat; MeshObjectData[] PlantBuildData = ReturnData.FoliagesBuildData; for (int i = 0; i < PlantBuildData.Length; i++) { Mesh plantMesh = PlantBuildData[i].ToMesh(); plantMesh.RecalculateBounds(); plantMesh.RecalculateNormals(); GameObject Foliage = new GameObject("Foliage"); Foliage.AddComponent <MeshFilter>().mesh = plantMesh; Foliage.AddComponent <MeshRenderer>(); Foliage.GetComponent <Renderer>().material = new Material(mat); UnityEngine.Random.InitState(ReturnData.TreeData.TreeSeed + i); Foliage.transform.localScale *= UnityEngine.Random.Range(ReturnData.TreeData.foliageScaleMin, ReturnData.TreeData.foliageScaleMax); Foliage.transform.position = PlantBuildData[i].position; Foliage.transform.SetParent(treeObject.transform); } treeObject.transform.position = TreeBuildData.position; ReturnData.OriginalCallBack(treeObject); }
static MeshObjectData CreateFoliage(TreeData data, TreeBranch Branch, int index) { MeshDraft m; switch (data.FoliageBasePrimitiveShape) { case TreeData.BasePrimitiveShapes.Dodecahedron: m = MeshDraft.Dodecahedron(0.5f); break; case TreeData.BasePrimitiveShapes.Icosahedron: m = MeshDraft.Icosahedron(0.5f, false); break; case TreeData.BasePrimitiveShapes.Prism: m = MeshDraft.Prism(0.5f, data.foliageSegments, 0.1f, false); break; case TreeData.BasePrimitiveShapes.Pyramid: m = MeshDraft.Pyramid(0.5f, data.foliageSegments, 0.5f, false); break; default: m = MeshDraft.Sphere(0.5f, data.foliageSegments, data.foliageSegments, false); break; } MeshObjectData plant = new MeshObjectData(); plant.vertices = m.vertices.ToArray(); plant.triangles = m.triangles.ToArray(); plant.tangents = m.tangents.ToArray(); plant.AutoWeldMesh(0.0001f, 0.4f); Vector3[] verts = plant.vertices; float currentNoise = data.noise; currentNoise *= 0.4f; Vector3 Pos = Branch.To; plant.position = Pos; int s = data.TreeSeed + index + Mathf.RoundToInt(Pos.x) + Mathf.RoundToInt(Pos.y) + Mathf.RoundToInt(Pos.z) + Mathf.RoundToInt(Branch.Length); Rand r = new Rand(s); for (int i = 0; i < verts.Length; i++) { verts[i].x += r.Range(-currentNoise, currentNoise); verts[i].y += r.Range(-currentNoise, currentNoise); verts[i].z += r.Range(-currentNoise, currentNoise); } plant.vertices = verts; plant.flatShade(); Color[] vertexColor = new Color[plant.vertices.Length]; Color CC = data.foliageColors[r.Range(0, data.foliageColors.Length)]; for (int c = 0; c < plant.vertices.Length; c++) { vertexColor[c] = CC; } plant.colors = vertexColor; return(plant); }
public static void Build(TreeThreadReturnData ReturnData) { TreeData data = ReturnData.TreeData; var root = new TreeBranch( data.generations, data.length, data.radius, data ); var vertices = new List <Vector3>(); var normals = new List <Vector3>(); var tangents = new List <Vector4>(); var uvs = new List <Vector2>(); var triangles = new List <int>(); List <MeshObjectData> Foliages = new List <MeshObjectData>(); Rand rand; int FolCheckCount = 0; float maxLength = TraverseMaxLength(root); Traverse(root, (branch) => { var offset = vertices.Count; var vOffset = branch.Offset / maxLength; var vLength = branch.Length / maxLength; for (int i = 0, n = branch.Segments.Count; i < n; i++) { var t = 1f * i / (n - 1); var v = vOffset + vLength * t; var segment = branch.Segments[i]; var N = segment.Frame.Normal; var B = segment.Frame.Binormal; for (int j = 0; j <= data.radialSegments; j++) { // 0.0 ~ 2π var u = 1f * j / data.radialSegments; float rad = u * PI2; float cos = Mathf.Cos(rad), sin = Mathf.Sin(rad); var normal = (cos * N + sin * B).normalized; vertices.Add(segment.Position + segment.Radius * normal); normals.Add(normal); var tangent = segment.Frame.Tangent; tangents.Add(new Vector4(tangent.x, tangent.y, tangent.z, 0f)); uvs.Add(new Vector2(u, v)); } } for (int j = 1; j <= data.heightSegments; j++) { for (int i = 1; i <= data.radialSegments; i++) { int a = (data.radialSegments + 1) * (j - 1) + (i - 1); int b = (data.radialSegments + 1) * j + (i - 1); int c = (data.radialSegments + 1) * j + i; int d = (data.radialSegments + 1) * (j - 1) + i; a += offset; b += offset; c += offset; d += offset; triangles.Add(a); triangles.Add(d); triangles.Add(b); triangles.Add(b); triangles.Add(d); triangles.Add(c); } } //plants if (branch.Children == null || branch.Children.Count == 0) { rand = new Rand(FolCheckCount + data.TreeSeed); float chance = rand.Range(0f, 100f); Debug.Log(chance); if (chance <= data.foliageChance) { Foliages.Add(CreateFoliage(data, branch, Foliages.Count + FolCheckCount)); } FolCheckCount++; } }); MeshObjectData TreeMeshData = new MeshObjectData(); TreeMeshData.vertices = vertices.ToArray(); TreeMeshData.triangles = triangles.ToArray(); TreeMeshData.position = ReturnData.TreePos; TreeMeshData.flatShade(); Color[] treecolor = new Color[TreeMeshData.vertices.Length]; for (int i = 0; i < TreeMeshData.vertices.Length; i++) { treecolor[i] = data.branchColor; } TreeMeshData.colors = treecolor; ReturnData.TreeBuildData = TreeMeshData; ReturnData.FoliagesBuildData = Foliages.ToArray(); ReturnData.ManagerCallBack(ReturnData); }
private IEnumerator CookieBaking() { yield return(null); var resolution = s_resolutionOptions[s_selectedCookieResolution]; // We only want to use GameObjects that are within the outer radius that we have set. So, a quick test // to see if something is within the ball park would be to check if their bounding box insersects with // a bounding box that contains the outer radius. Once that has been done, we can take a closer look at // what's left. var meshRenders = FindObjectsOfType <MeshRenderer>(); var lightCenter = s_currentLightComponent.transform.position; var lightBoundingBox = new UnityEngine.Bounds(lightCenter, 2.0f * s_outerRadius * Vector3.one); var intersectingBounds = new List <MeshRenderer>(); foreach (var meshRenderer in meshRenders) { var otherBounds = meshRenderer.bounds; if (otherBounds.Intersects(lightBoundingBox)) { intersectingBounds.Add(meshRenderer); } } yield return(null); // Now, we can limit things a bit more by finding the point within our mesh bounds that is nearset to // the light's center. We then check this point to see if it's within the distance of our outer radius. var intersectingOuterRadius = new List <MeshRenderer>(); foreach (var meshRenderer in intersectingBounds) { var otherBounds = meshRenderer.bounds; var nearsetPointToLight = otherBounds.ClosestPoint(lightCenter); if ((nearsetPointToLight - lightCenter).sqrMagnitude <= (s_outerRadius * s_outerRadius)) { intersectingOuterRadius.Add(meshRenderer); } } yield return(null); var processMeshRenderer = intersectingBounds; var processMeshFilter = new List <MeshFilter>(); // We're reusing the List from intersectingBounds and placing it under a more appropriate name. // Basically, we're reusing a piece of memory that's no longer needed instead of allocating a new List // with a new array. Also, this array shouldn't need to be resized to something larger because it // already a capacity that is greater than or equal to the number of items we will be processing. processMeshRenderer.Clear(); intersectingBounds = null; // Also, while we're at it, lets make sure that we are only baking items that are static. After all, // what's the point of baking the shadow casting item that might not be there? foreach (var meshRender in intersectingOuterRadius) { var gameObject = meshRender.gameObject; // If the gameObject isn't static, than it can be moved. Since we are not updating the cookie at // runtime, having a moving object would be bad. if (!gameObject.isStatic) { continue; } // The mesh for our object is inside the MeshFilter, so we need that to get the mesh. Also, since we're // dealing with static meshes, then we really shouldn't be dealing with any Skinned Mesh Renderers. var meshFilter = gameObject.GetComponent <MeshFilter>(); if (meshFilter == null) { continue; } // If there's no mesh, then what are we even bothering with this? if (meshFilter.sharedMesh == null) { continue; } processMeshRenderer.Add(meshRender); processMeshFilter.Add(meshFilter); } yield return(null); // Todo: With this design, items that use the same mesh will replicate the mesh verts and triangle index // arrays. I need to add a way to check if we have already added a mesh and if so, pull up the triangle // index array information to pass along to the meshRefDatum. var meshObjecRefData = new List <MeshObjectData>(processMeshRenderer.Count); var indexList = new List <int>(); var vertexList = new List <Vector3>(processMeshRenderer.Count * 50); for (int i = 0; i < processMeshRenderer.Count; i++) { var mesh = processMeshFilter[i].sharedMesh; var meshVerts = mesh.vertices; var meshTriangleIndices = mesh.triangles; var meshRefDatum = new MeshObjectData() { LocalToWorldMatrix = processMeshRenderer[i].localToWorldMatrix, IndicesOffset = indexList.Count, // The starting index of the vert-index for this object's mesh. IndicesCount = meshTriangleIndices.Length, // The number of indices used to form all of the mesh triangles. VerticesOffset = vertexList.Count, Bounds = new Bounds() { Center = processMeshRenderer[i].bounds.center, Extent = processMeshRenderer[i].bounds.extents } }; meshObjecRefData.Add(meshRefDatum); vertexList.AddRange(meshVerts); indexList.AddRange(meshTriangleIndices); } yield return(null); /// /// Try adding tracing code here. /// var backgroundWorkerArgs = new BackgroundWorkerArgs() { Indices = indexList, ObjectData = meshObjecRefData, Vertices = vertexList, LightPosition = lightCenter, LightForward = s_currentLightComponent.transform.forward, LightUpward = s_currentLightComponent.transform.up }; yield return(null); using (var backgroundWorker = new BackgroundWorker()) { backgroundWorker.DoWork += BackgroundWorker_DoWork; backgroundWorker.RunWorkerAsync(backgroundWorkerArgs); while (!backgroundWorkerArgs.Complete) { yield return(new EditorWaitForSeconds(0.15f)); } } s_currentBakeState = BakeState.Finalize; yield return(null); /// /// Code for saving our results into the project and applying them to the light component. /// // Create a Texture2D to place our results into. This Texture2D will be added to the project's assets // and it will be applied to the light source we were raytracing. Texture2D finalResults = new Texture2D(resolution, resolution, TextureFormat.RGBAFloat, false, true) { alphaIsTransparency = true, filterMode = FilterMode.Point, wrapMode = TextureWrapMode.Clamp, name = s_currentLightComponent.name + "_ShadowCookie_" + resolution.ToString() }; yield return(null); //finalResults.SetPixels(threadArgs.Result); yield return(null); // Check for the location where we will be saving the Texture2D. If that location doesn't exist, then // create it. var assetFolderPath = "Assets/Light_Cookies/" + s_currentLightComponent.gameObject.scene.name; if (!Directory.Exists(assetFolderPath)) { if (!Directory.Exists("Assets/Light_Cookies")) { AssetDatabase.CreateFolder("Assets", "Light_Cookies"); yield return(null); } AssetDatabase.CreateFolder("Assets/Light_Cookies", s_currentLightComponent.gameObject.scene.name); } yield return(null); // Save the Texture2D as a project asset. var assetPath = assetFolderPath + "/" + finalResults.name + ".asset"; AssetDatabase.CreateAsset(finalResults, assetPath); yield return(null); // Apply the Texture2D to the light as a cooke, then finish cleaning up. s_currentLightComponent.cookie = finalResults; EditorUtility.SetDirty(s_currentLightComponent.gameObject); s_bakingCoroutine = null; // The coroutines for MonoBehaviors have the option to manipulate them from the outside, which I have // made use of. With a bit of luck, those functions will be added to the EditorCoroutines at some point // in time. // -FCT yield return(null); s_currentBakeState = BakeState.SettingSelection; }
public static void AutoWeldMesh(this MeshObjectData mesh, float threshold, float bucketStep) { Vector3[] oldVertices = mesh.vertices; Vector3[] newVertices = new Vector3[oldVertices.Length]; int[] old2new = new int[oldVertices.Length]; int newSize = 0; // Find AABB Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue); for (int i = 0; i < oldVertices.Length; i++) { if (oldVertices[i].x < min.x) { min.x = oldVertices[i].x; } if (oldVertices[i].y < min.y) { min.y = oldVertices[i].y; } if (oldVertices[i].z < min.z) { min.z = oldVertices[i].z; } if (oldVertices[i].x > max.x) { max.x = oldVertices[i].x; } if (oldVertices[i].y > max.y) { max.y = oldVertices[i].y; } if (oldVertices[i].z > max.z) { max.z = oldVertices[i].z; } } // Make cubic buckets, each with dimensions "bucketStep" int bucketSizeX = Mathf.FloorToInt((max.x - min.x) / bucketStep) + 1; int bucketSizeY = Mathf.FloorToInt((max.y - min.y) / bucketStep) + 1; int bucketSizeZ = Mathf.FloorToInt((max.z - min.z) / bucketStep) + 1; List <int>[,,] buckets = new List <int> [bucketSizeX, bucketSizeY, bucketSizeZ]; // Make new vertices for (int i = 0; i < oldVertices.Length; i++) { // Determine which bucket it belongs to int x = Mathf.FloorToInt((oldVertices[i].x - min.x) / bucketStep); int y = Mathf.FloorToInt((oldVertices[i].y - min.y) / bucketStep); int z = Mathf.FloorToInt((oldVertices[i].z - min.z) / bucketStep); // Check to see if it's already been added if (buckets[x, y, z] == null) { buckets[x, y, z] = new List <int>(); // Make buckets lazily } for (int j = 0; j < buckets[x, y, z].Count; j++) { Vector3 to = newVertices[buckets[x, y, z][j]] - oldVertices[i]; if (Vector3.SqrMagnitude(to) < threshold) { old2new[i] = buckets[x, y, z][j]; goto skip; // Skip to next old vertex if this one is already there } } // Add new vertex newVertices[newSize] = oldVertices[i]; buckets[x, y, z].Add(newSize); old2new[i] = newSize; newSize++; skip :; } // Make new triangles int[] oldTris = mesh.triangles; int[] newTris = new int[oldTris.Length]; for (int i = 0; i < oldTris.Length; i++) { newTris[i] = old2new[oldTris[i]]; } Vector3[] finalVertices = new Vector3[newSize]; for (int i = 0; i < newSize; i++) { finalVertices[i] = newVertices[i]; } //mesh.Clear(); mesh.vertices = finalVertices; mesh.triangles = newTris; //mesh.RecalculateNormals(); }
public static void Build(RockThreadReturnData returnData) { RockData data = returnData.RockData; MeshDraft MD; switch (data.RockBasePrimitiveShape) { case RockData.BasePrimitiveShapes.Dodecahedron: MD = MeshDraft.Dodecahedron(0.5f); break; case RockData.BasePrimitiveShapes.Icosahedron: MD = MeshDraft.Icosahedron(0.5f, false); break; case RockData.BasePrimitiveShapes.Prism: MD = MeshDraft.Prism(0.5f, data.Segments, 1f, false); break; case RockData.BasePrimitiveShapes.Pyramid: MD = MeshDraft.Pyramid(0.5f, data.Segments, 1f, false); break; default: MD = MeshDraft.Sphere(0.5f, data.Segments, data.Segments, false); break; } ; MeshObjectData rock = new MeshObjectData(); rock.vertices = MD.vertices.ToArray(); rock.triangles = MD.triangles.ToArray(); rock.tangents = MD.tangents.ToArray(); rock.AutoWeldMesh(0.0001f, 0.4f); Vector3[] verts = rock.vertices; INoise noise = new SimplexNoise(data.RockSeed, 0.3f, 0.2f); Rand r = new Rand(data.RockSeed); for (int i = 0; i < verts.Length; i++) { float currentNoise = NoiseGen(noise, 3, verts[i].x / 0.5f, verts[i].y / 0.5f, verts[i].z / 0.5f); //currentNoise*=2; Vector3 norm = verts[i].normalized; verts[i].x += currentNoise * norm.x; verts[i].y += currentNoise * norm.y; verts[i].z += currentNoise * norm.z; verts[i].x *= 3; verts[i].y *= 1.2f; verts[i].z *= 1.5f; } rock.vertices = verts; rock.flatShade(); Color[] vertexColor = new Color[rock.vertices.Length]; for (int i = 0; i < rock.vertices.Length; i++) { vertexColor[i] = data.RockGradientColor.Color.Evaluate(1 - rock.vertices[i].y); } rock.colors = vertexColor; returnData.RockBuildData = rock; returnData.ManagerCallBack(returnData); }