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(); } } } }
/// <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; }
// 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; } }
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); }
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; } }
/// <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); } } }