[dali_2.3.19] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / model3d-view / model3d-view-impl.cpp
1 /*
2  * Copyright (c) 2021 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 "model3d-view-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali-toolkit/public-api/controls/model3d-view/model3d-view.h>
23 #include <dali/devel-api/adaptor-framework/file-loader.h>
24 #include <dali/devel-api/adaptor-framework/image-loading.h>
25 #include <dali/public-api/animation/constraint-source.h>
26 #include <dali/public-api/animation/constraint.h>
27 #include <dali/public-api/animation/constraints.h>
28 #include <dali/public-api/object/type-registry-helper.h>
29 #include <dali/public-api/object/type-registry.h>
30
31 // INTERNAL INCLUDES
32 #include <dali-toolkit/devel-api/controls/control-devel.h>
33 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
34 #include <dali-toolkit/internal/controls/model3d-view/obj-loader.h>
35 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
36
37 namespace Dali
38 {
39 namespace Toolkit
40 {
41 namespace Internal
42 {
43 namespace
44 {
45 // Texture indices are constants.
46 enum TextureIndex
47 {
48   DIFFUSE_TEXTURE_INDEX,
49   NORMAL_TEXTURE_INDEX,
50   GLOSS_TEXTURE_INDEX
51 };
52
53 /**
54  * @brief Loads a texture from a file.
55  * @param[in] imageUrl The URL of the file
56  * @return A texture if loading succeeds, an empty handle otherwise
57  */
58 Texture LoadTexture(const char* imageUrl)
59 {
60   Texture            texture;
61   Devel::PixelBuffer pixelBuffer = LoadImageFromFile(imageUrl);
62   if(pixelBuffer)
63   {
64     texture             = Texture::New(TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight());
65     PixelData pixelData = Devel::PixelBuffer::Convert(pixelBuffer);
66     texture.Upload(pixelData);
67     texture.GenerateMipmaps();
68   }
69
70   return texture;
71 }
72
73 // Type registration
74 BaseHandle Create()
75 {
76   return Toolkit::Model3dView::New();
77 }
78
79 // Setup properties, signals and actions using the type-registry.
80 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::Model3dView, Toolkit::Control, Create);
81
82 DALI_PROPERTY_REGISTRATION(Toolkit, Model3dView, "geometryUrl", STRING, GEOMETRY_URL)
83 DALI_PROPERTY_REGISTRATION(Toolkit, Model3dView, "materialUrl", STRING, MATERIAL_URL)
84 DALI_PROPERTY_REGISTRATION(Toolkit, Model3dView, "imagesUrl", STRING, IMAGES_URL)
85 DALI_PROPERTY_REGISTRATION(Toolkit, Model3dView, "illuminationType", INTEGER, ILLUMINATION_TYPE)
86 DALI_PROPERTY_REGISTRATION(Toolkit, Model3dView, "texture0Url", STRING, TEXTURE0_URL)
87 DALI_PROPERTY_REGISTRATION(Toolkit, Model3dView, "texture1Url", STRING, TEXTURE1_URL)
88 DALI_PROPERTY_REGISTRATION(Toolkit, Model3dView, "texture2Url", STRING, TEXTURE2_URL)
89
90 DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, Model3dView, "lightPosition", VECTOR3, LIGHT_POSITION)
91
92 DALI_TYPE_REGISTRATION_END()
93
94 } // anonymous namespace
95
96 using namespace Dali;
97
98 Model3dView::Model3dView()
99 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT))
100 {
101   mIlluminationType = Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP;
102
103   mCameraFOV = Math::PI_OVER_180 * 45.f;
104
105   mControlSize = Vector2(100., 100.);
106 }
107
108 Model3dView::~Model3dView()
109 {
110 }
111
112 Toolkit::Model3dView Model3dView::New()
113 {
114   Model3dView* impl = new Model3dView();
115
116   Dali::Toolkit::Model3dView handle = Dali::Toolkit::Model3dView(*impl);
117
118   // Second-phase init of the implementation
119   // This can only be done after the CustomActor connection has been made...
120   impl->Initialize();
121
122   return handle;
123 }
124
125 void Model3dView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
126 {
127   Toolkit::Model3dView model3dView = Toolkit::Model3dView::DownCast(Dali::BaseHandle(object));
128
129   if(model3dView)
130   {
131     Model3dView& impl(GetImpl(model3dView));
132     switch(index)
133     {
134       case Toolkit::Model3dView::Property::GEOMETRY_URL:
135       {
136         if(value.Get(impl.mObjUrl))
137         {
138           impl.LoadGeometry();
139           impl.CreateGeometry();
140         }
141         break;
142       }
143       case Toolkit::Model3dView::Property::MATERIAL_URL:
144       {
145         if(value.Get(impl.mTextureSetUrl))
146         {
147           impl.LoadMaterial();
148           impl.CreateMaterial();
149           impl.LoadTextures();
150         }
151         break;
152       }
153       case Toolkit::Model3dView::Property::IMAGES_URL:
154       {
155         if(value.Get(impl.mImagesUrl))
156         {
157           impl.LoadTextures();
158         }
159         break;
160       }
161       case Toolkit::Model3dView::Property::ILLUMINATION_TYPE:
162       {
163         int illuminationType;
164         if(value.Get(illuminationType))
165         {
166           impl.mIlluminationType = Toolkit::Model3dView::IlluminationType(illuminationType);
167           impl.CreateGeometry();
168           impl.CreateMaterial();
169           impl.LoadTextures();
170         }
171         break;
172       }
173       case Toolkit::Model3dView::Property::TEXTURE0_URL:
174       {
175         value.Get(impl.mTexture0Url);
176         break;
177       }
178       case Toolkit::Model3dView::Property::TEXTURE1_URL:
179       {
180         value.Get(impl.mTexture1Url);
181         break;
182       }
183       case Toolkit::Model3dView::Property::TEXTURE2_URL:
184       {
185         value.Get(impl.mTexture2Url);
186         break;
187       }
188     }
189   }
190 }
191
192 Property::Value Model3dView::GetProperty(BaseObject* object, Property::Index index)
193 {
194   Property::Value value;
195
196   Toolkit::Model3dView model3dView = Toolkit::Model3dView::DownCast(Dali::BaseHandle(object));
197
198   if(model3dView)
199   {
200     Model3dView& impl(GetImpl(model3dView));
201     switch(index)
202     {
203       case Toolkit::Model3dView::Property::GEOMETRY_URL:
204       {
205         value = impl.mObjUrl;
206         break;
207       }
208       case Toolkit::Model3dView::Property::MATERIAL_URL:
209       {
210         value = impl.mTextureSetUrl;
211         break;
212       }
213       case Toolkit::Model3dView::Property::IMAGES_URL:
214       {
215         value = impl.mImagesUrl;
216         break;
217       }
218       case Toolkit::Model3dView::Property::ILLUMINATION_TYPE:
219       {
220         value = int(impl.mIlluminationType);
221         break;
222       }
223       case Toolkit::Model3dView::Property::TEXTURE0_URL:
224       {
225         value = impl.mTexture0Url;
226         break;
227       }
228       case Toolkit::Model3dView::Property::TEXTURE1_URL:
229       {
230         value = impl.mTexture1Url;
231         break;
232       }
233       case Toolkit::Model3dView::Property::TEXTURE2_URL:
234       {
235         value = impl.mTexture2Url;
236         break;
237       }
238     }
239   }
240
241   return value;
242 }
243
244 /////////////////////////////////////////////////////////////
245
246 void Model3dView::OnSceneConnection(int depth)
247 {
248   CustomActor self = Self();
249   self.AddRenderer(mRenderer);
250
251   if(mObjLoader.IsSceneLoaded())
252   {
253     mMesh = mObjLoader.CreateGeometry(GetShaderProperties(mIlluminationType), true);
254
255     CreateMaterial();
256     LoadTextures();
257
258     mRenderer.SetGeometry(mMesh);
259
260     //create constraint for lightPosition Property with uLightPosition in the shader
261     Vector3               lightPosition(0, 0, 0);
262     Dali::Property::Index lightProperty = mShader.RegisterProperty("uLightPosition", lightPosition);
263     Constraint            constraint    = Constraint::New<Vector3>(mShader, lightProperty, EqualToConstraint());
264     constraint.AddSource(Source(self, Toolkit::Model3dView::Property::LIGHT_POSITION));
265     constraint.Apply();
266   }
267
268   Control::OnSceneConnection(depth);
269 }
270
271 ///////////////////////////////////////////////////////////
272 //
273 // Private methods
274 //
275
276 void Model3dView::OnInitialize()
277 {
278   //Create empty versions of the geometry and material so we always have a Renderer
279   Geometry mesh   = Geometry::New();
280   Shader   shader = Shader::New(SHADER_MODEL3D_VIEW_SIMPLE_SHADER_VERT, SHADER_MODEL3D_VIEW_SIMPLE_SHADER_FRAG);
281   mRenderer       = Renderer::New(mesh, shader);
282
283   DevelControl::SetAccessibilityConstructor(Self(), [](Dali::Actor actor) {
284     return std::unique_ptr<Dali::Accessibility::Accessible>(
285       new DevelControl::AccessibleImpl(actor, Dali::Accessibility::Role::IMAGE));
286   });
287 }
288
289 void Model3dView::LoadGeometry()
290 {
291   //Load file in adaptor
292   std::streampos     fileSize;
293   Dali::Vector<char> fileContent;
294
295   if(FileLoader::ReadFile(mObjUrl, fileSize, fileContent, FileLoader::TEXT))
296   {
297     mObjLoader.ClearArrays();
298     mObjLoader.LoadObject(fileContent.Begin(), fileSize);
299
300     //Get size information from the obj loaded
301     mSceneCenter = mObjLoader.GetCenter();
302     mSceneSize   = mObjLoader.GetSize();
303   }
304   else
305   {
306     //Error
307   }
308 }
309
310 void Model3dView::LoadMaterial()
311 {
312   //Load file in adaptor
313   std::streampos     fileSize;
314   Dali::Vector<char> fileContent;
315
316   if(FileLoader::ReadFile(mTextureSetUrl, fileSize, fileContent, FileLoader::TEXT))
317   {
318     mObjLoader.LoadMaterial(fileContent.Begin(), fileSize, mTexture0Url, mTexture1Url, mTexture2Url);
319   }
320   else
321   {
322     //Error
323   }
324 }
325
326 void Model3dView::Load()
327 {
328   LoadGeometry();
329   LoadMaterial();
330 }
331
332 void Model3dView::OnRelayout(const Vector2& size, RelayoutContainer& container)
333 {
334   UpdateView();
335 }
336
337 void Model3dView::UpdateView()
338 {
339   if(mObjLoader.IsSceneLoaded())
340   {
341     //The object will always be centred
342
343     Matrix scaleMatrix;
344     scaleMatrix.SetIdentityAndScale(Vector3(1.0, -1.0, 1.0));
345
346     mShader.RegisterProperty("uObjectMatrix", scaleMatrix);
347   }
348 }
349
350 void Model3dView::CreateGeometry()
351 {
352   if(mObjLoader.IsSceneLoaded())
353   {
354     mMesh = mObjLoader.CreateGeometry(GetShaderProperties(mIlluminationType), true);
355
356     if(mRenderer)
357     {
358       mRenderer.SetGeometry(mMesh);
359       mRenderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON);
360       mRenderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);
361     }
362   }
363 }
364
365 void Model3dView::UpdateShaderUniforms()
366 {
367   if(mShader)
368   {
369     //Update shader related info, uniforms, etc. for the new shader
370     UpdateView();
371
372     Vector3               lightPosition(0, 0, 0);
373     Dali::Property::Index lightProperty = mShader.RegisterProperty("uLightPosition", lightPosition);
374
375     CustomActor self = Self();
376
377     //create constraint for lightPosition Property with uLightPosition in the shader
378     if(lightProperty)
379     {
380       Constraint constraint = Constraint::New<Vector3>(mShader, lightProperty, EqualToConstraint());
381       constraint.AddSource(Source(self, Toolkit::Model3dView::Property::LIGHT_POSITION));
382       constraint.Apply();
383     }
384   }
385 }
386
387 void Model3dView::CreateMaterial()
388 {
389   if(mObjLoader.IsMaterialLoaded() && (mTexture0Url != "") && mObjLoader.IsTexturePresent())
390   {
391     if((mTexture2Url != "") && (mTexture1Url != "") && (mIlluminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP))
392     {
393       mShader = Shader::New(SHADER_MODEL3D_VIEW_NRMMAP_SHADER_VERT, SHADER_MODEL3D_VIEW_NRMMAP_SHADER_FRAG);
394     }
395     else if(mIlluminationType == Toolkit::Model3dView::DIFFUSE_WITH_TEXTURE ||
396             mIlluminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP)
397     {
398       mShader = Shader::New(SHADER_MODEL3D_VIEW_SHADER_VERT, SHADER_MODEL3D_VIEW_SHADER_FRAG);
399     }
400     else
401     {
402       mShader = Shader::New(SHADER_MODEL3D_VIEW_SIMPLE_SHADER_VERT, SHADER_MODEL3D_VIEW_SIMPLE_SHADER_FRAG);
403     }
404   }
405   else
406   {
407     mShader = Shader::New(SHADER_MODEL3D_VIEW_SIMPLE_SHADER_VERT, SHADER_MODEL3D_VIEW_SIMPLE_SHADER_FRAG);
408   }
409
410   mTextureSet = TextureSet::New();
411
412   if(mRenderer)
413   {
414     mRenderer.SetTextures(mTextureSet);
415     mRenderer.SetShader(mShader);
416     mRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
417   }
418
419   UpdateShaderUniforms();
420 }
421
422 void Model3dView::LoadTextures()
423 {
424   if(!mTextureSet)
425   {
426     return;
427   }
428
429   Sampler sampler = Sampler::New();
430   sampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR_MIPMAP_LINEAR);
431
432   // Setup diffuse texture.
433   if(!mTexture0Url.empty() && (mIlluminationType != Toolkit::Model3dView::DIFFUSE))
434   {
435     std::string imageUrl = mImagesUrl + mTexture0Url;
436
437     //Load textures
438     Texture diffuseTexture = LoadTexture(imageUrl.c_str());
439     if(diffuseTexture)
440     {
441       mTextureSet.SetTexture(DIFFUSE_TEXTURE_INDEX, diffuseTexture);
442       mTextureSet.SetSampler(DIFFUSE_TEXTURE_INDEX, sampler);
443     }
444   }
445
446   if(mIlluminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP)
447   {
448     // Setup normal map texture.
449     if(!mTexture1Url.empty())
450     {
451       std::string imageUrl = mImagesUrl + mTexture1Url;
452
453       //Load textures
454       Texture normalTexture = LoadTexture(imageUrl.c_str());
455       if(normalTexture)
456       {
457         mTextureSet.SetTexture(NORMAL_TEXTURE_INDEX, normalTexture);
458         mTextureSet.SetSampler(NORMAL_TEXTURE_INDEX, sampler);
459       }
460     }
461     if(!mTexture2Url.empty())
462     {
463       // Setup gloss map texture.
464       std::string imageUrl = mImagesUrl + mTexture2Url;
465
466       //Load textures
467       Texture glossTexture = LoadTexture(imageUrl.c_str());
468       if(glossTexture)
469       {
470         mTextureSet.SetTexture(GLOSS_TEXTURE_INDEX, glossTexture);
471         mTextureSet.SetSampler(GLOSS_TEXTURE_INDEX, sampler);
472       }
473     }
474   }
475 }
476
477 int Model3dView::GetShaderProperties(Toolkit::Model3dView::IlluminationType illuminationType)
478 {
479   int objectProperties = 0;
480
481   if(illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_TEXTURE ||
482      illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP)
483   {
484     objectProperties |= ObjLoader::TEXTURE_COORDINATES;
485   }
486
487   if(illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP)
488   {
489     objectProperties |= ObjLoader::TANGENTS | ObjLoader::BINORMALS;
490   }
491
492   return objectProperties;
493 }
494
495 } // namespace Internal
496 } // namespace Toolkit
497 } // namespace Dali