/*
* Copyright(c) 2022 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.Runtime.InteropServices;
using System.ComponentModel;
using Tizen.NUI;
using Tizen.NUI.Binding;
using Tizen.NUI.BaseComponents;
namespace Tizen.NUI.Scene3D
{
///
/// SceneView is a Class to show multiple 3D objects in a single 2D screen.
/// Each SceneView has its own 3D space, and 3D objects added to SceneView are positioned in the space.
/// SceneView uses left-handed coordinate system same as NUI. X as right, Y as down, and Z as forward.
///
/// SceneView has internal root container to control inner rendering process like depth test.
/// When a View is added to the SceneView with method, it is actually added on the root container.
/// Therefore, the added Views exist in the sub tree of SceneView, but are not direct children.
/// The sub tree of Views will be rendered with the SceneView's own Camera.
///
/// SceneView has one built-in camera by default.
/// The default Camera is not removed by using method.
/// method with index "0" returns the default camera,
/// and the minimum value returned by method is 1.
///
/// SceneView also provides multiple Camera and one of them can be used to render multiple objects.
/// , , ,
/// and are methods to manage Cameras of the SceneView.
/// User can place multiple cameras in a scene to display the entire scene or to display individual objects.
/// User can use the method to select the currently required camera.
///
/// When the SceneView's size changes, some camera properties that depend on its size may also change.
/// The changing properties are as follows: AspectRatio, LeftPlaneDistance, RightPlaneDistance, TopPlaneDistance, and BottomPlaneDistance.
/// The Camera's FieldOfView is vertical fov. The horizontal fov is updated internally according to the SceneView size.
///
/// The method sets the same IBL to all Model objects added to the SceneView.
/// For the IBL, two cube map textures(diffuse and specular) are required.
/// SceneView supports 4 types layout for Cube Map: Vertical/Horizontal Cross layouts, and Vertical/Horizontal Array layouts.
/// And also, ktx format with cube map is supported.
/// If a model already has an IBL, it is batch overridden with the IBL of the SceneView.
/// If the SceneView has IBL, the IBL of newly added models is also overridden.
///
/// The IBL textures start to be loaded asynchronously when method is called.
/// ResourcesLoaded signal notifies that the loading of the IBL resources have been completed.
///
/// If FBO is used, the rendering result of SceneView is drawn on the FBO and it is mapped on the plane of the SceneView.
/// It could decreases performance slightly, but it is useful to show SceneView according to the rendering order with other Views.
///
/// And since SceneView is a View, it can be placed together with other 2D UI components in the NUI window.
///
/// 10
public class SceneView : View
{
private bool inCameraTransition = false;
private Animation cameraTransition;
internal SceneView(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
{
}
///
/// Create an initialized SceneView.
///
/// 10
public SceneView() : this(Interop.SceneView.SceneNew(), true)
{
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
///
/// Copy constructor.
///
/// The source object.
/// 10
public SceneView(SceneView sceneView) : this(Interop.SceneView.NewScene(SceneView.getCPtr(sceneView)), true)
{
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
///
/// Assignment operator.
///
/// Handle to an object.
/// Reference to this.
internal SceneView Assign(SceneView sceneView)
{
SceneView ret = new SceneView(Interop.SceneView.SceneAssign(SwigCPtr, SceneView.getCPtr(sceneView)), false);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return ret;
}
///
/// Set/Get the ImageBasedLight ScaleFactor.
/// Scale factor controls light source intensity in [0.0f, 1.0f]
///
/// 10
public float ImageBasedLightScaleFactor
{
set
{
SetImageBasedLightScaleFactor(value);
}
get
{
return GetImageBasedLightScaleFactor();
}
}
///
/// Set/Get the UseFramebuffer.
/// If this property is true, rendering result of SceneView is drawn on FBO and it is mapping on this SceneView plane.
/// If this property is false, each item in SceneView is rendered on window directly.
/// Default is false.
///
///
/// If UseFramebuffer is true, it could decrease performance but entire rendering order is satisfied.
/// If UseFramebuffer is false, the performance becomes better but SceneView is rendered on the top of the other 2D components regardless tree order.
///
// This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
[EditorBrowsable(EditorBrowsableState.Never)]
public bool UseFramebuffer
{
set
{
SetUseFramebuffer(value);
}
get
{
return IsUsingFramebuffer();
}
}
///
/// Adds a Camera to the SceneView at the end of the camera list of SceneView.
/// The Camera can be used as a selected camera to render the scene by using or
///
/// Camera added on this SceneView.
///
/// Some properties of the Camera will be change depending on the Size of this SceneView.
/// Those properties are as follows:
/// AspectRatio, LeftPlaneDistance, RightPlaneDistance, TopPlaneDistance, and BottomPlaneDistance.
///
/// The FieldOfView of Camera is for vertical fov.
/// When the size of the SceneView is changed, the vertical fov is maintained
/// and the horizontal fov is automatically calculated according to the SceneView's AspectRatio.
///
/// 10
public void AddCamera(Camera camera)
{
if(camera != null)
{
Interop.SceneView.AddCamera(SwigCPtr, camera.SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
}
///
/// Removes a Camera from this SceneView.
/// If removed Camera is selected Camera,
/// first camera in the list becomes the selected Camera.
///
/// camera Camera to be removed from this Camera.
///
/// When Camera.Dispose() is called, the NUI object is disposed, but camera information is maintained internally.
/// Therefore, even if Camera.Dispose() is called, RemoveCamera() or RemoveCamera() methods can be used.
/// If RemoveCamera() is called too, all information is deleted together.
///
/// 10
public void RemoveCamera(Camera camera)
{
if(camera != null)
{
Interop.SceneView.RemoveCamera(SwigCPtr, camera.SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
}
///
/// Retrieves the number of cameras.
///
/// The number of Cameras.
/// 10
public uint GetCameraCount()
{
uint count = Interop.SceneView.GetCameraCount(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return count;
}
///
/// Retrieves a Camera of the index.
///
/// Index of Camera to be retrieved.
/// Camera of the index.
/// 10
public Camera GetCamera(uint index)
{
global::System.IntPtr cPtr = Interop.SceneView.GetCamera(SwigCPtr, index);
Camera camera = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Camera;
if(camera == null)
{
// Register new camera into Registry.
camera = new Camera(cPtr, true);
}
else
{
// We found matched NUI camera. Reduce cPtr reference count.
HandleRef handle = new HandleRef(this, cPtr);
Interop.Camera.DeleteCameraProperty(handle);
handle = new HandleRef(null, IntPtr.Zero);
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return camera;
}
///
/// Retrieves a Camera of the input name.
///
/// string keyword of Camera to be retrieved.
/// Camera that has the name as a View.Name property
/// 10
public Camera GetCamera(string name)
{
global::System.IntPtr cPtr = Interop.SceneView.GetCamera(SwigCPtr, name);
Camera camera = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Camera;
if(camera == null)
{
// Register new camera into Registry.
camera = new Camera(cPtr, true);
}
else
{
// We found matched NUI camera. Reduce cPtr reference count.
HandleRef handle = new HandleRef(this, cPtr);
Interop.Camera.DeleteCameraProperty(handle);
handle = new HandleRef(null, IntPtr.Zero);
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return camera;
}
///
/// Makes SceneView use a Camera of index as a selected camera.
///
/// Index of Camera to be used as a selected camera.
/// 10
public void SelectCamera(uint index)
{
if(inCameraTransition)
{
return;
}
Interop.SceneView.SelectCamera(SwigCPtr, index);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
///
/// Makes SceneView use a Camera of a name as a selected camera.
///
/// string keyword of Camera to be used as a selected camera.
/// 10
public void SelectCamera(string name)
{
if(inCameraTransition)
{
return;
}
Interop.SceneView.SelectCamera(SwigCPtr, name);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
///
/// Starts camera transition from currently selected camera to a camera of index.
/// Camera Position, Orientation and FieldOfView are smoothly animated.
///
///
/// The selected camera is switched when the transition is started.
/// During camera transition, Selected Camera cannot be changed by using SelectCamera() or CameraTransition() method.
///
/// Index of destination Camera of Camera transition.
/// The duration in milliseconds.
/// The alpha function to apply.
/// 10
public void CameraTransition(uint index, int durationMilliSeconds, AlphaFunction alphaFunction = null)
{
if(inCameraTransition)
{
return;
}
Camera source = GetSelectedCamera();
SelectCamera(index);
Camera destination = GetSelectedCamera();
CameraTransition(source, destination, durationMilliSeconds, alphaFunction);
}
///
/// Starts camera transition from currently selected camera to a camera of input name.
/// Camera Position, Orientation and FieldOfView are smoothly animated.
///
///
/// The selected camera is switched when the transition is started.
/// During camera transition, Selected Camera cannot be changed by using SelectCamera() or CameraTransition() method.
///
/// string keyword of destination Camera of Camera transition.
/// The duration in milliseconds.
/// The alpha function to apply.
/// 10
public void CameraTransition(string name, int durationMilliSeconds, AlphaFunction alphaFunction = null)
{
if(inCameraTransition)
{
return;
}
Camera source = GetSelectedCamera();
SelectCamera(name);
Camera destination = GetSelectedCamera();
CameraTransition(source, destination, durationMilliSeconds, alphaFunction);
}
///
/// Retrieves selected Camera.
///
/// Camera currently used in SceneView as a selected Camera.
/// 10
public Camera GetSelectedCamera()
{
global::System.IntPtr cPtr = Interop.SceneView.GetSelectedCamera(SwigCPtr);
Camera camera = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Camera;
if(camera == null)
{
// Register new camera into Registry.
camera = new Camera(cPtr, true);
}
else
{
// We found matched NUI camera. Reduce cPtr reference count.
HandleRef handle = new HandleRef(this, cPtr);
Interop.Camera.DeleteCameraProperty(handle);
handle = new HandleRef(null, IntPtr.Zero);
}
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return camera;
}
///
/// Changes Image Based Light as the input textures.
///
/// The path of Cube map image that can be used as a diffuse IBL source.
/// The path of Cube map image that can be used as a specular IBL source.
/// Scale factor that controls light source intensity in [0.0f, 1.0f]. Default value is 1.0f.
///
/// http://tizen.org/privilege/mediastorage for local files in media storage.
/// http://tizen.org/privilege/externalstorage for local files in external storage.
///
/// 10
public void SetImageBasedLightSource(string diffuseUrl, string specularUrl, float scaleFactor = 1.0f)
{
Interop.SceneView.SetImageBasedLightSource(SwigCPtr, diffuseUrl, specularUrl, scaleFactor);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
internal void SetUseFramebuffer(bool useFramebuffer)
{
Interop.SceneView.UseFramebuffer(SwigCPtr, useFramebuffer);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
internal bool IsUsingFramebuffer()
{
bool result = Interop.SceneView.IsUsingFramebuffer(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return result;
}
///
/// Set the ImageBasedLight ScaleFactor.
///
/// Scale factor that controls light source intensity in [0.0f, 1.0f].
private void SetImageBasedLightScaleFactor(float scaleFactor)
{
Interop.SceneView.SetImageBasedLightScaleFactor(SwigCPtr, scaleFactor);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
}
///
/// Get the ImageBasedLight ScaleFactor.
///
/// ImageBasedLightScaleFactor that controls light source intensity.
private float GetImageBasedLightScaleFactor()
{
float scaleFactor = Interop.SceneView.GetImageBasedLightScaleFactor(SwigCPtr);
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return scaleFactor;
}
private void CameraTransition(Camera sourceCamera, Camera destinationCamera, int durationMilliSeconds, AlphaFunction alphaFunction)
{
inCameraTransition = true;
Position sourcePosition = sourceCamera.Position;
Rotation sourceOrientation = sourceCamera.Orientation;
Radian sourceFieldOfView = sourceCamera.FieldOfView;
Position destinationPosition = destinationCamera.Position;
Rotation destinationOrientation = destinationCamera.Orientation;
Radian destinationFieldOfView = destinationCamera.FieldOfView;
// If ProjectionDirection is not equal, match the value.
if (sourceCamera.ProjectionDirection != destinationCamera.ProjectionDirection)
{
float aspect = destinationCamera.AspectRatio;
if (destinationCamera.ProjectionDirection == Camera.ProjectionDirectionType.Vertical)
{
sourceFieldOfView = Camera.ConvertFovFromHorizontalToVertical(aspect, sourceFieldOfView);
}
else
{
sourceFieldOfView = Camera.ConvertFovFromVerticalToHorizontal(aspect, sourceFieldOfView);
}
}
cameraTransition = new Animation(durationMilliSeconds);
KeyFrames positionKeyFrames = new KeyFrames();
positionKeyFrames.Add(0.0f, sourcePosition);
positionKeyFrames.Add(1.0f, destinationPosition);
KeyFrames orientationKeyFrames = new KeyFrames();
orientationKeyFrames.Add(0.0f, sourceOrientation);
orientationKeyFrames.Add(1.0f, destinationOrientation);
KeyFrames fieldOfViewKeyFrames = new KeyFrames();
fieldOfViewKeyFrames.Add(0.0f, sourceFieldOfView.ConvertToFloat());
fieldOfViewKeyFrames.Add(1.0f, destinationFieldOfView.ConvertToFloat());
cameraTransition.AnimateBetween(destinationCamera, "Position", positionKeyFrames, Animation.Interpolation.Linear, alphaFunction);
cameraTransition.AnimateBetween(destinationCamera, "Orientation", orientationKeyFrames, Animation.Interpolation.Linear, alphaFunction);
cameraTransition.AnimateBetween(destinationCamera, "FieldOfView", fieldOfViewKeyFrames, Animation.Interpolation.Linear, alphaFunction);
cameraTransition.Finished += (s, e) =>
{
inCameraTransition = false;
};
cameraTransition.Play();
}
///
/// Release swigCPtr.
///
// This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
[EditorBrowsable(EditorBrowsableState.Never)]
protected override void ReleaseSwigCPtr(global::System.Runtime.InteropServices.HandleRef swigCPtr)
{
Interop.SceneView.DeleteScene(swigCPtr);
}
}
}