Skip to content

Commit

Permalink
[NUI.Scene3D] Add Capture for SceneView
Browse files Browse the repository at this point in the history
Signed-off-by: Seungho Baek <[email protected]>
  • Loading branch information
bshsqa committed Jan 21, 2025
1 parent 08e456f commit 7d100a9
Show file tree
Hide file tree
Showing 5 changed files with 325 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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
{
/// <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;
}
}
}
105 changes: 105 additions & 0 deletions src/Tizen.NUI.Scene3D/src/public/Controls/SceneView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,16 @@ namespace Tizen.NUI.Scene3D
/// <since_tizen> 10 </since_tizen>
public class SceneView : View
{
private bool inCapture = false;
private bool inCameraTransition = false;
private Animation cameraTransition;

// CaptureFinishedEvent
private EventHandler<CaptureFinishedEventArgs> 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)
{
}
Expand All @@ -94,6 +101,29 @@ public SceneView(SceneView sceneView) : this(Interop.SceneView.NewScene(SceneVie
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}

/// <summary>
/// Dispose Explicit or Implicit
/// </summary>
[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);
}

/// <summary>
/// Assignment operator.
/// </summary>
Expand All @@ -106,6 +136,38 @@ internal SceneView Assign(SceneView sceneView)
return ret;
}


/// <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 Camera Transition is finished.
/// </summary>
Expand Down Expand Up @@ -469,6 +531,39 @@ public void ResetResolution()
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}

/// <summary>
/// Requests to capture this SceneView with the Camera.
/// </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.
/// 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.
/// </remarks>
/// <returns> capture id that id unique value to distinguish each request.
/// If capture is requested during previous capture, invalid index(-1) is returned.</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)
{
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);
Expand Down Expand Up @@ -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.
}

/// <summary>
/// Release swigCPtr.
Expand Down
163 changes: 163 additions & 0 deletions test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/Scene3DCaptureTest.cs
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Binary file not shown.

0 comments on commit 7d100a9

Please sign in to comment.