Ejemplo n.º 1
0
        private void DeleteMember()
        {
            if (lbGroupMember != null && lbGroupDesignerItem != null)
            {
                int groupMemberPos = lbGroup.groupMemberList.FindIndex(gmbr => gmbr == lbGroupMember);
                if (LBEditorHelper.PromptForDelete("Group Member", "", groupMemberPos, false))
                {
                    lbGroup.groupMemberList.Remove(lbGroupMember);
                    lbGroupMember = null;

                    if (lbGroupDesignerItem.lbGroupDesigner)
                    {
                        lbGroupDesignerItem.lbGroupDesigner.RefreshWorkspace();
                    }
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Find a random offset (position) within an area with the given radius for a GroupMember.
        /// USAGE: Vector2 offset = LBGroupMember.GetRandomOffset(lbGroupMember, basePlaneWidth / 2f, 100);
        /// </summary>
        /// <param name="lbGroupMember"></param>
        /// <param name="radius"></param>
        /// <param name="maxAttempts"></param>
        /// <returns></returns>
        public static Vector2 GetRandomOffset(LBGroupMember lbGroupMember, float radius, int maxAttempts)
        {
            Vector2 offset      = Vector2.zero;
            Vector2 noiseCoords = Vector2.zero;

            // Declare and initialise variables
            // To start with we have made zero attempts and not found a legal placement
            // (by this we mean a placement within the circle, and following some extra rules like noise)
            bool foundLegalPlacement = false;
            int  attempts            = 0;

            // Repeat until we find a legal placement or we try maxAttempts times
            while (!foundLegalPlacement && attempts < maxAttempts)
            {
                // Generate a random position - the chance of this placement not being legal
                // is (4 - PI) / 4 = 21.5% (so chance of it being legal is 78.5%)
                // Thus it is 99% likely that within 3 attempts we will have found a correct placement
                offset.x = UnityEngine.Random.Range(-radius, radius);
                offset.y = UnityEngine.Random.Range(-radius, radius);
                // Judge legality: First, based on whether placement is within radius...
                foundLegalPlacement = offset.magnitude < radius;
                if (foundLegalPlacement && lbGroupMember != null && lbGroupMember.useNoise)
                {
                    // ... then by whether it follows noise rules
                    noiseCoords.x = offset.x + lbGroupMember.noiseOffset;
                    noiseCoords.y = offset.y + lbGroupMember.noiseOffset;
                    float noiseValue = Mathf.Abs(LBNoise.PerlinFractalNoise(noiseCoords.x / lbGroupMember.noiseTileSize, noiseCoords.y / lbGroupMember.noiseTileSize, 5) - 0.5f) * 4f;
                    // If the noise value is less than (1 - prefab cutoff value) placement is not legal
                    foundLegalPlacement = noiseValue >= 1f - lbGroupMember.noisePlacementCutoff;
                }

                attempts++;
            }

            // If we have tried maxAttempts times and still not found a correct placement, just set the position to the centre
            if (!foundLegalPlacement)
            {
                offset = Vector2.zero;
            }

            return(offset);
        }
        public LBObjPathParameters()
        {
            landscape            = null;
            lbGroupOwner         = null;
            lbGroupMemberOwner   = null;
            prefabItemType       = LBPrefabItem.PrefabItemType.ObjPathPrefab;
            showErrors           = false;
            showProgress         = false;
            showProgressDelegate = null;
            clearingRotationY    = 0f;

            lbGroupMember     = null;
            prefab            = null;
            pathPoint         = Vector3.zero;
            distanceAlongPath = 0f;
            parentTfm         = null;
            terrainDataArray  = new TerrainData[0];
            terrainRectsArray = new Rect[0];
            terrainHeight     = 1000f;
        }
Ejemplo n.º 4
0
        // Clone constructor
        public LBGroupMember(LBGroupMember lbGroupMember)
        {
            if (lbGroupMember == null)
            {
                SetClassDefaults();
            }
            else
            {
                isDisabled          = lbGroupMember.isDisabled;
                showInEditor        = lbGroupMember.showInEditor;
                showtabInEditor     = lbGroupMember.showtabInEditor;
                showObjPathDesigner = lbGroupMember.showObjPathDesigner;

                GUID = lbGroupMember.GUID;

                // Placement rule variables
                isGroupOverride = lbGroupMember.isGroupOverride;
                minScale        = lbGroupMember.minScale;
                maxScale        = lbGroupMember.maxScale;
                minHeight       = lbGroupMember.minHeight;
                maxHeight       = lbGroupMember.maxHeight;
                minInclination  = lbGroupMember.minInclination;
                maxInclination  = lbGroupMember.maxInclination;

                // Prefab varibles
                prefab                   = lbGroupMember.prefab;
                prefabName               = lbGroupMember.prefabName;
                showPrefabPreview        = lbGroupMember.showPrefabPreview;
                isKeepPrefabConnection   = lbGroupMember.isKeepPrefabConnection;
                isCombineMesh            = lbGroupMember.isCombineMesh;
                isRemoveEmptyGameObjects = lbGroupMember.isRemoveEmptyGameObjects;
                isRemoveAnimator         = lbGroupMember.isRemoveAnimator;
                isCreateCollider         = lbGroupMember.isCreateCollider;
                maxPrefabSqrKm           = lbGroupMember.maxPrefabSqrKm;
                maxPrefabPerGroup        = lbGroupMember.maxPrefabPerGroup;
                isPlacedInCentre         = lbGroupMember.isPlacedInCentre;

                showXYZSettings = lbGroupMember.showXYZSettings;

                // Prefab offset variables
                modelOffsetX     = lbGroupMember.modelOffsetX;
                modelOffsetY     = lbGroupMember.modelOffsetY;
                modelOffsetZ     = lbGroupMember.modelOffsetZ;
                minOffsetX       = lbGroupMember.minOffsetX;
                minOffsetZ       = lbGroupMember.minOffsetZ;
                minOffsetY       = lbGroupMember.minOffsetY;
                maxOffsetY       = lbGroupMember.maxOffsetY;
                randomiseOffsetY = lbGroupMember.randomiseOffsetY;

                // Prefab rotation variables
                rotationType = lbGroupMember.rotationType;
                // This was prior to LB 2.1.0 Beta 4w a workaround for a, now unknown, issue with templates.
                // Not sure what the original problem was and why we needed to default randomiseRotationY to true.
                // Now that we do a deep copy (ConvertAll) in LBGroup(LBGroup lbGroup) it no longer works.
                //randomiseRotationY = true;
                randomiseRotationY  = lbGroupMember.randomiseRotationY;
                startRotationY      = lbGroupMember.startRotationY;
                endRotationY        = lbGroupMember.endRotationY;
                randomiseRotationXZ = lbGroupMember.randomiseRotationXZ;
                rotationX           = lbGroupMember.rotationX;
                rotationZ           = lbGroupMember.rotationZ;
                endRotationX        = lbGroupMember.endRotationX;
                endRotationZ        = lbGroupMember.endRotationZ;
                isLockTilt          = lbGroupMember.isLockTilt;

                // Noise variables
                useNoise             = lbGroupMember.useNoise;
                noiseOffset          = lbGroupMember.noiseOffset;
                noiseTileSize        = lbGroupMember.noiseTileSize;
                noisePlacementCutoff = lbGroupMember.noisePlacementCutoff;

                // Proximity variables
                proximityExtent            = lbGroupMember.proximityExtent;
                isIgnoreProximityOfOthers  = lbGroupMember.isIgnoreProximityOfOthers;
                isProximityIgnoredByOthers = lbGroupMember.isProximityIgnoredByOthers;
                removeGrassBlendDist       = lbGroupMember.removeGrassBlendDist;
                minGrassProximity          = lbGroupMember.minGrassProximity;
                isRemoveTree     = lbGroupMember.isRemoveTree;
                minTreeProximity = lbGroupMember.minTreeProximity;

                // Terrain Alignment
                isTerrainAligned = lbGroupMember.isTerrainAligned;

                // Prefab flatten terrain variables
                isTerrainFlattened  = lbGroupMember.isTerrainFlattened;
                flattenDistance     = lbGroupMember.flattenDistance;
                flattenHeightOffset = lbGroupMember.flattenHeightOffset;
                flattenBlendRate    = lbGroupMember.flattenBlendRate;

                // Zones
                if (lbGroupMember.zoneGUIDList == null)
                {
                    zoneGUIDList = new List <string>();
                }
                else
                {
                    zoneGUIDList = new List <string>(lbGroupMember.zoneGUIDList);
                }

                // Zone edge fill
                isZoneEdgeFillTop    = lbGroupMember.isZoneEdgeFillTop;
                isZoneEdgeFillBottom = lbGroupMember.isZoneEdgeFillBottom;
                isZoneEdgeFillLeft   = lbGroupMember.isZoneEdgeFillLeft;
                isZoneEdgeFillRight  = lbGroupMember.isZoneEdgeFillRight;
                zoneEdgeFillDistance = lbGroupMember.zoneEdgeFillDistance;

                // Object Paths
                lbMemberType = lbGroupMember.lbMemberType;

                if (lbGroupMember.lbObjPath == null)
                {
                    lbObjPath = null;
                }
                else
                {
                    lbObjPath = new LBObjPath(lbGroupMember.lbObjPath);
                }

                isPathOnly          = lbGroupMember.isPathOnly;
                lbObjectOrientation = lbGroupMember.lbObjectOrientation;
                usePathHeight       = lbGroupMember.usePathHeight;
                usePathSlope        = lbGroupMember.usePathSlope;
                useTerrainTrend     = lbGroupMember.useTerrainTrend;
            }
        }
Ejemplo n.º 5
0
        private void OnSceneGUI()
        {
            if (EditorApplication.isPlayingOrWillChangePlaymode)
            {
                return;
            }
            // Don't process scene requests for Group Designer items when the ObjPathDesigner is enabled
            else if (lbGroupDesignerItem.lbGroupDesigner != null && lbGroupDesignerItem.lbGroupDesigner.isObjDesignerEnabled)
            {
                lbGroupDesignerItem.transform.position   = prevPosition;
                lbGroupDesignerItem.transform.rotation   = lbGroupDesignerItem.rotation;
                lbGroupDesignerItem.transform.localScale = lbGroupDesignerItem.scale;
                Selection.activeGameObject = null;
                return;
            }

            Event current = Event.current;

            //Debug.Log("DesignerItemEditor " + lbGroupDesignerItem.name);

            if (current != null)
            {
                // Get the group member. If we can't, and this isn't a SubGroup item, get out.
                if (lbGroupMember == null)
                {
                    lbGroupMember = lbGroupDesignerItem.lbGroupMember; if (lbGroupMember == null && !lbGroupDesignerItem.isSubGroup)
                    {
                        return;
                    }
                }

                bool isInSubGroup = !string.IsNullOrEmpty(lbGroupDesignerItem.SubGroupGUID);

                if (lbGroupDesignerItem.isObjPathMember || isInSubGroup)
                {
                    // If user attempts to move/rotate or scale an ObjPath member, SubGroup member, or a SubGroup, reset it
                    lbGroupDesignerItem.transform.position   = prevPosition;
                    lbGroupDesignerItem.transform.rotation   = lbGroupDesignerItem.rotation;
                    lbGroupDesignerItem.transform.localScale = lbGroupDesignerItem.scale;
                }
                else
                {
                    #region Check if Scale is overridden
                    // If Override is off, scaling is at the group level, not the member level, so don't allow scaling.
                    if (!lbGroupMember.isGroupOverride && Tools.current == Tool.Scale)
                    {
                        Tools.current = Tool.None;
                    }
                    #if UNITY_2017_3_OR_NEWER
                    else if (Tools.current == Tool.Scale || Tools.current == Tool.Transform)
                    #else
                    else if (Tools.current == Tool.Scale)
                    #endif
                    {
                        // If using Transform tool in U2017.3+, may need to reset scaling back to pre-scaled value.
                        if (!lbGroupMember.isGroupOverride)
                        {
                            lbGroupDesignerItem.transform.localScale = lbGroupDesignerItem.scale;
                        }
                        else
                        {
                            // Equally scale all axis
                            float   maxScale   = 0f;
                            Vector3 localScale = lbGroupDesignerItem.transform.localScale;

                            // Get the max scale amount of any of the axis
                            if (Mathf.Abs(localScale.x) > maxScale)
                            {
                                maxScale = localScale.x;
                            }
                            if (Mathf.Abs(localScale.y) > maxScale)
                            {
                                maxScale = localScale.y;
                            }
                            if (Mathf.Abs(localScale.z) > maxScale)
                            {
                                maxScale = localScale.z;
                            }

                            // Make each axis the same
                            localScale = maxScale * Vector3.one;

                            // Clamp scaling to between 0.1 and 10
                            if (localScale.x < 0.1f)
                            {
                                localScale = Vector3.one * 0.1f;
                            }
                            if (localScale.x > 10f)
                            {
                                localScale = Vector3.one * 10f;
                            }
                            lbGroupDesignerItem.transform.localScale = localScale;
                        }
                    }
                    #endregion

                    #region Lock Y rotation if randomise is enabled
                    #if UNITY_2017_3_OR_NEWER
                    if ((Tools.current == Tool.Rotate || Tools.current == Tool.Transform) && lbGroupMember.randomiseRotationY)
                    #else
                    if (Tools.current == Tool.Rotate && lbGroupMember.randomiseRotationY)
                    #endif
                    {
                        // Pivotmode of center can cause issues with some prefabs that aren't centred correctly.
                        // Prevent x,z movement of prefab when only y rotation change is attempted
                        if (Tools.pivotMode == PivotMode.Center)
                        {
                            Tools.pivotMode = PivotMode.Pivot;
                        }

                        lbGroupDesignerItem.transform.eulerAngles = new Vector3(lbGroupDesignerItem.transform.eulerAngles.x, lbGroupDesignerItem.rotation.eulerAngles.y, lbGroupDesignerItem.transform.eulerAngles.z);
                    }
                    #endregion

                    #region Clamp MinOffsetX,Z
                    #if UNITY_2017_3_OR_NEWER
                    if (Tools.current == Tool.Move || Tools.current == Tool.Transform)
                    #else
                    if (Tools.current == Tool.Move)
                    #endif
                    {
                        if (lbGroupMember.isPlacedInCentre)
                        {
                            if (lbGroup != null && lbGroupDesignerItem.lbGroupDesigner != null)
                            {
                                // If the user attempts to move it outside the clearing radius, lock the position to the last known position
                                Vector3 newPos           = lbGroupDesignerItem.transform.position;
                                float   distanceToCentre = Vector2.Distance(lbGroupDesignerItem.lbGroupDesigner.grpBasePlaneCentre2D, new Vector2(newPos.x, newPos.z));
                                if (distanceToCentre > lbGroup.maxClearingRadius)
                                {
                                    lbGroupDesignerItem.transform.position = prevPosition;
                                }
                            }
                        }
                        else if (!lbGroupMember.randomiseOffsetY)
                        {
                            // Don't allow movement on x,z axis for items that aren't offset from the Centre of the clearing AND don't use randomiseOffsetY.
                            lbGroupDesignerItem.transform.position = new Vector3(lbGroupDesignerItem.position.x, lbGroupDesignerItem.transform.position.y, lbGroupDesignerItem.position.z);
                        }
                        else
                        {
                            // Don't allow movement on any axis for items that aren't offset from the Centre of the clearing.
                            lbGroupDesignerItem.transform.position = lbGroupDesignerItem.position;
                        }

                        // Update last known position
                        prevPosition = lbGroupDesignerItem.transform.position;
                    }
                    #endregion
                }
                bool isLeftButton  = (current.button == 0);
                bool isRightButton = (current.button == 1);

                // ISSUE (ignore if vertex snapping is not enabled [v key held down]) current.keyCode != KeyCode.V

                // Record the starting positions
                if (!lbGroupDesignerItem.isObjPathMember && current.type == EventType.MouseDown && isLeftButton)
                {
                    Tools.hidden = false;
                    //Debug.Log("Left Btn Down");
                    lbGroupDesignerItem.position = lbGroupDesignerItem.transform.position;
                    lbGroupDesignerItem.rotation = lbGroupDesignerItem.transform.rotation;
                    lbGroupDesignerItem.scale    = lbGroupDesignerItem.transform.localScale;
                }
                else if (!lbGroupDesignerItem.isObjPathMember && !isInSubGroup && current.type == EventType.MouseUp && lbGroupMember != null && isLeftButton)
                {
                    #region Move
#if UNITY_2017_3_OR_NEWER
                    if (Tools.current == Tool.Move || Tools.current == Tool.Transform)
#else
                    if (Tools.current == Tool.Move)
#endif
                    {
                        if (lbGroupMember.isPlacedInCentre)
                        {
                            lbGroupMember.minOffsetX = lbGroupDesignerItem.transform.position.x;
                            lbGroupMember.minOffsetZ = lbGroupDesignerItem.transform.position.z;
                            if (!lbGroupMember.randomiseOffsetY)
                            {
                                lbGroupMember.minOffsetY = lbGroupDesignerItem.transform.position.y - lbGroupDesignerItem.lbGroupDesigner.BasePlaneOffsetY;
                            }
                        }
                        else if (!lbGroupMember.randomiseOffsetY)
                        {
                            lbGroupMember.minOffsetY = lbGroupDesignerItem.transform.position.y - lbGroupDesignerItem.lbGroupDesigner.BasePlaneOffsetY;
                            lbGroupMember.maxOffsetY = lbGroupMember.minOffsetY;

                            // Update all the instances of this member in the Designer
                            if (lbGroupDesignerItem.lbGroupDesigner != null)
                            {
                                lbGroupDesignerItem.lbGroupDesigner.UpdateGroupMember(lbGroupMember);
                            }
                        }
                    }
                    #endregion

                    #region Rotate
#if UNITY_2017_3_OR_NEWER
                    if (Tools.current == Tool.Rotate || Tools.current == Tool.Transform)
#else
                    if (Tools.current == Tool.Rotate)
#endif
                    {
                        Vector3 newRotation = lbGroupDesignerItem.transform.rotation.eulerAngles;

                        lbGroupMember.rotationX = newRotation.x;
                        lbGroupMember.rotationZ = newRotation.z;

                        if (!lbGroupMember.randomiseRotationY)
                        {
                            lbGroupMember.startRotationY = newRotation.y;
                            lbGroupMember.endRotationY   = newRotation.y;

                            if (!lbGroupMember.isPlacedInCentre && lbGroupDesignerItem.lbGroupDesigner != null)
                            {
                                // Update all the instances of this member in the Designer
                                lbGroupDesignerItem.lbGroupDesigner.UpdateGroupMember(lbGroupMember);
                            }
                        }
                    }
                    #endregion

                    #region Scale
#if UNITY_2017_3_OR_NEWER
                    if ((Tools.current == Tool.Scale || Tools.current == Tool.Transform) && lbGroupMember.isGroupOverride)
#else
                    if (Tools.current == Tool.Scale && lbGroupMember.isGroupOverride)
#endif
                    {
                        //Debug.Log("Scale start:" + lbGroupDesignerItem.scale + " mouseup:" +  lbGroupDesignerItem.transform.localScale);
                        lbGroupMember.minScale = lbGroupDesignerItem.transform.localScale.x;
                        lbGroupMember.maxScale = lbGroupMember.minScale;

                        if (!lbGroupMember.isPlacedInCentre && lbGroupDesignerItem.lbGroupDesigner != null)
                        {
                            // Update all the instances of this member in the Designer
                            lbGroupDesignerItem.lbGroupDesigner.UpdateGroupMember(lbGroupMember);
                        }
                    }
                    #endregion

                    // Update the LB Editor Windows
                    LBEditorHelper.RepaintEditorWindow(typeof(LandscapeBuilderWindow));

                    //Debug.Log("Prefab: " + this.name + " start pos:" + lbGroupDesignerItem.position + " end pos:" + lbGroupDesignerItem.transform.position);
                }

                //if (current.keyCode == KeyCode.V && current.type == EventType.KeyUp)
                //if (current.keyCode != KeyCode.V)
                //{
                //    //LBIntegration.ReflectionOutputFields(typeof(Tools), true, true);
                //    bool isVertexDragging = false;
                //    try
                //    {
                //        isVertexDragging = LBIntegration.ReflectionGetValue<bool>(typeof(Tools), "vertexDragging", null, true, true);
                //    }
                //    catch (System.Exception ex)
                //    {
                //        Debug.LogWarning("LBGroupDesignerItemEditor could not find VertexDragging - PLEASE REPORT " + ex.Message);
                //    }

                //    Debug.Log("Vertex Snapping enabled..." + Time.realtimeSinceStartup + " vertexDragging: " + isVertexDragging);
                //}


                #region Display the Context-sensitive menu
                else if (current.type == EventType.MouseDown && isRightButton)
                {
                    bool isCheckProximity = (lbGroupDesignerItem.lbGroupDesigner == null ? true : lbGroupDesignerItem.lbGroupDesigner.isCheckProximity);

                    GenericMenu menu = new GenericMenu();
                    menu.AddItem(new GUIContent("Close Designer"), false, CloseGroupDesigner);
                    menu.AddItem(new GUIContent("Refresh Designer"), false, () => RefreshDesigner(true));
                    menu.AddItem(new GUIContent("Check Proximity"), isCheckProximity, CheckProximity, !isCheckProximity);
                    menu.AddItem(new GUIContent("Auto Refresh"), lbGroupDesignerItem.lbGroupDesigner.GetAutoRefresh, () => { lbGroupDesignerItem.lbGroupDesigner.SetAutoRefresh(!lbGroupDesignerItem.lbGroupDesigner.GetAutoRefresh); });
                    // The following context menu items only apply to GroupMembers.
                    // Also exclude members in a subgroup within the Clearing Group
                    if (!lbGroupDesignerItem.isSubGroup && !isInSubGroup)
                    {
                        menu.AddSeparator("");
                        if (lbGroupDesignerItem.lbGroupDesigner.showZones)
                        {
                            menu.AddItem(new GUIContent("Add/"), false, () => { });
                            menu.AddItem(new GUIContent("Add/Zone under Object"), false, AddZoneToObject);
                        }
                        menu.AddItem(new GUIContent("Reset/"), false, () => { });
                        menu.AddItem(new GUIContent("Reset/Reset Rotation"), false, ResetRotation);
                        menu.AddItem(new GUIContent("Reset/Reset Position"), false, ResetPosition);
                        if (lbGroupMember.isGroupOverride)
                        {
                            menu.AddItem(new GUIContent("Reset/Reset Scale"), false, ResetScale);
                        }
                        menu.AddItem(new GUIContent("Snap/"), false, () => { });
                        menu.AddItem(new GUIContent("Snap/Pivot to Ground"), false, () => SnapToGround(false));
                        menu.AddItem(new GUIContent("Snap/Model to Ground"), false, () => SnapToGround(true));
                        if (!lbGroupDesignerItem.isObjPathMember)
                        {
                            menu.AddItem(new GUIContent("Place In Centre +offset"), lbGroupMember.isPlacedInCentre, TogglePlaceInCentre);
                        }
                        menu.AddItem(new GUIContent("Override Group"), lbGroupMember.isGroupOverride, ToggleOverrideGroupDefaults);
                        menu.AddItem(new GUIContent("Rotation/Face 2 Group Centre"), lbGroupMember.rotationType == LBGroupMember.LBRotationType.Face2GroupCentre, SetRotationType, LBGroupMember.LBRotationType.Face2GroupCentre);
                        menu.AddItem(new GUIContent("Rotation/Face 2 Zone Centre"), lbGroupMember.rotationType == LBGroupMember.LBRotationType.Face2ZoneCentre, SetRotationType, LBGroupMember.LBRotationType.Face2ZoneCentre);
                        menu.AddItem(new GUIContent("Rotation/Group Space"), lbGroupMember.rotationType == LBGroupMember.LBRotationType.GroupSpace, SetRotationType, "GroupSpace");
                        menu.AddItem(new GUIContent("Rotation/World Space"), lbGroupMember.rotationType == LBGroupMember.LBRotationType.WorldSpace, SetRotationType, "WorldSpace");
                        menu.AddItem(new GUIContent("Rotation/"), false, () => { });
                        menu.AddItem(new GUIContent("Rotation/Randomise Y"), lbGroupMember.randomiseRotationY, () =>
                        {
                            lbGroupMember.randomiseRotationY = !lbGroupMember.randomiseRotationY;
                            lbGroupMember.showtabInEditor    = (int)LBGroupMember.LBMemberEditorTab.XYZ;
                            LBEditorHelper.RepaintEditorWindow(typeof(LandscapeBuilderWindow));
                            lbGroupDesignerItem.lbGroupDesigner.UpdateGroupMember(lbGroupMember);
                        });
                    }

                    menu.AddSeparator("");

                    if (lbGroupDesignerItem.isObjPathMember && !isInSubGroup)
                    {
                        menu.AddItem(new GUIContent("Show Object Path"), false, () =>
                        {
                            if (!string.IsNullOrEmpty(lbGroupDesignerItem.objPathGroupMemberGUID) && lbGroupDesignerItem.lbGroupDesigner.lbGroup != null)
                            {
                                LBGroupMember objPathGroupMember = lbGroupDesignerItem.lbGroupDesigner.lbGroup.GetMemberByGUID(lbGroupDesignerItem.objPathGroupMemberGUID, false);
                                if (objPathGroupMember != null)
                                {
                                    lbGroupDesignerItem.lbGroupDesigner.lbGroup.GroupMemberListExpand(false);
                                    lbGroupDesignerItem.lbGroupDesigner.lbGroup.showGroupMembersInEditor = true;
                                    objPathGroupMember.showInEditor = true;
                                    LBEditorHelper.RepaintLBW();
                                }
                                ;
                            }
                            ;
                        });
                    }

                    menu.AddItem(new GUIContent("Zoom Out"), false, () => { lbGroupDesignerItem.lbGroupDesigner.ZoomExtent(SceneView.lastActiveSceneView); });
                    menu.AddItem(new GUIContent("Zoom In"), false, () => { lbGroupDesignerItem.lbGroupDesigner.ZoomIn(SceneView.lastActiveSceneView); });
                    menu.AddItem(new GUIContent("Display/Group Extent"), lbGroupDesignerItem.lbGroupDesigner.showGroupExtent, () => { lbGroupDesignerItem.lbGroupDesigner.showGroupExtent = !lbGroupDesignerItem.lbGroupDesigner.showGroupExtent; });
                    menu.AddItem(new GUIContent("Display/SubGroup Extents"), lbGroupDesignerItem.lbGroupDesigner.showSubGroupExtent, () => { lbGroupDesignerItem.lbGroupDesigner.showSubGroupExtent = !lbGroupDesignerItem.lbGroupDesigner.showSubGroupExtent; });
                    menu.AddItem(new GUIContent("Display/Member Extent Proximity"), lbGroupDesignerItem.lbGroupDesigner.showProximity, () =>
                    {
                        lbGroupDesignerItem.lbGroupDesigner.showProximity = !lbGroupDesignerItem.lbGroupDesigner.showProximity;
                        if (lbGroupDesignerItem.lbGroupDesigner.showProximity)
                        {
                            lbGroupMember.showtabInEditor = (int)LBGroupMember.LBMemberEditorTab.Proximity;
                        }
                        LBEditorHelper.RepaintEditorWindow(typeof(LandscapeBuilderWindow));
                    });
                    menu.AddItem(new GUIContent("Display/Member Tree Proximity"), lbGroupDesignerItem.lbGroupDesigner.showTreeProximity, () =>
                    {
                        lbGroupDesignerItem.lbGroupDesigner.showTreeProximity = !lbGroupDesignerItem.lbGroupDesigner.showTreeProximity;
                        if (lbGroupDesignerItem.lbGroupDesigner.showTreeProximity)
                        {
                            lbGroupMember.showtabInEditor = (int)LBGroupMember.LBMemberEditorTab.Proximity;
                        }
                        LBEditorHelper.RepaintEditorWindow(typeof(LandscapeBuilderWindow));
                    });
                    menu.AddItem(new GUIContent("Display/Member Flatten Area"), lbGroupDesignerItem.lbGroupDesigner.showFlattenArea, () =>
                    {
                        lbGroupDesignerItem.lbGroupDesigner.showFlattenArea = !lbGroupDesignerItem.lbGroupDesigner.showFlattenArea;
                        if (lbGroupDesignerItem.lbGroupDesigner.showFlattenArea)
                        {
                            lbGroupMember.showtabInEditor = (int)LBGroupMember.LBMemberEditorTab.General;
                        }
                        LBEditorHelper.RepaintEditorWindow(typeof(LandscapeBuilderWindow));
                    });
                    menu.AddItem(new GUIContent("Display/Zones"), lbGroupDesignerItem.lbGroupDesigner.showZones, () =>
                    {
                        lbGroupDesignerItem.lbGroupDesigner.showZones = !lbGroupDesignerItem.lbGroupDesigner.showZones;
                        if (lbGroupDesignerItem.lbGroupDesigner.showZones)
                        {
                            lbGroupMember.showtabInEditor = (int)LBGroupMember.LBMemberEditorTab.Zone;
                        }
                        LBEditorHelper.RepaintEditorWindow(typeof(LandscapeBuilderWindow));
                    });
                    // Cannot directly delete items:
                    // 1. in an Object Path
                    // 2. in a subgroup within the current Group
                    // 3. a whole subgroup within the current Group
                    if (!lbGroupDesignerItem.isObjPathMember && !isInSubGroup && !lbGroupDesignerItem.isSubGroup)
                    {
                        menu.AddSeparator("");
                        menu.AddItem(new GUIContent("Delete"), false, DeleteMember);
                    }
                    menu.AddSeparator("");
                    menu.AddItem(new GUIContent("Unselect"), false, () => { Selection.activeObject = null; });
                    // The Cancel option is not really necessary as use can just click anywhere else. However, it may help some users.
                    menu.AddItem(new GUIContent("Cancel"), false, () => { });
                    menu.ShowAsContext();
                    current.Use();
                }
                #endregion
            }
        }
        /// <summary>
        /// Copy selected filtered roads to a Uniform Group.
        ///
        /// </summary>
        /// <param name="isUpdateExisting"></param>
        private void CopyRoads(bool isUpdateExisting)
        {
            if (lbGroupTarget != null)
            {
                LBRoad        lbRoadToCopy = null;
                LBGroupMember lbGroupMember = null;
                Vector2       leftSplinePt2D = Vector2.zero, rightSplinePt2D = Vector2.zero;
                bool          useWidth = false;
                bool          addNew   = false;

                if (filteredRoadList == null)
                {
                    filteredRoadList = new List <LBRoad>(numAllRoads + 20);
                }
                for (int i = 0; i < numFilteredRoads; i++)
                {
                    lbRoadToCopy = filteredRoadList[i];

                    //Debug.Log("[DEBUG] CopyRoads: " + lbRoadToCopy.roadName + " points: " + lbRoadToCopy.centreSpline.Length);

                    if (lbRoadToCopy.isSelected)
                    {
                        lbGroupMember = null;
                        addNew        = false;

                        // Validate that road has a matching left and right spline, else turn off useWidth.
                        int numPathPoints = lbRoadToCopy.centreSpline == null ? 0 : lbRoadToCopy.centreSpline.Length;
                        useWidth = lbRoadToCopy.leftSpline != null && lbRoadToCopy.rightSpline != null && lbRoadToCopy.centreSpline != null && numPathPoints == lbRoadToCopy.leftSpline.Length;

                        if (isUpdateExisting)
                        {
                            // Find a matching group member
                            lbGroupMember = lbGroupTarget.groupMemberList.Find(gm => gm.lbMemberType == LBGroupMember.LBMemberType.ObjPath && gm.lbObjPath != null && gm.lbObjPath.pathName == lbRoadToCopy.roadName);
                            if (lbGroupMember != null)
                            {
                                //Debug.Log("[DEBUG] found " + lbGroupMember.lbObjPath.pathName);

                                // Clear out the old points data
                                lbGroupMember.lbObjPath.showPathInScene = false;
                                lbGroupMember.lbObjPath.selectedList.Clear();
                                lbGroupMember.lbObjPath.positionList.Clear();
                                lbGroupMember.lbObjPath.pathPointList.Clear();
                                if (lbGroupMember.lbObjPath.widthList != null)
                                {
                                    lbGroupMember.lbObjPath.widthList.Clear();
                                }
                                else
                                {
                                    lbGroupMember.lbObjPath.widthList = new List <float>(numPathPoints);
                                }

                                // If useWidth was enabled but now shouldn't be, turn it off
                                if (lbGroupMember.lbObjPath.useWidth && !useWidth)
                                {
                                    // Disable use width
                                    lbGroupMember.lbObjPath.EnablePathWidth(false, true);
                                }
                            }
                            //else { Debug.Log("[DEBUG] " + lbRoadToCopy.roadName + " not found"); }
                        }

                        // Create a new Group Member if required
                        if (lbGroupMember == null)
                        {
                            lbGroupMember = new LBGroupMember();
                            lbGroupMember.lbMemberType = LBGroupMember.LBMemberType.ObjPath;
                            lbGroupMember.lbObjPath    = new LBObjPath();
                            if (lbGroupMember.lbObjPath != null)
                            {
                                lbGroupMember.lbObjPath.pathName = lbRoadToCopy.roadName;
                                lbGroupMember.lbObjPath.useWidth = useWidth;
                                lbGroupMember.lbObjPath.showDefaultSeriesInEditor = false;
                                addNew = true;
                            }
                        }

                        if (lbGroupMember != null)
                        {
                            if (lbGroupMember.lbObjPath != null)
                            {
                                lbGroupMember.showInEditor = false;

                                //Debug.Log("[DEBUG] CopyRoads: " + lbRoadToCopy.roadName + " points: " + numPathPoints);

                                lbGroupMember.lbObjPath.positionList.AddRange(lbRoadToCopy.centreSpline);
                                if (useWidth)
                                {
                                    lbGroupMember.lbObjPath.positionListLeftEdge.AddRange(lbRoadToCopy.leftSpline);
                                    lbGroupMember.lbObjPath.positionListRightEdge.AddRange(lbRoadToCopy.rightSpline);
                                }

                                for (int ptIdx = 0; ptIdx < numPathPoints; ptIdx++)
                                {
                                    lbGroupMember.lbObjPath.pathPointList.Add(new LBPathPoint());
                                    if (lbGroupMember.lbObjPath.useWidth)
                                    {
                                        // Calculate the 2D distance between the left and right splines. Currently Object Paths are flat
                                        // although they will support this in the future. Currently the Object Path rotation is only for objects on the path.
                                        leftSplinePt2D.x = lbRoadToCopy.leftSpline[ptIdx].x;
                                        leftSplinePt2D.y = lbRoadToCopy.leftSpline[ptIdx].z;

                                        rightSplinePt2D.x = lbRoadToCopy.rightSpline[ptIdx].x;
                                        rightSplinePt2D.y = lbRoadToCopy.rightSpline[ptIdx].z;

                                        lbGroupMember.lbObjPath.widthList.Add(Vector2.Distance(leftSplinePt2D, rightSplinePt2D));
                                    }
                                }

                                lbGroupMember.lbObjPath.showPathInScene = true;

                                if (addNew)
                                {
                                    lbGroupTarget.groupMemberList.Add(lbGroupMember);
                                }

                                //Debug.Log("[DEBUG] " + lbGroupTarget.groupName);
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Create a gameobject in the GroupDesigner that can be used to track location and size
        /// of SubGroups. Currently it assumes they are spawned from a Object Path in the parent
        /// Clearing Group. The LBPrefabItem component type may need to be changed for subgroups
        /// not created from an Object Path.
        /// </summary>
        /// <param name="parentTfrm"></param>
        /// <param name="parentGroup"></param>
        /// <param name="parentGroupMember"></param>
        /// <param name="subGroup"></param>
        /// <param name="subGroupPosition"></param>
        /// <param name="subGroupRotation"></param>
        /// <param name="subGroupRadius"></param>
        /// <param name="regionIdx"></param>
        /// <returns></returns>
        public static LBGroupDesignerItem CreateSubGroupItem(Transform parentTfrm, LBGroup parentGroup, LBGroupMember parentGroupMember, LBGroup subGroup, Vector3 subGroupPosition, Vector3 subGroupRotation, float subGroupRadius, int regionIdx)
        {
            LBGroupDesignerItem lbGroupDesignerItem = null;

            if (parentGroup != null && parentTfrm != null && subGroup != null)
            {
                // [DESIGNER] (parentGroupName.subgroup:subGroupName.regionnumber)
                GameObject subGroupGO = new GameObject("[DESIGNER] (" + (string.IsNullOrEmpty(parentGroup.groupName) ? "ParentGroup" : parentGroup.groupName) + ".subgroup:" + (string.IsNullOrEmpty(subGroup.groupName) ? "SubGroup" : subGroup.groupName) + "." + (regionIdx + 1) + ")");

                if (subGroupGO != null)
                {
                    Quaternion rotation = Quaternion.Euler(subGroupRotation);
                    subGroupGO.transform.SetPositionAndRotation(subGroupPosition, rotation);
                    subGroupGO.transform.SetParent(parentTfrm);
                    lbGroupDesignerItem = subGroupGO.AddComponent <LBGroupDesignerItem>();
                    if (lbGroupDesignerItem != null)
                    {
                        lbGroupDesignerItem.isSubGroup     = true;
                        lbGroupDesignerItem.SubGroupGUID   = subGroup == null ? string.Empty : subGroup.GUID;
                        lbGroupDesignerItem.position       = subGroupPosition;
                        lbGroupDesignerItem.rotation       = rotation;
                        lbGroupDesignerItem.subGroupRadius = subGroupRadius;
                        lbGroupDesignerItem.FindGroupDesigner();

                        if (parentGroupMember != null)
                        {
                            lbGroupDesignerItem.objPathGroupMemberGUID = parentGroupMember.GUID;
                            lbGroupDesignerItem.isObjPathMember        = parentGroupMember.lbMemberType == LBGroupMember.LBMemberType.ObjPath;
                        }
                        else
                        {
                            lbGroupDesignerItem.objPathGroupMemberGUID = string.Empty;
                            lbGroupDesignerItem.isObjPathMember        = false;
                        }
                    }

                    LBPrefabItem lbPrefabItem = subGroupGO.AddComponent <LBPrefabItem>();
                    if (lbPrefabItem != null)
                    {
                        lbPrefabItem.prefabItemType = LBPrefabItem.PrefabItemType.ObjPathDesignerPrefab;
                        // Add the GUID of the object path GroupMember so that we can track them in the scene
                        // It lets us enable/disable existing prefabs when using the Object Path Designer
                        // If this Group is being spawned from a parent group, assign the member GUID from that parent instead
                        // of using the current member GUID. This ensures that the Designers can disable and enable the correct gameobject
                        // for subgroups.
                        lbPrefabItem.groupMemberGUID = parentGroupMember != null ? parentGroupMember.GUID : subGroup.GUID;
                    }
                }
            }

            return(lbGroupDesignerItem);
        }
        public static bool SaveTemplate
        (
            LBLandscape landscape,
            string LBEditorVersion,
            string landscapeTemplateName,
            ref bool isSceneSaveRequired,
            bool createTemplatePackage,
            bool addMapTexturesToTemplatePackage,
            bool addLayerHeightmapTexturesToTemplatePackage,
            bool addLBLightingToTemplate,
            bool addPathsToTemplate,
            bool addStencilsToTemplate,
            bool addPathMeshMaterialsToTemplate
        )
        {
            bool isSuccessful = false;

            string landscapeName = landscape.name;

            // Create a new gameobject in the scene hierarchy that we can turn into a prefab
            GameObject newTemplateObj = new GameObject(landscapeName + " template");

            if (newTemplateObj == null)
            {
                Debug.LogWarning("Landscape Builder - could not create temporary gameobject for " + landscapeName + " template");
            }
            else
            {
                LBTemplate lbTemplate = newTemplateObj.AddComponent <LBTemplate>();
                if (lbTemplate == null)
                {
                    Debug.LogWarning("Landscape Builder - could not add LBTemplate component " + landscapeName + " template");
                    GameObject.DestroyImmediate(newTemplateObj);
                }
                else
                {
                    // This is the installed version of LB when the template was created
                    lbTemplate.LBVersion = LBEditorVersion;
                    // This is the version of the landscape when the template was created
                    lbTemplate.LastUpdatedVersion = landscape.LastUpdatedVersion;

                    // The version of Unity that created this template
                    lbTemplate.templateUnityVersion = Application.unityVersion;

                    lbTemplate.useLegacyNoiseOffset = landscape.useLegacyNoiseOffset;

                    // Name of the landscape game object
                    lbTemplate.landscapeName = landscapeName;
                    lbTemplate.size          = landscape.size;
                    lbTemplate.start         = landscape.start;

                    // Landscape terrain settings
                    Vector3 terrainSize3D = landscape.GetLandscapeTerrainSize();
                    lbTemplate.heightmapResolution       = landscape.GetLandscapeTerrainHeightmapResolution();
                    lbTemplate.terrainWidth              = terrainSize3D.x;
                    lbTemplate.terrainHeight             = terrainSize3D.y;
                    lbTemplate.pixelError                = landscape.GetLandscapeTerrainPixelError();
                    lbTemplate.baseMapDistance           = landscape.GetLandscapeTerrainBaseMapDist();
                    lbTemplate.alphaMapResolution        = landscape.GetLandscapeTerrainAlphaMapResolution();
                    lbTemplate.baseTextureResolution     = landscape.GetLandscapeTerrainBaseTextureResolution();
                    lbTemplate.treeDistance              = landscape.GetLandscapeTerrainTreeDistance();
                    lbTemplate.treeBillboardDistance     = landscape.GetLandscapeTerrainTreeBillboardStart();
                    lbTemplate.detailDistance            = landscape.GetLandscapeTerrainDetailDistance();
                    lbTemplate.detailDensity             = landscape.GetLandscapeTerrainDetailDensity();
                    lbTemplate.detailResolution          = landscape.GetLandscapeTerrainDetailResolution();
                    lbTemplate.treeFadeDistance          = landscape.GetLandscapeTerrainTreeFadeLength();
                    lbTemplate.grassWindSpeed            = landscape.GetLandscapeTerrainGrassWindSpeed();
                    lbTemplate.grassWindRippleSize       = landscape.GetLandscapeTerrainGrassWindRippleSize();
                    lbTemplate.grassWindBending          = landscape.GetLandscapeTerrainGrassWindBending();
                    lbTemplate.grassWindTint             = landscape.GetLandscapeTerrainGrassWindTint();
                    lbTemplate.terrainLegacySpecular     = landscape.GetLandscapeTerrainLegacySpecular();
                    lbTemplate.terrainLegacyShininess    = landscape.GetLandscapeTerrainLegacyShininess();
                    lbTemplate.useTerrainDrawInstanced   = landscape.GetLandscapeTerrainDrawInstanced();
                    lbTemplate.useTerrainPerPixelNormals = GetTerrainPerPixelNormals(landscape);
                    lbTemplate.terrainGroupingID         = landscape.GetLandscapeTerrainGroupingID();
                    lbTemplate.terrainAutoConnect        = landscape.GetLandscapeTerrainAutoConnect();

                    // Synchronize with a duplicate enum in lbTemplate
                    lbTemplate.terrainMaterialType = (LBTemplate.TerrainMaterialType)landscape.GetTerrainMaterialType();

                    // Check if any lists are null
                    if (landscape.topographyLayersList == null)
                    {
                        landscape.topographyLayersList = new List <LBLayer>();
                    }
                    if (landscape.terrainTexturesList == null)
                    {
                        landscape.terrainTexturesList = new List <LBTerrainTexture>();
                    }
                    if (landscape.terrainTreesList == null)
                    {
                        landscape.terrainTreesList = new List <LBTerrainTree>();
                    }
                    if (landscape.terrainGrassList == null)
                    {
                        landscape.terrainGrassList = new List <LBTerrainGrass>();
                    }
                    if (landscape.lbGroupList == null)
                    {
                        landscape.lbGroupList = new List <LBGroup>();
                    }
                    if (landscape.landscapeMeshList == null)
                    {
                        landscape.landscapeMeshList = new List <LBLandscapeMesh>();
                    }
                    if (landscape.landscapeWaterList == null)
                    {
                        landscape.landscapeWaterList = new List <LBWater>();
                    }

                    // Update texture names to help detect missing textures when importing templates into another project
                    for (int txIdx = 0; txIdx < landscape.terrainTexturesList.Count; txIdx++)
                    {
                        // Only attempt to update the textureName if it is null or an empty string.
                        // This avoids loosing details about an already missing texture
                        if (string.IsNullOrEmpty(landscape.terrainTexturesList[txIdx].textureName))
                        {
                            if (landscape.terrainTexturesList[txIdx].texture != null)
                            {
                                landscape.terrainTexturesList[txIdx].textureName = landscape.terrainTexturesList[txIdx].texture.name;
                                isSceneSaveRequired = true;
                            }
                        }

                        // Only attempt to update the normalMapName if it is null or an empty string.
                        // This avoids loosing details about an already missing normalMap
                        if (string.IsNullOrEmpty(landscape.terrainTexturesList[txIdx].normalMapName))
                        {
                            if (landscape.terrainTexturesList[txIdx].normalMap != null)
                            {
                                landscape.terrainTexturesList[txIdx].normalMapName = landscape.terrainTexturesList[txIdx].normalMap.name;
                                isSceneSaveRequired = true;
                            }
                        }
                    }

                    // Update Grass texture names to help detect missing textures when importing templates into another project
                    for (int grIdx = 0; grIdx < landscape.terrainGrassList.Count; grIdx++)
                    {
                        // Only attempt to update the grass textureName if it is null or an empty string.
                        // This avoids loosing details about an already missing grass texture
                        if (string.IsNullOrEmpty(landscape.terrainGrassList[grIdx].textureName))
                        {
                            if (landscape.terrainGrassList[grIdx].texture != null)
                            {
                                landscape.terrainGrassList[grIdx].textureName = landscape.terrainGrassList[grIdx].texture.name;
                                isSceneSaveRequired = true;
                            }
                        }

                        // Only attempt to update the grass meshPrefName if it is null or an empty string.
                        // This avoids loosing details about an already missing grass mesh prefab
                        if (string.IsNullOrEmpty(landscape.terrainGrassList[grIdx].meshPrefabName))
                        {
                            if (landscape.terrainGrassList[grIdx].meshPrefab != null)
                            {
                                landscape.terrainGrassList[grIdx].meshPrefabName = landscape.terrainGrassList[grIdx].meshPrefab.name;
                                isSceneSaveRequired = true;
                            }
                        }
                    }

                    // Update Tree prefab names to help detect missing prefabs when importing templates into another project
                    for (int trIdx = 0; trIdx < landscape.terrainTreesList.Count; trIdx++)
                    {
                        // Only attempt to update the tree prefabName if it is null or an empty string.
                        // This avoids loosing details about an already missing tree prefab
                        if (string.IsNullOrEmpty(landscape.terrainTreesList[trIdx].prefabName))
                        {
                            if (landscape.terrainTreesList[trIdx].prefab != null)
                            {
                                landscape.terrainTreesList[trIdx].prefabName = landscape.terrainTreesList[trIdx].prefab.name;
                                isSceneSaveRequired = true;
                            }
                        }
                    }

                    // Update Mesh prefab names to help detect missing prefabs when importing templates into another project
                    for (int meshIdx = 0; meshIdx < landscape.landscapeMeshList.Count; meshIdx++)
                    {
                        // Only attempt to update the mesh prefabName if it is null or an empty string.
                        // This avoids loosing details about an already missing mesh prefab
                        if (string.IsNullOrEmpty(landscape.landscapeMeshList[meshIdx].prefabName))
                        {
                            if (landscape.landscapeMeshList[meshIdx].prefab != null)
                            {
                                landscape.landscapeMeshList[meshIdx].prefabName = landscape.landscapeMeshList[meshIdx].prefab.name;
                                isSceneSaveRequired = true;
                            }
                        }
                    }

                    int numGroups = landscape.lbGroupList == null ? 0 : landscape.lbGroupList.Count;

                    // Update Group Member prefab names to help detect missing prefabs when importing templates into another project
                    for (int grpIdx = 0; grpIdx < numGroups; grpIdx++)
                    {
                        if (landscape.lbGroupList[grpIdx].groupMemberList != null)
                        {
                            List <LBGroupMember> groupMemberList = landscape.lbGroupList[grpIdx].groupMemberList;
                            for (int grpMbrIdx = 0; grpMbrIdx < groupMemberList.Count; grpMbrIdx++)
                            {
                                // Only attempt to update the prefab prefabName if it is null or an empty string.
                                // This avoids loosing details about an already missing prefab
                                if (string.IsNullOrEmpty(groupMemberList[grpMbrIdx].prefabName))
                                {
                                    if (groupMemberList[grpMbrIdx].prefab != null)
                                    {
                                        groupMemberList[grpMbrIdx].prefabName = groupMemberList[grpMbrIdx].prefab.name;
                                        isSceneSaveRequired = true;
                                    }
                                }
                            }
                        }
                    }

                    // Landscape class lists - perform deep copy (#SMS 2.0.6 Beta 6f)
                    // Attempts to avoid issue where a template prefab is created (and later deleted) which could delete data
                    // from the landscape meta-data in the scene.
                    lbTemplate.topographyLayersList = landscape.topographyLayersList.ConvertAll(lyr => new LBLayer(lyr));
                    lbTemplate.terrainTexturesList  = landscape.terrainTexturesList.ConvertAll(tt => new LBTerrainTexture(tt));
                    lbTemplate.terrainTreesList     = landscape.terrainTreesList.ConvertAll(tt => new LBTerrainTree(tt));
                    lbTemplate.terrainGrassList     = landscape.terrainGrassList.ConvertAll(tg => new LBTerrainGrass(tg));
                    lbTemplate.lbGroupList          = landscape.lbGroupList.ConvertAll(grp => new LBGroup(grp));
                    lbTemplate.landscapeMeshList    = landscape.landscapeMeshList.ConvertAll(msh => new LBLandscapeMesh(msh));
                    lbTemplate.landscapeWaterList   = landscape.landscapeWaterList.ConvertAll(wtr => new LBWater(wtr, false));

                    // Check for surface and/or base mesh materials in Group Object Paths
                    for (int grpIdx = 0; grpIdx < numGroups; grpIdx++)
                    {
                        int numGrpMembers = landscape.lbGroupList[grpIdx].groupMemberList == null ? 0 : landscape.lbGroupList[grpIdx].groupMemberList.Count;

                        for (int grpMbrIdx = 0; grpMbrIdx < numGrpMembers; grpMbrIdx++)
                        {
                            LBGroupMember _lbGroupMember = landscape.lbGroupList[grpIdx].groupMemberList[grpMbrIdx];
                            // A deep copy will attempt create a new instance of the material using our LBGroupMember clone constructor
                            // In this case we need to reference the actual material in the project folder.
                            // If the Object Path was a duplicate of an existing Object Path, then this may also fail.
                            if (_lbGroupMember != null && _lbGroupMember.lbMemberType == LBGroupMember.LBMemberType.ObjPath && _lbGroupMember.lbObjPath != null)
                            {
                                lbTemplate.lbGroupList[grpIdx].groupMemberList[grpMbrIdx].lbObjPath.surfaceMeshMaterial = _lbGroupMember.lbObjPath.surfaceMeshMaterial;
                                lbTemplate.lbGroupList[grpIdx].groupMemberList[grpMbrIdx].lbObjPath.baseMeshMaterial    = _lbGroupMember.lbObjPath.baseMeshMaterial;
                            }
                        }
                    }

                    // Final Pass varibles
                    lbTemplate.useFinalPassSmoothing        = landscape.useFinalPassSmoothing;
                    lbTemplate.finalPassSmoothingIterations = landscape.finalPassSmoothingIterations;
                    lbTemplate.finalPassPixelRange          = landscape.finalPassPixelRange;
                    lbTemplate.fPassSmoothStencilGUID       = landscape.fPassSmoothStencilGUID;
                    lbTemplate.fPassSmoothStencilLayerGUID  = landscape.fPassSmoothStencilLayerGUID;
                    lbTemplate.fPassSmoothFilterMode        = landscape.fPassSmoothFilterMode;

                    lbTemplate.thermalErosionPreset           = landscape.thermalErosionPreset;
                    lbTemplate.useThermalErosion              = landscape.useThermalErosion;
                    lbTemplate.thermalErosionIterations       = landscape.thermalErosionIterations;
                    lbTemplate.thermalErosionTalusAngle       = landscape.thermalErosionTalusAngle;
                    lbTemplate.thermalErosionStrength         = landscape.thermalErosionStrength;
                    lbTemplate.fPassThErosionStencilGUID      = landscape.fPassThErosionStencilGUID;
                    lbTemplate.fPassThErosionStencilLayerGUID = landscape.fPassThErosionStencilLayerGUID;
                    lbTemplate.fPassThErosionFilterMode       = landscape.fPassThErosionFilterMode;

                    // Group Settings
                    lbTemplate.autoRefreshGroupDesigner = landscape.autoRefreshGroupDesigner;

                    // Vegetation Integration variables
                    lbTemplate.useVegetationSystem         = landscape.useVegetationSystem;
                    lbTemplate.useVegetationSystemTextures = landscape.useVegetationSystemTextures;

                    // Landscape Extension
                    lbTemplate.useLandscapeExtension = landscape.useLandscapeExtension;
                    if (landscape.lbLandscapeExtension != null)
                    {
                        lbTemplate.lbLandscapeExtension = new LBLandscapeExtension(landscape.lbLandscapeExtension);
                    }
                    else
                    {
                        lbTemplate.lbLandscapeExtension = new LBLandscapeExtension();
                    }

                    // GPU acceleration
                    lbTemplate.useGPUTexturing  = landscape.useGPUTexturing;
                    lbTemplate.useGPUTopography = landscape.useGPUTopography;
                    lbTemplate.useGPUGrass      = landscape.useGPUGrass;
                    lbTemplate.useGPUPath       = landscape.useGPUPath;

                    // Undo override
                    lbTemplate.isUndoTopographyDisabled = landscape.isUndoTopographyDisabled;

                    // Topography Mask - there is one per landscape
                    lbTemplate.topographyMaskMode     = (LBTemplate.MaskMode)landscape.topographyMaskMode;
                    lbTemplate.distanceToCentreMask   = landscape.distanceToCentreMask;
                    lbTemplate.maskWarpAmount         = landscape.maskWarpAmount;
                    lbTemplate.maskNoiseTileSize      = landscape.maskNoiseTileSize;
                    lbTemplate.maskNoiseOffsetX       = landscape.maskNoiseOffsetX;
                    lbTemplate.maskNoiseOffsetY       = landscape.maskNoiseOffsetY;
                    lbTemplate.maskNoiseCurveModifier = landscape.maskNoiseCurveModifier;

                    // Does user wish to include LBLighting?
                    if (addLBLightingToTemplate)
                    {
                        // Only include LBLighting if it is in the scene
                        LBLighting lbLighting = GameObject.FindObjectOfType <LBLighting>();
                        if (lbLighting == null)
                        {
                            lbTemplate.isLBLightingIncluded = false;
                            Debug.LogWarning("Landscape Builder - Export Template - No LBLighting found in the scene");
                        }
                        else
                        {
                            lbTemplate.isLBLightingIncluded = true;
                            lbTemplate.AddLBLightingSettings(lbLighting);
                        }
                    }
                    else
                    {
                        lbTemplate.isLBLightingIncluded = false;
                    }

                    // Create an empty list of Asset Paths for the mesh materials
                    List <string> lbMapPathMaterialAssetPathList = new List <string>();

                    if (addPathsToTemplate)
                    {
                        // Find any Camera Paths in this landscape (changed from all in scene in 1.3.2 Beta 9b)
                        List <LBCameraPath> lbCameraPathList = LBCameraPath.GetCameraPathsInLandscape(landscape);
                        if (lbCameraPathList != null)
                        {
                            // Get the LBPath from each LBCameraPath and add it to the LBTemplate instance
                            foreach (LBCameraPath lbCameraPath in lbCameraPathList)
                            {
                                if (lbCameraPath.lbPath != null)
                                {
                                    // Update the path name before adding to the template
                                    if (lbCameraPath.gameObject != null)
                                    {
                                        lbCameraPath.lbPath.pathName = lbCameraPath.gameObject.name;
                                        isSceneSaveRequired          = true;
                                    }
                                    lbTemplate.lbPathList.Add(lbCameraPath.lbPath);
                                }
                            }
                        }

                        // Find all MapPaths in the this landscape
                        List <LBMapPath> lbMapPathList = LBMapPath.GetMapPathsInLandscape(landscape);
                        if (lbMapPathList != null)
                        {
                            // Get the LBPath from each LBMapPath and add it to the LBTemplate instance
                            foreach (LBMapPath lbMapPath in lbMapPathList)
                            {
                                if (lbMapPath.lbPath != null)
                                {
                                    // Update the path name before adding to the template
                                    if (lbMapPath.gameObject != null)
                                    {
                                        lbMapPath.lbPath.pathName = lbMapPath.gameObject.name;

                                        // Check for a mesh
                                        if (lbMapPath.lbPath.lbMesh != null && addPathMeshMaterialsToTemplate)
                                        {
                                            lbMapPath.lbPath.meshTempMaterial = lbMapPath.meshMaterial;

                                            // If also creating a package, remember the asset path for this mesh material
                                            if (createTemplatePackage)
                                            {
                                                string mapPathMatAssetPath = UnityEditor.AssetDatabase.GetAssetPath(lbMapPath.meshMaterial);
                                                if (!string.IsNullOrEmpty(mapPathMatAssetPath))
                                                {
                                                    lbMapPathMaterialAssetPathList.Add(mapPathMatAssetPath);
                                                }
                                            }
                                        }
                                        else
                                        {
                                            lbMapPath.lbPath.meshTempMaterial = null;
                                        }

                                        isSceneSaveRequired = true;
                                    }
                                    lbTemplate.lbPathList.Add(lbMapPath.lbPath);
                                }
                            }
                        }
                    }

                    if (addStencilsToTemplate)
                    {
                        List <LBStencil> lbStencilList = LBStencil.GetStencilsInLandscape(landscape, true);
                        if (lbTemplate.lbTemplateStencilList == null)
                        {
                            lbTemplate.lbTemplateStencilList = new List <LBTemplateStencil>();
                        }

                        if (lbTemplate.lbTemplateStencilList != null && lbStencilList != null)
                        {
                            // Create a LBTemplateStencil for each Stencil in the landscape, and add it to the Template.
                            // NOTE Does not copy the USHORT layerArray or render textures in the Stencil Layers as we want to keep
                            // the Template as small as possible.
                            foreach (LBStencil lbStencil in lbStencilList)
                            {
                                if (lbStencil != null)
                                {
                                    LBTemplateStencil lbTemplateStencil = new LBTemplateStencil(lbStencil);
                                    if (lbTemplateStencil != null)
                                    {
                                        lbTemplate.lbTemplateStencilList.Add(lbTemplateStencil);
                                    }
                                }
                            }
                        }
                    }

                    // Create template folders if they don't already exist
                    LBEditorHelper.CheckFolder("Assets/LandscapeBuilder/Templates");

                    if (!Directory.Exists("LandscapeBuilder/TemplatePackages"))
                    {
                        Directory.CreateDirectory("LandscapeBuilder/TemplatePackages");
                    }

                    // Create a prefab
                    bool   continueToSave    = false;
                    string templateFullPath  = Application.dataPath + "/LandscapeBuilder/Templates/" + landscapeTemplateName + ".prefab";
                    string templateAssetPath = "Assets/LandscapeBuilder/Templates/" + landscapeTemplateName + ".prefab";
                    if (File.Exists(templateFullPath))
                    {
                        if (EditorUtility.DisplayDialog("Template Already Exists", "Are you sure you want to save the template? The currently existing" +
                                                        " template will be lost.", "Overwrite", "Cancel"))
                        {
                            if (!AssetDatabase.DeleteAsset(templateAssetPath))
                            {
                                Debug.LogWarning("Landscape Builder - could not overwrite Assets/LandscapeBuilder/Templates/" + landscapeTemplateName + " for " + landscapeName + " template");
                            }
                            else
                            {
                                continueToSave = true;
                            }
                        }
                    }
                    else
                    {
                        continueToSave = true;
                    }

                    if (continueToSave)
                    {
                        #if UNITY_2018_3_OR_NEWER
                        GameObject templatePrefabGO = PrefabUtility.SaveAsPrefabAsset(newTemplateObj, templateAssetPath);
                        #else
                        GameObject templatePrefabGO = PrefabUtility.CreatePrefab(templateAssetPath, newTemplateObj);
                        #endif

                        if (templatePrefabGO != null && createTemplatePackage)
                        {
                            // Always include the template prefab (this is meta-data only)
                            List <string> templateAssetPaths = new List <string>();
                            templateAssetPaths.Add(templateAssetPath);

                            // Although technically users could include 3rd party textures in a LBMap texture, they are MUCH more likely to use
                            // map textures generated from within Landscape Builder.
                            if (addMapTexturesToTemplatePackage)
                            {
                                List <string> uniqueMapTexPaths = LBLandscapeOperations.GetMapTextureAssetPathsFromLandscape(landscape, true);
                                if (uniqueMapTexPaths != null)
                                {
                                    if (uniqueMapTexPaths.Count > 0)
                                    {
                                        templateAssetPaths.AddRange(uniqueMapTexPaths);
                                    }
                                }
                            }

                            if (addLayerHeightmapTexturesToTemplatePackage)
                            {
                                // Get a list of the image-based topography layers with a heightmap texture
                                List <LBLayer> layerList = landscape.topographyLayersList.FindAll(lyr => lyr.heightmapImage != null && (lyr.type == LBLayer.LayerType.ImageBase || lyr.type == LBLayer.LayerType.ImageAdditive || lyr.type == LBLayer.LayerType.ImageSubtractive || lyr.type == LBLayer.LayerType.ImageDetail));
                                if (layerList != null)
                                {
                                    //Debug.Log("Save Template found " + layerList.Count + " image layers with a heightmap");
                                    for (int lyrIdx = 0; lyrIdx < layerList.Count; lyrIdx++)
                                    {
                                        // Get the path in the asset db to the image-based layer heightmap texture
                                        string heightmapPath = UnityEditor.AssetDatabase.GetAssetPath(layerList[lyrIdx].heightmapImage);
                                        if (!string.IsNullOrEmpty(heightmapPath))
                                        {
                                            templateAssetPaths.Add(heightmapPath);
                                        }
                                    }
                                }
                            }

                            // Add the list of map path mesh materials we add earlier.
                            if (addPathMeshMaterialsToTemplate && lbMapPathMaterialAssetPathList.Count > 0)
                            {
                                templateAssetPaths.AddRange(lbMapPathMaterialAssetPathList);
                            }

                            AssetDatabase.ExportPackage(templateAssetPaths.ToArray(), "LandscapeBuilder/TemplatePackages/" + landscapeTemplateName + ".unitypackage", ExportPackageOptions.Interactive);
                        }
                        LBEditorHelper.HighlightItemInProjectWindow(templateAssetPath);
                    }

                    // Cleanup scene hierarchy
                    GameObject.DestroyImmediate(newTemplateObj);
                }
            }

            return(isSuccessful);
        }
Ejemplo n.º 9
0
        private static void CopyMeshToGroupMember(LBLandscapeMesh lMesh, LBGroup lbGroup, LBGroupMember lbGroupMember)
        {
            if (lMesh != null && lbGroup != null && lbGroupMember != null)
            {
                if (lMesh.meshPlacingMode != LBLandscapeMesh.MeshPlacingMode.ConstantInfluence || lMesh.minScale != 1f || lMesh.maxScale != 1f)
                {
                    // This may not the first member of the group so need to check if we need to override group defaults

                    // The first member of a group sets the group-level default settings
                    if (lbGroup.groupMemberList.Count < 1)
                    {
                        lbGroupMember.isGroupOverride = false;
                        lbGroup.minScale       = lMesh.minScale;
                        lbGroup.maxScale       = lMesh.maxScale;
                        lbGroup.minHeight      = lMesh.minHeight;
                        lbGroup.maxHeight      = lMesh.maxHeight;
                        lbGroup.minInclination = lMesh.minInclination;
                        lbGroup.maxInclination = lMesh.maxInclination;
                    }
                    // Does this member have the same rules as the group it will be placed into?
                    else if (lMesh.minScale != lbGroup.minScale || lMesh.maxScale != lbGroup.maxScale ||
                             lMesh.minHeight != lbGroup.minHeight || lMesh.maxHeight != lbGroup.maxHeight ||
                             lMesh.minInclination != lbGroup.minInclination || lMesh.maxInclination != lbGroup.maxInclination)
                    {
                        lbGroupMember.isGroupOverride = true;

                        lbGroupMember.minScale       = lMesh.minScale;
                        lbGroupMember.maxScale       = lMesh.maxScale;
                        lbGroupMember.minHeight      = lMesh.minHeight;
                        lbGroupMember.maxHeight      = lMesh.maxHeight;
                        lbGroupMember.minInclination = lMesh.minInclination;
                        lbGroupMember.maxInclination = lMesh.maxInclination;
                    }
                    else
                    {
                        // Member rules are the same as the Group, so no need to override group-level defaults
                        lbGroupMember.isGroupOverride = false;
                    }
                }

                lbGroupMember.prefab     = lMesh.prefab;
                lbGroupMember.prefabName = lMesh.prefabName;

                lbGroupMember.modelOffsetX = lMesh.offset.x;
                lbGroupMember.modelOffsetY = 0f;
                lbGroupMember.modelOffsetZ = lMesh.offset.z;

                lbGroupMember.randomiseOffsetY = false;
                lbGroupMember.minOffsetY       = lMesh.offset.y;
                lbGroupMember.maxOffsetY       = lbGroupMember.minOffsetY;

                lbGroupMember.randomiseRotationY = lMesh.randomiseYRotation;
                if (lMesh.randomiseYRotation)
                {
                    lbGroupMember.startRotationY = 0f;
                    lbGroupMember.endRotationY   = 359.9f;
                }
                else
                {
                    lbGroupMember.startRotationY = lMesh.fixedYRotation;
                    lbGroupMember.endRotationY   = lMesh.fixedYRotation;
                }
                lbGroupMember.randomiseRotationXZ = false;
                lbGroupMember.rotationX           = lMesh.XRotation;
                lbGroupMember.endRotationX        = lMesh.XRotation;
                lbGroupMember.rotationZ           = lMesh.ZRotation;
                lbGroupMember.endRotationZ        = lMesh.ZRotation;

                lbGroupMember.isCombineMesh            = lMesh.isCombineMesh;
                lbGroupMember.isKeepPrefabConnection   = lMesh.isKeepPrefabConnection;
                lbGroupMember.isCreateCollider         = lMesh.isCreateCollider;
                lbGroupMember.isRemoveEmptyGameObjects = lMesh.isRemoveEmptyGameObjects;

                lbGroupMember.useNoise             = lMesh.useNoise;
                lbGroupMember.noiseTileSize        = lMesh.noiseTileSize;
                lbGroupMember.noisePlacementCutoff = lMesh.meshPlacementCutoff;

                lbGroupMember.maxPrefabSqrKm    = lMesh.maxMeshes;
                lbGroupMember.maxPrefabPerGroup = 10000;

                // Slightly incompatible as old mesh is centre to centre distance
                // mesh.minProximity can be 0.0, while lbGroupMember.proximityExtent currently must be > 0.01.
                lbGroupMember.proximityExtent = lMesh.minProximity;

                lbGroupMember.isTerrainAligned = lMesh.isTerrainAligned;

                lbGroupMember.isTerrainFlattened  = lMesh.isTerrainFlattened;
                lbGroupMember.flattenBlendRate    = lMesh.flattenBlendRate;
                lbGroupMember.flattenDistance     = lMesh.flattenDistance;
                lbGroupMember.flattenHeightOffset = lMesh.flattenHeightOffset;

                lbGroupMember.minGrassProximity = lMesh.minGrassProximity;
                lbGroupMember.isRemoveTree      = true;
                lbGroupMember.minTreeProximity  = lMesh.minTreeProximity;
            }
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Attempt to convert Mesh/Prefabs to Uniform Groups
        /// </summary>
        /// <param name="landscape"></param>
        public static void ConvertMeshesToGroups(LBLandscape landscape)
        {
            string methodName = "LBUpdate.ConvertMeshesToGroups";

            if (landscape == null)
            {
                Debug.LogWarning("ERROR: " + methodName + " - landscape is null. Please Report.");
            }
            else if (landscape.landscapeMeshList != null && landscape.lbGroupList != null)
            {
                List <LBGroup> convertedGroupList = new List <LBGroup>();

                int numMeshes = landscape.landscapeMeshList.Count;

                for (int mIdx = 0; mIdx < numMeshes; mIdx++)
                {
                    LBLandscapeMesh lMesh = landscape.landscapeMeshList[mIdx];

                    if (lMesh.isDisabled)
                    {
                        Debug.Log("INFO: " + methodName + " skipping disabled Mesh " + (mIdx + 1));
                    }
                    else if (lMesh.meshPlacingMode == LBLandscapeMesh.MeshPlacingMode.Map || lMesh.meshPlacingMode == LBLandscapeMesh.MeshPlacingMode.HeightInclinationMap)
                    {
                        Debug.Log("INFO: " + methodName + " cannot convert Mesh " + (mIdx + 1) + " as it contains a Map texture. Recommendation: use a Stencil Layer instead");
                    }
                    else if (!lMesh.usePrefab)
                    {
                        Debug.Log("INFO: " + methodName + " cannot convert Mesh " + (mIdx + 1) + " as Groups only support prefabs.");
                    }
                    else if (lMesh.isClustered)
                    {
                        Debug.Log("INFO: " + methodName + " cannot convert Mesh " + (mIdx + 1) + " as source has Clusters enabled. Recommendation: Create a Procedural Clearing to replace this Mesh/Prefab.");
                    }
                    else
                    {
                        LBGroup       lbGroup       = null;
                        LBGroupMember lbGroupMember = null;

                        // If this is the first group always create a new one
                        if (convertedGroupList.Count == 0)
                        {
                            lbGroup = new LBGroup();
                            if (lbGroup != null)
                            {
                                lbGroup.showInEditor = false;
                                lbGroup.groupName    = "converted group " + (convertedGroupList.Count + 1).ToString("000");
                                lbGroup.lbGroupType  = LBGroup.LBGroupType.Uniform;
                                lbGroup.filterList   = GetSupportedMeshFilters(lMesh.filterList);
                                convertedGroupList.Add(lbGroup);
                            }
                        }
                        else
                        {
                            // Find a compatible group, else create a new one
                            List <LBFilter> supportedMeshFilterList = GetSupportedMeshFilters(lMesh.filterList);
                            int             numThisMeshFilters      = (supportedMeshFilterList == null ? 0 : supportedMeshFilterList.Count);

                            // Find a group with the same filters
                            for (int grpIdx = 0; grpIdx < convertedGroupList.Count; grpIdx++)
                            {
                                LBGroup searchGroup = convertedGroupList[grpIdx];

                                List <LBFilter> groupFilterList     = searchGroup.filterList;
                                int             numThisGroupFilters = (groupFilterList == null ? 0 : groupFilterList.Count);

                                if (numThisMeshFilters == 0 && numThisGroupFilters == 0)
                                {
                                    lbGroup = searchGroup; break;
                                }
                                else if (numThisMeshFilters == numThisGroupFilters)
                                {
                                    // Assume they match
                                    bool isMatch = true;
                                    for (int fIdx = 0; fIdx < numThisGroupFilters; fIdx++)
                                    {
                                        if (groupFilterList[fIdx].filterType != supportedMeshFilterList[fIdx].filterType)
                                        {
                                            isMatch = false; break;
                                        }
                                        else if (groupFilterList[fIdx].lbStencilGUID != supportedMeshFilterList[fIdx].lbStencilGUID)
                                        {
                                            isMatch = false; break;
                                        }
                                        else if (groupFilterList[fIdx].lbStencilLayerGUID != supportedMeshFilterList[fIdx].lbStencilLayerGUID)
                                        {
                                            isMatch = false; break;
                                        }
                                    }
                                    if (isMatch)
                                    {
                                        lbGroup = searchGroup; break;
                                    }
                                }
                            }

                            // If no suitable group was found, add a new one
                            if (lbGroup == null)
                            {
                                lbGroup = new LBGroup();
                                lbGroup.showInEditor = false;
                                lbGroup.groupName    = "converted group " + (convertedGroupList.Count + 1).ToString("000");
                                lbGroup.lbGroupType  = LBGroup.LBGroupType.Uniform;
                                lbGroup.filterList   = GetSupportedMeshFilters(lMesh.filterList);
                                convertedGroupList.Add(lbGroup);
                            }
                        }

                        if (lbGroup == null)
                        {
                            Debug.Log("INFO: " + methodName + " could not create a new LBGroup. Please Report");
                        }
                        else
                        {
                            lbGroupMember = new LBGroupMember();
                            if (lbGroupMember == null)
                            {
                                Debug.Log("INFO: " + methodName + " could not create a new LBGroupMember. Please Report");
                            }
                            else
                            {
                                lbGroupMember.showInEditor = false;

                                CopyMeshToGroupMember(lMesh, lbGroup, lbGroupMember);

                                lbGroup.groupMemberList.Add(lbGroupMember);

                                // Disable migrated mesh/prefab items
                                lMesh.isDisabled = true;
                            }
                        }
                    }
                }

                if (convertedGroupList.Count > 0)
                {
                    landscape.lbGroupList.AddRange(convertedGroupList);
                }
            }
        }