634 lines
19 KiB
C#
634 lines
19 KiB
C#
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();
|
||
}
|
||
} |