[NUI] Use smaller NearPlaneDistance and bigger FarPlaneDistance during CameraTransiti...
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Scene3D / src / public / Controls / SceneView.cs
1 /*
2  * Copyright(c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.Runtime.InteropServices;
20 using System.ComponentModel;
21 using Tizen.NUI;
22 using Tizen.NUI.Binding;
23 using Tizen.NUI.BaseComponents;
24
25 namespace Tizen.NUI.Scene3D
26 {
27     /// <summary>
28     /// SceneView is a Class to show multiple 3D objects in a single 2D screen.
29     /// Each SceneView has its own 3D space, and 3D objects added to SceneView are positioned in the space.
30     /// SceneView uses left-handed coordinate system same as NUI. X as right, Y as down, and Z as forward.
31     ///
32     /// SceneView has internal root container to control inner rendering process like depth test.
33     /// When a View is added to the SceneView with <see cref="View.Add(View)"/> method, it is actually added on the root container.
34     /// Therefore, the added Views exist in the sub tree of SceneView, but are not direct children.
35     /// The sub tree of Views will be rendered with the SceneView's own Camera.
36     ///
37     /// SceneView has one built-in camera by default.
38     /// The default Camera is not removed by using <see cref="RemoveCamera(Camera)"/> method.
39     /// <see cref="GetCamera(uint)"/> method with index "0" returns the default camera,
40     /// and the minimum value returned by <see cref="GetCameraCount()"/> method is 1.
41     ///
42     /// SceneView also provides multiple Camera and one of them can be used to render multiple objects.
43     /// <see cref="AddCamera(Camera)"/>, <see cref="RemoveCamera(Camera)"/>, <see cref="GetCamera(uint)"/>,
44     /// and <see cref="SelectCamera(uint)"/> are methods to manage Cameras of the SceneView.
45     /// User can place multiple cameras in a scene to display the entire scene or to display individual objects.
46     /// User can use the <see cref="SelectCamera(uint)"/> method to select the currently required camera.
47     ///
48     /// When the SceneView's size changes, some camera properties that depend on its size may also change.
49     /// The changing properties are as follows: AspectRatio, LeftPlaneDistance, RightPlaneDistance, TopPlaneDistance, and BottomPlaneDistance.
50     /// The Camera's FieldOfView is vertical fov. The horizontal fov is updated internally according to the SceneView size.
51     ///
52     /// The <see cref="SetImageBasedLightSource(string, string, float)"/> method sets the same IBL to all Model objects added to the SceneView.
53     /// For the IBL, two cube map textures(diffuse and specular) are required.
54     /// SceneView supports 4 types layout for Cube Map: Vertical/Horizontal Cross layouts, and Vertical/Horizontal Array layouts.
55     /// And also, ktx format with cube map is supported.
56     /// If a model already has an IBL, it is batch overridden with the IBL of the SceneView.
57     /// If the SceneView has IBL, the IBL of newly added models is also overridden.
58     ///
59     /// The IBL textures start to be loaded asynchronously when <see cref="SetImageBasedLightSource(string, string, float)"/> method is called.
60     /// ResourcesLoaded signal notifies that the loading of the IBL resources have been completed.
61     ///
62     /// If FBO is used, the rendering result of SceneView is drawn on the FBO and it is mapped on the plane of the SceneView.
63     /// It could decreases performance slightly, but it is useful to show SceneView according to the rendering order with other Views.
64     ///
65     /// And since SceneView is a View, it can be placed together with other 2D UI components in the NUI window.
66     /// </summary>
67     /// <since_tizen> 10 </since_tizen>
68     public class SceneView : View
69     {
70         private bool inCameraTransition = false;
71         private Animation cameraTransition;
72
73         internal SceneView(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
74         {
75         }
76
77         /// <summary>
78         /// Create an initialized SceneView.
79         /// </summary>
80         /// <since_tizen> 10 </since_tizen>
81         public SceneView() : this(Interop.SceneView.SceneNew(), true)
82         {
83             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
84         }
85
86         /// <summary>
87         /// Copy constructor.
88         /// </summary>
89         /// <param name="sceneView">The source object.</param>
90         /// <since_tizen> 10 </since_tizen>
91         public SceneView(SceneView sceneView) : this(Interop.SceneView.NewScene(SceneView.getCPtr(sceneView)), true)
92         {
93             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
94         }
95
96         /// <summary>
97         /// Assignment operator.
98         /// </summary>
99         /// <param name="sceneView">Handle to an object.</param>
100         /// <returns>Reference to this.</returns>
101         internal SceneView Assign(SceneView sceneView)
102         {
103             SceneView ret = new SceneView(Interop.SceneView.SceneAssign(SwigCPtr, SceneView.getCPtr(sceneView)), false);
104             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
105             return ret;
106         }
107
108         /// <summary>
109         /// An event emitted when Camera Transition is finished.
110         /// </summary>
111         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
112         [EditorBrowsable(EditorBrowsableState.Never)]
113         public event EventHandler CameraTransitionFinished;
114
115         /// <summary>
116         /// Set/Get the ImageBasedLight ScaleFactor.
117         /// Scale factor controls light source intensity in [0.0f, 1.0f]
118         /// </summary>
119         /// <since_tizen> 10 </since_tizen>
120         public float ImageBasedLightScaleFactor
121         {
122             set
123             {
124                 SetImageBasedLightScaleFactor(value);
125             }
126             get
127             {
128                 return GetImageBasedLightScaleFactor();
129             }
130         }
131
132         /// <summary>
133         /// Set/Get the UseFramebuffer.
134         /// If this property is true, rendering result of SceneView is drawn on FBO and it is mapping on this SceneView plane.
135         /// If this property is false, each item in SceneView is rendered on window directly.
136         /// Default is false.
137         /// </summary>
138         /// <remarks>
139         /// If UseFramebuffer is true, it could decrease performance but entire rendering order is satisfied.
140         /// If UseFramebuffer is false, the performance becomes better but SceneView is rendered on the top of the other 2D components regardless tree order.
141         /// </remarks>
142         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
143         [EditorBrowsable(EditorBrowsableState.Never)]
144         public bool UseFramebuffer
145         {
146             set
147             {
148                 SetUseFramebuffer(value);
149             }
150             get
151             {
152                 return IsUsingFramebuffer();
153             }
154         }
155
156         /// <summary>
157         /// Adds a Camera to the SceneView at the end of the camera list of SceneView.
158         /// The Camera can be used as a selected camera to render the scene by using <see cref="SelectCamera(uint)"/> or <see cref="SelectCamera(string)"/>
159         /// </summary>
160         /// <param name="camera">Camera added on this SceneView.</param>
161         /// <remarks>
162         /// Some properties of the Camera will be change depending on the Size of this SceneView.
163         /// Those properties are as follows:
164         /// AspectRatio, LeftPlaneDistance, RightPlaneDistance, TopPlaneDistance, and BottomPlaneDistance.
165         ///
166         /// The FieldOfView of Camera is for vertical fov.
167         /// When the size of the SceneView is changed, the vertical fov is maintained
168         /// and the horizontal fov is automatically calculated according to the SceneView's AspectRatio.
169         /// </remarks>
170         /// <since_tizen> 10 </since_tizen>
171         public void AddCamera(Camera camera)
172         {
173             if(camera != null)
174             {
175                 Interop.SceneView.AddCamera(SwigCPtr, camera.SwigCPtr);
176                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
177             }
178         }
179
180         /// <summary>
181         /// Removes a Camera from this SceneView.
182         /// If removed Camera is selected Camera,
183         /// first camera in the list becomes the selected Camera.
184         /// </summary>
185         /// <param name="camera"> camera Camera to be removed from this Camera.</param>
186         /// <remarks>
187         /// When Camera.Dispose() is called, the NUI object is disposed, but camera information is maintained internally.
188         /// Therefore, even if Camera.Dispose() is called, RemoveCamera() or RemoveCamera() methods can be used.
189         /// If RemoveCamera() is called too, all information is deleted together.
190         /// </remarks>
191         /// <since_tizen> 10 </since_tizen>
192         public void RemoveCamera(Camera camera)
193         {
194             if(camera != null)
195             {
196                 Interop.SceneView.RemoveCamera(SwigCPtr, camera.SwigCPtr);
197                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
198             }
199         }
200
201         /// <summary>
202         /// Retrieves the number of cameras.
203         /// </summary>
204         /// <returns>The number of Cameras.</returns>
205         /// <since_tizen> 10 </since_tizen>
206         public uint GetCameraCount()
207         {
208             uint count = Interop.SceneView.GetCameraCount(SwigCPtr);
209             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
210             return count;
211         }
212
213         /// <summary>
214         /// Retrieves a Camera of the index.
215         /// </summary>
216         /// <param name="index"> Index of Camera to be retrieved.</param>
217         /// <returns>Camera of the index.</returns>
218         /// <since_tizen> 10 </since_tizen>
219         public Camera GetCamera(uint index)
220         {
221             global::System.IntPtr cPtr = Interop.SceneView.GetCamera(SwigCPtr, index);
222             Camera camera = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Camera;
223             if(camera == null)
224             {
225                 // Register new camera into Registry.
226                 camera = new Camera(cPtr, true);
227             }
228             else
229             {
230                 // We found matched NUI camera. Reduce cPtr reference count.
231                 HandleRef handle = new HandleRef(this, cPtr);
232                 Interop.Camera.DeleteCameraProperty(handle);
233                 handle = new HandleRef(null, IntPtr.Zero);
234             }
235             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
236             return camera;
237         }
238
239         /// <summary>
240         /// Retrieves a Camera of the input name.
241         /// </summary>
242         /// <param name="name"> string keyword of Camera to be retrieved.</param>
243         /// <returns>Camera that has the name as a View.Name property</returns>
244         /// <since_tizen> 10 </since_tizen>
245         public Camera GetCamera(string name)
246         {
247             global::System.IntPtr cPtr = Interop.SceneView.GetCamera(SwigCPtr, name);
248             Camera camera = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Camera;
249             if(camera == null)
250             {
251                 // Register new camera into Registry.
252                 camera = new Camera(cPtr, true);
253             }
254             else
255             {
256                 // We found matched NUI camera. Reduce cPtr reference count.
257                 HandleRef handle = new HandleRef(this, cPtr);
258                 Interop.Camera.DeleteCameraProperty(handle);
259                 handle = new HandleRef(null, IntPtr.Zero);
260             }
261             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
262             return camera;
263         }
264
265         /// <summary>
266         /// Makes SceneView use a Camera of index as a selected camera.
267         /// </summary>
268         /// <param name="index"> Index of Camera to be used as a selected camera.</param>
269         /// <since_tizen> 10 </since_tizen>
270         public void SelectCamera(uint index)
271         {
272             if(inCameraTransition)
273             {
274                 return;
275             }
276             Interop.SceneView.SelectCamera(SwigCPtr, index);
277             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
278         }
279
280         /// <summary>
281         /// Makes SceneView use a Camera of a name as a selected camera.
282         /// </summary>
283         /// <param name="name"> string keyword of Camera to be used as a selected camera.</param>
284         /// <since_tizen> 10 </since_tizen>
285         public void SelectCamera(string name)
286         {
287             if(inCameraTransition)
288             {
289                 return;
290             }
291             Interop.SceneView.SelectCamera(SwigCPtr, name);
292             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
293         }
294
295         /// <summary>
296         /// Starts camera transition from currently selected camera to a camera of index.
297         /// Camera Position, Orientation and FieldOfView are smoothly animated.
298         /// </summary>
299         /// <remarks>
300         /// The selected camera is switched to the Camera of the index when the transition is started.
301         /// During camera transition, Selected Camera should not be changed by using SelectCamera() or CameraTransition() method.
302         /// During camera transition, Camera properties of Selected Camera should not be changed.
303         /// </remarks>
304         /// <param name="index"> Index of destination Camera of Camera transition.</param>
305         /// <param name="durationMilliSeconds">The duration in milliseconds.</param>
306         /// <param name="alphaFunction">The alpha function to apply.</param>
307         /// <since_tizen> 10 </since_tizen>
308         public void CameraTransition(uint index, int durationMilliSeconds, AlphaFunction alphaFunction = null)
309         {
310             if(inCameraTransition || GetSelectedCamera() == GetCamera(index))
311             {
312                 return;
313             }
314             Camera source = GetSelectedCamera();
315             SelectCamera(index);
316             Camera destination = GetSelectedCamera();
317             CameraTransition(source, destination, durationMilliSeconds, alphaFunction);
318         }
319
320         /// <summary>
321         /// Starts camera transition from currently selected camera to a camera of input name.
322         /// Camera Position, Orientation and FieldOfView are smoothly animated.
323         /// </summary>
324         /// <remarks>
325         /// The selected camera is switched to the Camera of the input name when the transition is started.
326         /// During camera transition, Selected Camera should not be changed by using SelectCamera() or CameraTransition() method.
327         /// During camera transition, Camera properties of Selected Camera should not be changed.
328         /// </remarks>
329         /// <param name="name"> string keyword of destination Camera of Camera transition.</param>
330         /// <param name="durationMilliSeconds">The duration in milliseconds.</param>
331         /// <param name="alphaFunction">The alpha function to apply.</param>
332         /// <since_tizen> 10 </since_tizen>
333         public void CameraTransition(string name, int durationMilliSeconds, AlphaFunction alphaFunction = null)
334         {
335             if(inCameraTransition || GetSelectedCamera() == GetCamera(name))
336             {
337                 return;
338             }
339             Camera source = GetSelectedCamera();
340             SelectCamera(name);
341             Camera destination = GetSelectedCamera();
342             CameraTransition(source, destination, durationMilliSeconds, alphaFunction);
343         }
344
345         /// <summary>
346         /// Retrieves selected Camera.
347         /// </summary>
348         /// <returns> Camera currently used in SceneView as a selected Camera.</returns>
349         /// <since_tizen> 10 </since_tizen>
350         public Camera GetSelectedCamera()
351         {
352             global::System.IntPtr cPtr = Interop.SceneView.GetSelectedCamera(SwigCPtr);
353             Camera camera = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Camera;
354             if(camera == null)
355             {
356                 // Register new camera into Registry.
357                 camera = new Camera(cPtr, true);
358             }
359             else
360             {
361                 // We found matched NUI camera. Reduce cPtr reference count.
362                 HandleRef handle = new HandleRef(this, cPtr);
363                 Interop.Camera.DeleteCameraProperty(handle);
364                 handle = new HandleRef(null, IntPtr.Zero);
365             }
366             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
367             return camera;
368         }
369
370         /// <summary>
371         /// Changes Image Based Light as the input textures.
372         /// </summary>
373         /// <param name="diffuseUrl">The path of Cube map image that can be used as a diffuse IBL source.</param>
374         /// <param name="specularUrl">The path of Cube map image that can be used as a specular IBL source.</param>
375         /// <param name="scaleFactor">Scale factor that controls light source intensity in [0.0f, 1.0f]. Default value is 1.0f.</param>
376         /// <remarks>
377         /// http://tizen.org/privilege/mediastorage for local files in media storage.
378         /// http://tizen.org/privilege/externalstorage for local files in external storage.
379         /// </remarks>
380         /// <since_tizen> 10 </since_tizen>
381         public void SetImageBasedLightSource(string diffuseUrl, string specularUrl, float scaleFactor = 1.0f)
382         {
383             Interop.SceneView.SetImageBasedLightSource(SwigCPtr, diffuseUrl, specularUrl, scaleFactor);
384             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
385         }
386
387         internal void SetUseFramebuffer(bool useFramebuffer)
388         {
389             Interop.SceneView.UseFramebuffer(SwigCPtr, useFramebuffer);
390             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
391         }
392
393         internal bool IsUsingFramebuffer()
394         {
395             bool result = Interop.SceneView.IsUsingFramebuffer(SwigCPtr);
396             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
397             return result;
398         }
399
400         /// <summary>
401         /// Set the ImageBasedLight ScaleFactor.
402         /// </summary>
403         /// <param name="scaleFactor">Scale factor that controls light source intensity in [0.0f, 1.0f].</param>
404         private void SetImageBasedLightScaleFactor(float scaleFactor)
405         {
406             Interop.SceneView.SetImageBasedLightScaleFactor(SwigCPtr, scaleFactor);
407             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
408         }
409
410         /// <summary>
411         /// Get the ImageBasedLight ScaleFactor.
412         /// </summary>
413         /// <returns>ImageBasedLightScaleFactor that controls light source intensity.</returns>
414         private float GetImageBasedLightScaleFactor()
415         {
416             float scaleFactor = Interop.SceneView.GetImageBasedLightScaleFactor(SwigCPtr);
417             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
418             return scaleFactor;
419         }
420
421         private void CameraTransition(Camera sourceCamera, Camera destinationCamera, int durationMilliSeconds, AlphaFunction alphaFunction)
422         {
423             inCameraTransition = true;
424
425             Position sourcePosition = sourceCamera.Position;
426             Rotation sourceOrientation = sourceCamera.Orientation;
427             Radian   sourceFieldOfView = sourceCamera.FieldOfView;
428
429             Position destinationPosition = destinationCamera.Position;
430             Rotation destinationOrientation = destinationCamera.Orientation;
431             Radian   destinationFieldOfView = destinationCamera.FieldOfView;
432
433             // If ProjectionDirection is not equal, match the value.
434             if (sourceCamera.ProjectionDirection != destinationCamera.ProjectionDirection)
435             {
436                 float aspect = destinationCamera.AspectRatio;
437                 if (destinationCamera.ProjectionDirection == Camera.ProjectionDirectionType.Vertical)
438                 {
439                     sourceFieldOfView = Camera.ConvertFovFromHorizontalToVertical(aspect, sourceFieldOfView);
440                 }
441                 else
442                 {
443                     sourceFieldOfView = Camera.ConvertFovFromVerticalToHorizontal(aspect, sourceFieldOfView);
444                 }
445             }
446
447             cameraTransition = new Animation(durationMilliSeconds);
448
449             KeyFrames positionKeyFrames = new KeyFrames();
450             positionKeyFrames.Add(0.0f, sourcePosition);
451             positionKeyFrames.Add(1.0f, destinationPosition);
452
453             KeyFrames orientationKeyFrames = new KeyFrames();
454             orientationKeyFrames.Add(0.0f, sourceOrientation);
455             orientationKeyFrames.Add(1.0f, destinationOrientation);
456
457             KeyFrames fieldOfViewKeyFrames = new KeyFrames();
458             fieldOfViewKeyFrames.Add(0.0f, sourceFieldOfView.ConvertToFloat());
459             fieldOfViewKeyFrames.Add(1.0f, destinationFieldOfView.ConvertToFloat());
460
461             cameraTransition.AnimateBetween(destinationCamera, "Position", positionKeyFrames, Animation.Interpolation.Linear, alphaFunction);
462             cameraTransition.AnimateBetween(destinationCamera, "Orientation", orientationKeyFrames, Animation.Interpolation.Linear, alphaFunction);
463             cameraTransition.AnimateBetween(destinationCamera, "FieldOfView", fieldOfViewKeyFrames, Animation.Interpolation.Linear, alphaFunction);
464
465             float destinationNearPlaneDistance = destinationCamera.NearPlaneDistance;
466             float destinationFarPlaneDistance = destinationCamera.FarPlaneDistance;
467             destinationCamera.NearPlaneDistance = Math.Min(sourceCamera.NearPlaneDistance, destinationCamera.NearPlaneDistance);
468             destinationCamera.FarPlaneDistance = Math.Max(sourceCamera.FarPlaneDistance, destinationCamera.FarPlaneDistance);
469
470             cameraTransition.Finished += (s, e) =>
471             {
472                 this.GetSelectedCamera().NearPlaneDistance = destinationNearPlaneDistance;
473                 this.GetSelectedCamera().FarPlaneDistance = destinationFarPlaneDistance;
474                 inCameraTransition = false;
475                 CameraTransitionFinished?.Invoke(this, EventArgs.Empty);
476             };
477             cameraTransition.Play();
478
479             sourceFieldOfView.Dispose();
480             positionKeyFrames.Dispose();
481             orientationKeyFrames.Dispose();
482             fieldOfViewKeyFrames.Dispose();
483         }
484
485         /// <summary>
486         /// Release swigCPtr.
487         /// </summary>
488         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
489         [EditorBrowsable(EditorBrowsableState.Never)]
490         protected override void ReleaseSwigCPtr(global::System.Runtime.InteropServices.HandleRef swigCPtr)
491         {
492             Interop.SceneView.DeleteScene(swigCPtr);
493         }
494     }
495 }