using UnityEditor; using UnityEngine; #if __MICROVERSE_SPLINES__ using UnityEngine.Splines; #endif using static JBooth.MicroVerseCore.Browser.ContentBrowser; namespace JBooth.MicroVerseCore.Browser { // default implimentation public class ContentTab { public virtual bool SupportsPlacementMode(PlacementMode pm) { return pm != PlacementMode.PaintSpline && pm != PlacementMode.PaintArea; } public virtual void DrawToolbar(ContentBrowser browser) { } public virtual GameObject Spawn(ContentBrowser browser, PresetItem preset, bool wasShiftPressed) { if (preset == null) return null; var cd = preset.content; if (cd == null) return null; GameObject instance = null; if (cd.prefab != null) { // TODO: If instantiated as a prefab, using the painter causes a hang // PrefabUtility.InstantiatePrefab(selected.prefab) as GameObject; // Update: Unpacking the prefab solves the problem. Keeping this comment here in case unforeseen issues arise // Original code was: instance = GameObject.Instantiate(selected.prefab); instance = PrefabUtility.InstantiatePrefab(cd.prefab) as GameObject; PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); instance.name = cd.prefab.name; instance.transform.SetParent(MicroVerse.instance?.transform); // overrides // shift at start of drag operation: force falloff type global bool forceFalloffTypeGlobal = wasShiftPressed; if (forceFalloffTypeGlobal) { FalloffOverride falloffOverride = instance.GetComponent(); if (falloffOverride && falloffOverride.enabled) { falloffOverride.filter.filterType = FalloffFilter.FilterType.Global; } else { Stamp[] stamps = instance.GetComponentsInChildren(); foreach (Stamp stamp in stamps) { if (stamp.GetFilterSet() == null) continue; stamp.GetFilterSet().falloffFilter.filterType = FalloffFilter.FilterType.Global; } } } MicroVerse.instance?.Invalidate(null); // TODO: Do better? } return instance; } #if __MICROVERSE_SPLINES__ /// /// Return if you want to continue spawning the object beyond the spline. /// /// /// /// public virtual bool BeforeSplineSpawn(PlacementMode pm, SplineContainer splineContainer, ContentCollection collection, PresetItem selectedPresetItem) { return true; } public virtual void AfterSplineSpawn(PlacementMode pm, GameObject spawned, SplineContainer splineContainer, SplineArea area, int areaUses) { if (area != null) { // add clear stamp if control is pressed and data needs it.. #if __MICROVERSE_VEGETATION__ if (Event.current.control) { // don't add if content is already setup to clear var clearStampExisting = spawned.GetComponentInChildren(); if (clearStampExisting == null) { bool clearObjects = false; bool clearDetails = spawned.GetComponentInChildren() != null; bool clearTrees = spawned.GetComponentInChildren() != null; #if __MICROVERSE_OBJECTS__ clearObjects = spawned.GetComponentInChildren() != null; #endif if (clearObjects || clearDetails || clearTrees) { GameObject clearObj = new GameObject("ClearStamp"); Undo.RegisterCreatedObjectUndo(clearObj, "Content Browser Create Object"); Undo.SetTransformParent(clearObj.transform, spawned.transform.parent, "Content Browser Create Object"); Undo.SetTransformParent(spawned.transform, clearObj.transform, "Content Browser Create Object"); var clear = Undo.AddComponent(clearObj); clear.filterSet.falloffFilter.filterType = FalloffFilter.FilterType.SplineArea; clear.filterSet.falloffFilter.splineArea = area; clear.filterSet.falloffFilter.splineAreaFalloff = 10; clear.clearTrees = clearTrees; clear.clearDetails = clearDetails; #if __MICROVERSE_OBJECTS__ clear.clearObjects = clearObjects; #endif } } else { clearStampExisting.filterSet.falloffFilter.splineArea = area; clearStampExisting.filterSet.falloffFilter.filterType = FalloffFilter.FilterType.SplineArea; } } #endif MicroVerse.instance.Invalidate(area.GetBounds()); } } #endif } public class TextureTab : ContentTab { public override bool SupportsPlacementMode(PlacementMode pm) { return true; } public override void DrawToolbar(ContentBrowser browser) { EditorGUILayout.LabelField("Size", GUILayout.Width(80)); browser.textureStampDefaultScale = EditorGUILayout.Vector3Field("", browser.textureStampDefaultScale, GUILayout.Width(200)); } public override GameObject Spawn(ContentBrowser browser, PresetItem preset, bool wasShiftPressed) { var instance = base.Spawn(browser, preset, wasShiftPressed); if (instance != null) instance.transform.localScale = browser.textureStampDefaultScale; return instance; } } public class ObjectTab : ContentTab { } public class AmbienceTab : ContentTab { public override bool SupportsPlacementMode(PlacementMode pm) { return true; } #if __MICROVERSE_AMBIANCE__ && __MICROVERSE_SPLINES__ public override void AfterSplineSpawn(PlacementMode pm, GameObject go, SplineContainer splineContainer, SplineArea area, int areaUses) { base.AfterSplineSpawn(pm, go, splineContainer, area, areaUses); var aas = go.GetComponentsInChildren(); foreach (var aa in aas) { if (pm == PlacementMode.PaintSpline) { aa.falloff = AmbientArea.AmbianceFalloff.Spline; aa.spline = splineContainer; } else { aa.falloff = AmbientArea.AmbianceFalloff.SplineArea; aa.spline = splineContainer; } if (areaUses == 0 && area != null) { Undo.DestroyObjectImmediate(area); } } } #endif } public class VegetationTab : ContentTab { public override bool SupportsPlacementMode(PlacementMode pm) { return true; } public override GameObject Spawn(ContentBrowser browser, PresetItem preset, bool wasShiftPressed) { GameObject instance = base.Spawn(browser, preset, wasShiftPressed); if (instance != null) { instance.transform.localScale = browser.vegetationStampDefaultScale; #if __MICROVERSE_VEGETATION__ var trees = instance.GetComponentsInChildren(); var details = instance.GetComponentsInChildren(); foreach (var t in trees) { t.seed = (uint)UnityEngine.Random.Range(0, 99); } #endif } return instance; } public override void DrawToolbar(ContentBrowser browser) { EditorGUILayout.LabelField("Size", GUILayout.Width(80)); browser.vegetationStampDefaultScale = EditorGUILayout.Vector3Field("", browser.vegetationStampDefaultScale, GUILayout.Width(200)); } } public class RoadTab : ContentTab { public override bool SupportsPlacementMode(PlacementMode pm) { return pm != PlacementMode.PaintArea; } #if __MICROVERSE_ROADS__ && __MICROVERSE_SPLINES__ RoadSystem FindOrCreateRoadSystem(ContentCollection collection) { RoadSystem[] roadSystems = null; if (MicroVerse.instance == null) { roadSystems = GameObject.FindObjectsOfType(); } else { roadSystems = MicroVerse.instance.GetComponentsInChildren(); } RoadSystem rs = null; foreach (var crs in roadSystems) { if (!string.IsNullOrEmpty(crs.contentID) && crs.contentID == collection.id) { rs = crs; break; } } if (rs == null) { string prefixName = "Road System"; RoadSystemConfig rscfg = null; if (collection.systemConfig != null) { rscfg = (RoadSystemConfig)collection.systemConfig; if (rscfg != null) { if (!string.IsNullOrEmpty(rscfg.namePrefix)) prefixName = rscfg.namePrefix + " System"; } } GameObject go = new GameObject(prefixName); rs = go.AddComponent(); rs.contentID = collection.id; rs.systemConfig = rscfg; if (MicroVerse.instance != null) { rs.transform.SetParent(MicroVerse.instance.transform); } rs.transform.localPosition = Vector3.zero; rs.transform.localScale = Vector3.one; rs.transform.localRotation = Quaternion.identity; } return rs; } public override GameObject Spawn(ContentBrowser browser, PresetItem preset, bool wasShiftPressed) { var instance = base.Spawn(browser, preset, wasShiftPressed); var rs = FindOrCreateRoadSystem(preset.collection); instance.transform.SetParent(rs.transform); rs.UpdateAll(); return instance; } public override bool BeforeSplineSpawn(PlacementMode pm, SplineContainer splineContainer, ContentCollection collection, PresetItem selectedPresetItem) { if (pm != PlacementMode.PaintSpline) return true; if (collection == null || collection.systemConfig == null) { GameObject.DestroyImmediate(splineContainer.gameObject); return false; } var rsc = (RoadSystemConfig)collection.systemConfig; if (rsc != null && rsc.splinePaintDefault != null) { var rs = FindOrCreateRoadSystem(collection); var road = Undo.AddComponent(splineContainer.gameObject); // find the road config for the selected intersection for the painting of the road RoadSystemConfig.SplinePaint selectedSplinePaint = null; if (selectedPresetItem != null) { foreach (RoadSystemConfig.SplinePaint splinePaint in rsc.splinePaintList) { if (splinePaint.intersection == selectedPresetItem.content.prefab) { selectedSplinePaint = splinePaint; break; } } } road.config = selectedSplinePaint != null ? selectedSplinePaint.config : rsc.splinePaintDefault; road.splineContainer = splineContainer; road.modifiesTerrain = rsc.modifyTerrainByDefault; road.gameObject.transform.SetParent(rs.transform); road.Generate(); rs.UpdateAll(); float grabDistance = 8f; road.UpdateConnections(rs, false, false, grabDistance); } return false; } #endif public override void DrawToolbar(ContentBrowser browser) { EditorGUILayout.LabelField("Height Offset", GUILayout.Width(120)); browser.roadHeightOffset = EditorGUILayout.FloatField("", browser.roadHeightOffset, GUILayout.Width(60)); } } public class CaveTab : RoadTab { } public class GlobalTab : ContentTab { public override bool SupportsPlacementMode(PlacementMode pm) { return false; } } public class BiomeTab : ContentTab { public override bool SupportsPlacementMode(PlacementMode pm) { return true; } } public class HeightTab : ContentTab { public override bool SupportsPlacementMode(PlacementMode pm) { return true; } public override void DrawToolbar(ContentBrowser browser) { EditorGUILayout.PrefixLabel("Falloff Type"); browser.filterTypeDefault = (FalloffDefault)EditorGUILayout.EnumPopup(browser.filterTypeDefault, GUILayout.Width(120)); EditorGUILayout.LabelField("Size", GUILayout.Width(80)); browser.heightStampDefaultScale = EditorGUILayout.Vector3Field("", browser.heightStampDefaultScale, GUILayout.Width(200)); } public override GameObject Spawn(ContentBrowser browser, PresetItem preset, bool wasShiftPressed) { GameObject instance = null; var cd = preset.content; if (cd == null) return null; if (cd.prefab != null) { // TODO: If instantiated as a prefab, using the painter causes a hang // PrefabUtility.InstantiatePrefab(selected.prefab) as GameObject; // Update: Unpacking the prefab solves the problem. Keeping this comment here in case unforeseen issues arise // Original code was: instance = GameObject.Instantiate(selected.prefab); instance = PrefabUtility.InstantiatePrefab(cd.prefab) as GameObject; PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); instance.name = cd.prefab.name; instance.transform.localScale = browser.heightStampDefaultScale; } else if (cd.stamp != null) { var tex = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(new GUID(cd.stamp))); if (tex != null) { instance = new GameObject(tex.name + " (Height Stamp)"); HeightStamp heightStamp = instance.AddComponent(); heightStamp.stamp = tex; heightStamp.mode = HeightStamp.CombineMode.Add; heightStamp.falloff.filterType = (FalloffFilter.FilterType)browser.filterTypeDefault; heightStamp.falloff.falloffRange = new Vector2(0.8f, 1f); // overrides bool autoScaleTerrain = wasShiftPressed; if (autoScaleTerrain) { Terrain[] terrains = MicroVerse.instance.GetComponentsInChildren(); Bounds worldBounds = TerrainUtil.ComputeTerrainBounds(terrains); // scale float x = worldBounds.size.x; float y = worldBounds.size.y; float z = worldBounds.size.z; // if y is dynamic and depends on the current bounds // however if it's very low or 0 in case it's the first terrain we use // a heuristic to calculate a resonable height. the values are just arbitrary float threshold = 10f; if (y < threshold) { if (Terrain.activeTerrain) { y = TerrainUtil.ComputeTerrainSize(Terrain.activeTerrain).y * 0.1f; } else { y = threshold; } } instance.transform.localScale = new Vector3(x, y, z); } else { instance.transform.localScale = browser.heightStampDefaultScale; } } } if (MicroVerse.instance != null) { instance.transform.SetParent(MicroVerse.instance.transform); } return instance; } } }