409 lines
19 KiB
C#
409 lines
19 KiB
C#
#if UNITY_EDITOR
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.Collections.Generic;
|
|
|
|
namespace O3DWB
|
|
{
|
|
public class ObjectPlacementBlockManualConstructionSession
|
|
{
|
|
#region Private Variables
|
|
private ObjectPlacementBlock _block;
|
|
private List<ObjectPlacementBoxStackSegment> _blockSegments;
|
|
private ObjectPlacementExtensionPlane _blockExtensionPlane;
|
|
private ObjectPlacementBlockManualConstructionSettings _manualConstructionSettings;
|
|
private ObjectPlacementBlockPaddingSettings _paddingSettings;
|
|
private ObjectPlacementBlockHeightAdjustmentSettings _heightAdjustmentSettings;
|
|
private ObjectPlacementBlockAutomaticRandomHeightAdjustmentSettings _automaticRandomHeightAdjustmentSettings;
|
|
private ObjectPlacementBlockSubdivisionSettings _subdivisionSettings;
|
|
|
|
private Vector3 _segmentExtensionDirection;
|
|
private Vector3 _segmentCollectionExtensionDirection;
|
|
|
|
private int _currentManualBlockHeight;
|
|
private GameObject _startObject;
|
|
private OrientedBox _startObjectHierarchyWorldOrientedBox;
|
|
|
|
private BlockObjectPlacementDataCalculator _blockObjectPlacementDataCalculator = new BlockObjectPlacementDataCalculator();
|
|
private ObjectPlacementBlockManualHeightAdjuster _manualHeightAdjuster = new ObjectPlacementBlockManualHeightAdjuster();
|
|
private ObjectPlacementBlockAutomaticRandomHeightAdjuster _automaticRandomHeightAdjuster = new ObjectPlacementBlockAutomaticRandomHeightAdjuster();
|
|
private ObjectPlacementBlockSubdivisionApplyOperation _subdivisionApplyOperation = new ObjectPlacementBlockSubdivisionApplyOperation();
|
|
|
|
private bool _isActive = false;
|
|
#endregion
|
|
|
|
#region Private Properties
|
|
private ObjectPlacementBoxStackSegment FirstSegment { get { return _blockSegments.Count != 0 ? _blockSegments[0] : null; } }
|
|
private ObjectPlacementBoxStackSegment LastSegment { get { return _blockSegments.Count != 0 ? _blockSegments[_blockSegments.Count - 1] : null; } }
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
public bool IsActive { get { return _isActive; } }
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
public void SetData(ObjectPlacementBlockManualConstructionSessionData sessionData)
|
|
{
|
|
if (!_isActive)
|
|
{
|
|
_block = sessionData.Block;
|
|
_blockSegments = sessionData.BlockSegments;
|
|
_blockExtensionPlane = sessionData.BlockExtensionPlane;
|
|
|
|
_startObject = sessionData.StartObject;
|
|
_startObjectHierarchyWorldOrientedBox = _startObject.GetHierarchyWorldOrientedBox();
|
|
_blockObjectPlacementDataCalculator.Block = _block;
|
|
|
|
_manualConstructionSettings = _block.Settings.ManualConstructionSettings;
|
|
_heightAdjustmentSettings = _manualConstructionSettings.HeightAdjustmentSettings;
|
|
_automaticRandomHeightAdjustmentSettings = _heightAdjustmentSettings.AutomaticRandomHeightAdjustmentSettings;
|
|
_paddingSettings = _manualConstructionSettings.PaddingSettings;
|
|
_subdivisionSettings = _manualConstructionSettings.SubdivisionSettings;
|
|
}
|
|
}
|
|
|
|
public void Begin()
|
|
{
|
|
if(CanBegin())
|
|
{
|
|
_isActive = true;
|
|
_currentManualBlockHeight = 1;
|
|
_blockSegments.Clear();
|
|
|
|
_segmentExtensionDirection = _blockExtensionPlane.RightAxis;
|
|
_segmentCollectionExtensionDirection = _blockExtensionPlane.LookAxis;
|
|
|
|
CreateFirstSegmentInBlock();
|
|
}
|
|
}
|
|
|
|
public List<ObjectPlacementData> End()
|
|
{
|
|
if (_isActive)
|
|
{
|
|
_isActive = false;
|
|
return _blockObjectPlacementDataCalculator.Calculate();
|
|
}
|
|
|
|
return new List<ObjectPlacementData>();
|
|
}
|
|
|
|
public void Cancel()
|
|
{
|
|
_isActive = false;
|
|
}
|
|
|
|
public void ManualRaiseBlock()
|
|
{
|
|
if (CanManualRaiseOrLowerBlock())
|
|
{
|
|
_currentManualBlockHeight = _manualHeightAdjuster.Raise(_block, _currentManualBlockHeight);
|
|
AdjustCornerExclusionHideFlagsForEntireBlock();
|
|
if (_subdivisionSettings.UseSubdivision) _subdivisionApplyOperation.ApplySubdivisionToEntireBlock(_blockSegments, _subdivisionSettings);
|
|
}
|
|
}
|
|
|
|
public void ManualLowerBlock()
|
|
{
|
|
if (CanManualRaiseOrLowerBlock())
|
|
{
|
|
_currentManualBlockHeight = _manualHeightAdjuster.Lower(_block, _currentManualBlockHeight);
|
|
AdjustCornerExclusionHideFlagsForEntireBlock();
|
|
if (_subdivisionSettings.UseSubdivision) _subdivisionApplyOperation.ApplySubdivisionToEntireBlock(_blockSegments, _subdivisionSettings);
|
|
}
|
|
}
|
|
|
|
public void UpdateForMouseMoveEvent()
|
|
{
|
|
if (_isActive) ExtendOrShrinkBlockAlongExtensionPlane();
|
|
}
|
|
|
|
public void OnExcludeCornersSettingsChanged()
|
|
{
|
|
if(_isActive)
|
|
{
|
|
AdjustCornerExclusionHideFlagsForEntireBlock();
|
|
SceneView.RepaintAll();
|
|
}
|
|
}
|
|
|
|
public void OnPaddingSettingsChanged()
|
|
{
|
|
if (_isActive)
|
|
{
|
|
ObjectPlacementBoxStackSegmentActions.SetPaddingForSegments(_blockSegments, _paddingSettings.PaddingAlongExtensionPlane, _paddingSettings.PaddingAlongGrowDirection);
|
|
for (int segmentIndex = 1; segmentIndex < _blockSegments.Count; ++segmentIndex)
|
|
{
|
|
AppendSegmentToSegment(_blockSegments[segmentIndex], _blockSegments[segmentIndex - 1]);
|
|
}
|
|
|
|
SceneView.RepaintAll();
|
|
}
|
|
}
|
|
|
|
public void OnHeightAdjustmentModeChanged()
|
|
{
|
|
if (_isActive)
|
|
{
|
|
AdjustHeightForStackRangeInAllSegments(0);
|
|
if (_manualConstructionSettings.ExcludeCorners) AdjustCornerExclusionHideFlagsForEntireBlock();
|
|
SceneView.RepaintAll();
|
|
}
|
|
}
|
|
|
|
public void OnAutomaticRandomHeightAdjustmentSettingsChanged()
|
|
{
|
|
if (_isActive)
|
|
{
|
|
AdjustHeightForStackRangeInAllSegments(0);
|
|
if (_manualConstructionSettings.ExcludeCorners) AdjustCornerExclusionHideFlagsForEntireBlock();
|
|
SceneView.RepaintAll();
|
|
}
|
|
}
|
|
|
|
public void OnSubdivisionSettingsChanged()
|
|
{
|
|
if(_isActive)
|
|
{
|
|
if (_subdivisionSettings.UseSubdivision) _subdivisionApplyOperation.ApplySubdivisionToEntireBlock(_blockSegments, _subdivisionSettings);
|
|
else ObjectPlacementBoxStackSegmentActions.ClearHideFlagsForAllStacksInSegments(_blockSegments, ObjectPlacementBoxHideFlags.BlockApplySubdivisions);
|
|
SceneView.RepaintAll();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
private bool CanBegin()
|
|
{
|
|
return !_isActive && IsSessionDataReady();
|
|
}
|
|
|
|
private bool CanExcludeCorners()
|
|
{
|
|
return _manualConstructionSettings.ExcludeCorners && _blockSegments.Count >= 3 && FirstSegment.NumberOfStacks >= 3;
|
|
}
|
|
|
|
private bool CanManualRaiseOrLowerBlock()
|
|
{
|
|
return _isActive && _heightAdjustmentSettings.HeightAdjustmentMode == ObjectPlacementBlockHeightAdjustmentMode.Manual;
|
|
}
|
|
|
|
private bool IsSessionDataReady()
|
|
{
|
|
bool isReady = (_block != null && _blockSegments != null && _startObject != null && _blockExtensionPlane != null);
|
|
if (!isReady) return false;
|
|
|
|
OrientedBox hierarchyWorldOrientedBox = _startObject.GetHierarchyWorldOrientedBox();
|
|
float absSizeRight = hierarchyWorldOrientedBox.GetRotatedAndScaledSizeAlongDirection(_blockExtensionPlane.RightAxis);
|
|
float absSizeLook = hierarchyWorldOrientedBox.GetRotatedAndScaledSizeAlongDirection(_blockExtensionPlane.LookAxis);
|
|
if (absSizeRight < 1e-4f || absSizeLook < 1e-4f)
|
|
{
|
|
Debug.LogWarning("Can not begin block construction because the object has a 0 size component along the extention plane axes.");
|
|
isReady = false;
|
|
}
|
|
|
|
return isReady;
|
|
}
|
|
|
|
private void CreateFirstSegmentInBlock()
|
|
{
|
|
ObjectPlacementBoxStackSegment firstSegmentInBlock = CreateNewSegment();
|
|
firstSegmentInBlock.SetExtensionDirection(_segmentExtensionDirection);
|
|
firstSegmentInBlock.SetFirstStackBasePosition(_startObjectHierarchyWorldOrientedBox.Center);
|
|
}
|
|
|
|
private ObjectPlacementBoxStackSegment CreateNewSegment()
|
|
{
|
|
var newSegment = new ObjectPlacementBoxStackSegment();
|
|
newSegment.SetExtensionDirection(_blockExtensionPlane.LookAxis);
|
|
newSegment.SetGrowAxis(_blockExtensionPlane.UpAxis);
|
|
newSegment.SetRotationForAllStacks(_startObjectHierarchyWorldOrientedBox.Rotation);
|
|
newSegment.SetBoxSizeForAllStacks(_startObjectHierarchyWorldOrientedBox.ScaledSize);
|
|
newSegment.SetPaddingAlongStackGrowDirection(_paddingSettings.PaddingAlongGrowDirection);
|
|
newSegment.SetPaddingAlongExtensionDirection(_paddingSettings.PaddingAlongExtensionPlane);
|
|
|
|
_blockSegments.Add(newSegment);
|
|
|
|
return newSegment;
|
|
}
|
|
|
|
private void ExtendOrShrinkBlockAlongExtensionPlane()
|
|
{
|
|
// Construct a new extension plane in order to take into account the block's Y offset
|
|
Plane extensionPlane = _blockExtensionPlane.Plane;
|
|
Vector3 pointOnBlockExtensionPlane = _blockExtensionPlane.PlaneQuad.Center;
|
|
pointOnBlockExtensionPlane += extensionPlane.normal * _manualConstructionSettings.OffsetAlongGrowDirection;
|
|
extensionPlane = new Plane(extensionPlane.normal, pointOnBlockExtensionPlane);
|
|
|
|
Vector3 extensionPlaneIntersectionPoint;
|
|
if (MouseCursor.Instance.IntersectsPlane(extensionPlane, out extensionPlaneIntersectionPoint))
|
|
{
|
|
Vector3 toIntersectionPoint = extensionPlaneIntersectionPoint - _blockExtensionPlane.Plane.ProjectPoint(FirstSegment.FirstStackBasePosition);
|
|
if (!FirstSegment.ExtensionDirection.IsPointingInSameGeneralDirection(toIntersectionPoint))
|
|
{
|
|
ObjectPlacementBoxStackSegmentActions.ReverseExtensionDirectionForSegments(_blockSegments);
|
|
_segmentExtensionDirection = FirstSegment.ExtensionDirection;
|
|
}
|
|
|
|
if (!toIntersectionPoint.IsPointingInSameGeneralDirection(_segmentCollectionExtensionDirection))
|
|
{
|
|
RemoveLastNumberOfSegments(_blockSegments.Count - 1);
|
|
_segmentCollectionExtensionDirection *= -1.0f;
|
|
}
|
|
|
|
// Calculate the number of stacks for all segments
|
|
float adjacentSideLength = FirstSegment.ExtensionDirection.GetAbsDot(toIntersectionPoint);
|
|
float numberOfStacks = adjacentSideLength / (FirstSegment.GetBoxSizeAlongNormalizedDirection(_segmentExtensionDirection) + _paddingSettings.PaddingAlongExtensionPlane);
|
|
int integerNumberOfStacks = (int)numberOfStacks + 1;
|
|
|
|
// Calculate the number of segments
|
|
adjacentSideLength = toIntersectionPoint.GetAbsDot(_segmentCollectionExtensionDirection);
|
|
float numberOfSegments = adjacentSideLength / (FirstSegment.GetBoxSizeAlongNormalizedDirection(_segmentCollectionExtensionDirection) + _paddingSettings.PaddingAlongExtensionPlane);
|
|
int integerNumberOfSegments = (int)numberOfSegments + 1;
|
|
|
|
int newNumberOfStacksInSegments = integerNumberOfStacks;
|
|
int newNumberOfSegmentsInBlock = integerNumberOfSegments;
|
|
if (AllShortcutCombos.Instance.Enable1To1RatioBlockAdjustment.IsActive())
|
|
{
|
|
int min = Mathf.Min(newNumberOfSegmentsInBlock, newNumberOfStacksInSegments);
|
|
newNumberOfStacksInSegments = min;
|
|
newNumberOfSegmentsInBlock = min;
|
|
}
|
|
|
|
if (_manualConstructionSettings.ContrainSize)
|
|
{
|
|
newNumberOfStacksInSegments = Mathf.Min(newNumberOfStacksInSegments, _manualConstructionSettings.MaxSize);
|
|
newNumberOfSegmentsInBlock = Mathf.Min(newNumberOfSegmentsInBlock, _manualConstructionSettings.MaxSize);
|
|
}
|
|
|
|
// Append or remove stacks from the segments based on the new number of stacks
|
|
int deltaNumberOfStacks = newNumberOfStacksInSegments - FirstSegment.NumberOfStacks;
|
|
AppendOrRemoveStacksToAllSegments(deltaNumberOfStacks);
|
|
|
|
// Append or remove segments from the block based on the new number of segments
|
|
int deltaNumberOfSegments = newNumberOfSegmentsInBlock - _blockSegments.Count;
|
|
AppendOrRemoveSegmentsToBlock(deltaNumberOfSegments);
|
|
|
|
// Apply any subdivision if necessary and adjust the corner exclusion hide flags. We need to do this every
|
|
// time the block is updated because when the block shrinks or grows, its structure is affected and so is
|
|
// the way in which subdivision and corner exclusion is applied.
|
|
if (_subdivisionSettings.UseSubdivision) _subdivisionApplyOperation.ApplySubdivisionToEntireBlock(_blockSegments, _subdivisionSettings);
|
|
AdjustCornerExclusionHideFlagsForEntireBlock();
|
|
|
|
SceneView.RepaintAll();
|
|
}
|
|
}
|
|
|
|
private void AppendOrRemoveStacksToAllSegments(int deltaNumberOfStacks)
|
|
{
|
|
if (deltaNumberOfStacks > 0)
|
|
{
|
|
if (CanExcludeCorners())
|
|
{
|
|
LastSegment.GetStackByIndex(0).ClearHideFlagForAllBoxes(ObjectPlacementBoxHideFlags.BlockExcludeCorners);
|
|
LastSegment.GetStackByIndex(LastSegment.NumberOfStacks - 1).ClearHideFlagForAllBoxes(ObjectPlacementBoxHideFlags.BlockExcludeCorners);
|
|
}
|
|
|
|
int currentNumberOfStacksInSegments = FirstSegment.NumberOfStacks;
|
|
ObjectPlacementBoxStackSegmentActions.ExtendSegmentsByAmount(_blockSegments, deltaNumberOfStacks);
|
|
AdjustHeightForStackRangeInAllSegments(currentNumberOfStacksInSegments);
|
|
}
|
|
else
|
|
ObjectPlacementBoxStackSegmentActions.ShrinkSegmentsByAmount(_blockSegments, Mathf.Abs(deltaNumberOfStacks));
|
|
}
|
|
|
|
private void AppendOrRemoveSegmentsToBlock(int deltaNumberOfSegments)
|
|
{
|
|
if (deltaNumberOfSegments > 0)
|
|
{
|
|
// New segments will be added, so the last segment must have its exclude corners hide flags cleared.
|
|
if (CanExcludeCorners())
|
|
{
|
|
LastSegment.GetStackByIndex(0).ClearHideFlagForAllBoxes(ObjectPlacementBoxHideFlags.BlockExcludeCorners);
|
|
LastSegment.GetStackByIndex(LastSegment.NumberOfStacks - 1).ClearHideFlagForAllBoxes(ObjectPlacementBoxHideFlags.BlockExcludeCorners);
|
|
}
|
|
|
|
int currentNumberOfSegmentsInBlock = _blockSegments.Count;
|
|
AppendSegments(deltaNumberOfSegments);
|
|
AdjustHeightForSegmentRange(currentNumberOfSegmentsInBlock);
|
|
}
|
|
else
|
|
{
|
|
int absDelta = Mathf.Abs(deltaNumberOfSegments);
|
|
if (absDelta == _blockSegments.Count) --absDelta; // We always want at least one segment
|
|
RemoveLastNumberOfSegments(absDelta);
|
|
}
|
|
}
|
|
|
|
private void AdjustCornerExclusionHideFlagsForEntireBlock()
|
|
{
|
|
ClearCornerExclusionHideFlagsInFirstAndLastSegments();
|
|
if (CanExcludeCorners())
|
|
{
|
|
AdjustCornerExlcusionHideFlagsInSegment(FirstSegment);
|
|
AdjustCornerExlcusionHideFlagsInSegment(LastSegment);
|
|
}
|
|
}
|
|
|
|
private void ClearCornerExclusionHideFlagsInFirstAndLastSegments()
|
|
{
|
|
FirstSegment.ClearHideFlagInAllStacks(ObjectPlacementBoxHideFlags.BlockExcludeCorners);
|
|
LastSegment.ClearHideFlagInAllStacks(ObjectPlacementBoxHideFlags.BlockExcludeCorners);
|
|
}
|
|
|
|
private void AdjustCornerExlcusionHideFlagsInSegment(ObjectPlacementBoxStackSegment segment)
|
|
{
|
|
segment.GetStackByIndex(0).SetHideFlagForAllBoxes(ObjectPlacementBoxHideFlags.BlockExcludeCorners);
|
|
segment.GetStackByIndex(LastSegment.NumberOfStacks - 1).SetHideFlagForAllBoxes(ObjectPlacementBoxHideFlags.BlockExcludeCorners);
|
|
}
|
|
|
|
private void RemoveLastNumberOfSegments(int numberOfSegmentsToRemove)
|
|
{
|
|
int removedSegments = 0;
|
|
while(removedSegments < numberOfSegmentsToRemove)
|
|
{
|
|
_blockSegments.RemoveAt(_blockSegments.Count - 1);
|
|
++removedSegments;
|
|
}
|
|
}
|
|
|
|
private void AppendSegments(int numberOfSegmentsToAppend)
|
|
{
|
|
for(int segmentIndex = 0; segmentIndex < numberOfSegmentsToAppend; ++segmentIndex)
|
|
{
|
|
ObjectPlacementBoxStackSegment lastSegmentInBlock = LastSegment;
|
|
ObjectPlacementBoxStackSegment segmentToAppend = CreateNewSegment();
|
|
segmentToAppend.SetExtensionDirection(_segmentExtensionDirection);
|
|
segmentToAppend.Extend(FirstSegment.NumberOfStacks);
|
|
|
|
AppendSegmentToSegment(segmentToAppend, lastSegmentInBlock);
|
|
}
|
|
}
|
|
|
|
private void AppendSegmentToSegment(ObjectPlacementBoxStackSegment sourceSegment, ObjectPlacementBoxStackSegment destinationSegment)
|
|
{
|
|
sourceSegment.ConnectFirstStackToFirstStackInSegment(destinationSegment, GetSegmentConnectionOffsetForAppendOperation());
|
|
}
|
|
|
|
private Vector3 GetSegmentConnectionOffsetForAppendOperation()
|
|
{
|
|
return _segmentCollectionExtensionDirection * (FirstSegment.GetBoxSizeAlongNormalizedDirection(_segmentCollectionExtensionDirection) + _paddingSettings.PaddingAlongExtensionPlane);
|
|
}
|
|
|
|
private void AdjustHeightForStackRangeInAllSegments(int indexOfFirstStackToAdjust)
|
|
{
|
|
ObjectPlacementBlockHeightAdjustmentMode heightAdjustmentMode = _heightAdjustmentSettings.HeightAdjustmentMode;
|
|
if (heightAdjustmentMode == ObjectPlacementBlockHeightAdjustmentMode.Manual) _manualHeightAdjuster.AdjustHeightForSegments(_blockSegments, indexOfFirstStackToAdjust, _currentManualBlockHeight);
|
|
else _automaticRandomHeightAdjuster.AdjustHeightForSegments(_blockSegments, 0, indexOfFirstStackToAdjust, _automaticRandomHeightAdjustmentSettings);
|
|
}
|
|
|
|
private void AdjustHeightForSegmentRange(int indexOfFirstSegmentToAdjust)
|
|
{
|
|
ObjectPlacementBlockHeightAdjustmentMode heightAdjustmentMode = _heightAdjustmentSettings.HeightAdjustmentMode;
|
|
if (heightAdjustmentMode == ObjectPlacementBlockHeightAdjustmentMode.Manual) _manualHeightAdjuster.AdjustHeightForSegments(_blockSegments, _currentManualBlockHeight);
|
|
else _automaticRandomHeightAdjuster.AdjustHeightForSegments(_blockSegments, indexOfFirstSegmentToAdjust, 0, _automaticRandomHeightAdjustmentSettings);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|
|
#endif |