EnergyEfficiencyManagement/Assets/Scripts/RawImageModelViewer.cs

429 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using SK.Framework;
/// <summary>
/// 在RawImage上显示和控制3D模型的查看器
/// 支持以模型为中心的旋转、缩放、平移等基本操作
/// </summary>
public class RawImageModelViewer : MonoBehaviour
{
[Header("【模型显示设置】")]
[Tooltip("要显示的3D模型预制件")]
public GameObject modelPrefab;
[Header("【UI组件引用】")]
[Tooltip("用于显示模型的RawImage组件")]
public RawImage displayImage;
[Tooltip("渲染纹理用于将3D模型渲染到UI上")]
public RenderTexture renderTexture;
[Tooltip("专门用于渲染模型到纹理的相机")]
public Camera renderCamera;
[Header("【旋转控制】")]
[Tooltip("旋转灵敏度")]
public float rotationSpeed = 5.0f;
[Tooltip("是否启用旋转功能")]
public bool enableRotation = true;
[Tooltip("垂直旋转角度限制")]
public Vector2 verticalAngleLimit = new Vector2(-80f, 80f);
[Header("【缩放控制】")]
[Tooltip("缩放灵敏度")]
public float zoomSpeed = 5.0f;
[Tooltip("最小缩放距离")]
public float minZoomDistance = 1.0f;
[Tooltip("最大缩放距离")]
public float maxZoomDistance = 20.0f;
[Tooltip("默认缩放距离")]
public float defaultZoomDistance = 5.0f;
[Tooltip("是否启用缩放功能")]
public bool enableZoom = true;
[Header("【平移控制】")]
[Tooltip("平移灵敏度")]
public float panSpeed = 0.5f;
[Tooltip("是否启用平移功能")]
public bool enablePan = true;
[Tooltip("X轴最大平移距离")]
public float maxPanDistanceX = 5.0f;
[Tooltip("Z轴最大平移距离")]
public float maxPanDistanceZ = 5.0f;
[Header("【相机设置】")]
[Tooltip("相机的初始位置偏移")]
public Vector3 cameraOffset = new Vector3(0, 0, -5);
[Tooltip("相机的视野角度")]
public float fieldOfView = 60f;
[Header("【模型设置】")]
[Tooltip("模型的初始旋转")]
public Vector3 modelInitialRotation = Vector3.zero;
[Tooltip("模型的初始缩放")]
public Vector3 modelInitialScale = Vector3.one;
[Header("【平滑设置】")]
[Tooltip("旋转平滑度")]
public float rotationSmoothness = 10.0f;
[Tooltip("缩放平滑度")]
public float zoomSmoothness = 10.0f;
[Tooltip("平移平滑度")]
public float panSmoothness = 10.0f;
// 内部变量
private GameObject displayedModel;
private Transform modelTransform;
private Vector3 modelCenter; // 模型中心点
private Vector3 currentRotation = Vector3.zero;
private Vector3 targetRotation = Vector3.zero;
private float currentDistance = 5.0f;
private float targetDistance = 5.0f;
private Vector3 targetPositionOffset = Vector3.zero;
private Vector3 currentTargetPositionOffset = Vector3.zero;
private Vector3 lastMousePosition = Vector3.zero;
private bool isDragging = false;
private bool isPanning = false;
private float childCurrentDistance = 3.0f;
private float childTargetDistance = 3.0f;
void Start()
{
//InitializeModelViewer();
}
void Update()
{
if (displayedModel != null)
{
HandleInput();
UpdateModelPosition();
}
}
/// <summary>
/// 初始化模型
/// </summary>
private void InitializeModelViewer()
{
if (modelPrefab == null)
{
Debug.LogWarning("RawImageModelViewer: 未指定模型预制件", this);
return;
}
if (displayImage == null || renderTexture == null || renderCamera == null)
{
Debug.LogWarning("RawImageModelViewer: 请设置必要的UI组件", this);
return;
}
if (renderCamera == null)
{
Debug.LogWarning("RawImageModelViewer: 未指定相机", this);
return;
}
displayedModel = Instantiate(modelPrefab, modelCenter, Quaternion.Euler(modelPrefab.transform.eulerAngles));
Debug.Log(modelPrefab.transform.eulerAngles);
modelTransform = displayedModel.transform;
MonoBehaviour[] scripts = displayedModel.GetComponentsInChildren<MonoBehaviour>();
foreach (MonoBehaviour script in scripts)
{
Destroy(script);
}
MeshRenderer[] meshRenderers = displayedModel.GetComponentsInChildren<MeshRenderer>();
foreach (MeshRenderer meshRenderer in meshRenderers)
{
meshRenderer.SetLayer(6);
}
renderCamera.targetTexture = renderTexture;
renderCamera.fieldOfView = fieldOfView;
displayImage.texture = renderTexture;
Bounds bounds = CalculateBounds(displayedModel);
modelCenter = bounds.center;
/*float objectSize = Mathf.Max(modelCenter.x, modelCenter.y, modelCenter.z);
targetDistance = objectSize * 1.5f;
targetDistance = Mathf.Clamp(childTargetDistance, minZoomDistance, maxZoomDistance);
currentDistance = targetDistance;
Vector3 toCamera = (renderCamera.transform.position - modelCenter).normalized;
targetRotation.x = Mathf.Atan2(toCamera.x, toCamera.z) * Mathf.Rad2Deg;
targetRotation.y = Mathf.Asin(toCamera.y) * Mathf.Rad2Deg;*/
ResetView();
}
private void HandleInput()
{
if (IsMouseOverRawImage())
{
// 旋转控制
if (enableRotation)
{
if (Input.GetMouseButtonDown(0))
{
isDragging = true;
lastMousePosition = Input.mousePosition;
}
if (Input.GetMouseButton(0) && isDragging)
{
Vector3 delta = Input.mousePosition - lastMousePosition;
targetRotation.y += delta.x * rotationSpeed * 0.01f;
targetRotation.x -= delta.y * rotationSpeed * 0.01f;
// 限制垂直旋转角度
targetRotation.x = Mathf.Clamp(targetRotation.x, verticalAngleLimit.x, verticalAngleLimit.y);
lastMousePosition = Input.mousePosition;
}
if (Input.GetMouseButtonUp(0))
{
isDragging = false;
}
}
// 平移控制
if (enablePan)
{
if (Input.GetMouseButtonDown(2))
{
isPanning = true;
lastMousePosition = Input.mousePosition;
}
if (Input.GetMouseButton(2) && isPanning)
{
Vector3 delta = Input.mousePosition - lastMousePosition;
Vector3 right = renderCamera.transform.right;
Vector3 up = renderCamera.transform.up;
right.y = 0;
up.y = 0;
// 应用平移限制
Vector3 panDelta = (right * delta.x + up * delta.y) * panSpeed * -0.01f;
ApplyPanWithLimits(panDelta);
lastMousePosition = Input.mousePosition;
}
if (Input.GetMouseButtonUp(2))
{
isPanning = false;
}
}
// 缩放控制
if (enableZoom)
{
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (Mathf.Abs(scroll) > 0.01f)
{
targetDistance -= scroll * zoomSpeed;
targetDistance = Mathf.Clamp(targetDistance, minZoomDistance, maxZoomDistance);
}
}
// 重置视图
if (Input.GetKeyDown(KeyCode.R))
{
ResetView();
}
// 聚焦到模型中心
if (Input.GetKeyDown(KeyCode.F))
{
FocusOnModel();
}
}
}
/// <summary>
/// 更新相机位置
/// </summary>
private void UpdateModelPosition()
{
// 旋转
currentRotation = Vector3.Lerp(currentRotation, targetRotation, Time.deltaTime * rotationSmoothness);
// 缩放
currentDistance = Mathf.Lerp(currentDistance, targetDistance, Time.deltaTime * zoomSmoothness);
// 平移
currentTargetPositionOffset = Vector3.Lerp(currentTargetPositionOffset, targetPositionOffset, Time.deltaTime * panSmoothness);
// 计算旋转后的相机偏移
Vector3 rotatedOffset = Quaternion.Euler(currentRotation) * cameraOffset.normalized * currentDistance;
Vector3 cameraPosition = modelCenter + currentTargetPositionOffset + rotatedOffset;
//childCurrentDistance = childTargetDistance;
renderCamera.transform.position = cameraPosition;
renderCamera.transform.LookAt(modelCenter + currentTargetPositionOffset);
}
/// <summary>
/// 检测鼠标
/// </summary>
private bool IsMouseOverRawImage()
{
if (displayImage == null) return false;
RectTransform rectTransform = displayImage.rectTransform;
Vector2 localPoint;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
rectTransform, Input.mousePosition, transform.root.GetComponent<Canvas>().worldCamera, out localPoint);
return rectTransform.rect.Contains(localPoint);
}
/// <summary>
/// 限制平移
/// </summary>
private void ApplyPanWithLimits(Vector3 panDelta)
{
Vector3 newOffset = targetPositionOffset - panDelta;
// 限制平移范围
newOffset.x = Mathf.Clamp(newOffset.x, -maxPanDistanceX, maxPanDistanceX);
newOffset.z = Mathf.Clamp(newOffset.z, -maxPanDistanceZ, maxPanDistanceZ);
targetPositionOffset = newOffset;
}
/// <summary>
/// 重置视图
/// </summary>
public void ResetView()
{
//targetRotation = modelInitialRotation;
targetRotation = new Vector3(20,130,0);
targetDistance = 1.0f;
targetPositionOffset = Vector3.zero;
}
/// <summary>
/// 聚焦到模型中心
/// </summary>
public void FocusOnModel()
{
if (modelTransform != null)
{
targetPositionOffset = Vector3.zero;
}
}
/// <summary>
/// 更换模型
/// </summary>
public void ChangeModel(GameObject newModelPrefab)
{
if (newModelPrefab == null) return;
// 销毁当前模型
if (displayedModel != null)
{
DestroyImmediate(displayedModel);
}
// 创建新模型
modelPrefab = newModelPrefab;
InitializeModelViewer();
}
/// <summary>
/// 设置模型的旋转
/// </summary>
public void SetModelRotation(Vector3 rotation)
{
if (modelTransform != null)
{
modelTransform.localRotation = Quaternion.Euler(rotation);
currentRotation = rotation;
targetRotation = rotation;
}
}
/// <summary>
/// 设置模型的缩放
/// </summary>
public void SetModelScale(Vector3 scale)
{
if (modelTransform != null)
{
modelTransform.localScale = scale;
}
}
/// <summary>
/// 设置相机的缩放距离
/// </summary>
public void SetZoomDistance(float distance)
{
targetDistance = Mathf.Clamp(distance, minZoomDistance, maxZoomDistance);
}
/// <summary>
/// 设置是否启用旋转功能
/// </summary>
public void SetEnableRotation(bool enable)
{
enableRotation = enable;
}
/// <summary>
/// 设置是否启用缩放功能
/// </summary>
public void SetEnableZoom(bool enable)
{
enableZoom = enable;
}
/// <summary>
/// 设置是否启用平移功能
/// </summary>
public void SetEnablePan(bool enable)
{
enablePan = enable;
}
/// <summary>
/// 设置平移限制
/// </summary>
public void SetPanLimits(float maxX, float maxZ)
{
maxPanDistanceX = Mathf.Max(0, maxX);
maxPanDistanceZ = Mathf.Max(0, maxZ);
}
private Bounds CalculateBounds(GameObject obj)
{
Renderer[] renderers = obj.GetComponentsInChildren<Renderer>();
if (renderers.Length == 0)
{
return new Bounds(obj.transform.position, Vector3.one);
}
Bounds bounds = renderers[0].bounds;
foreach (Renderer renderer in renderers)
{
bounds.Encapsulate(renderer.bounds);
}
return bounds;
}
void OnDestroy()
{
if (displayedModel != null)
{
DestroyImmediate(displayedModel);
}
}
}