├── .gitignore ├── CHANGELOG.md ├── DisguiseUnityRenderStream ├── Disguise.RenderStream.asmdef ├── Editor │ ├── Disguise.RenderStream.Editor.asmdef │ ├── HDRP │ │ └── DisguisePostProcessEditor.cs │ ├── DisguiseRemoteParametersEditor.cs │ ├── RenderingPipelineDefines.cs │ └── ReorderableListUtility.cs ├── DisguiseRenderStreamSettings.cs ├── DisguiseTimeControl.cs ├── HDRP │ ├── Resources │ │ └── DisguiseColourFramePostProcess.shader │ └── DisguiseCameraCaptureVolume.cs ├── DisguiseRemoteParameters.cs └── DisguiseCameraCapture.cs ├── .github └── workflows │ └── create_release_on_tag.yml ├── LICENSE └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.meta 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # RS2.0-Unity-v0 2 | Compatible with disguise designer version r25.0 and above. 3 | * Upgraded ABI to RS2.0 4 | * Added pluginVersion to Schema 5 | -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/Disguise.RenderStream.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Disguise.RenderStream", 3 | "references": [ 4 | "Unity.RenderPipelines.Core.Runtime", 5 | "Unity.RenderPipelines.HighDefinition.Runtime" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": true, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/Editor/Disguise.RenderStream.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Disguise.RenderStream.Editor", 3 | "references": [ 4 | "Disguise.RenderStream", 5 | "Unity.RenderPipelines.Core.Runtime", 6 | "Unity.RenderPipelines.Core.Editor", 7 | "Unity.RenderPipelines.HighDefinition.Runtime", 8 | "Unity.RenderPipelines.HighDefinition.Editor" 9 | ], 10 | "includePlatforms": [ 11 | "Editor" 12 | ], 13 | "excludePlatforms": [], 14 | "allowUnsafeCode": false, 15 | "overrideReferences": false, 16 | "precompiledReferences": [], 17 | "autoReferenced": true, 18 | "defineConstraints": [], 19 | "versionDefines": [], 20 | "noEngineReferences": false 21 | } -------------------------------------------------------------------------------- /.github/workflows/create_release_on_tag.yml: -------------------------------------------------------------------------------- 1 | name: Create Release On Tag 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-22.04 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | 16 | - name: Determine Tag Name 17 | id: tagname 18 | run: echo "TAG_NAME=${GITHUB_REF/refs\/tags\/}" >> $GITHUB_OUTPUT 19 | 20 | - name: Create Unity Package 21 | id: build_package 22 | run: | 23 | # Create a zip archive of the repository contents, exclude hidden files 24 | zip -r "${{ steps.tagname.outputs.TAG_NAME }}.zip" . -x ".*" 25 | 26 | - name: Upload Unity Package 27 | uses: actions/upload-artifact@v3 28 | with: 29 | name: unity-package 30 | path: ${{ steps.tagname.outputs.TAG_NAME }}.zip 31 | 32 | - name: Create Release 33 | id: create_release 34 | uses: softprops/action-gh-release@v1 35 | with: 36 | name: ${{ steps.tagname.outputs.TAG_NAME }} 37 | files: ${{ steps.tagname.outputs.TAG_NAME }}.zip 38 | tag_name: ${{ steps.tagname.outputs.TAG_NAME }} 39 | draft: false 40 | prerelease: false 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/Editor/HDRP/DisguisePostProcessEditor.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_PIPELINE_HDRP 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.Emit; 5 | using UnityEditor.Rendering; 6 | using UnityEngine; 7 | using UnityEngine.Rendering.HighDefinition; 8 | using UnityEditor; 9 | using UnityEditor.PackageManager; 10 | 11 | [VolumeComponentEditor(typeof(DisguiseCameraCaptureAfterPostProcess))] 12 | public sealed class DisguiseCameraCaptureAfterPostProcessEditor : VolumeComponentEditor 13 | { 14 | static class Labels 15 | { 16 | internal static readonly GUIContent Width = new GUIContent("Width"); 17 | internal static readonly GUIContent Height = new GUIContent("Height"); 18 | } 19 | 20 | SerializedDataParameter _width; 21 | SerializedDataParameter _height; 22 | 23 | public override void OnEnable() 24 | { 25 | var o = new PropertyFetcher(serializedObject); 26 | 27 | _width = Unpack(o.Find(x => x.width)); 28 | _height = Unpack(o.Find(x => x.height)); 29 | } 30 | 31 | public override void OnInspectorGUI() 32 | { 33 | EditorGUILayout.LabelField("RenderStream", EditorStyles.miniLabel); 34 | PropertyField(_width, Labels.Width); 35 | PropertyField(_height, Labels.Height); 36 | } 37 | } 38 | #endif // UNITY_PIPELINE_HDRP -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/DisguiseRenderStreamSettings.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System; 4 | using System.IO; 5 | 6 | class DisguiseRenderStreamSettings : ScriptableObject 7 | { 8 | public enum SceneControl 9 | { 10 | Manual, 11 | Selection 12 | } 13 | 14 | [SerializeField] 15 | public SceneControl sceneControl; 16 | 17 | #if UNITY_EDITOR 18 | [UnityEditor.Callbacks.DidReloadScripts] 19 | private static void OnReloadScripts() 20 | { 21 | // Ensure resource is created 22 | DisguiseRenderStreamSettings.GetOrCreateSettings(); 23 | } 24 | #endif 25 | 26 | public static DisguiseRenderStreamSettings GetOrCreateSettings() 27 | { 28 | var settings = Resources.Load("DisguiseRenderStreamSettings"); 29 | if (settings == null) 30 | { 31 | Debug.Log("Using default DisguiseRenderStreamSettings"); 32 | settings = ScriptableObject.CreateInstance(); 33 | settings.sceneControl = SceneControl.Manual; 34 | #if UNITY_EDITOR 35 | if (!Directory.Exists("Assets/Resources")) 36 | Directory.CreateDirectory("Assets/Resources"); 37 | UnityEditor.AssetDatabase.CreateAsset(settings, "Assets/Resources/DisguiseRenderStreamSettings.asset"); 38 | UnityEditor.AssetDatabase.SaveAssets(); 39 | #endif 40 | } 41 | return settings; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/DisguiseTimeControl.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Playables; 3 | 4 | using System; 5 | 6 | [AddComponentMenu("Disguise RenderStream/Time Control")] 7 | [RequireComponent(typeof(PlayableDirector))] 8 | public class DisguiseTimeControl : MonoBehaviour 9 | { 10 | void Start() 11 | { 12 | playableDirector = GetComponent(); 13 | playableDirector.timeUpdateMode = DirectorUpdateMode.Manual; 14 | } 15 | 16 | void Update() 17 | { 18 | if (DisguiseRenderStream.newFrameData) 19 | { 20 | if (DisguiseRenderStream.frameData.localTime < playableDirector.initialTime || DisguiseRenderStream.frameData.localTimeDelta <= 0) 21 | playableDirector.Pause(); 22 | else 23 | playableDirector.Resume(); 24 | 25 | playableDirector.time = Math.Max(0, DisguiseRenderStream.frameData.localTime - playableDirector.initialTime); 26 | 27 | switch (playableDirector.extrapolationMode) 28 | { 29 | case DirectorWrapMode.Hold: 30 | if (playableDirector.time > playableDirector.duration) 31 | playableDirector.time = playableDirector.duration; 32 | break; 33 | case DirectorWrapMode.Loop: 34 | playableDirector.time = (playableDirector.time % playableDirector.duration); 35 | break; 36 | } 37 | 38 | playableDirector.Evaluate(); 39 | } 40 | } 41 | 42 | private PlayableDirector playableDirector; 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Disguise Technologies Ltd 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/Editor/DisguiseRemoteParametersEditor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using UnityEditorInternal; 4 | using Disguise.RenderStream; 5 | 6 | [CustomEditor(typeof(DisguiseRemoteParameters))] 7 | public class DisguiseRemoteParametersEditor : Editor 8 | { 9 | private SerializedProperty _fieldsProp; 10 | 11 | public override void OnInspectorGUI() 12 | { 13 | base.OnInspectorGUI(); 14 | 15 | serializedObject.Update(); 16 | 17 | ReorderableListUtility.DoLayoutListWithFoldout(list); 18 | 19 | GUILayout.BeginHorizontal(); 20 | if (GUILayout.Button("Select All")) 21 | { 22 | for (int i = 0; i < _fieldsProp.arraySize; i++) 23 | { 24 | var element = _fieldsProp.GetArrayElementAtIndex(i); 25 | element.FindPropertyRelative("exposed").boolValue = true; 26 | } 27 | } 28 | if (GUILayout.Button("Select None")) 29 | { 30 | for (int i = 0; i < _fieldsProp.arraySize; i++) 31 | { 32 | var element = _fieldsProp.GetArrayElementAtIndex(i); 33 | element.FindPropertyRelative("exposed").boolValue = false; 34 | } 35 | } 36 | GUILayout.EndHorizontal(); 37 | 38 | serializedObject.ApplyModifiedProperties(); 39 | } 40 | 41 | private void OnEnable() 42 | { 43 | _fieldsProp = this.serializedObject.FindProperty("fields"); 44 | list = ReorderableListUtility.CreateAutoLayout(_fieldsProp, true, true, false, false); 45 | } 46 | 47 | private ReorderableList list; 48 | } -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/HDRP/Resources/DisguiseColourFramePostProcess.shader: -------------------------------------------------------------------------------- 1 | Shader "Hidden/Shader/DisguiseColourFramePostProcess" 2 | { 3 | HLSLINCLUDE 4 | 5 | #pragma target 4.5 6 | #pragma only_renderers d3d11 ps4 xboxone vulkan metal switch 7 | 8 | #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" 9 | #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" 10 | #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" 11 | #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/FXAA.hlsl" 12 | #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/RTUpscale.hlsl" 13 | 14 | struct Attributes 15 | { 16 | uint vertexID : SV_VertexID; 17 | UNITY_VERTEX_INPUT_INSTANCE_ID 18 | }; 19 | 20 | struct Varyings 21 | { 22 | float4 positionCS : SV_POSITION; 23 | float2 texcoord : TEXCOORD0; 24 | UNITY_VERTEX_OUTPUT_STEREO 25 | }; 26 | 27 | Varyings Vert(Attributes input) 28 | { 29 | Varyings output; 30 | UNITY_SETUP_INSTANCE_ID(input); 31 | UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); 32 | output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID); 33 | output.texcoord = GetFullScreenTriangleTexCoord(input.vertexID); 34 | return output; 35 | } 36 | 37 | // List of properties to control your post process effect 38 | float _Intensity; 39 | TEXTURE2D_X(_InputTexture); 40 | 41 | float4 CustomPostProcess(Varyings input) : SV_Target 42 | { 43 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 44 | 45 | uint2 positionSS = input.texcoord * _ScreenSize.xy; 46 | float3 outColor = LOAD_TEXTURE2D_X(_InputTexture, positionSS).xyz; 47 | outColor = max(1.055f * pow(outColor, 0.416666667f) - 0.055f, 0.0f); 48 | 49 | return float4(outColor, 1); 50 | } 51 | 52 | ENDHLSL 53 | 54 | SubShader 55 | { 56 | Pass 57 | { 58 | Name "DisguiseColourFramePostProcess" 59 | 60 | ZWrite Off 61 | ZTest Always 62 | Blend Off 63 | Cull Off 64 | 65 | HLSLPROGRAM 66 | #pragma fragment CustomPostProcess 67 | #pragma vertex Vert 68 | ENDHLSL 69 | } 70 | } 71 | Fallback Off 72 | } 73 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # RenderStream Unity Plugin 2 | 3 | ![alt text](https://download.disguise.one/media/6921/unity.png) 4 | 5 | For more info please refer to our [RenderStream and Unity Help page](https://help.disguise.one/Content/Configuring/Render-engines/RenderStream-Unity.htm) 6 | 7 | A **Demo Unity Project** can be found on the [disguise Resources page](https://download.disguise.one/#resources) 8 | 9 | ## Importing the RenderStream Unity Plugin 10 | 11 | * Copy/Import the top-level **DisguiseUnityRenderStream** folder to your Unity Project's **Assets** folder. 12 | * If you get an error RegistryKey/Registry is not part of namespace Microsoft.Win32 go to File > Build Settings > Player Settings... > Other Settings: 13 | * If available **change Api Compatibility level to .NET 4.x.** 14 | * Else, change Scripting runtime version to .NET 3.5 equivalent. 15 | * If there is an error about unsafe code tick allow 'unsafe' code, this is project wide and should not be done unless unity ignores the asmdef for this plugin. 16 | 17 | ## Using the RenderStream Unity Plugin 18 | 19 | The act of importing the **DisguiseUnityRenderStream** plugin to your Unity Project's Asset folder, is enough to enable RenderStream in your built executable. 20 | 21 | More control can be added using the included Disguise RenderStream components: 22 | 23 | * **To enable control of GameObject(s) properties**, attach a Disguise RenderStream > **Remote Parameters** component to the appropriate game object for remote parameter control. 24 | * Note: you can enable/disable the exact GameObject properties using the List editor in the Unity Inspector. 25 | * **To add designer timeline control**, attach a Disguise RenderStream > **Time Control** component to a Playable Director 26 | 27 | ## Building a RenderStream asset for disguise designer 28 | 29 | To use your Unity Project with disguise designer, you build an executable and import it into your designer project. To do this: 30 | * Ensure Build Settings are set to build a **Windows x86_64** executable 31 | * Copy the build folder to your **RenderStream Projects** folder 32 | * In designer, set up a new RenderStream Layer and point the Asset to your built executable. 33 | 34 | ## Notes: 35 | 36 | * Without a disguise instance running and sending data to the Unity camera, if you run your scene the camera will be placed at the world origin and cannot be moved (it's position is overriden every frame). 37 | * Firewall settings can interfere with transport streams. You may see a Windows security dialog pop-up the first time you run the asset. 38 | * Click Enable / Allow on this Windows pop-up to allow RenderStream communication to/from designer. 39 | * If the firewall is the problem check your outbound firewall rules on the Unity machine, and the inbound firewall rules on the disguise machine for either software being specifically blocked. 40 | -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/Editor/RenderingPipelineDefines.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using UnityEditor; 4 | using UnityEngine.Rendering; 5 | 6 | [InitializeOnLoad] 7 | public class RenderingPipelineDefines 8 | { 9 | enum PipelineType 10 | { 11 | Unsupported, 12 | BuiltInPipeline, 13 | UniversalPipeline, 14 | HDRPipeline 15 | } 16 | 17 | static RenderingPipelineDefines() 18 | { 19 | UpdateDefines(); 20 | } 21 | 22 | static void UpdateDefines() 23 | { 24 | var pipeline = GetPipeline(); 25 | 26 | if (pipeline == PipelineType.UniversalPipeline) 27 | AddDefine("UNITY_PIPELINE_URP"); 28 | else 29 | RemoveDefine("UNITY_PIPELINE_URP"); 30 | 31 | if (pipeline == PipelineType.HDRPipeline) 32 | AddDefine("UNITY_PIPELINE_HDRP"); 33 | else 34 | RemoveDefine("UNITY_PIPELINE_HDRP"); 35 | } 36 | 37 | static PipelineType GetPipeline() 38 | { 39 | #if UNITY_2019_1_OR_NEWER 40 | if (GraphicsSettings.renderPipelineAsset != null) 41 | { 42 | var srpType = GraphicsSettings.renderPipelineAsset.GetType().ToString(); 43 | 44 | if (srpType.Contains("HDRenderPipelineAsset")) 45 | return PipelineType.HDRPipeline; 46 | 47 | if (srpType.Contains("UniversalRenderPipelineAsset") || srpType.Contains("LightweightRenderPipelineAsset")) 48 | return PipelineType.UniversalPipeline; 49 | 50 | return PipelineType.Unsupported; 51 | } 52 | 53 | #elif UNITY_2017_1_OR_NEWER 54 | if (GraphicsSettings.renderPipelineAsset != null) 55 | return PipelineType.Unsupported; // SRP not supported before 2019 56 | #endif 57 | 58 | return PipelineType.BuiltInPipeline; 59 | } 60 | 61 | static void AddDefine(string define) 62 | { 63 | var definesList = GetDefines(); 64 | if (definesList.Contains(define)) 65 | return; 66 | definesList.Add(define); 67 | SetDefines(definesList); 68 | } 69 | 70 | public static void RemoveDefine(string define) 71 | { 72 | var definesList = GetDefines(); 73 | if (!definesList.Contains(define)) 74 | return; 75 | definesList.Remove(define); 76 | SetDefines(definesList); 77 | } 78 | 79 | public static List GetDefines() 80 | { 81 | var target = EditorUserBuildSettings.activeBuildTarget; 82 | var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(target); 83 | var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup); 84 | 85 | return defines.Split(';').ToList(); 86 | } 87 | 88 | public static void SetDefines(List definesList) 89 | { 90 | var target = EditorUserBuildSettings.activeBuildTarget; 91 | var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(target); 92 | var defines = string.Join(";", definesList.ToArray()); 93 | 94 | PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, defines); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/HDRP/DisguiseCameraCaptureVolume.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_STANDALONE_WIN 2 | #define PLUGIN_AVAILABLE 3 | #endif 4 | 5 | #if UNITY_PIPELINE_HDRP 6 | using System; 7 | using System.Threading; 8 | using UnityEngine; 9 | using UnityEngine.Rendering; 10 | using UnityEngine.Rendering.HighDefinition; 11 | 12 | [Serializable, VolumeComponentMenu("Post-processing/Custom/DisguiseCameraCapturePostProcess")] 13 | public sealed class DisguiseCameraCaptureAfterPostProcess : CustomPostProcessVolumeComponent, IPostProcessComponent 14 | { 15 | public IntParameter width = new IntParameter(1920); 16 | public IntParameter height = new IntParameter(1080); 17 | public RenderTexture colourRenderTexture; 18 | public Texture2D colourTex2D; 19 | 20 | public bool IsActive() => m_colourMaterial != null; 21 | public override CustomPostProcessInjectionPoint injectionPoint => CustomPostProcessInjectionPoint.AfterPostProcess; 22 | 23 | public override void Setup() 24 | { 25 | CreateMaterial(); 26 | CreateTexture(); 27 | } 28 | 29 | public override void Render(CommandBuffer cmd, HDCamera camera, RTHandle source, RTHandle destination) 30 | { 31 | if (m_colourMaterial == null) 32 | return; 33 | m_colourMaterial.SetTexture("_InputTexture", source); 34 | cmd.Blit(source.rt, colourRenderTexture, m_colourMaterial); 35 | FetchTarget(colourRenderTexture, colourTex2D); 36 | 37 | Camera[] cams = Camera.allCameras; 38 | foreach(Camera cam in cams) 39 | { 40 | if (cam.name != camera.camera.name) 41 | continue; 42 | DisguiseCameraCapture dcc = cam.GetComponent(); 43 | if (dcc == null) 44 | continue; 45 | if (dcc.m_frameSender == null) 46 | continue; 47 | dcc.m_frameSender.SendFrame(colourTex2D); 48 | } 49 | 50 | //SaveAsRawImage(colourTex2D, "dump.raw"); 51 | } 52 | 53 | public override void Cleanup() 54 | { 55 | CoreUtils.Destroy(m_colourMaterial); 56 | } 57 | 58 | private void CreateMaterial() 59 | { 60 | if (Shader.Find(kColourShaderName) != null) 61 | m_colourMaterial = new Material(Shader.Find(kColourShaderName)); 62 | else 63 | Debug.LogError($"Unable to find Disguise RenderStream shader '{kColourShaderName}'"); 64 | } 65 | 66 | private void CreateTexture() 67 | { 68 | colourRenderTexture = new RenderTexture(width.value, height.value, 24, RenderTextureFormat.ARGBFloat); 69 | colourTex2D = new Texture2D(width.value, height.value, TextureFormat.RGBAFloat, false); 70 | } 71 | 72 | // Debug code... 73 | private void FetchTarget(RenderTexture renderTexture, Texture2D texture2D) 74 | { 75 | RenderTexture activeRenderTexture = RenderTexture.active; 76 | RenderTexture unflipped = RenderTexture.GetTemporary(renderTexture.width, renderTexture.height, 0, renderTexture.format); 77 | RenderTexture.active = unflipped; 78 | Graphics.Blit(renderTexture, unflipped, new Vector2(1.0f, -1.0f), new Vector2(0.0f, 1.0f)); 79 | Graphics.CopyTexture(unflipped, texture2D); 80 | RenderTexture.active = activeRenderTexture; 81 | RenderTexture.ReleaseTemporary(unflipped); 82 | } 83 | 84 | private void SaveAsRawImage(Texture2D texture2D, string filename) 85 | { 86 | byte[] imageData = texture2D.GetRawTextureData(); 87 | var thread = new Thread(() => System.IO.File.WriteAllBytes(filename, imageData)); 88 | thread.Start(); 89 | } 90 | 91 | private Material m_colourMaterial; 92 | private const string kColourShaderName = "Hidden/Shader/DisguiseColourFramePostProcess"; 93 | } 94 | #endif // UNITY_PIPELINE_HDRP 95 | -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/Editor/ReorderableListUtility.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 Siyuan Wang. 3 | // Licensed under the Apache License Version 2.0. See LICENSE file in the project root for full license information. 4 | // 5 | 6 | using System.Collections.Generic; 7 | using UnityEditor; 8 | using UnityEditorInternal; 9 | using UnityEngine; 10 | 11 | public static class ReorderableListUtility 12 | { 13 | public static ReorderableList CreateAutoLayout(SerializedProperty property, float columnSpacing = 10f) 14 | { 15 | return CreateAutoLayout(property, true, true, true, true, null, null, columnSpacing); 16 | } 17 | 18 | public static ReorderableList CreateAutoLayout(SerializedProperty property, string[] headers, float?[] columnWidth = null, float columnSpacing = 10f) 19 | { 20 | return CreateAutoLayout(property, true, true, true, true, headers, columnWidth, columnSpacing); 21 | } 22 | 23 | public static ReorderableList CreateAutoLayout(SerializedProperty property, bool draggable, bool displayHeader, bool displayAddButton, bool displayRemoveButton, float columnSpacing = 10f) 24 | { 25 | return CreateAutoLayout(property, draggable, displayHeader, displayAddButton, displayRemoveButton, null, null, columnSpacing); 26 | } 27 | 28 | public static ReorderableList CreateAutoLayout(SerializedProperty property, bool draggable, bool displayHeader, bool displayAddButton, bool displayRemoveButton, string[] headers, float?[] columnWidth = null, float columnSpacing = 10f) 29 | { 30 | var list = new ReorderableList(property.serializedObject, property, draggable, displayHeader, displayAddButton, displayRemoveButton); 31 | var colmuns = new List(); 32 | 33 | list.drawElementCallback = DrawElement(list, GetColumnsFunc(list, headers, columnWidth, colmuns), columnSpacing); 34 | list.drawHeaderCallback = DrawHeader(list, GetColumnsFunc(list, headers, columnWidth, colmuns), columnSpacing); 35 | 36 | return list; 37 | } 38 | 39 | public static bool DoLayoutListWithFoldout(ReorderableList list, string label = null) 40 | { 41 | var property = list.serializedProperty; 42 | property.isExpanded = EditorGUILayout.Foldout(property.isExpanded, label != null ? label : property.displayName); 43 | if (property.isExpanded) 44 | { 45 | list.DoLayoutList(); 46 | } 47 | 48 | return property.isExpanded; 49 | } 50 | 51 | private static ReorderableList.ElementCallbackDelegate DrawElement(ReorderableList list, System.Func> getColumns, float columnSpacing) 52 | { 53 | return (rect, index, isActive, isFocused) => 54 | { 55 | var property = list.serializedProperty; 56 | var columns = getColumns(); 57 | var layouts = CalculateColumnLayout(columns, rect, columnSpacing); 58 | 59 | var arect = rect; 60 | arect.height = EditorGUIUtility.singleLineHeight; 61 | for (var ii = 0; ii < columns.Count; ii++) 62 | { 63 | var c = columns[ii]; 64 | 65 | arect.width = layouts[ii]; 66 | EditorGUI.PropertyField(arect, property.GetArrayElementAtIndex(index).FindPropertyRelative(c.PropertyName), GUIContent.none); 67 | arect.x += arect.width + columnSpacing; 68 | } 69 | }; 70 | } 71 | 72 | private static ReorderableList.HeaderCallbackDelegate DrawHeader(ReorderableList list, System.Func> getColumns, float columnSpacing) 73 | { 74 | return (rect) => 75 | { 76 | var columns = getColumns(); 77 | 78 | if (list.draggable) 79 | { 80 | rect.width -= 15; 81 | rect.x += 15; 82 | } 83 | 84 | var layouts = CalculateColumnLayout(columns, rect, columnSpacing); 85 | var arect = rect; 86 | arect.height = EditorGUIUtility.singleLineHeight; 87 | for (var ii = 0; ii < columns.Count; ii++) 88 | { 89 | var c = columns[ii]; 90 | 91 | arect.width = layouts[ii]; 92 | EditorGUI.LabelField(arect, c.DisplayName); 93 | arect.x += arect.width + columnSpacing; 94 | } 95 | }; 96 | } 97 | 98 | private static System.Func> GetColumnsFunc(ReorderableList list, string[] headers, float?[] columnWidth, List output) 99 | { 100 | var property = list.serializedProperty; 101 | return () => 102 | { 103 | if (output.Count <= 0 || list.serializedProperty != property) 104 | { 105 | output.Clear(); 106 | property = list.serializedProperty; 107 | 108 | if (property.isArray && property.arraySize > 0) 109 | { 110 | var it = property.GetArrayElementAtIndex(0).Copy(); 111 | var prefix = it.propertyPath; 112 | var index = 0; 113 | if (it.Next(true)) 114 | { 115 | do 116 | { 117 | if (it.propertyPath.StartsWith(prefix)) 118 | { 119 | var c = new Column(); 120 | c.DisplayName = (headers != null && headers.Length > index) ? headers[index] : it.displayName; 121 | c.PropertyName = it.propertyPath.Substring(prefix.Length + 1); 122 | c.Width = (columnWidth != null && columnWidth.Length > index) ? columnWidth[index] : null; 123 | 124 | output.Add(c); 125 | } 126 | else 127 | { 128 | break; 129 | } 130 | 131 | index += 1; 132 | } 133 | while (it.Next(false)); 134 | } 135 | } 136 | } 137 | 138 | return output; 139 | }; 140 | } 141 | 142 | private static List CalculateColumnLayout(List columns, Rect rect, float columnSpacing) 143 | { 144 | var autoWidth = rect.width; 145 | var autoCount = 0; 146 | foreach (var column in columns) 147 | { 148 | if (column.Width.HasValue) 149 | { 150 | autoWidth -= column.Width.Value; 151 | } 152 | else 153 | { 154 | autoCount += 1; 155 | } 156 | } 157 | 158 | autoWidth -= (columns.Count - 1) * columnSpacing; 159 | autoWidth /= autoCount; 160 | 161 | var widths = new List(columns.Count); 162 | foreach (var column in columns) 163 | { 164 | if (column.Width.HasValue) 165 | { 166 | widths.Add(column.Width.Value); 167 | } 168 | else 169 | { 170 | widths.Add(autoWidth); 171 | } 172 | } 173 | 174 | return widths; 175 | } 176 | 177 | private struct Column 178 | { 179 | public string DisplayName; 180 | public string PropertyName; 181 | public float? Width; 182 | } 183 | } -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/DisguiseRemoteParameters.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Rendering; 3 | using UnityEngine.Serialization; 4 | 5 | using System; 6 | using System.Reflection; 7 | using System.Linq; 8 | using System.Collections.Generic; 9 | using Disguise.RenderStream; 10 | 11 | [AddComponentMenu("Disguise RenderStream/Remote Parameters")] 12 | public class DisguiseRemoteParameters : MonoBehaviour 13 | { 14 | [SerializeField] 15 | public UnityEngine.Object exposedObject; 16 | public string prefix; 17 | [System.Serializable] 18 | public struct ExposedField 19 | { 20 | public ExposedField(bool exposed_, string fieldName_, string groupName_) 21 | { 22 | exposed = exposed_; 23 | fieldName = fieldName_; 24 | groupName = groupName_; 25 | } 26 | public bool exposed; 27 | public string fieldName; 28 | public string groupName; 29 | } 30 | [HideInInspector] 31 | public List fields; 32 | 33 | void Reset() 34 | { 35 | prefix = Guid.NewGuid().ToString(); 36 | } 37 | 38 | #if UNITY_EDITOR 39 | void OnValidate() 40 | { 41 | if (fields == null) 42 | fields = new List(); 43 | if (exposedObject == null) 44 | { 45 | fields.Clear(); 46 | return; 47 | } 48 | 49 | HashSet displayNames = new HashSet(); 50 | if (exposedObject is Material material) 51 | { 52 | if (material.mainTexture is RenderTexture) 53 | displayNames.Add("Main texture"); 54 | } 55 | else if (exposedObject is Transform transform) 56 | { 57 | displayNames.Add("Transform"); 58 | } 59 | UnityEditor.SerializedObject so = new UnityEditor.SerializedObject(exposedObject); 60 | UnityEditor.SerializedProperty property = so.GetIterator(); 61 | if (property.NextVisible(true)) 62 | { 63 | do 64 | { 65 | displayNames.Add(property.displayName); 66 | } 67 | while (property.NextVisible(false)); 68 | } 69 | fields.RemoveAll(field => !displayNames.Contains(field.fieldName)); 70 | foreach (string displayName in displayNames) 71 | { 72 | if (!fields.Any(field => field.fieldName == displayName)) 73 | fields.Add(new ExposedField(true, displayName, "")); 74 | } 75 | } 76 | 77 | private ManagedRemoteParameter createField(string group, string displayName_, string key_, RemoteParameterType type, string suffix, string undecoratedSuffix, float min, float max, float step, object defaultValue, string[] options) 78 | { 79 | string key = key_ + (String.IsNullOrEmpty(undecoratedSuffix) ? "" : "_" + undecoratedSuffix); 80 | string displayName = exposedObject.name + " " + displayName_ + (String.IsNullOrEmpty(suffix) ? "" : " " + suffix); 81 | if (string.IsNullOrEmpty(group)) group = "Properties"; 82 | 83 | ManagedRemoteParameter parameter = new ManagedRemoteParameter(); 84 | parameter.group = group; 85 | parameter.displayName = displayName; 86 | parameter.key = key; 87 | parameter.type = type; 88 | parameter.min = min; 89 | parameter.max = max; 90 | parameter.step = step; 91 | parameter.defaultValue = defaultValue; 92 | parameter.options = options; 93 | parameter.dmxOffset = -1; 94 | parameter.dmxType = RemoteParameterDmxType.RS_DMX_16_BE; 95 | return parameter; 96 | } 97 | 98 | public List exposedParameters() 99 | { 100 | List parameters = new List(); 101 | if (exposedObject == null) 102 | return parameters; 103 | if (exposedObject is Material material) 104 | { 105 | if (material.mainTexture is RenderTexture) 106 | { 107 | if (fields.Any(field => field.exposed && field.fieldName == "Main texture")) 108 | { 109 | string group = fields.FirstOrDefault(field => field.fieldName == "Main texture").groupName; 110 | parameters.Add(createField(group, "Main texture", prefix + " " + "mainTexture", RemoteParameterType.RS_PARAMETER_IMAGE, "", "", 0, 255, 1, 0, new string[0])); 111 | } 112 | } 113 | } 114 | else if (exposedObject is Transform transform) 115 | { 116 | if (System.Object.ReferenceEquals(transform.transform, transform)) 117 | { 118 | if (fields.Any(field => field.exposed && field.fieldName == "Transform")) 119 | { 120 | string group = fields.FirstOrDefault(field => field.fieldName == "Transform").groupName; 121 | parameters.Add(createField(group, "Transform", prefix + " " + "transform", RemoteParameterType.RS_PARAMETER_TRANSFORM, "", "", 0, 255, 1, 0, new string[0])); 122 | } 123 | } 124 | } 125 | UnityEditor.SerializedObject so = new UnityEditor.SerializedObject(exposedObject); 126 | UnityEditor.SerializedProperty property = so.GetIterator(); 127 | if (property.NextVisible(true)) 128 | { 129 | Stack nesting = new Stack(); 130 | int depth = property.depth; 131 | string previousName = ""; 132 | do 133 | { 134 | for (; depth < property.depth; ++depth) 135 | nesting.Push(previousName); 136 | for (; depth > property.depth; --depth) 137 | nesting.Pop(); 138 | string propertyPath = property.propertyPath; 139 | MemberInfo info = GetMemberInfoFromPropertyPath(propertyPath); 140 | if (info == null && propertyPath.StartsWith("m_")) 141 | { 142 | string modifiedPropertyPath = char.ToLower(propertyPath[2]) + propertyPath.Substring(3); 143 | info = GetMemberInfoFromPropertyPath(modifiedPropertyPath); 144 | if (info != null) 145 | propertyPath = modifiedPropertyPath; 146 | } 147 | HeaderAttribute header = info != null ? info.GetCustomAttributes(typeof(HeaderAttribute), true).FirstOrDefault() as HeaderAttribute : null; 148 | RangeAttribute range = info != null ? info.GetCustomAttributes(typeof(RangeAttribute), true).FirstOrDefault() as RangeAttribute : null; 149 | MinAttribute min = info != null ? info.GetCustomAttributes(typeof(MinAttribute), true).FirstOrDefault() as MinAttribute : null; 150 | if (header != null) 151 | nesting.Push(header.header); 152 | string group = String.Join(" ", new Stack(nesting).ToArray()); 153 | if (fields.Any(field => field.exposed && field.fieldName == property.displayName && !string.IsNullOrEmpty(field.groupName))) 154 | { 155 | group = fields.FirstOrDefault(field => field.fieldName == property.displayName).groupName; 156 | } 157 | if (fields.Any(field => !field.exposed && field.fieldName == property.displayName)) 158 | { 159 | //Debug.Log("Unexposed property: " + property.displayName); 160 | } 161 | else if (!property.editable) 162 | { 163 | Debug.Log("Uneditable property: " + property.displayName); 164 | } 165 | else if (info == null) 166 | { 167 | Debug.Log("Unreflected property: " + property.propertyPath); 168 | } 169 | else if (property.propertyType == UnityEditor.SerializedPropertyType.Float) 170 | { 171 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "", "", 172 | min != null ? min.min : (range != null ? range.min : -1.0f), range != null ? range.max : +1.0f, 0.001f, property.floatValue, new string[0])); 173 | } 174 | else if (property.propertyType == UnityEditor.SerializedPropertyType.Integer) 175 | { 176 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "", "", 177 | min != null ? min.min : (range != null ? range.min : -1000), range != null ? range.max : +1000, 1, property.intValue, new string[0])); 178 | } 179 | else if (property.propertyType == UnityEditor.SerializedPropertyType.Boolean) 180 | { 181 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "", "", 182 | 0, 1, 1, property.boolValue ? 1 : 0, new string[] { "Off", "On" })); 183 | } 184 | else if (property.propertyType == UnityEditor.SerializedPropertyType.Enum && property.enumNames.Length > 1) 185 | { 186 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "", "", 187 | 0, property.enumNames.Length - 1, 1, property.enumValueIndex, property.enumDisplayNames)); 188 | } 189 | else if (property.propertyType == UnityEditor.SerializedPropertyType.Vector2) 190 | { 191 | Vector2 v = property.vector2Value; 192 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "x", "x", 193 | min != null ? min.min : (range != null ? range.min : -1.0f), range != null ? range.max : +1.0f, 0.001f, v.x, new string[0])); 194 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "y", "y", 195 | min != null ? min.min : (range != null ? range.min : -1.0f), range != null ? range.max : +1.0f, 0.001f, v.y, new string[0])); 196 | } 197 | else if (property.propertyType == UnityEditor.SerializedPropertyType.Vector2Int) 198 | { 199 | Vector2Int v = property.vector2IntValue; 200 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "x", "x", 201 | min != null ? min.min : (range != null ? range.min : -1000), range != null ? range.max : +1000, 0.001f, v.x, new string[0])); 202 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "y", "y", 203 | min != null ? min.min : (range != null ? range.min : -1000), range != null ? range.max : +1000, 0.001f, v.y, new string[0])); 204 | } 205 | else if (property.propertyType == UnityEditor.SerializedPropertyType.Vector3) 206 | { 207 | Vector3 v = property.vector3Value; 208 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "x", "x", 209 | min != null ? min.min : (range != null ? range.min : -1.0f), range != null ? range.max : +1.0f, 0.001f, v.x, new string[0])); 210 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "y", "y", 211 | min != null ? min.min : (range != null ? range.min : -1.0f), range != null ? range.max : +1.0f, 0.001f, v.y, new string[0])); 212 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "z", "z", 213 | min != null ? min.min : (range != null ? range.min : -1.0f), range != null ? range.max : +1.0f, 0.001f, v.z, new string[0])); 214 | } 215 | else if (property.propertyType == UnityEditor.SerializedPropertyType.Vector3Int) 216 | { 217 | Vector3Int v = property.vector3IntValue; 218 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "x", "x", 219 | min != null ? min.min : (range != null ? range.min : -1000), range != null ? range.max : +1000, 0.001f, v.x, new string[0])); 220 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "y", "y", 221 | min != null ? min.min : (range != null ? range.min : -1000), range != null ? range.max : +1000, 0.001f, v.y, new string[0])); 222 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "z", "z", 223 | min != null ? min.min : (range != null ? range.min : -1000), range != null ? range.max : +1000, 0.001f, v.z, new string[0])); 224 | } 225 | else if (property.propertyType == UnityEditor.SerializedPropertyType.Vector4) 226 | { 227 | Vector4 v = property.vector4Value; 228 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "x", "x", 229 | min != null ? min.min : (range != null ? range.min : -1.0f), range != null ? range.max : +1.0f, 0.001f, v.x, new string[0])); 230 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "y", "y", 231 | min != null ? min.min : (range != null ? range.min : -1.0f), range != null ? range.max : +1.0f, 0.001f, v.y, new string[0])); 232 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "z", "z", 233 | min != null ? min.min : (range != null ? range.min : -1.0f), range != null ? range.max : +1.0f, 0.001f, v.z, new string[0])); 234 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "w", "w", 235 | min != null ? min.min : (range != null ? range.min : -1.0f), range != null ? range.max : +1.0f, 0.001f, v.w, new string[0])); 236 | } 237 | else if (property.propertyType == UnityEditor.SerializedPropertyType.Color) 238 | { 239 | Color v = property.colorValue; 240 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "r", "r", 241 | min != null ? min.min : (range != null ? range.min : 0.0f), range != null ? range.max : 1.0f, 0.001f, v.r, new string[0])); 242 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "g", "g", 243 | min != null ? min.min : (range != null ? range.min : 0.0f), range != null ? range.max : 1.0f, 0.001f, v.g, new string[0])); 244 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "b", "b", 245 | min != null ? min.min : (range != null ? range.min : 0.0f), range != null ? range.max : 1.0f, 0.001f, v.b, new string[0])); 246 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_NUMBER, "a", "a", 247 | min != null ? min.min : (range != null ? range.min : 0.0f), range != null ? range.max : 1.0f, 0.001f, v.a, new string[0])); 248 | } 249 | else if (property.propertyType == UnityEditor.SerializedPropertyType.ObjectReference) 250 | { 251 | if (property.objectReferenceValue is RenderTexture texture) 252 | { 253 | parameters.Add(createField("", property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_IMAGE, "", "", 0, 255, 1, 0, new string[0])); 254 | } 255 | else if (property.objectReferenceValue is Transform transform) 256 | { 257 | parameters.Add(createField("", property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_TRANSFORM, "", "", 0, 255, 1, 0, new string[0])); 258 | } 259 | else 260 | { 261 | Debug.Log("Unsupported exposed object: " + property.displayName); 262 | } 263 | } 264 | else if (property.propertyType == UnityEditor.SerializedPropertyType.ObjectReference) 265 | { 266 | if (property.objectReferenceValue is RenderTexture texture) 267 | { 268 | parameters.Add(createField("", property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_IMAGE, "", "", 0, 255, 1, 0, new string[0])); 269 | } 270 | else if (property.objectReferenceValue is Transform transform) 271 | { 272 | parameters.Add(createField("", property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_TRANSFORM, "", "", 0, 255, 1, 0, new string[0])); 273 | } 274 | else 275 | { 276 | Debug.Log("Unsupported exposed object: " + property.displayName); 277 | } 278 | } 279 | else if (property.propertyType == UnityEditor.SerializedPropertyType.String) 280 | { 281 | parameters.Add(createField(group, property.displayName, prefix + " " + propertyPath, RemoteParameterType.RS_PARAMETER_TEXT, "", "", 0, 0, 0, property.stringValue, new string[0])); 282 | } 283 | else 284 | { 285 | Debug.Log("Unsupported exposed property: " + property.displayName); 286 | } 287 | previousName = property.displayName; 288 | if (header != null) 289 | nesting.Pop(); 290 | } 291 | while(property.NextVisible(false)); 292 | } 293 | return parameters; 294 | } 295 | #endif 296 | 297 | public MemberInfo GetMemberInfoFromPropertyPath(string propertyPath) 298 | { 299 | if (exposedObject == null) 300 | return null; 301 | MemberInfo info = null; 302 | for (Type currentType = exposedObject.GetType(); info == null && currentType != null; currentType = currentType.BaseType) 303 | { 304 | FieldInfo fieldInfo = currentType.GetField(propertyPath, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 305 | if (fieldInfo != null && (!fieldInfo.IsInitOnly || fieldInfo.FieldType.IsSubclassOf(typeof(UnityEngine.Object)))) 306 | { 307 | info = (MemberInfo)fieldInfo; 308 | return info; 309 | } 310 | 311 | } 312 | for (Type currentType = exposedObject.GetType(); info == null && currentType != null; currentType = currentType.BaseType) 313 | { 314 | PropertyInfo propertyInfo = currentType.GetProperty(propertyPath, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 315 | if (propertyInfo != null && propertyInfo.CanRead && (propertyInfo.CanWrite || propertyInfo.PropertyType.IsSubclassOf(typeof(UnityEngine.Object)))) 316 | { 317 | info = (MemberInfo)propertyInfo; 318 | return info; 319 | } 320 | } 321 | return info; 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /DisguiseUnityRenderStream/DisguiseCameraCapture.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_STANDALONE_WIN 2 | #define PLUGIN_AVAILABLE 3 | #endif 4 | 5 | using System; 6 | using System.Net; 7 | using System.Net.Sockets; 8 | using System.IO; 9 | using System.Collections; 10 | using System.Collections.Generic; 11 | using System.Runtime.InteropServices; 12 | using System.Reflection; 13 | 14 | using Microsoft.Win32; 15 | 16 | using Unity.Collections.LowLevel.Unsafe; 17 | using UnityEngine; 18 | using UnityEngine.Rendering; 19 | using UnityEngine.Serialization; 20 | using UnityEngine.SceneManagement; 21 | using System.Threading; 22 | using System.Runtime.Remoting; 23 | using Disguise.RenderStream; 24 | using System.Linq; 25 | 26 | #if UNITY_EDITOR 27 | class DisguiseRenderStream : UnityEditor.Build.IPreprocessBuildWithReport 28 | #else 29 | class DisguiseRenderStream 30 | #endif 31 | { 32 | #if UNITY_EDITOR 33 | public int callbackOrder { get { return 0; } } 34 | public void OnPreprocessBuild(UnityEditor.Build.Reporting.BuildReport report) 35 | { 36 | DisguiseRenderStreamSettings settings = DisguiseRenderStreamSettings.GetOrCreateSettings(); 37 | schema = new ManagedSchema(); 38 | schema.channels = new string[0]; 39 | switch (settings.sceneControl) 40 | { 41 | case DisguiseRenderStreamSettings.SceneControl.Selection: 42 | Debug.Log("Generating scene-selection schema for: " + SceneManager.sceneCountInBuildSettings + " scenes"); 43 | schema.scenes = new ManagedRemoteParameters[SceneManager.sceneCountInBuildSettings]; 44 | if (SceneManager.sceneCountInBuildSettings == 0) 45 | Debug.LogWarning("No scenes in build settings. Schema will be empty."); 46 | break; 47 | case DisguiseRenderStreamSettings.SceneControl.Manual: 48 | default: 49 | Debug.Log("Generating manual schema"); 50 | schema.scenes = new ManagedRemoteParameters[1]; 51 | break; 52 | } 53 | } 54 | 55 | [UnityEditor.Callbacks.PostProcessSceneAttribute(0)] 56 | static void OnPostProcessScene() 57 | { 58 | if (!UnityEditor.BuildPipeline.isBuildingPlayer) 59 | return; 60 | 61 | Scene activeScene = SceneManager.GetActiveScene(); 62 | DisguiseRenderStreamSettings settings = DisguiseRenderStreamSettings.GetOrCreateSettings(); 63 | switch (settings.sceneControl) 64 | { 65 | case DisguiseRenderStreamSettings.SceneControl.Selection: 66 | { 67 | if (activeScene.buildIndex < 0 || activeScene.buildIndex >= SceneManager.sceneCountInBuildSettings) 68 | { 69 | Debug.Log("Ignoring scene: " + activeScene.name + " (not in build's scene list)"); 70 | return; 71 | } 72 | 73 | Debug.Log("Processing scene: " + activeScene.name + " (" + activeScene.buildIndex + '/' + SceneManager.sceneCountInBuildSettings + ')'); 74 | 75 | HashSet channels = new HashSet(schema.channels); 76 | channels.UnionWith(getTemplateCameras().Select(camera => camera.name)); 77 | schema.channels = channels.ToArray(); 78 | 79 | List parameters = new List(); 80 | foreach (var parameter in UnityEngine.Object.FindObjectsOfType(typeof(DisguiseRemoteParameters)) as DisguiseRemoteParameters[]) 81 | parameters.AddRange(parameter.exposedParameters()); 82 | schema.scenes[activeScene.buildIndex] = new ManagedRemoteParameters(); 83 | ManagedRemoteParameters scene = schema.scenes[activeScene.buildIndex]; 84 | scene.name = activeScene.name; 85 | scene.parameters = parameters.ToArray(); 86 | break; 87 | } 88 | case DisguiseRenderStreamSettings.SceneControl.Manual: 89 | default: 90 | { 91 | Debug.Log("Processing scene: " + activeScene.name); 92 | 93 | HashSet channels = new HashSet(schema.channels); 94 | channels.UnionWith(getTemplateCameras().Select(camera => camera.name)); 95 | schema.channels = channels.ToArray(); 96 | 97 | if (schema.scenes[0] == null) 98 | { 99 | schema.scenes[0] = new ManagedRemoteParameters(); 100 | schema.scenes[0].parameters = new ManagedRemoteParameter[0]; 101 | } 102 | ManagedRemoteParameters scene = schema.scenes[0]; 103 | scene.name = "Default"; 104 | List parameters = new List(scene.parameters); 105 | foreach (var parameter in UnityEngine.Object.FindObjectsOfType(typeof(DisguiseRemoteParameters)) as DisguiseRemoteParameters[]) 106 | parameters.AddRange(parameter.exposedParameters()); 107 | scene.parameters = parameters.ToArray(); 108 | break; 109 | } 110 | } 111 | } 112 | 113 | [UnityEditor.Callbacks.PostProcessBuildAttribute(0)] 114 | static void OnPostProcessBuild(UnityEditor.BuildTarget target, string pathToBuiltProject) 115 | { 116 | if (target != UnityEditor.BuildTarget.StandaloneWindows64) 117 | { 118 | Debug.LogError("DisguiseRenderStream: RenderStream is only available for 64-bit Windows (x86_64)."); 119 | return; 120 | } 121 | 122 | if (PluginEntry.instance.IsAvailable == false) 123 | { 124 | Debug.LogError("DisguiseRenderStream: RenderStream DLL not available, could not save schema"); 125 | return; 126 | } 127 | 128 | RS_ERROR error = PluginEntry.instance.saveSchema(pathToBuiltProject, ref schema); 129 | if (error != RS_ERROR.RS_ERROR_SUCCESS) 130 | { 131 | Debug.LogError(string.Format("DisguiseRenderStream: Failed to save schema {0}", error)); 132 | } 133 | } 134 | #endif 135 | 136 | [RuntimeInitializeOnLoadMethod] 137 | static void OnLoad() 138 | { 139 | if (Application.isEditor) 140 | { 141 | // No play in editor support currently 142 | return; 143 | } 144 | 145 | if (PluginEntry.instance.IsAvailable == false) 146 | { 147 | Debug.LogError("DisguiseRenderStream: RenderStream DLL not available"); 148 | return; 149 | } 150 | 151 | string pathToBuiltProject = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; 152 | RS_ERROR error = PluginEntry.instance.loadSchema(pathToBuiltProject, ref schema); 153 | if (error == RS_ERROR.RS_ERROR_SUCCESS) 154 | { 155 | sceneFields = new SceneFields[schema.scenes.Length]; 156 | Scene activeScene = SceneManager.GetActiveScene(); 157 | if (activeScene.isLoaded) 158 | OnSceneLoaded(activeScene, LoadSceneMode.Single); 159 | SceneManager.sceneLoaded += OnSceneLoaded; 160 | } 161 | else 162 | { 163 | Debug.LogError(string.Format("DisguiseRenderStream: Failed to load schema {0}", error)); 164 | schema = new ManagedSchema(); 165 | schema.channels = new string[0]; 166 | schema.scenes = new ManagedRemoteParameters[1]; 167 | schema.scenes[0] = new ManagedRemoteParameters(); 168 | schema.scenes[0].name = "Default"; 169 | schema.scenes[0].parameters = new ManagedRemoteParameter[0]; 170 | sceneFields = new SceneFields[schema.scenes.Length]; 171 | CreateStreams(); 172 | } 173 | } 174 | 175 | static void OnSceneLoaded(Scene loadedScene, LoadSceneMode mode) 176 | { 177 | CreateStreams(); 178 | int sceneIndex = 0; 179 | DisguiseRenderStreamSettings settings = DisguiseRenderStreamSettings.GetOrCreateSettings(); 180 | switch (settings.sceneControl) 181 | { 182 | case DisguiseRenderStreamSettings.SceneControl.Selection: 183 | sceneIndex = loadedScene.buildIndex; 184 | break; 185 | } 186 | DisguiseRemoteParameters[] remoteParameters = UnityEngine.Object.FindObjectsOfType(typeof(DisguiseRemoteParameters)) as DisguiseRemoteParameters[]; 187 | ManagedRemoteParameters scene = schema.scenes[sceneIndex]; 188 | sceneFields[sceneIndex] = new SceneFields{ numerical = new List(), images = new List(), texts = new List() }; 189 | SceneFields fields = sceneFields[sceneIndex]; 190 | for (int j = 0; j < scene.parameters.Length;) 191 | { 192 | string key = scene.parameters[j].key; 193 | DisguiseRemoteParameters remoteParams = Array.Find(remoteParameters, rp => key.StartsWith(rp.prefix)); 194 | ObjectField field = new ObjectField(); 195 | field.target = remoteParams.exposedObject; 196 | field.info = null; 197 | if (field.info == null && key.EndsWith("_x")) 198 | { 199 | string baseKey = key.Substring(0, key.Length - 2); 200 | field.info = remoteParams.GetMemberInfoFromPropertyPath(baseKey.Substring(remoteParams.prefix.Length + 1)); 201 | Type fieldType = field.FieldType; 202 | if ((fieldType == typeof(Vector2) || fieldType == typeof(Vector2Int)) && 203 | j + 1 < scene.parameters.Length && scene.parameters[j + 1].key == baseKey + "_y") 204 | { 205 | j += 2; 206 | } 207 | else if ((fieldType == typeof(Vector3) || fieldType == typeof(Vector3Int)) && 208 | j + 2 < scene.parameters.Length && scene.parameters[j + 1].key == baseKey + "_y" && scene.parameters[j + 2].key == baseKey + "_z") 209 | { 210 | j += 3; 211 | } 212 | else if (fieldType == typeof(Vector4) && 213 | j + 3 < scene.parameters.Length && scene.parameters[j + 1].key == baseKey + "_y" && scene.parameters[j + 2].key == baseKey + "_z" && scene.parameters[j + 3].key == baseKey + "_w") 214 | { 215 | j += 4; 216 | } 217 | else 218 | { 219 | field.info = null; 220 | } 221 | } 222 | if (field.info == null && key.EndsWith("_r")) 223 | { 224 | string baseKey = key.Substring(0, key.Length - 2); 225 | field.info = remoteParams.GetMemberInfoFromPropertyPath(baseKey.Substring(remoteParams.prefix.Length + 1)); 226 | Type fieldType = field.FieldType; 227 | if (fieldType == typeof(Color) && 228 | j + 3 < scene.parameters.Length && scene.parameters[j + 1].key == baseKey + "_g" && scene.parameters[j + 2].key == baseKey + "_b" && scene.parameters[j + 3].key == baseKey + "_a") 229 | { 230 | j += 4; 231 | } 232 | else 233 | { 234 | field.info = null; 235 | } 236 | } 237 | if (field.info == null) 238 | { 239 | field.info = remoteParams.GetMemberInfoFromPropertyPath(key.Substring(remoteParams.prefix.Length + 1)); 240 | ++j; 241 | } 242 | if (field.info == null) 243 | { 244 | Debug.LogError("Unhandled remote parameter: " + key); 245 | } 246 | 247 | if (field.FieldType == typeof(Texture)) 248 | fields.images.Add(field); 249 | else if (field.FieldType == typeof(String) || field.FieldType == typeof(String[])) 250 | fields.texts.Add(field); 251 | else 252 | fields.numerical.Add(field); 253 | } 254 | } 255 | 256 | static void CreateStreams() 257 | { 258 | if (PluginEntry.instance.IsAvailable == false) 259 | { 260 | Debug.LogError("DisguiseRenderStream: RenderStream DLL not available"); 261 | return; 262 | } 263 | 264 | do 265 | { 266 | RS_ERROR error = PluginEntry.instance.getStreams(ref streams); 267 | if (error != RS_ERROR.RS_ERROR_SUCCESS) 268 | { 269 | Debug.LogError(string.Format("DisguiseRenderStream: Failed to get streams {0}", error)); 270 | return; 271 | } 272 | 273 | if (streams.Length == 0) 274 | { 275 | Debug.Log("Waiting for streams..."); 276 | Thread.Sleep(1000); 277 | } 278 | } while (streams.Length == 0); 279 | 280 | Debug.Log(string.Format("Found {0} streams", streams.Length)); 281 | foreach (var camera in cameras) 282 | UnityEngine.Object.Destroy(camera); 283 | cameras = new GameObject[streams.Length]; 284 | 285 | // cache the template cameras prior to instantiating our instance cameras 286 | Camera[] templateCameras = getTemplateCameras(); 287 | const int cullUIOnly = ~(1 << 5); 288 | 289 | for (int i = 0; i < streams.Length; ++i) 290 | { 291 | StreamDescription stream = streams[i]; 292 | Camera channelCamera = DisguiseRenderStream.GetChannelCamera(stream.channel); 293 | if (channelCamera) 294 | { 295 | cameras[i] = UnityEngine.Object.Instantiate(channelCamera.gameObject, channelCamera.gameObject.transform.parent); 296 | cameras[i].name = stream.name; 297 | } 298 | else if (Camera.main) 299 | { 300 | cameras[i] = UnityEngine.Object.Instantiate(Camera.main.gameObject, Camera.main.gameObject.transform.parent); 301 | cameras[i].name = stream.name; 302 | } 303 | else 304 | { 305 | cameras[i] = new GameObject(stream.name); 306 | cameras[i].AddComponent(); 307 | } 308 | 309 | GameObject cameraObject = cameras[i]; 310 | Camera camera = cameraObject.GetComponent(); 311 | camera.enabled = true; // ensure the camera component is enable 312 | camera.cullingMask &= cullUIOnly; // cull the UI so RenderStream and other error messages don't render to RenderStream outputs 313 | DisguiseCameraCapture capture = cameraObject.GetComponent(typeof(DisguiseCameraCapture)) as DisguiseCameraCapture; 314 | if (capture == null) 315 | capture = cameraObject.AddComponent(typeof(DisguiseCameraCapture)) as DisguiseCameraCapture; 316 | // Blocks HDRP streams in r18.2 317 | // #if UNITY_PIPELINE_HDRP 318 | // Volume volume = cameraObject.GetComponent(); 319 | // if (volume == null) 320 | // volume = cameraObject.AddComponent(); 321 | // volume.profile = ScriptableObject.CreateInstance(); 322 | // var captureAfterPostProcess = volume.profile.Add(true); 323 | // captureAfterPostProcess.width.value = (Int32)stream.width; 324 | // captureAfterPostProcess.height.value = (Int32)stream.height; 325 | // #endif 326 | camera.enabled = true; 327 | } 328 | 329 | // stop template cameras impacting performance 330 | foreach (var templateCam in templateCameras) 331 | { 332 | templateCam.enabled = false; // disable the camera component on the template camera so these cameras won't render and impact performance 333 | // we don't want to disable the game object otherwise we won't be able to find the object again to instantiate instance cameras if we get a streams changed event 334 | } 335 | 336 | frameData = new FrameData(); 337 | awaiting = false; 338 | } 339 | 340 | static public IEnumerator AwaitFrame() 341 | { 342 | if (awaiting) 343 | yield break; 344 | awaiting = true; 345 | DisguiseRenderStreamSettings settings = DisguiseRenderStreamSettings.GetOrCreateSettings(); 346 | List scratchTextures = new List(); 347 | while (true) 348 | { 349 | yield return new WaitForEndOfFrame(); 350 | RS_ERROR error = PluginEntry.instance.awaitFrameData(500, ref frameData); 351 | if (error == RS_ERROR.RS_ERROR_QUIT) 352 | Application.Quit(); 353 | if (error == RS_ERROR.RS_ERROR_STREAMS_CHANGED) 354 | CreateStreams(); 355 | switch (settings.sceneControl) 356 | { 357 | case DisguiseRenderStreamSettings.SceneControl.Selection: 358 | if (SceneManager.GetActiveScene().buildIndex != frameData.scene) 359 | { 360 | newFrameData = false; 361 | SceneManager.LoadScene((int)frameData.scene); 362 | yield break; 363 | } 364 | break; 365 | } 366 | newFrameData = (error == RS_ERROR.RS_ERROR_SUCCESS); 367 | if (newFrameData && frameData.scene < schema.scenes.Length) 368 | { 369 | ManagedRemoteParameters spec = schema.scenes[frameData.scene]; 370 | SceneFields fields = sceneFields[frameData.scene]; 371 | int nNumericalParameters = 0; 372 | int nImageParameters = 0; 373 | int nTextParameters = 0; 374 | for (int i = 0; i < spec.parameters.Length; ++i) 375 | { 376 | if (spec.parameters[i].type == RemoteParameterType.RS_PARAMETER_NUMBER) 377 | ++nNumericalParameters; 378 | else if (spec.parameters[i].type == RemoteParameterType.RS_PARAMETER_IMAGE) 379 | ++nImageParameters; 380 | else if (spec.parameters[i].type == RemoteParameterType.RS_PARAMETER_POSE || spec.parameters[i].type == RemoteParameterType.RS_PARAMETER_TRANSFORM) 381 | nNumericalParameters += 16; 382 | else if (spec.parameters[i].type == RemoteParameterType.RS_PARAMETER_TEXT) 383 | ++nTextParameters; 384 | } 385 | float[] parameters = new float[nNumericalParameters]; 386 | ImageFrameData[] imageData = new ImageFrameData[nImageParameters]; 387 | if (PluginEntry.instance.getFrameParameters(spec.hash, ref parameters) == RS_ERROR.RS_ERROR_SUCCESS && PluginEntry.instance.getFrameImageData(spec.hash, ref imageData) == RS_ERROR.RS_ERROR_SUCCESS) 388 | { 389 | if (fields.numerical != null) 390 | { 391 | int i = 0; 392 | foreach (var field in fields.numerical) 393 | { 394 | Type fieldType = field.FieldType; 395 | if (fieldType.IsEnum) 396 | { 397 | field.SetValue(Enum.ToObject(fieldType, Convert.ToUInt64(parameters[i]))); 398 | ++i; 399 | } 400 | else if (fieldType == typeof(Vector2)) 401 | { 402 | Vector2 v = new Vector2(parameters[i + 0], parameters[i + 1]); 403 | field.SetValue(v); 404 | i += 2; 405 | } 406 | else if (fieldType == typeof(Vector2Int)) 407 | { 408 | Vector2Int v = new Vector2Int((int)parameters[i + 0], (int)parameters[i + 1]); 409 | field.SetValue(v); 410 | i += 2; 411 | } 412 | else if (fieldType == typeof(Vector3)) 413 | { 414 | Vector3 v = new Vector3(parameters[i + 0], parameters[i + 1], parameters[i + 2]); 415 | field.SetValue(v); 416 | i += 3; 417 | } 418 | else if (fieldType == typeof(Vector3Int)) 419 | { 420 | Vector3Int v = new Vector3Int((int)parameters[i + 0], (int)parameters[i + 1], (int)parameters[i + 2]); 421 | field.SetValue(v); 422 | i += 3; 423 | } 424 | else if (fieldType == typeof(Vector4)) 425 | { 426 | Vector4 v = new Vector4(parameters[i + 0], parameters[i + 1], parameters[i + 2], parameters[i + 3]); 427 | field.SetValue(v); 428 | i += 4; 429 | } 430 | else if (fieldType == typeof(Color)) 431 | { 432 | Color v = new Color(parameters[i + 0], parameters[i + 1], parameters[i + 2], parameters[i + 3]); 433 | field.SetValue(v); 434 | i += 4; 435 | } 436 | else if (fieldType == typeof(Transform)) 437 | { 438 | Matrix4x4 m = new Matrix4x4(); 439 | m.SetColumn(0, new Vector4(parameters[i + 0], parameters[i + 1], parameters[i + 2], parameters[i + 3])); 440 | m.SetColumn(1, new Vector4(parameters[i + 4], parameters[i + 5], parameters[i + 6], parameters[i + 7])); 441 | m.SetColumn(2, new Vector4(parameters[i + 8], parameters[i + 9], parameters[i + 10], parameters[i + 11])); 442 | m.SetColumn(3, new Vector4(parameters[i + 12], parameters[i + 13], parameters[i + 14], parameters[i + 15])); 443 | Transform transform = field.GetValue() as Transform; 444 | transform.localPosition = new Vector3(m[0, 3], m[1, 3], m[2, 3]); 445 | transform.localScale = m.lossyScale; 446 | transform.localRotation = m.rotation; 447 | i += 16; 448 | } 449 | else 450 | { 451 | if (field.info != null) 452 | field.SetValue(Convert.ChangeType(parameters[i], fieldType)); 453 | ++i; 454 | } 455 | } 456 | } 457 | if (fields.images != null) 458 | { 459 | while (scratchTextures.Count < imageData.Length) 460 | { 461 | int index = scratchTextures.Count; 462 | scratchTextures.Add(new Texture2D((int)imageData[index].width, (int)imageData[index].height, PluginEntry.ToTextureFormat(imageData[index].format), false, true)); 463 | } 464 | uint i = 0; 465 | foreach (var field in fields.images) 466 | { 467 | if (field.GetValue() is RenderTexture renderTexture) 468 | { 469 | Texture2D texture = scratchTextures[(int)i]; 470 | if (texture.width != imageData[i].width || texture.height != imageData[i].height || texture.format != PluginEntry.ToTextureFormat(imageData[i].format)) 471 | { 472 | scratchTextures[(int)i] = new Texture2D((int)imageData[i].width, (int)imageData[i].height, PluginEntry.ToTextureFormat(imageData[i].format), false, true); 473 | texture = scratchTextures[(int)i]; 474 | } 475 | if (PluginEntry.instance.getFrameImage(imageData[i].imageId, ref texture) == RS_ERROR.RS_ERROR_SUCCESS) 476 | { 477 | texture.IncrementUpdateCount(); 478 | Graphics.Blit(texture, renderTexture, new Vector2(1.0f, -1.0f), new Vector2(0.0f, 1.0f)); 479 | renderTexture.IncrementUpdateCount(); 480 | } 481 | } 482 | ++i; 483 | } 484 | } 485 | if (fields.texts != null) 486 | { 487 | uint i = 0; 488 | foreach (var field in fields.texts) 489 | { 490 | string text = ""; 491 | if (PluginEntry.instance.getFrameText(spec.hash, i, ref text) == RS_ERROR.RS_ERROR_SUCCESS) 492 | { 493 | if (field.FieldType == typeof(String[])) 494 | field.SetValue(text.Split(' ')); 495 | else 496 | field.SetValue(text); 497 | } 498 | } 499 | ++i; 500 | } 501 | } 502 | } 503 | } 504 | } 505 | 506 | static Camera[] getTemplateCameras() 507 | { 508 | return Camera.allCameras; 509 | } 510 | 511 | static Camera GetChannelCamera(string channel) 512 | { 513 | try 514 | { 515 | return Array.Find(getTemplateCameras(), camera => camera.name == channel); 516 | } 517 | catch (ArgumentNullException) 518 | { 519 | return Camera.main; 520 | } 521 | } 522 | 523 | static public StreamDescription[] streams = { }; 524 | static public bool awaiting = false; 525 | static public FrameData frameData; 526 | static public bool newFrameData = false; 527 | 528 | static private GameObject[] cameras = { }; 529 | static private ManagedSchema schema = new ManagedSchema(); 530 | public class ObjectField 531 | { 532 | public object target; 533 | public MemberInfo info; 534 | public Type FieldType { 535 | get { 536 | if (info is FieldInfo fieldInfo) 537 | return fieldInfo.FieldType; 538 | else if (info is PropertyInfo propertyInfo) 539 | return propertyInfo.PropertyType; 540 | return typeof(void); 541 | } 542 | } 543 | public void SetValue(object value) 544 | { 545 | if (info is FieldInfo fieldInfo) 546 | fieldInfo.SetValue(target, value); 547 | else if (info is PropertyInfo propertyInfo) 548 | propertyInfo.SetValue(target, value); 549 | } 550 | public object GetValue() 551 | { 552 | if (info is FieldInfo fieldInfo) 553 | return fieldInfo.GetValue(target); 554 | else if (info is PropertyInfo propertyInfo) 555 | return propertyInfo.GetValue(target); 556 | return null; 557 | } 558 | } 559 | public struct SceneFields 560 | { 561 | public List numerical; 562 | public List images; 563 | public List texts; 564 | } 565 | static private SceneFields[] sceneFields = new SceneFields[0]; 566 | } 567 | 568 | [AddComponentMenu("")] 569 | [RequireComponent(typeof(Camera))] 570 | public class DisguiseCameraCapture : MonoBehaviour 571 | { 572 | // Start is called before the first frame update 573 | public IEnumerator Start() 574 | { 575 | if (PluginEntry.instance.IsAvailable == false) 576 | { 577 | Debug.LogError("DisguiseCameraCapture: RenderStream DLL not available, capture cannot start."); 578 | enabled = false; 579 | yield break; 580 | } 581 | 582 | m_cameraData = new CameraData(); 583 | 584 | m_camera = GetComponent(); 585 | m_frameSender = new Disguise.RenderStream.FrameSender(gameObject.name, m_camera); 586 | RenderPipelineManager.endFrameRendering += RenderPipelineManager_endFrameRendering; 587 | 588 | if (Application.isPlaying == false) 589 | yield break; 590 | 591 | if (!DisguiseRenderStream.awaiting) 592 | yield return StartCoroutine(DisguiseRenderStream.AwaitFrame()); 593 | } 594 | 595 | // Update is called once per frame 596 | public void Update() 597 | { 598 | // set tracking 599 | m_newFrameData = DisguiseRenderStream.newFrameData && m_frameSender != null && m_frameSender.GetCameraData(ref m_cameraData); 600 | float cameraAspect = m_camera.aspect; 601 | Vector2 lensShift = new Vector2(0.0f, 0.0f); 602 | if (m_newFrameData) 603 | { 604 | cameraAspect = m_cameraData.sensorX / m_cameraData.sensorY; 605 | if (m_cameraData.cameraHandle != 0) // If no camera, only set aspect 606 | { 607 | transform.localPosition = new Vector3(m_cameraData.x, m_cameraData.y, m_cameraData.z); 608 | transform.localRotation = Quaternion.Euler(new Vector3(-m_cameraData.rx, m_cameraData.ry, -m_cameraData.rz)); 609 | m_camera.nearClipPlane = m_cameraData.nearZ; 610 | m_camera.farClipPlane = m_cameraData.farZ; 611 | 612 | if (m_cameraData.orthoWidth > 0.0f) // Use an orthographic camera 613 | { 614 | m_camera.orthographic = true; 615 | m_camera.orthographicSize = 0.5f * m_cameraData.orthoWidth / cameraAspect; 616 | transform.localPosition = new Vector3(m_cameraData.x, m_cameraData.y, m_cameraData.z); 617 | transform.localRotation = Quaternion.Euler(new Vector3(-m_cameraData.rx, m_cameraData.ry, -m_cameraData.rz)); 618 | } 619 | else // Perspective projection, use camera lens properties 620 | { 621 | m_camera.usePhysicalProperties = true; 622 | m_camera.sensorSize = new Vector2(m_cameraData.sensorX, m_cameraData.sensorY); 623 | m_camera.focalLength = m_cameraData.focalLength; 624 | lensShift = new Vector2(-m_cameraData.cx, m_cameraData.cy); 625 | } 626 | } 627 | } 628 | else if (m_frameSender != null) 629 | { 630 | // By default aspect is resolution aspect. We need to undo the effect of the subregion on this to get the whole image aspect. 631 | cameraAspect = m_camera.aspect * (m_frameSender.subRegion.height / m_frameSender.subRegion.width); 632 | } 633 | 634 | // Clip to correct subregion and calculate projection matrix 635 | if (m_frameSender != null) 636 | { 637 | Rect subRegion = m_frameSender.subRegion; 638 | 639 | float imageHeight, imageWidth; 640 | if (m_camera.orthographic) 641 | { 642 | imageHeight = 2.0f * m_camera.orthographicSize; 643 | imageWidth = cameraAspect * imageHeight; 644 | } 645 | else 646 | { 647 | float fovV = m_camera.fieldOfView * Mathf.Deg2Rad; 648 | float fovH = Camera.VerticalToHorizontalFieldOfView(m_camera.fieldOfView, cameraAspect) * Mathf.Deg2Rad; 649 | imageWidth = 2.0f * (float)Math.Tan(0.5f * fovH); 650 | imageHeight = 2.0f * (float)Math.Tan(0.5f * fovV); 651 | } 652 | 653 | float l = (-0.5f + subRegion.xMin) * imageWidth; 654 | float r = (-0.5f + subRegion.xMax) * imageWidth; 655 | float t = (-0.5f + 1.0f - subRegion.yMin) * imageHeight; 656 | float b = (-0.5f + 1.0f - subRegion.yMax) * imageHeight; 657 | 658 | Matrix4x4 projectionMatrix; 659 | if (m_camera.orthographic) 660 | projectionMatrix = Matrix4x4.Ortho(l, r, b, t, m_camera.nearClipPlane, m_camera.farClipPlane); 661 | else 662 | projectionMatrix = PerspectiveOffCenter(l * m_camera.nearClipPlane, r * m_camera.nearClipPlane, b * m_camera.nearClipPlane, t * m_camera.nearClipPlane, m_camera.nearClipPlane, m_camera.farClipPlane); 663 | 664 | Matrix4x4 clippingTransform = Matrix4x4.Translate(new Vector3(-lensShift.x / subRegion.width, lensShift.y / subRegion.height, 0.0f)); 665 | m_camera.projectionMatrix = clippingTransform * projectionMatrix; 666 | } 667 | } 668 | 669 | // From http://docs.unity3d.com/ScriptReference/Camera-projectionMatrix.html 670 | static Matrix4x4 PerspectiveOffCenter(float left, float right, float bottom, float top, float near, float far) 671 | { 672 | float x = 2.0F * near / (right - left); 673 | float y = 2.0F * near / (top - bottom); 674 | float a = (right + left) / (right - left); 675 | float b = (top + bottom) / (top - bottom); 676 | float c = -(far + near) / (far - near); 677 | float d = -(2.0F * far * near) / (far - near); 678 | float e = -1.0F; 679 | Matrix4x4 m = new Matrix4x4(); 680 | m[0, 0] = x; 681 | m[0, 1] = 0; 682 | m[0, 2] = a; 683 | m[0, 3] = 0; 684 | m[1, 0] = 0; 685 | m[1, 1] = y; 686 | m[1, 2] = b; 687 | m[1, 3] = 0; 688 | m[2, 0] = 0; 689 | m[2, 1] = 0; 690 | m[2, 2] = c; 691 | m[2, 3] = d; 692 | m[3, 0] = 0; 693 | m[3, 1] = 0; 694 | m[3, 2] = e; 695 | m[3, 3] = 0; 696 | return m; 697 | } 698 | 699 | public void OnRenderImage(RenderTexture source, RenderTexture destination) 700 | { 701 | CheckAndSendFrame(); 702 | } 703 | 704 | private void CheckAndSendFrame() 705 | { 706 | if (m_newFrameData) 707 | { 708 | if (m_frameSender != null) 709 | m_frameSender.SendFrame(DisguiseRenderStream.frameData, m_cameraData); 710 | m_newFrameData = false; 711 | } 712 | } 713 | 714 | private void RenderPipelineManager_endFrameRendering(ScriptableRenderContext context, Camera[] cameras) 715 | { 716 | foreach (var cam in cameras) 717 | { 718 | if (cam == m_camera) 719 | CheckAndSendFrame(); 720 | } 721 | } 722 | 723 | public void OnDestroy() 724 | { 725 | } 726 | 727 | public void OnDisable() 728 | { 729 | if (m_frameSender != null) 730 | { 731 | m_frameSender.DestroyStream(); 732 | } 733 | RenderPipelineManager.endFrameRendering -= RenderPipelineManager_endFrameRendering; 734 | } 735 | 736 | Camera m_camera; 737 | public Disguise.RenderStream.FrameSender m_frameSender; 738 | 739 | CameraData m_cameraData; 740 | bool m_newFrameData = false; 741 | } 742 | 743 | namespace Disguise.RenderStream 744 | { 745 | // d3renderstream/d3renderstream.h 746 | using StreamHandle = UInt64; 747 | using CameraHandle = UInt64; 748 | delegate void logger_t(string message); 749 | 750 | public enum RSPixelFormat : UInt32 751 | { 752 | RS_FMT_INVALID, 753 | 754 | RS_FMT_BGRA8, 755 | RS_FMT_BGRX8, 756 | 757 | RS_FMT_RGBA32F, 758 | 759 | RS_FMT_RGBA16, 760 | 761 | RS_FMT_RGBA8, 762 | RS_FMT_RGBX8, 763 | } 764 | 765 | public enum SenderFrameType : UInt32 766 | { 767 | RS_FRAMETYPE_HOST_MEMORY = 0x00000000, 768 | RS_FRAMETYPE_DX11_TEXTURE, 769 | RS_FRAMETYPE_DX12_TEXTURE, 770 | RS_FRAMETYPE_OPENGL_TEXTURE, 771 | RS_FRAMETYPE_VULKAN_TEXTURE, 772 | RS_FRAMETYPE_UNKNOWN 773 | } 774 | 775 | public enum UseDX12SharedHeapFlag : UInt32 776 | { 777 | RS_DX12_USE_SHARED_HEAP_FLAG, 778 | RS_DX12_DO_NOT_USE_SHARED_HEAP_FLAG 779 | } 780 | 781 | public enum RS_ERROR : UInt32 782 | { 783 | RS_ERROR_SUCCESS = 0, 784 | 785 | // Core is not initialised 786 | RS_NOT_INITIALISED, 787 | 788 | // Core is already initialised 789 | RS_ERROR_ALREADYINITIALISED, 790 | 791 | // Given handle is invalid 792 | RS_ERROR_INVALIDHANDLE, 793 | 794 | // Maximum number of frame senders have been created 795 | RS_MAXSENDERSREACHED, 796 | 797 | RS_ERROR_BADSTREAMTYPE, 798 | 799 | RS_ERROR_NOTFOUND, 800 | 801 | RS_ERROR_INCORRECTSCHEMA, 802 | 803 | RS_ERROR_INVALID_PARAMETERS, 804 | 805 | RS_ERROR_BUFFER_OVERFLOW, 806 | 807 | RS_ERROR_TIMEOUT, 808 | 809 | RS_ERROR_STREAMS_CHANGED, 810 | 811 | RS_ERROR_INCOMPATIBLE_VERSION, 812 | 813 | RS_ERROR_FAILED_TO_GET_DXDEVICE_FROM_RESOURCE, 814 | 815 | RS_ERROR_FAILED_TO_INITIALISE_GPGPU, 816 | 817 | RS_ERROR_QUIT, 818 | 819 | RS_ERROR_UNSPECIFIED 820 | } 821 | 822 | // Bitmask flags 823 | public enum FRAMEDATA_FLAGS : UInt32 824 | { 825 | FRAMEDATA_NO_FLAGS = 0, 826 | FRAMEDATA_RESET = 1 827 | } 828 | 829 | public enum REMOTEPARAMETER_FLAGS : UInt32 830 | { 831 | REMOTEPARAMETER_NO_FLAGS = 0, 832 | REMOTEPARAMETER_NO_SEQUENCE = 1, 833 | REMOTEPARAMETER_READ_ONLY = 2 834 | } 835 | 836 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 837 | public struct D3TrackingData 838 | { 839 | public byte virtualReprojectionRequired; 840 | } // Tracking data required by d3 but not used to render content 841 | 842 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 843 | public struct CameraData 844 | { 845 | public StreamHandle streamHandle; 846 | public CameraHandle cameraHandle; 847 | public float x, y, z; 848 | public float rx, ry, rz; 849 | public float focalLength; 850 | public float sensorX, sensorY; 851 | public float cx, cy; 852 | public float nearZ, farZ; 853 | public float orthoWidth; // If > 0, an orthographic camera should be used 854 | public float aperture; // Apply if > 0 855 | public float focusDistance; // Apply if > 0 856 | public D3TrackingData d3Tracking; 857 | } 858 | 859 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 860 | public struct FrameData 861 | { 862 | public double tTracked; 863 | public double localTime; 864 | public double localTimeDelta; 865 | public UInt32 frameRateNumerator; 866 | public UInt32 frameRateDenominator; 867 | public UInt32 flags; // FRAMEDATA_FLAGS 868 | public UInt32 scene; 869 | } 870 | 871 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 872 | public struct CameraResponseData 873 | { 874 | public double tTracked; 875 | public CameraData camera; 876 | } 877 | 878 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 879 | public struct FrameResponseData 880 | { 881 | public /*CameraResponseData**/ IntPtr cameraData; 882 | public UInt64 schemaHash; 883 | public UInt64 parameterDataSize; 884 | public IntPtr parameterData; 885 | public UInt32 textDataCount; 886 | public /*const char***/ IntPtr textData; 887 | } 888 | 889 | [StructLayout(LayoutKind.Explicit)] 890 | public struct SenderFrame 891 | { 892 | [FieldOffset(0)] 893 | public SenderFrameType type; 894 | /*union*/ 895 | // struct HostMemoryData 896 | [FieldOffset(4)] 897 | public /*uint8_t**/ IntPtr host_data; 898 | [FieldOffset(12)] 899 | public UInt32 host_stride; 900 | // struct Dx11Data 901 | [FieldOffset(4)] 902 | public /*struct ID3D11Resource**/ IntPtr dx11_resource; 903 | // struct Dx12Data 904 | [FieldOffset(4)] 905 | public /*struct ID3D12Resource**/ IntPtr dx12_resource; 906 | // struct OpenGlData 907 | [FieldOffset(4)] 908 | public /*GLuint**/ UInt32 gl_texture; 909 | // struct VulkanData 910 | [FieldOffset(4)] 911 | public /*VkDeviceMemory*/ IntPtr cameraData; 912 | [FieldOffset(12)] 913 | public /*VkDeviceSize*/ UInt64 size; 914 | [FieldOffset(20)] 915 | public RSPixelFormat format; 916 | [FieldOffset(24)] 917 | public UInt32 width; 918 | [FieldOffset(28)] 919 | public UInt32 height; 920 | [FieldOffset(32)] 921 | public /*VkSemaphore*/ IntPtr waitSemaphore; 922 | [FieldOffset(40)] 923 | public UInt64 waitSemaphoreValue; 924 | [FieldOffset(48)] 925 | public /*VkSemaphore*/ IntPtr signalSemaphore; 926 | [FieldOffset(56)] 927 | public UInt64 signalSemaphoreValue; 928 | } 929 | 930 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 931 | public struct FrameRegion 932 | { 933 | public UInt32 xOffset; 934 | public UInt32 yOffset; 935 | public UInt32 width; 936 | public UInt32 height; 937 | } 938 | 939 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 940 | public struct ProjectionClipping 941 | { 942 | public float left; 943 | public float right; 944 | public float top; 945 | public float bottom; 946 | } 947 | 948 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 949 | public struct StreamDescription 950 | { 951 | public StreamHandle handle; 952 | [MarshalAs(UnmanagedType.LPStr)] 953 | public string channel; 954 | public UInt64 mappingId; 955 | public Int32 iViewpoint; 956 | [MarshalAs(UnmanagedType.LPStr)] 957 | public string name; 958 | public UInt32 width; 959 | public UInt32 height; 960 | public RSPixelFormat format; 961 | public ProjectionClipping clipping; 962 | [MarshalAs(UnmanagedType.LPStr)] 963 | public string mappingName; 964 | public Int32 iFragment; 965 | } 966 | 967 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 968 | public struct StreamDescriptions 969 | { 970 | public UInt32 nStreams; 971 | public /*StreamDescription**/ IntPtr streams; 972 | } 973 | 974 | public enum RemoteParameterType : UInt32 975 | { 976 | RS_PARAMETER_NUMBER, 977 | RS_PARAMETER_IMAGE, 978 | RS_PARAMETER_POSE, // 4x4 TR matrix 979 | RS_PARAMETER_TRANSFORM, // 4x4 TRS matrix 980 | RS_PARAMETER_TEXT, 981 | RS_PARAMETER_EVENT, 982 | RS_PARAMETER_SKELETON 983 | } 984 | 985 | public enum RemoteParameterDmxType : UInt32 986 | { 987 | RS_DMX_DEFAULT, 988 | RS_DMX_8, 989 | RS_DMX_16_BE, 990 | } 991 | 992 | [StructLayout(LayoutKind.Explicit)] 993 | public /*union*/ struct RemoteParameterTypeDefaults 994 | { 995 | [FieldOffset(0)] 996 | public float numerical_min; 997 | [FieldOffset(4)] 998 | public float numerical_max; 999 | [FieldOffset(8)] 1000 | public float numerical_step; 1001 | [FieldOffset(12)] 1002 | public float numerical_defaultValue; 1003 | [FieldOffset(0)] 1004 | public /*const char**/ IntPtr text_defaultValue; 1005 | } 1006 | 1007 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 1008 | public struct ImageFrameData 1009 | { 1010 | public UInt32 width; 1011 | public UInt32 height; 1012 | public RSPixelFormat format; 1013 | public Int64 imageId; 1014 | } 1015 | 1016 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 1017 | public struct RemoteParameter 1018 | { 1019 | [MarshalAs(UnmanagedType.LPStr)] 1020 | public string group; 1021 | [MarshalAs(UnmanagedType.LPStr)] 1022 | public string displayName; 1023 | [MarshalAs(UnmanagedType.LPStr)] 1024 | public string key; 1025 | public RemoteParameterType type; 1026 | public RemoteParameterTypeDefaults defaults; 1027 | public UInt32 nOptions; 1028 | public /*const char***/ IntPtr options; 1029 | 1030 | public Int32 dmxOffset; // DMX channel offset or auto (-1) 1031 | public RemoteParameterDmxType dmxType; 1032 | public UInt32 flags; // REMOTEPARAMETER_FLAGS 1033 | } 1034 | 1035 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 1036 | public struct RemoteParameters 1037 | { 1038 | [MarshalAs(UnmanagedType.LPStr)] 1039 | public string name; 1040 | public UInt32 nParameters; 1041 | public /*RemoteParameter**/ IntPtr parameters; 1042 | public UInt64 hash; 1043 | } 1044 | 1045 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 1046 | public struct Scenes 1047 | { 1048 | public UInt32 nScenes; 1049 | public /*RemoteParameters**/ IntPtr scenes; 1050 | } 1051 | 1052 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 1053 | public struct Channels 1054 | { 1055 | public UInt32 nChannels; 1056 | public /*const char***/ IntPtr channels; 1057 | } 1058 | 1059 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 1060 | public struct Schema 1061 | { 1062 | [MarshalAs(UnmanagedType.LPStr)] 1063 | public string engineName; 1064 | [MarshalAs(UnmanagedType.LPStr)] 1065 | public string engineVersion; 1066 | [MarshalAs(UnmanagedType.LPStr)] 1067 | public string pluginVersion; 1068 | [MarshalAs(UnmanagedType.LPStr)] 1069 | public string info; 1070 | public Channels channels; 1071 | public Scenes scenes; 1072 | } 1073 | 1074 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 1075 | public struct ProfilingEntry 1076 | { 1077 | public string name; 1078 | public float value; 1079 | } 1080 | 1081 | 1082 | public class ManagedRemoteParameter 1083 | { 1084 | public string group; 1085 | public string displayName; 1086 | public string key; 1087 | public RemoteParameterType type; 1088 | public float min; 1089 | public float max; 1090 | public float step; 1091 | public object defaultValue; 1092 | public string[] options = { }; 1093 | 1094 | public Int32 dmxOffset; 1095 | public RemoteParameterDmxType dmxType; 1096 | } 1097 | 1098 | public class ManagedRemoteParameters 1099 | { 1100 | public string name; 1101 | public ManagedRemoteParameter[] parameters = { }; 1102 | public UInt64 hash; 1103 | } 1104 | 1105 | public class ManagedSchema 1106 | { 1107 | public string[] channels = { }; 1108 | public ManagedRemoteParameters[] scenes = { }; 1109 | } 1110 | 1111 | [Serializable] 1112 | public sealed class PluginEntry 1113 | { 1114 | private class Nested 1115 | { 1116 | // Explicit static constructor to tell C# compiler 1117 | // not to mark type as beforefieldinit 1118 | static Nested() { } 1119 | 1120 | internal static readonly PluginEntry instance = new PluginEntry(); 1121 | } 1122 | 1123 | public static PluginEntry instance { get { return Nested.instance; } } 1124 | 1125 | public static TextureFormat ToTextureFormat(RSPixelFormat fmt) 1126 | { 1127 | switch (fmt) 1128 | { 1129 | case RSPixelFormat.RS_FMT_BGRA8: return TextureFormat.BGRA32; 1130 | case RSPixelFormat.RS_FMT_BGRX8: return TextureFormat.BGRA32; 1131 | case RSPixelFormat.RS_FMT_RGBA32F: return TextureFormat.RGBAFloat; 1132 | case RSPixelFormat.RS_FMT_RGBA16: return TextureFormat.RGBAFloat; 1133 | case RSPixelFormat.RS_FMT_RGBA8: return TextureFormat.RGBA32; 1134 | case RSPixelFormat.RS_FMT_RGBX8: return TextureFormat.RGBA32; 1135 | default: return TextureFormat.BGRA32; 1136 | } 1137 | } 1138 | 1139 | public static RenderTextureFormat ToRenderTextureFormat(RSPixelFormat fmt) 1140 | { 1141 | switch (fmt) 1142 | { 1143 | case RSPixelFormat.RS_FMT_BGRA8: return RenderTextureFormat.ARGBFloat; 1144 | case RSPixelFormat.RS_FMT_BGRX8: return RenderTextureFormat.ARGBFloat; 1145 | case RSPixelFormat.RS_FMT_RGBA32F: return RenderTextureFormat.ARGBFloat; 1146 | case RSPixelFormat.RS_FMT_RGBA16: return RenderTextureFormat.ARGBFloat; 1147 | case RSPixelFormat.RS_FMT_RGBA8: return RenderTextureFormat.ARGBFloat; 1148 | case RSPixelFormat.RS_FMT_RGBX8: return RenderTextureFormat.ARGBFloat; 1149 | default: return RenderTextureFormat.ARGBFloat; 1150 | } 1151 | } 1152 | 1153 | // isolated functions, do not require init prior to use 1154 | unsafe delegate void pRegisterLogFunc(logger_t logger); 1155 | unsafe delegate void pUnregisterLogFunc(); 1156 | 1157 | unsafe delegate RS_ERROR pInitialise(int expectedVersionMajor, int expectedVersionMinor); 1158 | unsafe delegate RS_ERROR pInitialiseGpGpuWithoutInterop(/*ID3D11Device**/ IntPtr device); 1159 | unsafe delegate RS_ERROR pInitialiseGpGpuWithDX11Device(/*ID3D11Device**/ IntPtr device); 1160 | unsafe delegate RS_ERROR pInitialiseGpGpuWithDX11Resource(/*ID3D11Resource**/ IntPtr resource); 1161 | unsafe delegate RS_ERROR pInitialiseGpGpuWithDX12DeviceAndQueue(/*ID3D12Device**/ IntPtr device, /*ID3D12CommandQueue**/ IntPtr queue); 1162 | unsafe delegate RS_ERROR pInitialiseGpGpuWithOpenGlContexts(/*HGLRC**/ IntPtr glContext, /*HDC**/ IntPtr deviceContext); 1163 | unsafe delegate RS_ERROR pInitialiseGpGpuWithVulkanDevice(/*VkDevice**/ IntPtr device); 1164 | unsafe delegate RS_ERROR pShutdown(); 1165 | 1166 | // non-isolated functions, these require init prior to use 1167 | 1168 | unsafe delegate RS_ERROR pUseDX12SharedHeapFlag(ref UseDX12SharedHeapFlag flag); 1169 | unsafe delegate RS_ERROR pSaveSchema(string assetPath, /*Schema**/ IntPtr schema); // Save schema for project file/custom executable at (assetPath) 1170 | unsafe delegate RS_ERROR pLoadSchema(string assetPath, /*Out*/ /*Schema**/ IntPtr schema, /*InOut*/ ref UInt32 nBytes); // Load schema for project file/custom executable at (assetPath) into a buffer of size (nBytes) starting at (schema) 1171 | 1172 | // workload functions, these require the process to be running inside d3's asset launcher environment 1173 | 1174 | unsafe delegate RS_ERROR pSetSchema(/*InOut*/ /*Schema**/ IntPtr schema); // Set schema and fill in per-scene hash for use with rs_getFrameParameters 1175 | 1176 | unsafe delegate RS_ERROR pGetStreams(/*Out*/ /*StreamDescriptions**/ IntPtr streams, /*InOut*/ ref UInt32 nBytes); // Populate streams into a buffer of size (nBytes) starting at (streams) 1177 | 1178 | unsafe delegate RS_ERROR pAwaitFrameData(int timeoutMs, /*Out*/ /*FrameData**/ IntPtr data); // waits for any asset, any stream to request a frame, provides the parameters for that frame. 1179 | unsafe delegate RS_ERROR pSetFollower(int isFollower); // Used to mark this node as relying on alternative mechanisms to distribute FrameData. Users must provide correct CameraResponseData to sendFrame, and call rs_beginFollowerFrame at the start of the frame, where awaitFrame would normally be called. 1180 | unsafe delegate RS_ERROR pBeginFollowerFrame(double tTracked); // Pass the engine-distributed tTracked value in, if you have called rs_setFollower(1) otherwise do not call this function. 1181 | 1182 | unsafe delegate RS_ERROR pGetFrameParameters(UInt64 schemaHash, /*Out*/ /*void**/ IntPtr outParameterData, UInt64 outParameterDataSize); // returns the remote parameters for this frame. 1183 | unsafe delegate RS_ERROR pGetFrameImageData(UInt64 schemaHash, /*Out*/ /*ImageFrameData**/ IntPtr outParameterData, UInt64 outParameterDataCount); // returns the remote image data for this frame. 1184 | unsafe delegate RS_ERROR pGetFrameImage(Int64 imageId, /*const SenderFrame**/ IntPtr data); // fills in (data) with the remote image 1185 | unsafe delegate RS_ERROR pGetFrameText(UInt64 schemaHash, UInt32 textParamIndex, /*Out*/ /*const char***/ ref IntPtr outTextPtr); // // returns the remote text data (pointer only valid until next rs_awaitFrameData) 1186 | 1187 | unsafe delegate RS_ERROR pGetFrameCamera(StreamHandle streamHandle, /*Out*/ /*CameraData**/ IntPtr outCameraData); // returns the CameraData for this stream, or RS_ERROR_NOTFOUND if no camera data is available for this stream on this frame 1188 | unsafe delegate RS_ERROR pSendFrame(StreamHandle streamHandle, /*SenderFrame**/ IntPtr data, /*const void**/ IntPtr frameData); // publish a frame buffer which was generated from the associated tracking and timing information. 1189 | 1190 | unsafe delegate RS_ERROR pReleaseImage(/*const SenderFrame**/ IntPtr data); 1191 | 1192 | unsafe delegate RS_ERROR pLogToD3(string str); 1193 | unsafe delegate RS_ERROR pSendProfilingData(/*ProfilingEntry**/ IntPtr entries, int count); 1194 | unsafe delegate RS_ERROR pSetNewStatusMessage(string msg); 1195 | 1196 | pRegisterLogFunc m_registerLoggingFunc = null; 1197 | pRegisterLogFunc m_registerErrorLoggingFunc = null; 1198 | pRegisterLogFunc m_registerVerboseLoggingFunc = null; 1199 | 1200 | pUnregisterLogFunc m_unregisterLoggingFunc = null; 1201 | pUnregisterLogFunc m_unregisterErrorLoggingFunc = null; 1202 | pUnregisterLogFunc m_unregisterVerboseLoggingFunc = null; 1203 | 1204 | pInitialise m_initialise = null; 1205 | pInitialiseGpGpuWithoutInterop m_initialiseGpGpuWithoutInterop = null; 1206 | pInitialiseGpGpuWithDX11Device m_initialiseGpGpuWithDX11Device = null; 1207 | pInitialiseGpGpuWithDX11Resource m_initialiseGpGpuWithDX11Resource = null; 1208 | pInitialiseGpGpuWithDX12DeviceAndQueue m_initialiseGpGpuWithDX12DeviceAndQueue = null; 1209 | pInitialiseGpGpuWithOpenGlContexts m_initialiseGpGpuWithOpenGlContexts = null; 1210 | pInitialiseGpGpuWithVulkanDevice m_initialiseGpGpuWithVulkanDevice = null; 1211 | 1212 | pShutdown m_shutdown = null; 1213 | 1214 | pUseDX12SharedHeapFlag m_useDX12SharedHeapFlag = null; 1215 | pSaveSchema m_saveSchema = null; 1216 | pLoadSchema m_loadSchema = null; 1217 | 1218 | pSetSchema m_setSchema = null; 1219 | pGetStreams m_getStreams = null; 1220 | 1221 | pAwaitFrameData m_awaitFrameData = null; 1222 | pSetFollower m_setFollower = null; 1223 | pBeginFollowerFrame m_beginFollowerFrame = null; 1224 | 1225 | pGetFrameParameters m_getFrameParameters = null; 1226 | pGetFrameImageData m_getFrameImageData = null; 1227 | pGetFrameImage m_getFrameImage = null; 1228 | pGetFrameText m_getFrameText = null; 1229 | 1230 | pGetFrameCamera m_getFrameCamera = null; 1231 | pSendFrame m_sendFrame = null; 1232 | 1233 | pReleaseImage m_releaseImage = null; 1234 | 1235 | pLogToD3 m_logToD3 = null; 1236 | pSendProfilingData m_sendProfilingData = null; 1237 | pSetNewStatusMessage m_setNewStatusMessage = null; 1238 | 1239 | logger_t m_logInfo; 1240 | logger_t m_logError; 1241 | 1242 | void logInfo(string message) 1243 | { 1244 | Debug.Log(message); 1245 | } 1246 | 1247 | void logError(string message) 1248 | { 1249 | Debug.LogError(message); 1250 | } 1251 | 1252 | void logToD3(string logString, string stackTrace, LogType type) 1253 | { 1254 | if (m_logToD3 == null) 1255 | return; 1256 | 1257 | string prefix = ""; 1258 | switch(type) 1259 | { 1260 | case LogType.Error: 1261 | prefix = "!!!!! "; 1262 | break; 1263 | case LogType.Assert: 1264 | prefix = "!!!!! ASSERT: "; 1265 | break; 1266 | case LogType.Warning: 1267 | prefix = "!!! "; 1268 | break; 1269 | case LogType.Exception: 1270 | prefix = "!!!!! Exception: "; 1271 | break; 1272 | } 1273 | 1274 | string trace = String.IsNullOrEmpty(stackTrace) ? "" : "\nTrace: " + stackTrace; 1275 | 1276 | m_logToD3(prefix + logString + trace); 1277 | } 1278 | 1279 | void setNewStatusMessage(string message) 1280 | { 1281 | m_setNewStatusMessage?.Invoke(message); 1282 | } 1283 | 1284 | ManagedSchema schemaToManagedSchema(Schema cSchema) 1285 | { 1286 | ManagedSchema schema = new ManagedSchema(); 1287 | schema.channels = new string[cSchema.channels.nChannels]; 1288 | for (int i = 0; i < cSchema.channels.nChannels; ++i) 1289 | { 1290 | IntPtr channelPtr = Marshal.ReadIntPtr(cSchema.channels.channels, i * Marshal.SizeOf(typeof(IntPtr))); 1291 | schema.channels[i] = Marshal.PtrToStringAnsi(channelPtr); 1292 | } 1293 | schema.scenes = new ManagedRemoteParameters[cSchema.scenes.nScenes]; 1294 | for (int i = 0; i < cSchema.scenes.nScenes; ++i) 1295 | { 1296 | schema.scenes[i] = new ManagedRemoteParameters(); 1297 | ManagedRemoteParameters managedParameters = schema.scenes[i]; 1298 | RemoteParameters parameters = (RemoteParameters)Marshal.PtrToStructure(cSchema.scenes.scenes + i * Marshal.SizeOf(typeof(RemoteParameters)), typeof(RemoteParameters)); 1299 | managedParameters.name = parameters.name; 1300 | managedParameters.parameters = new ManagedRemoteParameter[parameters.nParameters]; 1301 | for (int j = 0; j < parameters.nParameters; ++j) 1302 | { 1303 | managedParameters.parameters[j] = new ManagedRemoteParameter(); 1304 | ManagedRemoteParameter managedParameter = managedParameters.parameters[j]; 1305 | RemoteParameter parameter = (RemoteParameter)Marshal.PtrToStructure(parameters.parameters + j * Marshal.SizeOf(typeof(RemoteParameter)), typeof(RemoteParameter)); 1306 | managedParameter.group = parameter.group; 1307 | managedParameter.displayName = parameter.displayName; 1308 | managedParameter.key = parameter.key; 1309 | managedParameter.type = parameter.type; 1310 | if (parameter.type == RemoteParameterType.RS_PARAMETER_NUMBER) 1311 | { 1312 | managedParameter.min = parameter.defaults.numerical_min; 1313 | managedParameter.max = parameter.defaults.numerical_max; 1314 | managedParameter.step = parameter.defaults.numerical_step; 1315 | managedParameter.defaultValue = parameter.defaults.numerical_defaultValue; 1316 | } 1317 | else if (parameter.type == RemoteParameterType.RS_PARAMETER_TEXT) 1318 | { 1319 | managedParameter.defaultValue = Marshal.PtrToStringAnsi(parameter.defaults.text_defaultValue); 1320 | } 1321 | managedParameter.options = new string[parameter.nOptions]; 1322 | for (int k = 0; k < parameter.nOptions; ++k) 1323 | { 1324 | IntPtr optionPtr = Marshal.ReadIntPtr(parameter.options, k * Marshal.SizeOf(typeof(IntPtr))); 1325 | managedParameter.options[i] = Marshal.PtrToStringAnsi(optionPtr); 1326 | } 1327 | managedParameter.dmxOffset = parameter.dmxOffset; 1328 | managedParameter.dmxType = parameter.dmxType; 1329 | } 1330 | managedParameters.hash = parameters.hash; 1331 | } 1332 | return schema; 1333 | } 1334 | 1335 | public RS_ERROR saveSchema(string assetPath, ref ManagedSchema schema) 1336 | { 1337 | if (m_saveSchema == null) 1338 | return RS_ERROR.RS_NOT_INITIALISED; 1339 | 1340 | List allocations = new List(); 1341 | try 1342 | { 1343 | Schema cSchema = new Schema(); 1344 | cSchema.engineName = "Unity Engine"; 1345 | cSchema.engineVersion = Application.unityVersion; 1346 | cSchema.pluginVersion = "RS2.0-Unity-v0"; 1347 | cSchema.info = Application.productName; 1348 | cSchema.channels.nChannels = (UInt32)schema.channels.Length; 1349 | cSchema.channels.channels = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)) * (int)cSchema.channels.nChannels); 1350 | allocations.Add(cSchema.channels.channels); 1351 | for (int i = 0; i < cSchema.channels.nChannels; ++i) 1352 | { 1353 | IntPtr channelPtr = Marshal.StringToHGlobalAnsi(schema.channels[i]); 1354 | allocations.Add(channelPtr); 1355 | Marshal.WriteIntPtr(cSchema.channels.channels, i * Marshal.SizeOf(typeof(IntPtr)), channelPtr); 1356 | } 1357 | 1358 | cSchema.scenes.nScenes = (UInt32)schema.scenes.Length; 1359 | cSchema.scenes.scenes = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(RemoteParameters)) * (int)cSchema.scenes.nScenes); 1360 | allocations.Add(cSchema.scenes.scenes); 1361 | for (int i = 0; i < cSchema.scenes.nScenes; ++i) 1362 | { 1363 | ManagedRemoteParameters managedParameters = schema.scenes[i]; 1364 | RemoteParameters parameters = new RemoteParameters(); 1365 | parameters.name = managedParameters.name; 1366 | parameters.nParameters = (UInt32)managedParameters.parameters.Length; 1367 | parameters.parameters = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(RemoteParameter)) * (int)parameters.nParameters); 1368 | allocations.Add(parameters.parameters); 1369 | for (int j = 0; j < parameters.nParameters; ++j) 1370 | { 1371 | ManagedRemoteParameter managedParameter = managedParameters.parameters[j]; 1372 | RemoteParameter parameter = new RemoteParameter(); 1373 | parameter.group = managedParameter.group; 1374 | parameter.displayName = managedParameter.displayName; 1375 | parameter.key = managedParameter.key; 1376 | parameter.type = managedParameter.type; 1377 | if (parameter.type == RemoteParameterType.RS_PARAMETER_NUMBER) 1378 | { 1379 | parameter.defaults.numerical_min = managedParameter.min; 1380 | parameter.defaults.numerical_max = managedParameter.max; 1381 | parameter.defaults.numerical_step = managedParameter.step; 1382 | parameter.defaults.numerical_defaultValue = Convert.ToSingle(managedParameter.defaultValue); 1383 | } 1384 | else if (parameter.type == RemoteParameterType.RS_PARAMETER_TEXT) 1385 | { 1386 | IntPtr textPtr = Marshal.StringToHGlobalAnsi(Convert.ToString(managedParameter.defaultValue)); 1387 | allocations.Add(textPtr); 1388 | parameter.defaults.text_defaultValue = textPtr; 1389 | } 1390 | parameter.nOptions = (UInt32)managedParameter.options.Length; 1391 | parameter.options = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)) * (int)parameter.nOptions); 1392 | allocations.Add(parameter.options); 1393 | for (int k = 0; k < parameter.nOptions; ++k) 1394 | { 1395 | IntPtr optionPtr = Marshal.StringToHGlobalAnsi(managedParameter.options[k]); 1396 | allocations.Add(optionPtr); 1397 | Marshal.WriteIntPtr(parameter.options, k * Marshal.SizeOf(typeof(IntPtr)), optionPtr); 1398 | } 1399 | parameter.dmxOffset = managedParameter.dmxOffset; 1400 | parameter.dmxType = managedParameter.dmxType; 1401 | Marshal.StructureToPtr(parameter, parameters.parameters + j * Marshal.SizeOf(typeof(RemoteParameter)), false); 1402 | } 1403 | Marshal.StructureToPtr(parameters, cSchema.scenes.scenes + i * Marshal.SizeOf(typeof(RemoteParameters)), false); 1404 | } 1405 | 1406 | IntPtr schemaPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Schema))); 1407 | allocations.Add(schemaPtr); 1408 | Marshal.StructureToPtr(cSchema, schemaPtr, false); 1409 | RS_ERROR error = m_saveSchema(assetPath, schemaPtr); 1410 | if (error == RS_ERROR.RS_ERROR_SUCCESS) 1411 | { 1412 | cSchema = (Schema)Marshal.PtrToStructure(schemaPtr, typeof(Schema)); 1413 | schema = schemaToManagedSchema(cSchema); 1414 | } 1415 | return error; 1416 | } 1417 | finally 1418 | { 1419 | foreach (IntPtr ptr in allocations) 1420 | Marshal.FreeHGlobal(ptr); 1421 | } 1422 | //return RS_ERROR.RS_ERROR_UNSPECIFIED; 1423 | } 1424 | 1425 | public RS_ERROR loadSchema(string assetPath, ref ManagedSchema schema) 1426 | { 1427 | if (m_loadSchema == null) 1428 | return RS_ERROR.RS_NOT_INITIALISED; 1429 | 1430 | IntPtr descMem = IntPtr.Zero; 1431 | UInt32 nBytes = 0; 1432 | m_loadSchema(assetPath, descMem, ref nBytes); 1433 | 1434 | const int MAX_TRIES = 3; 1435 | int iterations = 0; 1436 | 1437 | RS_ERROR res = RS_ERROR.RS_ERROR_BUFFER_OVERFLOW; 1438 | try 1439 | { 1440 | do 1441 | { 1442 | Marshal.FreeHGlobal(descMem); 1443 | descMem = Marshal.AllocHGlobal((int)nBytes); 1444 | res = m_loadSchema(assetPath, descMem, ref nBytes); 1445 | if (res == RS_ERROR.RS_ERROR_SUCCESS) 1446 | { 1447 | Schema cSchema = (Schema)Marshal.PtrToStructure(descMem, typeof(Schema)); 1448 | schema = schemaToManagedSchema(cSchema); 1449 | } 1450 | 1451 | ++iterations; 1452 | } while (res == RS_ERROR.RS_ERROR_BUFFER_OVERFLOW && iterations < MAX_TRIES); 1453 | } 1454 | finally 1455 | { 1456 | Marshal.FreeHGlobal(descMem); 1457 | } 1458 | return res; 1459 | } 1460 | 1461 | public RS_ERROR getStreams(ref StreamDescription[] streams) 1462 | { 1463 | if (m_getStreams == null) 1464 | return RS_ERROR.RS_NOT_INITIALISED; 1465 | 1466 | IntPtr descMem = IntPtr.Zero; 1467 | UInt32 nBytes = 0; 1468 | m_getStreams(descMem, ref nBytes); 1469 | 1470 | const int MAX_TRIES = 3; 1471 | int iterations = 0; 1472 | 1473 | RS_ERROR res = RS_ERROR.RS_ERROR_BUFFER_OVERFLOW; 1474 | try 1475 | { 1476 | do 1477 | { 1478 | Marshal.FreeHGlobal(descMem); 1479 | descMem = Marshal.AllocHGlobal((int)nBytes); 1480 | res = m_getStreams(descMem, ref nBytes); 1481 | if (res == RS_ERROR.RS_ERROR_SUCCESS) 1482 | { 1483 | StreamDescriptions desc = (StreamDescriptions)Marshal.PtrToStructure(descMem, typeof(StreamDescriptions)); 1484 | streams = new StreamDescription[desc.nStreams]; 1485 | for (int i = 0; i < desc.nStreams; ++i) 1486 | { 1487 | IntPtr current = desc.streams + i * Marshal.SizeOf(typeof(StreamDescription)); 1488 | streams[i] = (StreamDescription)Marshal.PtrToStructure(current, typeof(StreamDescription)); 1489 | } 1490 | } 1491 | 1492 | ++iterations; 1493 | } while (res == RS_ERROR.RS_ERROR_BUFFER_OVERFLOW && iterations < MAX_TRIES); 1494 | } 1495 | finally 1496 | { 1497 | Marshal.FreeHGlobal(descMem); 1498 | } 1499 | return res; 1500 | } 1501 | 1502 | public RS_ERROR sendFrame(StreamHandle streamHandle, ref SenderFrame data, ref FrameResponseData frameData) 1503 | { 1504 | if (m_sendFrame == null) 1505 | return RS_ERROR.RS_NOT_INITIALISED; 1506 | 1507 | if (handleReference.IsAllocated) 1508 | handleReference.Free(); 1509 | handleReference = GCHandle.Alloc(frameData, GCHandleType.Pinned); 1510 | 1511 | if (handleReference2.IsAllocated) 1512 | handleReference2.Free(); 1513 | handleReference2 = GCHandle.Alloc(data, GCHandleType.Pinned); 1514 | 1515 | try 1516 | { 1517 | RS_ERROR error = m_sendFrame(streamHandle, handleReference2.AddrOfPinnedObject(), handleReference.AddrOfPinnedObject()); 1518 | return error; 1519 | } 1520 | finally 1521 | { 1522 | if (handleReference.IsAllocated) 1523 | handleReference.Free(); 1524 | } 1525 | //return RS_ERROR.RS_ERROR_UNSPECIFIED; 1526 | } 1527 | 1528 | public RS_ERROR getFrameImage(Int64 imageId, ref SenderFrame data) 1529 | { 1530 | if (m_getFrameImage == null) 1531 | return RS_ERROR.RS_NOT_INITIALISED; 1532 | 1533 | if (handleReference.IsAllocated) 1534 | handleReference.Free(); 1535 | handleReference = GCHandle.Alloc(data, GCHandleType.Pinned); 1536 | 1537 | try 1538 | { 1539 | RS_ERROR error = m_getFrameImage(imageId, handleReference.AddrOfPinnedObject()); 1540 | return error; 1541 | } 1542 | finally 1543 | { 1544 | if (handleReference.IsAllocated) 1545 | handleReference.Free(); 1546 | } 1547 | //return RS_ERROR.RS_ERROR_UNSPECIFIED; 1548 | } 1549 | 1550 | public RS_ERROR awaitFrameData(int timeoutMs, ref FrameData data) 1551 | { 1552 | if (m_awaitFrameData == null) 1553 | return RS_ERROR.RS_NOT_INITIALISED; 1554 | 1555 | if (handleReference.IsAllocated) 1556 | handleReference.Free(); 1557 | handleReference = GCHandle.Alloc(data, GCHandleType.Pinned); 1558 | try 1559 | { 1560 | RS_ERROR error = m_awaitFrameData(timeoutMs, handleReference.AddrOfPinnedObject()); 1561 | if (error == RS_ERROR.RS_ERROR_SUCCESS) 1562 | { 1563 | data = (FrameData)Marshal.PtrToStructure(handleReference.AddrOfPinnedObject(), typeof(FrameData)); 1564 | } 1565 | return error; 1566 | } 1567 | finally 1568 | { 1569 | if (handleReference.IsAllocated) 1570 | handleReference.Free(); 1571 | } 1572 | //return RS_ERROR.RS_ERROR_UNSPECIFIED; 1573 | } 1574 | 1575 | public RS_ERROR setFollower(int isFollower) 1576 | { 1577 | if (m_setFollower == null) 1578 | return RS_ERROR.RS_NOT_INITIALISED; 1579 | 1580 | try 1581 | { 1582 | RS_ERROR error = m_setFollower(isFollower); 1583 | return error; 1584 | } 1585 | finally 1586 | { 1587 | } 1588 | } 1589 | 1590 | public RS_ERROR beginFollowerFrame(double tTracked) 1591 | { 1592 | if (m_beginFollowerFrame == null) 1593 | return RS_ERROR.RS_NOT_INITIALISED; 1594 | 1595 | try 1596 | { 1597 | RS_ERROR error = beginFollowerFrame(tTracked); 1598 | return error; 1599 | } 1600 | finally 1601 | { 1602 | } 1603 | } 1604 | 1605 | public RS_ERROR getFrameParameters(UInt64 schemaHash, ref float[] outParameterData) 1606 | { 1607 | if (m_getFrameParameters == null) 1608 | return RS_ERROR.RS_NOT_INITIALISED; 1609 | 1610 | if (handleReference.IsAllocated) 1611 | handleReference.Free(); 1612 | handleReference = GCHandle.Alloc(outParameterData, GCHandleType.Pinned); 1613 | try 1614 | { 1615 | RS_ERROR error = m_getFrameParameters(schemaHash, handleReference.AddrOfPinnedObject(), (UInt64)outParameterData.Length * sizeof(float)); 1616 | if (error == RS_ERROR.RS_ERROR_SUCCESS) 1617 | { 1618 | Marshal.Copy(handleReference.AddrOfPinnedObject(), outParameterData, 0, outParameterData.Length); 1619 | } 1620 | return error; 1621 | } 1622 | finally 1623 | { 1624 | if (handleReference.IsAllocated) 1625 | handleReference.Free(); 1626 | } 1627 | //return RS_ERROR.RS_ERROR_UNSPECIFIED; 1628 | } 1629 | 1630 | public RS_ERROR getFrameImageData(UInt64 schemaHash, ref ImageFrameData[] outParameterData) 1631 | { 1632 | if (m_getFrameImageData == null) 1633 | return RS_ERROR.RS_NOT_INITIALISED; 1634 | 1635 | if (handleReference.IsAllocated) 1636 | handleReference.Free(); 1637 | handleReference = GCHandle.Alloc(outParameterData, GCHandleType.Pinned); 1638 | try 1639 | { 1640 | var size = Marshal.SizeOf(typeof(ImageFrameData)); 1641 | RS_ERROR error = m_getFrameImageData(schemaHash, handleReference.AddrOfPinnedObject(), (UInt64)outParameterData.Length); 1642 | if (error == RS_ERROR.RS_ERROR_SUCCESS) 1643 | { 1644 | for (int i = 0; i < outParameterData.Length; ++i) 1645 | { 1646 | IntPtr ptr = new IntPtr(handleReference.AddrOfPinnedObject().ToInt64() + i * size); 1647 | outParameterData[i] = Marshal.PtrToStructure(ptr); 1648 | } 1649 | } 1650 | return error; 1651 | } 1652 | finally 1653 | { 1654 | if (handleReference.IsAllocated) 1655 | handleReference.Free(); 1656 | } 1657 | //return RS_ERROR.RS_ERROR_UNSPECIFIED; 1658 | } 1659 | 1660 | public RS_ERROR getFrameImage(Int64 imageId, ref Texture2D texture) 1661 | { 1662 | if (m_getFrameImage == null) 1663 | return RS_ERROR.RS_NOT_INITIALISED; 1664 | 1665 | try 1666 | { 1667 | SenderFrame data = new SenderFrame(); 1668 | data.type = SenderFrameType.RS_FRAMETYPE_DX11_TEXTURE; 1669 | data.dx11_resource = texture.GetNativeTexturePtr(); 1670 | RS_ERROR error = PluginEntry.instance.getFrameImage(imageId, ref data); 1671 | return error; 1672 | } 1673 | finally 1674 | { 1675 | } 1676 | //return RS_ERROR.RS_ERROR_UNSPECIFIED; 1677 | } 1678 | 1679 | public RS_ERROR getFrameText(UInt64 schemaHash, UInt32 textParamIndex, ref string text) 1680 | { 1681 | if (m_getFrameText == null) 1682 | return RS_ERROR.RS_NOT_INITIALISED; 1683 | 1684 | try 1685 | { 1686 | IntPtr textPtr = IntPtr.Zero; 1687 | RS_ERROR error = m_getFrameText(schemaHash, textParamIndex, ref textPtr); 1688 | if (error == RS_ERROR.RS_ERROR_SUCCESS) 1689 | text = Marshal.PtrToStringAnsi(textPtr); 1690 | return error; 1691 | } 1692 | finally 1693 | { 1694 | } 1695 | //return RS_ERROR.RS_ERROR_UNSPECIFIED; 1696 | } 1697 | 1698 | public RS_ERROR getFrameCamera(StreamHandle streamHandle, ref CameraData outCameraData) 1699 | { 1700 | if (m_getFrameCamera == null) 1701 | return RS_ERROR.RS_NOT_INITIALISED; 1702 | 1703 | if (handleReference.IsAllocated) 1704 | handleReference.Free(); 1705 | handleReference = GCHandle.Alloc(outCameraData, GCHandleType.Pinned); 1706 | try 1707 | { 1708 | RS_ERROR error = m_getFrameCamera(streamHandle, handleReference.AddrOfPinnedObject()); 1709 | if (error == RS_ERROR.RS_ERROR_SUCCESS) 1710 | { 1711 | outCameraData = (CameraData)Marshal.PtrToStructure(handleReference.AddrOfPinnedObject(), typeof(CameraData)); 1712 | } 1713 | return error; 1714 | } 1715 | finally 1716 | { 1717 | if (handleReference.IsAllocated) 1718 | handleReference.Free(); 1719 | } 1720 | //return RS_ERROR.RS_ERROR_UNSPECIFIED; 1721 | } 1722 | 1723 | #if PLUGIN_AVAILABLE 1724 | 1725 | [DllImport("kernel32.dll")] 1726 | static extern IntPtr LoadLibraryEx(string lpLibFileName, IntPtr fileHandle, int flags); 1727 | 1728 | [DllImport("kernel32.dll")] 1729 | static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); 1730 | 1731 | [DllImport("kernel32.dll", CharSet = CharSet.Ansi)] 1732 | static extern bool FreeLibrary(IntPtr hModule); 1733 | 1734 | private void free() 1735 | { 1736 | #if UNITY_EDITOR 1737 | UnityEditor.EditorApplication.quitting -= free; 1738 | UnityEditor.AssemblyReloadEvents.beforeAssemblyReload -= free; 1739 | #else 1740 | Application.quitting -= free; 1741 | #endif 1742 | 1743 | if (functionsLoaded) 1744 | { 1745 | if (m_logToD3 != null) 1746 | Application.logMessageReceivedThreaded -= logToD3; 1747 | 1748 | if (m_unregisterErrorLoggingFunc != null) 1749 | m_unregisterErrorLoggingFunc(); 1750 | if (m_unregisterLoggingFunc != null) 1751 | m_unregisterLoggingFunc(); 1752 | 1753 | RS_ERROR error = m_shutdown(); 1754 | if (error != RS_ERROR.RS_ERROR_SUCCESS) 1755 | Debug.LogError(string.Format("Failed to shutdown: {0}", error)); 1756 | functionsLoaded = false; 1757 | Debug.Log("Shut down RenderStream"); 1758 | } 1759 | 1760 | if (d3RenderStreamDLL != IntPtr.Zero) 1761 | { 1762 | FreeLibrary(d3RenderStreamDLL); 1763 | d3RenderStreamDLL = IntPtr.Zero; 1764 | Debug.Log("Unloaded RenderStream"); 1765 | } 1766 | 1767 | if (handleReference.IsAllocated) 1768 | handleReference.Free(); 1769 | } 1770 | 1771 | public bool IsAvailable 1772 | { 1773 | get 1774 | { 1775 | UnityEngine.Rendering.GraphicsDeviceType gapi = UnityEngine.SystemInfo.graphicsDeviceType; 1776 | return functionsLoaded && (gapi == UnityEngine.Rendering.GraphicsDeviceType.Direct3D11); 1777 | } 1778 | } 1779 | #else 1780 | private void free() {} 1781 | public bool IsAvailable { get { return false; } } 1782 | #endif 1783 | 1784 | const int LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010; 1785 | const int LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100; 1786 | const int LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200; 1787 | const int LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400; 1788 | const int LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; 1789 | 1790 | const string _dllName = "d3renderstream"; 1791 | 1792 | const int RENDER_STREAM_VERSION_MAJOR = 2; 1793 | const int RENDER_STREAM_VERSION_MINOR = 0; 1794 | 1795 | bool functionsLoaded = false; 1796 | IntPtr d3RenderStreamDLL = IntPtr.Zero; 1797 | GCHandle handleReference; // Everything is run under coroutines with odd lifetimes, so store a reference to GCHandle 1798 | GCHandle handleReference2; 1799 | 1800 | string name; 1801 | 1802 | // https://answers.unity.com/questions/16804/retrieving-project-name.html?childToView=478633#answer-478633 1803 | public string GetProjectName() 1804 | { 1805 | string[] s = Application.dataPath.Split('/'); 1806 | if (s.Length >= 2) 1807 | { 1808 | string projectName = s[s.Length - 2]; 1809 | return projectName; 1810 | } 1811 | return "UNKNOWN UNITY PROJECT"; 1812 | } 1813 | 1814 | private bool LoadFn(ref T fn, string fnName) where T : Delegate 1815 | { 1816 | fn = DelegateBuilder(d3RenderStreamDLL, fnName); 1817 | if (fn == null) 1818 | { 1819 | Debug.LogError(string.Format("Failed load function \"{0}\" from {1}.dll", fnName, _dllName)); 1820 | return false; 1821 | } 1822 | return true; 1823 | } 1824 | 1825 | private PluginEntry() 1826 | { 1827 | #if PLUGIN_AVAILABLE 1828 | RegistryKey d3Key = Registry.CurrentUser.OpenSubKey("Software"); 1829 | if (d3Key != null) 1830 | { 1831 | d3Key = d3Key.OpenSubKey("d3 Technologies"); 1832 | if (d3Key != null) 1833 | { 1834 | d3Key = d3Key.OpenSubKey("d3 Production Suite"); 1835 | } 1836 | } 1837 | 1838 | if (d3Key == null) 1839 | { 1840 | Debug.LogError(string.Format("Failed to find path to {0}.dll. d3 Not installed?", _dllName)); 1841 | return; 1842 | } 1843 | 1844 | string d3ExePath = d3Key.GetValue("exe path").ToString(); 1845 | d3ExePath = d3ExePath.Replace(@"\\", @"\"); 1846 | int endSeparator = d3ExePath.LastIndexOf(Path.DirectorySeparatorChar); 1847 | if (endSeparator != d3ExePath.Length - 1) 1848 | d3ExePath = d3ExePath.Substring(0, endSeparator + 1); 1849 | 1850 | string libPath = d3ExePath + _dllName + ".dll"; 1851 | d3RenderStreamDLL = LoadWin32Library(libPath); 1852 | if (d3RenderStreamDLL == IntPtr.Zero) 1853 | { 1854 | Debug.LogError(string.Format("Failed to load {0}.dll from {1}", _dllName, d3ExePath)); 1855 | return; 1856 | } 1857 | 1858 | functionsLoaded = true; 1859 | 1860 | functionsLoaded &= LoadFn(ref m_registerLoggingFunc, "rs_registerLoggingFunc"); 1861 | functionsLoaded &= LoadFn(ref m_registerErrorLoggingFunc, "rs_registerErrorLoggingFunc"); 1862 | functionsLoaded &= LoadFn(ref m_registerVerboseLoggingFunc, "rs_registerVerboseLoggingFunc"); 1863 | 1864 | functionsLoaded &= LoadFn(ref m_unregisterLoggingFunc, "rs_unregisterLoggingFunc"); 1865 | functionsLoaded &= LoadFn(ref m_unregisterErrorLoggingFunc, "rs_unregisterErrorLoggingFunc"); 1866 | functionsLoaded &= LoadFn(ref m_unregisterVerboseLoggingFunc, "rs_unregisterVerboseLoggingFunc"); 1867 | 1868 | functionsLoaded &= LoadFn(ref m_initialise, "rs_initialise"); 1869 | functionsLoaded &= LoadFn(ref m_initialiseGpGpuWithoutInterop, "rs_initialiseGpGpuWithoutInterop"); 1870 | functionsLoaded &= LoadFn(ref m_initialiseGpGpuWithDX11Device, "rs_initialiseGpGpuWithDX11Device"); 1871 | functionsLoaded &= LoadFn(ref m_initialiseGpGpuWithDX11Resource, "rs_initialiseGpGpuWithDX11Resource"); 1872 | functionsLoaded &= LoadFn(ref m_initialiseGpGpuWithDX12DeviceAndQueue, "rs_initialiseGpGpuWithDX12DeviceAndQueue"); 1873 | functionsLoaded &= LoadFn(ref m_initialiseGpGpuWithOpenGlContexts, "rs_initialiseGpGpuWithOpenGlContexts"); 1874 | functionsLoaded &= LoadFn(ref m_initialiseGpGpuWithVulkanDevice, "rs_initialiseGpGpuWithVulkanDevice"); 1875 | functionsLoaded &= LoadFn(ref m_shutdown, "rs_shutdown"); 1876 | 1877 | functionsLoaded &= LoadFn(ref m_useDX12SharedHeapFlag, "rs_useDX12SharedHeapFlag"); 1878 | functionsLoaded &= LoadFn(ref m_saveSchema, "rs_saveSchema"); 1879 | functionsLoaded &= LoadFn(ref m_loadSchema, "rs_loadSchema"); 1880 | 1881 | functionsLoaded &= LoadFn(ref m_setSchema, "rs_setSchema"); 1882 | 1883 | functionsLoaded &= LoadFn(ref m_getStreams, "rs_getStreams"); 1884 | 1885 | functionsLoaded &= LoadFn(ref m_awaitFrameData, "rs_awaitFrameData"); 1886 | functionsLoaded &= LoadFn(ref m_setFollower, "rs_setFollower"); 1887 | functionsLoaded &= LoadFn(ref m_beginFollowerFrame, "rs_beginFollowerFrame"); 1888 | 1889 | functionsLoaded &= LoadFn(ref m_getFrameParameters, "rs_getFrameParameters"); 1890 | functionsLoaded &= LoadFn(ref m_getFrameImageData, "rs_getFrameImageData"); 1891 | functionsLoaded &= LoadFn(ref m_getFrameImage, "rs_getFrameImage2"); 1892 | functionsLoaded &= LoadFn(ref m_getFrameText, "rs_getFrameText"); 1893 | 1894 | functionsLoaded &= LoadFn(ref m_getFrameCamera, "rs_getFrameCamera"); 1895 | functionsLoaded &= LoadFn(ref m_sendFrame, "rs_sendFrame2"); 1896 | 1897 | functionsLoaded &= LoadFn(ref m_releaseImage, "rs_releaseImage2"); 1898 | 1899 | functionsLoaded &= LoadFn(ref m_logToD3, "rs_logToD3"); 1900 | functionsLoaded &= LoadFn(ref m_sendProfilingData, "rs_sendProfilingData"); 1901 | functionsLoaded &= LoadFn(ref m_setNewStatusMessage, "rs_setNewStatusMessage"); 1902 | 1903 | if (!functionsLoaded) 1904 | { 1905 | Debug.LogError(string.Format("One or more functions failed load from {0}.dll", _dllName)); 1906 | return; 1907 | } 1908 | 1909 | // There is an issue with these logging callbacks sometimes throwing inside of the dll which can cause all kinds of problems 1910 | // exception consistentency is questionable, often the same exception can be seen at the same point in time 1911 | // however periodically a minor difference may occur where the exception is not thrown where expected or even at all 1912 | 1913 | m_logInfo = logInfo; 1914 | m_logError = logError; 1915 | 1916 | if (m_registerLoggingFunc != null) 1917 | m_registerLoggingFunc(m_logInfo); 1918 | if (m_registerErrorLoggingFunc != null) 1919 | m_registerErrorLoggingFunc(m_logError); 1920 | 1921 | if (m_logToD3 != null) 1922 | Application.logMessageReceivedThreaded += logToD3; 1923 | 1924 | RS_ERROR error = m_initialise(RENDER_STREAM_VERSION_MAJOR, RENDER_STREAM_VERSION_MINOR); 1925 | if (error == RS_ERROR.RS_ERROR_INCOMPATIBLE_VERSION) 1926 | Debug.LogError(string.Format("Unsupported RenderStream library, expected version {0}.{1}", RENDER_STREAM_VERSION_MAJOR, RENDER_STREAM_VERSION_MINOR)); 1927 | else if (error != RS_ERROR.RS_ERROR_SUCCESS) 1928 | Debug.LogError(string.Format("Failed to initialise: {0}", error)); 1929 | else 1930 | { 1931 | Texture2D texture = new Texture2D(1, 1); 1932 | error = m_initialiseGpGpuWithDX11Resource(texture.GetNativeTexturePtr()); 1933 | if (error != RS_ERROR.RS_ERROR_SUCCESS) 1934 | Debug.LogError(string.Format("Failed to initialise GPU interop: {0}", error)); 1935 | } 1936 | 1937 | Debug.Log("Loaded RenderStream"); 1938 | 1939 | #if UNITY_EDITOR 1940 | UnityEditor.EditorApplication.quitting += free; 1941 | UnityEditor.AssemblyReloadEvents.beforeAssemblyReload += free; 1942 | #else 1943 | Application.quitting += free; 1944 | #endif 1945 | 1946 | name = GetProjectName(); 1947 | #else 1948 | Debug.LogError(string.Format("{0}.dll is only available on Windows", _dllName)); 1949 | #endif 1950 | } 1951 | 1952 | ~PluginEntry() 1953 | { 1954 | free(); 1955 | } 1956 | 1957 | static IntPtr LoadWin32Library(string dllFilePath) 1958 | { 1959 | System.IntPtr moduleHandle = IntPtr.Zero ; 1960 | #if PLUGIN_AVAILABLE 1961 | moduleHandle = LoadLibraryEx(dllFilePath, IntPtr.Zero, LOAD_IGNORE_CODE_AUTHZ_LEVEL | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS); 1962 | if (moduleHandle == IntPtr.Zero) 1963 | { 1964 | // I'm gettin last dll error 1965 | int errorCode = Marshal.GetLastWin32Error(); 1966 | Debug.LogError(string.Format("There was an error during dll loading : {0}, error - {1}", dllFilePath, errorCode)); 1967 | } 1968 | #endif 1969 | return moduleHandle; 1970 | } 1971 | 1972 | static T DelegateBuilder(IntPtr loadedDLL, string functionName) where T : Delegate 1973 | { 1974 | IntPtr pAddressOfFunctionToCall = IntPtr.Zero; 1975 | #if PLUGIN_AVAILABLE 1976 | pAddressOfFunctionToCall = GetProcAddress(loadedDLL, functionName); 1977 | if (pAddressOfFunctionToCall == IntPtr.Zero) 1978 | { 1979 | return null; 1980 | } 1981 | #endif 1982 | T functionDelegate = Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(T)) as T; 1983 | return functionDelegate; 1984 | } 1985 | } 1986 | 1987 | public class FrameSender 1988 | { 1989 | struct Frame 1990 | { 1991 | public FrameRegion region; 1992 | public RSPixelFormat fmt; 1993 | public AsyncGPUReadbackRequest readback; 1994 | public CameraResponseData responseData; 1995 | } 1996 | 1997 | private FrameSender() { } 1998 | public FrameSender(string name, Camera cam) 1999 | { 2000 | m_name = name; 2001 | Cam = cam; 2002 | 2003 | Debug.Log(string.Format("Creating stream {0}", m_name)); 2004 | StreamDescription stream = Array.Find(DisguiseRenderStream.streams, s => s.name == name); 2005 | Debug.Log(string.Format(" Channel {0} at {1}x{2}@{3}", stream.channel, stream.width, stream.height, stream.format)); 2006 | 2007 | m_lastFrameCount = -1; 2008 | m_streamHandle = stream.handle; 2009 | m_width = (int)stream.width; 2010 | m_height = (int)stream.height; 2011 | 2012 | m_frameRegion = new Rect(stream.clipping.left, stream.clipping.top, stream.clipping.right - stream.clipping.left, stream.clipping.bottom - stream.clipping.top); 2013 | 2014 | RenderTextureDescriptor desc = new RenderTextureDescriptor(m_width, m_height, PluginEntry.ToRenderTextureFormat(stream.format), 24); 2015 | m_sourceTex = new RenderTexture(desc) 2016 | { 2017 | name = m_name + " Texture" 2018 | }; 2019 | Cam.targetTexture = m_sourceTex; 2020 | m_convertedTex = new Texture2D(m_sourceTex.width, m_sourceTex.height, PluginEntry.ToTextureFormat(stream.format), false, false); 2021 | 2022 | Debug.Log(string.Format("Created stream {0} with handle {1}", m_name, m_streamHandle)); 2023 | } 2024 | 2025 | public bool GetCameraData(ref CameraData cameraData) 2026 | { 2027 | return PluginEntry.instance.getFrameCamera(m_streamHandle, ref cameraData) == RS_ERROR.RS_ERROR_SUCCESS; 2028 | } 2029 | 2030 | public void SendFrame(Texture2D frame) 2031 | { 2032 | unsafe 2033 | { 2034 | SenderFrame data = new SenderFrame(); 2035 | data.type = SenderFrameType.RS_FRAMETYPE_DX11_TEXTURE; 2036 | data.dx11_resource = frame.GetNativeTexturePtr(); 2037 | RS_ERROR error = PluginEntry.instance.sendFrame(m_streamHandle, ref data, ref m_responseData); 2038 | if (error != RS_ERROR.RS_ERROR_SUCCESS) 2039 | Debug.LogError(string.Format("Error sending frame: {0}", error)); 2040 | } 2041 | } 2042 | 2043 | public void SendFrame(FrameData frameData, CameraData cameraData) 2044 | { 2045 | if (m_lastFrameCount == Time.frameCount) 2046 | return; 2047 | 2048 | m_lastFrameCount = Time.frameCount; 2049 | 2050 | if (m_convertedTex.width != m_sourceTex.width || m_convertedTex.height != m_sourceTex.height) 2051 | m_convertedTex.Resize(m_sourceTex.width, m_sourceTex.height, m_convertedTex.format, false); 2052 | 2053 | m_cameraResponseData = new CameraResponseData { tTracked = frameData.tTracked, camera = cameraData }; 2054 | 2055 | if (cameraHandleReference.IsAllocated) 2056 | cameraHandleReference.Free(); 2057 | cameraHandleReference = GCHandle.Alloc(m_cameraResponseData, GCHandleType.Pinned); 2058 | 2059 | m_responseData = new FrameResponseData{ cameraData = cameraHandleReference.AddrOfPinnedObject() }; 2060 | 2061 | // Blocks HDRP streams in r18.2 2062 | // #if UNITY_PIPELINE_HDRP 2063 | // Volume volume = Cam.GetComponent(); 2064 | // if (!volume.profile) 2065 | // Debug.Log("Missing profile"); 2066 | 2067 | // if (!volume.profile.TryGet(out m_captureAfterPostProcess)) 2068 | // { 2069 | // Debug.Log("Missing captureAfterPostProcess"); 2070 | // m_captureAfterPostProcess = volume.profile.Add(true); 2071 | // } 2072 | // m_captureAfterPostProcess.width.value = (Int32)m_width; 2073 | // m_captureAfterPostProcess.height.value = (Int32)m_height; 2074 | // #else 2075 | RenderTexture unflipped = RenderTexture.GetTemporary(m_sourceTex.width, m_sourceTex.height, 0, m_sourceTex.format); 2076 | Graphics.Blit(m_sourceTex, unflipped, new Vector2(1.0f, -1.0f), new Vector2(0.0f, 1.0f)); 2077 | Graphics.ConvertTexture(unflipped, m_convertedTex); 2078 | RenderTexture.ReleaseTemporary(unflipped); 2079 | 2080 | try 2081 | { 2082 | SendFrame(m_convertedTex); 2083 | } 2084 | finally 2085 | { 2086 | if (cameraHandleReference.IsAllocated) 2087 | cameraHandleReference.Free(); 2088 | } 2089 | 2090 | // #endif 2091 | } 2092 | 2093 | public void DestroyStream() 2094 | { 2095 | m_streamHandle = 0; 2096 | } 2097 | 2098 | public Camera Cam { get; set; } 2099 | 2100 | private RenderTexture m_sourceTex; 2101 | private FrameResponseData m_responseData; 2102 | private CameraResponseData m_cameraResponseData; 2103 | private GCHandle cameraHandleReference; 2104 | 2105 | string m_name; 2106 | Texture2D m_convertedTex; 2107 | int m_lastFrameCount; 2108 | 2109 | StreamHandle m_streamHandle; 2110 | int m_width; 2111 | int m_height; 2112 | Rect m_frameRegion; 2113 | public Rect subRegion 2114 | { 2115 | get 2116 | { 2117 | return m_frameRegion; 2118 | } 2119 | } 2120 | // Blocks HDRP streams in r18.2 2121 | // #if UNITY_PIPELINE_HDRP 2122 | // private DisguiseCameraCaptureAfterPostProcess m_captureAfterPostProcess; 2123 | // #endif 2124 | } 2125 | 2126 | } 2127 | --------------------------------------------------------------------------------