private static void AutoExtractTypes(FoliagePainterEditTime painter, Terrain terrain) { TreeInstance[] terrainTreeInstances = terrain.terrainData.treeInstances; TreePrototype[] terrainTreePrototypes = terrain.terrainData.treePrototypes; for (int i = 0; i < terrainTreePrototypes.Length; i++) { TreePrototype proto = terrainTreePrototypes[i]; GameObject prefab = proto.prefab; // See if we already have it if (painter.HasFoliageType(prefab) == false) { // Attempt to add it FoliageTypeBuilder builder; bool anyError; FoliageUtilitiesEditor.ConfigurePrefab(prefab, out builder, out anyError); if (anyError == false) { painter.AddFoliageType(builder); } else { FoliageLog.e("Could not add foliage type: " + prefab.name + " due to error. Check the log for more info."); } } } }
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 ConfigurePrefab(GameObject foliage, out FoliageTypeBuilder outBuilder, out bool outAnyError) { PrefabType type = PrefabUtility.GetPrefabType(foliage); if (type != PrefabType.ModelPrefab && type != PrefabType.Prefab) { FoliageLog.e("The prefab type of: " + foliage.name + " is: '" + type + "'! Must be a 'ModelPrefab' or a 'Prefab'!"); outAnyError = true; } string path = AssetDatabase.GetAssetPath(foliage); FoliageLog.i("Path of added foliage type: " + path); FoliageTypeBuilder builder = new FoliageTypeBuilder(); // Build all it's required data builder.m_PaintInfo = new FoliageTypePaintInfo(); builder.m_RenderInfo = new FoliageTypeRenderInfo(); bool anyWeirdGrassError = false; // Attempt to auto-configure the foliage for the easiest possible user interaction if (path.EndsWith(".spm")) { // We have a SpeedTree LODGroup lodGroup = foliage.GetComponent <LODGroup>(); if (lodGroup != null) { if (lodGroup.lodCount == 1) { FoliageLog.i("Detected a SpeedTree grass, since it has 1 LOD!"); builder.m_Type = EFoliageType.SPEEDTREE_GRASS; builder.m_EnableCollision = false; if (foliage.GetComponentInChildren <MeshRenderer>() == null) { anyWeirdGrassError = true; FoliageLog.e("SpeedTree grass without any MeshRenderer detected!"); } } else { FoliageLog.i("Detected a SpeedTree tree, since it has 1+ LODS!"); builder.m_Type = EFoliageType.SPEEDTREE_TREE; builder.m_EnableCollision = foliage.GetComponentInChildren <Collider>() != null ? true : false; if (foliage.GetComponentInChildren <BillboardRenderer>() != null) { builder.m_Type = EFoliageType.SPEEDTREE_TREE_BILLBOARD; } if (foliage.GetComponentInChildren <MeshRenderer>() == null) { anyWeirdGrassError = true; FoliageLog.e("SpeedTree tree without any MeshRenderer detected!"); } } // Set the bounds builder.m_Bounds = lodGroup.GetLODs()[0].renderers[0].GetComponent <MeshFilter>().sharedMesh.bounds; } else { FoliageLog.e("Weird, we have a SpeedTree without a lod group. Anything wrong here? Please fix."); anyWeirdGrassError = true; builder.m_Bounds = foliage.GetComponentInChildren <MeshFilter>().sharedMesh.bounds; } } else { LODGroup lodGroup = foliage.GetComponent <LODGroup>(); if (lodGroup != null && lodGroup.lodCount > 1) { FoliageLog.i("Detected an object tree!"); builder.m_Type = EFoliageType.OTHER_TREE; builder.m_EnableCollision = foliage.GetComponentInChildren <Collider>() != null ? true : false; if (foliage.GetComponentInChildren <MeshRenderer>() == null) { anyWeirdGrassError = true; FoliageLog.e("Object tree without any MeshRenderer detected!"); } // Set the bounds builder.m_Bounds = lodGroup.GetLODs()[0].renderers[0].GetComponent <MeshFilter>().sharedMesh.bounds; } else { FoliageLog.i("Detected an object grass!"); builder.m_Type = EFoliageType.OTHER_GRASS; builder.m_EnableCollision = false; if (foliage.GetComponentInChildren <MeshRenderer>() == null) { anyWeirdGrassError = true; FoliageLog.e("Object grass without any LOD group or MeshRenderer detected!"); } // Set the bounds builder.m_Bounds = foliage.GetComponentInChildren <MeshFilter>().sharedMesh.bounds; } } LODGroup group = foliage.GetComponent <LODGroup>(); if (group != null && group.lodCount > 0) { LOD[] lods = group.GetLODs(); for (int i = 0; i < lods.Length; i++) { Renderer[] rends = lods[i].renderers; if (rends == null || rends.Length != 1) { anyWeirdGrassError = true; FoliageLog.e("Detected object with a lod without any renderers on it or with more than one renderer attached to it!"); } } } if (anyWeirdGrassError) { EditorUtility.DisplayDialog("Error", "Found error for foliage: " + foliage.name + "! Could not add to system! Check the log for more info!", "Ok"); FoliageLog.e("Found error for foliage: " + foliage.name + "! Could not add to system!"); } else { // Set the prefab builder.m_Prefab = foliage; builder.m_PaintEnabled = true; switch (builder.m_Type) { case EFoliageType.OTHER_GRASS: case EFoliageType.SPEEDTREE_GRASS: builder.m_RenderInfo.m_CastShadow = false; builder.m_RenderInfo.m_MaxDistance = 30; break; case EFoliageType.OTHER_TREE: case EFoliageType.SPEEDTREE_TREE: case EFoliageType.SPEEDTREE_TREE_BILLBOARD: builder.m_RenderInfo.m_CastShadow = true; builder.m_RenderInfo.m_MaxDistance = 100; break; } } switch (builder.m_Type) { case EFoliageType.OTHER_GRASS: case EFoliageType.SPEEDTREE_GRASS: builder.m_PaintInfo.m_SurfaceAlign = true; break; case EFoliageType.OTHER_TREE: case EFoliageType.SPEEDTREE_TREE_BILLBOARD: case EFoliageType.SPEEDTREE_TREE: builder.m_PaintInfo.m_SurfaceAlign = false; break; } if (anyWeirdGrassError == false) { if (builder.m_Type == EFoliageType.SPEEDTREE_GRASS || builder.m_Type == EFoliageType.SPEEDTREE_TREE || builder.m_Type == EFoliageType.SPEEDTREE_TREE_BILLBOARD) { // Get a hue builder.m_RenderInfo.m_Hue = foliage.GetComponentInChildren <MeshRenderer>().sharedMaterial.GetColor("_HueVariation"); builder.m_RenderInfo.m_Color = foliage.GetComponentInChildren <MeshRenderer>().sharedMaterial.GetColor("_Color"); if (builder.m_RenderInfo.m_Hue == new Color(0, 0, 0, 0)) { builder.m_RenderInfo.m_Hue = Color.white; } if (builder.m_RenderInfo.m_Color == new Color(0, 0, 0, 0)) { builder.m_RenderInfo.m_Color = Color.white; } } else { builder.m_RenderInfo.m_Hue = Color.white; builder.m_RenderInfo.m_Color = Color.white; } } outAnyError = anyWeirdGrassError; outBuilder = builder; }
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); }
void OnWizardCreate() { if (m_TerrainsExtract.Count > 0) { if (m_TerrainsExtract.Count > 1) { for (int i = 1; i < m_TerrainsExtract.Count; i++) { if (HasSameDetails(m_TerrainsExtract[0].GetComponent <Terrain>(), m_TerrainsExtract[i].GetComponent <Terrain>()) == false) { FoliageLog.e("Missing type when verified! Don't modify the types/details while extracting details!"); return; } } } Terrain terrain = m_TerrainsExtract[0].GetComponent <Terrain>(); DetailPrototype[] proto = terrain.terrainData.detailPrototypes; List <FoliageDetailExtracterMapping> mappings = new List <FoliageDetailExtracterMapping>(); // Build all the extracting data for (int i = 0; i < m_PrototypesData.Length; i++) { var data = m_PrototypesData[i]; if (data.m_ShouldExtract == true && data.m_NoneMapping == false) { FoliageDetailExtracterMapping mapping = new FoliageDetailExtracterMapping(); mapping.m_DetailLayer = data.m_DetailLayer; mapping.m_FoliageTypeHash = data.m_FoliageHashMapping; mapping.m_ExtractedDensity = data.m_ExtractedDensity; mappings.Add(mapping); } } if (mappings.Count > 0) { // Check that we have all the types and nothing has been tampered between the create for (int i = 0; i < mappings.Count; i++) { if (m_Painter.HasFoliageType(mappings[i].m_FoliageTypeHash) == false) { FoliageLog.e("Missing type when created! Don't modify the types while extracting details!"); return; } if (mappings[i].m_DetailLayer < 0 || mappings[i].m_DetailLayer >= proto.Length) { FoliageLog.e("Missing type when created! Don't modify the types while extracting details!"); return; } } // Proceed with the extraction m_Callback(mappings, m_TerrainsExtract, m_DisableAfterExtraction, m_DeleteAfterExtraction); } else { FoliageLog.i("Nothing to extract!"); } } else { FoliageLog.i("Nothing to extract!"); } }