holopy3/Assets/Klak/Spout/Runtime/SpoutReceiver.cs
2020-12-10 15:25:12 +01:00

212 lines
6.5 KiB
C#

// KlakSpout - Spout video frame sharing plugin for Unity
// https://github.com/keijiro/KlakSpout
using UnityEngine;
namespace Klak.Spout
{
[ExecuteInEditMode]
[AddComponentMenu("Klak/Spout/Spout Receiver")]
public sealed class SpoutReceiver : MonoBehaviour
{
#region Source settings
[SerializeField] string _sourceName;
public string sourceName {
get { return _sourceName; }
set {
if (_sourceName == value) return;
_sourceName = value;
RequestReconnect();
}
}
#endregion
#region Target settings
[SerializeField] RenderTexture _targetTexture;
public RenderTexture targetTexture {
get { return _targetTexture; }
set { _targetTexture = value; }
}
[SerializeField] Renderer _targetRenderer;
public Renderer targetRenderer {
get { return _targetRenderer; }
set { _targetRenderer = value; }
}
[SerializeField] string _targetMaterialProperty = null;
public string targetMaterialProperty {
get { return _targetMaterialProperty; }
set { _targetMaterialProperty = value; }
}
#endregion
#region Runtime properties
RenderTexture _receivedTexture;
public Texture receivedTexture {
get { return _targetTexture != null ? _targetTexture : _receivedTexture; }
}
#endregion
#region Private members
System.IntPtr _plugin;
Texture2D _sharedTexture;
Material _blitMaterial;
MaterialPropertyBlock _propertyBlock;
#endregion
#region Internal members
internal void RequestReconnect()
{
OnDisable();
}
#endregion
#region MonoBehaviour implementation
void OnDisable()
{
if (_plugin != System.IntPtr.Zero)
{
Util.IssuePluginEvent(PluginEntry.Event.Dispose, _plugin);
_plugin = System.IntPtr.Zero;
}
Util.Destroy(_sharedTexture);
}
void OnDestroy()
{
Util.Destroy(_blitMaterial);
Util.Destroy(_receivedTexture);
}
void Update()
{
// Release the plugin instance when the previously established
// connection is now invalid.
if (_plugin != System.IntPtr.Zero && !PluginEntry.CheckValid(_plugin))
{
Util.IssuePluginEvent(PluginEntry.Event.Dispose, _plugin);
_plugin = System.IntPtr.Zero;
}
// Plugin lazy initialization
if (_plugin == System.IntPtr.Zero)
{
_plugin = PluginEntry.CreateReceiver(_sourceName);
if (_plugin == System.IntPtr.Zero) return; // Spout may not be ready.
}
Util.IssuePluginEvent(PluginEntry.Event.Update, _plugin);
// Texture information retrieval
var ptr = PluginEntry.GetTexturePointer(_plugin);
var width = PluginEntry.GetTextureWidth(_plugin);
var height = PluginEntry.GetTextureHeight(_plugin);
// Resource validity check
if (_sharedTexture != null)
{
if (ptr != _sharedTexture.GetNativeTexturePtr() ||
width != _sharedTexture.width ||
height != _sharedTexture.height)
{
// Not match: Destroy to get refreshed.
Util.Destroy(_sharedTexture);
}
}
// Shared texture lazy (re)initialization
if (_sharedTexture == null && ptr != System.IntPtr.Zero)
{
_sharedTexture = Texture2D.CreateExternalTexture(
width, height, TextureFormat.ARGB32, false, false, ptr
);
_sharedTexture.hideFlags = HideFlags.DontSave;
// Destroy the previously allocated receiver texture to
// refresh specifications.
Util.Destroy(_receivedTexture);
}
// Texture format conversion with the blit shader
if (_sharedTexture != null)
{
// Blit shader lazy initialization
if (_blitMaterial == null)
{
_blitMaterial = new Material(Shader.Find("Hidden/Spout/Blit"));
_blitMaterial.hideFlags = HideFlags.DontSave;
}
if (_targetTexture != null)
{
// Blit the shared texture to the target texture.
Graphics.Blit(_sharedTexture, _targetTexture, _blitMaterial, 1);
}
else
{
// Receiver texture lazy initialization
if (_receivedTexture == null)
{
_receivedTexture = new RenderTexture
(_sharedTexture.width, _sharedTexture.height, 0);
_receivedTexture.hideFlags = HideFlags.DontSave;
}
// Blit the shared texture to the receiver texture.
Graphics.Blit(_sharedTexture, _receivedTexture, _blitMaterial, 1);
}
}
// Renderer override
if (_targetRenderer != null && receivedTexture != null)
{
// Material property block lazy initialization
if (_propertyBlock == null)
_propertyBlock = new MaterialPropertyBlock();
// Read-modify-write
_targetRenderer.GetPropertyBlock(_propertyBlock);
_propertyBlock.SetTexture(_targetMaterialProperty, receivedTexture);
_targetRenderer.SetPropertyBlock(_propertyBlock);
}
}
#if UNITY_EDITOR
// Invoke update on repaint in edit mode. This is needed to update the
// shared texture without getting the object marked dirty.
void OnRenderObject()
{
if (Application.isPlaying) return;
// Graphic.Blit used in Update will change the current active RT,
// so let us back it up and restore after Update.
var activeRT = RenderTexture.active;
Update();
RenderTexture.active = activeRT;
}
#endif
#endregion
}
}