Files
tysdk-intergration/sdk-intergration/LocalPackages/LeviathanVideo/Runtime/LeviathanVideo3DRenderer.cs
2026-03-24 11:43:22 +08:00

302 lines
8.8 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.
/*----------------------------------------------------------------
// Copyright (C) 2025 Beijing All rights reserved.
//
// Author: huachangmiao
// Create Date: 2025/04/11
// Module Describe: 3D MeshRenderer 视频渲染组件
//----------------------------------------------------------------*/
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
/// <summary>
/// 3D MeshRenderer 视频渲染组件
/// 继承自 LeviathanVideoDecoderBase用于在 3D 物体(如 Cube、Plane上显示视频
/// </summary>
[RequireComponent(typeof(MeshRenderer))]
public unsafe class LeviathanVideo3DRenderer : LeviathanVideoDecoderBase
{
private static readonly int MainTexIdx = Shader.PropertyToID("_MainTex");
[Header("3D Renderer Settings")]
[SerializeField]
[Tooltip("是否翻转Y轴UV用于修正不同模型的UV方向")]
private bool _reverseY = true;
[SerializeField]
[Tooltip("材质所在的索引(用于多材质渲染器)")]
private int _materialIndex = 0;
private Material _material;
private new static void Log(string message)
{
LeviathanLogConfig.Log("Video3DRenderer", message);
}
private new static void LogWarning(string message)
{
LeviathanLogConfig.LogWarning("Video3DRenderer", message);
}
private new static void LogError(string message)
{
LeviathanLogConfig.LogError("Video3DRenderer", message);
}
/// <summary>
/// 启用渲染目标
/// </summary>
protected override void OnEnableRenderTarget()
{
MeshRenderer meshRenderer = GetComponent<MeshRenderer>();
if (meshRenderer)
{
meshRenderer.enabled = true;
}
}
/// <summary>
/// 禁用渲染目标
/// </summary>
protected override void OnDisableRenderTarget()
{
MeshRenderer meshRenderer = GetComponent<MeshRenderer>();
if (meshRenderer)
{
meshRenderer.enabled = false;
}
}
/// <summary>
/// 更新渲染目标尺寸
/// 3D渲染器不需要调整尺寸由模型本身决定
/// </summary>
protected override void OnUpdateRenderTargetSize()
{
// 3D渲染器不需要像UI那样自动调整尺寸
// 尺寸由模型本身决定
}
/// <summary>
/// 初始化材质
/// </summary>
protected override void OnInitMaterial()
{
MeshRenderer meshRenderer = GetComponent<MeshRenderer>();
if (meshRenderer)
{
UpdateMeshMaterial(meshRenderer);
}
}
/// <summary>
/// 更新材质
/// </summary>
protected override void OnUpdateMaterial()
{
MeshRenderer meshRenderer = GetComponent<MeshRenderer>();
if (meshRenderer)
{
UpdateMeshMaterial(meshRenderer);
}
}
protected void UpdateMeshMaterial(MeshRenderer meshRenderer)
{
if (_textures == null || _textures.Length < 3) return;
// 创建新材质或更新现有材质
Material newMaterial;
switch (_alphaType)
{
case PlayAlphaType.RightSide:
newMaterial = new Material(Shader.Find("LeviathanVideo/YUV420P_3D_Alpha_Right"));
newMaterial.SetFloat(ValidWidthRatioIdx, (float)_videoValidWidth / _videoWidth);
break;
case PlayAlphaType.BottomSide:
newMaterial = new Material(Shader.Find("LeviathanVideo/YUV420P_3D_Alpha_Bottom"));
break;
case PlayAlphaType.ChromaKey:
newMaterial = new Material(Shader.Find("LeviathanVideo/YUV420P_3D_ChromaKey"));
break;
case PlayAlphaType.None:
default:
newMaterial = new Material(Shader.Find("LeviathanVideo/YUV420P_3D"));
break;
}
// 设置纹理
newMaterial.SetTexture(MainTexIdx, _textures[0]);
newMaterial.SetTexture(UTexIdx, _textures[1]);
newMaterial.SetTexture(VTexIdx, _textures[2]);
// 设置Y轴翻转
newMaterial.SetFloat("_ReverseY", _reverseY ? 1 : 0);
// 清理旧材质
if (_material != null)
{
Destroy(_material);
}
_material = newMaterial;
// 应用材质到渲染器
var materials = meshRenderer.materials;
if (_materialIndex >= 0 && _materialIndex < materials.Length)
{
materials[_materialIndex] = newMaterial;
meshRenderer.materials = materials;
}
else
{
meshRenderer.material = newMaterial;
}
}
protected override void OnDestroy()
{
base.OnDestroy();
// 清理材质
if (_material != null)
{
Destroy(_material);
_material = null;
}
}
/// <summary>
/// 设置是否翻转Y轴
/// </summary>
public void SetReverseY(bool reverseY)
{
_reverseY = reverseY;
if (_material != null)
{
_material.SetFloat("_ReverseY", _reverseY ? 1 : 0);
}
}
/// <summary>
/// 设置材质索引
/// </summary>
public void SetMaterialIndex(int index)
{
_materialIndex = index;
if (_textures != null && _textures.Length > 0)
{
OnUpdateMaterial();
}
}
#if UNITY_EDITOR
private void OnValidate()
{
// 使用 delayCall 避免在 OnValidate 中调用 SendMessage 相关操作
var currentBytesFile = bytesFile;
var lastBytesFile = _lastBytesFile;
UnityEditor.EditorApplication.delayCall += () =>
{
if (this == null) return;
SetAlphaType(_alphaType);
// 检测 bytesFile 是否发生变化
if (currentBytesFile != lastBytesFile)
{
_lastBytesFile = currentBytesFile;
if (IsPlaying && currentBytesFile != null)
{
Log($"编辑器中检测到 bytesFile 变化,自动播放新视频");
Stop();
Play();
}
}
};
}
#endif
}
#if UNITY_EDITOR
[CustomEditor(typeof(LeviathanVideo3DRenderer))]
public class LeviathanVideo3DRendererEditor : Editor
{
// 格式化时间:秒转换为 HH:MM:SS.mmm 格式
private static string FormatTimeSeconds(double totalSeconds)
{
int hours = (int)(totalSeconds / 3600);
int minutes = (int)((totalSeconds % 3600) / 60);
int seconds = (int)(totalSeconds % 60);
int milliseconds = (int)((totalSeconds % 1) * 1000);
return $"{hours:D2}:{minutes:D2}:{seconds:D2}.{milliseconds:D3}";
}
// 格式化时间:微秒转换为 HH:MM:SS.mmm 格式
private static string FormatTimeMicroseconds(long microseconds)
{
return FormatTimeSeconds(microseconds / 1000000.0);
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
var mono = (LeviathanVideo3DRenderer)target;
// Display video dimensions as read-only fields
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.TextField("帧数", $"{mono.PlayFrameIndex} / {mono.FrameCount}");
// FrameInterval 单位是秒Duration 单位是微秒
EditorGUILayout.TextField("播放时间", FormatTimeSeconds(mono.PlayFrameIndex * mono.FrameInterval));
EditorGUILayout.TextField("总时间", FormatTimeMicroseconds(mono.Duration));
EditorGUILayout.TextField("FPS", mono.FrameInterval > 0 ? $"{1.0 / mono.FrameInterval:F2}" : "0");
EditorGUILayout.TextField("纹理尺寸", $"{mono.VideoWidth} × {mono.VideoHeight}");
EditorGUILayout.TextField("视频有效尺寸", $"{mono.VideoValidWidth} × {mono.VideoHeight}");
EditorGUI.EndDisabledGroup();
{
bool oldValue = mono.IsPlaying;
// 绘制checkbox
bool newValue = EditorGUILayout.Toggle("isPlaying", oldValue);
// 如果用户切换了checkbox
if (oldValue != newValue)
{
if (newValue)
{
mono.Play();
}
else
{
mono.Stop();
}
EditorUtility.SetDirty(target);
}
}
{
bool oldValue = mono.IsPause;
// 绘制checkbox
bool newValue = EditorGUILayout.Toggle("isPause", oldValue);
// 如果用户切换了checkbox
if (oldValue != newValue)
{
if (newValue)
{
mono.Pause();
}
else
{
mono.Resume();
}
EditorUtility.SetDirty(target);
}
}
this.Repaint();
}
}
#endif