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