public static GameObject ReplaceGameObjectHierarchyPrefab(GameObject gameObject, GameObject newPrefab) { if (gameObject == null || PrefabUtility.GetPrefabType(newPrefab) != PrefabType.Prefab) { return(null); } // Store any needed object data OrientedBox originalWorldOrientedBox = gameObject.GetHierarchyWorldOrientedBox(); if (originalWorldOrientedBox.IsInvalid()) { return(null); } int originalObjectLayer = gameObject.layer; bool isObjectStatic = gameObject.isStatic; Transform originalObjectTransform = gameObject.transform; Vector3 worldScale = originalObjectTransform.lossyScale; Quaternion worldRotation = originalObjectTransform.rotation; // Create a new game object from the specified prefab GameObject newObject = PrefabUtility.InstantiatePrefab(newPrefab) as GameObject; if (newObject != null) { // Register the created object for Undo and set its transform data. Also store any significant // data that the original object had before it was destroyed. UndoEx.RegisterCreatedGameObject(newObject); Transform newObjectTransform = newObject.transform; newObjectTransform.localScale = worldScale; newObjectTransform.rotation = worldRotation; newObjectTransform.parent = originalObjectTransform.transform.parent; newObject.SetSelectedHierarchyWireframeHidden(ObjectPlacementSettings.Get().HideWireframeWhenPlacingObjects); newObject.layer = originalObjectLayer; newObject.isStatic = isObjectStatic; // We will adjust the new object's position such that its center is the same as the // one the original object had. This produces better results especially when the new // object is a multi-level object hierarchy. OrientedBox newHierarchyWorldOrientedBox = newObject.GetHierarchyWorldOrientedBox(); if (newHierarchyWorldOrientedBox.IsInvalid()) { GameObject.DestroyImmediate(newObject); return(null); } newObjectTransform.position = ObjectPositionCalculator.CalculateObjectHierarchyPosition(newPrefab, originalWorldOrientedBox.Center, worldScale, worldRotation); // We will also inform the scene that a new object was created so that all the necessary steps can be performed Octave3DScene.Get().RegisterObjectHierarchy(newObject); // Destroy the old object UndoEx.DestroyObjectImmediate(gameObject); return(newObject); } return(null); }
public static void MirrorObjectHierarchyTransform(Plane mirrorPlane, GameObject root, bool mirrorRotation) { Transform rootTransform = root.transform; OrientedBox hierarchyWorldOrientedBox = root.GetHierarchyWorldOrientedBox(); if (!hierarchyWorldOrientedBox.IsValid()) { return; } if (mirrorRotation) { MirrorTransformRotation(mirrorPlane, rootTransform); } Vector3 mirroredCenter = MirrorPosition(mirrorPlane, hierarchyWorldOrientedBox.Center); rootTransform.position = ObjectPositionCalculator.CalculateObjectHierarchyPosition(root, mirroredCenter, rootTransform.lossyScale, rootTransform.rotation); }
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)); }
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); }
public List <ObjectPlacementData> Calculate() { if (_path == null || _path.NumberOfSegments == 0 || !ObjectPlacementGuide.ExistsInScene) { return(new List <ObjectPlacementData>()); } Prefab placementGuidePrefab = ObjectPlacementGuide.Instance.SourcePrefab; Vector3 placementGuideWorldScale = ObjectPlacementGuide.Instance.WorldScale; float objectMissChance = _path.Settings.ManualConstructionSettings.ObjectMissChance; Vector3 yOffsetVector = _path.ExtensionPlane.normal * _path.Settings.ManualConstructionSettings.OffsetAlongGrowDirection; bool allowObjectIntersection = ObjectPlacementSettings.Get().ObjectIntersectionSettings.AllowIntersectionForPathPlacement; Quaternion placementGuideRotation = ObjectPlacementGuide.Instance.WorldRotation; bool rotateObjectsToFollowPath = _path.Settings.ManualConstructionSettings.RotateObjectsToFollowPath; Vector3 pathExtensionPlaneNormal = _path.ExtensionPlane.normal; Vector3 firstSegmentExtensionDir = _path.GetSegmentByIndex(0).ExtensionDirection; Quaternion firstSegmentRotation = Quaternion.LookRotation(firstSegmentExtensionDir, pathExtensionPlaneNormal); bool randomizePrefabs = _path.Settings.ManualConstructionSettings.RandomizePrefabs; PrefabCategory activePrefabCategory = PrefabCategoryDatabase.Get().ActivePrefabCategory; var objectPlacementDataInstances = new List <ObjectPlacementData>(_path.NumberOfSegments * 10); for (int segmentIndex = 0; segmentIndex < _path.NumberOfSegments; ++segmentIndex) { ObjectPlacementBoxStackSegment segment = _path.GetSegmentByIndex(segmentIndex); Quaternion worldRotation = placementGuideRotation; if (rotateObjectsToFollowPath) { // Note: ObjectPlacementPathManualConstructionSession.cs line 451. The design is a wreck. AGAIN :) if ((segmentIndex == 0 && _path.NumberOfSegments > 1 && segment.NumberOfStacks == 1)) { Vector3 dirBetweenStacks = segment.GetStackByIndex(0).BasePosition - _path.GetSegmentByIndex(1).GetStackByIndex(0).BasePosition; dirBetweenStacks.Normalize(); Quaternion segmentRotation = Quaternion.LookRotation(dirBetweenStacks, pathExtensionPlaneNormal); Quaternion fromPlacementGuideRotationToThis = QuaternionExtensions.GetRelativeRotation(placementGuideRotation, segmentRotation); worldRotation = fromPlacementGuideRotationToThis * worldRotation; } else if (segmentIndex != 0) { Quaternion segmentRotation = Quaternion.LookRotation(segment.ExtensionDirection, pathExtensionPlaneNormal); Quaternion fromFirstToThis = QuaternionExtensions.GetRelativeRotation(firstSegmentRotation, segmentRotation); worldRotation = fromFirstToThis * worldRotation; } } for (int stackIndex = 0; stackIndex < segment.NumberOfStacks; ++stackIndex) { ObjectPlacementBoxStack stack = segment.GetStackByIndex(stackIndex); if (stack.IsOverlappedByAnotherStack) { continue; } for (int stackBoxIndex = 0; stackBoxIndex < stack.NumberOfBoxes; ++stackBoxIndex) { ObjectPlacementBox box = stack.GetBoxByIndex(stackBoxIndex); if (box.IsHidden) { continue; } if (ObjectPlacementMissChance.Missed(objectMissChance, ObjectPlacementPathManualConstructionSettings.MinObjectMissChance, ObjectPlacementPathManualConstructionSettings.MaxObjectMissChance)) { continue; } if (!allowObjectIntersection && ObjectQueries.IntersectsAnyObjectsInScene(box.OrientedBox, true)) { continue; } Vector3 worldScale = placementGuideWorldScale; Prefab prefab = placementGuidePrefab; if (randomizePrefabs && activePrefabCategory.NumberOfPrefabs != 0) { int randomPrefabIndex = UnityEngine.Random.Range(0, activePrefabCategory.NumberOfPrefabs); Prefab randomPrefab = activePrefabCategory.GetPrefabByIndex(randomPrefabIndex); if (randomPrefab != null && randomPrefab.UnityPrefab != null) { prefab = activePrefabCategory.GetPrefabByIndex(randomPrefabIndex); worldScale = prefab.UnityPrefab.transform.lossyScale; } } var objectPlacementData = new ObjectPlacementData(); objectPlacementData.WorldPosition = ObjectPositionCalculator.CalculateObjectHierarchyPosition(prefab, box.Center + yOffsetVector, worldScale, worldRotation); objectPlacementData.WorldScale = worldScale; objectPlacementData.WorldRotation = worldRotation; objectPlacementData.Prefab = prefab; objectPlacementDataInstances.Add(objectPlacementData); } } } return(objectPlacementDataInstances); }
public List <ObjectPlacementData> Calculate() { if (_block == null || _block.NumberOfSegments == 0 || !ObjectPlacementGuide.ExistsInScene) { return(new List <ObjectPlacementData>()); } Prefab placementGuidePrefab = ObjectPlacementGuide.Instance.SourcePrefab; Vector3 placementGuideWorldScale = ObjectPlacementGuide.Instance.WorldScale; Quaternion placementGuideWorldRotation = ObjectPlacementGuide.Instance.WorldRotation; float objectMissChance = _block.Settings.ManualConstructionSettings.ObjectMissChance; ObjectRotationRandomizationSettings blockObjectRotationRandomizationSettings = _block.Settings.ManualConstructionSettings.ObjectRotationRandomizationSettings; bool randomizeRotations = blockObjectRotationRandomizationSettings.RandomizeRotation; Vector3 objectOffsetAlongExtensionPlaneNormal = _block.Settings.ManualConstructionSettings.OffsetAlongGrowDirection * _block.ExtensionPlane.normal; bool allowObjectIntersection = ObjectPlacementSettings.Get().ObjectIntersectionSettings.AllowIntersectionForBlockPlacement; bool randomizePrefabs = _block.Settings.ManualConstructionSettings.RandomizePrefabs; PrefabCategory activePrefabCategory = PrefabCategoryDatabase.Get().ActivePrefabCategory; ObjectPlacementBlockProjectionSettings projectionSettings = _block.Settings.BlockProjectionSettings; bool canProject = projectionSettings.ProjectOnSurface && (projectionSettings.CanProjectOnMesh || projectionSettings.CanProjectOnTerrain); var objectPlacementDataInstances = new List <ObjectPlacementData>(_block.NumberOfSegments * 10); for (int segmentIndex = 0; segmentIndex < _block.NumberOfSegments; ++segmentIndex) { ObjectPlacementBoxStackSegment segment = _block.GetSegmentByIndex(segmentIndex); for (int stackIndex = 0; stackIndex < segment.NumberOfStacks; ++stackIndex) { ObjectPlacementBoxStack stack = segment.GetStackByIndex(stackIndex); if (stack.IsOverlappedByAnotherStack || stack.NumberOfBoxes == 0) { continue; } Vector3 projectionOffset = Vector3.zero; Vector3 projectionDirection = Vector3.zero; Quaternion prjAlignRotation = Quaternion.identity; GameObjectRayHit projectionSurfaceHit = null; if (canProject) { Vector3 rayOrigin = stack.GetBoxByIndex(0).Center; Vector3 rayDir = Vector3.zero; if (projectionSettings.ProjectionDirection == ObjectBlockProjectionDir.BlockUp) { rayDir = _block.ExtensionPlane.normal; } else { rayDir = -_block.ExtensionPlane.normal; } projectionDirection = rayDir; Ray ray = new Ray(rayOrigin, rayDir); GameObjectRayHit closestMeshHit = null; GameObjectRayHit closestTerrainHit = null; if (projectionSettings.CanProjectOnMesh) { closestMeshHit = Octave3DScene.Get().RaycastAllMeshClosest(ray); } if (projectionSettings.CanProjectOnTerrain) { closestTerrainHit = Octave3DScene.Get().RaycastAllTerainsClosest(ray); } // Ignore stack if no surface was found and non-projectables must be rejected if (closestMeshHit == null && closestTerrainHit == null) { if (projectionSettings.RejectNonProjectables) { continue; } } else { projectionSurfaceHit = closestMeshHit; if (closestMeshHit == null || (closestTerrainHit != null && closestMeshHit.HitEnter > closestTerrainHit.HitEnter)) { projectionSurfaceHit = closestTerrainHit; } } if (projectionSurfaceHit != null) { ObjectPlacementBox projectionBox = stack.GetBoxByIndex(0); projectionOffset = projectionSurfaceHit.HitPoint - stack.GetBoxByIndex(0).Center; if (projectionOffset.sqrMagnitude > (projectionSurfaceHit.HitPoint - stack.GetBoxByIndex(stack.NumberOfBoxes - 1).Center).sqrMagnitude) { projectionBox = stack.GetBoxByIndex(stack.NumberOfBoxes - 1); projectionOffset = projectionSurfaceHit.HitPoint - projectionBox.Center; } if (!projectionSettings.AlignToSurfaceNormal) { var oobb = projectionBox.OrientedBox; Vector3 oldCenter = oobb.Center; GameObjectExtensions.EmbedObjectBoxInSurface(oobb, projectionDirection, projectionSurfaceHit.HitObject); projectionOffset = oobb.Center - oldCenter; } } } for (int stackBoxIndex = 0; stackBoxIndex < stack.NumberOfBoxes; ++stackBoxIndex) { ObjectPlacementBox box = stack.GetBoxByIndex(stackBoxIndex); if (box.IsHidden) { continue; } if (ObjectPlacementMissChance.Missed(objectMissChance, ObjectPlacementBlockManualConstructionSettings.MinObjectMissChance, ObjectPlacementBlockManualConstructionSettings.MaxObjectMissChance)) { continue; } if (!allowObjectIntersection && ObjectQueries.IntersectsAnyObjectsInScene(box.OrientedBox, true)) { continue; } Quaternion worldRotation = placementGuideWorldRotation; if (randomizeRotations) { worldRotation = ObjectRotationRandomization.GenerateRandomRotationQuaternion(blockObjectRotationRandomizationSettings); } Vector3 worldScale = placementGuideWorldScale; Prefab prefab = placementGuidePrefab; if (randomizePrefabs && activePrefabCategory.NumberOfPrefabs != 0) { int randomPrefabIndex = UnityEngine.Random.Range(0, activePrefabCategory.NumberOfPrefabs); Prefab randomPrefab = activePrefabCategory.GetPrefabByIndex(randomPrefabIndex); if (randomPrefab != null && randomPrefab.UnityPrefab != null) { prefab = activePrefabCategory.GetPrefabByIndex(randomPrefabIndex); worldScale = prefab.UnityPrefab.transform.lossyScale; } } Vector3 boxCenter = box.Center + objectOffsetAlongExtensionPlaneNormal + projectionOffset; if (projectionSurfaceHit != null) { if (projectionSettings.AlignToSurfaceNormal) { worldRotation = AxisAlignment.CalculateRotationQuaternionForAxisAlignment(worldRotation, projectionSettings.AlignmentAxis, projectionSurfaceHit.HitNormal); OrientedBox prefabWorldOOBB = prefab.UnityPrefab.GetHierarchyWorldOrientedBox(); Vector3 oobbSize = prefabWorldOOBB.ScaledSize; int axisIndex = (int)((int)(projectionSettings.AlignmentAxis) * 0.5f); boxCenter = projectionSurfaceHit.HitPoint + projectionSurfaceHit.HitNormal * oobbSize[axisIndex] * 0.5f + (oobbSize[axisIndex] * stackBoxIndex * projectionSurfaceHit.HitNormal); } } var objectPlacementData = new ObjectPlacementData(); objectPlacementData.WorldPosition = ObjectPositionCalculator.CalculateObjectHierarchyPosition(prefab, boxCenter, worldScale, worldRotation); objectPlacementData.WorldScale = worldScale; objectPlacementData.WorldRotation = worldRotation; objectPlacementData.Prefab = prefab; objectPlacementDataInstances.Add(objectPlacementData); } } } return(objectPlacementDataInstances); }
public List <ObjectPlacementData> Calculate() { if (!ValidatePlacementDataCalculationConditions()) { return(new List <ObjectPlacementData>()); } _allowObjectIntersection = ObjectPlacementSettings.Get().ObjectIntersectionSettings.AllowIntersectionForPathPlacement; List <ObjectPlacementBoxStackSegment> allPathSegments = _path.GetAllSegments(); float objectMissChance = _path.Settings.ManualConstructionSettings.ObjectMissChance; bool usingSprites = _path.Settings.TileConnectionSettings.UsesSprites(); var tileConnectionDetector = new ObjectPlacementPathTileConnectionDetector(); var tileConnectionRotationCalculator = new ObjectPlacementPathTileConnectionRotationCalculator(); List <ObjectPlacementPathTileConnectionGridCell> tileConnectionGridCells = tileConnectionDetector.Detect(_path, _tileConnectionXZSize); if (tileConnectionGridCells.Count == 0) { return(new List <ObjectPlacementData>()); } List <Prefab> tileConnectionPrefabsExceptAutofill = PathObjectPlacement.Get().PathSettings.TileConnectionSettings.GetAllTileConnectionPrefabs(true); List <Vector3> tileConnectionWorldScaleValuesExceptAutofill = CalculateWorldScaleForAllTileConnectionPrefabsExceptAutofill(PrefabQueries.GetTransformsForAllPrefabs(tileConnectionPrefabsExceptAutofill), PrefabQueries.GetHierarchyWorldOrientedBoxesForAllPrefabs(tileConnectionPrefabsExceptAutofill)); List <Vector3> tileConnectionOffsetsExceptAutofill = CalculateOffsetsForAllTileConnectionsExceptAutofill(); var objectPlacementDataInstances = new List <ObjectPlacementData>(allPathSegments.Count * 10); foreach (ObjectPlacementPathTileConnectionGridCell tileConnectionGridCell in tileConnectionGridCells) { ObjectPlacementPathTileConnectionType tileConnectionType = tileConnectionGridCell.TileConnectionType; ObjectPlacementBoxStack tileConnectionStack = tileConnectionGridCell.TileConnectionStack; if (tileConnectionStack.IsOverlappedByAnotherStack) { continue; } Prefab tileConnectionPrefab = tileConnectionPrefabsExceptAutofill[(int)tileConnectionType]; Quaternion tileConnectionRotation = tileConnectionRotationCalculator.Calculate(tileConnectionGridCell); Vector3 tileConnectionWorldScale = tileConnectionWorldScaleValuesExceptAutofill[(int)tileConnectionType]; Vector3 tileConnectionOffset = tileConnectionOffsetsExceptAutofill[(int)tileConnectionType]; for (int stackBoxIndex = 0; stackBoxIndex < tileConnectionStack.NumberOfBoxes; ++stackBoxIndex) { ObjectPlacementBox box = tileConnectionStack.GetBoxByIndex(stackBoxIndex); if (box.IsHidden) { continue; } if (ObjectPlacementMissChance.Missed(objectMissChance, ObjectPlacementPathManualConstructionSettings.MinObjectMissChance, ObjectPlacementPathManualConstructionSettings.MaxObjectMissChance)) { continue; } if (!_allowObjectIntersection && ObjectQueries.IntersectsAnyObjectsInScene(box.OrientedBox, true)) { continue; } var objectPlacementData = new ObjectPlacementData(); objectPlacementData.WorldPosition = ObjectPositionCalculator.CalculateObjectHierarchyPosition(tileConnectionPrefab, box.Center, tileConnectionWorldScale, tileConnectionRotation); objectPlacementData.WorldPosition += tileConnectionOffset; objectPlacementData.WorldScale = tileConnectionWorldScale; objectPlacementData.WorldRotation = tileConnectionRotation; objectPlacementData.Prefab = tileConnectionPrefab; objectPlacementDataInstances.Add(objectPlacementData); } // Apply extrusion if necessary if (!usingSprites) { List <OrientedBox> extrusionOrientedBoxes = ObjectPlacementPathTileConnectionExtrusion.GetTileConnectionExtrusionOrientedBoxes(tileConnectionGridCell); foreach (OrientedBox extrusionBox in extrusionOrientedBoxes) { if (ObjectPlacementMissChance.Missed(objectMissChance, ObjectPlacementPathManualConstructionSettings.MinObjectMissChance, ObjectPlacementPathManualConstructionSettings.MaxObjectMissChance)) { continue; } if (!_allowObjectIntersection && ObjectQueries.IntersectsAnyObjectsInScene(extrusionBox, true)) { continue; } var objectPlacementData = new ObjectPlacementData(); objectPlacementData.WorldPosition = ObjectPositionCalculator.CalculateObjectHierarchyPosition(tileConnectionPrefab, extrusionBox.Center, tileConnectionWorldScale, tileConnectionRotation); objectPlacementData.WorldScale = tileConnectionWorldScale; objectPlacementData.WorldRotation = tileConnectionRotation; objectPlacementData.Prefab = tileConnectionPrefab; objectPlacementDataInstances.Add(objectPlacementData); } } } ObjectPlacementPathTileConnectionSettings tileConnectionSettings = _path.Settings.TileConnectionSettings; if (tileConnectionSettings.DoesAutofillTileConnectionHavePrefabAssociated()) { objectPlacementDataInstances.AddRange(GetPlacementDataForAutofillTiles(tileConnectionGridCells[0].ParentGrid)); } return(objectPlacementDataInstances); }
private List <ObjectPlacementData> GetPlacementDataForAutofillTiles(ObjectPlacementPathTileConnectionGrid tileConnectionGrid) { Prefab autofillPrefab = _path.Settings.TileConnectionSettings.GetSettingsForTileConnectionType(ObjectPlacementPathTileConnectionType.Autofill).Prefab; OrientedBox autoFillPrefabWorldOrientedBox = autofillPrefab.UnityPrefab.GetHierarchyWorldOrientedBox(); Vector3 autofillPrefabBoxSize = autoFillPrefabWorldOrientedBox.ScaledSize; Plane extensionPlane = _path.ExtensionPlane; float objectMissChance = _path.Settings.ManualConstructionSettings.ObjectMissChance; bool usingSprites = _path.Settings.TileConnectionSettings.UsesSprites(); var tileConnectionScaleCalculator = new ObjectPlacementPathTileConnectionScaleCalculator(); var tileConnectionYOffsetVectorCalculator = new ObjectPlacementPathTileConnectionYOffsetVectorCalculator(); Vector3 worldScale = tileConnectionScaleCalculator.CalculateWorldScale(_tileConnectionXZSize, autofillPrefabBoxSize, autofillPrefab.UnityPrefab.transform, _path); Vector3 yOffsetVector = tileConnectionYOffsetVectorCalculator.Calculate(_path.Settings.TileConnectionSettings.GetSettingsForTileConnectionType(ObjectPlacementPathTileConnectionType.Autofill), _path); List <ObjectPlacementPathTileConnectionGridCell> autoFillTileConnectionGridCells = tileConnectionGrid.CreateAndReturnAutofillTileConnectionCells(); if (autoFillTileConnectionGridCells.Count == 0) { return(new List <ObjectPlacementData>()); } // Note: All autofill tiles have the same rotation. Quaternion worldRotation = (new ObjectPlacementPathTileConnectionRotationCalculator()).Calculate(autoFillTileConnectionGridCells[0]); var objectPlacementDataInstances = new List <ObjectPlacementData>(autoFillTileConnectionGridCells.Count); foreach (ObjectPlacementPathTileConnectionGridCell autofillGridCell in autoFillTileConnectionGridCells) { if (ObjectPlacementMissChance.Missed(objectMissChance, ObjectPlacementPathManualConstructionSettings.MinObjectMissChance, ObjectPlacementPathManualConstructionSettings.MaxObjectMissChance)) { continue; } Vector3 cellPosition = tileConnectionGrid.CalculateCellPosition(autofillGridCell.CellIndices); Vector3 cellPositionOnPathExtensionPlane = extensionPlane.ProjectPoint(cellPosition); OrientedBox tileOrientedBox = new OrientedBox(autoFillPrefabWorldOrientedBox); tileOrientedBox.Center = cellPositionOnPathExtensionPlane + extensionPlane.normal * (usingSprites ? 0.0f : autofillPrefabBoxSize.y * 0.5f); tileOrientedBox.Rotation = worldRotation; tileOrientedBox.Scale = worldScale; if (!_allowObjectIntersection && ObjectQueries.IntersectsAnyObjectsInScene(tileOrientedBox, true)) { continue; } var objectPlacementData = new ObjectPlacementData(); objectPlacementData.WorldPosition = ObjectPositionCalculator.CalculateObjectHierarchyPosition(autofillPrefab, tileOrientedBox.Center, worldScale, worldRotation) + yOffsetVector; objectPlacementData.WorldScale = worldScale; objectPlacementData.WorldRotation = worldRotation; objectPlacementData.Prefab = autofillPrefab; objectPlacementDataInstances.Add(objectPlacementData); // Apply extrusion if necessary if (!usingSprites) { List <OrientedBox> extrusionOrientedBoxes = ObjectPlacementPathTileConnectionExtrusion.GetTileConnectionExtrusionOrientedBoxes(autofillGridCell); foreach (OrientedBox extrusionBox in extrusionOrientedBoxes) { if (ObjectPlacementMissChance.Missed(objectMissChance, ObjectPlacementPathManualConstructionSettings.MinObjectMissChance, ObjectPlacementPathManualConstructionSettings.MaxObjectMissChance)) { continue; } if (!_allowObjectIntersection && ObjectQueries.IntersectsAnyObjectsInScene(extrusionBox, true)) { continue; } objectPlacementData = new ObjectPlacementData(); objectPlacementData.WorldPosition = ObjectPositionCalculator.CalculateObjectHierarchyPosition(autofillPrefab, extrusionBox.Center, worldScale, worldRotation); objectPlacementData.WorldScale = worldScale; objectPlacementData.WorldRotation = worldRotation; objectPlacementData.Prefab = autofillPrefab; objectPlacementDataInstances.Add(objectPlacementData); } } } return(objectPlacementDataInstances); }
public List <ObjectPlacementData> Calculate() { if (_block == null || _block.NumberOfSegments == 0 || !ObjectPlacementGuide.ExistsInScene) { return(new List <ObjectPlacementData>()); } Prefab placementGuidePrefab = ObjectPlacementGuide.Instance.SourcePrefab; Vector3 placementGuideWorldScale = ObjectPlacementGuide.Instance.WorldScale; Quaternion placementGuideWorldRotation = ObjectPlacementGuide.Instance.WorldRotation; float objectMissChance = _block.Settings.ManualConstructionSettings.ObjectMissChance; ObjectRotationRandomizationSettings blockObjectRotationRandomizationSettings = _block.Settings.ManualConstructionSettings.ObjectRotationRandomizationSettings; bool randomizeRotations = blockObjectRotationRandomizationSettings.RandomizeRotation; Vector3 objectOffsetAlongExtensionPlaneNormal = _block.Settings.ManualConstructionSettings.OffsetAlongGrowDirection * _block.ExtensionPlane.normal; bool allowObjectIntersection = ObjectPlacementSettings.Get().ObjectIntersectionSettings.AllowIntersectionForBlockPlacement; bool randomizePrefabs = _block.Settings.ManualConstructionSettings.RandomizePrefabs; PrefabCategory activePrefabCategory = PrefabCategoryDatabase.Get().ActivePrefabCategory; var objectPlacementDataInstances = new List <ObjectPlacementData>(_block.NumberOfSegments * 10); for (int segmentIndex = 0; segmentIndex < _block.NumberOfSegments; ++segmentIndex) { ObjectPlacementBoxStackSegment segment = _block.GetSegmentByIndex(segmentIndex); for (int stackIndex = 0; stackIndex < segment.NumberOfStacks; ++stackIndex) { ObjectPlacementBoxStack stack = segment.GetStackByIndex(stackIndex); if (stack.IsOverlappedByAnotherStack) { continue; } for (int stackBoxIndex = 0; stackBoxIndex < stack.NumberOfBoxes; ++stackBoxIndex) { ObjectPlacementBox box = stack.GetBoxByIndex(stackBoxIndex); if (box.IsHidden) { continue; } if (ObjectPlacementMissChance.Missed(objectMissChance, ObjectPlacementBlockManualConstructionSettings.MinObjectMissChance, ObjectPlacementBlockManualConstructionSettings.MaxObjectMissChance)) { continue; } if (!allowObjectIntersection && ObjectQueries.IntersectsAnyObjectsInScene(box.OrientedBox, true)) { continue; } Quaternion worldRotation = placementGuideWorldRotation; if (randomizeRotations) { worldRotation = ObjectRotationRandomization.GenerateRandomRotationQuaternion(blockObjectRotationRandomizationSettings); } Vector3 worldScale = placementGuideWorldScale; Prefab prefab = placementGuidePrefab; if (randomizePrefabs && activePrefabCategory.NumberOfPrefabs != 0) { int randomPrefabIndex = UnityEngine.Random.Range(0, activePrefabCategory.NumberOfPrefabs); Prefab randomPrefab = activePrefabCategory.GetPrefabByIndex(randomPrefabIndex); if (randomPrefab != null && randomPrefab.UnityPrefab != null) { prefab = activePrefabCategory.GetPrefabByIndex(randomPrefabIndex); worldScale = prefab.UnityPrefab.transform.lossyScale; } } var objectPlacementData = new ObjectPlacementData(); objectPlacementData.WorldPosition = ObjectPositionCalculator.CalculateObjectHierarchyPosition(prefab, box.Center + objectOffsetAlongExtensionPlaneNormal, worldScale, placementGuideWorldRotation); objectPlacementData.WorldScale = worldScale; objectPlacementData.WorldRotation = worldRotation; objectPlacementData.Prefab = prefab; objectPlacementDataInstances.Add(objectPlacementData); } } } return(objectPlacementDataInstances); }