/// <summary> /// First creates a poisson grid based on the passed density. /// Positions are then filtered based on the passed object placement /// type taking into account height and angle constraints. /// </summary> /// <param name="m">Mesh to sample height and angle values from</param> /// <param name="type">object placement type to sample</param> /// <param name="density">How dense should the samples be</param> /// <returns>List of vectors within the grid and sample constraints</returns> public List <Vector3> GetFilteredGrid(TerrainTile tile, ObjectPlacementType type, float density) { MeshFilter mf = tile.GetComponent <MeshFilter>(); if (mf == null) { throw new ArgumentException("The passed TerrainTile does not have an attached MeshFilter. Has a mesh been created?"); } return(GetFilteredGrid(mf.sharedMesh, type)); }
/// <summary> /// Computes positions for this TerrainTile and caches /// them. /// </summary> public void ComputePositions() { Positions = new PositionsContainer[Pool.Placer.ObjectsToPlace.Count]; for (int i = 0; i < Positions.Length; i++) { ObjectPlacementType type = Pool.Placer.ObjectsToPlace[i]; Vector3[] locations = Pool.Placer.GetFilteredGrid(Tile, type, 1).ToArray(); Positions[i] = new PositionsContainer(locations, type); } }
/// <summary> /// Gets the container that holds the passed /// ObjectPlacementType /// </summary> /// <param name="type">type to search for</param> /// <returns>ObjectContainer, null if no matches were found</returns> ObjectContainer GetContainerForType(ObjectPlacementType type) { foreach (ObjectContainer c in Pool.Containers) { if (c.Type.Equals(type)) { return(c); } } return(null); }
/// <summary> /// Calculates a grid using the poisson disc sampling method. /// The 2D grid positions fall within the range of [0, 1]. /// /// Can be called off of Unity's main thread. /// </summary> /// <param name="opt">Object placement type to sample</param> /// <returns>List of vectors within the grid</returns> public List <Vector2> GetPoissonGrid(ObjectPlacementType opt) { PoissonDiscSampler pds = new PoissonDiscSampler(opt.GridSize, opt.GridSize, opt.Density); List <Vector2> total = new List <Vector2>(); foreach (Vector2 sample in pds.Samples()) { total.Add(sample); } return(total); }
/// <summary> /// First creates a poisson grid based on the passed density. /// Positions are then filtered based on the passed object placement /// type taking into account height and angle constraints. /// /// Unlike the <c>GetFilteredGrid(ObjectPlacementType, float)</c> method /// this method samples from the passed Mesh rather than pulling /// mesh information from TerraSettings. /// </summary> /// <param name="m">Mesh to sample height and angle values from</param> /// <param name="type">object placement type to sample</param> /// <returns>List of vectors within the grid and sample constraints</returns> public List <Vector3> GetFilteredGrid(Mesh m, ObjectPlacementType type) { MeshSampler sampler = new MeshSampler(m, Settings.MeshResolution); List <Vector2> grid = GetPoissonGrid(type.Spread / 10); List <Vector3> toAdd = new List <Vector3>(); foreach (Vector2 pos in grid) { MeshSampler.MeshSample sample = sampler.SampleAt(pos.x, pos.y); if (type.ShouldPlaceAt(sample.Height, sample.Angle)) { Vector3 newPos = new Vector3(pos.x, sample.Height, pos.y); toAdd.Add(newPos); } } return(toAdd); }
public PositionsContainer(Vector3[] positions, ObjectPlacementType type) { Positions = positions; Type = type; }
public ObjectContainer(ObjectPlacementType type) { Type = type; }
/// <summary> /// Displays GUI elements for the "Object Placement" tab /// </summary> public void ObjectPlacement() { //Display each type for (int i = 0; i < Settings.ObjectPlacementSettings.Count; i++) { EditorGUILayout.Space(); //Surround each material w/ box GUIStyle boxStyle = new GUIStyle(); boxStyle.padding = new RectOffset(3, 3, 3, 3); boxStyle.normal.background = GetWhiteTexture(); EditorGUILayout.BeginVertical(boxStyle); ObjectPlacementType type = Settings.ObjectPlacementSettings[i]; //Close button / name if (GUILayout.Button("X", GUILayout.Height(16), GUILayout.Width(18))) { Settings.ObjectPlacementSettings.RemoveAt(i); i--; continue; } //General type.Prefab = (GameObject)EditorGUILayout.ObjectField("Prefab", type.Prefab, typeof(GameObject), false); type.AllowsIntersection = EditorGUILayout.Toggle("Can Intersect", type.AllowsIntersection); type.PlacementProbability = EditorGUILayout.IntSlider("Place Probability", type.PlacementProbability, 0, 100); type.Spread = EditorGUILayout.Slider("Object Spread", type.Spread, 5f, 50f); type.MaxObjects = EditorGUILayout.IntField("Max Objects", type.MaxObjects); if (type.MaxObjects < 1) { type.MaxObjects = 1; } //Height type.ConstrainHeight = EditorGUILayout.Toggle("Constrain Height", type.ConstrainHeight); if (type.ConstrainHeight) { EditorGUI.indentLevel = 1; type.MinHeight = EditorGUILayout.FloatField("Min Height", type.MinHeight); type.MaxHeight = EditorGUILayout.FloatField("Max Height", type.MaxHeight); FitMinMax(ref type.MinHeight, ref type.MaxHeight); EditorGUILayout.BeginHorizontal(); type.HeightProbCurve = EditorGUILayout.CurveField("Probability", type.HeightProbCurve, Color.green, new Rect(0, 0, 1, 1)); if (GUILayout.Button("?", GUILayout.Width(25))) { const string msg = "This is the height probability curve. The X axis represents the " + "min to max height and the Y axis represents the probability an " + "object will spawn. By default, the curve is set to a 100% probability " + "meaning all objects will spawn."; EditorUtility.DisplayDialog("Help - Height Probability", msg, "Close"); } EditorGUILayout.EndHorizontal(); EditorGUI.indentLevel = 0; } //Angle type.ConstrainAngle = EditorGUILayout.Toggle("Constrain Angle", type.ConstrainAngle); if (type.ConstrainAngle) { EditorGUI.indentLevel = 1; type.MinAngle = EditorGUILayout.FloatField("Min Angle", type.MinAngle); type.MaxAngle = EditorGUILayout.FloatField("Max Angle", type.MaxAngle); FitMinMax(ref type.MinAngle, ref type.MaxAngle); EditorGUILayout.BeginHorizontal(); type.AngleProbCurve = EditorGUILayout.CurveField("Probability", type.AngleProbCurve, Color.green, new Rect(0, 0, 180, 1)); if (GUILayout.Button("?", GUILayout.Width(25))) { const string msg = "This is the angle probability curve. The X axis represents " + "0 to 180 degrees and the Y axis represents the probability an " + "object will spawn. By default, the curve is set to a 100% probability " + "meaning all objects will spawn."; EditorUtility.DisplayDialog("Help - Angle Probability", msg, "Close"); } EditorGUILayout.EndHorizontal(); EditorGUI.indentLevel = 0; } //Translate EditorGUI.indentLevel = 1; type.ShowTranslateFoldout = EditorGUILayout.Foldout(type.ShowTranslateFoldout, "Translate"); if (type.ShowTranslateFoldout) { type.TranslationAmount = EditorGUILayout.Vector3Field("Translate", type.TranslationAmount); EditorGUILayout.BeginHorizontal(); type.IsRandomTranslate = EditorGUILayout.Toggle("Random", type.IsRandomTranslate); if (GUILayout.Button("?", GUILayout.Width(25))) { const string msg = "Optionally randomly translate the placed object. " + "Max and min extents for the random number generator can " + "be set."; EditorUtility.DisplayDialog("Help - Random Translate", msg, "Close"); } EditorGUILayout.EndHorizontal(); if (type.IsRandomTranslate) { EditorGUI.indentLevel = 2; type.RandomTranslateExtents.Min = EditorGUILayout.Vector3Field("Min", type.RandomTranslateExtents.Min); type.RandomTranslateExtents.Max = EditorGUILayout.Vector3Field("Max", type.RandomTranslateExtents.Max); FitMinMax(ref type.RandomTranslateExtents.Min, ref type.RandomTranslateExtents.Max); EditorGUI.indentLevel = 1; } } //Rotate type.ShowRotateFoldout = EditorGUILayout.Foldout(type.ShowRotateFoldout, "Rotate"); if (type.ShowRotateFoldout) { type.RotationAmount = EditorGUILayout.Vector3Field("Rotation", type.RotationAmount); EditorGUILayout.BeginHorizontal(); type.IsRandomRotation = EditorGUILayout.Toggle("Random", type.IsRandomRotation); if (GUILayout.Button("?", GUILayout.Width(25))) { const string msg = "Optionally randomly rotate the placed object. " + "Max and min extents for the random number generator can " + "be set."; EditorUtility.DisplayDialog("Help - Random Rotate", msg, "Close"); } EditorGUILayout.EndHorizontal(); if (type.IsRandomRotation) { EditorGUI.indentLevel = 2; type.RandomRotationExtents.Min = EditorGUILayout.Vector3Field("Min", type.RandomRotationExtents.Min); type.RandomRotationExtents.Max = EditorGUILayout.Vector3Field("Max", type.RandomRotationExtents.Max); FitMinMax(ref type.RandomRotationExtents.Min, ref type.RandomRotationExtents.Max); EditorGUI.indentLevel = 1; } } //Scale type.ShowScaleFoldout = EditorGUILayout.Foldout(type.ShowScaleFoldout, "Scale"); if (type.ShowScaleFoldout) { type.ScaleAmount = EditorGUILayout.Vector3Field("Scale", type.ScaleAmount); EditorGUILayout.BeginHorizontal(); type.IsRandomScale = EditorGUILayout.Toggle("Random", type.IsRandomScale); if (GUILayout.Button("?", GUILayout.Width(25))) { const string msg = "Optionally randomly scale the placed object. " + "Max and min extents for the random number generator can " + "be set."; EditorUtility.DisplayDialog("Help - Random Scale", msg, "Close"); } EditorGUILayout.EndHorizontal(); if (type.IsRandomScale) { type.IsUniformScale = EditorGUILayout.Toggle("Scale Uniformly", type.IsUniformScale); EditorGUI.indentLevel = 2; if (type.IsUniformScale) { type.UniformScaleMin = EditorGUILayout.FloatField("Min", type.UniformScaleMin); type.UniformScaleMax = EditorGUILayout.FloatField("Max", type.UniformScaleMax); } else { type.RandomScaleExtents.Min = EditorGUILayout.Vector3Field("Min", type.RandomScaleExtents.Min); type.RandomScaleExtents.Max = EditorGUILayout.Vector3Field("Max", type.RandomScaleExtents.Max); FitMinMax(ref type.RandomScaleExtents.Min, ref type.RandomScaleExtents.Max); } EditorGUI.indentLevel = 1; } } EditorGUI.indentLevel = 0; EditorGUILayout.EndVertical(); } //Add new button EditorGUILayout.Space(); if (GUILayout.Button("Add Object")) { if (Settings.ObjectPlacementSettings == null) { Settings.ObjectPlacementSettings = new List <ObjectPlacementType>(); } Settings.ObjectPlacementSettings.Add(new ObjectPlacementType(TerraSettings.GenerationSeed)); } //Update preview if (Settings.DisplayPreview && GUILayout.Button("Update Preview")) { Settings.Preview.TriggerObjectPlacementUpdate(); } }
public ObjectContainer(ObjectPlacementType type, Transform parent) { Type = type; Parent = parent; }