using System; using FairyGUI; using FairyGUI.Utils; using System.Collections; using UnityEngine; using UnityEngine.Rendering.Universal; using Object = UnityEngine.Object; namespace NBF { [Serializable] public class ModelViewRenderImage { public Transform modelRoot { get; private set; } public Camera Camera => _camera; public RenderTexture RT => _renderTexture; public ModelViewerSettings ViewerSettings { get; private set; } Camera _camera; Image _image; Transform _root; Transform _background; Transform _model; RenderTexture _renderTexture; int _width; int _height; bool _cacheTexture; // Vector3 _rotating; const int RENDER_LAYER = 0; const int HIDDEN_LAYER = 10; public ModelViewRenderImage(GGraph holder) { _width = (int)holder.width; _height = (int)holder.height; _cacheTexture = true; _image = new Image(); holder.SetNativeObject(_image); _root = new GameObject("RenderImage").transform; _root.transform.position = Vector3.zero; // _root.SetParent(_camera.transform, false); SetLayer(_root.gameObject, HIDDEN_LAYER); Object.DontDestroyOnLoad(_root.gameObject); Object prefab = Resources.Load("RenderTexture/RenderImageCamera"); GameObject go = (GameObject)Object.Instantiate(prefab, _root, false); _camera = go.GetComponent(); _camera.transform.position = Vector3.zero; _camera.cullingMask = 1 << RENDER_LAYER; _camera.enabled = false; Object.DontDestroyOnLoad(_camera.gameObject); modelRoot = new GameObject("model_root").transform; modelRoot.SetParent(_root, false); _background = new GameObject("background").transform; _background.SetParent(_root, false); _image.onAddedToStage.Add(OnAddedToStage); _image.onRemovedFromStage.Add(OnRemoveFromStage); // _viewerSettings = new ModelViewerSettings(); if (_image.stage != null) OnAddedToStage(); else _camera.gameObject.SetActive(false); } public void SetModel(GameObject model, ModelViewerSettings settings) { this.UnloadModel(); ViewerSettings = settings; if (model == null) return; var joint = model.GetComponent(); if (joint != null) { Object.Destroy(joint); } _model = model.transform; _model.SetParent(this.modelRoot, false); _model.localPosition = Vector3.zero; _model.localScale = Vector3.one; _model.localEulerAngles = Vector3.zero; if (ViewerSettings == null) { ViewerSettings = new ModelViewerSettings(); ModelViewerUtils.InitSetting(model, ViewerSettings); } Review(); } // public void LoadModel(string model, ModelViewerSettings settings) // { // this.UnloadModel(); // ViewerSettings = settings; // // Object prefab = Resources.Load(model); // if (prefab == null) return; // GameObject go = ((GameObject)Object.Instantiate(prefab)); // var joint = go.GetComponent(); // if (joint != null) // { // Object.Destroy(joint); // } // // _model = go.transform; // _model.SetParent(this.modelRoot, false); // _model.localPosition = Vector3.zero; // _model.localScale = Vector3.one; // _model.localEulerAngles = Vector3.zero; // if (ViewerSettings == null) // { // ViewerSettings = new ModelViewerSettings(); // ModelViewerUtils.InitSetting(go, ViewerSettings); // } // // Review(); // } public void UnloadModel() { if (_model != null) { ViewerSettings = null; _model.gameObject.SetActive(false); Object.Destroy(_model.gameObject); _model = null; } } public void Review() { _model.position = ViewerSettings.objectPosition; _model.localScale = ViewerSettings.objectScale; _model.eulerAngles = ViewerSettings.objectRotation; _camera.clearFlags = CameraClearFlags.Nothing; _camera.backgroundColor = new Color(0, 0, 0, 0); // 完全透明 _camera.transform.position = ViewerSettings.cameraPosition; _camera.transform.LookAt(ViewerSettings.cameraTarget); _camera.orthographic = ViewerSettings.cameraOrtho; _camera.orthographicSize = ViewerSettings.cameraSize; _camera.orthographicSize /= ViewerSettings.camerasScaleFactor; _camera.fieldOfView = ViewerSettings.cameraFov; float fov = 2 * Mathf.Rad2Deg * Mathf.Atan2( Mathf.Tan(_camera.fieldOfView * Mathf.Deg2Rad / 2), ViewerSettings.camerasScaleFactor ); if (fov < 0) fov += 180; _camera.fieldOfView = fov; _camera.nearClipPlane = 0.001f; _camera.farClipPlane = 10000; _camera.depthTextureMode = DepthTextureMode.Depth; _camera.clearFlags = CameraClearFlags.Color; _camera.GetUniversalAdditionalCameraData().renderPostProcessing = false; //URP only } public void Dispose() { UnloadModel(); Object.Destroy(_camera.gameObject); DestroyTexture(); _image.Dispose(); _image = null; } void CreateTexture() { if (_renderTexture != null) return; _renderTexture = new RenderTexture(1024, 1024, 24, RenderTextureFormat.ARGB32) { antiAliasing = 8, filterMode = FilterMode.Bilinear, anisoLevel = 0, useMipMap = false }; // _renderTexture.Create(); _image.texture = new NTexture(_renderTexture); _image.shader = "Unlit/Transparent"; } void DestroyTexture() { if (_renderTexture != null) { Object.Destroy(_renderTexture); _renderTexture = null; _image.texture = null; } } void OnAddedToStage() { if (_renderTexture == null) CreateTexture(); Timers.inst.AddUpdate(Render); _camera.gameObject.SetActive(true); Render(); } void OnRemoveFromStage() { if (!_cacheTexture) DestroyTexture(); Timers.inst.Remove(Render); _camera.gameObject.SetActive(false); } void Render(object param = null) { // if (_rotating != Vector3.zero && this.modelRoot != null) // { // // 使用 Quaternion 来避免欧拉角带来的问题 // modelRoot.Rotate(_rotating, Space.World); // } SetLayer(_root.gameObject, RENDER_LAYER); _camera.targetTexture = _renderTexture; RenderTexture old = RenderTexture.active; RenderTexture.active = _renderTexture; GL.Clear(true, true, Color.clear); _camera.Render(); RenderTexture.active = old; SetLayer(_root.gameObject, HIDDEN_LAYER); UpdateDebugSave(); } void SetLayer(GameObject go, int layer) { Transform[] transforms = go.GetComponentsInChildren(true); foreach (Transform t in transforms) { t.gameObject.layer = layer; } } #region 旋转 // 在 ModelViewRenderImage 中添加新字段 private Vector2 _lastMousePosition; private bool _isDragging; public void StartRotate(Vector2 currentMousePosition) { _lastMousePosition = currentMousePosition; _isDragging = true; } public void UpdateRotation(Vector2 currentMousePosition) { if (!_isDragging || modelRoot == null || _camera == null) return; Vector2 delta = currentMousePosition - _lastMousePosition; // 使用适中的灵敏度 float sensitivity = 0.2f; float rotX = -delta.y * sensitivity; float rotY = -delta.x * sensitivity; Quaternion yRotation = Quaternion.AngleAxis(rotY, _camera.transform.up); Quaternion xRotation = Quaternion.AngleAxis(rotX, _camera.transform.right); // 组合旋转并应用 modelRoot.rotation = yRotation * xRotation * modelRoot.rotation; _lastMousePosition = currentMousePosition; } public void StopRotate() { _isDragging = false; } // 重置到初始状态的方法 public void ResetToInitialRotation() { modelRoot.localRotation = Quaternion.identity; _isDragging = false; } #endregion #region MyRegion void UpdateDebugSave() { if (Input.GetKeyDown(KeyCode.F9)) { SaveRenderTextureToPNG(_renderTexture, "RenderTextureDebug.png"); } } void SaveRenderTextureToPNG(RenderTexture rt, string fileName) { if (rt == null) { Debug.LogWarning("RenderTexture 为 null,无法保存。"); return; } RenderTexture current = RenderTexture.active; RenderTexture.active = rt; Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.RGBA32, false); tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); tex.Apply(); byte[] bytes = tex.EncodeToPNG(); string path = System.IO.Path.Combine(Application.dataPath, "../" + fileName); System.IO.File.WriteAllBytes(path, bytes); Debug.Log($"✅ RenderTexture 已保存到: {path}"); UnityEngine.Object.Destroy(tex); RenderTexture.active = current; } #endregion } }