// 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 } }