private void ProcessCell(FoliageCellDataRuntime cell, float colliderDistSqr) { for (int foliageType = 0; foliageType < cell.m_TypeHashLocationsRuntime.Length; foliageType++) { int foliageTypeKey = cell.m_TypeHashLocationsRuntime[foliageType].Key; FoliageType type = m_FoliageTypes[foliageTypeKey]; if (type.m_EnableCollision == false) { continue; } var batches = cell.m_TypeHashLocationsRuntime[foliageType].Value.m_EditTime; int hash = type.m_Hash; float x, y, z; float dist; for (int i = 0; i < batches.Length; i++) { Vector3 pos = batches[i].m_Position; x = pos.x - m_LastPosition.x; y = pos.y - m_LastPosition.y; z = pos.z - m_LastPosition.z; dist = x * x + y * y + z * z; if (dist <= colliderDistSqr) { GameObject collider = GetColliderForPrototype(hash); // Set the layer collider collider.layer = m_Layer; if (collider != null) { FoliageColliderData data = collider.GetComponent <FoliageColliderData>(); if (data == null) { data = collider.AddComponent <FoliageColliderData>(); } // Append the collision data for query at the runtime data.m_FoliageType = foliageTypeKey; data.m_FoliageInstance = batches[i]; // Update it's transform values collider.transform.position = batches[i].m_Position; collider.transform.rotation = batches[i].m_Rotation; collider.transform.localScale = batches[i].m_Scale; // Increment the active collider count m_DataIssuedActiveColliders++; } } } } }
public static bool CanChangeType(FoliageType type, EFoliageType newType) { GameObject prefab = type.m_Prefab; // PrefabType prefabType = PrefabUtility.GetPrefabType(prefab); string path = AssetDatabase.GetAssetPath(prefab); if (IsSpeedTree(newType)) { // We want to change to a SpeedTree type if (path.EndsWith(".spm") == false) { FoliageLog.e("Can't change to SpeedTree foliage: " + type.m_Name + " since it's path does not end with '.spm'! It means that it's not a SpeedTree!"); return(false); } LODGroup group = prefab.GetComponent <LODGroup>(); if (group == null) { FoliageLog.e("Can't change to SpeedTree foliage: " + type.m_Name + " since it doesn't contain a 'LODGroup' component!"); return(false); } if (newType == EFoliageType.SPEEDTREE_TREE_BILLBOARD) { bool containsBillboardRenderer = false; LOD[] lods = group.GetLODs(); foreach (LOD lod in lods) { if (lod.renderers[0].GetComponent <BillboardRenderer>() != null) { containsBillboardRenderer = true; break; } } if (containsBillboardRenderer == false) { FoliageLog.e("Can't change to SpeedTree with billboard the foliage: " + type.m_Name + " since it doesn't contain a billboard renderer!"); return(false); } } } // We're cool no error return(true); }
public static void DestroyBillboards(GameObject owner, int cellHash, FoliageType type) { string name = string.Format("MeshCell[{0}_{1}]", cellHash, type.m_Prefab.name); Transform existing = owner.transform.Find(name); if (existing != null) { #if UNITY_EDITOR if (UnityEditor.EditorApplication.isPlaying == false) { Object.DestroyImmediate(existing.gameObject); } else { Object.Destroy(existing.gameObject); } #else Object.Destroy(existing.gameObject); #endif existing = null; } }
public static void GenerateBillboards(Bounds bounds, FoliageCell cell, GameObject owner, List <FoliageInstance> trees, FoliageType type, bool addLodGroup, float screenFadeSize, bool animatedCrossFade) { int[] originalTriangles = m_SystemQuadTriangles; GameObject meshObj = new GameObject(); // Mark object as static #if UNITY_EDITOR GameObjectUtility.SetStaticEditorFlags(meshObj, StaticEditorFlags.OccludeeStatic | StaticEditorFlags.ReflectionProbeStatic); #endif string name = string.Format("MeshCell[{0}_{1}]", cell.GetHashCode(), type.m_Prefab.name); Transform existing = owner.transform.Find(name); if (existing != null) { #if UNITY_EDITOR if (UnityEditor.EditorApplication.isPlaying == false) { Object.DestroyImmediate(existing.gameObject); } else { Object.Destroy(existing.gameObject); } #else Object.Destroy(existing.gameObject); #endif existing = null; } meshObj.transform.SetParent(owner.transform); meshObj.name = name; var data = type.m_RuntimeData.m_SpeedTreeData; Vector3 worldScale = new Vector3(data.m_Size.x, data.m_Size.y, data.m_Size.x); // Set material MeshRenderer rend = meshObj.AddComponent <MeshRenderer>(); rend.sharedMaterial = GenerateBillboardMaterial(type.m_RuntimeData.m_SpeedTreeData); MeshFilter filter = meshObj.AddComponent <MeshFilter>(); Mesh treeMesh = new Mesh(); treeMesh.name = meshObj.name; List <Vector4> m_TempWorldPositions = new List <Vector4>(); List <Vector3> m_TempWorldScales = new List <Vector3>(); List <Vector3> m_TempQuadVertices = new List <Vector3>(); List <Vector4> m_TempQuadTangents = new List <Vector4>(); List <Vector3> m_TempQuadNormals = new List <Vector3>(); List <int> m_TempQuadIndices = new List <int>(); for (int treeIndex = 0; treeIndex < trees.Count; treeIndex++) { Vector3 position = trees[treeIndex].m_Position; Vector3 scale = trees[treeIndex].m_Scale; float rot = trees[treeIndex].m_Rotation.eulerAngles.y * Mathf.Deg2Rad; // Offset world position, by the grounding factor Vector3 instancePos = position; // Don't use this, but offset in shader, so that we can have that correct hue // instancePos.y += data.m_Size.z; // Scale by the world scale too so that we don't have to do an extra multip Vector3 instanceScale = scale; instanceScale.Scale(worldScale); // Add the world and scale data for (int index = 0; index < 4; index++) { Vector4 posAndRot = instancePos; posAndRot.w = rot; m_TempWorldPositions.Add(posAndRot); m_TempWorldScales.Add(instanceScale); } // Add stanard quad data m_TempQuadVertices.AddRange(m_SystemQuadVertices); m_TempQuadTangents.AddRange(m_SystemQuadTangents); m_TempQuadNormals.AddRange(m_SystemQuadNormals); // Calculate triangle indixes m_TempQuadIndices.AddRange(originalTriangles); for (int triIndex = 0; triIndex < 6; triIndex++) { // Just add to the triangles the existing triangles + the new indices m_TempQuadIndices[triIndex + 6 * treeIndex] = originalTriangles[triIndex] + 4 * treeIndex; } } treeMesh.Clear(); // Set standard data treeMesh.SetVertices(m_TempQuadVertices); treeMesh.SetNormals(m_TempQuadNormals); treeMesh.SetTangents(m_TempQuadTangents); // Set the custom data treeMesh.SetUVs(1, m_TempWorldPositions); treeMesh.SetUVs(2, m_TempWorldScales); // Set triangles and do not calculate bounds treeMesh.SetTriangles(m_TempQuadIndices, 0, false); // Set the manually calculated bounds treeMesh.bounds = bounds; treeMesh.UploadMeshData(true); // Set the mesh filter.mesh = treeMesh; if (addLodGroup) { // Add the mesh' lod group LODGroup group = meshObj.AddComponent <LODGroup>(); group.animateCrossFading = false; if (animatedCrossFade) { group.fadeMode = LODFadeMode.CrossFade; group.animateCrossFading = true; } else { group.fadeMode = LODFadeMode.None; group.animateCrossFading = false; } group.SetLODs(new LOD[] { new LOD(screenFadeSize, new Renderer[] { rend }) }); group.RecalculateBounds(); } #if UNITY_EDITOR UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(meshObj.scene); #endif }
/** To be used at runtime. Will create all we need for SpeedTree wind and other data */ public static void BuildDataRuntime(FoliagePainter painter, FoliageType type, Transform attachmentPoint) { FoliageLog.Assert(type.IsRuntimeInitialized == false, "Runtime data already initialized!"); // Everyone has MPB's type.m_RuntimeData.m_TypeMPB = new MaterialPropertyBlock(); if (type.IsSpeedTreeType) { // Create the glued mesh var speedData = type.m_RuntimeData.m_SpeedTreeData; FoliageLog.Assert(speedData != null, "Speed data must already be partly generated if we have a SpeedTree!"); // Get the lod and instnatiade the one with the least instructions LOD[] lods = type.m_Prefab.GetComponent <LODGroup>().GetLODs(); for (int i = lods.Length - 1; i >= 0; i--) { if (lods[i].renderers[0].GetComponent <BillboardRenderer>() != null) { continue; } else { // Init the object with the lowest possible LOD speedData.m_SpeedTreeWindObject = GameObject.Instantiate(lods[i].renderers[0].gameObject, attachmentPoint); break; } } // Set the data speedData.m_SpeedTreeWindObjectMesh = speedData.m_SpeedTreeWindObject.GetComponentInChildren <MeshRenderer>(); // Set the NULL invisible shader Shader nullShader = painter.GetShaderNull(); FoliageLog.Assert(nullShader, "Null shader not found! Make sure it exists and that it compiled!"); // Set the invisible null shader, we only need the wind Material[] mats = speedData.m_SpeedTreeWindObjectMesh.materials; for (int i = 0; i < mats.Length; i++) { mats[i].shader = nullShader; } // Attach the wind object. Ensures that we are enabled. speedData.m_SpeedTreeWindObject.AddComponent <FoliageWindTreeWind>(); speedData.m_SpeedTreeWindObjectMesh.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; speedData.m_SpeedTreeWindObject.transform.SetParent(attachmentPoint, false); speedData.m_SpeedTreeWindObject.transform.localPosition = new Vector3(0, 0, 0); MeshFilter m = speedData.m_SpeedTreeWindObject.GetComponentInChildren <MeshFilter>(); Bounds b = m.mesh.bounds; b.Expand(4.5f); m.mesh.bounds = b; speedData.m_SpeedTreeWindObject.GetComponentInChildren <MeshFilter>().mesh = m.mesh; } // Not used here, since at edit time we already created the materials /* * if (type.Type == EFoliageType.SPEEDTREE_GRASS) * { * Shader shader = painter.GetShaderGrass(); * FoliageLog.Assert(shader, "Could not find shader: Critias/SpeedTree_Grass! Make sure that it is added to the project and that it compiled!"); * * FoliageTypeLODGrass lodGrass = type.m_RuntimeData.m_LODDataGrass; * * // Override the material at runtime * // lodGrass.m_Material = new Material(lodGrass.m_Material); * // lodGrass.m_Material.shader = shader; * * // Enable it for instancing * // lodGrass.m_Material.enableInstancing = true; * } * else if(type.Type == EFoliageType.SPEEDTREE_TREE || type.Type == EFoliageType.SPEEDTREE_TREE_BILLBOARD) * { * Shader shader = painter.GetShaderTreeMaster(); * FoliageLog.Assert(shader, "Could not find shader: Critias/SpeedTree_Master! Make sure that it is added to the project and that it compiled!"); * * FoliageTypeLODTree[] lodTree = type.m_RuntimeData.m_LODDataTree; * * for(int i = 0; i < lodTree.Length; i++) * { * FoliageTypeLODTree tree = lodTree[i]; * * Material[] mats = tree.m_Materials; * * for(int m = 0; m < mats.Length; m++) * { * // Set the new material * //mats[m] = new Material(mats[m]); * //mats[m].shader = shader; * * // Enable instancing * //mats[m].enableInstancing = true; * } * * tree.m_Materials = mats; * } * } */ // We did initialize type.IsRuntimeInitialized = true; }
/** To be used at edit-time */ public static void BuildDataPartialEditTime(FoliagePainter painter, FoliageType type) { GameObject prefab = type.m_Prefab; // Update the type type.Type = type.Type; FoliageLog.Assert(prefab != null, "Null foliage prefab!"); if (type.m_RuntimeData == null) { type.m_RuntimeData = new FoliageTypeRuntimeData(); } FoliageTypeRuntimeData runtime = type.m_RuntimeData; List <Material> checkMaterials = new List <Material>(); // Init the SpeedTree data if (type.IsSpeedTreeType) { if (runtime.m_SpeedTreeData == null) { runtime.m_SpeedTreeData = new FoliageTypeSpeedTreeData(); } } if (type.IsGrassType) { // Build the data universally for all grass types if (runtime.m_LODDataGrass == null) { runtime.m_LODDataGrass = new FoliageTypeLODGrass(); } runtime.m_LODDataGrass.m_Mesh = prefab.GetComponentInChildren <MeshFilter>().sharedMesh; runtime.m_LODDataGrass.m_Material = prefab.GetComponentInChildren <MeshRenderer>().sharedMaterial; checkMaterials.Add(runtime.m_LODDataGrass.m_Material); FoliageLog.Assert(runtime.m_LODDataGrass.m_Mesh != null, "Could not find mesh for type: " + prefab.name + ". Make sure that is has at least one mesh and one material"); FoliageLog.Assert(runtime.m_LODDataGrass.m_Material != null, "Could not find material for type: " + prefab.name + ". Make sure that is has at least one mesh and one material"); } else { LODGroup group = prefab.GetComponent <LODGroup>(); if (group == null) { FoliageLog.w("Detected tree: " + prefab.name + " without a lod group. Are you sure that you require a tree for this?"); if (runtime.m_LODDataTree == null || runtime.m_LODDataTree.Length == 0) { runtime.m_LODDataTree = new FoliageTypeLODTree[1]; } runtime.m_LODDataTree[0] = new FoliageTypeLODTree(); runtime.m_LODDataTree[0].m_Mesh = prefab.GetComponentInChildren <MeshFilter>().sharedMesh; runtime.m_LODDataTree[0].m_Materials = prefab.GetComponentInChildren <MeshRenderer>().sharedMaterials; runtime.m_LODDataTree[0].m_EndDistance = type.m_RenderInfo.m_MaxDistance; checkMaterials.AddRange(runtime.m_LODDataTree[0].m_Materials); } else { List <FoliageTypeLODTree> treeLods = new List <FoliageTypeLODTree>(group.lodCount); LOD[] lods = group.GetLODs(); for (int i = 0; i < group.lodCount; i++) { if (lods[i].renderers[0].gameObject.GetComponent <BillboardRenderer>() != null) { // Extract the billboard data var speedData = runtime.m_SpeedTreeData; FoliageWindTreeUtilities.ExtractBillboardData(lods[i].renderers[0].gameObject.GetComponent <BillboardRenderer>(), speedData); continue; } FoliageTypeLODTree treeLod = new FoliageTypeLODTree(); MeshRenderer rend = lods[i].renderers[0].gameObject.GetComponent <MeshRenderer>(); MeshFilter filter = lods[i].renderers[0].gameObject.GetComponent <MeshFilter>(); treeLod.m_Mesh = filter.sharedMesh; treeLod.m_Materials = rend.sharedMaterials; checkMaterials.AddRange(rend.sharedMaterials); treeLods.Add(treeLod); } runtime.m_LODDataTree = treeLods.ToArray(); // Update the LOD distances UpdateDistancesLOD(runtime.m_LODDataTree, lods, type.m_RenderInfo.m_MaxDistance, type.IsSpeedTreeType); } } if (checkMaterials.Count > 0) { if (type.IsSpeedTreeType) { if (type.m_RenderInfo.m_Hue == new Color(0, 0, 0, 0)) { type.m_RenderInfo.m_Hue = checkMaterials[0].GetColor("_HueVariation"); } if (type.m_RenderInfo.m_Color == new Color(0, 0, 0, 0)) { type.m_RenderInfo.m_Color = checkMaterials[0].GetColor("_Color"); } } for (int i = 0; i < checkMaterials.Count; i++) { if (checkMaterials[i].enableInstancing == false) { checkMaterials[i].enableInstancing = true; FoliageLog.w("Material: [" + checkMaterials[i].name + "] did not had instancing enabled! We enabled it!"); } } } // Moved the build at partial edit-time if (type.Type == EFoliageType.SPEEDTREE_GRASS) { Shader shader = painter.GetShaderGrass(); FoliageLog.Assert(shader, "Could not find shader: Critias/SpeedTree_Grass! Make sure that it is added to the project and that it compiled!"); FoliageTypeLODGrass lodGrass = type.m_RuntimeData.m_LODDataGrass; // Override the material at runtime lodGrass.m_Material = new Material(lodGrass.m_Material); lodGrass.m_Material.shader = shader; // Enable it for instancing lodGrass.m_Material.enableInstancing = true; } else if (type.Type == EFoliageType.SPEEDTREE_TREE || type.Type == EFoliageType.SPEEDTREE_TREE_BILLBOARD) { Shader shader = painter.GetShaderTreeMaster(); FoliageLog.Assert(shader, "Could not find shader: Critias/SpeedTree_Master! Make sure that it is added to the project and that it compiled!"); FoliageTypeLODTree[] lodTree = type.m_RuntimeData.m_LODDataTree; for (int i = 0; i < lodTree.Length; i++) { FoliageTypeLODTree tree = lodTree[i]; Material[] mats = tree.m_Materials; for (int m = 0; m < mats.Length; m++) { // Set the new material mats[m] = new Material(mats[m]); mats[m].shader = shader; // Enable instancing mats[m].enableInstancing = true; } tree.m_Materials = mats; } } // Set the materials the values for enabling the bend stuff if we have it if (type.IsGrassType) { if (type.m_EnableBend) { type.m_RuntimeData.m_LODDataGrass.m_Material.EnableKeyword("CRITIAS_DISTANCE_BEND"); } else { type.m_RuntimeData.m_LODDataGrass.m_Material.DisableKeyword("CRITIAS_DISTANCE_BEND"); } } }
private void ProcessSubdividedCell(FoliageCellDataRuntime cell, FoliageCellSubdividedDataRuntime cellSubdivided, float distance) { for (int foliageType = 0, foliageTypeCount = cellSubdivided.m_TypeHashLocationsRuntime.Length; foliageType < foliageTypeCount; foliageType++) { FoliageType type = m_FoliageTypes[cellSubdivided.m_TypeHashLocationsRuntime[foliageType].Key]; float maxTypeDist = type.m_RenderInfo.m_MaxDistance * type.m_RenderInfo.m_MaxDistance; if (distance <= maxTypeDist) { var batches = cellSubdivided.m_TypeHashLocationsRuntime[foliageType].Value.m_EditTime; // Set the MPB values that are universal for all grass types MaterialPropertyBlock mpb = type.m_RuntimeData.m_TypeMPB; mpb.SetFloat(m_ShaderIDCritiasFoliageDistance, type.m_RenderInfo.m_MaxDistance); mpb.SetFloat(m_ShaderIDCritiasFoliageDistanceSqr, maxTypeDist); // TODO: Set bend data if we have it for this type if (type.m_EnableBend) { mpb.SetFloat(m_ShaderIDCritiasBendDistance, type.m_BendDistance); mpb.SetFloat(m_ShaderIDCritiasBendScale, type.m_BendPower); mpb.SetVector(m_ShaderIDCritiasBendPosition, m_CurrentFrameBendPosition); } // Get data from the type Mesh mesh = type.m_RuntimeData.m_LODDataGrass.m_Mesh; Material mat = type.m_RuntimeData.m_LODDataGrass.m_Material; // Only if we have the type for rendering indirect if (type.RenderIndirect && m_CurrentFrameAllowIndirect) { long indirectCachedDataKey = ((((long)cell.m_Position.GetHashCode()) << 32) | ((long)cellSubdivided.m_Position.GetHashCode())) + type.m_Hash; // * 0xF01226E02D41B if (m_CachedGPUBufferData.ContainsKey(indirectCachedDataKey) == false) { GPUBufferCellCachedData data = new GPUBufferCellCachedData(); // Merge the buffers Matrix4x4[] allInstances; // Build all the data if (batches.Length > 1) { int totalCount = 0; for (int batchIdx = 0; batchIdx < batches.Length; batchIdx++) { totalCount += batches[batchIdx].Length; } List <Matrix4x4> concat = new List <Matrix4x4>(totalCount); for (int batchIdx = 0; batchIdx < batches.Length; batchIdx++) { concat.AddRange(batches[batchIdx]); } allInstances = concat.ToArray(); } else { allInstances = batches[0]; } // Set the position data data.m_BufferPositions = new ComputeBuffer(allInstances.Length, 64); data.m_BufferPositions.SetData(allInstances); // Set the arguments data.m_BufferArguments = new ComputeBuffer(1, m_TempDrawArgs.Length * sizeof(uint), ComputeBufferType.IndirectArguments); data.m_IndexCount = mesh.GetIndexCount(0); // Set the instance count data.m_InstanceCount = (uint)allInstances.Length; // Add the data. The cache will take care of clearing the data that is in excess m_CachedGPUBufferData.Add(indirectCachedDataKey, data); } GPUBufferCellCachedData indirectData = m_CachedGPUBufferData[indirectCachedDataKey]; // Set the buffer positions mpb.SetBuffer(m_ShaderIDCritiasInstanceBuffer, indirectData.m_BufferPositions); // Set the draw count and send it m_TempDrawArgs[0] = indirectData.m_IndexCount; m_TempDrawArgs[1] = (uint)(indirectData.m_InstanceCount * m_Settings.m_GrassDensity); indirectData.m_BufferArguments.SetData(m_TempDrawArgs); // Draw without shadows Graphics.DrawMeshInstancedIndirect(mesh, 0, mat, cellSubdivided.m_Bounds, indirectData.m_BufferArguments, 0, mpb, ShadowCastingMode.Off, true, m_CurrentFrameLayer, m_CurrentFrameCameraDraw); } else { // Cast shadow only if the type allows ShadowCastingMode castShadow = type.m_RenderInfo.m_CastShadow ? ShadowCastingMode.On : ShadowCastingMode.Off; // Pass with the MPB the data related to per-type distance for (int i = 0, batchCount = batches.Length; i < batchCount; i++) { Graphics.DrawMeshInstanced(mesh, 0, mat, batches[i], (int)(batches[i].Length * m_Settings.m_GrassDensity), mpb, castShadow, true, m_CurrentFrameLayer, m_CurrentFrameCameraDraw); m_DrawStats.m_ProcessedDrawCalls++; } } } } }
private void ProcessCellTree(FoliageCellDataRuntime cell, float distanceSqr, bool shadowCorrection, float shadowCorrectionDistanceSqr, bool shadowOnly) { // Process tree cell content with types for (int foliageType = 0, foliageTypeCount = cell.m_TypeHashLocationsRuntime.Length; foliageType < foliageTypeCount; foliageType++) { FoliageType type = m_FoliageTypes[cell.m_TypeHashLocationsRuntime[foliageType].Key]; var batches = cell.m_TypeHashLocationsRuntime[foliageType].Value.m_EditTime; float maxDistance = type.m_RenderInfo.m_MaxDistance; float maxDistanceSqr = maxDistance * maxDistance; MaterialPropertyBlock mpb = type.m_RuntimeData.m_TypeMPB; // Set the global per-type data mpb.SetFloat(m_ShaderIDCritiasFoliageDistance, maxDistance); mpb.SetFloat(m_ShaderIDCritiasFoliageDistanceSqr, maxDistanceSqr); // Set the per-lod data FoliageTypeLODTree[] treeLods = type.m_RuntimeData.m_LODDataTree; bool castAnyShadow = type.m_RenderInfo.m_CastShadow; ShadowCastingMode shadow = castAnyShadow ? ShadowCastingMode.On : ShadowCastingMode.Off; // Reset the temp count for (int i = 0; i < m_MtxLODTempCount.Length; i++) { m_MtxLODTempCount[i] = 0; m_MtxLODTempShadowCount[i] = 0; } float x, y, z; float dist; for (int treeIndex = 0, treeIndexCount = batches.Length; treeIndex < treeIndexCount; treeIndex++) { // Test the distance and the frustum cull Vector3 pos = batches[treeIndex].m_Position; x = pos.x - m_CurrentFrameCameraPosition.x; y = pos.y - m_CurrentFrameCameraPosition.y; z = pos.z - m_CurrentFrameCameraPosition.z; dist = x * x + y * y + z * z; if (dist <= maxDistanceSqr && GeometryUtility.TestPlanesAABB(m_CameraPlanes, batches[treeIndex].m_Bounds) && shadowOnly == false) { // Get the current LOD int currentLOD = GetCurrentLOD(ref treeLods, Mathf.Sqrt(dist)); int currentIdx = m_MtxLODTempCount[currentLOD]; // Add it to the LOD matrix m_MtxLODTemp[currentLOD][currentIdx].m00 = batches[treeIndex].m_Matrix.m00; m_MtxLODTemp[currentLOD][currentIdx].m01 = batches[treeIndex].m_Matrix.m01; m_MtxLODTemp[currentLOD][currentIdx].m02 = batches[treeIndex].m_Matrix.m02; m_MtxLODTemp[currentLOD][currentIdx].m03 = batches[treeIndex].m_Matrix.m03; m_MtxLODTemp[currentLOD][currentIdx].m10 = batches[treeIndex].m_Matrix.m10; m_MtxLODTemp[currentLOD][currentIdx].m11 = batches[treeIndex].m_Matrix.m11; m_MtxLODTemp[currentLOD][currentIdx].m12 = batches[treeIndex].m_Matrix.m12; m_MtxLODTemp[currentLOD][currentIdx].m13 = batches[treeIndex].m_Matrix.m13; m_MtxLODTemp[currentLOD][currentIdx].m20 = batches[treeIndex].m_Matrix.m20; m_MtxLODTemp[currentLOD][currentIdx].m21 = batches[treeIndex].m_Matrix.m21; m_MtxLODTemp[currentLOD][currentIdx].m22 = batches[treeIndex].m_Matrix.m22; m_MtxLODTemp[currentLOD][currentIdx].m23 = batches[treeIndex].m_Matrix.m23; // Increment the LOD count m_MtxLODTempCount[currentLOD]++; // If we reached 1000 elements, submit the batch if (m_MtxLODTempCount[currentLOD] >= FoliageGlobals.RENDER_BATCH_SIZE) { // Issue the draw and reset the count IssueBatchLOD(m_MtxLODTemp[currentLOD], m_MtxLODTempCount[currentLOD], treeLods[currentLOD], mpb, shadow); m_MtxLODTempCount[currentLOD] = 0; } } else if (castAnyShadow && shadowCorrection && dist <= shadowCorrectionDistanceSqr) { int currentLOD = GetCurrentLOD(ref treeLods, Mathf.Sqrt(dist)); int currentIdx = m_MtxLODTempShadowCount[currentLOD]; // Add it to the shadow matrix m_MtxLODTempShadow[currentLOD][currentIdx].m00 = batches[treeIndex].m_Matrix.m00; m_MtxLODTempShadow[currentLOD][currentIdx].m01 = batches[treeIndex].m_Matrix.m01; m_MtxLODTempShadow[currentLOD][currentIdx].m02 = batches[treeIndex].m_Matrix.m02; m_MtxLODTempShadow[currentLOD][currentIdx].m03 = batches[treeIndex].m_Matrix.m03; m_MtxLODTempShadow[currentLOD][currentIdx].m10 = batches[treeIndex].m_Matrix.m10; m_MtxLODTempShadow[currentLOD][currentIdx].m11 = batches[treeIndex].m_Matrix.m11; m_MtxLODTempShadow[currentLOD][currentIdx].m12 = batches[treeIndex].m_Matrix.m12; m_MtxLODTempShadow[currentLOD][currentIdx].m13 = batches[treeIndex].m_Matrix.m13; m_MtxLODTempShadow[currentLOD][currentIdx].m20 = batches[treeIndex].m_Matrix.m20; m_MtxLODTempShadow[currentLOD][currentIdx].m21 = batches[treeIndex].m_Matrix.m21; m_MtxLODTempShadow[currentLOD][currentIdx].m22 = batches[treeIndex].m_Matrix.m22; m_MtxLODTempShadow[currentLOD][currentIdx].m23 = batches[treeIndex].m_Matrix.m23; // Increment count m_MtxLODTempShadowCount[currentLOD]++; if (m_MtxLODTempShadowCount[currentLOD] >= FoliageGlobals.RENDER_BATCH_SIZE) { IssueBatchLOD(m_MtxLODTempShadow[currentLOD], m_MtxLODTempShadowCount[currentLOD], treeLods[currentLOD], mpb, ShadowCastingMode.ShadowsOnly); m_MtxLODTempShadowCount[currentLOD] = 0; } } } // If we have any leftovers for (int i = 0; i < treeLods.Length; i++) { if (m_MtxLODTempCount[i] > 0) { // Issue the draw and reset the count IssueBatchLOD(m_MtxLODTemp[i], m_MtxLODTempCount[i], treeLods[i], mpb, shadow); m_MtxLODTempCount[i] = 0; } if (m_MtxLODTempShadowCount[i] > 0) { IssueBatchLOD(m_MtxLODTempShadow[i], m_MtxLODTempShadowCount[i], treeLods[i], mpb, ShadowCastingMode.ShadowsOnly); m_MtxLODTempShadowCount[i] = 0; } } m_DrawStats.m_ProcessedInstances += batches.Length; } }
private GameObject GetColliderForPrototype(int hash) { FoliageType data = m_FoliageTypes[hash]; if (data.m_EnableCollision == false) { return(null); } if (m_Cache.ContainsKey(hash) == false) { // If we don't contain the key create and add it // If we don't have a tree with a collider, like a bush or something, just add a null mapping if (data.m_Prefab.GetComponentInChildren <Collider>() == null) { m_Cache.Add(hash, null); return(null); } else { // Create the collider prototype and remove all it's mesh renderers and stuff GameObject colliderPrototype = Instantiate(data.m_Prefab, m_ColliderHolder.transform); colliderPrototype.name = "ColliderPrototype_" + data.m_Prefab.name; // Clear the lod group LODGroup lod = colliderPrototype.GetComponent <LODGroup>(); if (lod) { DestroyImmediate(lod); } // Clear any owned GObjects that don't have colliders for (int i = colliderPrototype.transform.childCount - 1; i >= 0; i--) { GameObject owned = colliderPrototype.transform.GetChild(i).gameObject; if (owned.GetComponent <Collider>() == null) { DestroyImmediate(owned); } } Component[] components = colliderPrototype.GetComponentsInChildren <Component>(); // Delete all non-colliders for (int i = 0; i < components.Length; i++) { if ((components[i] is Collider) == false && (components[i] is Transform) == false) { DestroyImmediate(components[i]); } } // Deactivate it colliderPrototype.SetActive(false); // Create the cache entry CollisionCache cache = new CollisionCache(colliderPrototype, m_ColliderHolder); // Add the collision cache to our dictionary m_Cache.Add(hash, cache); return(cache.RetrieveInstance()); } } else { var cache = m_Cache[hash]; // We contain the cache, just retrieve an object if (cache != null) { return(m_Cache[hash].RetrieveInstance()); } else { return(null); } } }
private static void ExtractDetailsFromTerrain(FoliagePainter painter, Terrain terrain, List <FoliageDetailExtracterMapping> mappings, bool disable, bool delete) { string label = FoliageGlobals.LABEL_TERRAIN_DETAILS_EXTRACTED + terrain.name; FoliagePainterEditTime edit = painter.GetEditTime; int detailMapSizeW = terrain.terrainData.detailWidth; int detailMapSizeH = terrain.terrainData.detailHeight; float patchSizeW = terrain.terrainData.size.x / detailMapSizeW; float patchSizeH = terrain.terrainData.size.z / detailMapSizeH; int extracted = 0; // Extract the types for all the mapping for (int m = 0; m < mappings.Count; m++) { int layer = mappings[m].m_DetailLayer; int mapping = mappings[m].m_FoliageTypeHash; float density = mappings[m].m_ExtractedDensity; FoliageLog.Assert(painter.HasFoliageType(mapping), "Must have foliage hash!!"); // Get the foliage type FoliageType type = painter.GetFoliageTypeByHash(mapping); DetailPrototype proto = terrain.terrainData.detailPrototypes[layer]; // If we should align to the surface bool followTerrainNormal = type.m_PaintInfo.m_SurfaceAlign; Vector2 alignPercentage = type.m_PaintInfo.m_SurfaceAlignInfluence; // Get the terrain data int[,] data = terrain.terrainData.GetDetailLayer(0, 0, detailMapSizeW, detailMapSizeH, layer); // Iterate data for (int i = 0; i < detailMapSizeH; i++) { for (int j = 0; j < detailMapSizeW; j++) { // j,i not i,j int count = data[j, i]; if (count > 0) { // Minimum 1 never 0 count = Mathf.Clamp(Mathf.CeilToInt(count * density), 1, count + 1); // Map from local space cell space to local terrain space Vector2 cellMin = new Vector2(patchSizeW * i, patchSizeH * j); Vector2 cellMax = cellMin + new Vector2(patchSizeW, patchSizeH); for (int d = 0; d < count; d++) { Vector3 randomInCell; randomInCell.x = Random.Range(cellMin.x, cellMax.x); randomInCell.z = Random.Range(cellMin.y, cellMax.y); randomInCell.y = 0; randomInCell = FoliageTerrainUtilities.TerrainLocalToTerrainNormalizedPos(randomInCell, terrain); float y = FoliageTerrainUtilities.TerrainHeight(randomInCell, terrain); // Build the rotation Quaternion rotation = Quaternion.identity; if (followTerrainNormal) { Quaternion slopeOrientation = Quaternion.LookRotation(FoliageTerrainUtilities.TerrainNormal(randomInCell, terrain)) * Quaternion.Euler(90, 0, 0); // How much we orient towards the slope rotation = Quaternion.Slerp(rotation, slopeOrientation, Random.Range(alignPercentage.x, alignPercentage.y)); } // Rotate around the Y axis rotation *= Quaternion.Euler(0, Random.Range(0, 360), 0); // Random in cell in world position randomInCell = FoliageTerrainUtilities.TerrainNormalizedToWorldPos(randomInCell, terrain); randomInCell.y = y; Vector3 scale; float x = Random.Range(proto.minWidth, proto.maxWidth); scale.x = x; scale.z = x; scale.y = Random.Range(proto.minHeight, proto.maxHeight); // Construct a foliage instance based on the foliage type data FoliageInstance instance = new FoliageInstance(); // Build the foliage data based on the type instance.m_Position = randomInCell; instance.m_Rotation = rotation; instance.m_Scale = scale; // Instantiate at a random pos in that cell edit.AddFoliageInstance(mapping, instance, label); extracted++; } } // If we should delete if (delete) { data[j, i] = 0; } } } // End detail array iteration // If we deleted set the new detail layer data if (delete) { terrain.terrainData.SetDetailLayer(0, 0, layer, data); } } // End types iteration // If we should disable the trees and foliage draw if (disable) { terrain.drawTreesAndFoliage = false; } // If we deleted anything we need to save if (delete) { UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(terrain.gameObject.scene); } FoliageLog.i("Extracted details: " + extracted + " from: " + terrain.name); }
private static void ExtractFromTerrain(FoliagePainter painterRaw, FoliagePainterEditTime painter, Terrain terrain, bool autoExtract, bool disable, bool delete) { string label = FoliageGlobals.LABEL_TERRAIN_EXTRACTED + terrain.name; // Ensure that we have all the required foliage types List <TreeInstance> terrainTreeInstances = new List <TreeInstance>(terrain.terrainData.treeInstances); TreePrototype[] terrainTreePrototypes = terrain.terrainData.treePrototypes; // Attempt to build the prefab's that we don't have if (autoExtract) { AutoExtractTypes(painter, terrain); } int extracted = 0; for (int i = terrainTreeInstances.Count - 1; i >= 0; i--) { GameObject proto = terrainTreePrototypes[terrainTreeInstances[i].prototypeIndex].prefab; if (painter.HasFoliageType(proto) == false) { continue; } int hash = painter.GetFoliageTypeHash(proto); FoliageType type = painterRaw.GetFoliageTypeByHash(hash); FoliageInstance instance = new FoliageInstance(); // Populate the data // Get the world data float YOffset = Random.Range(type.m_PaintInfo.m_YOffset.x, type.m_PaintInfo.m_YOffset.y); Vector3 worldPosition = FoliageTerrainUtilities.TerrainNormalizedToWorldPos(terrainTreeInstances[i].position, terrain) + new Vector3(0, YOffset, 0); // YOffset too Vector3 worldScale = new Vector3(terrainTreeInstances[i].widthScale, terrainTreeInstances[i].heightScale, terrainTreeInstances[i].widthScale); Vector3 worldRotation = new Vector3(0, terrainTreeInstances[i].rotation * Mathf.Rad2Deg, 0); instance.m_Position = worldPosition; instance.m_Scale = worldScale; instance.m_Rotation = Quaternion.Euler(worldRotation.x, worldRotation.y, worldRotation.z); // Add the foliage instance painter.AddFoliageInstance(hash, instance, label); extracted++; // Delete the instance from the terrain if we have to if (delete) { terrainTreeInstances.RemoveAt(i); } } if (disable) { terrain.drawTreesAndFoliage = false; } // If we should delete then delete the instance if (delete) { terrain.terrainData.treeInstances = terrainTreeInstances.ToArray(); UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(terrain.gameObject.scene); } FoliageLog.i("Extracted objects: " + extracted + " from: " + terrain.name); }