private void Awake() { if (m_Painter == null) { m_Painter = FindObjectOfType <FoliagePainter>(); } }
/** * Extracts the given detail layers from the terrains. * * @param terrains * The terrains from which we extract the data from. All must have the exact same terrain detail layers * @param mapping * The mapping of the items that we want to extract. The key is the terrain detail layer that we want to extract from and the value is the foliage type hash * that we are going to instantiate for that detail type's layer */ public static void ExtractDetailsFromTerrains(FoliagePainter painter, IEnumerable <Terrain> terrains, List <FoliageDetailExtracterMapping> mappings, bool disable, bool delete) { foreach (Terrain t in terrains) { ExtractDetailsFromTerrain(painter, t, mappings, disable, delete); } }
public void Init(FoliagePainter painter, OnExtractDetailsPressed extract) { m_Painter = painter; m_Callback = extract; m_TypesRuntime = m_Painter.GetFoliageTypesRuntime(); }
/** * Prepare the renderer for the rendering. */ public void InitRenderer(FoliagePainter painter, FoliageDataRuntime dataToRender, List <FoliageType> foliageTypes) { m_FoliageData = dataToRender; foreach (FoliageType type in foliageTypes) { FoliageTypeUtilities.BuildDataRuntime(painter, type, m_Settings.m_WindTransform); } UpdateFoliageTypes(foliageTypes); }
public static void ExtractFoliage(FoliagePainter painter, IEnumerable <GameObject> objects, bool autoExtract, bool disable, bool delete) { foreach (GameObject obj in objects) { if (obj.GetComponent <Terrain>() != null) { ExtractFromTerrain(painter, painter.GetEditTime, obj.GetComponent <Terrain>(), autoExtract, disable, delete); } else { ExtractFromRootObject(painter.GetEditTime, obj, disable, delete); } } // Re-generate the billboards painter.GetEditTime.GenerateBillboards(); }
/** 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"); } } }
public FoliagePainterRuntime(FoliagePainter painter) { m_Painter = painter; }
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); }
public void Init(FoliagePainter painter, System.Action <int[]> extract) { m_Painter = painter; m_Callback = extract; }
public FoliagePainterEditTime(FoliagePainter painter) { m_Painter = painter; }