EnergyEfficiencyManagement/Assets/Zion/Scripts/Utils/ModelViewerWithUIPanel.cs

634 lines
19 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 System.Collections.Generic;
public class ModelViewerWithUIPanel : MonoBehaviour
{
[Header("观察目标")]
[SerializeField] private Transform target; // 要观察的主模型
[Header("控制开关")]
[SerializeField] private bool enableClickToFocus = true; // 是否启用点击子物体聚焦功能
[SerializeField] private bool enableMainModelControls = true; // 是否启用在正常模式下对主模型的操作
[Header("主模型观察设置")]
[SerializeField] private float rotationSpeed = 5.0f;
[SerializeField] private float autoRotationSpeed = 0.5f;
[SerializeField] private float rotationSmoothness = 5.0f;
[SerializeField] private float zoomSpeed = 5.0f;
[SerializeField] private float minZoomDistance = 1.0f;
[SerializeField] private float maxZoomDistance = 20.0f;
[SerializeField] private float defaultZoomDistance = 5.0f;
[SerializeField] private float panSpeed = 0.5f;
[SerializeField] private float maxPanDistanceX = 5.0f;
[SerializeField] private float maxPanDistanceZ = 5.0f;
[SerializeField] private Vector2 verticalAngleLimit = new Vector2(10f, 80f);
[Header("UI面板设置")]
[SerializeField] private RawImage modelDisplayImage; // 左侧UI面板用于显示模型
[SerializeField] private Camera uiModelCamera; // 专门用于渲染UI中模型的相机
[SerializeField] private RenderTexture modelRenderTexture; // 渲染纹理
[SerializeField] private Transform modelDisplayPosition; // UI中模型显示位置
[Header("UI面板模型控制")]
[SerializeField] private float uiModelRotationSpeed = 2.0f;
[SerializeField] private float uiModelZoomSpeed = 2.0f;
[SerializeField] private float uiModelMinZoom = 0.5f;
[SerializeField] private float uiModelMaxZoom = 5.0f;
[SerializeField] private float uiModelDefaultZoom = 2.0f;
[Header("点击检测设置")]
[SerializeField] private LayerMask selectableLayers = -1; // 可选择图层
[SerializeField] private LayerMask excludedLayers = 0; // 要排除的图层
[SerializeField] private string[] excludeTags = { "MainCamera", "Light", "UI" };
[Header("模型高亮设置")]
[SerializeField] private Color highlightColor = new Color(0f, 0.5f, 1f, 1f); // 蓝色高亮
[SerializeField] private float highlightIntensity = 2.0f;
[SerializeField] private float highlightFadeSpeed = 2.0f;
[Header("UI面板显示控制")]
[SerializeField] private GameObject uiPanel; // 整个UI面板
[SerializeField] private Text modelNameText; // 模型名称文本
[SerializeField] private Text modelStatusText; // 状态文本
[SerializeField] private Button closePanelButton; // 关闭面板按钮
// 主模型观察相关变量
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;
// UI面板模型相关变量
private bool isUIPanelActive = false;
private GameObject clonedModel = null;
private GameObject currentSelectedModel = null;
private Vector3 uiModelRotation = Vector3.zero;
private float uiModelCurrentZoom = 2.0f;
private float uiModelTargetZoom = 2.0f;
// 高亮相关
private Material[] originalMaterials = null;
private Material highlightMaterial = null;
private float highlightTimer = 0f;
private bool isHighlighting = false;
// 鼠标控制相关
private Vector2 lastMousePosition = Vector2.zero;
private bool isDraggingUIModel = false;
void Start()
{
if (target == null)
{
Debug.LogWarning("未指定观察目标请在Inspector中指定要观察的主模型");
}
// 初始化主相机观察参数
currentDistance = defaultZoomDistance;
targetDistance = defaultZoomDistance;
Vector3 angles = transform.eulerAngles;
currentRotation = new Vector3(angles.y, angles.x, 0);
targetRotation = currentRotation;
// 初始化UI面板
InitializeUIPanel();
// 创建高亮材质
CreateHighlightMaterial();
// 更新相机位置
UpdateCameraPosition();
}
private void InitializeUIPanel()
{
if (uiPanel != null)
{
uiPanel.SetActive(false);
}
if (closePanelButton != null)
{
closePanelButton.onClick.AddListener(CloseUIPanel);
}
// 设置UI模型相机
if (uiModelCamera != null)
{
uiModelCamera.enabled = false;
if (modelRenderTexture != null)
{
uiModelCamera.targetTexture = modelRenderTexture;
}
if (modelDisplayImage != null && modelRenderTexture != null)
{
modelDisplayImage.texture = modelRenderTexture;
}
}
}
private void CreateHighlightMaterial()
{
highlightMaterial = new Material(Shader.Find("Standard"));
highlightMaterial.color = highlightColor;
highlightMaterial.EnableKeyword("_EMISSION");
highlightMaterial.SetColor("_EmissionColor", highlightColor * highlightIntensity);
highlightMaterial.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;
}
void LateUpdate()
{
if (target == null) return;
HandleInput();
UpdateCameraPosition();
UpdateUIPanelControls();
UpdateHighlightEffect();
}
private void HandleInput()
{
if (isUIPanelActive)
{
// UI面板激活时只处理UI面板相关输入
HandleUIPanelInput();
}
else
{
// 正常模式下,处理主模型观察输入
HandleMainModelInput();
}
}
private void HandleMainModelInput()
{
if (!enableMainModelControls) return;
// 旋转控制
if (Input.GetMouseButton(0))
{
float horizontal = Input.GetAxis("Mouse X") * rotationSpeed;
float vertical = -Input.GetAxis("Mouse Y") * rotationSpeed;
targetRotation.x += horizontal;
targetRotation.y += vertical;
targetRotation.y = Mathf.Clamp(targetRotation.y, verticalAngleLimit.x, verticalAngleLimit.y);
}
// 平移控制
if (Input.GetMouseButton(2)) // 中键
{
float horizontal = Input.GetAxis("Mouse X") * panSpeed * 0.1f;
float vertical = Input.GetAxis("Mouse Y") * panSpeed * 0.1f;
Vector3 panDelta = new Vector3(-horizontal, 0, -vertical);
ApplyPanWithLimits(panDelta);
}
// 缩放控制
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (Mathf.Abs(scroll) > 0.01f)
{
targetDistance -= scroll * zoomSpeed;
targetDistance = Mathf.Clamp(targetDistance, minZoomDistance, maxZoomDistance);
}
// 点击选择子物体
if (enableClickToFocus && Input.GetMouseButtonDown(0))
{
HandleObjectClick();
}
// 重置按钮
if (Input.GetKeyDown(KeyCode.R))
{
ResetMainView();
}
// 自动旋转切换
if (Input.GetKeyDown(KeyCode.A))
{
//ToggleAutoRotation(!autoRotationSpeed > 0);
}
// 聚焦到模型
if (Input.GetKeyDown(KeyCode.F))
{
FocusOnMainModel();
}
}
private void HandleUIPanelInput()
{
// 检查鼠标是否在UI面板区域内
if (modelDisplayImage != null && IsMouseOverUIPanel())
{
// 旋转UI中的模型
if (Input.GetMouseButtonDown(0))
{
isDraggingUIModel = true;
lastMousePosition = Input.mousePosition;
}
if (Input.GetMouseButton(0) && isDraggingUIModel)
{
Vector2 delta = (Vector2)Input.mousePosition - lastMousePosition;
uiModelRotation.x += delta.x * uiModelRotationSpeed * 0.1f;
uiModelRotation.y += delta.y * uiModelRotationSpeed * 0.1f;
lastMousePosition = Input.mousePosition;
}
if (Input.GetMouseButtonUp(0))
{
isDraggingUIModel = false;
}
// 缩放UI中的模型
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (Mathf.Abs(scroll) > 0.01f)
{
uiModelTargetZoom -= scroll * uiModelZoomSpeed;
uiModelTargetZoom = Mathf.Clamp(uiModelTargetZoom, uiModelMinZoom, uiModelMaxZoom);
}
}
else
{
isDraggingUIModel = false;
}
// ESC键关闭面板
if (Input.GetKeyDown(KeyCode.Escape))
{
CloseUIPanel();
}
}
private bool IsMouseOverUIPanel()
{
if (modelDisplayImage == null) return false;
RectTransform rectTransform = modelDisplayImage.rectTransform;
Vector2 localMousePosition = rectTransform.InverseTransformPoint(Input.mousePosition);
return rectTransform.rect.Contains(localMousePosition);
}
private void HandleObjectClick()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, Mathf.Infinity, selectableLayers))
{
GameObject hitObject = hit.collider.gameObject;
// 检查是否在排除的图层中
if (IsInExcludedLayers(hitObject))
{
return;
}
// 检查是否是要排除的标签
if (ShouldExcludeObjectByTag(hitObject))
{
return;
}
// 检查是否点击了目标或目标的子物体
if (hit.collider.transform == target || IsChildOfTarget(hit.collider.transform))
{
SelectModelForUIPanel(hitObject);
}
}
}
private void SelectModelForUIPanel(GameObject selectedModel)
{
if (isUIPanelActive)
{
CloseUIPanel();
}
currentSelectedModel = selectedModel;
// 高亮显示被选中的模型
HighlightModel(selectedModel);
// 克隆模型到UI面板
CloneModelToUIPanel(selectedModel);
// 显示UI面板
ShowUIPanel();
// 更新UI面板信息
UpdateUIPanelInfo(selectedModel);
}
private void HighlightModel(GameObject model)
{
// 清除之前的高亮
ClearHighlight();
// 获取模型的渲染器
Renderer renderer = model.GetComponent<Renderer>();
if (renderer != null)
{
originalMaterials = renderer.materials;
Material[] newMaterials = new Material[originalMaterials.Length];
for (int i = 0; i < newMaterials.Length; i++)
{
newMaterials[i] = highlightMaterial;
}
renderer.materials = newMaterials;
isHighlighting = true;
highlightTimer = 0f;
}
}
private void ClearHighlight()
{
if (currentSelectedModel != null && originalMaterials != null)
{
Renderer renderer = currentSelectedModel.GetComponent<Renderer>();
if (renderer != null)
{
renderer.materials = originalMaterials;
}
}
originalMaterials = null;
isHighlighting = false;
}
private void UpdateHighlightEffect()
{
if (!isHighlighting || currentSelectedModel == null) return;
highlightTimer += Time.deltaTime * highlightFadeSpeed;
if (highlightTimer >= 1f)
{
ClearHighlight();
}
}
private void CloneModelToUIPanel(GameObject model)
{
// 删除之前的克隆模型
if (clonedModel != null)
{
Destroy(clonedModel);
}
// 克隆模型
clonedModel = Instantiate(model, modelDisplayPosition.position, Quaternion.identity, modelDisplayPosition);
// 删除不需要的组件
RemoveUnwantedComponents(clonedModel);
// 计算合适的缩放比例
Bounds bounds = CalculateBounds(clonedModel);
float maxSize = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z);
float scaleFactor = 1f / maxSize;
clonedModel.transform.localScale = Vector3.one * scaleFactor;
clonedModel.transform.localPosition = Vector3.zero;
// 重置旋转
clonedModel.transform.localRotation = Quaternion.identity;
uiModelRotation = Vector3.zero;
// 重置缩放
uiModelCurrentZoom = uiModelDefaultZoom;
uiModelTargetZoom = uiModelDefaultZoom;
// 启用UI相机
if (uiModelCamera != null)
{
uiModelCamera.enabled = true;
}
}
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;
}
private void RemoveUnwantedComponents(GameObject obj)
{
// 移除脚本组件
MonoBehaviour[] scripts = obj.GetComponentsInChildren<MonoBehaviour>();
foreach (MonoBehaviour script in scripts)
{
Destroy(script);
}
// 移除碰撞体
Collider[] colliders = obj.GetComponentsInChildren<Collider>();
foreach (Collider collider in colliders)
{
Destroy(collider);
}
// 移除刚体
Rigidbody[] rigidbodies = obj.GetComponentsInChildren<Rigidbody>();
foreach (Rigidbody rb in rigidbodies)
{
Destroy(rb);
}
}
private void ShowUIPanel()
{
if (uiPanel != null)
{
uiPanel.SetActive(true);
isUIPanelActive = true;
}
}
private void CloseUIPanel()
{
if (uiPanel != null)
{
uiPanel.SetActive(false);
isUIPanelActive = false;
}
// 禁用UI相机
if (uiModelCamera != null)
{
uiModelCamera.enabled = false;
}
// 删除克隆的模型
if (clonedModel != null)
{
Destroy(clonedModel);
clonedModel = null;
}
// 清除高亮
ClearHighlight();
currentSelectedModel = null;
}
private void UpdateUIPanelInfo(GameObject model)
{
if (modelNameText != null)
{
modelNameText.text = model.name;
}
if (modelStatusText != null)
{
// 这里可以根据需要从模型获取更多信息
modelStatusText.text = "设备状态: 正常\n在线时长: 15小时30分钟";
}
}
private void UpdateUIPanelControls()
{
if (!isUIPanelActive || clonedModel == null) return;
// 应用旋转
Quaternion targetRot = Quaternion.Euler(uiModelRotation.y, -uiModelRotation.x, 0);
clonedModel.transform.localRotation = Quaternion.Slerp(clonedModel.transform.localRotation, targetRot, 10f * Time.deltaTime);
// 应用缩放
uiModelCurrentZoom = Mathf.Lerp(uiModelCurrentZoom, uiModelTargetZoom, 5f * Time.deltaTime);
clonedModel.transform.localScale = Vector3.one * uiModelCurrentZoom;
}
private void UpdateCameraPosition()
{
// 平滑旋转
currentRotation = Vector3.Lerp(currentRotation, targetRotation, rotationSmoothness * Time.deltaTime);
// 平滑距离
currentDistance = Mathf.Lerp(currentDistance, targetDistance, rotationSmoothness * Time.deltaTime);
// 平滑平移
currentTargetPositionOffset = Vector3.Lerp(currentTargetPositionOffset, targetPositionOffset, rotationSmoothness * Time.deltaTime);
// 计算观察中心
Vector3 lookAtPoint = target.position + currentTargetPositionOffset;
// 计算相机位置
Quaternion rotation = Quaternion.Euler(currentRotation.y, currentRotation.x, 0);
Vector3 position = rotation * new Vector3(0, 0, -currentDistance) + lookAtPoint;
// 更新相机位置
transform.position = position;
transform.LookAt(lookAtPoint);
}
private bool IsInExcludedLayers(GameObject obj)
{
int layerMask = 1 << obj.layer;
return (excludedLayers.value & layerMask) != 0;
}
private bool ShouldExcludeObjectByTag(GameObject obj)
{
foreach (string tag in excludeTags)
{
if (obj.CompareTag(tag))
{
return true;
}
}
return false;
}
private bool IsChildOfTarget(Transform child)
{
Transform parent = child.parent;
while (parent != null)
{
if (parent == target)
{
return true;
}
parent = parent.parent;
}
return false;
}
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;
}
public void ResetMainView()
{
targetRotation = new Vector3(0, 45f, 0);
targetDistance = defaultZoomDistance;
targetPositionOffset = Vector3.zero;
}
public void FocusOnMainModel()
{
if (target != null)
{
targetPositionOffset = Vector3.zero;
}
}
public void ToggleAutoRotation(bool enable)
{
autoRotationSpeed = enable ? 0.5f : 0f;
}
public void SetEnableClickToFocus(bool enable)
{
enableClickToFocus = enable;
}
public void SetEnableMainModelControls(bool enable)
{
enableMainModelControls = enable;
}
void OnDestroy()
{
// 清理资源
if (highlightMaterial != null)
{
Destroy(highlightMaterial);
}
if (clonedModel != null)
{
Destroy(clonedModel);
}
ClearHighlight();
}
}