/** * Runs basic code to add a tree at runtime. You should implement your own logic and not call this each frame * since it will cause a severe FPS drop. */ void Update() { if (Input.GetMouseButtonDown(0) == false) { return; } CritiasFoliage.FoliagePainterRuntime runtime = FindObjectOfType <CritiasFoliage.FoliagePainter>().GetRuntime; var types = runtime.GetFoliageTypes(); CritiasFoliage.FoliageTypeRuntime treeType = default(CritiasFoliage.FoliageTypeRuntime); bool foundTree = false; for (int i = 0; i < types.Count; i++) { if (types[i].m_IsGrassType == false) { treeType = types[i]; foundTree = true; } } if (foundTree == false) { Debug.LogError("Could not find a tree type! Please add it in the inspector!"); return; } RaycastHit hit; if (Physics.Raycast(transform.position, transform.forward, out hit, 100, ~0)) { if (hit.collider) { CritiasFoliage.FoliageInstance inst = new CritiasFoliage.FoliageInstance(); // All the data that we need to set inst.m_Position = hit.point; inst.m_Scale = Vector3.one; inst.m_Rotation = Quaternion.Euler(0, Random.Range(0, 360), 0); runtime.AddFoliageInstance(treeType.m_Hash, inst); } } }
protected override bool DrawWizardGUI() { EditorGUILayout.LabelField("Add terrains or objects here: "); using (new ScopedLayout(() => { EditorGUILayout.BeginVertical("Box"); }, EBeginMode.BEGIN_HORIZONTAL)) { GameObject possibleTerrains = EditorGUILayout.ObjectField(null, typeof(GameObject), true) as GameObject; if (possibleTerrains != null) { List <Terrain> terrains = new List <Terrain>(); RecursivelyExtractTerrains(possibleTerrains, terrains); for (int i = 0; i < terrains.Count; i++) { Terrain extr = terrains[i]; if (m_TerrainsExtract.Contains(extr) == false) { if (extr.GetComponent <Terrain>() != null) { // If it's the first (main) terrain if (m_TerrainsExtract.Count == 0) { DetailPrototype[] protos = extr.GetComponent <Terrain>().terrainData.detailPrototypes; if (protos != null && protos.Length > 0) { m_TerrainsExtract.Add(extr); } else { EditorUtility.DisplayDialog("Warning!", "The first added (main) terrain does not have any terrain details!", "Ok"); } } else { // Check if the same details appear if (HasSameDetails(m_TerrainsExtract[0].GetComponent <Terrain>(), extr.GetComponent <Terrain>())) { m_TerrainsExtract.Add(extr); } else { EditorUtility.DisplayDialog("Warning!", "The added terrain does not have the same details as the first (main) terrain!", "Ok"); } } } else { EditorUtility.DisplayDialog("Warning!", "You can only extract details from terrains!", "Ok"); } } } } } // Display all the detail data if (m_TerrainsExtract.Count == 0) { return(false); } EditorGUILayout.Space(); // Extracted terrains EditorGUILayout.LabelField("Terrain to extract details from: "); // Show the data using (new ScopedLayout(() => { EditorGUILayout.BeginVertical("Box"); }, EBeginMode.BEGIN_HORIZONTAL)) { for (int i = 0; i < m_TerrainsExtract.Count; i++) { EditorGUILayout.LabelField("(T) " + m_TerrainsExtract[i].name + (i == 0 ? " [Main Details]" : "")); } } EditorGUILayout.Space(); EditorGUILayout.LabelField("Details mappings: "); if (m_Prototypes == null) { m_Prototypes = m_TerrainsExtract[0].GetComponent <Terrain>().terrainData.detailPrototypes; if (m_Prototypes == null || m_Prototypes.Length == 0) { FoliageLog.e("No terrain details found!"); return(false); } // Generate the UI data m_PrototypesData = new DetailPrototypeData[m_Prototypes.Length]; for (int i = 0; i < m_PrototypesData.Length; i++) { m_PrototypesData[i] = new DetailPrototypeData(); m_PrototypesData[i].m_DetailLayer = i; string protoName = m_Prototypes[i].prototype != null ? m_Prototypes[i].prototype.name : m_Prototypes[i].prototypeTexture.name; // Attempt to search through the data to check for a name or something int foundIdx = m_TypesRuntime.FindIndex((x) => { return(x.m_Name.ToLowerInvariant().Replace(" ", "").Contains(protoName.ToLowerInvariant().Replace(" ", ""))); }); if (foundIdx >= 0) { m_PrototypesData[i].m_NoneMapping = false; m_PrototypesData[i].m_FoliageHashMapping = m_TypesRuntime[foundIdx].m_Hash; m_PrototypesData[i].m_FoliageTypeNameMapping = m_TypesRuntime[foundIdx].m_Name; } } } // Set all the data related to that terrain and stuff DetailPrototype[] prototypes = m_Prototypes; using (new ScopedLayout(() => { EditorGUILayout.BeginVertical("Box"); }, EBeginMode.BEGIN_VERTICAL)) { for (int i = 0; i < prototypes.Length; i++) { DetailPrototype proto = prototypes[i]; DetailPrototypeData protoData = m_PrototypesData[i]; using (new ScopedLayout(() => { EditorGUILayout.BeginVertical(); }, EBeginMode.BEGIN_HORIZONTAL)) { string name; if (proto.prototype != null) { name = proto.prototype.name; } else { name = proto.prototypeTexture.name; } // Each prototype data using (new ScopedLayout(() => { EditorGUILayout.BeginHorizontal(); }, EBeginMode.BEGIN_HORIZONTAL)) { protoData.m_ShouldExtract = EditorGUILayout.Toggle(new GUIContent("Extract: [" + name + "]", "If we should extract that type"), protoData.m_ShouldExtract); if (protoData.m_ShouldExtract) { EditorGUILayout.LabelField("as: ", GUILayout.Width(30)); bool dropdown = EditorGUILayout.DropdownButton(new GUIContent(protoData.m_NoneMapping ? "None" : protoData.m_FoliageTypeNameMapping, "To what foliage type this detail will be changed transformed when extracting from the terrain"), FocusType.Passive); if (dropdown) { GenericMenu menu = new GenericMenu(); menu.AddItem(new GUIContent("None"), protoData.m_NoneMapping, (object obj) => { protoData.m_NoneMapping = true; }, null); menu.AddSeparator(""); for (int r = 0; r < m_TypesRuntime.Count; r++) { FoliageTypeRuntime rt = m_TypesRuntime[r]; bool on = protoData.m_NoneMapping == false && protoData.m_FoliageHashMapping == rt.m_Hash; menu.AddItem(new GUIContent(rt.m_Name), on, (object obj) => { protoData.m_NoneMapping = false; protoData.m_FoliageHashMapping = ((FoliageTypeRuntime)obj).m_Hash; protoData.m_FoliageTypeNameMapping = ((FoliageTypeRuntime)obj).m_Name; }, rt); } menu.ShowAsContext(); } } } if (protoData.m_ShouldExtract) { protoData.m_ExtractedDensity = EditorGUILayout.Slider(new GUIContent("[" + name + "] Density [0..1]", "A multiplier for the count of extracted details from the terrain. 1 means extract all instances, 0.5 means extract half of them, 0 extract none. " + "Use mostly for extracted grass, but not details like rocks or anything else like that. Since on the terrain we only have a lot of billboards as " + "grass and we might map them to some 3D mesh clumps it's higly likely that we will only need a half (0.5) or a third (0.3) of that existing terrain data. "), protoData.m_ExtractedDensity, 0, 1); } } } } EditorGUILayout.Space(); // Settings EditorGUILayout.LabelField("Settings: "); using (new ScopedLayout(() => { EditorGUILayout.BeginVertical("Box"); }, EBeginMode.BEGIN_VERTICAL)) { m_DisableAfterExtraction = EditorGUILayout.Toggle(new GUIContent( "Disable After Extraction", "If we should disable the details after we extracted them from the terrain. This will will set 'Terrain.drawTreesAndFoliage' to false."), m_DisableAfterExtraction, GUILayout.ExpandWidth(true)); bool deleteAfterExtraction = EditorGUILayout.Toggle(new GUIContent( "Delete After Extraction", "If this is checked it will delete all the details that were extracted. Will delete the extracted details. Not advisable!"), m_DeleteAfterExtraction, GUILayout.ExpandWidth(true)); if (m_DeleteAfterExtraction != deleteAfterExtraction) { if (deleteAfterExtraction) { bool sure = EditorUtility.DisplayDialog("Warning", "Setting this to true will delete all the extracted details from the terrain! " + "Not recomended if you want to try multiple iterations! Are you sure?", "Yes", "No"); if (sure) { m_DeleteAfterExtraction = true; } } else { m_DeleteAfterExtraction = deleteAfterExtraction; } } } return(false); }