Merge branch 'GizmoSnaping' of https://github.com/NoriteSC/FlaxEngineFork into NoriteSC-GizmoSnaping

This commit is contained in:
Wojtek Figat
2024-02-24 07:57:27 +01:00
8 changed files with 543 additions and 262 deletions

View File

@@ -269,9 +269,9 @@ namespace FlaxEditor.Gizmo
protected override int SelectionCount => _selectionParents.Count;
/// <inheritdoc />
protected override Transform GetSelectedObject(int index)
protected override SceneGraphNode GetSelectedObject(int index)
{
return _selectionParents[index].Transform;
return _selectionParents[index];
}
/// <inheritdoc />

View File

@@ -72,139 +72,160 @@ namespace FlaxEditor.Gizmo
const float gizmoModelsScale2RealGizmoSize = 0.075f;
Mesh sphereMesh, cubeMesh;
cubeMesh = _modelCube.LODs[0].Meshes[0];
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3);
Matrix.Multiply(ref m3, ref world, out m1);
mx1 = m1;
mx1.M41 += 0.05f;
switch (_activeMode)
{
case Mode.Translate:
{
if (!_modelTranslationAxis || !_modelTranslationAxis.IsLoaded || !_modelCube || !_modelCube.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded)
break;
var transAxisMesh = _modelTranslationAxis.LODs[0].Meshes[0];
cubeMesh = _modelCube.LODs[0].Meshes[0];
sphereMesh = _modelSphere.LODs[0].Meshes[0];
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3);
Matrix.Multiply(ref m3, ref world, out m1);
mx1 = m1;
mx1.M41 += 0.05f;
case Mode.Translate:
{
if (!_modelTranslationAxis || !_modelTranslationAxis.IsLoaded || !_modelCube || !_modelCube.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded)
break;
var transAxisMesh = _modelTranslationAxis.LODs[0].Meshes[0];
// X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
sphereMesh = _modelSphere.LODs[0].Meshes[0];
// Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
// X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
// Z axis
Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
// Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
// XY plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
// Z axis
Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
// ZX plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
// XY plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
// YZ plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
// ZX plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
// Center sphere
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3);
// YZ plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
break;
// Center sphere
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3);
break;
}
case Mode.Rotate:
{
if (!_modelRotationAxis || !_modelRotationAxis.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded)
break;
var rotationAxisMesh = _modelRotationAxis.LODs[0].Meshes[0];
sphereMesh = _modelSphere.LODs[0].Meshes[0];
// X axis
Matrix.RotationZ(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
rotationAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
// Y axis
rotationAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m1);
// Z axis
Matrix.RotationX(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
rotationAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
// Center box
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3);
break;
}
case Mode.Scale:
{
if (!_modelScaleAxis || !_modelScaleAxis.IsLoaded || !_modelCube || !_modelCube.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded)
break;
var scaleAxisMesh = _modelScaleAxis.LODs[0].Meshes[0];
cubeMesh = _modelCube.LODs[0].Meshes[0];
sphereMesh = _modelSphere.LODs[0].Meshes[0];
// X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref mx1, out m3);
scaleAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
// Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
scaleAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
// Z axis
Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
scaleAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
// XY plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
// ZX plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
// YZ plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
// Center box
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3);
break;
}
}
case Mode.Rotate:
if (verts != null && selectedvert != -1)
{
if (!_modelRotationAxis || !_modelRotationAxis.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded)
break;
var rotationAxisMesh = _modelRotationAxis.LODs[0].Meshes[0];
sphereMesh = _modelSphere.LODs[0].Meshes[0];
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3);
Matrix.Multiply(ref m3, ref world, out m1);
mx1 = m1;
mx1.M41 += 0.05f;
Transform t = thisTransform;
Vector3 selected = ((verts[selectedvert].Position * t.Orientation) * t.Scale) + t.Translation;
Matrix matrix = new Transform(selected, t.Orientation, new Float3(gizmoModelsScale2RealGizmoSize)).GetWorld();
cubeMesh.Draw(ref renderContext, _materialSphere, ref matrix);
// X axis
Matrix.RotationZ(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
rotationAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
if (otherVerts != null && otherSelectedvert != -1)
{
t = otherTransform;
Vector3 other = ((otherVerts[otherSelectedvert].Position * t.Orientation) * t.Scale) + t.Translation;
matrix = new Transform(selected, t.Orientation, new Float3(gizmoModelsScale2RealGizmoSize)).GetWorld();
cubeMesh.Draw(ref renderContext, _materialSphere, ref matrix);
DebugDraw.DrawLine(other, selected, Color.Aqua);
}
// Y axis
rotationAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m1);
//t = otherTransform;
//for (int i = 0; i < otherVerts.Length; i++)
//{
// Matrix matrix = new Transform(((otherVerts[i].Position * t.Orientation) * t.Scale) + t.Translation, t.Orientation, new Float3(gizmoModelsScale2RealGizmoSize)).GetWorld();
// cubeMesh.Draw(ref renderContext, i == otherSelectedvert ? _materialAxisFocus : _materialSphere, ref matrix);
//}
// Z axis
Matrix.RotationX(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
rotationAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
//t = GetSelectedObject(0).Transform;
// Center box
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3);
break;
}
case Mode.Scale:
{
if (!_modelScaleAxis || !_modelScaleAxis.IsLoaded || !_modelCube || !_modelCube.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded)
break;
var scaleAxisMesh = _modelScaleAxis.LODs[0].Meshes[0];
cubeMesh = _modelCube.LODs[0].Meshes[0];
sphereMesh = _modelSphere.LODs[0].Meshes[0];
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3);
Matrix.Multiply(ref m3, ref world, out m1);
mx1 = m1;
mx1.M41 -= 0.05f;
// X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref mx1, out m3);
scaleAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
// Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
scaleAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
// Z axis
Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
scaleAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
// XY plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
// ZX plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
// YZ plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
// Center box
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3);
break;
}
}
}
}

View File

@@ -27,7 +27,7 @@ namespace FlaxEditor.Gizmo
// Get center point
Vector3 center = Vector3.Zero;
for (int i = 0; i < count; i++)
center += GetSelectedObject(i).Translation;
center += GetSelectedObject(i).Transform.Translation;
// Return arithmetic average or whatever it means
return center / count;
@@ -47,6 +47,11 @@ namespace FlaxEditor.Gizmo
private void SelectAxis()
{
if (Owner.IsControlDown)
{
}
// Get mouse ray
Ray ray = Owner.MouseRay;

View File

@@ -12,42 +12,42 @@ namespace FlaxEditor.Gizmo
/// <summary>
/// None.
/// </summary>
None,
None = 0,
/// <summary>
/// The X axis.
/// </summary>
X,
X = 1,
/// <summary>
/// The Y axis.
/// </summary>
Y,
Y = 2,
/// <summary>
/// The Z axis.
/// </summary>
Z,
Z = 4,
/// <summary>
/// The XY plane.
/// </summary>
XY,
XY = X|Y,
/// <summary>
/// The ZX plane.
/// </summary>
ZX,
ZX = Z|X,
/// <summary>
/// The YZ plane.
/// </summary>
YZ,
YZ = Y|Z,
/// <summary>
/// The center point.
/// </summary>
Center,
Center = 8,
};
/// <summary>

View File

@@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using FlaxEditor.SceneGraph;
using FlaxEngine;
using FlaxEditor.Surface;
namespace FlaxEditor.Gizmo
{
@@ -108,7 +109,7 @@ namespace FlaxEditor.Gizmo
_startTransforms.Capacity = Mathf.NextPowerOfTwo(count);
for (var i = 0; i < count; i++)
{
_startTransforms.Add(GetSelectedObject(i));
_startTransforms.Add(GetSelectedObject(i).Transform);
}
GetSelectedObjectsBounds(out _startBounds, out _navigationDirty);
@@ -137,16 +138,22 @@ namespace FlaxEditor.Gizmo
{
switch (_activePivotType)
{
case PivotType.ObjectCenter:
if (SelectionCount > 0)
Position = GetSelectedObject(0).Translation;
break;
case PivotType.SelectionCenter:
Position = GetSelectionCenter();
break;
case PivotType.WorldOrigin:
Position = Vector3.Zero;
break;
case PivotType.ObjectCenter:
if (SelectionCount > 0)
Position = GetSelectedObject(0).Transform.Translation;
break;
case PivotType.SelectionCenter:
Position = GetSelectionCenter();
break;
case PivotType.WorldOrigin:
Position = Vector3.Zero;
break;
}
if(verts != null)
{
Transform t = thisTransform;
Vector3 selected = ((verts[selectedvert].Position * t.Orientation) * t.Scale) + t.Translation;
Position += -(Position - selected);
}
Position += _translationDelta;
}
@@ -180,7 +187,7 @@ namespace FlaxEditor.Gizmo
_screenScale = (float)(vLength.Length / GizmoScaleFactor * gizmoSize);
}
// Setup world
Quaternion orientation = GetSelectedObject(0).Orientation;
Quaternion orientation = GetSelectedObject(0).Transform.Orientation;
_gizmoWorld = new Transform(position, orientation, new Float3(_screenScale));
if (_activeTransformSpace == TransformSpace.World && _activeMode != Mode.Scale)
{
@@ -210,88 +217,88 @@ namespace FlaxEditor.Gizmo
Real intersection;
switch (_activeAxis)
{
case Axis.X:
{
var plane = planeDotXY > planeDotZX ? planeXY : planeZX;
if (ray.Intersects(ref plane, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(_tDelta.X, 0, 0);
}
break;
}
case Axis.Y:
{
var plane = planeDotXY > planeDotYZ ? planeXY : planeYZ;
if (ray.Intersects(ref plane, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(0, _tDelta.Y, 0);
}
break;
}
case Axis.Z:
{
var plane = planeDotZX > planeDotYZ ? planeZX : planeYZ;
if (ray.Intersects(ref plane, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(0, 0, _tDelta.Z);
}
break;
}
case Axis.YZ:
{
if (ray.Intersects(ref planeYZ, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(0, _tDelta.Y, _tDelta.Z);
}
break;
}
case Axis.XY:
{
if (ray.Intersects(ref planeXY, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(_tDelta.X, _tDelta.Y, 0);
}
break;
}
case Axis.ZX:
{
if (ray.Intersects(ref planeZX, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(_tDelta.X, 0, _tDelta.Z);
}
break;
}
case Axis.Center:
{
var gizmoToView = Position - Owner.ViewPosition;
var plane = new Plane(-Vector3.Normalize(gizmoToView), gizmoToView.Length);
if (ray.Intersects(ref plane, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
}
delta = _tDelta;
break;
}
case Axis.X:
{
var plane = planeDotXY > planeDotZX ? planeXY : planeZX;
if (ray.Intersects(ref plane, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(_tDelta.X, 0, 0);
}
break;
}
case Axis.Y:
{
var plane = planeDotXY > planeDotYZ ? planeXY : planeYZ;
if (ray.Intersects(ref plane, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(0, _tDelta.Y, 0);
}
break;
}
case Axis.Z:
{
var plane = planeDotZX > planeDotYZ ? planeZX : planeYZ;
if (ray.Intersects(ref plane, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(0, 0, _tDelta.Z);
}
break;
}
case Axis.YZ:
{
if (ray.Intersects(ref planeYZ, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(0, _tDelta.Y, _tDelta.Z);
}
break;
}
case Axis.XY:
{
if (ray.Intersects(ref planeXY, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(_tDelta.X, _tDelta.Y, 0);
}
break;
}
case Axis.ZX:
{
if (ray.Intersects(ref planeZX, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(_tDelta.X, 0, _tDelta.Z);
}
break;
}
case Axis.Center:
{
var gizmoToView = Position - Owner.ViewPosition;
var plane = new Plane(-Vector3.Normalize(gizmoToView), gizmoToView.Length);
if (ray.Intersects(ref plane, out intersection))
{
_intersectPosition = ray.Position + ray.Direction * intersection;
if (_lastIntersectionPosition != Vector3.Zero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
}
delta = _tDelta;
break;
}
}
// Modifiers
@@ -364,36 +371,45 @@ namespace FlaxEditor.Gizmo
switch (_activeAxis)
{
case Axis.X:
case Axis.Y:
case Axis.Z:
{
Float3 dir;
if (_activeAxis == Axis.X)
dir = Float3.Right * _gizmoWorld.Orientation;
else if (_activeAxis == Axis.Y)
dir = Float3.Up * _gizmoWorld.Orientation;
else
dir = Float3.Forward * _gizmoWorld.Orientation;
case Axis.X:
case Axis.Y:
case Axis.Z:
{
Float3 dir;
if (_activeAxis == Axis.X)
dir = Float3.Right * _gizmoWorld.Orientation;
else if (_activeAxis == Axis.Y)
dir = Float3.Up * _gizmoWorld.Orientation;
else
dir = Float3.Forward * _gizmoWorld.Orientation;
Float3 viewDir = Owner.ViewPosition - Position;
Float3.Dot(ref viewDir, ref dir, out float dot);
if (dot < 0.0f)
delta *= -1;
Float3 viewDir = Owner.ViewPosition - Position;
Float3.Dot(ref viewDir, ref dir, out float dot);
if (dot < 0.0f)
delta *= -1;
Quaternion.RotationAxis(ref dir, delta, out _rotationDelta);
break;
}
Quaternion.RotationAxis(ref dir, delta, out _rotationDelta);
break;
}
default:
_rotationDelta = Quaternion.Identity;
break;
default:
_rotationDelta = Quaternion.Identity;
break;
}
}
/// <inheritdoc />
public override bool IsControllingMouse => _isTransforming;
//vertex snaping stff
Mesh.Vertex[] verts;
Mesh.Vertex[] otherVerts;
Transform otherTransform;
StaticModel SelectedModel;
Transform thisTransform => SelectedModel.Transform;
bool hasSelectedVertex;
int selectedvert;
int otherSelectedvert;
/// <inheritdoc />
public override void Update(float dt)
{
@@ -421,20 +437,34 @@ namespace FlaxEditor.Gizmo
{
switch (_activeMode)
{
case Mode.Scale:
case Mode.Translate:
UpdateTranslateScale();
break;
case Mode.Rotate:
UpdateRotate(dt);
break;
case Mode.Translate:
UpdateTranslateScale();
if (Owner.UseSnapping)
VertexSnap();
break;
case Mode.Scale:
UpdateTranslateScale();
break;
case Mode.Rotate:
UpdateRotate(dt);
break;
}
}
else
{
// If nothing selected, try to select any axis
if (!isLeftBtnDown && !Owner.IsRightMouseButtonDown)
{
if (Owner.IsAltKeyDown && _activeMode == Mode.Translate)
SelectVertexSnaping();
SelectAxis();
}
else if (Owner.IsAltKeyDown)
{
verts = null;
otherVerts = null;
}
}
// Set positions of the gizmo
@@ -516,6 +546,166 @@ namespace FlaxEditor.Gizmo
// Update
UpdateMatrices();
}
void SelectVertexSnaping()
{
Vector3 point = Vector3.Zero;
var ray = Owner.MouseRay;
Real lastdistance = Real.MaxValue;
StaticModel Lastmodel = null;
int index = 0;
int i = 0;
//get first
for (; i < SelectionCount; i++)
{
if (GetSelectedObject(i).EditableObject is StaticModel model)
{
var bb = model.EditorBox;
if (CollisionsHelper.RayIntersectsBox(ref ray, ref bb, out Vector3 p))
{
Lastmodel = model;
point = p;
index = 0;
break;
}
}
}
if (Lastmodel == null) // nothing to do return
return;
//find closest bounding box in selection
for (; i < SelectionCount; i++)
{
if (GetSelectedObject(i).EditableObject is StaticModel model)
{
var bb = model.EditorBox;
//check for other we might have one closer
var d = Vector3.Distance(model.Transform.Translation, ray.Position);
if (lastdistance < d)
{
if (CollisionsHelper.RayIntersectsBox(ref ray, ref bb, out Vector3 p))
{
lastdistance = d;
Lastmodel = model;
point = p;
index = i;
}
}
}
}
SelectedModel = Lastmodel;
//find closest vertex to bounding box point (collision detection approximation)
//[ToDo] replace this with collision detection with is suporting concave shapes (compute shader)
point = thisTransform.WorldToLocal(point);
//[To Do] comlite this there is not suport for multy mesh model
verts = Lastmodel.Model.LODs[0].Meshes[0].DownloadVertexBuffer();
lastdistance = Vector3.Distance(point, verts[0].Position);
for (int j = 0; j < verts.Length; j++)
{
var d = Vector3.Distance(point, verts[j].Position);
if (d <= lastdistance)
{
lastdistance = d;
selectedvert = j;
}
}
}
void VertexSnap()
{
Profiler.BeginEvent("VertexSnap");
//ray cast others
if (verts != null)
{
var ray = Owner.MouseRay;
SceneGraphNode.RayCastData rayCast = new SceneGraphNode.RayCastData()
{
Ray = ray,
Exclude = new List<Actor>() {},
Scan = new List<Type>() { typeof(StaticModel) }
};
for (int i = 0; i < SelectionCount; i++)
{
rayCast.Exclude.Add((Actor)GetSelectedObject(i).EditableObject);
}
//grab scene and raycast
var actor = GetSelectedObject(0).ParentScene.RayCast(ref rayCast, out var distance, out var _);
if (actor != null)
{
if (actor.EditableObject is StaticModel model)
{
otherTransform = model.Transform;
Vector3 p = rayCast.Ray.Position + (rayCast.Ray.Direction * distance);
//[To Do] comlite this there is not suport for multy mesh model
otherVerts = model.Model.LODs[0].Meshes[0].DownloadVertexBuffer();
//find closest vertex to bounding box point (collision detection approximation)
//[ToDo] replace this with collision detection with is suporting concave shapes (compute shader)
p = actor.Transform.WorldToLocal(p);
Real lastdistance = Vector3.Distance(p, otherVerts[0].Position);
for (int i = 0; i < otherVerts.Length; i++)
{
var d = Vector3.Distance(p, otherVerts[i].Position);
if (d <= lastdistance)
{
lastdistance = d;
otherSelectedvert = i;
}
}
if(lastdistance > 25)
{
otherSelectedvert = -1;
otherVerts = null;
}
}
}
}
if (verts != null && otherVerts != null)
{
Transform t = thisTransform;
Vector3 selected = ((verts[selectedvert].Position * t.Orientation) * t.Scale) + t.Translation;
t = otherTransform;
Vector3 other = ((otherVerts[otherSelectedvert].Position * t.Orientation) * t.Scale) + t.Translation;
// Translation
var projection = -(selected - other);
//at some point
//Quaternion inverse = t.Orientation;
//inverse.Invert();
//projection *= inverse; //world to local
//flip mask
//var Not = ~_activeAxis;
//LockAxisWorld(Not, ref projection); //lock axis
//projection *= t.Orientation; //local to world
_translationDelta = projection;
}
Profiler.EndEvent();
}
/// <summary>
/// zeros the <see cref="Vector3"/> <paramref name="v"/> component using the <see cref="Axis"/> <paramref name="lockAxis"/>
/// </summary>
public static void LockAxisWorld(Axis lockAxis, ref Vector3 v)
{
if (lockAxis.HasFlag(Axis.X))
v.X = 0;
if (lockAxis.HasFlag(Axis.Y))
v.Y = 0;
if (lockAxis.HasFlag(Axis.Z))
v.Z = 0;
}
/// <summary>
/// Gets a value indicating whether this tool can transform objects.
@@ -536,7 +726,7 @@ namespace FlaxEditor.Gizmo
/// Gets the selected object transformation.
/// </summary>
/// <param name="index">The selected object index.</param>
protected abstract Transform GetSelectedObject(int index);
protected abstract SceneGraphNode GetSelectedObject(int index);
/// <summary>
/// Gets the selected objects bounding box (contains the whole selection).

View File

@@ -10,10 +10,21 @@ namespace FlaxEditor.SceneGraph.Actors
[HideInEditor]
public sealed class FoliageNode : ActorNode
{
private FoliageInstance instance;
/// <summary>
/// The selected instance index
/// </summary>
public int SelectedInstanceIndex;
/// <inheritdoc />
public FoliageNode(Actor actor)
public FoliageNode(Foliage actor, int selectedInstanceIndex = -1)
: base(actor)
{
SelectedInstanceIndex = selectedInstanceIndex;
if (selectedInstanceIndex != -1)
{
instance = actor.GetInstance(selectedInstanceIndex);
}
}
}
}

View File

@@ -13,6 +13,7 @@ using FlaxEditor.Modules;
using FlaxEditor.SceneGraph.Actors;
using FlaxEditor.Windows;
using FlaxEngine;
using System.Runtime.CompilerServices;
namespace FlaxEditor.SceneGraph
{
@@ -216,6 +217,29 @@ namespace FlaxEditor.SceneGraph
/// The flags.
/// </summary>
public FlagTypes Flags;
/// <summary>
/// The exclude list actors specyfaied in <see cref="Exclude"/>
/// </summary>
public List<Actor> Exclude = new List<Actor>();
/// <summary>
/// The scan for types
/// </summary>
public List<Type> Scan = new List<Type>();
/// <summary>
/// if this is true it will include the types specyfaied in <see cref="Scan"/>
/// otherwise it will include all types excluding types specyfaied in <see cref="Scan"/>
/// </summary>
public bool ExcludeScan;
/// <summary>
/// Initializes a new instance of the <see cref="RayCastData"/> struct.
/// </summary>
public RayCastData()
{
}
}
/// <summary>
@@ -271,16 +295,18 @@ namespace FlaxEditor.SceneGraph
normal = Vector3.Up;
return null;
}
// Check itself
SceneGraphNode minTarget = null;
Real minDistance = Real.MaxValue;
Vector3 minDistanceNormal = Vector3.Up;
if (RayCastSelf(ref ray, out distance, out normal))
if (Mask(ref ray))
{
minTarget = this;
minDistance = distance;
minDistanceNormal = normal;
// Check itself
if (RayCastSelf(ref ray, out distance, out normal))
{
minTarget = this;
minDistance = distance;
minDistanceNormal = normal;
}
}
// Check all children
@@ -294,12 +320,40 @@ namespace FlaxEditor.SceneGraph
minDistanceNormal = normal;
}
}
// Return result
distance = minDistance;
normal = minDistanceNormal;
return minTarget;
}
/// <summary>
/// Masks the objects base of <see cref="RayCastData.Exclude"/> and <see cref="RayCastData.Scan"/>.
/// </summary>
/// <param name="ray">The ray.</param>
/// <returns>true if can pass through mask</returns>
private bool Mask(ref RayCastData ray)
{
//filter actors
for (int j = 0; j < ray.Exclude.Count; j++)
{
if ((EditableObject is Actor a) && a == ray.Exclude[j])
{
//remove form exclude
//because it is pased by ref and funcion is recursive it will slowly shrink the list untile nothing is left
//micro optimization
ray.Exclude.RemoveAt(j);
return false;
}
}
//filter types
for (int i = 0; i < ray.Scan.Count; i++)
{
if (EditableObject.GetType() != ray.Scan[i])
{
return false;
}
}
return true;
}
/// <summary>
/// Checks if given ray intersects with the node.

View File

@@ -3,6 +3,7 @@
using System;
using FlaxEditor.Gizmo;
using FlaxEditor.SceneGraph;
using FlaxEditor.SceneGraph.Actors;
using FlaxEditor.Tools.Foliage.Undo;
using FlaxEngine;
@@ -69,7 +70,7 @@ namespace FlaxEditor.Tools.Foliage
}
/// <inheritdoc />
protected override Transform GetSelectedObject(int index)
protected override SceneGraphNode GetSelectedObject(int index)
{
var foliage = GizmoMode.SelectedFoliage;
if (!foliage)
@@ -77,8 +78,7 @@ namespace FlaxEditor.Tools.Foliage
var instanceIndex = GizmoMode.SelectedInstanceIndex;
if (instanceIndex < 0 || instanceIndex >= foliage.InstancesCount)
throw new InvalidOperationException("No foliage instance selected.");
var instance = foliage.GetInstance(instanceIndex);
return foliage.Transform.LocalToWorld(instance.Transform);
return new FoliageNode(foliage, instanceIndex);
}
/// <inheritdoc />