public void RemoveAndDestroyElement(DecorPaintObjectPlacementBrushElement element)
 {
     if (ContainsElement(element) && element != null)
     {
         _elements.Remove(element);
         ClampActiveElementIndex();
         UndoEx.DestroyObjectImmediate(element);
     }
 }
        public DecorPaintObjectPlacementBrushElement CreateNewElement()
        {
            DecorPaintObjectPlacementBrushElement newElement = Octave3DWorldBuilder.ActiveInstance.CreateScriptableObject <DecorPaintObjectPlacementBrushElement>();

            newElement.ParentBrush = this;
            _elements.Add(newElement);

            if (_activeElementIndex < 0)
            {
                SetActiveElement(newElement);
            }

            return(newElement);
        }
 public void SetActiveElement(DecorPaintObjectPlacementBrushElement newActiveElement)
 {
     if (newActiveElement == null)
     {
         _activeElementIndex = -1;
     }
     else
     {
         int newActiveIndex = _elements.FindIndex(item => item == newActiveElement);
         if (newActiveIndex >= 0)
         {
             _activeElementIndex = newActiveIndex;
         }
     }
 }
        protected override void PerformDrop()
        {
            if (_dropDestination == DropDestination.Element)
            {
                if (_destinationDecorPaintBrushElement == null)
                {
                    return;
                }

                List <GameObject> validUnityPrefabsInvolvedInDropOperation = PrefabValidator.GetValidPrefabsFromEntityCollection(DragAndDrop.objectReferences, false);
                if (validUnityPrefabsInvolvedInDropOperation.Count != 0)
                {
                    PerformDropUsingFirstPrefabInValidUnityPrefabCollection(validUnityPrefabsInvolvedInDropOperation);
                }
            }
            else
            {
                if (_destinationBrush == null)
                {
                    return;
                }

                List <GameObject> validUnityPrefabs = PrefabValidator.GetValidPrefabsFromEntityCollection(DragAndDrop.objectReferences, false);
                if (validUnityPrefabs.Count != 0)
                {
                    UndoEx.RecordForToolAction(_destinationBrush);
                    foreach (GameObject unityPrefab in validUnityPrefabs)
                    {
                        DecorPaintObjectPlacementBrushElement newElement = _destinationBrush.CreateNewElement();

                        PrefabCategory categoryWhichContainsPrefab = PrefabCategoryDatabase.Get().GetPrefabCategoryWhichContainsPrefab(unityPrefab);
                        if (categoryWhichContainsPrefab != null)
                        {
                            newElement.Prefab = categoryWhichContainsPrefab.GetPrefabByUnityPrefab(unityPrefab);
                        }
                        else
                        {
                            Prefab prefab = PrefabFactory.Create(unityPrefab);
                            UndoEx.RecordForToolAction(_destinationBrush.DestinationCategoryForElementPrefabs);
                            PrefabWithPrefabCategoryAssociationQueue.Instance.Enqueue(PrefabWithPrefabCategoryAssociationFactory.Create(prefab, _destinationBrush.DestinationCategoryForElementPrefabs));
                            newElement.Prefab = prefab;
                        }
                    }
                }
            }

            Octave3DWorldBuilder.ActiveInstance.RepaintAllEditorWindows();
        }
Example #5
0
 private void AdjustObjectBoxCenterToSitOnSurface(OrientedBox objectBox, ObjectSurfaceData objectSurfaceData, DecorPaintObjectPlacementBrushElement brushElement)
 {
     if (brushElement.AlignToSurface)
     {
         objectBox.Center = objectSurfaceData.BasePosition + objectSurfaceData.SurfaceNormal * 0.5f * objectBox.GetSizeAlongDirection(objectSurfaceData.SurfaceNormal);
     }
     else
     {
         BoxFace boxFaceWhichFacesSurfaceNormal = objectBox.GetBoxFaceWhichFacesNormal(_workingBrushCircle.Plane.normal);
         Vector3 faceCenter = objectBox.GetBoxFaceCenter(boxFaceWhichFacesSurfaceNormal);
         Vector3 fromFaceCenterToBoxCenter = objectBox.Center - faceCenter;
         objectBox.Center = objectSurfaceData.BasePosition + fromFaceCenterToBoxCenter;
     }
 }
Example #6
0
 private void AdjustObjectBoxRotationOnSurface(OrientedBox objectBox, ObjectSurfaceData objectSurfaceData, DecorPaintObjectPlacementBrushElement brushElement)
 {
     if (brushElement.AlignToSurface)
     {
         objectBox.Rotation = AxisAlignment.CalculateRotationQuaternionForAxisAlignment(objectBox.Rotation, brushElement.AlignmentAxis, objectSurfaceData.SurfaceNormal);
     }
 }
Example #7
0
        private MatrixObjectBoxPair CalculateMatrixObjectBoxPair(int brushElementIndex, ObjectSurfaceData objectSurfaceData)
        {
            // Store the needed data for easy access
            DecorPaintObjectPlacementBrushElement brushElement = _allValidBrushElements[brushElementIndex];
            OrientedBox objectBox = new OrientedBox(_brushElementsPrefabOrientedBoxes[brushElementIndex]);

            // Establish the object's scale. Randomize the scale if necessary. Otherwise, just use the brush element's scale.
            Vector3 xyzObjectBoxScale = _brushElementsPrefabWorldScaleValues[brushElementIndex];

            if (brushElement.ScaleRandomizationSettings.RandomizeScale)
            {
                // Generate a random scale factor and apply it to the current scale
                ObjectUniformScaleRandomizationSettings uniformScaleRandomizationSettings = brushElement.ScaleRandomizationSettings.UniformScaleRandomizationSettings;
                xyzObjectBoxScale *= UnityEngine.Random.Range(uniformScaleRandomizationSettings.MinScale, uniformScaleRandomizationSettings.MaxScale);;
            }
            else
            {
                xyzObjectBoxScale *= brushElement.Scale;
            }

            // Apply the scale that we have calculated
            objectBox.Scale = xyzObjectBoxScale;

            // Now we will calculate the rotation. First we will calculate a quaternion which allow us to take the
            // specified rotation offset into account. The quaternion rotates around the surface normal by an angle
            // of 'RotationOffset' degrees.
            Quaternion rotationOffset = brushElement.AlignToSurface ? Quaternion.AngleAxis(brushElement.RotationOffsetInDegrees, objectSurfaceData.SurfaceNormal) : Quaternion.identity;

            // If we need to align to stroke, we have some more work to do. Otherwise, we will just set the
            // rotation to be the same as the prefab's rotation, but offset by 'rotationOffset'.
            if (brushElement.AlignToStroke)
            {
                // First calculate the rotation without any offset. This is the rotation of the prefab plus the rotation which
                // must be applied for alignment.
                Quaternion rotationWithoutOffset = _rotationToApplyForStrokeAlignment * _elementToCurrentPrefabRotation[brushElement];

                // The rotation of the box must be set to the rotation which we just calculated (which ensures proper alingment) plus
                // the rotation offset.
                objectBox.Rotation = rotationOffset * rotationWithoutOffset;

                // Store the rotation inside the dictionary if an entry doesn't already exist
                if (!_elementToNewPrefabRotation.ContainsKey(brushElement))
                {
                    _elementToNewPrefabRotation.Add(brushElement, rotationWithoutOffset);
                }
            }
            else
            {
                objectBox.Rotation = rotationOffset * brushElement.Prefab.UnityPrefab.transform.rotation;
            }

            // Adjust the rotation of the object so that its axis is aligned with the placement surface if necessary
            if (brushElement.AlignToSurface)
            {
                AdjustObjectBoxRotationOnSurface(objectBox, objectSurfaceData, brushElement);
            }

            // The final step is to rotate the object by a random amount around the placement surface
            if (brushElement.RotationRandomizationMode != BrushElementRotationRandomizationMode.None)
            {
                Vector3 rotationAxis = objectSurfaceData.SurfaceNormal;
                if (brushElement.RotationRandomizationMode == BrushElementRotationRandomizationMode.X)
                {
                    rotationAxis = Vector3.right;
                }
                else if (brushElement.RotationRandomizationMode == BrushElementRotationRandomizationMode.Y)
                {
                    rotationAxis = Vector3.up;
                }
                else if (brushElement.RotationRandomizationMode == BrushElementRotationRandomizationMode.Z)
                {
                    rotationAxis = Vector3.forward;
                }

                if (!brushElement.AlignToStroke)
                {
                    // When stroke alignment is not required, we will generate a random rotation angle and apply it
                    // to the current box rotation.
                    float      randomRotationAngle = UnityEngine.Random.Range(0.0f, 360.0f);
                    Quaternion additionalRotation  = Quaternion.AngleAxis(randomRotationAngle, rotationAxis);
                    objectBox.Rotation = additionalRotation * objectBox.Rotation;
                }
                else
                {
                    // When both rotation randomization and stroke alingment are turned on, we will proudce a small
                    // random rotation offset to randomize the alingmnet a little bit.
                    float      randomRotationAngle = UnityEngine.Random.Range(0.0f, 25.0f);
                    Quaternion additionalRotation  = Quaternion.AngleAxis(randomRotationAngle, rotationAxis);
                    objectBox.Rotation = additionalRotation * objectBox.Rotation;
                }
            }

            // Place the object on the surface
            AdjustObjectBoxCenterToSitOnSurface(objectBox, objectSurfaceData, brushElement);

            // Construct the object matrix and return the object/marix pair
            TransformMatrix objectMatrix = new TransformMatrix(ObjectPositionCalculator.CalculateObjectHierarchyPosition(brushElement.Prefab,
                                                                                                                         objectBox.Center, xyzObjectBoxScale, objectBox.Rotation), objectBox.Rotation, xyzObjectBoxScale);

            return(new MatrixObjectBoxPair(objectMatrix, objectBox));
        }
Example #8
0
 private bool DoesObjectSurfacePassSlopeTest(ObjectSurfaceData objectSurfaceData, DecorPaintObjectPlacementBrushElement brushElement)
 {
     if (!_workingBrushCircle.IsSittingOnTerrain && brushElement.SlopeSettings.UseSlopeOnlyForTerrainObjects)
     {
         return(true);
     }
     return(brushElement.SlopeSettings.IsNormalInSlopeRange(objectSurfaceData.SurfaceNormal));
 }
Example #9
0
        public List <ObjectPlacementData> Calculate(Quaternion rotationToApplyForStrokeAlignment)
        {
            if (!ValidateCalculationRequirements())
            {
                return(new List <ObjectPlacementData>());
            }

            _objectNodeNetwork.Clear();
            _rotationToApplyForStrokeAlignment = rotationToApplyForStrokeAlignment;
            CreateSurfaceColliderProjector();
            _elementToNewPrefabRotation.Clear();
            _allowObjectIntersection = ObjectPlacementSettings.Get().ObjectIntersectionSettings.AllowIntersectionForDecorPaintBrushModeDrag;

            int currentObjectIndex           = 0;
            var objectPlacementDataInstances = new List <ObjectPlacementData>(_workingBrush.MaxNumberOfObjects);

            while (currentObjectIndex < _workingBrush.MaxNumberOfObjects)
            {
                DecorPaintObjectPlacementBrushElement brushElement = _brushElementSpawnChanceTable.PickEntity(UnityEngine.Random.Range(0.0f, 1.0f));
                int brushElementIndex = _allValidBrushElements.FindIndex(item => item == brushElement);
                ++currentObjectIndex;

                // No object nodes were created yet?
                if (_objectNodeNetwork.NumberOfNodes == 0)
                {
                    // Create the first node at a random position inside the brush circle
                    Vector3             randomPositionInsideCircle = _workingBrushCircle.GetRandomPointInside();
                    ObjectSurfaceData   objectSurfaceData          = CalculateObjectSurfaceData(randomPositionInsideCircle);
                    MatrixObjectBoxPair matrixObjectBoxPair        = CalculateMatrixObjectBoxPair(brushElementIndex, objectSurfaceData);

                    // We need to know if the normal of the surface on which the object resides lies within the desired slope range
                    bool passesSlopeTest = DoesObjectSurfacePassSlopeTest(objectSurfaceData, brushElement);

                    // Note: Even if the slope test is not passed, we will still create an object node. The reason for this is that
                    //       we want to have some kind of continuity in the algorithm. Imagine that the brush circle is large and is
                    //       divided by a large terrain mountain which sits in the middle. If the object generation starts on one side
                    //       of the mountain, the algorithm might never get a chance to go over the other side if the slope condition
                    //       is not satisifed. We want to spread objects as much as possible so even though this object will not be
                    //       placed in the scene, we will still add it to the node network.
                    _objectNodeNetwork.AddNodeToEnd(matrixObjectBoxPair.ObjectBox, objectSurfaceData);
                    if (passesSlopeTest && DoesBoxPassObjectIntersectionTest(matrixObjectBoxPair.ObjectBox, brushElement.Prefab.UnityPrefab, matrixObjectBoxPair.ObjectMatrix))
                    {
                        objectPlacementDataInstances.Add(new ObjectPlacementData(matrixObjectBoxPair.ObjectMatrix, brushElement.Prefab, brushElement.MustEmbedInSurface));
                    }
                }
                else
                {
                    // Are there any node segments available?
                    if (_objectNodeNetwork.NumberOfSegments != 0)
                    {
                        // The first step is to generate a random node index and store references to that node and its immediate neighbour
                        _objectNodeNetwork.RemoveAllNodesWhichGenerateConcavities(_workingBrushCircle.Plane);
                        int        randomNodeIndex = _objectNodeNetwork.GetRandomNodeIndex();
                        ObjectNode firstNode       = _objectNodeNetwork.GetNodeByIndex(randomNodeIndex);
                        ObjectNode secondNode      = _objectNodeNetwork.GetNodeByIndex(randomNodeIndex + 1);

                        // Calculate the plane of the segment which unites the 2 nodes. We will also store the
                        // actual segment and the middle point on the segment. We will use this middle point to
                        // generate the initial object position.
                        Segment3D   nodeSegment     = ObjectNodeNetwork.CalculateSegmentBetweenNodes(firstNode, secondNode);
                        Vector3     segmentMidPoint = nodeSegment.GetPoint(0.5f);
                        Plane       segmentPlane    = ObjectNodeNetwork.CalculateSegmentPlaneNormal(firstNode, secondNode);
                        OrientedBox firstNodeBox    = firstNode.ObjectBox;
                        OrientedBox secondNodeBox   = secondNode.ObjectBox;

                        // Calculate the furthest point in front of the plane using the corner points of the
                        // 2 nodes. The idea is to move the new object as much as possible from the bulk of
                        // objects that have already been generated.
                        Vector3        furthestPointFromPlane;
                        List <Vector3> nodeCornerPoints = firstNodeBox.GetCornerPoints();
                        nodeCornerPoints.AddRange(secondNodeBox.GetCornerPoints());
                        if (!segmentPlane.GetFurthestPointInFront(nodeCornerPoints, out furthestPointFromPlane))
                        {
                            continue;
                        }

                        // Use the calculated furthest point from plane and the the existing plane normal to calculate the
                        // pivot plane. The new object will reside at some distance away from this plane.
                        Plane pivotPlane = new Plane(segmentPlane.normal, furthestPointFromPlane);

                        // Calculate the new object transform data. We will use the segment's mid point to generate the
                        // initial object position.
                        ObjectSurfaceData   objectSurfaceData   = CalculateObjectSurfaceData(segmentMidPoint);
                        MatrixObjectBoxPair matrixObjectBoxPair = CalculateMatrixObjectBoxPair(brushElementIndex, objectSurfaceData);
                        OrientedBox         objectBox           = matrixObjectBoxPair.ObjectBox;

                        // Identify the objects's furthest point behind the plane
                        Vector3        objectBoxPivotPoint;
                        List <Vector3> objectBoxCornerPoints = objectBox.GetCornerPoints();
                        if (!pivotPlane.GetFurthestPointBehind(objectBoxCornerPoints, out objectBoxPivotPoint))
                        {
                            continue;
                        }

                        // Use the furthest point to move the object in front of the plane and take the distance between objects into account
                        Vector3 fromPivotPointToCenter = objectBox.Center - objectBoxPivotPoint;
                        Vector3 projectedPivotPoint    = pivotPlane.ProjectPoint(objectBoxPivotPoint);
                        objectBox.Center = projectedPivotPoint + fromPivotPointToCenter + pivotPlane.normal * _workingBrush.DistanceBetweenObjects;

                        // Generate the object surface data
                        objectSurfaceData = CalculateObjectSurfaceData(objectBox.Center);
                        bool passesSlopeTest = DoesObjectSurfacePassSlopeTest(objectSurfaceData, brushElement);

                        // Now we need to adjust the orientation and center of the box. If the calculated center
                        // lies outside the brush circle, we will ignore this node.
                        AdjustObjectBoxRotationOnSurface(objectBox, objectSurfaceData, brushElement);
                        AdjustObjectBoxCenterToSitOnSurface(objectBox, objectSurfaceData, brushElement);
                        if (!_workingBrushCircle.ContainsPoint(_workingBrushCircle.Plane.ProjectPoint(objectBox.Center)))
                        {
                            continue;
                        }

                        // Recalculate the object matrix using the new box data
                        TransformMatrix objectMatrix = matrixObjectBoxPair.ObjectMatrix;
                        objectMatrix.Rotation    = objectBox.Rotation;
                        objectMatrix.Translation = ObjectPositionCalculator.CalculateObjectHierarchyPosition(brushElement.Prefab, objectBox.Center, objectMatrix.Scale, objectBox.Rotation);

                        // We have been modifying the matrix and box data independently so we will ensure that the box uses the latest data
                        OrientedBox finalBox = new OrientedBox(objectBox);
                        finalBox.SetTransformMatrix(objectMatrix);

                        // If the slope test passed, we will calculate an object placement data instance. Otherwise, we will just insert a new node.
                        if (passesSlopeTest && DoesBoxPassObjectIntersectionTest(finalBox, brushElement.Prefab.UnityPrefab, objectMatrix))
                        {
                            objectPlacementDataInstances.Add(new ObjectPlacementData(objectMatrix, brushElement.Prefab, brushElement.MustEmbedInSurface));
                        }
                        _objectNodeNetwork.InsertAfterNode(objectBox, objectSurfaceData, randomNodeIndex);
                    }
                    else
                    {
                        // When there are no segments available it means we have only one node. We will use this node to generate
                        // a new one at some distance away from it. First we will store some data that we will need during the entire
                        // procedure.
                        ObjectNode  pivotNode = _objectNodeNetwork.GetFirstNode();
                        Vector3     pivotNodeSurfaceTangent = pivotNode.ObjectSurfaceData.GetSurfaceTangentVector();
                        OrientedBox pivotNodeObjectBox      = pivotNode.ObjectBox;

                        // We will place the new node at some distance away from the first node's face which points
                        // along the calculated tangent vector. We will call this the pivot face.
                        BoxFace pivotBoxFace   = pivotNodeObjectBox.GetBoxFaceMostAlignedWithNormal(pivotNodeSurfaceTangent);
                        Plane   pivotFacePlane = pivotNodeObjectBox.GetBoxFacePlane(pivotBoxFace);

                        // Generate the data for the new node in the same position as the first node.
                        // Note: Although the same position is used, the rotation and scale will differ and they will
                        //       be established by 'CalculateMatrixObjectBoxPair'.
                        MatrixObjectBoxPair matrixObjectBoxPair = CalculateMatrixObjectBoxPair(brushElementIndex, pivotNode.ObjectSurfaceData);
                        OrientedBox         objectBox           = matrixObjectBoxPair.ObjectBox;

                        // At this point we have to start moving the generated object box to its new positino along the
                        // tangent vector. We will do this by calculating the furthest box point which lies behind the
                        // pivot plane and then move the box so that this point resides on that plane. We will call this
                        // the pivot point.
                        // Note: We will perform a safety check to see if this point could not be calculated and use the
                        //       closest point in front if necessary. However, this check should not be necessary. Because
                        //       we are placing te object box in the center of the previous box, we can be usre that there
                        //       will always be a point which lies behind the pivot plane.
                        Vector3        objectBoxPivotPoint;
                        List <Vector3> objectBoxCornerPoints = objectBox.GetCornerPoints();
                        if (!pivotFacePlane.GetFurthestPointBehind(objectBoxCornerPoints, out objectBoxPivotPoint) &&
                            !pivotFacePlane.GetClosestPointInFront(objectBoxCornerPoints, out objectBoxPivotPoint))
                        {
                            continue;
                        }

                        // Project the pivot point onto the pivot plane. We will also store a vector which goes from the
                        // original pivot point to the box center. This will allow us to maintain the relationship between
                        // the projected pivot point and the box center so that the center can be adjusted accordingly.
                        Vector3 fromPivotPointToCenter = objectBox.Center - objectBoxPivotPoint;
                        Vector3 projectedPivotPoint    = pivotFacePlane.ProjectPoint(objectBoxPivotPoint);

                        // Adjust the center using the projected pivot point and also take the distance between objects into account
                        objectBox.Center = projectedPivotPoint + fromPivotPointToCenter + pivotNodeSurfaceTangent * _workingBrush.DistanceBetweenObjects;

                        // Generate the object surface data at the current box position.
                        // Note: This is the step which can actually cause objects to intersect a little bit. The surface data is
                        //       calculated by projecting along the brush circle plane normal. If we are placing objects on a terrain
                        //       and the center of the circle lies somewhere at the base of the terrain where the normal points straight
                        //       up, but the center of the box resides somewhere on a clif, the new center might move the box closer
                        //       or even further away from the pivot node. This however, should not be a problem especially if the distance
                        //       between objects is not 0.
                        ObjectSurfaceData objectSurfaceData = CalculateObjectSurfaceData(objectBox.Center);
                        bool passesSlopeTest = DoesObjectSurfacePassSlopeTest(objectSurfaceData, brushElement);

                        // Now we need to adjust the orientation and center of the box. If the calculated center
                        // lies outside the brush circle, we will ignore this node.
                        AdjustObjectBoxRotationOnSurface(objectBox, objectSurfaceData, brushElement);
                        AdjustObjectBoxCenterToSitOnSurface(objectBox, objectSurfaceData, brushElement);
                        if (!_workingBrushCircle.ContainsPoint(_workingBrushCircle.Plane.ProjectPoint(objectBox.Center)))
                        {
                            continue;
                        }

                        // Recalculate the object matrix using the new box data
                        TransformMatrix objectMatrix = matrixObjectBoxPair.ObjectMatrix;
                        objectMatrix.Rotation    = objectBox.Rotation;
                        objectMatrix.Translation = ObjectPositionCalculator.CalculateObjectHierarchyPosition(brushElement.Prefab, objectBox.Center, objectMatrix.Scale, objectBox.Rotation);

                        // We have been modifying the matrix and box data independently so we will ensure that the box uses the latest data
                        OrientedBox finalBox = new OrientedBox(objectBox);
                        finalBox.SetTransformMatrix(objectMatrix);

                        // If the slope test passed, we will calculate an object placement data instance. Otherwise, we will just insert a new node.
                        if (passesSlopeTest && DoesBoxPassObjectIntersectionTest(finalBox, brushElement.Prefab.UnityPrefab, objectMatrix))
                        {
                            objectPlacementDataInstances.Add(new ObjectPlacementData(objectMatrix, brushElement.Prefab, brushElement.MustEmbedInSurface));
                        }
                        _objectNodeNetwork.InsertAfterNode(objectBox, objectSurfaceData, 0);
                    }
                }
            }

            // Adjust the prefab rotations for the next time the function is called
            if (_elementToNewPrefabRotation.Count != 0)
            {
                foreach (var prefabRotationPair in _elementToNewPrefabRotation)
                {
                    DecorPaintObjectPlacementBrushElement brushElement = prefabRotationPair.Key;
                    if (_elementToCurrentPrefabRotation.ContainsKey(brushElement))
                    {
                        _elementToCurrentPrefabRotation[brushElement] = prefabRotationPair.Value;
                    }
                }
            }

            return(objectPlacementDataInstances);
        }
        private void RenderPrefabPreviewRows()
        {
            for (int prefabIndex = 0; prefabIndex < _filteredPrefabs.Count; ++prefabIndex)
            {
                // Start a new row?
                if (prefabIndex % ViewData.NumberOfPrefabsPerRow == 0)
                {
                    if (prefabIndex != 0)
                    {
                        EditorGUILayout.EndHorizontal();
                    }
                    EditorGUILayout.BeginHorizontal();
                }

                // Render the prefab entry
                Prefab prefab = _filteredPrefabs[prefabIndex];
                var    previewButtonRenderData = new PrefabPreviewButtonRenderData();
                previewButtonRenderData.ExtractFromPrefab(prefab, ViewData.PrefabPreviewScale);

                EditorGUILayout.BeginVertical(GUILayout.Width(previewButtonRenderData.ButtonWidth));

                // Render the prefab preview button
                EditorGUIColor.Push(prefab == _prefabCategory.ActivePrefab ? ViewData.ActivePrefabTint : Color.white);
                if (EditorGUILayoutEx.PrefabPreview(prefab, true, previewButtonRenderData))
                {
                    ObjectPlacementSettings placementSettings = ObjectPlacementSettings.Get();
                    if (placementSettings.ObjectPlacementMode == ObjectPlacementMode.DecorPaint &&
                        placementSettings.DecorPaintObjectPlacementSettings.DecorPaintMode == DecorPaintMode.Brush &&
                        DecorPaintObjectPlacementBrushDatabase.Get().ActiveBrush != null && Event.current.button == (int)MouseButton.Right)
                    {
                        UndoEx.RecordForToolAction(DecorPaintObjectPlacementBrushDatabase.Get().ActiveBrush);
                        DecorPaintObjectPlacementBrushElement brushElement = DecorPaintObjectPlacementBrushDatabase.Get().ActiveBrush.CreateNewElement();
                        brushElement.Prefab = prefab;
                        Octave3DWorldBuilder.ActiveInstance.RepaintAllEditorWindows();
                        Octave3DWorldBuilder.ActiveInstance.Inspector.Repaint();
                    }
                    else
                    if (Octave3DWorldBuilder.ActiveInstance.Inspector.ActiveInspectorGUIIdentifier == InspectorGUIIdentifier.ObjectSelection &&
                        AllShortcutCombos.Instance.ReplacePrefabsForSelectedObjects.IsActive())
                    {
                        ObjectSelection.Get().ReplaceSelectedObjectsWithPrefab(prefab);
                    }
                    else
                    {
                        UndoEx.RecordForToolAction(_prefabCategory);
                        _prefabCategory.SetActivePrefab(prefab);
                    }
                }
                EditorGUIColor.Pop();

                // Render the prefab name labels if necessary
                if (ViewData.ShowPrefabNames)
                {
                    Rect previewRectangle = GUILayoutUtility.GetLastRect();
                    EditorGUILayoutEx.LabelInMiddleOfControlRect(previewRectangle, prefab.Name, previewButtonRenderData.ButtonHeight, GetStyleForPrefabNameLabel());
                }

                // Render the remove prefab button
                if (GUILayout.Button(GetRemovePrefabButtonContent()))
                {
                    UndoEx.RecordForToolAction(_prefabCategory);
                    _prefabCategory.RemoveAndDestroyPrefab(prefab);
                    Octave3DWorldBuilder.ActiveInstance.Inspector.Repaint();
                }

                EditorGUILayout.EndVertical();
            }

            // End the last row (if any)
            if (_filteredPrefabs.Count != 0)
            {
                EditorGUILayout.EndHorizontal();
            }
        }
Example #11
0
        public DecorPaintObjectPlacementBrushElementView(DecorPaintObjectPlacementBrushElement brushElement)
        {
            _brushElement = brushElement;

            SurroundWithBox = true;
        }
 public bool ContainsElement(DecorPaintObjectPlacementBrushElement element)
 {
     return(_elements.Contains(element));
 }
Example #13
0
        protected override void RenderContent()
        {
            RenderNameChangeField();
            RenderRadiusField();
            RenderMaxNumberOfObjectsField();
            RenderDistanceBetweenObjectsField();
            RenderIgnoreObjectsOutsideOfPaintSurfaceToggle();
            RenderDestinationCategoryForElementPrefabsSelectionPopup();

            EditorGUILayout.Separator();
            if (!_brush.IsEmpty)
            {
                Octave3DWorldBuilder.ActiveInstance.ShowGUIHint("Left click on an element's preview to change its parameters. Right click to remove the element from the brush. SHIFT + Right click to toggle the element on/off.");
            }
            Data.ElementsScrollPos = EditorGUILayout.BeginScrollView(Data.ElementsScrollPos, "Box", GUILayout.Height(Data.ElementsScrollViewHeight));
            if (_brush.IsEmpty)
            {
                EditorGUILayout.HelpBox("There are no brush elements available. You can drag and drop prefabs onto this area or " +
                                        "right click on prefabs inside the active category to create new elements. All brush elements will " +
                                        "be shown with a preview inside this area.", UnityEditor.MessageType.None);
            }
            else
            {
                List <DecorPaintObjectPlacementBrushElement> allBrushElements = _brush.GetAllBrushElements();
                for (int brushElemIndex = 0; brushElemIndex < allBrushElements.Count; ++brushElemIndex)
                {
                    if (brushElemIndex % Data.NumElementsPerRow == 0)
                    {
                        if (brushElemIndex != 0)
                        {
                            EditorGUILayout.EndHorizontal();
                        }
                        EditorGUILayout.BeginHorizontal();
                    }

                    DecorPaintObjectPlacementBrushElement brushElement = allBrushElements[brushElemIndex];
                    var previewButtonRenderData = new PrefabPreviewButtonRenderData();
                    previewButtonRenderData.ExtractFromPrefab(brushElement.Prefab, Data.ElementPreviewScale);

                    Color previewTint = brushElement != _brush.ActiveElement ? Color.white : Data.ActiveElementTintColor;
                    if (_brush.ActiveElement != brushElement && !brushElement.IsEnabled)
                    {
                        previewTint = Data.DisabledElementTintColor;
                    }

                    EditorGUILayout.BeginVertical(GUILayout.Width(previewButtonRenderData.ButtonWidth));
                    EditorGUIColor.Push(previewTint);
                    if (EditorGUILayoutEx.PrefabPreview(brushElement.Prefab, true, previewButtonRenderData))
                    {
                        if (Event.current.button == (int)MouseButton.Left)
                        {
                            if (brushElement != _brush.ActiveElement)
                            {
                                UndoEx.RecordForToolAction(_brush);
                                _brush.SetActiveElement(brushElement);
                            }
                        }
                        else
                        if (Event.current.button == (int)MouseButton.Right)
                        {
                            if (!Event.current.shift)
                            {
                                UndoEx.RecordForToolAction(_brush);
                                _brush.RemoveAndDestroyElement(brushElement);
                            }
                            else
                            {
                                UndoEx.RecordForToolAction(brushElement);
                                brushElement.IsEnabled = !brushElement.IsEnabled;
                            }
                        }
                    }
                    EditorGUIColor.Pop();
                    EditorGUILayout.EndVertical();
                }
                EditorGUILayout.EndHorizontal();
            }
            EditorGUILayout.EndScrollView();
            Rect prefabDropRect = GUILayoutUtility.GetLastRect();

            EditorGUILayout.BeginHorizontal();
            var content = new GUIContent();

            content.text    = "Load active category";
            content.tooltip = "Loads all the prefabs from the active category inside the active brush. Note: Prefabs which already exist in the brush, will be ignored.";
            if (GUILayout.Button(content, GUILayout.Width(130.0f)))
            {
                UndoEx.RecordForToolAction(_brush);
                _brush.LoadAllPrefabsInActiveCategory();
            }
            RenderRemoveAllElementsButton();

            content.text    = "Look and feel...";
            content.tooltip = "Opens up a new window which allows you to control the look and feel of the brush elements view.";
            if (GUILayout.Button(content, GUILayout.Width(110.0f)))
            {
                Octave3DWorldBuilder.ActiveInstance.EditorWindowPool.DecorPaintBrushViewLookAndFeelWindow.ViewData = Data;
                Octave3DWorldBuilder.ActiveInstance.EditorWindowPool.DecorPaintBrushViewLookAndFeelWindow.ShowOctave3DWindow();
            }
            EditorGUILayout.EndHorizontal();

            if (_brush.ActiveElement != null)
            {
                EditorGUILayout.Separator();
                _brush.ActiveElement.View.Render();
            }

            PrefabsToDecorPaintBrushEventHandler.Get().DropDest         = PrefabsToDecorPaintBrushEventHandler.DropDestination.Brush;
            PrefabsToDecorPaintBrushEventHandler.Get().DestinationBrush = _brush;
            PrefabsToDecorPaintBrushEventHandler.Get().Handle(Event.current, prefabDropRect);
        }