410 lines
14 KiB
C#
410 lines
14 KiB
C#
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
|
||
{
|
||
private ModelViewerSettings _viewerSettings;
|
||
public Transform modelRoot { get; private set; }
|
||
public Camera Camera => _camera;
|
||
|
||
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>();
|
||
_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 LoadModel(string model)
|
||
{
|
||
this.UnloadModel();
|
||
|
||
Object prefab = Resources.Load(model);
|
||
GameObject go = ((GameObject)Object.Instantiate(prefab));
|
||
var joint = go.GetComponent<Joint>();
|
||
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;
|
||
ModelViewerUtils.InitSetting(go, _viewerSettings);
|
||
Review();
|
||
}
|
||
|
||
public void UnloadModel()
|
||
{
|
||
if (_model != null)
|
||
{
|
||
_model.gameObject.SetActive(false);
|
||
Object.Destroy(_model.gameObject);
|
||
_model = null;
|
||
}
|
||
|
||
_rotating = Vector3.zero;
|
||
}
|
||
|
||
|
||
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
|
||
|
||
// var urpCamData = _camera.GetUniversalAdditionalCameraData();
|
||
// urpCamData.renderPostProcessing = false;
|
||
// urpCamData.requiresColorOption = CameraOverrideOption.On;
|
||
// urpCamData.requiresDepthOption = CameraOverrideOption.Off;
|
||
// urpCamData.requiresColorTexture = false;
|
||
// urpCamData.SetRenderer(1); // 关键步骤:指定“透明输出”的Renderer
|
||
}
|
||
|
||
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 = 1,
|
||
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)
|
||
{
|
||
Vector3 localRotation = modelRoot.localRotation.eulerAngles;
|
||
localRotation += _rotating;
|
||
modelRoot.localRotation = Quaternion.Euler(localRotation);
|
||
}
|
||
|
||
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<Transform>(true);
|
||
foreach (Transform t in transforms)
|
||
{
|
||
t.gameObject.layer = layer;
|
||
}
|
||
}
|
||
|
||
#region 旋转
|
||
|
||
public void StartRotate(Vector3 delta)
|
||
{
|
||
_rotating = delta;
|
||
}
|
||
|
||
public void StopRotate()
|
||
{
|
||
_rotating = Vector3.zero;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region MyRegion
|
||
|
||
/// <summary>
|
||
/// The rendertexture is not transparent. So if you want to the UI elements can be seen in the back of the models/particles in rendertexture,
|
||
/// you can set a maximunm two images for background.
|
||
/// Be careful if your image is 9 grid scaling, you must make sure the place holder is inside the middle box(dont cover from border to middle).
|
||
/// </summary>
|
||
/// <param name="image"></param>
|
||
public void SetBackground(GObject image)
|
||
{
|
||
SetBackground(image, null);
|
||
}
|
||
|
||
/// <summary>
|
||
/// The rendertexture is not transparent. So if you want to the UI elements can be seen in the back of the models/particles in rendertexture,
|
||
/// you can set a maximunm two images for background.
|
||
/// </summary>
|
||
/// <param name="image1"></param>
|
||
/// <param name="image2"></param>
|
||
public void SetBackground(GObject image1, GObject image2)
|
||
{
|
||
Image source1 = (Image)image1.displayObject;
|
||
Image source2 = image2 != null ? (Image)image2.displayObject : null;
|
||
|
||
Vector3 pos = _background.position;
|
||
pos.z = _camera.farClipPlane;
|
||
_background.position = pos;
|
||
|
||
Vector2[] uv = new Vector2[4];
|
||
Vector2[] uv2 = null;
|
||
|
||
Rect rect = _image.TransformRect(new Rect(0, 0, this._width, this._height), source1);
|
||
Rect uvRect = GetImageUVRect(source1, rect, uv);
|
||
|
||
if (source2 != null)
|
||
{
|
||
rect = _image.TransformRect(new Rect(0, 0, this._width, this._height), source2);
|
||
uv2 = new Vector2[4];
|
||
GetImageUVRect(source2, rect, uv2);
|
||
}
|
||
|
||
Vector3[] vertices = new Vector3[4];
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
Vector2 v = uv[i];
|
||
vertices[i] = new Vector3((v.x - uvRect.x) / uvRect.width * 2 - 1,
|
||
(v.y - uvRect.y) / uvRect.height * 2 - 1, 0);
|
||
}
|
||
|
||
Mesh mesh = new Mesh();
|
||
mesh.vertices = vertices;
|
||
mesh.uv = uv;
|
||
if (uv2 != null)
|
||
mesh.uv2 = uv2;
|
||
mesh.colors32 = new Color32[] { Color.white, Color.white, Color.white, Color.white };
|
||
mesh.triangles = new int[] { 0, 1, 2, 2, 3, 0 };
|
||
|
||
MeshFilter meshFilter = this._background.gameObject.GetComponent<MeshFilter>();
|
||
if (meshFilter == null)
|
||
meshFilter = this._background.gameObject.AddComponent<MeshFilter>();
|
||
meshFilter.mesh = mesh;
|
||
MeshRenderer meshRenderer = this._background.gameObject.GetComponent<MeshRenderer>();
|
||
if (meshRenderer == null)
|
||
meshRenderer = this._background.gameObject.AddComponent<MeshRenderer>();
|
||
#if (UNITY_5 || UNITY_5_3_OR_NEWER)
|
||
meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||
#else
|
||
meshRenderer.castShadows = false;
|
||
#endif
|
||
meshRenderer.receiveShadows = false;
|
||
Shader shader = Shader.Find("Game/FullScreen");
|
||
Material mat = new Material(shader);
|
||
mat.mainTexture = source1.texture.nativeTexture;
|
||
if (source2 != null)
|
||
mat.SetTexture("_Tex2", source2.texture.nativeTexture);
|
||
meshRenderer.material = mat;
|
||
}
|
||
|
||
Rect GetImageUVRect(Image image, Rect localRect, Vector2[] uv)
|
||
{
|
||
Rect imageRect = new Rect(0, 0, image.size.x, image.size.y);
|
||
Rect bound = ToolSet.Intersection(ref imageRect, ref localRect);
|
||
Rect uvRect = image.texture.uvRect;
|
||
|
||
if (image.scale9Grid != null)
|
||
{
|
||
Rect gridRect = (Rect)image.scale9Grid;
|
||
float sourceW = image.texture.width;
|
||
float sourceH = image.texture.height;
|
||
uvRect = Rect.MinMaxRect(Mathf.Lerp(uvRect.xMin, uvRect.xMax, gridRect.xMin / sourceW),
|
||
Mathf.Lerp(uvRect.yMin, uvRect.yMax, (sourceH - gridRect.yMax) / sourceH),
|
||
Mathf.Lerp(uvRect.xMin, uvRect.xMax, gridRect.xMax / sourceW),
|
||
Mathf.Lerp(uvRect.yMin, uvRect.yMax, (sourceH - gridRect.yMin) / sourceH));
|
||
|
||
float vw = imageRect.width - (sourceW - gridRect.width);
|
||
float vh = imageRect.height - (sourceH - gridRect.height);
|
||
uvRect = Rect.MinMaxRect(Mathf.Lerp(uvRect.xMin, uvRect.xMax, (bound.x - gridRect.x) / vw),
|
||
Mathf.Lerp(uvRect.yMin, uvRect.yMax,
|
||
(imageRect.height - bound.yMax - (sourceH - gridRect.yMax)) / vh),
|
||
Mathf.Lerp(uvRect.xMin, uvRect.xMax, (bound.xMax - gridRect.x) / vw),
|
||
Mathf.Lerp(uvRect.yMin, uvRect.yMax, (imageRect.height - bound.yMin - gridRect.y) / vh));
|
||
}
|
||
else
|
||
{
|
||
uvRect = Rect.MinMaxRect(Mathf.Lerp(uvRect.xMin, uvRect.xMax, bound.xMin / imageRect.width),
|
||
Mathf.Lerp(uvRect.yMin, uvRect.yMax, (imageRect.height - bound.yMax) / imageRect.height),
|
||
Mathf.Lerp(uvRect.xMin, uvRect.xMax, bound.xMax / imageRect.width),
|
||
Mathf.Lerp(uvRect.yMin, uvRect.yMax, (imageRect.height - bound.yMin) / imageRect.height));
|
||
}
|
||
|
||
uv[0] = uvRect.position;
|
||
uv[1] = new Vector2(uvRect.xMin, uvRect.yMax);
|
||
uv[2] = new Vector2(uvRect.xMax, uvRect.yMax);
|
||
uv[3] = new Vector2(uvRect.xMax, uvRect.yMin);
|
||
|
||
if (image.texture.rotated)
|
||
ToolSet.RotateUV(uv, ref image.texture.uvRect);
|
||
|
||
return uvRect;
|
||
}
|
||
|
||
#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
|
||
}
|
||
} |