From 7d100a93d44c74863e0943e66672d77269a7bdb1 Mon Sep 17 00:00:00 2001 From: Seungho Baek Date: Tue, 13 Aug 2024 18:40:13 +0900 Subject: [PATCH] [NUI.Scene3D] Add Capture for SceneView Signed-off-by: Seungho Baek --- .../src/internal/Interop/Interop.SceneView.cs | 9 + .../Controls/CaptureFinishedEventArgs.cs | 48 ++++++ .../src/public/Controls/SceneView.cs | 105 +++++++++++ .../Samples/Scene3DCaptureTest.cs | 163 ++++++++++++++++++ .../res/models/BoxAnimated.glb | Bin 0 -> 11944 bytes 5 files changed, 325 insertions(+) create mode 100644 src/Tizen.NUI.Scene3D/src/public/Controls/CaptureFinishedEventArgs.cs create mode 100755 test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/Scene3DCaptureTest.cs create mode 100644 test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/models/BoxAnimated.glb diff --git a/src/Tizen.NUI.Scene3D/src/internal/Interop/Interop.SceneView.cs b/src/Tizen.NUI.Scene3D/src/internal/Interop/Interop.SceneView.cs index 2923edc3fac..f6638f8c063 100755 --- a/src/Tizen.NUI.Scene3D/src/internal/Interop/Interop.SceneView.cs +++ b/src/Tizen.NUI.Scene3D/src/internal/Interop/Interop.SceneView.cs @@ -96,6 +96,15 @@ internal static partial class SceneView [global::System.Runtime.InteropServices.DllImport(Libraries.Scene3D, EntryPoint = "CSharp_Dali_SceneView_GetFramebufferMultiSamplingLevel")] public static extern uint GetFramebufferMultiSamplingLevel(global::System.Runtime.InteropServices.HandleRef sceneView); + + [global::System.Runtime.InteropServices.DllImport(Libraries.Scene3D, EntryPoint = "CSharp_Dali_SceneView_Capture")] + public static extern int Capture(global::System.Runtime.InteropServices.HandleRef sceneView, global::System.Runtime.InteropServices.HandleRef camera, global::System.Runtime.InteropServices.HandleRef size); + + [global::System.Runtime.InteropServices.DllImport(Libraries.Scene3D, EntryPoint = "CSharp_Dali_SceneView_CaptureFinishedSignal_Connect")] + public static extern void CaptureFinishedConnect(global::System.Runtime.InteropServices.HandleRef actor, global::System.Runtime.InteropServices.HandleRef handler); + + [global::System.Runtime.InteropServices.DllImport(Libraries.Scene3D, EntryPoint = "CSharp_Dali_SceneView_CaptureFinishedSignal_Disconnect")] + public static extern void CaptureFinishedDisconnect(global::System.Runtime.InteropServices.HandleRef actor, global::System.Runtime.InteropServices.HandleRef handler); } } } diff --git a/src/Tizen.NUI.Scene3D/src/public/Controls/CaptureFinishedEventArgs.cs b/src/Tizen.NUI.Scene3D/src/public/Controls/CaptureFinishedEventArgs.cs new file mode 100644 index 00000000000..588f9cefbbd --- /dev/null +++ b/src/Tizen.NUI.Scene3D/src/public/Controls/CaptureFinishedEventArgs.cs @@ -0,0 +1,48 @@ +/* + * Copyright(c) 2024 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +using System; +using System.ComponentModel; + +namespace Tizen.NUI.Scene3D +{ + /// + /// Event arguments of SceneView capture finished event. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public class CaptureFinishedEventArgs : EventArgs + { + /// + /// Integer ID of the capture request. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public int CaptureId + { + get; set; + } + + /// + /// ImageUrl of the captured result + /// If the capture is failed, it is null. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ImageUrl CapturedImageUrl + { + get; set; + } + } +} diff --git a/src/Tizen.NUI.Scene3D/src/public/Controls/SceneView.cs b/src/Tizen.NUI.Scene3D/src/public/Controls/SceneView.cs index 27bfbdf9544..1361e3971a1 100755 --- a/src/Tizen.NUI.Scene3D/src/public/Controls/SceneView.cs +++ b/src/Tizen.NUI.Scene3D/src/public/Controls/SceneView.cs @@ -68,9 +68,16 @@ namespace Tizen.NUI.Scene3D /// 10 public class SceneView : View { + private bool inCapture = false; private bool inCameraTransition = false; private Animation cameraTransition; + // CaptureFinishedEvent + private EventHandler captureFinishedEventHandler; + private CaptureFinishedEventCallbackType captureFinishedEventCallback; + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CaptureFinishedEventCallbackType(IntPtr data, int captureId, IntPtr capturedImageUrl); + internal SceneView(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn) { } @@ -94,6 +101,29 @@ public SceneView(SceneView sceneView) : this(Interop.SceneView.NewScene(SceneVie if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); } + /// + /// Dispose Explicit or Implicit + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void Dispose(DisposeTypes type) + { + if (Disposed) + { + return; + } + + if (captureFinishedEventCallback != null) + { + NUILog.Debug($"[Dispose] captureFinishedEventCallback"); + Interop.SceneView.CaptureFinishedDisconnect(GetBaseHandleCPtrHandleRef, captureFinishedEventCallback.ToHandleRef(this)); + NDalicPINVOKE.ThrowExceptionIfExistsDebug(); + captureFinishedEventCallback = null; + } + LayoutCount = 0; + + base.Dispose(type); + } + /// /// Assignment operator. /// @@ -106,6 +136,38 @@ internal SceneView Assign(SceneView sceneView) return ret; } + + /// + /// An event emitted when Capture is finished. + /// If Capture is successed, CaptureFinishedEventArgs includes finished capture ID and ImageUrl of the captured image. + /// If Capture is failed, the ImageUrl is null. + /// + // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler CaptureFinished + { + add + { + if (captureFinishedEventHandler == null) + { + captureFinishedEventCallback = OnCaptureFinished; + Interop.SceneView.CaptureFinishedConnect(SwigCPtr, captureFinishedEventCallback.ToHandleRef(this)); + NDalicPINVOKE.ThrowExceptionIfExists(); + } + captureFinishedEventHandler += value; + } + remove + { + captureFinishedEventHandler -= value; + if (captureFinishedEventHandler == null && captureFinishedEventCallback != null) + { + Interop.SceneView.CaptureFinishedDisconnect(SwigCPtr, captureFinishedEventCallback.ToHandleRef(this)); + NDalicPINVOKE.ThrowExceptionIfExists(); + captureFinishedEventCallback = null; + } + } + } + /// /// An event emitted when Camera Transition is finished. /// @@ -469,6 +531,39 @@ public void ResetResolution() if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); } + /// + /// Requests to capture this SceneView with the Camera. + /// + /// Camera to be used for capture. + /// captured size. + /// + /// The input camera should not be used for any other purpose during Capture. + /// The selected camera cannot be used for input camera. + /// The camera is required to be added in this SceneView. + /// If the SceneView is disconnected from Scene, the left capture request is canceled. + /// + /// capture id that id unique value to distinguish each request. + /// If capture is requested during previous capture, invalid index(-1) is returned. + // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public int Capture(Scene3D.Camera camera, Vector2 size) + { + if(inCapture) + { + Tizen.Log.Error("NUI", "The previous capture request is not finished yet.\n"); + return -1; // invalid index + } + if(camera == null) + { + Tizen.Log.Error("NUI", "Invalid Camera is used.\n"); + return -1; // invalid index + } + inCapture = true; + int id = Interop.SceneView.Capture(SwigCPtr, camera.SwigCPtr, Vector2.getCPtr(size)); + if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve(); + return id; + } + internal void SetUseFramebuffer(bool useFramebuffer) { Interop.SceneView.UseFramebuffer(SwigCPtr, useFramebuffer); @@ -611,6 +706,16 @@ private void CameraTransition(Camera sourceCamera, Camera destinationCamera, int positionKeyFrames.Dispose(); orientationKeyFrames.Dispose(); } + + // Callback for capture finished signal + private void OnCaptureFinished(IntPtr data, int captureId, IntPtr capturedImageUrl) + { + CaptureFinishedEventArgs e = new CaptureFinishedEventArgs(); + e.CaptureId = captureId; + e.CapturedImageUrl = new ImageUrl(capturedImageUrl, false); + captureFinishedEventHandler?.Invoke(this, e); + inCapture = false; // To prevent to request capture in the capture finished callback. + } /// /// Release swigCPtr. diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/Scene3DCaptureTest.cs b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/Scene3DCaptureTest.cs new file mode 100755 index 00000000000..a1f15860f44 --- /dev/null +++ b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/Scene3DCaptureTest.cs @@ -0,0 +1,163 @@ +using System.Numerics; +using System.Reflection.Metadata.Ecma335; +using System.Threading.Tasks; +using global::System; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Scene3D; + +namespace Tizen.NUI.Samples +{ + using log = Tizen.Log; + public class Scene3DCaptureTest : IExample + { + private Window window; + private SceneView sceneView; + private static readonly string resourcePath = Tizen.Applications.Application.Current.DirectoryInfo.Resource; + private Tizen.NUI.Scene3D.Camera[] cameras; + private string[] cameraName; + private int cameraIndex; + int captureId; + ImageView imageView; + ImageUrl imageUrl; + bool inCapture = false; + + Animation rotAnimation; + public void Activate() + { + window = NUIApplication.GetDefaultWindow(); + Size2D windowSize = window.Size; + + sceneView = new SceneView() + { + Size = new Size(windowSize.Width, windowSize.Height), + PivotPoint = PivotPoint.TopLeft, + ParentOrigin = ParentOrigin.TopLeft, + PositionUsesPivotPoint = true, + BackgroundColor = new Color(0.85f, 0.85f, 0.85f, 1.0f), + UseFramebuffer = true, + }; + window.Add(sceneView); + + Light light = new Light() + { + Color = new Vector4(0.4f, 0.4f, 0.4f, 1.0f), + Position = new Vector3(-1.0f, 0.0f, 1.1f), + PositionUsesPivotPoint = true, + }; + light.LookAt(new Vector3(0.0f, 0.0f, 0.0f)); + sceneView.Add(light); + + cameras = new Scene3D.Camera[2]; + cameraName = new string[]{"camera1", "camera2"}; + Vector3[] cameraPosition = new Vector3[]{new Vector3(1.5f, 0.0f, 1.5f), new Vector3(-1.5f, -1.5f, 1.5f)}; + Vector3 modelPosition = new Vector3(-1.5f, 0.0f, 0.0f); + + cameraIndex = 0; + for(uint i = 0; i<2; ++i) + { + cameras[i] = new Scene3D.Camera() + { + Name = cameraName[i], + Position = cameraPosition[i], + NearPlaneDistance = 1.0f, + FarPlaneDistance = 10.0f, + }; + sceneView.AddCamera(cameras[i]); + } + cameras[1].FieldOfView = new Radian(new Degree(70.0f)); + + Model model = new Model(resourcePath + "models/BoxAnimated.glb") + { + PositionUsesPivotPoint = true, + Position = modelPosition, + Size = new Size(0.5f, 0.5f, 0.5f), + }; + sceneView.Add(model); + model.Add(cameras[0]); + sceneView.SelectCamera(cameraName[0]); + model.ResourcesLoaded += (s, e) => + { + SceneCapture(1); + }; + sceneView.Add(cameras[1]); + + cameras[0].LookAt(modelPosition); + cameras[1].LookAt(modelPosition); + + rotAnimation = new Animation(3000); + KeyFrames keyFrames = new KeyFrames(); + keyFrames.Add(0.0f, new Rotation(new Radian(new Degree(0.0f)), Vector3.YAxis)); + keyFrames.Add(0.25f, new Rotation(new Radian(new Degree(90.0f)), Vector3.YAxis)); + keyFrames.Add(0.5f, new Rotation(new Radian(new Degree(180.0f)), Vector3.YAxis)); + keyFrames.Add(0.75f, new Rotation(new Radian(new Degree(270.0f)), Vector3.YAxis)); + keyFrames.Add(1.0f, new Rotation(new Radian(new Degree(360.0f)), Vector3.YAxis)); + rotAnimation.AnimateBetween(model, "Orientation", keyFrames); + rotAnimation.Looping = true; + rotAnimation.Play(); + + + sceneView.CaptureFinished += (s, e) => + { + Tizen.Log.Error("NUI", $"Finished Capture ID : {e.CaptureId}\n"); + if(e.CapturedImageUrl == null) + { + Tizen.Log.Error("NUI", $"Capture Failed\n"); + return; + } + CreateImageView(e.CapturedImageUrl); + }; + + window.KeyEvent += WindowKeyEvent; + } + + private void WindowKeyEvent(object sender, Window.KeyEventArgs e) + { + if (e.Key.State == Key.StateType.Down) + { + if (e.Key.KeyPressedName == "1") + { + SceneCapture(1); + } + else + { + return; + } + } + } + + void SceneCapture(int captureCameraIndex) + { + captureId = sceneView.Capture(cameras[captureCameraIndex], new Vector2(300, 300)); + Tizen.Log.Error("NUI", $"Requestd Capture ID : {captureId}\n"); + } + + void CreateImageView(ImageUrl capturedImageUrl) + { + if (imageView != null) + { + imageView.Dispose(); + } + if (imageUrl != null) + { + imageUrl.Dispose(); + } + imageUrl = capturedImageUrl; + + imageView = new ImageView(imageUrl.ToString()) + { + Size = new Size(300, 300), + PositionUsesPivotPoint = true, + ParentOrigin = ParentOrigin.BottomLeft, + PivotPoint = PivotPoint.BottomLeft + }; + window.Add(imageView); + } + + public void Deactivate() + { + window.KeyEvent -= WindowKeyEvent; + sceneView?.Dispose(); + } + } +} \ No newline at end of file diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/models/BoxAnimated.glb b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/models/BoxAnimated.glb new file mode 100644 index 0000000000000000000000000000000000000000..69481ec3caeb57428c806fdac08bfbe40d19ff55 GIT binary patch literal 11944 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 H0_=YRA*={7 literal 0 HcmV?d00001