[Tizen] Refactoring of SceneView and Model.
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / controls / scene-view / scene-view-impl.cpp
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 // CLASS HEADER
19 #include <dali-scene3d/internal/controls/scene-view/scene-view-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali-toolkit/dali-toolkit.h>
23 #include <dali-toolkit/devel-api/controls/control-devel.h>
24 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
25 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
26 #include <dali-toolkit/public-api/image-loader/image-url.h>
27 #include <dali-toolkit/public-api/image-loader/image.h>
28 #include <dali/devel-api/actors/camera-actor-devel.h>
29 #include <dali/devel-api/adaptor-framework/window-devel.h>
30 #include <dali/devel-api/common/stage.h>
31 #include <dali/integration-api/adaptor-framework/adaptor.h>
32 #include <dali/integration-api/debug.h>
33 #include <dali/public-api/object/type-registry-helper.h>
34 #include <dali/public-api/object/type-registry.h>
35
36 // INTERNAL INCLUDES
37 #include <dali-scene3d/internal/controls/model/model-impl.h>
38 #include <dali-scene3d/public-api/loader/cube-map-loader.h>
39
40 using namespace Dali;
41
42 namespace Dali
43 {
44 namespace Scene3D
45 {
46 namespace Internal
47 {
48 namespace
49 {
50 BaseHandle Create()
51 {
52   return Scene3D::SceneView::New();
53 }
54
55 // Setup properties, signals and actions using the type-registry.
56 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::SceneView, Toolkit::Control, Create);
57 DALI_TYPE_REGISTRATION_END()
58
59 Property::Index   RENDERING_BUFFER    = Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX + 1;
60 constexpr int32_t DEFAULT_ORIENTATION = 0;
61
62 } // anonymous namespace
63
64 SceneView::SceneView()
65 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
66   mWindowOrientation(DEFAULT_ORIENTATION),
67   mIblLoadedCallback(nullptr)
68 {
69 }
70
71 SceneView::~SceneView()
72 {
73   if(mIblLoadedCallback && Adaptor::IsAvailable())
74   {
75     // Removes the callback from the callback manager in case the control is destroyed before the callback is executed.
76     Adaptor::Get().RemoveIdle(mIblLoadedCallback);
77   }
78 }
79
80 Dali::Scene3D::SceneView SceneView::New()
81 {
82   SceneView* impl = new SceneView();
83
84   Dali::Scene3D::SceneView handle = Dali::Scene3D::SceneView(*impl);
85
86   // Second-phase init of the implementation
87   // This can only be done after the CustomActor connection has been made...
88   impl->Initialize();
89
90   return handle;
91 }
92
93 void SceneView::AddCamera(CameraActor camera)
94 {
95   if(camera)
96   {
97     if(mCameras.empty())
98     {
99       UpdateCamera(camera);
100     }
101     mCameras.push_back(camera);
102   }
103 }
104
105 void SceneView::RemoveCamera(CameraActor camera)
106 {
107   if(camera == mDefaultCamera)
108   {
109     DALI_LOG_ERROR("Default Camera cannot removed.\n");
110     return;
111   }
112
113   if(camera)
114   {
115     for(uint32_t i = 0; i < mCameras.size(); ++i)
116     {
117       if(mCameras[i] == camera)
118       {
119         mCameras.erase(mCameras.begin() + i);
120         break;
121       }
122     }
123
124     if(mSelectedCamera == camera)
125     {
126       CameraActor newCurrentCamera = *mCameras.begin();
127       UpdateCamera(newCurrentCamera);
128     }
129   }
130 }
131
132 uint32_t SceneView::GetCameraCount() const
133 {
134   return mCameras.size();
135 }
136
137 CameraActor SceneView::GetSelectedCamera() const
138 {
139   return mSelectedCamera;
140 }
141
142 CameraActor SceneView::GetCamera(uint32_t index) const
143 {
144   if(index < mCameras.size())
145   {
146     return mCameras[index];
147   }
148   DALI_LOG_ERROR("Input index is out of bounds\n");
149   return CameraActor();
150 }
151
152 CameraActor SceneView::GetCamera(const std::string& name) const
153 {
154   CameraActor returnCamera;
155   for(auto&& camera : mCameras)
156   {
157     if(camera.GetProperty<std::string>(Actor::Property::NAME) == name)
158     {
159       returnCamera = camera;
160       break;
161     }
162   }
163   return returnCamera;
164 }
165
166 void SceneView::SelectCamera(uint32_t index)
167 {
168   UpdateCamera(GetCamera(index));
169 }
170
171 void SceneView::SelectCamera(const std::string& name)
172 {
173   UpdateCamera(GetCamera(name));
174 }
175
176 void SceneView::RegisterSceneItem(Scene3D::Internal::ImageBasedLightObserver *item)
177 {
178   if(item)
179   {
180     item->NotifyImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor);
181     mItems.push_back(item);
182   }
183 }
184
185 void SceneView::UnregisterSceneItem(Scene3D::Internal::ImageBasedLightObserver *item)
186 {
187   if(item)
188   {
189     for(uint32_t i = 0; i < mItems.size(); ++i)
190     {
191       if(mItems[i] == item)
192       {
193         mItems.erase(mItems.begin() + i);
194         break;
195       }
196     }
197   }
198 }
199
200 void SceneView::SetImageBasedLightSource(const std::string& diffuseUrl, const std::string& specularUrl, float scaleFactor)
201 {
202   // Request asynchronous model loading
203   if(!mIblLoadedCallback)
204   {
205     mIBLResourceReady = false;
206     mDiffuseIblUrl    = diffuseUrl;
207     mSpecularIblUrl   = specularUrl;
208     mIblScaleFactor   = scaleFactor;
209     // The callback manager takes the ownership of the callback object.
210     mIblLoadedCallback = MakeCallback(this, &SceneView::OnLoadComplete);
211     Adaptor::Get().AddIdle(mIblLoadedCallback, false);
212   }
213 }
214
215 void SceneView::SetImageBasedLightScaleFactor(float scaleFactor)
216 {
217   mIblScaleFactor = scaleFactor;
218   for(auto&& item : mItems)
219   {
220     if(item)
221     {
222       item->NotifyImageBasedLightScaleFactor(scaleFactor);
223     }
224   }
225 }
226
227 float SceneView::GetImageBasedLightScaleFactor() const
228 {
229   return mIblScaleFactor;
230 }
231
232 void SceneView::UseFramebuffer(bool useFramebuffer)
233 {
234   if(mUseFrameBuffer != useFramebuffer)
235   {
236     mUseFrameBuffer = useFramebuffer;
237     UpdateRenderTask();
238   }
239 }
240
241 bool SceneView::IsUsingFramebuffer() const
242 {
243   return mUseFrameBuffer;
244 }
245
246 ///////////////////////////////////////////////////////////
247 //
248 // Private methods
249 //
250
251 void SceneView::OnSceneConnection(int depth)
252 {
253   UpdateRenderTask();
254
255   Window window = DevelWindow::Get(Self());
256   if(window)
257   {
258     window.ResizeSignal().Connect(this, &SceneView::OnWindowResized);
259   }
260
261   Control::OnSceneConnection(depth);
262 }
263
264 void SceneView::OnSceneDisconnection()
265 {
266   mItems.clear();
267
268   Window window = DevelWindow::Get(Self());
269   if(window)
270   {
271     window.ResizeSignal().Disconnect(this, &SceneView::OnWindowResized);
272   }
273
274   Control::OnSceneDisconnection();
275 }
276
277 void SceneView::OnInitialize()
278 {
279   Actor self = Self();
280   mRootLayer = Layer::New();
281   mRootLayer.SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_3D);
282   mRootLayer.SetProperty(Layer::Property::DEPTH_TEST, true);
283   // The models in the SceneView should be have independent coordinate with DALi default coordinate.
284   mRootLayer.SetProperty(Dali::Actor::Property::INHERIT_POSITION, false);
285   mRootLayer.SetProperty(Dali::Actor::Property::INHERIT_ORIENTATION, false);
286   mRootLayer.SetProperty(Dali::Actor::Property::INHERIT_SCALE, false);
287   self.Add(mRootLayer);
288
289   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
290   mRenderTask             = taskList.CreateTask();
291   mRenderTask.SetSourceActor(mRootLayer);
292   mRenderTask.SetExclusive(true);
293   mRenderTask.SetInputEnabled(true);
294   mRenderTask.SetCullMode(false);
295   mRenderTask.SetScreenToFrameBufferMappingActor(Self());
296
297   mDefaultCamera = Dali::CameraActor::New();
298   mDefaultCamera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
299   mDefaultCamera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
300   mDefaultCamera.SetNearClippingPlane(1.0f);
301   AddCamera(mDefaultCamera);
302   UpdateCamera(mDefaultCamera);
303 }
304
305 void SceneView::OnChildAdd(Actor& child)
306 {
307   if(child != mRootLayer)
308   {
309     mRootLayer.Add(child);
310   }
311   Control::OnChildAdd(child);
312 }
313
314 void SceneView::OnChildRemove(Actor& child)
315 {
316   mRootLayer.Remove(child);
317   Control::OnChildRemove(child);
318 }
319
320 float SceneView::GetHeightForWidth(float width)
321 {
322   Extents padding;
323   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
324   return Control::GetHeightForWidth(width) + padding.top + padding.bottom;
325 }
326
327 float SceneView::GetWidthForHeight(float height)
328 {
329   Extents padding;
330   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
331   return Control::GetWidthForHeight(height) + padding.start + padding.end;
332 }
333
334 void SceneView::OnRelayout(const Vector2& size, RelayoutContainer& container)
335 {
336   Control::OnRelayout(size, container);
337   // Change canvas size of camera actor.
338   UpdateRenderTask();
339 }
340
341 bool SceneView::IsResourceReady() const
342 {
343   return mIBLResourceReady;
344 }
345
346 void SceneView::UpdateCamera(CameraActor camera)
347 {
348   if(camera)
349   {
350     if(mSelectedCamera && mSelectedCamera.GetParent())
351     {
352       mSelectedCamera.Unparent();
353     }
354     mRootLayer.Add(camera);
355   }
356
357   mSelectedCamera = camera;
358   UpdateRenderTask();
359 }
360
361 void SceneView::UpdateRenderTask()
362 {
363   if(mRenderTask)
364   {
365     if(mSelectedCamera != mRenderTask.GetCameraActor())
366     {
367       mRenderTask.SetCameraActor(mSelectedCamera);
368     }
369
370     Vector3     size        = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
371     const float aspectRatio = size.width / size.height;
372     mSelectedCamera.SetAspectRatio(aspectRatio);
373     const bool projectionVertical = mSelectedCamera.GetProperty<int>(Dali::DevelCameraActor::Property::PROJECTION_DIRECTION) == Dali::DevelCameraActor::VERTICAL;
374
375     // if projectionVertical, Top / Bottom is +-ve to keep consistency with orthographic values
376     // else, Left / Right is +-ve to keep consistency with orthographic values
377     const float orthographicSize = DALI_LIKELY(projectionVertical) ? mSelectedCamera[Dali::CameraActor::Property::TOP_PLANE_DISTANCE] : mSelectedCamera[Dali::CameraActor::Property::RIGHT_PLANE_DISTANCE];
378     const float halfHeight       = DALI_LIKELY(projectionVertical) ? orthographicSize : orthographicSize / aspectRatio;
379     const float halfWidth        = DALI_LIKELY(projectionVertical) ? orthographicSize * aspectRatio : orthographicSize;
380
381     mSelectedCamera[Dali::CameraActor::Property::LEFT_PLANE_DISTANCE]   = -halfWidth;
382     mSelectedCamera[Dali::CameraActor::Property::RIGHT_PLANE_DISTANCE]  = halfWidth;
383     mSelectedCamera[Dali::CameraActor::Property::TOP_PLANE_DISTANCE]    = halfHeight;
384     mSelectedCamera[Dali::CameraActor::Property::BOTTOM_PLANE_DISTANCE] = -halfHeight;
385
386     if(mUseFrameBuffer)
387     {
388       Dali::FrameBuffer currentFrameBuffer = mRenderTask.GetFrameBuffer();
389       if(!currentFrameBuffer ||
390          currentFrameBuffer.GetColorTexture().GetWidth() != size.width ||
391          currentFrameBuffer.GetColorTexture().GetHeight() != size.height)
392       {
393         mRenderTask.ResetViewportGuideActor();
394         mRenderTask.SetViewport(Dali::Viewport(Vector4::ZERO));
395
396         // create offscreen buffer of new size to render our child actors to
397         mTexture      = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, unsigned(size.width), unsigned(size.height));
398         mRenderTarget = FrameBuffer::New(size.width, size.height, FrameBuffer::Attachment::DEPTH_STENCIL);
399         mRenderTarget.AttachColorTexture(mTexture);
400         Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::Image::GenerateUrl(mRenderTarget, 0u);
401
402         Property::Map imagePropertyMap;
403         imagePropertyMap.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
404         imagePropertyMap.Insert(Toolkit::ImageVisual::Property::URL, imageUrl.GetUrl());
405         // To flip rendered scene without CameraActor::SetInvertYAxis() to avoid backface culling.
406         imagePropertyMap.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, Vector4(0.0f, 1.0f, 1.0f, -1.0f));
407         mVisual = Toolkit::VisualFactory::Get().CreateVisual(imagePropertyMap);
408
409         Toolkit::DevelControl::RegisterVisual(*this, RENDERING_BUFFER, mVisual);
410
411         mRenderTask.SetFrameBuffer(mRenderTarget);
412         mRenderTask.SetClearEnabled(true);
413         mRenderTask.SetClearColor(Color::TRANSPARENT);
414       }
415     }
416     else
417     {
418       mRenderTask.SetViewportGuideActor(Self());
419       if(mRenderTask.GetFrameBuffer())
420       {
421         FrameBuffer framebuffer;
422         mRenderTask.SetFrameBuffer(framebuffer);
423         mRenderTask.SetClearEnabled(false);
424
425         Toolkit::DevelControl::UnregisterVisual(*this, RENDERING_BUFFER);
426
427         mVisual.Reset();
428         mRenderTarget.Reset();
429         mTexture.Reset();
430       }
431     }
432
433     RotateCamera();
434   }
435 }
436
437 void SceneView::OnWindowResized(Window window, Window::WindowSize size)
438 {
439   mWindowOrientation = DevelWindow::GetPhysicalOrientation(window);
440   RotateCamera();
441 }
442
443 void SceneView::RotateCamera()
444 {
445   if(mUseFrameBuffer)
446   {
447     DevelCameraActor::RotateProjection(mSelectedCamera, DEFAULT_ORIENTATION);
448   }
449   else
450   {
451     DevelCameraActor::RotateProjection(mSelectedCamera, mWindowOrientation);
452   }
453 }
454
455 void SceneView::LoadImageBasedLight()
456 {
457   Texture diffuseTexture  = Dali::Scene3D::Loader::LoadCubeMap(mDiffuseIblUrl);
458   Texture specularTexture = Dali::Scene3D::Loader::LoadCubeMap(mSpecularIblUrl);
459
460   if(diffuseTexture && specularTexture)
461   {
462     mDiffuseTexture  = diffuseTexture;
463     mSpecularTexture = specularTexture;
464     for(auto&& item : mItems)
465     {
466       if(item)
467       {
468         item->NotifyImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor);
469       }
470     }
471   }
472 }
473
474 void SceneView::OnLoadComplete()
475 {
476   // TODO: In this implementation, we cannot know which request occurs this OnLoadComplete Callback.
477   // Currently it is no problem because the all loading is processed in this method.
478
479   // Prevent to emit unnecessary resource ready signal.
480   if(IsResourceReady())
481   {
482     return;
483   }
484
485   if(!mIBLResourceReady)
486   {
487     LoadImageBasedLight();
488     mIBLResourceReady  = true;
489     mIblLoadedCallback = nullptr;
490   }
491
492   if(IsResourceReady())
493   {
494     Control::SetResourceReady(false);
495   }
496 }
497
498 } // namespace Internal
499 } // namespace Scene3D
500 } // namespace Dali