using UnityEngine; using Noesis; using System; using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine.Rendering; //[ExecuteInEditMode] [AddComponentMenu("NoesisGUI/Noesis View")] [HelpURL("https://www.noesisengine.com/docs")] [DisallowMultipleComponent] public class NoesisView: MonoBehaviour { #region Public properties /// /// User interface definition XAML /// public NoesisXaml Xaml { set { this._xaml = value; } get { return this._xaml; } } /// /// The texture to render this View into /// public RenderTexture Texture { set { this._texture = value; } get { return this._texture; } } /// /// PPAA is a 'cheap' antialiasing algorithm useful when GPU MSAA is not enabled /// public bool IsPPAAEnabled { set { this._isPPAAEnabled = value; } get { return this._isPPAAEnabled; } } /// /// Tessellation curve tolerance in screen space. 'Medium Quality' is usually fine for PPAA (non-multisampled) /// while 'High Quality' is the recommended pixel error if you are rendering to a 8x multisampled surface /// public float TessellationMaxPixelError { set { this._tessellationMaxPixelError = value; } get { return this._tessellationMaxPixelError; } } /// /// Bit flags used for debug rendering purposes. /// public RenderFlags RenderFlags { set { this._renderFlags = value; } get { return this._renderFlags; } } /// /// When continuous rendering is disabled rendering only happens when needed. For performance /// purposes and to save battery this is the default mode when rendering to texture. /// public bool ContinuousRendering { set { this._continuousRendering = value; } get { return this._continuousRendering; } } /// /// After LateUpdate() has been invoked this flag indicates if the GUI needs to be repainted. /// This flag can be used on manually painted cameras to optimize performance and save battery. /// public bool NeedsRendering { set { this._needsRendering = value; } get { return this._needsRendering; } } /// /// Enables keyboard input management. /// public bool EnableKeyboard { set { this._enableKeyboard = value; } get { return this._enableKeyboard; } } /// /// Enables mouse input management. /// public bool EnableMouse { set { this._enableMouse = value; } get { return this._enableMouse; } } /// /// Enables touch input management. /// public bool EnableTouch { set { this._enableTouch = value; } get { return this._enableTouch; } } /// /// Enables gamepad input management. /// public bool EnableGamepad { set { this._enableGamepad = value; } get { return this._enableGamepad; } } /// /// Emulate touch input with mouse. /// public bool EmulateTouch { set { this._emulateTouch = value; } get { return this._emulateTouch; } } /// /// When enabled, UI is updated using Time.realtimeSinceStartup. /// public bool UseRealTimeClock { set { this._useRealTimeClock = value; } get { return this._useRealTimeClock; } } /// /// Gets the root of the loaded Xaml. /// /// Root element. public FrameworkElement Content { get { return _uiView != null ? _uiView.Content : null; } } /// /// Indicates if this component is rendering UI to a RenderTexture. /// /// public bool IsRenderToTexture() { return _textureCamera != null || gameObject.GetComponent() == null; } #endregion #region Public events #region Render public event RenderingEventHandler Rendering { add { if (_uiView != null) { _uiView.Rendering += value; } } remove { if (_uiView != null) { _uiView.Rendering -= value; } } } public ViewStats GetStats() { if (_uiView != null) { return _uiView.GetStats(); } return new ViewStats(); } #endregion #region Keyboard input events /// /// Notifies Renderer that a key was pressed. /// /// Key identifier. public bool KeyDown(Noesis.Key key) { if (_uiView != null) { return _uiView.KeyDown(key); } return false; } /// /// Notifies Renderer that a key was released. /// /// Key identifier. public bool KeyUp(Noesis.Key key) { if (_uiView != null) { return _uiView.KeyUp(key); } return false; } /// /// Notifies Renderer that a key was translated to the corresponding character. /// /// Unicode character value. public bool Char(uint ch) { if (_uiView != null) { return _uiView.Char(ch); } return false; } #endregion #region Mouse input events /// /// Notifies Renderer that mouse was moved. The mouse position is specified in renderer /// surface pixel coordinates. /// /// Mouse x-coordinate. /// Mouse y-coordinate. public bool MouseMove(int x, int y) { if (_uiView != null) { return _uiView.MouseMove(x, y); } return false; } /// /// Notifies Renderer that a mouse button was pressed. The mouse position is specified in /// renderer surface pixel coordinates. /// /// Mouse x-coordinate. /// Mouse y-coordinate. /// Indicates which button was pressed. public bool MouseButtonDown(int x, int y, Noesis.MouseButton button) { if (_uiView != null) { return _uiView.MouseButtonDown(x, y, button); } return false; } /// Notifies Renderer that a mouse button was released. The mouse position is specified in /// renderer surface pixel coordinates. /// /// Mouse x-coordinate. /// Mouse y-coordinate. /// Indicates which button was released. public bool MouseButtonUp(int x, int y, Noesis.MouseButton button) { if (_uiView != null) { return _uiView.MouseButtonUp(x, y, button); } return false; } /// /// Notifies Renderer of a mouse button double click. The mouse position is specified in /// renderer surface pixel coordinates. /// /// Mouse x-coordinate. /// Mouse y-coordinate. /// Indicates which button was pressed. public bool MouseDoubleClick(int x, int y, Noesis.MouseButton button) { if (_uiView != null) { return _uiView.MouseDoubleClick(x, y, button); } return false; } /// /// Notifies Renderer that mouse wheel was rotated. The mouse position is specified in /// renderer surface pixel coordinates. /// /// Mouse x-coordinate. /// Mouse y-coordinate. /// Indicates the amount mouse wheel has changed. public bool MouseWheel(int x, int y, int wheelRotation) { if (_uiView != null) { return _uiView.MouseWheel(x, y, wheelRotation); } return false; } #endregion #region Touch input events /// /// Notifies Renderer that a finger is moving on the screen. The finger position is /// specified in renderer surface pixel coordinates. /// /// Finger x-coordinate. /// Finger y-coordinate. /// Finger identifier. public bool TouchMove(int x, int y, uint touchId) { if (_uiView != null) { return _uiView.TouchMove(x, y, touchId); } return false; } /// /// Notifies Renderer that a finger touches the screen. The finger position is /// specified in renderer surface pixel coordinates. /// /// Finger x-coordinate. /// Finger y-coordinate. /// Finger identifier. public bool TouchDown(int x, int y, uint touchId) { if (_uiView != null) { return _uiView.TouchDown(x, y, touchId); } return false; } /// /// Notifies Renderer that a finger is raised off the screen. The finger position is /// specified in renderer surface pixel coordinates. /// /// Finger x-coordinate. /// Finger y-coordinate. /// Finger identifier. public bool TouchUp(int x, int y, uint touchId) { if (_uiView != null) { return _uiView.TouchUp(x, y, touchId); } return false; } #endregion #endregion #region Public methods /// /// Loads the user interface specified in the XAML property /// public void LoadXaml(bool force) { if (force) { DestroyView(); } if (_xaml != null && _uiView == null) { FrameworkElement content = _xaml.Load() as FrameworkElement; if (content == null) { throw new System.Exception("XAML root is not FrameworkElement"); } CreateView(content); } } #endregion #region Private members #region MonoBehavior component messages /// /// Called once when component is attached to GameObject for the first time /// void Reset() { _isPPAAEnabled = true; _tessellationMaxPixelError = Noesis.TessellationMaxPixelError.MediumQuality.Error; _renderFlags = 0; _continuousRendering = gameObject.GetComponent() != null; _enableKeyboard = true; _enableMouse = true; _enableTouch = true; _enableGamepad = false; _emulateTouch = false; _useRealTimeClock = false; } Camera _myCamera; void OnEnable() { _commands = new UnityEngine.Rendering.CommandBuffer(); if (GetComponent() == null) { if (_texture != null && _textureCamera == null) { _textureCamera = gameObject.AddComponent(); _textureCamera.clearFlags = CameraClearFlags.SolidColor; _textureCamera.backgroundColor = new UnityEngine.Color(0.0f, 0.0f, 0.0f, 0.0f); _textureCamera.renderingPath = RenderingPath.Forward; _textureCamera.depthTextureMode = DepthTextureMode.None; _textureCamera.opaqueSortMode = UnityEngine.Rendering.OpaqueSortMode.NoDistanceSort; _textureCamera.transparencySortMode = TransparencySortMode.Orthographic; _textureCamera.clearStencilAfterLightingPass = false; #if UNITY_5_6_OR_NEWER _textureCamera.allowHDR = false; #else _textureCamera.hdr = false; #endif _textureCamera.useOcclusionCulling = false; _textureCamera.cullingMask = 0; _textureCamera.targetTexture = _texture; _textureCamera.enabled = false; } } _myCamera = GetComponent(); LoadXaml(false); Camera.onPreRender += PreRender; #if UNITY_2019_1_OR_NEWER UnityEngine.Rendering.RenderPipelineManager.beginCameraRendering += BeginCameraRendering; UnityEngine.Rendering.RenderPipelineManager.endCameraRendering += EndCameraRendering; #endif } void OnDisable() { Camera.onPreRender -= PreRender; #if UNITY_2019_1_OR_NEWER UnityEngine.Rendering.RenderPipelineManager.beginCameraRendering -= BeginCameraRendering; UnityEngine.Rendering.RenderPipelineManager.endCameraRendering -= EndCameraRendering; #endif } bool _haveNoesisVertical = false; bool _haveNoesisHorizontal = false; bool _haveNoesisAccept = false; bool _haveNoesisCancel = false; bool _haveNoesisMenu = false; bool _haveNoesisView = false; bool _haveNoesisPageLeft = false; bool _haveNoesisPageRight = false; bool _haveNoesisPageUp = false; bool _haveNoesisPageDown = false; bool _haveNoesisScroll = false; bool _haveNoesisHScroll = false; void Awake() { // Unity does not offer a better way to detect mapped axis try { Input.GetAxis("Noesis_Vertical"); _haveNoesisVertical = true; } catch (Exception) {} try { Input.GetAxis("Noesis_Horizontal"); _haveNoesisHorizontal = true; } catch (Exception) {} try { Input.GetAxis("Noesis_Accept"); _haveNoesisAccept = true; } catch (Exception) {} try { Input.GetAxis("Noesis_Cancel"); _haveNoesisCancel = true; } catch (Exception) {} try { Input.GetAxis("Noesis_Menu"); _haveNoesisMenu = true; } catch (Exception) {} try { Input.GetAxis("Noesis_View"); _haveNoesisView = true; } catch (Exception) {} try { Input.GetAxis("Noesis_PageLeft"); _haveNoesisPageLeft = true; } catch (Exception) {} try { Input.GetAxis("Noesis_PageRight"); _haveNoesisPageRight = true; } catch (Exception) {} try { Input.GetAxis("Noesis_PageUp"); _haveNoesisPageUp = true; } catch (Exception) {} try { Input.GetAxis("Noesis_PageDown"); _haveNoesisPageDown = true; } catch (Exception) {} try { Input.GetAxis("Noesis_Scroll"); _haveNoesisScroll = true; } catch (Exception) {} try { Input.GetAxis("Noesis_HScroll"); _haveNoesisHScroll = true; } catch (Exception) {} } #if UNITY_2019_1_OR_NEWER private void BeginCameraRendering(ScriptableRenderContext context, Camera camera) { if (_myCamera == camera) { RenderOffscreen(); } } private void EndCameraRendering(ScriptableRenderContext context, Camera camera) { if (_myCamera == camera) { RenderOnscreen(); } } #endif void OnDestroy() { if (_textureCamera != null) { UnityEngine.Object.Destroy(_textureCamera); _textureCamera = null; } DestroyView(); } UnityEngine.EventSystems.PointerEventData _pointerData; private UnityEngine.Vector2 ProjectPointer(float x, float y) { if (_textureCamera == null) { return new UnityEngine.Vector2(x, UnityEngine.Screen.height - y); } else if (_texture != null) { // Project using texture coordinates // First try with Unity GUI RawImage objects UnityEngine.EventSystems.EventSystem eventSystem = UnityEngine.EventSystems.EventSystem.current; if (eventSystem != null && eventSystem.IsPointerOverGameObject()) { UnityEngine.Vector2 pos = new UnityEngine.Vector2(x, y); if (_pointerData == null) { _pointerData = new UnityEngine.EventSystems.PointerEventData(eventSystem) { pointerId = 0, position = pos }; } else { _pointerData.Reset(); } _pointerData.delta = pos - _pointerData.position; _pointerData.position = pos; UnityEngine.RectTransform rect = GetComponent(); if (rect != null && UnityEngine.RectTransformUtility.ScreenPointToLocalPointInRectangle( rect, _pointerData.position, _pointerData.pressEventCamera, out pos)) { UnityEngine.Vector2 pivot = new UnityEngine.Vector2( rect.pivot.x * rect.rect.width, rect.pivot.y * rect.rect.height); float texCoordX = (pos.x + pivot.x) / rect.rect.width; float texCoordY = (pos.y + pivot.y) / rect.rect.height; float localX = _texture.width * texCoordX; float localY = _texture.height * (1.0f - texCoordY); return new UnityEngine.Vector2(localX, localY); } } // NOTE: A MeshCollider must be attached to the target to obtain valid // texture coordinates, otherwise Hit Testing won't work UnityEngine.Ray ray = UnityEngine.Camera.main.ScreenPointToRay(new UnityEngine.Vector3(x, y, 0)); UnityEngine.RaycastHit hit; if (UnityEngine.Physics.Raycast(ray, out hit)) { if (hit.collider.gameObject == gameObject) { float localX = _texture.width * hit.textureCoord.x; float localY = _texture.height * (1.0f - hit.textureCoord.y); return new UnityEngine.Vector2(localX, localY); } } return new UnityEngine.Vector2(-1, -1); } return Vector2.zero; } private bool _touchEmulated = false; private UnityEngine.Vector3 _mousePos; private void UpdateMouse() { // mouse move if (_mousePos != UnityEngine.Input.mousePosition) { _mousePos = UnityEngine.Input.mousePosition; UnityEngine.Vector2 mouse = ProjectPointer(_mousePos.x, _mousePos.y); if (_emulateTouch && _touchEmulated) { _uiView.TouchMove((int)mouse.x, (int)mouse.y, 0); } else { _uiView.MouseMove((int)mouse.x, (int)mouse.y); } } } private void UpdateTouch() { for (int i = 0; i < UnityEngine.Input.touchCount; i++) { UnityEngine.Touch touch = UnityEngine.Input.GetTouch(i); uint id = (uint)touch.fingerId; UnityEngine.Vector2 pos = ProjectPointer(touch.position.x, touch.position.y); UnityEngine.TouchPhase phase = touch.phase; if (phase == UnityEngine.TouchPhase.Began) { _uiView.TouchDown((int)pos.x, (int)pos.y, id); } else if (phase == UnityEngine.TouchPhase.Moved || phase == UnityEngine.TouchPhase.Stationary) { _uiView.TouchMove((int)pos.x, (int)pos.y, id); } else { _uiView.TouchUp((int)pos.x, (int)pos.y, id); } } } [FlagsAttribute] enum GamepadButtons { Up = 1, Down = 2, Left = 4, Right = 8, Accept = 16, Cancel = 32, Menu = 64, View = 128, PageUp = 256, PageDown = 512, PageLeft = 1024, PageRight = 2048 } private static readonly Dictionary GamepadButtonsMap = new Dictionary { { GamepadButtons.Up, Key.GamepadUp }, { GamepadButtons.Down, Key.GamepadDown }, { GamepadButtons.Left, Key.GamepadLeft }, { GamepadButtons.Right, Key.GamepadRight }, { GamepadButtons.Accept, Key.GamepadAccept }, { GamepadButtons.Cancel, Key.GamepadCancel }, { GamepadButtons.Menu, Key.GamepadMenu}, { GamepadButtons.View, Key.GamepadView }, { GamepadButtons.PageUp, Key.GamepadPageUp }, { GamepadButtons.PageDown, Key.GamepadPageDown }, { GamepadButtons.PageLeft, Key.GamepadPageLeft }, { GamepadButtons.PageRight, Key.GamepadPageRight }, }; private GamepadButtons _gamepadButtons = 0; private void UpdateGamepad() { GamepadButtons gamepadButtons = 0; if (_haveNoesisVertical) { float v = Input.GetAxis("Noesis_Vertical"); if (v > 0.5f) gamepadButtons |= GamepadButtons.Up; if (v < -0.5f) gamepadButtons |= GamepadButtons.Down; } if (_haveNoesisHorizontal) { float v = Input.GetAxis("Noesis_Horizontal"); if (v > 0.5f) gamepadButtons |= GamepadButtons.Right; if (v < -0.5f) gamepadButtons |= GamepadButtons.Left; } if (_haveNoesisAccept) { if (Input.GetButton("Noesis_Accept")) gamepadButtons |= GamepadButtons.Accept; } if (_haveNoesisCancel) { if (Input.GetButton("Noesis_Cancel")) gamepadButtons |= GamepadButtons.Cancel; } if (_haveNoesisMenu) { if (Input.GetButton("Noesis_Menu")) gamepadButtons |= GamepadButtons.Menu; } if (_haveNoesisView) { if (Input.GetButton("Noesis_View")) gamepadButtons |= GamepadButtons.View; } if (_haveNoesisPageLeft) { if (Input.GetButton("Noesis_PageLeft")) gamepadButtons |= GamepadButtons.PageLeft; } if (_haveNoesisPageRight) { if (Input.GetButton("Noesis_PageRight")) gamepadButtons |= GamepadButtons.PageRight; } if (_haveNoesisPageUp) { if (Input.GetAxis("Noesis_PageUp") > 0.5f) gamepadButtons |= GamepadButtons.PageUp; } if (_haveNoesisPageDown) { if (Input.GetAxis("Noesis_PageDown") > 0.5f) gamepadButtons |= GamepadButtons.PageDown; } if (_haveNoesisScroll) { float v = Input.GetAxisRaw("Noesis_Scroll"); if (Math.Abs(v) > 0.05f) _uiView.Scroll(v); } if (_haveNoesisHScroll) { float v = Input.GetAxisRaw("Noesis_HScroll"); if (Math.Abs(v) > 0.05f) _uiView.HScroll(v); } GamepadButtons delta = gamepadButtons ^ _gamepadButtons; if (delta != 0) { foreach (var pair in GamepadButtonsMap) { if ((delta & pair.Key) > 0) { if ((gamepadButtons & pair.Key) > 0) { _uiView.KeyDown(pair.Value); } else { _uiView.KeyUp(pair.Value); } } } } _gamepadButtons = gamepadButtons; } private void UpdateInputs() { if (_enableMouse) { UpdateMouse(); } if (_enableTouch) { UpdateTouch(); } if (_enableGamepad) { UpdateGamepad(); } } private void UpdateSettings() { if (_myCamera != null) { _uiView.SetSize(_myCamera.pixelWidth, _myCamera.pixelHeight); } _uiView.SetIsPPAAEnabled(_isPPAAEnabled); _uiView.SetTessellationMaxPixelError(_tessellationMaxPixelError); _uiView.SetFlags(_renderFlags); } private bool _visible = true; void LateUpdate() { if (_uiView != null && _visible) { UpdateInputs(); UpdateSettings(); NoesisUnity.IME.Update(_uiView); NoesisUnity.TouchKeyboard.Update(); Noesis_UnityUpdate(); _needsRendering = _uiView.Update(_useRealTimeClock ? Time.realtimeSinceStartup : Time.time); if (_textureCamera != null) { if (_continuousRendering || _needsRendering) { _textureCamera.Render(); } } } } void OnBecameInvisible() { if (_uiView != null && _textureCamera != null) { _visible = false; } } void OnBecameVisible() { if (_uiView != null && _textureCamera != null) { _visible = true; } } private bool _updatePending = true; private void PreRender(Camera cam) { // In case there are several cameras rendering to the same texture (Camera Stacking), // the camera rendered first (less depth) is the one that must apply our offscreen phase // to avoid inefficient Load/Store in Tiled architectures // Note that camera stacking is deprecated in LWRP and HDRP if (_updatePending && cam.targetTexture == _myCamera.targetTexture && cam.depth <= _myCamera.depth) { RenderOffscreen(); _updatePending = false; } } private void RenderOffscreen() { if (_uiView != null && _visible) { _commands.Clear(); _commands.name = "NoesisView_Offscreen"; NoesisRenderer.RenderOffscreen(_uiView, _commands); Graphics.ExecuteCommandBuffer(_commands); // CommandBuffer.IssuePluginEventAndData does not invalidate state (CommandBuffer.IssuePluginEvent does) GL.InvalidateState(); // Unity should restore the render target at this point but sometimes (for example a scene without lights) // it doesn't. We use this hack to flush the active render target and force unity to set the camera RT afterward RenderTexture surface = RenderTexture.GetTemporary(1,1); Graphics.SetRenderTarget(surface); RenderTexture.ReleaseTemporary(surface); } } private bool IsGL() { return SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLCore; } private bool FlipRender() { // In D3D when Unity is rendering to an intermmediate texture instead of the back buffer, we need to vertically flip the output // Note that camera.activeTexture should only be checked from OnPostRender if (!IsGL()) { return _myCamera.activeTexture != null; } return false; } private void RenderOnscreen() { if (_uiView != null && _visible) { _commands.Clear(); _commands.name = "NoesisView_Onscreen"; NoesisRenderer.RenderOnscreen(_uiView, FlipRender(), _commands); Graphics.ExecuteCommandBuffer(_commands); GL.InvalidateState(); if (_texture != null) { _texture.DiscardContents(false, true); } _updatePending = true; } } private void OnPostRender() { RenderOnscreen(); } private UnityEngine.EventModifiers _modifiers = 0; private void ProcessModifierKey(EventModifiers modifiers, EventModifiers delta, EventModifiers flag, Noesis.Key key) { if ((delta & flag) > 0) { if ((modifiers & flag) > 0) { _uiView.KeyDown(key); } else { _uiView.KeyUp(key); } } } private bool HitTest(float x, float y) { return VisualTreeHelper.HitTest(_uiView.Content, new Point(x, y)).VisualHit != null; } #if !UNITY_EDITOR && UNITY_STANDALONE_OSX private static int lastFrame; private static Noesis.Key lastKeyDown; #endif private void ProcessEvent(UnityEngine.Event ev, bool enableKeyboard, bool enableMouse, bool emulateTouch) { // Process keyboard modifiers if (enableKeyboard) { EventModifiers delta = ev.modifiers ^ _modifiers; if (delta > 0) { _modifiers = ev.modifiers; ProcessModifierKey(ev.modifiers, delta, EventModifiers.Shift, Key.LeftShift); ProcessModifierKey(ev.modifiers, delta, EventModifiers.Control, Key.LeftCtrl); ProcessModifierKey(ev.modifiers, delta, EventModifiers.Command, Key.LeftCtrl); ProcessModifierKey(ev.modifiers, delta, EventModifiers.Alt, Key.LeftAlt); } } switch (ev.type) { case UnityEngine.EventType.MouseDown: { if (enableMouse) { UnityEngine.Vector2 mouse = ProjectPointer(ev.mousePosition.x, UnityEngine.Screen.height - ev.mousePosition.y); if (HitTest(mouse.x, mouse.y)) { ev.Use(); } if (emulateTouch) { _uiView.TouchDown((int)mouse.x, (int)mouse.y, 0); _touchEmulated = true; } else { // Ignore events generated by Unity to simulate a mouse down when a touch event occurs bool mouseEmulated = Input.simulateMouseWithTouches && Input.touchCount > 0; if (!mouseEmulated) { if (ev.clickCount == 1) { _uiView.MouseButtonDown((int)mouse.x, (int)mouse.y, (Noesis.MouseButton)ev.button); } else { _uiView.MouseDoubleClick((int)mouse.x, (int)mouse.y, (Noesis.MouseButton)ev.button); } } } } break; } case UnityEngine.EventType.MouseUp: { if (enableMouse) { UnityEngine.Vector2 mouse = ProjectPointer(ev.mousePosition.x, UnityEngine.Screen.height - ev.mousePosition.y); if (HitTest(mouse.x, mouse.y)) { ev.Use(); } if (emulateTouch && _touchEmulated) { _uiView.TouchUp((int)mouse.x, (int)mouse.y, 0); _touchEmulated = false; } else { // Ignore events generated by Unity to simulate a mouse up when a touch event occurs bool mouseEmulated = Input.simulateMouseWithTouches && Input.touchCount > 0; if (!mouseEmulated) { _uiView.MouseButtonUp((int)mouse.x, (int)mouse.y, (Noesis.MouseButton)ev.button); } } } break; } case UnityEngine.EventType.ScrollWheel: { if (enableMouse) { UnityEngine.Vector2 mouse = ProjectPointer(ev.mousePosition.x, UnityEngine.Screen.height - ev.mousePosition.y); if (ev.delta.y != 0.0f) { _uiView.MouseWheel((int)mouse.x, (int)mouse.y, -(int)(ev.delta.y * 40.0f)); } if (ev.delta.x != 0.0f) { _uiView.MouseHWheel((int)mouse.x, (int)mouse.y, (int)(ev.delta.x * 40.0f)); } } break; } case UnityEngine.EventType.KeyDown: { if (enableKeyboard) { // Don't process key when IME composition is being used if (ev.keyCode != KeyCode.None && NoesisUnity.IME.compositionString == "") { Noesis.Key noesisKeyCode = NoesisKeyCodes.Convert(ev.keyCode); if (noesisKeyCode != Noesis.Key.None) { #if !UNITY_EDITOR && UNITY_STANDALONE_OSX // In OSX Standalone, CMD + key always sends two KeyDown events for the key. // This seems to be a bug in Unity. if (!ev.command || lastFrame != Time.frameCount || lastKeyDown != noesisKeyCode) { lastFrame = Time.frameCount; lastKeyDown = noesisKeyCode; #endif _uiView.KeyDown(noesisKeyCode); #if !UNITY_EDITOR && UNITY_STANDALONE_OSX } #endif } } if (ev.character != 0) { // Filter out character events when CTRL is down bool isControl = (_modifiers & EventModifiers.Control) != 0 || (_modifiers & EventModifiers.Command) != 0; bool isAlt = (_modifiers & EventModifiers.Alt) != 0; bool filter = isControl && !isAlt; if (!filter) { #if !UNITY_EDITOR && UNITY_STANDALONE_LINUX // It seems that linux is sending KeySyms instead of Unicode points // https://github.com/substack/node-keysym/blob/master/data/keysyms.txt ev.character = NoesisKeyCodes.KeySymToUnicode(ev.character); #endif _uiView.Char((uint)ev.character); } } } break; } case UnityEngine.EventType.KeyUp: { // Don't process key when IME composition is being used if (enableKeyboard) { if (ev.keyCode != KeyCode.None && NoesisUnity.IME.compositionString == "") { Noesis.Key noesisKeyCode = NoesisKeyCodes.Convert(ev.keyCode); if (noesisKeyCode != Noesis.Key.None) { _uiView.KeyUp(noesisKeyCode); } } } break; } } } void OnGUI() { if (_uiView != null) { UnityEngine.GUI.depth = -(int)_myCamera.depth; ProcessEvent(UnityEngine.Event.current, _enableKeyboard, _enableMouse, _emulateTouch); } } void OnApplicationFocus(bool focused) { if (_uiView != null) { if (NoesisUnity.TouchKeyboard.keyboard == null) { if (focused) { _uiView.Activate(); } else { _uiView.Deactivate(); } } } } #endregion void SetRenderSettings() { NoesisSettings settings = NoesisSettings.Get(); bool linearRendering = false; switch (settings.linearRendering) { case NoesisSettings.LinearRendering._SamesAsUnity: { linearRendering = QualitySettings.activeColorSpace == ColorSpace.Linear; break; } case NoesisSettings.LinearRendering._Enabled: { linearRendering = true; break; } case NoesisSettings.LinearRendering._Disabled: { linearRendering = false; break; } } int sampleCount = 1; switch (settings.offscreenSampleCount) { case NoesisSettings.OffscreenSampleCount._SameAsUnity: { sampleCount = QualitySettings.antiAliasing; break; } case NoesisSettings.OffscreenSampleCount._1x: { sampleCount = 1; break; } case NoesisSettings.OffscreenSampleCount._2x: { sampleCount = 2; break; } case NoesisSettings.OffscreenSampleCount._4x: { sampleCount = 4; break; } case NoesisSettings.OffscreenSampleCount._8x: { sampleCount = 8; break; } } uint offscreenDefaultNumSurfaces = settings.offscreenInitSurfaces; uint offscreenMaxNumSurfaces = settings.offscreenMaxSurfaces; uint glyphCacheMeshThreshold = settings.glyphMeshThreshold; int glyphCacheTextureWidth = 1024; int glyphCacheTextureHeight = 1024; switch (settings.glyphTextureSize) { case NoesisSettings.TextureSize._256x256: { glyphCacheTextureWidth = 256; glyphCacheTextureHeight = 256; break; } case NoesisSettings.TextureSize._512x512: { glyphCacheTextureWidth = 512; glyphCacheTextureHeight = 512; break; } case NoesisSettings.TextureSize._1024x1024: { glyphCacheTextureWidth = 1024; glyphCacheTextureHeight = 1024; break; } case NoesisSettings.TextureSize._2048x2048: { glyphCacheTextureWidth = 2048; glyphCacheTextureHeight = 2048; break; } case NoesisSettings.TextureSize._4096x4096: { glyphCacheTextureWidth = 4096; glyphCacheTextureHeight = 4096; break; } } int colorGlyphCacheTextureWidth = 0; int colorGlyphCacheTextureHeight = 0; switch (settings.colorGlyphTextureSize) { case NoesisSettings.ColorTextureSize._Auto: { colorGlyphCacheTextureWidth = 0; colorGlyphCacheTextureHeight = 0; break; } case NoesisSettings.ColorTextureSize._256x256: { colorGlyphCacheTextureWidth = 256; colorGlyphCacheTextureHeight = 256; break; } case NoesisSettings.ColorTextureSize._512x512: { colorGlyphCacheTextureWidth = 512; colorGlyphCacheTextureHeight = 512; break; } case NoesisSettings.ColorTextureSize._1024x1024: { colorGlyphCacheTextureWidth = 1024; colorGlyphCacheTextureHeight = 1024; break; } case NoesisSettings.ColorTextureSize._2048x2048: { colorGlyphCacheTextureWidth = 2048; colorGlyphCacheTextureHeight = 2048; break; } case NoesisSettings.ColorTextureSize._4096x4096: { colorGlyphCacheTextureWidth = 4096; colorGlyphCacheTextureHeight = 4096; break; } } Noesis_RendererSettings(linearRendering, sampleCount, offscreenDefaultNumSurfaces, offscreenMaxNumSurfaces, glyphCacheTextureWidth, glyphCacheTextureHeight, colorGlyphCacheTextureWidth, colorGlyphCacheTextureHeight, glyphCacheMeshThreshold); } private void CreateView(FrameworkElement content) { if (_uiView == null) { // Send settings for the internal device, created by the first view SetRenderSettings(); _uiView = new Noesis.View(content); _commands.Clear(); NoesisRenderer.RegisterView(_uiView, _commands); Graphics.ExecuteCommandBuffer(_commands); } } private void DestroyView() { if (_uiView != null) { _commands.Clear(); NoesisRenderer.UnregisterView(_uiView, _commands); Graphics.ExecuteCommandBuffer(_commands); _uiView = null; } } private Noesis.View _uiView; private Camera _textureCamera; private UnityEngine.Rendering.CommandBuffer _commands; private bool _needsRendering = false; #region Serialized properties public NoesisXaml _xaml; public RenderTexture _texture; public bool _isPPAAEnabled = true; public float _tessellationMaxPixelError = Noesis.TessellationMaxPixelError.MediumQuality.Error; public RenderFlags _renderFlags = 0; public bool _continuousRendering = true; public bool _enableKeyboard = true; public bool _enableMouse = true; public bool _enableTouch = true; public bool _enableGamepad = false; public bool _emulateTouch = false; public bool _useRealTimeClock = false; #endregion #region Imports [DllImport(Library.Name)] static extern void Noesis_UnityUpdate(); [DllImport(Library.Name)] static extern void Noesis_RendererSettings(bool linearSpaceRendering, int offscreenSampleCount, uint offscreenDefaultNumSurfaces, uint offscreenMaxNumSurfaces, int glyphCacheTextureWidth, int glyphCacheTextureHeight, int colorGlyphCacheTextureWidth, int colorGlyphCacheTextureHeight, uint glyphCacheMeshTreshold); #endregion #endregion }