private void CreateBiomeMaskArea()
        {
            // create new gameobject
            GameObject go = new GameObject("Biome Mask Area");

            // add this component
            go.AddComponent <BiomeMaskAreaExtension>();

            // modify created biome
            BiomeMaskArea mask = go.GetComponent <BiomeMaskArea>();

            mask.BiomeType = extension.biomeSettings.biomeType;

            // TODO: raycast terrain
            // position it to the center of the viewport
            SceneView.lastActiveSceneView.MoveToView(go.transform); // TODO: center of screen, recreate nodes

            // ensure gameobject gets reparented if this was a context click (otherwise does nothing)
            GameObjectUtility.SetParentAndAlign(go, extension.transform.gameObject);
            // reparent gameobject
            //go.transform.SetParent(extension.transform.gameObject.transform);

            // register the creation in the undo system
            Undo.RegisterCreatedObjectUndo(go, "Create " + go.name);
        }
        /// <summary>
        /// Insert a new node between every node segment of the area
        /// </summary>
        public static void Subdivide(BiomeMaskArea mask)
        {
            List <Node> originalNodes = new List <Node>();

            originalNodes.AddRange(mask.Nodes);

            for (var i = 0; i < originalNodes.Count; i++)
            {
                Node curr = originalNodes[i];
                Node next = mask.GetNextNode(curr);

                Vector3[] segment = new Vector3[] { curr.Position, next.Position };

                Vector3 meanVector = PolygonUtils.GetMeanVector(segment);

                int index = mask.GetNodeIndex(curr);

                Node newNode = new Node()
                {
                    Position = meanVector
                };

                mask.Nodes.Insert(index + 1, newNode);
            }

            UpdateMask(mask);
        }
Example #3
0
        /// <summary>
        /// Get a position and angle along the biome mask's edge.
        /// </summary>
        /// <param name="position"></param>
        /// <param name="angle"></param>
        private void GetRandomBiomeEdgePosition(out Vector3 position, out float angle)
        {
            BiomeMaskArea mask = editor.extension.lineSettings.biomeMaskArea;

            // parameter consistency check
            if (mask == null)
            {
                Debug.LogError("No mask defined");

                position = Vector3.zero;
                angle    = 0;
                return;
            }

            List <Vector3> positions = BiomeMaskUtils.GetPositions(mask);

            // sort clockwise, so that the pick algorithm works
            // if this were counterclockwise, then the angle would make the lines face inwards
            PolygonUtils.SortClockWise(positions);

            // get from node index
            int nodeIndexFrom = Random.Range(0, positions.Count); // note: int is exclusive last

            // get to node index, consider overlap
            int nodeIndexTo = nodeIndexFrom + 1;

            if (nodeIndexTo >= mask.Nodes.Count)
            {
                nodeIndexTo = 0;
            }

            // get nodes
            Vector3 positionFrom = mask.transform.position + positions[nodeIndexFrom];
            Vector3 positionTo   = mask.transform.position + positions[nodeIndexTo];

            // having the lines flip inwards into the biome is just a matter of changing the access order of the nodes
            // leaving this here, maybe we find a use case later
            bool flipAngle = editor.extension.lineSettings.attachedAngleFlip;

            if (flipAngle)
            {
                Vector3 tmp = positionFrom;
                positionFrom = positionTo;
                positionTo   = tmp;
            }

            float   distance  = (positionTo - positionFrom).magnitude;
            Vector3 direction = (positionTo - positionFrom).normalized;

            // the position along the edge. 0=from, 0.5=center, 1=to
            float relativePosition = Random.Range(0f, 1f);

            // calculate the position
            position = positionFrom + direction * distance * relativePosition;

            // calculate the angle 90 degrees to the from-to points and convert to degrees
            angle = Mathf.Atan2(positionTo.z - positionFrom.z, positionTo.x - positionFrom.x) * Mathf.Rad2Deg;
        }
        /// <summary>
        /// Force update on the mask in scene view
        /// </summary>
        /// <param name="mask"></param>
        public static void UpdateMask(BiomeMaskArea mask)
        {
            // TODO: that's just a quick hack to update the mask. mask.PositionNodes(); doesn't seem to work and mask.Update() has an optimization in it
            mask.transform.position = mask.transform.position + new Vector3(0, 1, 0);
            mask.transform.position = mask.transform.position + new Vector3(0, -1, 0);

            // apply the mask to the vegetation
            mask.UpdateBiomeMask();
        }
        public void OnInspectorGUI()
        {
            EditorGUILayout.Space();

            EditorGUILayout.LabelField("Terrain Processing", GUIStyles.GroupTitleStyle);

            EditorGUILayout.PropertyField(partitionAlgorithm, new GUIContent("Algorithm", "The algorithm to use for terrain partitioning."));

            EditorGUILayout.PropertyField(terrainProcessing, new GUIContent("Bounds", "Process all terrains as a single combined terrain or all terrains individually."));

            BoundsProcessing selectedTerrainProcessing = (BoundsProcessing)System.Enum.GetValues(typeof(BoundsProcessing)).GetValue(terrainProcessing.enumValueIndex);

            if (selectedTerrainProcessing == BoundsProcessing.Biome)
            {
                EditorGUI.BeginChangeCheck();

                EditorGUILayout.PropertyField(boundsBiomeMaskArea, new GUIContent("Biome Mask", "The Biome used for clipping."));

                // check if the changed biome mask is convex
                if (EditorGUI.EndChangeCheck() || editor.performInitialConsistencyCheck)
                {
                    if (boundsBiomeMaskArea.objectReferenceValue != null)
                    {
                        boundsBiomeMaskAreaValid.boolValue = false;

                        BiomeMaskArea biomeMaskArea = (BiomeMaskArea)boundsBiomeMaskArea.objectReferenceValue;

                        Vector2[] clipPolygon = editor.GetBiomeClipPolygon(biomeMaskArea);

                        if (clipPolygon != null)
                        {
                            // consistency check: clip polygon must be convex for sutherland hodgman
                            bool isConvex = PolygonUtils.PolygonIsConvex(clipPolygon);
                            if (isConvex)
                            {
                                boundsBiomeMaskAreaValid.boolValue = true;
                            }
                            else
                            {
                                Debug.LogError("Invalid clipping mask: " + biomeMaskArea.name + " (" + biomeMaskArea.MaskName + ")");
                            }
                        }
                    }
                }

                // show error in case the mask doesn't exist
                if (boundsBiomeMaskArea.objectReferenceValue == null)
                {
                    EditorGUILayout.HelpBox("The Biome Mask must be defined!", MessageType.Error);
                }
                // show error in case the mask isn't convex
                else if (!boundsBiomeMaskAreaValid.boolValue)
                {
                    EditorGUILayout.HelpBox("The Biome Mask must be convex!", MessageType.Error);
                }
            }
        }
        /// <summary>
        /// Clear the nodes of the mask and set the provided ones.
        /// </summary>
        /// <param name="mask"></param>
        /// <param name="nodes"></param>
        private void SetMaskNodes(BiomeMaskArea mask, List <Vector3> nodes)
        {
            mask.ClearNodes();

            foreach (Vector3 node in nodes)
            {
                mask.AddNodeToEnd(node);
            }

            mask.PositionNodes();
        }
        public static void CreateHexagon(BiomeMaskArea mask)
        {
            float radius = GetRadius(mask);

            Vector3[] hexagon = ShapeCreator.CreateHexagon(mask.transform.position, radius);

            mask.Nodes.Clear();
            mask.AddNodesToEnd(hexagon);

            // center main handle, implicitly updates the mask
            CenterMainHandle(mask);
        }
        public void CreateBiomeMaskArea(string gameObjectName, string maskName, Vector3 position, List <Vector3> nodes, float blendDistance)
        {
            GameObject parentGameObject = extension.transform.gameObject;

            // create new gameobject
            GameObject biomeGameObject = new GameObject(gameObjectName);

            // add this component
            biomeGameObject.AddComponent <BiomeMaskAreaExtension>();

            // ensure gameobject gets reparented if this was a context click (otherwise does nothing)
            GameObjectUtility.SetParentAndAlign(biomeGameObject, parentGameObject);

            // set position
            biomeGameObject.transform.position = position; // that's actually not necessary, we call CenterMainHandle after the mask nodes were created

            // modify created biome
            BiomeMaskArea mask = biomeGameObject.GetComponent <BiomeMaskArea>();

            mask.BiomeType = extension.biomeSettings.biomeType;

            // blend distance
            mask.BlendDistance = blendDistance;

            // create nodes
            mask.MaskName = maskName;

            // grow/shrink mask
            nodes = PolygonUtils.Resize(nodes, extension.shapeSettings.resizeFactor);

            // random shape inside bounds
            SetMaskNodes(mask, nodes);

            // put move handle into the center of the polygon
            BiomeMaskUtils.CenterMainHandle(mask);

            #region Lake Polygon
            // create a lake and re-parent the biome to it
            if (extension.lakeSettings.createLake)
            {
                lakeModule.CreateLake(mask, gameObjectName, nodes);
            }
            #endregion Lake Polygon

            // tegister the creation in the undo system
            Undo.RegisterCreatedObjectUndo(biomeGameObject, "Create " + biomeGameObject.name);
        }
        /// <summary>
        /// Resize the mask
        /// </summary>
        /// <param name="factor">Positive values for growing, negative for shrinking. 0.1 means grow by 10%, -0.1 means shrink by 10%</param>
        public static void ResizeMask(BiomeMaskArea mask, float factor)
        {
            Vector3 center = GetMaskCenter(mask);

            foreach (Node node in mask.Nodes)
            {
                Vector3 worldPos = mask.transform.TransformPoint(node.Position);

                Vector3 distance = worldPos - center;

                worldPos += distance * factor;

                node.Position = mask.transform.InverseTransformPoint(worldPos);
            }

            UpdateMask(mask);
        }
        /// <summary>
        /// Transform the mask and convert it into its convex hull.
        /// </summary>
        /// <param name="mask"></param>
        public static void ConvexHull(BiomeMaskArea mask)
        {
            Vector3 maskPosition = mask.transform.position;

            List <Vector2> positionsXY = mask.Nodes.ConvertAll <Vector2>(item => new Vector2(item.Position.x, item.Position.z));

            List <Vector2> convexHull = PolygonUtility.GetConvexHull(positionsXY);

            mask.Nodes.Clear();
            foreach (Vector2 nodePosition in convexHull)
            {
                mask.AddNodeToEnd(new Vector3(maskPosition.x + nodePosition.x, 0, maskPosition.z + nodePosition.y));
            }

            // center main handle, implicitly updates the mask
            CenterMainHandle(mask);
        }
        public static float GetRadius(BiomeMaskArea mask)
        {
            Vector3        maskCenter     = GetMaskCenter(mask);
            List <Vector3> worldPositions = mask.GetWorldSpaceNodePositions();

            // calculate the radius by using the average distance from the mask center
            float magnitudeSum = 0f;

            foreach (Vector3 worldPosition in worldPositions)
            {
                magnitudeSum += (worldPosition - maskCenter).magnitude;
            }

            float radius = magnitudeSum / mask.Nodes.Count;

            return(radius);
        }
        /// <summary>
        /// If bounds clip setting is set to biome, get the clip polygon from it
        /// </summary>
        /// <returns></returns>
        public Vector2[] GetBiomeClipPolygon(BiomeMaskArea biomeMaskArea)
        {
            if (biomeMaskArea == null)
            {
                return(null);
            }

            float biomePositionX = biomeMaskArea.transform.position.x;
            float biomePositionZ = biomeMaskArea.transform.position.z;

            Vector2[] clipPolygon = biomeMaskArea.Nodes.ConvertAll <Vector2>(
                item => new Vector2(
                    biomePositionX + item.Position.x,
                    biomePositionZ + item.Position.z
                    )).ToArray();

            return(clipPolygon);
        }
        /// <summary>
        /// Move the main handle into the center of the maks polygon
        /// </summary>
        /// <param name="mask"></param>
        public static void CenterMainHandle(BiomeMaskArea mask)
        {
            Vector3 center = GetMaskCenter(mask);
            Vector3 offset = mask.transform.position - center;

            mask.transform.position = center;

            foreach (AwesomeTechnologies.VegetationSystem.Biomes.Node node in mask.Nodes)
            {
                Vector3 worldPos = mask.transform.TransformPoint(node.Position);

                worldPos += offset;

                node.Position = mask.transform.InverseTransformPoint(worldPos);
            }

            UpdateMask(mask);
        }
        /// <summary>
        /// Remove every 2nd node
        /// </summary>
        public static void Unsubdivide(BiomeMaskArea mask)
        {
            // ensure there is at least the specified number of nodes left
            int minimumNodeCount = 3;

            if (mask.Nodes.Count <= minimumNodeCount)
            {
                return;
            }

            int count = mask.Nodes.Count;

            for (var i = mask.Nodes.Count - 1; i >= 0; i -= 2)
            {
                mask.Nodes.RemoveAt(i);

                if (mask.Nodes.Count < minimumNodeCount)
                {
                    break;
                }
            }

            UpdateMask(mask);
        }
Example #15
0
        /// <summary>
        /// Create a lake and re-parent the mask so that the lake becomes the child of the container and the mask the child of the lake.
        ///
        /// Note: the lake uses a bezier curve on the points, so the lake most likely won't be 100% in line with the mask.
        /// </summary>
        /// <param name="maskGameObject"></param>
        /// <param name="gameObjectName"></param>
        /// <param name="nodes"></param>
        public static LakePolygon CreateLakePolygon(LakeSettings lakeSettings, BiomeMaskArea mask, GameObject maskGameObject, string gameObjectName, List <Vector3> nodes)
        {
            GameObject maskParentGameObject = maskGameObject.transform.parent.gameObject;
            Vector3    maskPosition         = maskGameObject.transform.position;

            // lake
            LakePolygon lakePolygon = LakePolygon.CreatePolygon(AssetDatabase.GetBuiltinExtraResource <Material>("Default-Diffuse.mat"));

            // apply profile
            lakePolygon.currentProfile = lakeSettings.lakeProfile;

            // apply gameobject data
            lakePolygon.transform.localPosition = Vector3.zero;
            lakePolygon.name = "Lake " + gameObjectName;

            // add biome nodes
            if (lakeSettings.ramInternalLakeCreation)
            {
                // apply settings
                lakePolygon.angleSimulation            = lakeSettings.angleSimulation;
                lakePolygon.closeDistanceSimulation    = lakeSettings.closeDistanceSimulation;
                lakePolygon.checkDistanceSimulation    = lakeSettings.checkDistanceSimulation;
                lakePolygon.removeFirstPointSimulation = lakeSettings.removeFirstPointSimulation;

                // add point
                lakePolygon.AddPoint(maskPosition);

                // start simulation
                lakePolygon.Simulation();
            }
            // use mask shape
            else
            {
                foreach (Vector3 node in nodes)
                {
                    lakePolygon.AddPoint(maskParentGameObject.transform.InverseTransformPoint(node));
                }
            }


            // generate the lake
            lakePolygon.GeneratePolygon();

            // re-parent the mask to the lake and the lake to the parent
            GameObjectUtility.SetParentAndAlign(lakePolygon.gameObject, maskParentGameObject);
            GameObjectUtility.SetParentAndAlign(maskGameObject, lakePolygon.gameObject);

            // adjust the lake position
            lakePolygon.transform.position = maskPosition;

            // reset the mask position, it's now a child of the lake
            if (lakeSettings.ramInternalLakeCreation)
            {
                maskGameObject.transform.position = Vector3.zero;

                List <Vector3> newPoints = new List <Vector3>();

                foreach (Vector3 node in lakePolygon.points)
                {
                    newPoints.Add(lakePolygon.transform.TransformPoint(node));
                }

                // set the lake's polygon as new mask nodes
                SetMaskNodes(mask, newPoints);

                // put move handle into the center of the polygon
                BiomeMaskUtils.CenterMainHandle(mask);
            }

            // re-apply the material so that the water becomes immediately visible
            MeshRenderer meshRenderer = lakePolygon.GetComponent <MeshRenderer>();

            meshRenderer.sharedMaterial = lakePolygon.currentProfile.lakeMaterial;

            /* carving is disabled for now
             * if ( extension.lakeSettings.carveTerrain)
             * {
             *  Debug.Log("Start carve");
             *  lakePolygon.terrainSmoothMultiplier = 2f;
             *  lakePolygon.distSmooth = 10f;
             *  lakePolygon.TerrainCarve();
             * }
             */

            // EditorUtility.SetDirty(lakePolygon);

            return(lakePolygon);
        }
 public void Awake()
 {
     extension = (BiomeMaskAreaExtension)target;
     mask      = extension.GetComponent <BiomeMaskArea>();
 }
 /// <summary>
 /// Shrink the mask by the given factor
 /// </summary>
 /// <param name="mask"></param>
 /// <param name="factor"></param>
 public static void Shrink(BiomeMaskArea mask, float factor)
 {
     ResizeMask(mask, -Mathf.Abs(factor));
 }
 /// <summary>
 /// Grow the mask by the given factor
 /// </summary>
 /// <param name="mask"></param>
 /// <param name="factor"></param>
 public static void Grow(BiomeMaskArea mask, float factor)
 {
     ResizeMask(mask, Mathf.Abs(factor));
 }
        /// <summary>
        /// Get the center of the mask polygon
        /// </summary>
        /// <param name="mask"></param>
        /// <returns></returns>
        public static Vector3 GetMaskCenter(BiomeMaskArea mask)
        {
            List <Vector3> worldPositions = mask.GetWorldSpaceNodePositions();

            return(PolygonUtils.GetMeanVector(worldPositions.ToArray()));
        }
 /// <summary>
 /// Create a list of node positions.
 /// </summary>
 /// <param name="mask"></param>
 /// <returns></returns>
 public static List <Vector3> GetPositions(BiomeMaskArea mask)
 {
     return(mask.Nodes.ConvertAll <Vector3>(item => new Vector3(item.Position.x, item.Position.y, item.Position.z)));
 }
Example #21
0
        public void CreateLake(BiomeMaskArea mask, string gameObjectName, List <Vector3> nodes)
        {
#if RAM_2019
            LakePolygon lakePolygon = RamLakeCreator.CreateLakePolygon(editor.extension.lakeSettings, mask, mask.transform.gameObject, gameObjectName, nodes);
#endif
        }