Files
XCEngine/参考/TransformGizmo/Scripts/RuntimeRotateGizmo.cs
2026-03-29 01:36:53 +08:00

285 lines
12 KiB
C#

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UIElements;
public class RuntimeRotateGizmo : RuntimeTransformGizmo
{
private string prefabPath = "Prefabs/RotateGizmo";
private GameObject rotatePlane;
private GameObject rotatePlaneHandle;
private GameObject rotateXYZ;
private GameObject rotateX;
private GameObject rotateY;
private GameObject rotateZ;
private GameObject rotateSphere;
enum RotateSelectState { X, Y, Z, Plane, Sphere, UnSelected }
RotateSelectState rotateSelectState;
//Offset
private float XYZOffsetOnSelect;
private Vector2 sphereOffsetOnSelect;
//Rate
private float XYZRotateRate = 0.1f;
private float planeRotateRate = 0.1f;
private float sphereRotateRate = 0.1f;
private Quaternion rotationOnSelect;
private Vector3 viewLocalDirectionX;
private Vector3 viewLocalDirectionY;
private Vector3 viewLocalDirectionZ;
private Quaternion lastTargetRotation;
public override void Initialize()
{
//Base Initialize
base.Initialize();
//Material Initialize
redMaterial = Resources.Load<Material>("Materials/RotateRed");
greenMaterial = Resources.Load<Material>("Materials/RotateGreen");
blueMaterial = Resources.Load<Material>("Materials/RotateBlue");
////Prefab Initialize
prefab = Resources.Load<GameObject>(prefabPath);
if (prefab != null)
{
gizmo = Object.Instantiate(prefab) as GameObject;
}
HideBeindCamera();
//Component Initialize
rotatePlane = gizmo.GetComponent<Transform>().Find("RotatePlane").gameObject;
rotatePlaneHandle = rotatePlane.GetComponent<Transform>().Find("RotatePlaneHandle").gameObject;
rotateXYZ = gizmo.GetComponent<Transform>().Find("RotateXYZ").gameObject;
rotateX = rotateXYZ.GetComponent<Transform>().Find("RotateX").gameObject;
rotateY = rotateXYZ.GetComponent<Transform>().Find("RotateY").gameObject;
rotateZ = rotateXYZ.GetComponent<Transform>().Find("RotateZ").gameObject;
rotateSphere = gizmo.GetComponent<Transform>().Find("RotateSphere").gameObject;
//Other Initialzie
rotateSelectState = RotateSelectState.UnSelected;
rotateSphere.GetComponent<MeshRenderer>().material.SetFloat("_AlphaScale", 0.2f);
scaleRate = 160.0f;
handleNames = new List<string> { "RotatePlaneHandle", "RotatePlaneDetect", "RotateXDetect", "RotateX", "RotateYDetect", "RotateY", "RotateZDetect", "RotateZ", "RotateSphere" };
}
public override void Update()
{
//hide
if(enabled)
{
//Render Update
RenderUpdate();
//Follow Update
FollowUpdate();
//Interaction Update
if (rotateSelectState != RotateSelectState.UnSelected)
{
OnSelecting();
}
}
else
{
HideBeindCamera();
}
}
protected override void RenderUpdate()
{
//Base Update
base.RenderUpdate();
gizmo.GetComponent<Transform>().localScale /= scaleRate;
//Rotate Plane Update
Vector3 viewDirection = -Camera.main.transform.forward;
Quaternion rotatePlaneRotation = Quaternion.LookRotation(viewDirection, Vector3.up);
rotatePlane.GetComponent<Transform>().rotation = rotatePlaneRotation;
//Clip Plane Update
Vector3 normalizedNormal = viewDirection.normalized;
Vector3 targetPosition = gizmo.GetComponent<Transform>().position;
float D = -Vector3.Dot(targetPosition, normalizedNormal);
Vector4 clipPlane = new Vector4(normalizedNormal.x, normalizedNormal.y, normalizedNormal.z, D);
redMaterial.SetVector("_ClipPlane", clipPlane);
greenMaterial.SetVector("_ClipPlane", clipPlane);
blueMaterial.SetVector("_ClipPlane", clipPlane);
}
public override void EnableGizmo(GameObject target)
{
base.EnableGizmo(target);
lastTargetRotation = target.GetComponent<Transform>().rotation;
}
protected override void FollowUpdate()
{
if (target == null) { return; }
//Position Follow
gizmo.GetComponent<Transform>().position = target.GetComponent<Transform>().position;
target.GetComponent<Transform>().rotation = gizmo.GetComponent<Transform>().rotation;
//Rotation Follow
Quaternion targetRotation = target.GetComponent<Transform>().rotation;
if (targetRotation != lastTargetRotation)
{
gizmo.GetComponent<Transform>().rotation = targetRotation;
}
else
{
target.GetComponent<Transform>().rotation = gizmo.GetComponent<Transform>().rotation;
}
lastTargetRotation = targetRotation;
}
public override void OnSelect(string handleName)
{
if (!handleNames.Contains(handleName)) { return; }
rotationOnSelect = gizmo.GetComponent<Transform>().rotation;
Vector3 gizmoPosition = gizmo.GetComponent<Transform>().position;
Vector2 mousePositionOnSelect = Input.mousePosition;
gizmoXYZScreenStart = Camera.main.WorldToScreenPoint(gizmoPosition);
if (handleName.Contains("X") || handleName.Contains("Y") || handleName.Contains("Z")) //RotateX RotateY RotateZ
{
Vector3 gizmoEndPosition = new Vector3();
switch (handleName)
{
case "RotateX":
case "RotateXDetect":
rotateSelectState = RotateSelectState.X;
gizmoEndPosition = gizmoPosition + gizmo.GetComponent<Transform>().right;
break;
case "RotateY":
case "RotateYDetect":
rotateSelectState = RotateSelectState.Y;
gizmoEndPosition = gizmoPosition + gizmo.GetComponent<Transform>().up;
break;
case "RotateZ":
case "RotateZDetect":
rotateSelectState = RotateSelectState.Z;
gizmoEndPosition = gizmoPosition + gizmo.GetComponent<Transform>().forward;
break;
}
Vector2 gizmoXYZScreenEnd = Camera.main.WorldToScreenPoint(gizmoEndPosition);
gizmoXYZScreenDirection = (gizmoXYZScreenEnd - gizmoXYZScreenStart).normalized;
Vector2 gizmoXYZXcreenVerticalDirection = new Vector2(gizmoXYZScreenDirection.y, -gizmoXYZScreenDirection.x);
Vector2 mouseVector = mousePositionOnSelect - gizmoXYZScreenStart;
XYZOffsetOnSelect = Vector2.Dot(mouseVector, gizmoXYZXcreenVerticalDirection);
}
else if (handleName == "RotatePlaneDetect" || handleName == "RotatePlaneHandle")
{
rotateSelectState = RotateSelectState.Plane;
gizmoXYZScreenDirection = (mousePositionOnSelect - gizmoXYZScreenStart).normalized;
XYZOffsetOnSelect = 0.0f;
Vector3 viewDirectionZ = Camera.main.transform.forward;
viewLocalDirectionZ = (gizmo.GetComponent<Transform>().worldToLocalMatrix * viewDirectionZ).normalized;
}
else if(handleName == "RotateSphere")
{
rotateSelectState = RotateSelectState.Sphere;
sphereOffsetOnSelect = mousePositionOnSelect - gizmoXYZScreenStart;
Vector3 viewDirectionX = Camera.main.transform.right;
viewLocalDirectionX = (gizmo.GetComponent<Transform>().worldToLocalMatrix * viewDirectionX).normalized;
Vector3 viewDirectionY = Camera.main.transform.up;
viewLocalDirectionY = (gizmo.GetComponent<Transform>().worldToLocalMatrix * viewDirectionY).normalized;
}
ChangeRenderOnSelect();
}
public override void OnUnSelect()
{
if (rotateSelectState == RotateSelectState.UnSelected) { return; }
rotateSelectState = RotateSelectState.UnSelected;
//Render
ChangeRenderOnUnSelect();
}
protected override void OnSelecting()
{
Vector3 gizmoPosition = gizmo.GetComponent<Transform>().position;
Vector2 mousePosition = Input.mousePosition;
Vector2 mouseVector = mousePosition - gizmoXYZScreenStart;
if (rotateSelectState <= RotateSelectState.Plane)
{
//Rotate 90 degrees clockwise
Vector2 gizmoXYZScreenVerticalDirection = new Vector2(gizmoXYZScreenDirection.y, -gizmoXYZScreenDirection.x);
float dragDistance = Vector2.Dot(mouseVector, gizmoXYZScreenVerticalDirection) - XYZOffsetOnSelect;
switch (rotateSelectState)
{
case RotateSelectState.X:
gizmo.GetComponent<Transform>().rotation = rotationOnSelect * Quaternion.Euler(-Vector3.right * dragDistance * XYZRotateRate);
break;
case RotateSelectState.Y:
gizmo.GetComponent<Transform>().rotation = rotationOnSelect * Quaternion.Euler(-Vector3.up * dragDistance * XYZRotateRate);
break;
case RotateSelectState.Z:
gizmo.GetComponent<Transform>().rotation = rotationOnSelect * Quaternion.Euler(-Vector3.forward * dragDistance * XYZRotateRate);
break;
case RotateSelectState.Plane:
gizmo.GetComponent<Transform>().rotation = rotationOnSelect * Quaternion.Euler(-viewLocalDirectionZ * dragDistance * planeRotateRate);
break;
}
}
else if (rotateSelectState == RotateSelectState.Sphere)
{
Vector2 dragVector = mouseVector - sphereOffsetOnSelect;
gizmo.GetComponent<Transform>().rotation = rotationOnSelect * Quaternion.Euler(-viewLocalDirectionY * dragVector.x * sphereRotateRate);
gizmo.GetComponent<Transform>().rotation = gizmo.transform.rotation * Quaternion.Euler(viewLocalDirectionX * dragVector.y * sphereRotateRate);
}
}
protected override void ChangeRenderOnSelect()
{
switch (rotateSelectState)
{
case RotateSelectState.X:
rotatePlaneHandle.SetActive(false);
rotateX.GetComponent<MeshRenderer>().material = yellowMaterial;
break;
case RotateSelectState.Y:
rotatePlaneHandle.SetActive(false);
rotateY.GetComponent<MeshRenderer>().material = yellowMaterial;
break;
case RotateSelectState.Z:
rotatePlaneHandle.SetActive(false);
rotateZ.GetComponent<MeshRenderer>().material = yellowMaterial;
break;
case RotateSelectState.Plane:
rotateX.GetComponent<MeshRenderer>().material = whiteMaterial;
rotateY.GetComponent<MeshRenderer>().material = whiteMaterial;
rotateZ.GetComponent<MeshRenderer>().material = whiteMaterial;
rotatePlaneHandle.GetComponent<MeshRenderer>().material = yellowMaterial;
break;
case RotateSelectState.Sphere:
rotateSphere.GetComponent<MeshRenderer>().material.SetFloat("_AlphaScale", 0.4f);
rotateSphere.SetActive(true);
rotatePlaneHandle.GetComponent<MeshRenderer>().material = whiteMaterial;
break;
}
}
protected override void ChangeRenderOnUnSelect()
{
rotatePlaneHandle.SetActive(true);
rotateX.SetActive(true);
rotateY.SetActive(true);
rotateZ.SetActive(true);
rotatePlaneHandle.GetComponent<MeshRenderer>().material = whiteMaterial;
rotateX.GetComponent<MeshRenderer>().material = redMaterial;
rotateY.GetComponent<MeshRenderer>().material = greenMaterial;
rotateZ.GetComponent<MeshRenderer>().material = blueMaterial;
rotateSphere.GetComponent<MeshRenderer>().material.SetFloat("_AlphaScale", 0.2f);
}
public void SphereRenderUpdate(string handleName)
{
if (rotateSelectState != RotateSelectState.UnSelected) { return; }
rotateSphere.SetActive(handleName.Equals("RotateSphere"));
}
}