[global::System.Runtime.InteropServices.DllImport(Libraries.Scene3D, EntryPoint = "CSharp_Dali_SceneView_CameraTransitionFinishedSignal_Disconnect")]
public static extern void CameraTransitionFinishedDisconnect(global::System.Runtime.InteropServices.HandleRef actor, global::System.Runtime.InteropServices.HandleRef handler);
+
+ [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);
}
}
}
--- /dev/null
+/*
+ * 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
+{
+ /// <summary>
+ /// Event arguments of SceneView capture finished event.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class CaptureFinishedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Integer ID of the capture request.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public int CaptureId
+ {
+ get; set;
+ }
+
+ /// <summary>
+ /// ImageUrl of the captured result
+ /// If the capture is failed, it is null.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ImageUrl CapturedImageUrl
+ {
+ get; set;
+ }
+ }
+}
using Tizen.NUI;
using Tizen.NUI.Binding;
using Tizen.NUI.BaseComponents;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Diagnostics;
namespace Tizen.NUI.Scene3D
{
/// <since_tizen> 10 </since_tizen>
public class SceneView : View
{
+ private Dictionary<int, TaskCompletionSource<ImageUrl>> asyncCaptureIds = new();
private string skyboxUrl;
+ // CameraTransitionFinishedEvent
private EventHandler cameraTransitionFinishedEventHandler;
private CameraTransitionFinishedEventCallbackType cameraTransitionFinishedEventCallback;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void CameraTransitionFinishedEventCallbackType(IntPtr data);
+ // CaptureFinishedEvent
+ private EventHandler<CaptureFinishedEventArgs> captureFinishedEventHandler;
+ private EventHandler<CaptureFinishedEventArgs> asyncCaptureFinishedEventHandler;
+ private CaptureFinishedEventCallbackType captureFinishedEventCallback;
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate void CaptureFinishedEventCallbackType(IntPtr data, int captureId, IntPtr capturedImageUrl);
+
internal SceneView(global::System.IntPtr cPtr, bool cMemoryOwn) : this(cPtr, cMemoryOwn, cMemoryOwn)
{
}
cameraTransitionFinishedEventCallback = null;
}
+ if (captureFinishedEventCallback != null)
+ {
+ NUILog.Debug($"[Dispose] captureFinishedEventCallback");
+
+ Interop.SceneView.CaptureFinishedDisconnect(GetBaseHandleCPtrHandleRef, captureFinishedEventCallback.ToHandleRef(this));
+ NDalicPINVOKE.ThrowExceptionIfExistsDebug();
+ captureFinishedEventCallback = null;
+ }
+
LayoutCount = 0;
base.Dispose(type);
}
}
+ /// <summary>
+ /// 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.
+ /// </summary>
+ // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<CaptureFinishedEventArgs> 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;
+ }
+ }
+ }
+
+ /// <summary>
+ /// An event emitted when CaptureAsync is finished.
+ /// </summary>
+ private event EventHandler<CaptureFinishedEventArgs> AsyncCaptureFinished
+ {
+ add
+ {
+ if (asyncCaptureFinishedEventHandler == null)
+ {
+ CaptureFinished += dummy;
+ }
+ asyncCaptureFinishedEventHandler += value;
+ }
+
+ remove
+ {
+ asyncCaptureFinishedEventHandler -= value;
+ if (asyncCaptureFinishedEventHandler == null)
+ {
+ CaptureFinished += dummy;
+ }
+ }
+ }
+
+ void dummy(object sender, CaptureFinishedEventArgs e) {}
+
/// <summary>
/// Set/Get the ImageBasedLight ScaleFactor.
/// Scale factor controls light source intensity in [0.0f, 1.0f]
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
+ /// <summary>
+ /// Requests to capture this SceneView with the Camera.
+ /// When the capture is finished, CaptureFinished Event is emited.
+ /// <see cref="CaptureFinishedEventArgs"/> includes <see cref="CaptureFinishedEventArgs.CaptureId"/> and <see cref="CaptureFinishedEventArgs.CapturedImageUrl"/>.
+ /// If the capture is successful, the <see cref="CaptureFinishedEventArgs.CapturedImageUrl"/> contains url of captured image.
+ /// If the capture fails, the <see cref="CaptureFinishedEventArgs.CapturedImageUrl"/> is null.
+ /// </summary>
+ /// <param name="camera">Camera to be used for capture.</param>
+ /// <param name="size">captured size.</param>
+ /// <remarks>
+ /// The input camera should not be used for any other purpose during Capture.
+ /// (Simultaneous usage elsewhere may result in incorrect rendering.)
+ /// The camera is required to be added in this SceneView. (Not need to be a selected camera)
+ /// If the SceneView is disconnected from Scene, the left capture requests are canceled with fail.
+ /// </remarks>
+ /// <returns> capture id that id unique value to distinguish each requiest.</returns>
+ // 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)
+ {
+ int id = Interop.SceneView.Capture(SwigCPtr, camera.SwigCPtr, Vector2.getCPtr(size));
+ if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ return id;
+ }
+
+ /// <summary>
+ /// Requests to capture this SceneView with the Camera asynchronously.
+ /// </summary>
+ /// <param name="camera">Camera to be used for capture.</param>
+ /// <param name="size">captured size.</param>
+ /// <remarks>
+ /// The input camera should not be used for any other purpose during Capture.
+ /// (Simultaneous usage elsewhere may result in incorrect rendering.)
+ /// The camera is required to be added in this SceneView. (Not need to be a selected camera)
+ /// If the SceneView is disconnected from Scene, the left capture requests are canceled with fail.
+ /// </remarks>
+ /// <returns>
+ /// A task that represents the asynchronous operation. The task result contains the URL of the captured image.
+ /// If the capture is successful, the task result is the ImageURL.
+ /// If the capture fails, the task will complete with an <see cref="InvalidOperationException"/>
+ /// </returns>
+ // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Task<ImageUrl> CaptureAsync(Scene3D.Camera camera, Vector2 size)
+ {
+ void Handler(object _, CaptureFinishedEventArgs e)
+ {
+ if (asyncCaptureIds.TryGetValue(e.CaptureId, out var tcs))
+ {
+ try
+ {
+ if (e.CapturedImageUrl != null)
+ {
+ tcs.SetResult(e.CapturedImageUrl);
+ }
+ else
+ {
+ tcs.SetException(new InvalidOperationException("Fail to Capture"));
+ }
+ }
+ finally
+ {
+ AsyncCaptureFinished -= Handler;
+ asyncCaptureIds.Remove(e.CaptureId);
+ }
+ }
+ };
+
+ AsyncCaptureFinished += Handler;
+ var captureId = Interop.SceneView.Capture(SwigCPtr, camera.SwigCPtr, Vector2.getCPtr(size));
+ TaskCompletionSource<ImageUrl> ret = new TaskCompletionSource<ImageUrl>();
+ asyncCaptureIds.Add(captureId, ret);
+
+ return ret.Task;
+ }
+
internal void SetUseFramebuffer(bool useFramebuffer)
{
Interop.SceneView.UseFramebuffer(SwigCPtr, useFramebuffer);
{
cameraTransitionFinishedEventHandler?.Invoke(this, EventArgs.Empty);
}
+
+ // Callback for capture finished signal
+ private void OnCaptureFinished(IntPtr data, int captureId, IntPtr capturedImageUrl)
+ {
+ CaptureFinishedEventArgs e = new CaptureFinishedEventArgs();
+ ImageUrl imageUrl = new ImageUrl(NUI.Interop.ImageUrl.NewImageUrl(new ImageUrl(capturedImageUrl, false).SwigCPtr), true);
+ NDalicPINVOKE.ThrowExceptionIfExists();
+
+ e.CaptureId = captureId;
+ e.CapturedImageUrl = imageUrl.HasBody() ? imageUrl : null;
+
+ if (asyncCaptureIds.ContainsKey(e.CaptureId))
+ {
+ asyncCaptureFinishedEventHandler?.Invoke(this, e);
+ }
+ else
+ {
+ captureFinishedEventHandler?.Invoke(this, e);
+ }
+ }
}
}
{
internal static partial class ImageUrl
{
+
+ [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_new_ImageUrl_Copy")]
+ public static extern global::System.IntPtr NewImageUrl(global::System.Runtime.InteropServices.HandleRef csImageUrl);
+
[DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_delete_ImageUrl")]
public static extern void Delete(HandleRef jarg1);
/// <since_tizen> 3 </since_tizen>
PARENT
}
-
}
--- /dev/null
+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;
+
+ 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);
+
+ 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(cameraIndex);
+ }
+ else if (e.Key.KeyPressedName == "2")
+ {
+ SceneCaptureAsync(cameraIndex);
+ }
+ else
+ {
+ return;
+ }
+
+ cameraIndex = 1 - cameraIndex;
+ sceneView.SelectCamera(cameraName[cameraIndex]);
+ }
+ }
+
+ void SceneCapture(int captureCameraIndex)
+ {
+ captureId = sceneView.Capture(cameras[captureCameraIndex], new Vector2(300, 300));
+ Tizen.Log.Error("NUI", $"Requestd Capture ID : {captureId}\n");
+ }
+
+ async void SceneCaptureAsync(int captureCameraIndex)
+ {
+ try
+ {
+ var url = await sceneView.CaptureAsync(cameras[captureCameraIndex], new Vector2(300, 300));
+ Tizen.Log.Error("NUI", $"Finished Capture url : {url.ToString()}\n");
+ CreateImageView(url);
+ }
+ catch (InvalidOperationException e)
+ {
+ Tizen.Log.Error("NUI", "Oops Capture is failed.\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?.DisposeRecursively();
+ }
+ }
+}