From: Seungho, Baek Date: Fri, 7 Sep 2018 08:43:50 +0000 (+0900) Subject: Scene and glTF Loader X-Git-Tag: dali_1.3.45~1 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=f66c8a201b4ca45d2b65229bdae411b6e18633fa;hp=2f53f42339402200d6f100f4c46f5893644739ec Scene and glTF Loader - Scene for the 3D Mesh object(Animated or not). - PBR rendering with Image Based Lighting - glTF Loader that loads scene from glTF( TODO: sparse accessor, skeletal animation, morphing. ) - UTC Change-Id: I4ee6b5db3315b8d165bbf90269ce01c86cc70531 Signed-off-by: Seungho, Baek --- diff --git a/automated-tests/resources/AnimatedCube.bin b/automated-tests/resources/AnimatedCube.bin new file mode 100644 index 0000000..72f7d2d Binary files /dev/null and b/automated-tests/resources/AnimatedCube.bin differ diff --git a/automated-tests/resources/AnimatedCube.gltf b/automated-tests/resources/AnimatedCube.gltf new file mode 100644 index 0000000..2f78e20 --- /dev/null +++ b/automated-tests/resources/AnimatedCube.gltf @@ -0,0 +1,401 @@ +{ + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "max" : [ + 2.000000 + ], + "min" : [ + 0.000000 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "max" : [ + 0.000000, + 1.000000, + 0.000000, + 1.000000 + ], + "min" : [ + 0.000000, + -8.742278e-008, + 0.000000, + -1.000000 + ], + "type" : "VEC4" + }, + { + "bufferView" : 2, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 36, + "max" : [ + 35 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 3, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 36, + "max" : [ + 1.000000, + 1.000000, + 1.000001 + ], + "min" : [ + -1.000000, + -1.000000, + -1.000000 + ], + "type" : "VEC3" + }, + { + "bufferView" : 4, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 36, + "max" : [ + 1.000000, + 1.000000, + 1.000000 + ], + "min" : [ + -1.000000, + -1.000000, + -1.000000 + ], + "type" : "VEC3" + }, + { + "bufferView" : 5, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 36, + "max" : [ + 1.000000, + -0.000000, + -0.000000, + 1.000000 + ], + "min" : [ + 0.000000, + -0.000000, + -1.000000, + -1.000000 + ], + "type" : "VEC4" + }, + { + "bufferView" : 6, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 36, + "max" : [ + 1.000000, + 1.000000 + ], + "min" : [ + -1.000000, + -1.000000 + ], + "type" : "VEC2" + } + ], + "animations" : [ + { + "channels" : [ + { + "sampler" : 0, + "target" : { + "node" : 0, + "path" : "rotation" + } + } + ], + "name" : "animation_AnimatedCube", + "samplers" : [ + { + "input" : 0, + "interpolation" : "LINEAR", + "output" : 1 + } + ] + } + ], + "asset" : { + "generator" : "VKTS glTF 2.0 exporter", + "version" : "2.0" + }, + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 12, + "byteOffset" : 0 + }, + { + "buffer" : 0, + "byteLength" : 48, + "byteOffset" : 12 + }, + { + "buffer" : 0, + "byteLength" : 72, + "byteOffset" : 60, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 432, + "byteOffset" : 132, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 432, + "byteOffset" : 564, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 576, + "byteOffset" : 996, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 288, + "byteOffset" : 1572, + "target" : 34962 + } + ], + "buffers" : [ + { + "byteLength" : 1860, + "uri" : "AnimatedCube.bin" + } + ], + "images" : [ + { + "uri" : "AnimatedCube_BaseColor.png" + }, + { + "uri" : "AnimatedCube_MetallicRoughness.png" + } + ], + "materials" : [ + { + "name" : "AnimatedCube", + "pbrMetallicRoughness" : { + "baseColorTexture" : { + "index" : 0 + }, + "metallicRoughnessTexture" : { + "index" : 1 + }, + "baseColorFactor": [ 1.000, 0.766, 0.336, 1.0 ], + "metallicFactor": 1.0, + "roughnessFactor": 0.0 + }, + "normalTexture": { + "scale": 1, + "index": 0 + }, + "occlusionTexture": { + "index": 0 + }, + "emissiveTexture": { + "index": 0 + }, + "emissiveFactor": [ 0.2, 0.1, 0.0 ], + "doubleSided": false, + "alphaMode": "MASK", + "alphaCutoff": 0.5 + }, + { + "name" : "AnimatedCube2", + "pbrMetallicRoughness" : { + "baseColorTexture" : { + "index" : 0 + }, + "metallicRoughnessTexture" : { + "index" : 1 + }, + "baseColorFactor": [ 1.000, 0.766, 0.336, 1.0 ], + "metallicFactor": 1.0, + "roughnessFactor": 0.0 + }, + "normalTexture": { + "scale": 1, + "index": 0 + }, + "occlusionTexture": { + "index": 0 + }, + "emissiveTexture": { + "index": 0 + }, + "emissiveFactor": [ 0.2, 0.1, 0.0 ], + "doubleSided": false, + "alphaMode": "OPAQUE" + } + ], + "meshes" : [ + { + "name" : "AnimatedCube", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 4, + "POSITION" : 3, + "TANGENT" : 5, + "TEXCOORD_0" : 6, + "COLOR_0" : 3 + }, + "indices" : 2, + "material" : 0, + "mode" : 4 + } + ] + }, + { + "name" : "AnimatedCube2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 4, + "POSITION" : 3, + "TANGENT" : 5, + "TEXCOORD_0" : 6, + "COLOR_0" : 3 + }, + "indices" : 2, + "material" : 1, + "mode" : 4 + } + ] + } + ], + "nodes" : [ + { + "mesh" : 0, + "name" : "AnimatedCube", + "rotation" : [ + 0.000000, + -1.000000, + 0.000000, + 0.000000 + ] + }, + { + "mesh" : 1, + "name" : "AnimatedCube2" + }, + { + + "camera" : 0, + "scale" : [ 0.5, 0.5, 3.0 ] + }, + { + "camera" : 1, + "translation" : [ 0.5, 0.5, 3.0 ], + "children": [ + 4 + ] + }, + { + "camera" : 2, + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + } + ], + "scene" : 0, + "scenes" : [ + { + "nodes" : [ + 0, 1, 2, 3 + ] + } + ], + "textures" : [ + { + "sampler" : 0, + "source" : 0 + }, + { + "sampler" : 1, + "source" : 1 + } + ], + "cameras" : [ + { + "type": "perspective", + "perspective": { + "aspectRatio": 1.0, + "yfov": 0.7, + "zfar": 100.0, + "znear": 0.01 + } + }, + { + "type": "orthographic", + "orthographic": { + "xmag": 1.0, + "ymag": 1.0, + "zfar": 100.0, + "znear": 0.01 + } + }, + { + "type": "orthographic", + "orthographic": { + "xmag": 1.0, + "ymag": 1.0, + "zfar": 100.0, + "znear": 0.01 + } + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9987, + "wrapS": 33071, + "wrapT": 10497 + }, + { + "magFilter": 9728, + "minFilter": 9986, + "wrapS": 33071, + "wrapT": 33648 + } + ] +} \ No newline at end of file diff --git a/automated-tests/resources/AnimatedCube_BaseColor.png b/automated-tests/resources/AnimatedCube_BaseColor.png new file mode 100644 index 0000000..5e5cb20 Binary files /dev/null and b/automated-tests/resources/AnimatedCube_BaseColor.png differ diff --git a/automated-tests/resources/AnimatedCube_MetallicRoughness.png b/automated-tests/resources/AnimatedCube_MetallicRoughness.png new file mode 100644 index 0000000..efd2026 Binary files /dev/null and b/automated-tests/resources/AnimatedCube_MetallicRoughness.png differ diff --git a/automated-tests/resources/forest_diffuse_cubemap.png b/automated-tests/resources/forest_diffuse_cubemap.png new file mode 100644 index 0000000..ff3e237 Binary files /dev/null and b/automated-tests/resources/forest_diffuse_cubemap.png differ diff --git a/automated-tests/resources/forest_specular_cubemap.png b/automated-tests/resources/forest_specular_cubemap.png new file mode 100644 index 0000000..d5cb909 Binary files /dev/null and b/automated-tests/resources/forest_specular_cubemap.png differ diff --git a/automated-tests/src/dali-toolkit/CMakeLists.txt b/automated-tests/src/dali-toolkit/CMakeLists.txt index 9d79595..f193458 100755 --- a/automated-tests/src/dali-toolkit/CMakeLists.txt +++ b/automated-tests/src/dali-toolkit/CMakeLists.txt @@ -27,6 +27,7 @@ SET(TC_SOURCES utc-Dali-KeyInputFocusManager.cpp utc-Dali-Layouting.cpp utc-Dali-PageTurnView.cpp + utc-Dali-Scene3dView.cpp utc-Dali-Script.cpp utc-Dali-ScrollBar.cpp utc-Dali-ScrollView.cpp diff --git a/automated-tests/src/dali-toolkit/utc-Dali-Scene3dView.cpp b/automated-tests/src/dali-toolkit/utc-Dali-Scene3dView.cpp new file mode 100644 index 0000000..7c8dad4 --- /dev/null +++ b/automated-tests/src/dali-toolkit/utc-Dali-Scene3dView.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2018 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. + * + */ + +#include +#include +#include +#include +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +void dali_scene_view_startup(void) +{ + test_return_value = TET_UNDEF; +} + +void dali_scene_view_cleanup(void) +{ + test_return_value = TET_PASS; +} + +namespace +{ + +/** + * For the AnimatedCube.gltf and its Assets + * Donated by Norbert Nopper for glTF testing. + * Take from https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/AnimatedCube + */ +const char* TEST_GLTF_FILE_NAME = TEST_RESOURCE_DIR "/AnimatedCube.gltf"; +/** + * For the diffuse and specular cube map texture. + * These textures are based off version of Wave engine sample + * Take from https://github.com/WaveEngine/Samples + * + * Copyright (c) 2016 Wave Coorporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +const char* TEST_DIFFUSE_TEXTURE = TEST_RESOURCE_DIR "/forest_diffuse_cubemap.png"; +const char* TEST_SPECULAR_TEXTURE = TEST_RESOURCE_DIR "/forest_specular_cubemap.png"; +} + +int UtcDaliScene3dViewConstructorP(void) +{ + TestApplication application; + + Scene3dView scene3dView; + + DALI_TEST_CHECK( !scene3dView ); + END_TEST; +} + +int UtcDaliScene3dViewCopyConstructorP(void) +{ + TestApplication application; + + // Initialize an object, ref count == 1 + Scene3dView scene3dView = Scene3dView::New( TEST_GLTF_FILE_NAME ); + + Scene3dView copy( scene3dView ); + DALI_TEST_CHECK( copy ); + END_TEST; +} + +int UtcDaliScene3dViewCopyConstructor2P(void) +{ + TestApplication application; + + // Initialize an object, ref count == 1 + Toolkit::Scene3dView scene3dView = Toolkit::Scene3dView::New( TEST_GLTF_FILE_NAME, TEST_DIFFUSE_TEXTURE, TEST_SPECULAR_TEXTURE, Vector4::ONE ); + + Scene3dView copy( scene3dView ); + DALI_TEST_CHECK( copy ); + END_TEST; +} + +int UtcDaliScene3dViewAssignmentOperatorP(void) +{ + TestApplication application; + + Scene3dView scene3dView = Scene3dView::New( TEST_GLTF_FILE_NAME ); + + Scene3dView copy( scene3dView ); + DALI_TEST_CHECK( copy ); + + DALI_TEST_CHECK( scene3dView == copy ); + END_TEST; +} + +int UtcDaliScene3dViewNewP(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScene3dViewNewP"); + + // Create the Slider actor + Scene3dView scene3dView; + DALI_TEST_CHECK( !scene3dView ); + + scene3dView = Scene3dView::New( TEST_GLTF_FILE_NAME ); + DALI_TEST_CHECK( scene3dView ); + + END_TEST; +} + +int UtcDaliScene3dViewDestructorP(void) +{ + ToolkitTestApplication application; + + Scene3dView* scene3dView = new Scene3dView(); + delete scene3dView; + + DALI_TEST_CHECK( true ); + END_TEST; +} + +int UtcDaliScene3dViewDownCast(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScene3dViewDownCast"); + + Toolkit::Scene3dView view = Toolkit::Scene3dView::New( TEST_GLTF_FILE_NAME ); + BaseHandle handle(view); + + Toolkit::Scene3dView scene3dView = Toolkit::Scene3dView::DownCast( handle ); + DALI_TEST_CHECK( view ); + DALI_TEST_CHECK( scene3dView ); + DALI_TEST_CHECK( scene3dView == view ); + END_TEST; +} + +int UtcDaliScene3dViewSetLight(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScene3dViewSetLight"); + + Toolkit::Scene3dView view = Toolkit::Scene3dView::New( TEST_GLTF_FILE_NAME ); + + bool lightSet = view.SetLight( Scene3dView::LightType::DIRECTIONAL_LIGHT, Vector3( 1.0, 1.0, -1.0 ), Vector3( 0.3, 0.3, 0.3 ) ); + DALI_TEST_CHECK( lightSet ); + bool lightSet2 = view.SetLight( Scene3dView::LightType::POINT_LIGHT, Vector3( 1.0, 1.0, -1.0 ), Vector3( 0.3, 0.3, 0.3 ) ); + DALI_TEST_CHECK( lightSet2 ); + + END_TEST; +} + +int UtcDaliScene3dViewGetCamera(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScene3dViewGetCamera"); + + Toolkit::Scene3dView view = Toolkit::Scene3dView::New( TEST_GLTF_FILE_NAME ); + + CameraActor camera = view.GetDefaultCamera(); + DALI_TEST_CHECK( camera ); + + CameraActor camera2 = view.GetCamera( -1 ); + DALI_TEST_CHECK( !camera2 ); + + CameraActor camera3 = view.GetCamera( 0 ); + DALI_TEST_CHECK( camera3 ); + + CameraActor camera4 = view.GetCamera( view.GetCameraCount() - 1 ); + DALI_TEST_CHECK( camera4 ); + + END_TEST; +} + +int UtcDaliScene3dViewAnimations(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScene3dViewAnimations"); + + Toolkit::Scene3dView view = Toolkit::Scene3dView::New( TEST_GLTF_FILE_NAME ); + + bool playAnimation = view.PlayAnimations(); + DALI_TEST_CHECK( playAnimation ); + + END_TEST; +} + +int UtcDaliScene3dViewAnimations2(void) +{ + ToolkitTestApplication application; + tet_infoline(" UtcDaliScene3dViewAnimations2"); + + Toolkit::Scene3dView view = Toolkit::Scene3dView::New( TEST_GLTF_FILE_NAME ); + + bool animated = true; + unsigned int animationCount = view.GetAnimationCount(); + for( unsigned int i = 0; i < animationCount; ++i ) + { + animated = ( animated && view.PlayAnimation( i ) ); + } + DALI_TEST_CHECK( animated ); + + END_TEST; +} diff --git a/build/tizen/dali-toolkit/Makefile.am b/build/tizen/dali-toolkit/Makefile.am index 62fa7ae..3d77bb8 100644 --- a/build/tizen/dali-toolkit/Makefile.am +++ b/build/tizen/dali-toolkit/Makefile.am @@ -142,6 +142,7 @@ develapimagnifierdir = $(develapicontrolsdir)/magnifier develapinavigationviewdir = $(develapicontrolsdir)/navigation-view develapipageturnviewdir = $(develapicontrolsdir)/page-turn-view develapipopupdir = $(develapicontrolsdir)/popup +develapiscene3dviewdir = $(develapicontrolsdir)/scene3d-view develapishadowviewdir = $(develapicontrolsdir)/shadow-view develapisuperblurviewdir = $(develapicontrolsdir)/super-blur-view develapiwebviewdir = $(develapicontrolsdir)/web-view @@ -180,6 +181,7 @@ develapipopup_HEADERS = $(devel_api_popup_header_files) develapivisualfactory_HEADERS = $(devel_api_visual_factory_header_files) develapivisuals_HEADERS = $(devel_api_visuals_header_files) develapiscripting_HEADERS = $(devel_api_scripting_header_files) +develapiscene3dview_HEADERS = $(devel_api_scene3d_view_header_files) develapishadowview_HEADERS = $(devel_api_shadow_view_header_files) develapishadereffects_HEADERS = $(devel_api_shader_effects_header_files) develapistyling_HEADERS = $(devel_api_styling_header_files) diff --git a/dali-toolkit/devel-api/controls/scene3d-view/scene3d-view.cpp b/dali-toolkit/devel-api/controls/scene3d-view/scene3d-view.cpp new file mode 100644 index 0000000..605d146 --- /dev/null +++ b/dali-toolkit/devel-api/controls/scene3d-view/scene3d-view.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018 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. + * + */ + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +Scene3dView::Scene3dView() +{ +} + +Scene3dView::~Scene3dView() +{ +} + +Scene3dView::Scene3dView( const Scene3dView& handle ) + : Control( handle ) +{ +} + +Scene3dView& Scene3dView::operator=( const Scene3dView& handle ) +{ + BaseHandle::operator=( handle ); + return *this; +} + +Scene3dView Scene3dView::New( const std::string& filePath ) +{ + return Internal::Scene3dView::New( filePath ); +} + +Scene3dView Scene3dView::New( const std::string& filePath, const std::string& diffuseTexturePath, const std::string& specularTexturePath, Vector4 scaleFactor ) +{ + return Internal::Scene3dView::New( filePath, diffuseTexturePath, specularTexturePath, scaleFactor ); +} + +Scene3dView::Scene3dView( Internal::Scene3dView& implementation ) + : Control( implementation ) +{ +} + +Scene3dView::Scene3dView( Dali::Internal::CustomActor* internal ) + : Control( internal ) +{ + VerifyCustomActorPointer( internal ); +} + +Scene3dView Scene3dView::DownCast( BaseHandle handle ) +{ + return Control::DownCast( handle ); +} + +uint32_t Scene3dView::GetAnimationCount() +{ + return GetImpl( *this ).GetAnimationCount(); +} + +bool Scene3dView::PlayAnimation( uint32_t index ) +{ + return GetImpl( *this ).PlayAnimation( index ); +} + +bool Scene3dView::PlayAnimations() +{ + return GetImpl( *this ).PlayAnimations(); +} + +bool Scene3dView::SetLight( LightType type, Vector3 lightVector, Vector3 lightColor ) +{ + return GetImpl( *this ).SetLight( type, lightVector, lightColor ); +} + +CameraActor Scene3dView::GetDefaultCamera() +{ + return GetImpl( *this ).GetDefaultCamera(); +} + +uint32_t Scene3dView::GetCameraCount() +{ + return GetImpl( *this ).GetCameraCount(); +} + +CameraActor Scene3dView::GetCamera( uint32_t cameraIndex ) +{ + return GetImpl( *this ).GetCamera( cameraIndex ); +} + +}//namespace Toolkit + +}//namespace Dali + diff --git a/dali-toolkit/devel-api/controls/scene3d-view/scene3d-view.h b/dali-toolkit/devel-api/controls/scene3d-view/scene3d-view.h new file mode 100644 index 0000000..0e6d6b1 --- /dev/null +++ b/dali-toolkit/devel-api/controls/scene3d-view/scene3d-view.h @@ -0,0 +1,210 @@ +#ifndef DALI_TOOLKIT_SCENE3D_VIEW_H +#define DALI_TOOLKIT_SCENE3D_VIEW_H + +/* + * Copyright (c) 2018 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. + * + */ + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ + +/** + * Scene3dView implementation class + */ +class Scene3dView; + +} + +/** + * + * Scene3dView is a class for containing scene elements loaded from scene format file(e.g., glTF). Scene elements mean scene graph, cameras, and animations. + * + * Basic idea:- + * + * 1) The Scene3dView is initialized with diffuse and specular cube map for the Image Based Lighting.\n + * If the Scene3dView initialized without cube map, the objects of the Scene3dView cannot be rendered with IBL.\n + * 2) The Scene3dView is loaded from each scene format file(e.g., glTF).\n + * 3) The Scene3dView can have a point light or a directional light.(optional)\n + * 4) The Scene3dView playes each actor's animation.\n + * + * + * Usage example: - + * + * @code + * + * void Scene3dViewExample::Create( Application& application ) + * { + * // Use 'Scene3dView::New( URL_SCENE_FILE )', if you don't want to render with IBL. + * Scene3dView scene3dView = Scene3dView::New( URL_SCENE_FILE, URL_DIFFUSE_TEXTURE, URL_SPECULAR_TEXTURE ); + * + * Stage::GetCurrent().Add( scene3dView ); + * scene3dView.PlayAnimations(); + * + * scene3dView.SetLight( Scene3dView::LightType::DIRECTIONAL_LIGHT, Vector3( 1.0, 1.0, -1.0 ), Vector3( 0.3, 0.3, 0.3 ) ); + * } + * + * @endcode + * + * @remarks This control makes 3D Layer internally. Therefore, if any 2D UI + * control is added as a child of this Scene3dView, the functionality of the 2D UI + * may not work well. + */ + +class DALI_TOOLKIT_API Scene3dView : public Control +{ +public: + + enum LightType + { + // Scene doesn't use both of point and directional light + NONE = 0, + // Scene use point light + POINT_LIGHT, + // Scene use directional light + DIRECTIONAL_LIGHT, + // Scene use Image Based Lighting + IMAGE_BASED_LIGHT, + // Scene use Image Based Lighting and point light + IMAGE_BASED_LIGHT_AND_POINT_LIGHT, + // Scene use Image Based Lighting and directional light + IMAGE_BASED_LIGHT_AND_DIRECTIONAL_LIGHT + }; + + /** + * @brief Create an uninitialized Scene3dView; this can be initialized with Scene3dView::New() + * Calling member functions with an uninitialized Dali::Object is not allowed. + */ + Scene3dView(); + + /** + * @brief Copy constructor. Creates another handle that points to the same real object + */ + Scene3dView( const Scene3dView& handle ); + + /** + * @brief Assignment operator. Changes this handle to point to another real object + */ + Scene3dView& operator=( const Scene3dView& handle ); + + /** + * @brief Destructor + * This is non-virtual since derived Handle types must not contain data or virtual methods. + */ + ~Scene3dView(); + + /** + * @brief Downcast an Object handle to Scene3dView. If handle points to a Scene3dView the + * downcast produces valid handle. If not the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return handle to a Scene3dView or an uninitialized handle + */ + static Scene3dView DownCast( BaseHandle handle ); + + /** + * @brief Create an initialized Scene3dView. + * @param[in] filePath File path of scene format file (e.g., glTF). + * @return A handle to a newly allocated Dali resource + */ + static Scene3dView New( const std::string& filePath ); + + /** + * @brief Create an initialized Scene3dView. + * @param[in] filePath File path of scene format file (e.g., glTF). + * @param[in] diffuseTexturePath The texture path of diffuse cube map that used to render with Image Based Lighting. + * @param[in] specularTexturePath The texture path of specular cube map that used to render with Image Based Lighting. + * @param[in] scaleFactor Scaling factor for the Image Based Lighting. + * @return A handle to a newly allocated Dali resource + */ + static Scene3dView New( const std::string& filePath, const std::string& diffuseTexturePath, const std::string& specularTexturePath, Vector4 scaleFactor ); + + /** + * @brief Get animation count. + * @return number of animations. + */ + uint32_t GetAnimationCount(); + + /** + * @brief Play an animation. + * @param[in] index Animation index + * @return true if animation is played. + */ + bool PlayAnimation( uint32_t index ); + + /** + * @brief Play all animations. + * @return true if animations are played. + */ + bool PlayAnimations(); + + /** + * @brief Set point light or directional light. If SetLight is not called, this scene doesn't use these kind of light. + * @param[in] type The light type. If the light is point light set this LightType::POINT_LIGHT, + * or if the light is directional light set this LightType::DIRECTIONAL_LIGHT. + * @param[in] lightVector The point light position when light type is LightType::POINT_LIGHT. + * The light direction when light type is LightType::DIRECTIONAL_LIGHT. + * @param[in] lightColor Vector3 value that denotes the light color of point light or directional light. Since this is the light color, we don't need to use alpha value. + * @return true if point light or directional light is set. + */ + bool SetLight( LightType type, Vector3 lightVector, Vector3 lightColor ); + + /** + * @brief Get default CameraActor. Dali::Camera::Type = Dali::Camera::LOOK_AT_TARGET , near clipping plane = 0.1, and camera position = Vector3( 0.0, 0.0, 0.0 ). + * @return CameraActor. + */ + CameraActor GetDefaultCamera(); + + /** + * @brief Get camera count. + * @return number of cameras. + */ + uint32_t GetCameraCount(); + + /** + * @brief Get CameraActor. If there is no CameraActor in the list, then returns default CameraActor. + * @param[in] cameraIndex Index of CameraActor list. + * @return CameraActor. + */ + CameraActor GetCamera( uint32_t cameraIndex ); + + // Not intended for developer use +public: + + /** + * @brief Creates a handle using the Toolkit::Internal implementation. + * @param[in] implementation The UI Control implementation. + */ + DALI_INTERNAL Scene3dView( Toolkit::Internal::Scene3dView& implementation ); + + explicit DALI_INTERNAL Scene3dView( Dali::Internal::CustomActor* internal ); +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif // DALI_TOOLKIT_SCENE3D_VIEW_H diff --git a/dali-toolkit/devel-api/file.list b/dali-toolkit/devel-api/file.list index 48e1214..3627396 100755 --- a/dali-toolkit/devel-api/file.list +++ b/dali-toolkit/devel-api/file.list @@ -19,6 +19,7 @@ devel_api_src_files = \ $(devel_api_src_dir)/controls/page-turn-view/page-turn-view.cpp \ $(devel_api_src_dir)/controls/popup/confirmation-popup.cpp \ $(devel_api_src_dir)/controls/popup/popup.cpp \ + $(devel_api_src_dir)/controls/scene3d-view/scene3d-view.cpp \ $(devel_api_src_dir)/controls/shadow-view/shadow-view.cpp \ $(devel_api_src_dir)/controls/super-blur-view/super-blur-view.cpp \ $(devel_api_src_dir)/controls/text-controls/text-editor-devel.cpp \ @@ -132,6 +133,9 @@ devel_api_visuals_header_files = \ $(devel_api_src_dir)/visuals/text-visual-properties-devel.h \ $(devel_api_src_dir)/visuals/visual-properties-devel.h +devel_api_scene3d_view_header_files = \ + $(devel_api_src_dir)/controls/scene3d-view/scene3d-view.h + devel_api_shadow_view_header_files = \ $(devel_api_src_dir)/controls/shadow-view/shadow-view.h diff --git a/dali-toolkit/internal/controls/scene3d-view/gltf-loader.cpp b/dali-toolkit/internal/controls/scene3d-view/gltf-loader.cpp new file mode 100644 index 0000000..4115fb7 --- /dev/null +++ b/dali-toolkit/internal/controls/scene3d-view/gltf-loader.cpp @@ -0,0 +1,1878 @@ +/* + * Copyright (c) 2018 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. + * + */ + +// CLASS HEADER +#include +#include + +// EXTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace Gltf +{ + +namespace +{ + +// Utility functions +const TreeNode* Tidx( const TreeNode *node, uint32_t index ) +{ + uint32_t i = 0; + for( auto it = node->CBegin(), end = node->CEnd(); it != end; ++it, ++i ) + { + if( i == index ) + { + return &( ( *it ).second ); + } + } + return NULL; +} + +bool ReadBool( const TreeNode* node, bool& num ) +{ + if( !node ) + { + return false; + } + bool returnValue = false; + + if( node->GetType() == TreeNode::BOOLEAN ) + { + num = node->GetBoolean(); + returnValue = true; + } + + return returnValue; +} + +bool ReadInt( const TreeNode* node, int32_t& num ) +{ + if( !node ) + { + return false; + } + bool returnValue = false; + if( node->GetType() == TreeNode::INTEGER ) + { + num = node->GetInteger(); + returnValue = true; + } + else if( node->GetType() == TreeNode::FLOAT ) + { + num = node->GetFloat(); + returnValue = true; + } + + return returnValue; +} + +bool ReadFloat( const TreeNode* node, float& num ) +{ + if( !node ) + { + return false; + } + bool returnValue = false; + + if( node->GetType() == TreeNode::FLOAT ) + { + num = node->GetFloat(); + returnValue = true; + } + else if( node->GetType() == TreeNode::INTEGER ) + { + int32_t tempNum; + ReadInt( node, tempNum ); + num = static_cast( tempNum ); + returnValue = true; + } + + return returnValue; +} + +bool ReadVector( const TreeNode* node, float* num, uint32_t size ) +{ + if( !node ) + { + return false; + } + bool returnValue = false; + + if( ( node->Size() >= size ) && ( node->GetType() == TreeNode::ARRAY ) ) + { + uint32_t offset = 0u; + for( auto it = node->CBegin(); offset < size; ++it, ++offset ) + { + const TreeNode& coord = ( *it ).second; + if( !ReadFloat( &coord, *( num + offset ) ) ) + { + return false; + } + } + returnValue = true; + } + + return returnValue; +} + +bool ReadString( const TreeNode* node, std::string& strValue ) +{ + if( !node ) + { + return false; + } + bool returnValue = false; + if( node->GetType() == TreeNode::STRING ) + { + strValue = node->GetString(); + returnValue = true; + } + return returnValue; +} + +template +float IntToFloat( T element, bool normalize ) +{ + if( !normalize ) + { + return static_cast( element ); + } + + if( std::is_same::value ) + { + return std::max( static_cast( element ) / 127.0, -1.0 ); + } + if( std::is_same::value ) + { + return static_cast( element ) / 255.0; + } + if( std::is_same::value ) + { + return std::max( static_cast( element ) / 32767.0, -1.0 ); + } + if( std::is_same::value ) + { + return static_cast( element ) / 65535.0; + } + return -1.0; +} + +template +void FitBuffer( Dali::Vector& bufferDestination, Dali::Vector& bufferSource, int32_t bufferSize, int32_t elementNumOfByteStride, bool normalize ) +{ + bufferDestination.Resize( bufferSize ); + int32_t count = bufferSource.Size() / elementNumOfByteStride; + for( int32_t i = 0; i( bufferSource[i * elementNumOfByteStride] ); + } +} + +template +void FitBuffer( Dali::Vector& bufferDestination, Dali::Vector& bufferSource, int32_t bufferSize, int32_t elementNumOfByteStride, bool normalize ) +{ + bufferDestination.Resize( bufferSize ); + int32_t count = bufferSource.Size() / elementNumOfByteStride; + for( int32_t i = 0; i +void FitBuffer( Dali::Vector& bufferDestination, Dali::Vector& bufferSource, int32_t bufferSize, int32_t elementNumOfByteStride, bool normalize ) +{ + bufferDestination.Resize( bufferSize ); + int32_t count = bufferSource.Size() / elementNumOfByteStride; + for( int32_t i = 0; i +void FitBuffer( Dali::Vector& bufferDestination, Dali::Vector& bufferSource, int32_t bufferSize, int32_t elementNumOfByteStride, bool normalize ) +{ + bufferDestination.Resize( bufferSize ); + int32_t count = bufferSource.Size() / elementNumOfByteStride; + for( int32_t i = 0; i +bool ReadBinFile( Vector &dataBuffer, std::string url, int32_t offset, int32_t count ) +{ + dataBuffer.Resize( count ); + FILE* fp = fopen( url.c_str(), "rb" ); + if( fp == NULL ) + { + return false; + } + ssize_t result = -1; + if( !fseek( fp, offset, SEEK_SET ) ) + { + result = fread( &dataBuffer[0], sizeof( T ), count, fp ); + } + fclose( fp ); + + return ( result >= 0 ); +} + +template +void LoadDataFromAccessor( int32_t accessorIdx, Dali::Vector& bufferData, std::string path, std::vector& accessorArray, std::vector& bufferViewArray, std::vector& bufferArray ) +{ + AccessorInfo accessor = accessorArray[accessorIdx]; + BufferViewInfo bufferView = bufferViewArray[accessor.bufferView]; + std::string load_uri = bufferArray[bufferView.buffer].uri; + + // In the glTF 2.0 Specification, 5121 is UNSIGNED BYTE, 5123 is UNSIGNED SHORT + int32_t elementByteSize = ( accessor.componentType <= 5121 ) ? 1 : + ( ( accessor.componentType <= 5123 ) ? 2 : 4 ); + int32_t elementNum = 1; + if( accessor.type == "VEC2" ) + { + elementNum = 2; + } + else if( accessor.type == "VEC3" ) + { + elementNum = 3; + } + else if( accessor.type == "VEC4" || accessor.type == "MAT2" ) + { + elementNum = 4; + } + else if( accessor.type == "MAT3" ) + { + elementNum = 9; + } + else if( accessor.type == "MAT4" ) + { + elementNum = 16; + } + else + { + elementNum = 1; + } + int32_t elementNumOfByteStride = elementNum; + if( bufferView.byteStride > 0 ) + { + elementNumOfByteStride = bufferView.byteStride / elementByteSize; + } + + /** + * glTF 2.0 Specification + * Component Type + * 5120 : BYTE + * 5121 : UNSIGNED_BYTE + * 5122 : SHORT + * 5123 : UNSIGNED_SHORT + * 5125 : UNSIGNED_INT + * 5126 : FLOAT + */ + if( accessor.componentType == 5120 ) + { + Dali::Vector inputBufferData; + ReadBinFile( inputBufferData, path + load_uri, bufferView.byteOffset + accessor.byteOffset, elementNumOfByteStride * accessor.count ); + FitBuffer( bufferData, inputBufferData, accessor.count, elementNumOfByteStride, accessor.normalized ); + } + else if( accessor.componentType == 5121 ) + { + Dali::Vector inputBufferData; + ReadBinFile( inputBufferData, path + load_uri, bufferView.byteOffset + accessor.byteOffset, elementNumOfByteStride * accessor.count ); + FitBuffer( bufferData, inputBufferData, accessor.count, elementNumOfByteStride, accessor.normalized ); + } + else if( accessor.componentType == 5122 ) + { + Dali::Vector inputBufferData; + ReadBinFile( inputBufferData, path + load_uri, bufferView.byteOffset + accessor.byteOffset, elementNumOfByteStride * accessor.count ); + FitBuffer( bufferData, inputBufferData, accessor.count, elementNumOfByteStride, accessor.normalized ); + } + else if( accessor.componentType == 5123 ) + { + Dali::Vector inputBufferData; + ReadBinFile( inputBufferData, path + load_uri, bufferView.byteOffset + accessor.byteOffset, elementNumOfByteStride * accessor.count ); + FitBuffer( bufferData, inputBufferData, accessor.count, elementNumOfByteStride, accessor.normalized ); + } + else if( accessor.componentType == 5125 ) + { + Dali::Vector inputBufferData; + ReadBinFile( inputBufferData, path + load_uri, bufferView.byteOffset + accessor.byteOffset, elementNumOfByteStride * accessor.count ); + FitBuffer( bufferData, inputBufferData, accessor.count, elementNumOfByteStride, accessor.normalized ); + } + else if( accessor.componentType == 5126 ) + { + Dali::Vector inputBufferData; + ReadBinFile( inputBufferData, path + load_uri, bufferView.byteOffset + accessor.byteOffset, elementNumOfByteStride * accessor.count ); + FitBuffer( bufferData, inputBufferData, accessor.count, elementNumOfByteStride, accessor.normalized ); + } +} + +void SetMeshInfoAndCanonize( MeshInfo& meshInfo, Dali::Vector &vertexBufferData ) +{ + Vector3 pointMin( std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() ); + Vector3 pointMax( std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min() ); + for( auto&& data : vertexBufferData ) + { + pointMin.x = std::min( data.x, pointMin.x ); + pointMin.y = std::min( data.y, pointMin.y ); + pointMin.z = std::min( data.z, pointMin.z ); + + pointMax.x = std::max( data.x, pointMax.x ); + pointMax.y = std::max( data.y, pointMax.y ); + pointMax.z = std::max( data.z, pointMax.z ); + } + meshInfo.size = pointMax - pointMin; + meshInfo.pivot.x = ( -pointMin.x ) / ( pointMax.x - pointMin.x ); + meshInfo.pivot.y = ( -pointMin.y ) / ( pointMax.y - pointMin.y ); + meshInfo.pivot.z = ( -pointMin.z ) / ( pointMax.z - pointMin.z ); + + Vector3 center = meshInfo.size * 0.5 + pointMin; + for( auto&& data : vertexBufferData ) + { + data = data - center; + data.x = data.x / meshInfo.size.x; + data.y = data.y / meshInfo.size.y; + data.z = data.z / meshInfo.size.z; + } +} + +template +PropertyBuffer CreatePropertyBuffer( Vector bufferData, std::string map, int32_t type ) +{ + Property::Map positionMap; + positionMap[map] = type; + + PropertyBuffer propertyBuffer = PropertyBuffer::New( positionMap ); + propertyBuffer.SetData( bufferData.Begin(), bufferData.Count() ); + return propertyBuffer; +} + +void SetVertexBufferData( MeshInfo& meshInfo, std::string path, std::vector& accessorArray, std::vector& bufferViewArray, std::vector& bufferArray, int32_t accessorIdx, std::string map, int32_t type ) +{ + if( accessorIdx >= 0 ) + { + Dali::Vector bufferData; + LoadDataFromAccessor( accessorIdx, bufferData, path, accessorArray, bufferViewArray, bufferArray ); + SetMeshInfoAndCanonize( meshInfo, bufferData ); + + PropertyBuffer propertyBuffer = CreatePropertyBuffer( bufferData, map, type ); + meshInfo.geometry.AddVertexBuffer( propertyBuffer ); + } +} + +template +void SetAttributeBufferData( MeshInfo& meshInfo, std::string path, std::vector& accessorArray, std::vector& bufferViewArray, std::vector& bufferArray, int32_t accessorIdx, std::string map, int32_t type ) +{ + if( accessorIdx >= 0 ) + { + Dali::Vector bufferData; + LoadDataFromAccessor( accessorIdx, bufferData, path, accessorArray, bufferViewArray, bufferArray ); + + PropertyBuffer propertyBuffer = CreatePropertyBuffer( bufferData, map, type ); + meshInfo.geometry.AddVertexBuffer( propertyBuffer ); + } +} + +void SetIndexBuffersData( MeshInfo& meshInfo, std::string path, std::vector& accessorArray, std::vector& bufferViewArray, std::vector& bufferArray, int32_t indexIdx ) +{ + Dali::Vector indexBufferData; + LoadDataFromAccessor( indexIdx, indexBufferData, path, accessorArray, bufferViewArray, bufferArray ); + meshInfo.geometry.SetIndexBuffer( &indexBufferData[0], indexBufferData.Size() ); +} + +template +float LoadKeyFrames( const AnimationSamplerInfo& currentSampler, const Property::Index propIndex, KeyFrames& keyframes, std::string path, std::vector& accessorArray, std::vector& bufferViewArray, std::vector& bufferArray ) +{ + Dali::Vector inputBufferData; + Dali::Vector outputBufferData; + + LoadDataFromAccessor( currentSampler.input, inputBufferData, path, accessorArray, bufferViewArray, bufferArray ); + LoadDataFromAccessor( currentSampler.output, outputBufferData, path, accessorArray, bufferViewArray, bufferArray ); + + uint32_t keyframeNum = inputBufferData.Size(); + float lengthAnimation = inputBufferData[inputBufferData.Size() - 1]; + for( uint32_t i = 0; i < keyframeNum; i++ ) + { + if( propIndex == Dali::Actor::Property::ORIENTATION ) + { + Vector4 vectorOrientation( outputBufferData[i] ); + float vW = vectorOrientation.w; + vW = ( vW < 0.0f ) ? std::max( vW, -1.0f ) : std::min( vW, 1.0f ); + vectorOrientation.w = vW; + keyframes.Add( inputBufferData[i] / lengthAnimation, Quaternion( Vector4( vectorOrientation ) ) ); + } + else if( propIndex == Dali::Actor::Property::POSITION ) + { + keyframes.Add( inputBufferData[i] / lengthAnimation, Vector3( outputBufferData[i] ) ); + } + else if( propIndex == Dali::Actor::Property::SCALE ) + { + keyframes.Add( inputBufferData[i] / lengthAnimation, Vector3( outputBufferData[i] ) ); + } + } + return lengthAnimation; +} + +bool LoadBuffer( const TreeNode& buffer, std::vector& bufferArray ) +{ + BufferInfo bufferInfo; + + const TreeNode* uriNode = buffer.GetChild( "uri" ); + if( uriNode ) + { + ReadString( uriNode, bufferInfo.uri ); + } + + const TreeNode* byteLengthNode = buffer.GetChild( "byteLength" ); + if( byteLengthNode ) + { + ReadInt( byteLengthNode, bufferInfo.byteLength ); + if( bufferInfo.byteLength < 0 ) + { + return false; + } + } + + const TreeNode* nameNode = buffer.GetChild( "name" ); + if( nameNode ) + { + ReadString( nameNode, bufferInfo.name ); + } + + bufferArray.push_back( bufferInfo ); + + return true; +} + +bool LoadBufferView( const TreeNode& buffer, std::vector& bufferViewArray ) +{ + BufferViewInfo bufferViewInfo; + + const TreeNode* bufferNode = buffer.GetChild( "buffer" ); + if( bufferNode ) + { + ReadInt( bufferNode, bufferViewInfo.buffer ); + if( bufferViewInfo.buffer < 0 ) + { + return false; + } + } + + const TreeNode* byteOffsetNode = buffer.GetChild( "byteOffset" ); + if( byteOffsetNode ) + { + ReadInt( byteOffsetNode, bufferViewInfo.byteOffset ); + } + + const TreeNode* byteLengthNode = buffer.GetChild( "byteLength" ); + if( byteLengthNode ) + { + ReadInt( byteLengthNode, bufferViewInfo.byteLength ); + if( bufferViewInfo.byteLength < 0 ) + { + return false; + } + } + + const TreeNode* byteStrideNode = buffer.GetChild( "byteStride" ); + if( byteStrideNode ) + { + ReadInt( byteStrideNode, bufferViewInfo.byteStride ); + } + + const TreeNode* targetNode = buffer.GetChild( "target" ); + if( targetNode ) + { + ReadInt( targetNode, bufferViewInfo.target ); + } + + const TreeNode* nameNode = buffer.GetChild( "name" ); + if( nameNode ) + { + ReadString( nameNode, bufferViewInfo.name ); + } + + bufferViewArray.push_back( bufferViewInfo ); + + return true; +} + +bool LoadAccessor( const TreeNode& buffer, std::vector& accessorArray ) +{ + AccessorInfo accessorInfo; + + const TreeNode* bufferViewNode = buffer.GetChild( "bufferView" ); + if( bufferViewNode ) + { + ReadInt( bufferViewNode, accessorInfo.bufferView ); + } + + const TreeNode* byteOffsetNode = buffer.GetChild( "byteOffset" ); + if( byteOffsetNode ) + { + ReadInt( byteOffsetNode, accessorInfo.byteOffset ); + } + + const TreeNode* componentTypeNode = buffer.GetChild( "componentType" ); + if( componentTypeNode ) + { + ReadInt( componentTypeNode, accessorInfo.componentType ); + if( accessorInfo.componentType < 0 ) + { + return false; + } + } + + const TreeNode* normalizedNode = buffer.GetChild( "normalized" ); + if( normalizedNode ) + { + ReadBool( normalizedNode, accessorInfo.normalized ); + } + + const TreeNode* countNode = buffer.GetChild( "count" ); + if( countNode ) + { + ReadInt( countNode, accessorInfo.count ); + if( accessorInfo.count < 0 ) + { + return false; + } + } + + const TreeNode* typeNode = buffer.GetChild( "type" ); + if( typeNode ) + { + ReadString( typeNode, accessorInfo.type ); + if( accessorInfo.type == "" ) + { + return false; + } + } + + const TreeNode* maxNode = buffer.GetChild( "max" ); + if( maxNode ) + { + ReadInt( maxNode, accessorInfo.max ); + } + + const TreeNode* minNode = buffer.GetChild( "min" ); + if( minNode ) + { + ReadInt( minNode, accessorInfo.min ); + } + + const TreeNode* nameNode = buffer.GetChild( "name" ); + if( nameNode ) + { + ReadString( nameNode, accessorInfo.name ); + } + + accessorArray.push_back( accessorInfo ); + + return true; +} + +bool LoadBinaryData( const TreeNode& root, std::vector& bufferArray, std::vector& bufferViewArray, std::vector& accessorArray ) +{ + const TreeNode* buffersNode = root.GetChild( "buffers" ); + if( !buffersNode ) + { + return false; + } + for( auto bufferIter = buffersNode->CBegin(), end = buffersNode->CEnd(); bufferIter != end; ++bufferIter ) + { + LoadBuffer( ( *bufferIter ).second, bufferArray ); + } + + const TreeNode* bufferViewsNode = root.GetChild( "bufferViews" ); + if( !bufferViewsNode ) + { + return false; + } + for( auto bufferViewIter = bufferViewsNode->CBegin(), end = bufferViewsNode->CEnd(); bufferViewIter != end; ++bufferViewIter ) + { + LoadBufferView( ( *bufferViewIter ).second, bufferViewArray ); + } + + const TreeNode* accessorsNode = root.GetChild( "accessors" ); + if( !accessorsNode ) + { + return false; + } + for( auto accesorIter = accessorsNode->CBegin(), end = accessorsNode->CEnd(); accesorIter != end; ++accesorIter ) + { + LoadAccessor( ( *accesorIter ).second, accessorArray ); + } + + return true; +} + +FilterMode::Type GetFilterMode( uint32_t mode ) +{ + FilterMode::Type retValue = FilterMode::DEFAULT; + /** + * glTF 2.0 Specification + * Filter Code + * 9728 : NEAREST + * 9729 : LINEAR + * 9984 : NEAREST_MIPMAP_NEAREST + * 9985 : LINEAR_MIPMAP_NEAREST + * 9986 : NEAREST_MIPMAP_LINEAR + * 9987 : LINEAR_MIPMAP_LINEAR + */ + switch( mode ) + { + case 9728: + { + retValue = FilterMode::NEAREST; + break; + } + case 9729: + { + retValue = FilterMode::LINEAR; + break; + } + case 9984: + { + retValue = FilterMode::NEAREST_MIPMAP_NEAREST; + break; + } + case 9985: + { + retValue = FilterMode::LINEAR_MIPMAP_NEAREST; + break; + } + case 9986: + { + retValue = FilterMode::NEAREST_MIPMAP_LINEAR; + break; + } + case 9987: + { + retValue = FilterMode::LINEAR_MIPMAP_LINEAR; + break; + } + } + + return retValue; +} + +WrapMode::Type GetWrapMode( uint32_t mode ) +{ + WrapMode::Type retValue = WrapMode::REPEAT; + /** + * glTF 2.0 Specification + * Wrapping mode Code + * 33071 : CLAMP_TO_EDGE + * 33648 : MIRRORED_REPEAT + * 10497 : REPEAT + */ + switch( mode ) + { + case 33071: + { + retValue = WrapMode::CLAMP_TO_EDGE; + break; + } + case 33648: + { + retValue = WrapMode::MIRRORED_REPEAT; + break; + } + case 10497: + { + retValue = WrapMode::REPEAT; + break; + } + } + + return retValue; +} + +Texture LoadTexture( const char* imageUrl, bool generateMipmaps ) +{ + Texture texture; + Devel::PixelBuffer pixelBuffer = LoadImageFromFile( imageUrl ); + if( pixelBuffer ) + { + texture = Texture::New( TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight() ); + PixelData pixelData = Devel::PixelBuffer::Convert( pixelBuffer ); + texture.Upload( pixelData ); + + if( generateMipmaps ) + { + texture.GenerateMipmaps(); + } + } + + return texture; +} + +Sampler LoadSampler( const TreeNode& samplerNode ) +{ + Sampler sampler = Sampler::New(); + + FilterMode::Type minFilter = FilterMode::DEFAULT; + FilterMode::Type magFilter = FilterMode::DEFAULT; + const TreeNode* magFilterNode = samplerNode.GetChild( "magFilter" ); + if( magFilterNode ) + { + int32_t magFilter_integer = 0; + ReadInt( magFilterNode, magFilter_integer ); + magFilter = GetFilterMode( magFilter_integer ); + } + + const TreeNode* minFilterNode = samplerNode.GetChild( "minFilter" ); + if( minFilterNode ) + { + int32_t minFilter_integer = 0; + ReadInt( minFilterNode, minFilter_integer ); + minFilter = GetFilterMode( minFilter_integer ); + } + + WrapMode::Type wrapR = WrapMode::REPEAT; + WrapMode::Type wrapS = WrapMode::REPEAT; + WrapMode::Type wrapT = WrapMode::REPEAT; + const TreeNode* wrapNode = samplerNode.GetChild( "wrapS" ); + if( wrapNode ) + { + wrapS = GetWrapMode( wrapNode->GetInteger() ); + } + + wrapNode = samplerNode.GetChild( "wrapT" ); + if( wrapNode ) + { + wrapT = GetWrapMode( wrapNode->GetInteger() ); + } + + sampler.SetFilterMode( minFilter, magFilter ); + sampler.SetWrapMode( wrapR, wrapS, wrapT ); + + return sampler; +} + +bool LoadTextureArray( const TreeNode& root, std::string path, std::vector& sourceArray, std::vector& samplerArray, std::vector& textureArray ) +{ + const TreeNode* imagesNode = root.GetChild( "images" ); + if( imagesNode ) + { + for( auto imageIter = imagesNode->CBegin(), end = imagesNode->CEnd(); imageIter != end; ++imageIter ) + { + std::string imageUrl; + const TreeNode* uriNode = ( &( ( *imageIter ).second ) )->GetChild( "uri" ); + if( uriNode ) + { + std::string uri; + ReadString( uriNode, uri ); + imageUrl = path + uri; + } + + sourceArray.push_back( LoadTexture( imageUrl.c_str(), true ) ); + } + } + + const TreeNode* samplersNode = root.GetChild( "samplers" ); + if( samplersNode ) + { + for( auto samplerIter = samplersNode->CBegin(), end = samplersNode->CEnd(); samplerIter != end; ++samplerIter ) + { + samplerArray.push_back( LoadSampler( ( ( *samplerIter ).second ) ) ); + } + } + + const TreeNode* texturesNode = root.GetChild( "textures" ); + if( texturesNode ) + { + for( auto textureIter = texturesNode->CBegin(), end = texturesNode->CEnd(); textureIter != end; ++textureIter ) + { + const TreeNode* TextureNode = &( ( *textureIter ).second ); + + TextureInfo texture; + const TreeNode* sourceNode = TextureNode->GetChild( "source" ); + if( sourceNode ) + { + ReadInt( sourceNode, texture.sourceIdx ); + } + + const TreeNode* samplerNode = TextureNode->GetChild( "sampler" ); + if( samplerNode ) + { + ReadInt( samplerNode, texture.samplerIdx ); + } + + textureArray.push_back( texture ); + } + } + return true; +} + +bool LoadPbrMetallicRoughness( const TreeNode& material, MaterialInfo& materialInfo ) +{ + float floatVec[4]; + const TreeNode* pbrMetallicRoughnessNode = material.GetChild( "pbrMetallicRoughness" ); + if( !pbrMetallicRoughnessNode ) + { + return true; + } + + const TreeNode* tempNode; + tempNode = pbrMetallicRoughnessNode->GetChild( "metallicFactor" ); + if( tempNode ) + { + ReadFloat( tempNode, materialInfo.metallicFactor ); + } + + tempNode = pbrMetallicRoughnessNode->GetChild( "roughnessFactor" ); + if( tempNode ) + { + ReadFloat( tempNode, materialInfo.roughnessFactor ); + } + + tempNode = pbrMetallicRoughnessNode->GetChild( "baseColorFactor" ); + if( tempNode && ReadVector( tempNode, floatVec, 4 ) ) + { + materialInfo.baseColorFactor = Vector4( floatVec[0], floatVec[1], floatVec[2], floatVec[3] ); + } + + const TreeNode* baseColorTextureNode = pbrMetallicRoughnessNode->GetChild( "baseColorTexture" ); + if( baseColorTextureNode ) + { + tempNode = baseColorTextureNode->GetChild( "index" ); + if( tempNode ) + { + materialInfo.baseColorTexture.index = tempNode->GetInteger(); + } + + tempNode = baseColorTextureNode->GetChild( "texCoord" ); + if( tempNode ) + { + materialInfo.baseColorTexture.texCoord = tempNode->GetInteger(); + } + } + + const TreeNode* metallicRoughnessTextureNode = pbrMetallicRoughnessNode->GetChild( "metallicRoughnessTexture" ); + if( metallicRoughnessTextureNode ) + { + tempNode = metallicRoughnessTextureNode->GetChild( "index" ); + if( tempNode ) + { + materialInfo.metallicRoughnessTexture.index = tempNode->GetInteger(); + } + + tempNode = metallicRoughnessTextureNode->GetChild( "texCoord" ); + if( tempNode ) + { + materialInfo.metallicRoughnessTexture.texCoord = tempNode->GetInteger(); + } + } + + return true; +} + +bool LoadMaterialSetArray( const TreeNode& root, std::vector& materialArray ) +{ + const TreeNode* materialsNode = root.GetChild( "materials" ); + if( !materialsNode ) + { + return false; + } + + for( auto materialIter = materialsNode->CBegin(), end = materialsNode->CEnd(); materialIter != end; ++materialIter ) + { + MaterialInfo materialInfo; + LoadPbrMetallicRoughness( ( ( *materialIter ).second ), materialInfo ); + + const TreeNode* materialNode = &( ( *materialIter ).second ); + const TreeNode* tempNode = materialNode->GetChild( "name" ); + if( tempNode ) + { + ReadString( tempNode, materialInfo.name ); + } + + materialInfo.alphaMode = "OPAQUE"; + tempNode = materialNode->GetChild( "alphaMode" ); + if( tempNode ) + { + ReadString( tempNode, materialInfo.alphaMode ); + } + + materialInfo.alphaCutoff = 1.0; + tempNode = materialNode->GetChild( "alphaCutoff" ); + if( tempNode ) + { + ReadFloat( tempNode, materialInfo.alphaCutoff ); + } + + materialInfo.doubleSided = false; + tempNode = materialNode->GetChild( "doubleSided" ); + if( tempNode ) + { + ReadBool( tempNode, materialInfo.doubleSided ); + } + + float floatVec[3]; + tempNode = materialNode->GetChild( "emissiveFactor" ); + if( tempNode && ReadVector( tempNode, floatVec, 3 ) ) + { + materialInfo.emissiveFactor = Vector3( floatVec[0], floatVec[1], floatVec[2] ); + } + + const TreeNode* texture = materialNode->GetChild( "normalTexture" ); + if( texture ) + { + tempNode = texture->GetChild( "index" ); + if( tempNode ) + { + materialInfo.normalTexture.index = tempNode->GetInteger(); + } + + tempNode = texture->GetChild( "texCoord" ); + if( tempNode ) + { + materialInfo.normalTexture.texCoord = tempNode->GetInteger(); + } + + materialInfo.normalTexture.value = 1.0; + tempNode = texture->GetChild( "scale" ); + if( tempNode ) + { + ReadFloat( tempNode, materialInfo.normalTexture.value ); + } + } + + texture = materialNode->GetChild( "occlusionTexture" ); + if( texture ) + { + tempNode = texture->GetChild( "index" ); + if( tempNode ) + { + materialInfo.occlusionTexture.index = tempNode->GetInteger(); + } + + tempNode = texture->GetChild( "texCoord" ); + if( tempNode ) + { + materialInfo.occlusionTexture.texCoord = tempNode->GetInteger(); + } + + + tempNode = texture->GetChild( "strength" ); + if( tempNode ) + { + ReadFloat( tempNode, materialInfo.occlusionTexture.value ); + } + } + + texture = materialNode->GetChild( "emissiveTexture" ); + if( texture ) + { + tempNode = texture->GetChild( "index" ); + if( tempNode ) + { + materialInfo.emissiveTexture.index = tempNode->GetInteger(); + } + + tempNode = texture->GetChild( "texCoord" ); + if( tempNode ) + { + materialInfo.emissiveTexture.texCoord = tempNode->GetInteger(); + } + } + materialArray.push_back( materialInfo ); + } + return true; +} + +bool LoadAttribute( const TreeNode* primitive, MeshInfo& meshInfo ) +{ + const TreeNode* attrbuteNode = primitive->GetChild( "attributes" ); + if( !attrbuteNode ) + { + return false; + } + + const TreeNode* tempNode; + tempNode = attrbuteNode->GetChild( "POSITION" ); + if( tempNode ) + { + meshInfo.attribute.POSITION = tempNode->GetInteger(); + } + + tempNode = attrbuteNode->GetChild( "NORMAL" ); + if( tempNode ) + { + meshInfo.attribute.NORMAL = tempNode->GetInteger(); + } + + tempNode = attrbuteNode->GetChild( "TANGENT" ); + if( tempNode ) + { + meshInfo.attribute.TANGENT = tempNode->GetInteger(); + } + + uint32_t index = 0; + meshInfo.attribute.TEXCOORD.clear(); + tempNode = attrbuteNode->GetChild( "TEXCOORD_" + std::to_string( index ) ); + while( tempNode ) + { + uint32_t value = tempNode->GetInteger(); + meshInfo.attribute.TEXCOORD.push_back( value ); + tempNode = attrbuteNode->GetChild( "TEXCOORD_" + std::to_string( ++index ) ); + } + + index = 0; + meshInfo.attribute.COLOR.clear(); + tempNode = attrbuteNode->GetChild( "COLOR_" + std::to_string( index ) ); + while( tempNode ) + { + uint32_t value = tempNode->GetInteger(); + meshInfo.attribute.COLOR.push_back( value ); + tempNode = attrbuteNode->GetChild( "COLOR" + std::to_string( ++index ) ); + } + + return true; +} + +bool LoadPrimitive( const TreeNode& mesh, MeshInfo& meshInfo ) +{ + const TreeNode* primitivesNode = mesh.GetChild( "primitives" ); + if( !primitivesNode ) + { + return false; + } + + for( auto primitiveIter = primitivesNode->CBegin(), end = primitivesNode->CEnd(); primitiveIter != end; ++primitiveIter ) + { + const TreeNode* primitiveNode = ( &( *primitiveIter ).second ); + const TreeNode* tempNode; + + tempNode = primitiveNode->GetChild( "indices" ); + if( tempNode ) + { + meshInfo.indicesIdx = tempNode->GetInteger(); + } + + tempNode = primitiveNode->GetChild( "material" ); + if( tempNode ) + { + meshInfo.materialsIdx = tempNode->GetInteger(); + } + + tempNode = primitiveNode->GetChild( "mode" ); + if( tempNode ) + { + meshInfo.mode = tempNode->GetInteger(); + } + + LoadAttribute( primitiveNode, meshInfo ); + } + + return true; +} + +bool SetGeometry( MeshInfo& meshInfo, std::string path, std::vector& bufferArray, std::vector& bufferViewArray, std::vector& accessorArray ) +{ + int32_t indicesIdx = meshInfo.indicesIdx; + + if( meshInfo.mode != 0 ) + { + meshInfo.geometry.SetType( ( Dali::Geometry::Type )meshInfo.mode ); + } + + if( indicesIdx >= 0 ) + { + SetIndexBuffersData( meshInfo, path, accessorArray, bufferViewArray, bufferArray, indicesIdx ); + } + + SetVertexBufferData( meshInfo, path, accessorArray, bufferViewArray, bufferArray, meshInfo.attribute.POSITION, "aPosition", Property::VECTOR3 ); + SetAttributeBufferData( meshInfo, path, accessorArray, bufferViewArray, bufferArray, meshInfo.attribute.NORMAL, "aNormal", Property::VECTOR3 ); + SetAttributeBufferData( meshInfo, path, accessorArray, bufferViewArray, bufferArray, meshInfo.attribute.TANGENT, "aTangent", Property::VECTOR4 ); + + for( uint32_t i = 0; i < meshInfo.attribute.TEXCOORD.size(); ++i ) + { + int32_t accessorIdx = meshInfo.attribute.TEXCOORD[i]; + std::ostringstream texCoordString; + texCoordString << "aTexCoord" << i; + SetAttributeBufferData( meshInfo, path, accessorArray, bufferViewArray, bufferArray, accessorIdx, texCoordString.str(), Property::VECTOR2 ); + } + + for( auto&& accessorIdx : meshInfo.attribute.COLOR ) + { + if( accessorIdx < 0 ) + { + break; + } + + if( accessorArray[accessorIdx].type == "VEC3" ) + { + Dali::Vector inputBufferData; + LoadDataFromAccessor( accessorIdx, inputBufferData, path, accessorArray, bufferViewArray, bufferArray ); + + Dali::Vector bufferData; + bufferData.Resize( inputBufferData.Size() ); + for( uint32_t i = 0; i( bufferData, "aVertexColor", Property::VECTOR4 ); + meshInfo.geometry.AddVertexBuffer( propertyBuffer ); + } + else if( accessorArray[accessorIdx].type == "VEC4" ) + { + SetAttributeBufferData( meshInfo, path, accessorArray, bufferViewArray, bufferArray, accessorIdx, "aVertexColor", Property::VECTOR4 ); + } + } + return true; +} + +bool LoadMeshArray( const TreeNode& root, std::string path, std::vector& meshArray, std::vector& bufferArray, std::vector& bufferViewArray, std::vector& accessorArray ) +{ + const TreeNode* meshesNode = root.GetChild( "meshes" ); + if( !meshesNode ) + { + return false; + } + + for( auto meshIter = meshesNode->CBegin(), end = meshesNode->CEnd(); meshIter != end; ++meshIter ) + { + MeshInfo meshInfo; + const TreeNode* nameNode = ( &( *meshIter ).second )->GetChild( "name" ); + if( nameNode ) + { + ReadString( nameNode, meshInfo.name ); + } + meshInfo.geometry = Geometry::New(); + + //Need to add weights for Morph targets. + LoadPrimitive( ( *meshIter ).second, meshInfo ); + SetGeometry( meshInfo, path, bufferArray, bufferViewArray, accessorArray ); + meshArray.push_back( meshInfo ); + } + + return true; +} + +} // namespace + +Loader::Loader() + : mNodes( NULL ), + mRoot( NULL ) +{ +} + +Loader::~Loader() +{ +} + +bool Loader::LoadScene( const std::string& filePath, Internal::Scene3dView& scene3dView ) +{ + // Extracting directory path from full path to load resources. + if( std::string::npos != filePath.rfind('/') ) + { + mPath = filePath.substr( 0, filePath.rfind('/') ) + "/"; + } + + if( !ParseGltf( filePath ) ) + { + DALI_LOG_ERROR( "Fail to parse json file\n" ); + return false; + } + + mRoot = mParser.GetRoot(); + if( mRoot && + LoadAssets() && + CreateScene( scene3dView ) ) + { + return true; + } + return false; +} + +bool Loader::ParseGltf( const std::string& filePath ) +{ + std::ifstream fileStream( filePath.c_str() ); + std::string fileBuffer( ( std::istreambuf_iterator( fileStream ) ), + ( std::istreambuf_iterator() ) ); + mParser = Dali::Toolkit::JsonParser::New(); + + return mParser.Parse( fileBuffer ); +} + +bool Loader::LoadAssets() +{ + if( LoadBinaryData( *mRoot, mBufferArray, mBufferViewArray, mAccessorArray ) && + LoadTextureArray( *mRoot, mPath, mSourceArray, mSamplerArray, mTextureArray ) && + LoadMaterialSetArray( *mRoot, mMaterialArray ) && + LoadMeshArray( *mRoot, mPath, mMeshArray, mBufferArray, mBufferViewArray, mAccessorArray ) + ) + { + return true; + } + return false; +} + +bool Loader::CreateScene( Internal::Scene3dView& scene3dView ) +{ + scene3dView.SetDefaultCamera( Dali::Camera::LOOK_AT_TARGET, 0.01, Vector3::ZERO ); + LoadCamera( scene3dView ); + + if( LoadSceneNodes( scene3dView ) && + LoadAnimation( scene3dView ) ) + { + return true; + } + return false; +} + +void Loader::LoadCamera( Scene3dView& scene3dView ) +{ + const TreeNode* camerasNode = mRoot->GetChild( "cameras" ); + if( !camerasNode ) + { + return; + } + + for( auto cameraIter = camerasNode->CBegin(), end = camerasNode->CEnd(); cameraIter != end; ++cameraIter ) + { + const TreeNode* tempNode = ( &( *cameraIter ).second )->GetChild( "name" ); + CameraInfo cameraInfo; + if( tempNode ) + { + ReadString( tempNode, cameraInfo.name ); + } + + tempNode = ( &( *cameraIter ).second )->GetChild( "type" ); + if( tempNode ) + { + ReadString( tempNode, cameraInfo.type ); + } + + CameraActor cameraActor = CameraActor::New(); + cameraActor.SetParentOrigin( ParentOrigin::CENTER ); + cameraActor.SetAnchorPoint( AnchorPoint::CENTER ); + + if( cameraInfo.type == "orthographic" ) + { + LoadOrthoGraphic( ( *cameraIter ).second, cameraInfo ); + float xMag_2 = cameraInfo.orthographic.xmag / 2.0; + float yMag_2 = cameraInfo.orthographic.ymag / 2.0; + cameraActor.SetOrthographicProjection( -xMag_2, xMag_2, yMag_2, -yMag_2, + cameraInfo.orthographic.znear, cameraInfo.orthographic.zfar ); + } + else if( cameraInfo.type == "perspective" ) + { + if( !LoadPerspective( ( *cameraIter ).second, cameraInfo ) ) + { + return; + } + cameraActor.SetProjectionMode( Dali::Camera::PERSPECTIVE_PROJECTION ); + cameraActor.SetFieldOfView( cameraInfo.perspective.yfov ); + cameraActor.SetNearClippingPlane( cameraInfo.perspective.znear ); + + if( cameraInfo.perspective.zfar > 0.0 ) + { + cameraActor.SetFarClippingPlane( cameraInfo.perspective.zfar ); + } + if( cameraInfo.perspective.aspectRatio > 0.0 ) + { + cameraActor.SetAspectRatio( cameraInfo.perspective.aspectRatio ); + } + } + + scene3dView.AddCamera( cameraActor ); + } +} + +bool Loader::LoadOrthoGraphic( const TreeNode& camera, CameraInfo& cameraInfo ) +{ + const TreeNode* orthographicNode = camera.GetChild( "orthographic" ); + if( !orthographicNode ) + { + return false; + } + + const TreeNode* tempNode; + tempNode = orthographicNode->GetChild( "xmag" ); + if( tempNode ) + { + ReadFloat( tempNode, cameraInfo.orthographic.xmag ); + } + + tempNode = orthographicNode->GetChild( "ymag" ); + if( tempNode ) + { + ReadFloat( tempNode, cameraInfo.orthographic.ymag ); + } + + tempNode = orthographicNode->GetChild( "zfar" ); + if( tempNode ) + { + ReadFloat( tempNode, cameraInfo.orthographic.zfar ); + } + + tempNode = orthographicNode->GetChild( "znear" ); + if( tempNode ) + { + ReadFloat( tempNode, cameraInfo.orthographic.znear ); + } + + return true; +} + +bool Loader::LoadPerspective( const TreeNode& camera, CameraInfo& cameraInfo ) +{ + const TreeNode* perspectiveNode = camera.GetChild( "perspective" ); + if( !perspectiveNode ) + { + return false; + } + + const TreeNode* tempNode; + tempNode = perspectiveNode->GetChild( "aspectRatio" ); + if( tempNode ) + { + ReadFloat( tempNode, cameraInfo.perspective.aspectRatio ); + } + + tempNode = perspectiveNode->GetChild( "yfov" ); + if( tempNode ) + { + ReadFloat( tempNode, cameraInfo.perspective.yfov ); + } + + tempNode = perspectiveNode->GetChild( "zfar" ); + if( tempNode ) + { + ReadFloat( tempNode, cameraInfo.perspective.zfar ); + } + + tempNode = perspectiveNode->GetChild( "znear" ); + if( tempNode ) + { + ReadFloat( tempNode, cameraInfo.perspective.znear ); + } + + return true; +} + +bool Loader::LoadSceneNodes( Scene3dView& scene3dView ) +{ + const TreeNode* sceneNode = mRoot->GetChild( "scene" ); + uint32_t sceneNum = 0; + if( sceneNode ) + { + sceneNum = sceneNode->GetInteger(); + } + + const TreeNode* scenesNode = mRoot->GetChild( "scenes" ); + if( !( scenesNode && ( mNodes = mRoot->GetChild( "nodes" ) ) ) ) + { + return false; + } + + const TreeNode* tempNode = Tidx( scenesNode, sceneNum ); + if( !tempNode ) + { + return false; + } + + tempNode = tempNode->GetChild( "nodes" ); + if( !tempNode ) + { + return false; + } + + for( auto nodeIter = tempNode->CBegin(), end = tempNode->CEnd(); nodeIter != end; ++nodeIter ) + { + Actor actor = AddNode( scene3dView, ( ( *nodeIter ).second ).GetInteger() ); + actor.SetParentOrigin( ParentOrigin::CENTER ); + scene3dView.GetRoot().Add( actor ); + } + + return true; +} + +Actor Loader::AddNode( Scene3dView& scene3dView, uint32_t index ) +{ + const TreeNode* node = Tidx( mNodes, index ); + Actor actor = Actor::New(); + Vector3 actorSize( Vector3::ONE ); + + Vector3 translation = Vector3( 0.0, 0.0, 0.0 ); + Vector3 scale = Vector3( 1.0, 1.0, 1.0 ); + Quaternion orientation( Vector4( 0.0, 0.0, 0.0, 1.0 ) ); + + Vector3 anchorPoint = AnchorPoint::CENTER; + + const TreeNode* tempNode = NULL; + if( ( tempNode = node->GetChild( "translation" ) ) ) + { + float floatVec[3] = { 0.0, 0.0, 0.0 }; + if( tempNode && ReadVector( tempNode, floatVec, 3 ) ) + { + translation = Vector3( floatVec[0], floatVec[1], floatVec[2] ); + } + } + + if( ( tempNode = node->GetChild( "scale" ) ) ) + { + float floatVec[3] = { 1.0, 1.0, 1.0 }; + if( tempNode && ReadVector( tempNode, floatVec, 3 ) ) + { + scale = Vector3( floatVec[0], floatVec[1], floatVec[2] ); + } + } + + if( ( tempNode = node->GetChild( "rotation" ) ) ) + { + float floatVec[4] = { 0.0, 0.0, 0.0, 1.0 }; + if( tempNode && ReadVector( tempNode, floatVec, 4 ) ) + { + orientation = Quaternion( Vector4( floatVec[0], floatVec[1], floatVec[2], floatVec[3] ) ); + } + } + + if( ( tempNode = node->GetChild( "matrix" ) ) ) + { + float floatVec[16] = { 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 }; + if( tempNode && ReadVector( tempNode, floatVec, 16 ) ) + { + Matrix nodeMatrix = Matrix( floatVec ); + nodeMatrix.GetTransformComponents( translation, orientation, scale ); + } + } + + if( ( tempNode = node->GetChild( "mesh" ) ) ) + { + MeshInfo meshInfo = mMeshArray[tempNode->GetInteger()]; + bool isMaterial = ( meshInfo.materialsIdx >= 0 ); + + TextureSet textureSet; + textureSet = TextureSet::New(); + + int32_t addIdx = 0; + int32_t shaderTypeIndex = 0; + int32_t maxMipmapLevel = 0; + bool isBaseColorTexture = false; + bool isMetallicRoughnessTexture = false; + bool isNormalTexture = false; + bool isOcclusionTexture = false; + bool isEmissiveTexture = false; + + std::string VERTEX_SHADER, FRAGMENT_SHADER; + VERTEX_SHADER = GLES_VERSION_300; + VERTEX_SHADER += PHYSICALLY_BASED_VERTEX_SHADER; + FRAGMENT_SHADER = GLES_VERSION_300; + + bool useIBL = ( scene3dView.GetLightType() >= Toolkit::Scene3dView::LightType::IMAGE_BASED_LIGHT ); + if( isMaterial ) + { + MaterialInfo materialInfo = mMaterialArray[meshInfo.materialsIdx]; + if( SetTextureAndSampler( textureSet, materialInfo.baseColorTexture.index, FRAGMENT_SHADER, DEFINE_BASECOLOR_TEXTURE, addIdx ) ) + { + shaderTypeIndex += static_cast( ShaderType::BASECOLOR_SHADER ); + isBaseColorTexture = true; + } + if( SetTextureAndSampler( textureSet, materialInfo.metallicRoughnessTexture.index, FRAGMENT_SHADER, DEFINE_METALLICROUGHNESS_TEXTURE, addIdx ) ) + { + shaderTypeIndex += static_cast( ShaderType::METALLICROUGHNESS_SHADER ); + isMetallicRoughnessTexture = true; + } + if( SetTextureAndSampler( textureSet, materialInfo.normalTexture.index, FRAGMENT_SHADER, DEFINE_NORMAL_TEXTURE, addIdx ) ) + { + shaderTypeIndex += static_cast( ShaderType::NORMAL_SHADER ); + isNormalTexture = true; + } + if( SetTextureAndSampler( textureSet, materialInfo.occlusionTexture.index, FRAGMENT_SHADER, DEFINE_OCCLUSION_TEXTURE, addIdx ) ) + { + shaderTypeIndex += static_cast( ShaderType::OCCLUSION_SHADER ); + isOcclusionTexture = true; + } + if( SetTextureAndSampler( textureSet, materialInfo.emissiveTexture.index, FRAGMENT_SHADER, DEFINE_EMIT_TEXTURE, addIdx ) ) + { + shaderTypeIndex += static_cast( ShaderType::EMIT_SHADER ); + isEmissiveTexture = true; + } + + if( useIBL ) + { + shaderTypeIndex += static_cast( ShaderType::IBL_SHADER ); + FRAGMENT_SHADER += DEFINE_IBL_TEXTURE; + + Sampler sampler = Sampler::New(); + sampler.SetFilterMode( FilterMode::DEFAULT, FilterMode::DEFAULT ); + sampler.SetWrapMode( WrapMode::REPEAT, WrapMode::REPEAT, WrapMode::REPEAT ); + + textureSet.SetTexture( addIdx, scene3dView.GetBRDFTexture() ); + textureSet.SetSampler( addIdx++, sampler ); + Sampler samplerIBL = Sampler::New(); + samplerIBL.SetFilterMode( FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR ); + samplerIBL.SetWrapMode( WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE ); + textureSet.SetTexture( addIdx, scene3dView.GetDiffuseTexture() ); + textureSet.SetSampler( addIdx++, samplerIBL ); + Texture specularTexture = scene3dView.GetSpecularTexture(); + textureSet.SetTexture( addIdx, specularTexture ); + textureSet.SetSampler( addIdx++, samplerIBL ); + + int32_t textureSize = std::min( specularTexture.GetWidth(), specularTexture.GetHeight() ); + maxMipmapLevel = 0; + while( textureSize >= 1 ) + { + maxMipmapLevel++; + textureSize /= 2; + } + } + } + + FRAGMENT_SHADER += PHYSICALLY_BASED_FRAGMENT_SHADER; + if( !mShaderCache[shaderTypeIndex] ) + { + mShaderCache[shaderTypeIndex] = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER ); + scene3dView.AddShader( mShaderCache[shaderTypeIndex] ); + } + Shader shader = mShaderCache[shaderTypeIndex]; + + Renderer renderer = Renderer::New( meshInfo.geometry, shader ); + renderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON ); + renderer.SetProperty( Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON ); + renderer.SetTextures( textureSet ); + + anchorPoint = meshInfo.pivot; + actor.SetAnchorPoint( anchorPoint ); + + actor.SetSize( Vector3( meshInfo.size.x, meshInfo.size.y, meshInfo.size.z ) ); + actor.AddRenderer( renderer ); + + actor.SetScale( scale ); + actor.RotateBy( orientation ); + actor.SetPosition( translation ); + + shader.RegisterProperty( "uLightType", ( scene3dView.GetLightType() & ~Toolkit::Scene3dView::LightType::IMAGE_BASED_LIGHT ) ); + shader.RegisterProperty( "uLightVector", scene3dView.GetLightVector() ); + shader.RegisterProperty( "uLightColor", scene3dView.GetLightColor() ); + + actor.RegisterProperty( "uIsColor", meshInfo.attribute.COLOR.size() > 0 ); + if( isMaterial ) + { + MaterialInfo materialInfo = mMaterialArray[meshInfo.materialsIdx]; + actor.RegisterProperty( "uBaseColorFactor", materialInfo.baseColorFactor ); + actor.RegisterProperty( "uMetallicRoughnessFactors", Vector2( materialInfo.metallicFactor, materialInfo.roughnessFactor ) ); + + if( materialInfo.alphaMode == "OPAQUE" ) + { + actor.RegisterProperty( "alphaMode", 0 ); + } + else if( materialInfo.alphaMode == "MASK" ) + { + actor.RegisterProperty( "alphaMode", 1 ); + } + else + { + actor.RegisterProperty( "alphaMode", 2 ); + } + actor.RegisterProperty( "alphaCutoff", materialInfo.alphaCutoff ); + + if( isBaseColorTexture ) + { + actor.RegisterProperty( "uBaseColorTexCoordIndex", materialInfo.baseColorTexture.texCoord ); + } + if( isMetallicRoughnessTexture ) + { + actor.RegisterProperty( "uMetallicRoughnessTexCoordIndex", materialInfo.metallicRoughnessTexture.texCoord ); + } + if( isNormalTexture ) + { + actor.RegisterProperty( "uNormalScale", materialInfo.normalTexture.value ); + actor.RegisterProperty( "uNormalTexCoordIndex", materialInfo.normalTexture.texCoord ); + } + if( isOcclusionTexture ) + { + actor.RegisterProperty( "uOcclusionTexCoordIndex", materialInfo.occlusionTexture.texCoord ); + actor.RegisterProperty( "uOcclusionStrength", materialInfo.occlusionTexture.value ); + } + if( isEmissiveTexture ) + { + actor.RegisterProperty( "uEmissiveTexCoordIndex", materialInfo.emissiveTexture.texCoord ); + actor.RegisterProperty( "uEmissiveFactor", materialInfo.emissiveFactor ); + } + } + + if( isMaterial && useIBL ) + { + actor.RegisterProperty( "uScaleIBLAmbient", scene3dView.GetIBLScaleFactor() ); + actor.RegisterProperty( "uMipmapLevel", static_cast( maxMipmapLevel ) ); + } + } + else + { + actor.SetAnchorPoint( AnchorPoint::CENTER ); + actor.SetPosition( translation ); + actor.RotateBy( orientation ); + actor.SetSize( actorSize ); + } + + tempNode = node->GetChild( "camera" ); + if( tempNode ) + { + int32_t cameraNum = tempNode->GetInteger(); + CameraActor cameraActor = scene3dView.GetCamera( cameraNum ); + if( cameraActor ) + { + actor.Add( cameraActor ); + } + } + + tempNode = node->GetChild( "name" ); + if( tempNode ) + { + std::string nameString; + ReadString( tempNode, nameString ); + actor.SetName( nameString ); + } + + SetActorCache( actor, index ); + if( ( tempNode = node->GetChild( "children" ) ) ) + { + for( auto childIter = tempNode->CBegin(), end = tempNode->CEnd(); childIter != end; ++childIter ) + { + Actor childActor = AddNode( scene3dView, ( ( *childIter ).second ).GetInteger() ); + childActor.SetParentOrigin( anchorPoint ); + actor.Add( childActor ); + } + } + + return actor; +} + +void Loader::SetActorCache( Actor& actor, uint32_t index ) +{ + if( mActorCache.size() < index + 1 ) + { + mActorCache.resize( index + 1 ); + } + mActorCache[index] = actor; +} + +bool Loader::SetTextureAndSampler( TextureSet& textureSet, int32_t textureIdx, std::string& toShader, std::string shader, int32_t& addIdx ) +{ + if( textureIdx >= 0 ) + { + toShader += shader; + TextureInfo textureInfo = mTextureArray[textureIdx]; + if( textureInfo.sourceIdx >= 0 ) + { + textureSet.SetTexture( addIdx, mSourceArray[textureInfo.sourceIdx] ); + } + if( textureInfo.samplerIdx >= 0 ) + { + textureSet.SetSampler( addIdx, mSamplerArray[textureInfo.samplerIdx] ); + } + else + { + Sampler sampler = Sampler::New(); + sampler.SetFilterMode( FilterMode::DEFAULT, FilterMode::DEFAULT ); + sampler.SetWrapMode( WrapMode::REPEAT, WrapMode::REPEAT, WrapMode::REPEAT ); + textureSet.SetSampler( addIdx, sampler ); + } + addIdx++; + return true; + } + return false; +} + +bool Loader::LoadAnimation( Scene3dView& scene3dView ) +{ + const TreeNode* animationsNode = mRoot->GetChild( "animations" ); + if( !animationsNode ) + { + return true; + } + + for( auto animationIter = animationsNode->CBegin(), end = animationsNode->CEnd(); animationIter != end; ++animationIter ) + { + const TreeNode* nameNode = ( &( *animationIter ).second )->GetChild( "name" ); + AnimationInfo animationInfo; + if( nameNode ) + { + ReadString( nameNode, animationInfo.name ); + } + + Property::Index propIndex = Property::INVALID_INDEX; + LoadAnimationChannels( ( *animationIter ).second, animationInfo ); + if( animationInfo.channelArray.size() == 0 ) + { + continue; + } + + LoadAnimationSamplers( ( *animationIter ).second, animationInfo ); + + for( auto&& currentChannel : animationInfo.channelArray ) + { + if( currentChannel.path == "rotation" ) + { + propIndex = Dali::Actor::Property::ORIENTATION; + } + else if( currentChannel.path == "translation" ) + { + propIndex = Dali::Actor::Property::POSITION; + } + else if( currentChannel.path == "scale" ) + { + propIndex = Dali::Actor::Property::SCALE; + } + + float duration = 0.0f; + KeyFrames keyframes = KeyFrames::New(); + if( propIndex == Dali::Actor::Property::ORIENTATION ) + { + duration = LoadKeyFrames( animationInfo.samplerArray[currentChannel.sampler], propIndex, keyframes, mPath, mAccessorArray, mBufferViewArray, mBufferArray ); + } + else + { + duration = LoadKeyFrames( animationInfo.samplerArray[currentChannel.sampler], propIndex, keyframes, mPath, mAccessorArray, mBufferViewArray, mBufferArray ); + } + + Animation animation = Animation::New( duration ); + Animation::Interpolation interpolation = Animation::Interpolation::Linear; + if( animationInfo.samplerArray[currentChannel.sampler].interpolation == "CUBICSPLINE" ) + { + interpolation = Animation::Interpolation::Cubic; + } + if( animationInfo.samplerArray[currentChannel.sampler].interpolation == "STEP" ) + { + } + + animation.AnimateBetween( Property( mActorCache[currentChannel.targetNode], propIndex ), keyframes, interpolation ); + + animation.SetLooping( false ); + scene3dView.AddAnimation( animation ); + } + } + + return true; +} + +bool Loader::LoadAnimationChannels( const TreeNode& animation, AnimationInfo& animationInfo ) +{ + const TreeNode* channelsNode = animation.GetChild( "channels" ); + if( !channelsNode ) + { + return false; + } + + for( auto channelIter = channelsNode->CBegin(), end = channelsNode->CEnd(); channelIter != end; ++channelIter ) + { + AnimationChannelInfo animationChannelInfo; + const TreeNode* channelNode = ( &( *channelIter ).second ); + const TreeNode* samplerNode = channelNode->GetChild( "sampler" ); + if( samplerNode ) + { + animationChannelInfo.sampler = samplerNode->GetInteger(); + } + + const TreeNode* targetNode = channelNode->GetChild( "target" ); + if( targetNode ) + { + const TreeNode* tempNode = targetNode->GetChild( "node" ); + if( tempNode ) + { + animationChannelInfo.targetNode = tempNode->GetInteger(); + } + else + { + continue; + } + + tempNode = targetNode->GetChild( "path" ); + if( tempNode ) + { + ReadString( tempNode, animationChannelInfo.path ); + } + } + + animationInfo.channelArray.push_back( animationChannelInfo ); + } + return true; +} + +bool Loader::LoadAnimationSamplers( const TreeNode& animation, AnimationInfo& animationInfo ) +{ + const TreeNode* samplersNode = animation.GetChild( "samplers" ); + if( !samplersNode ) + { + return false; + } + + for( auto sampler = samplersNode->CBegin(), end = samplersNode->CEnd(); sampler != end; ++sampler ) + { + AnimationSamplerInfo animationSamplerInfo; + const TreeNode* samplerNode = ( &( *sampler ).second ); + const TreeNode* tempNode = samplerNode->GetChild( "input" ); + if( tempNode ) + { + animationSamplerInfo.input = tempNode->GetInteger(); + } + + tempNode = samplerNode->GetChild( "output" ); + if( tempNode ) + { + animationSamplerInfo.output = tempNode->GetInteger(); + } + + tempNode = samplerNode->GetChild( "interpolation" ); + if( tempNode ) + { + ReadString( tempNode, animationSamplerInfo.interpolation ); + } + + animationInfo.samplerArray.push_back( animationSamplerInfo ); + } + + return true; +} + +}//namespace Gltf + +}//namespace Internal + +}//namespace Toolkit + +}//namespace Dali diff --git a/dali-toolkit/internal/controls/scene3d-view/gltf-loader.h b/dali-toolkit/internal/controls/scene3d-view/gltf-loader.h new file mode 100644 index 0000000..42ee4dd --- /dev/null +++ b/dali-toolkit/internal/controls/scene3d-view/gltf-loader.h @@ -0,0 +1,496 @@ +#ifndef DALI_TOOLKIT_INTERNAL_GLTF_LOADER_H +#define DALI_TOOLKIT_INTERNAL_GLTF_LOADER_H + +/* + * Copyright (c) 2018 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. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include + +using namespace Dali; + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace Gltf +{ + +enum ShaderType +{ + NO_TEXTURE_SHADER, + BASECOLOR_SHADER, + METALLICROUGHNESS_SHADER, + BASECOLOR_METALLICROUGHNESS_SHADER, + NORMAL_SHADER, + BASECOLOR_NORMAL_SHADER, + METALLICROUGHNESS_NORMAL_SHADER, + BASECOLOR_METALLICROUGHNESS_NORMAL_SHADER, + OCCLUSION_SHADER, + BASECOLOR_OCCLUSION_SHADER, + METALLICROUGHNESS_OCCLUSION_SHADER, + BASECOLOR_METALLICROUGHNESS_OCCLUSION_SHADER, + NORMAL_OCCLUSION_SHADER, + BASECOLOR_NORMAL_OCCLUSION_SHADER, + METALLICROUGHNESS_NORMAL_OCCLUSION_SHADER, + BASECOLOR_METALLICROUGHNESS_NORMAL_OCCLUSION_SHADER, + EMIT_SHADER, + BASECOLOR_EMIT_SHADER, + METALLICROUGHNESS_EMIT_SHADER, + BASECOLOR_METALLICROUGHNESS_EMIT_SHADER, + NORMAL_EMIT_SHADER, + BASECOLOR_NORMAL_EMIT_SHADER, + METALLICROUGHNESS_NORMAL_EMIT_SHADER, + BASECOLOR_METALLICROUGHNESS_NORMAL_EMIT_SHADER, + OCCLUSION_EMIT_SHADER, + BASECOLOR_OCCLUSION_EMIT_SHADER, + METALLICROUGHNESS_OCCLUSION_EMIT_SHADER, + BASECOLOR_METALLICROUGHNESS_OCCLUSION_EMIT_SHADER, + NORMAL_OCCLUSION_EMIT_SHADER, + BASECOLOR_NORMAL_OCCLUSION_EMIT_SHADER, + METALLICROUGHNESS_NORMAL_OCCLUSION_EMIT_SHADER, + BASECOLOR_METALLICROUGHNESS_NORMAL_OCCLUSION_EMIT_SHADER, + IBL_SHADER, + IBL_BASECOLOR_SHADER, + IBL_METALLICROUGHNESS_SHADER, + IBL_BASECOLOR_METALLICROUGHNESS_SHADER, + IBL_NORMAL_SHADER, + IBL_BASECOLOR_NORMAL_SHADER, + IBL_METALLICROUGHNESS_NORMAL_SHADER, + IBL_BASECOLOR_METALLICROUGHNESS_NORMAL_SHADER, + IBL_OCCLUSION_SHADER, + IBL_BASECOLOR_OCCLUSION_SHADER, + IBL_METALLICROUGHNESS_OCCLUSION_SHADER, + IBL_BASECOLOR_METALLICROUGHNESS_OCCLUSION_SHADER, + IBL_NORMAL_OCCLUSION_SHADER, + IBL_BASECOLOR_NORMAL_OCCLUSION_SHADER, + IBL_METALLICROUGHNESS_NORMAL_OCCLUSION_SHADER, + IBL_BASECOLOR_METALLICROUGHNESS_NORMAL_OCCLUSION_SHADER, + IBL_EMIT_SHADER, + IBL_BASECOLOR_EMIT_SHADER, + IBL_METALLICROUGHNESS_EMIT_SHADER, + IBL_BASECOLOR_METALLICROUGHNESS_EMIT_SHADER, + IBL_NORMAL_EMIT_SHADER, + IBL_BASECOLOR_NORMAL_EMIT_SHADER, + IBL_METALLICROUGHNESS_NORMAL_EMIT_SHADER, + IBL_BASECOLOR_METALLICROUGHNESS_NORMAL_EMIT_SHADER, + IBL_OCCLUSION_EMIT_SHADER, + IBL_BASECOLOR_OCCLUSION_EMIT_SHADER, + IBL_METALLICROUGHNESS_OCCLUSION_EMIT_SHADER, + IBL_BASECOLOR_METALLICROUGHNESS_OCCLUSION_EMIT_SHADER, + IBL_NORMAL_OCCLUSION_EMIT_SHADER, + IBL_BASECOLOR_NORMAL_OCCLUSION_EMIT_SHADER, + IBL_METALLICROUGHNESS_NORMAL_OCCLUSION_EMIT_SHADER, + IBL_BASECOLOR_METALLICROUGHNESS_NORMAL_OCCLUSION_EMIT_SHADER, + SHADER_TYPE_MAX = IBL_BASECOLOR_METALLICROUGHNESS_NORMAL_OCCLUSION_EMIT_SHADER +}; + +struct BufferInfo +{ + BufferInfo() + : byteLength( -1 ), + uri( "" ), + name( "" ) + { + } + + ~BufferInfo() + { + } + + int32_t byteLength; + std::string uri; + std::string name; +}; + +struct BufferViewInfo +{ + BufferViewInfo() + : buffer( -1 ), + byteOffset( 0 ), + byteLength( 0 ), + byteStride( 0 ), + target( 0 ), + name( "" ) + { + } + + ~BufferViewInfo() + { + } + + int32_t buffer; + int32_t byteOffset; + int32_t byteLength; + int32_t byteStride; + int32_t target; + std::string name; +}; + +struct TextureInfo +{ + TextureInfo() + : sourceIdx( -1 ), + samplerIdx( -1 ) + { + } + + ~TextureInfo() + { + } + int32_t sourceIdx; + int32_t samplerIdx; +}; + +struct PbrTextureInfo +{ + PbrTextureInfo() + : index( -1 ), + texCoord( 0 ), + value( 0.0 ) + { + } + ~PbrTextureInfo() + { + } + + int32_t index; + int32_t texCoord; + float value; +}; + +struct MaterialInfo +{ + MaterialInfo() + : baseColorFactor( 1, 1, 1, 1 ), + metallicFactor( 1.0 ), + roughnessFactor( 1.0 ), + emissiveFactor( 0.0, 0.0, 0.0 ), + alphaMode( "OPAQUE" ), + alphaCutoff( 0.5 ), + doubleSided( false ), + name( "" ) + { + } + + ~MaterialInfo() + { + } + + Vector4 baseColorFactor; + float metallicFactor; + float roughnessFactor; + Vector3 emissiveFactor; + std::string alphaMode; + float alphaCutoff; + bool doubleSided; + + PbrTextureInfo baseColorTexture; + PbrTextureInfo metallicRoughnessTexture; + PbrTextureInfo normalTexture; + PbrTextureInfo occlusionTexture; + PbrTextureInfo emissiveTexture; + + std::string name; + //need to add max, min +}; + +struct AccessorInfo +{ + AccessorInfo() + : bufferView( -1 ), + byteOffset( 0 ), + componentType( -1 ), + normalized( false ), + count( 0 ), + type( "" ), + max( 0 ), + min( 0 ), + name( "" ) + { + } + + ~AccessorInfo() + { + } + + int32_t bufferView; + int32_t byteOffset; + int32_t componentType; + bool normalized; + int32_t count; + std::string type; + int32_t max; + int32_t min; + std::string name; + //need to add max, min +}; + +struct Attribute +{ + Attribute() + : POSITION( -1 ), + NORMAL( -1 ), + TANGENT( -1 ) + { + } + + ~Attribute() + { + } + + int32_t POSITION; + int32_t NORMAL; + int32_t TANGENT; + + std::vector TEXCOORD; + std::vector COLOR; +}; + +struct MeshInfo +{ + MeshInfo() + : indicesIdx( -1 ), + materialsIdx( -1 ), + mode( 4 ) + { + } + + ~MeshInfo() + { + } + Geometry geometry; + std::string name; + + int32_t indicesIdx; + int32_t materialsIdx; + int32_t mode; + + Vector3 size; + Vector3 pivot; + + Attribute attribute; + //need to add max, min +}; + +struct AnimationChannelInfo +{ + AnimationChannelInfo() + : sampler( -1 ), + targetNode( -1 ), + path( "" ) + { + } + + ~AnimationChannelInfo() + { + } + + int32_t sampler; + int32_t targetNode; + std::string path; + +}; + +struct AnimationSamplerInfo +{ + AnimationSamplerInfo() + : input( -1 ), + output( -1 ), + interpolation( "" ) + { + } + + ~AnimationSamplerInfo() + { + } + + int32_t input; + int32_t output; + std::string interpolation; +}; + +struct AnimationInfo +{ + AnimationInfo() + : name( "" ) + { + } + + ~AnimationInfo() + { + } + + std::string name; + std::vector channelArray; + std::vector samplerArray; +}; + +struct OrthographicInfo +{ + OrthographicInfo() + : xmag( 0.0f ), + ymag( 0.0f ), + zfar( 0.0f ), + znear( 0.0f ) + { + } + + ~OrthographicInfo() + { + } + + float xmag; + float ymag; + float zfar; + float znear; +}; + +struct PerspectiveInfo +{ + PerspectiveInfo() + : aspectRatio( 0.0f ), + yfov( 0.0f ), + zfar( 0.0f ), + znear( 0.0f ) + { + } + + ~PerspectiveInfo() + { + } + + float aspectRatio; + float yfov; + float zfar; + float znear; +}; + +struct CameraInfo +{ + CameraInfo() + : name( "" ), + type( "" ) + { + } + + ~CameraInfo() + { + } + + std::string name; + std::string type; + OrthographicInfo orthographic; + PerspectiveInfo perspective; +}; + +/** + * + * Loader is a class to parse glTf, to load data from file, and to generate Scene. + * This glTF loader supports glTF 2.0 features. + * + * @remarks glTF loader didn't support such features. + * - Sparse accessor + * - Morphing + * - Skeletal Animation + * These features will be supported soon. + * + */ +class Loader +{ +public: + + /** + * @brief Create an uninitialized Loader. + */ + Loader(); + + /** + * @brief Destructor + */ + ~Loader(); + + /** + * @brief Load Scene3dView from scene format file(e.g., glTF). + * @param[in] filePath Path of scene format file. + * @param[in] scene3dView Scene3dView data loaded from file. + * @return true if scene is successfully loaded + */ + bool LoadScene( const std::string& filePath, Internal::Scene3dView& scene3dView ); + +private: + bool ParseGltf( const std::string& filePath ); + bool LoadAssets(); + + bool CreateScene( Internal::Scene3dView& scene3dView ); + + void LoadCamera( Scene3dView& scene3dView ); + bool LoadOrthoGraphic( const TreeNode& camera, CameraInfo& cameraInfo ); + bool LoadPerspective( const TreeNode& camera, CameraInfo& cameraInfo ); + + bool LoadSceneNodes( Scene3dView& scene3dView ); + Actor AddNode( Scene3dView& scene3dView, uint32_t index ); + void SetActorCache( Actor& actor, uint32_t index ); + bool SetTextureAndSampler( TextureSet& textureSet, int32_t textureIdx, std::string& toShader, std::string shader, int32_t& addIdx ); + + bool LoadAnimation( Scene3dView& scene3dView ); + bool LoadAnimationChannels( const TreeNode& animation, AnimationInfo& animationInfo ); + bool LoadAnimationSamplers( const TreeNode& animation, AnimationInfo& animationInfo ); + +private: + Dali::Toolkit::JsonParser mParser; + const TreeNode* mNodes; + const TreeNode* mRoot; + + std::string mPath; + + std::vector mActorCache; + Shader mShaderCache[ShaderType::SHADER_TYPE_MAX + 1]; + + std::vector mBufferArray; + std::vector mBufferViewArray; + std::vector mAccessorArray; + + std::vector mMeshArray; + std::vector mMaterialArray; + std::vector mTextureArray; + + std::vector mSourceArray; + std::vector mSamplerArray; +}; + +}//namespace Gltf + +}//namespace Internal + +}//namespace Toolkit + +}//namespace Dali + +#endif // DALI_TOOLKIT_INTERNAL_GLTF_LOADER_H diff --git a/dali-toolkit/internal/controls/scene3d-view/gltf-shader.h b/dali-toolkit/internal/controls/scene3d-view/gltf-shader.h new file mode 100644 index 0000000..e51b77c --- /dev/null +++ b/dali-toolkit/internal/controls/scene3d-view/gltf-shader.h @@ -0,0 +1,346 @@ +#ifndef DALI_TOOLKIT_INTERNAL_GLTF_SHADER_H +#define DALI_TOOLKIT_INTERNAL_GLTF_SHADER_H + +/* + * Belows Vertex Shader and Fragment Shader code are based off glTF WebGL PBR. + * https://github.com/KhronosGroup/glTF-WebGL-PBR/ + * + * Copyright (c) 2016-2017 Mohamad Moneimne and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +const char* GLES_VERSION_300 = { + "#version 300 es\n\n" + "precision highp float;\n\n" +}; + +const char* DEFINE_BASECOLOR_TEXTURE = { + "#define TEXTURE_BASECOLOR\n\n" + "uniform sampler2D uBaseColorSampler;\n" + "uniform int uBaseColorTexCoordIndex;\n\n" +}; + +const char* DEFINE_METALLICROUGHNESS_TEXTURE = { + "#define TEXTURE_METALLICROUGHNESS\n\n" + "uniform sampler2D uMetallicRoughnessSampler;\n" + "uniform int uMetallicRoughnessTexCoordIndex;\n\n" +}; + +const char* DEFINE_NORMAL_TEXTURE = { + "#define TEXTURE_NORMAL\n\n" + "uniform sampler2D uNormalSampler;\n" + "uniform float uNormalScale;\n" + "uniform int uNormalTexCoordIndex;\n\n" +}; + +const char* DEFINE_OCCLUSION_TEXTURE = { + "#define TEXTURE_OCCLUSION\n\n" + "uniform sampler2D uOcclusionSampler;\n" + "uniform int uOcclusionTexCoordIndex;\n" + "uniform float uOcclusionStrength;\n\n" +}; + +const char* DEFINE_EMIT_TEXTURE = { + "#define TEXTURE_EMIT\n\n" + "uniform sampler2D uEmissiveSampler;\n" + "uniform int uEmissiveTexCoordIndex;\n" + "uniform vec3 uEmissiveFactor;\n\n" +}; + +const char* DEFINE_IBL_TEXTURE = { + "#define TEXTURE_IBL\n\n" + "uniform sampler2D ubrdfLUT;\n" + "uniform samplerCube uDiffuseEnvSampler;\n" + "uniform samplerCube uSpecularEnvSampler;\n" + "uniform vec4 uScaleIBLAmbient;\n" + "uniform highp float uMipmapLevel;\n" +}; + +const char* PHYSICALLY_BASED_VERTEX_SHADER = { + "in highp vec3 aPosition;\n" + "in mediump vec2 aTexCoord0;\n" + "in mediump vec2 aTexCoord1;\n" + "in lowp vec3 aNormal;\n" + "in lowp vec4 aTangent;\n" + "in lowp vec4 aVertexColor;\n" + + "uniform mediump vec3 uSize;\n" + "uniform mediump mat4 uModelMatrix;\n" + "uniform mediump mat4 uViewMatrix;\n" + "uniform mediump mat4 uProjection;\n" + "uniform lowp int uLightType;\n" + "uniform mediump vec3 uLightVector;\n" + "uniform lowp int uIsColor;\n" + + "out lowp vec2 vUV[2];\n" + "out lowp mat3 vTBN;\n" + "out lowp vec4 vColor;\n" + "flat out int visLight;\n" + "out highp vec3 vLightDirection;\n" + "out highp vec3 vPositionToCamera;\n" + + "void main()\n" + "{\n" + " highp vec4 invY = vec4(1.0, -1.0, 1.0, 1.0);\n" + " highp vec4 positionW = uModelMatrix * vec4( aPosition * uSize, 1.0 );\n" + " highp vec4 positionV = uViewMatrix * ( invY * positionW );\n" + + " vPositionToCamera = transpose( mat3( uViewMatrix ) ) * ( -vec3( positionV.xyz / positionV.w ) );\n" + " vPositionToCamera *= invY.xyz;\n" + + " lowp vec3 bitangent = cross(aNormal, aTangent.xyz) * aTangent.w;\n" + " vTBN = mat3( uModelMatrix ) * mat3(aTangent.xyz, bitangent, aNormal);\n" + + " vUV[0] = aTexCoord0;\n" + " vUV[1] = aTexCoord1;\n" + + " visLight = 1;\n" + " if( uLightType == 1 )\n" + " {\n" + " vLightDirection = ( invY.xyz * uLightVector ) - ( positionW.xyz / positionW.w );\n" + " }\n" + " else if( uLightType == 2 )\n" + " {\n" + " vLightDirection = -( invY.xyz * uLightVector );\n" + " }\n" + " else\n" + " {\n" + " visLight = 0;\n" + " }\n" + + " vColor = vec4( 1.0 );\n" + " if( uIsColor == 1 )\n" + " {\n" + " vColor = aVertexColor;\n" + " }\n" + + " gl_Position = uProjection * positionV;\n" // needs w for proper perspective correction + " gl_Position = gl_Position/gl_Position.w;\n" + "}\n" +}; + +const char* PHYSICALLY_BASED_FRAGMENT_SHADER = { + "uniform lowp vec3 uLightColor;\n" + "uniform lowp vec4 uBaseColorFactor;\n" + "uniform lowp vec2 uMetallicRoughnessFactors;\n" + "uniform lowp int alphaMode;\n" + "uniform lowp float alphaCutoff;\n" + + "in lowp vec2 vUV[2];\n" + "in lowp mat3 vTBN;\n" + "in lowp vec4 vColor;\n" + "flat in int visLight;\n" + "in highp vec3 vLightDirection;\n" + "in highp vec3 vPositionToCamera;\n" + + "out vec4 FragColor;" + + "struct PBRInfo\n" + "{\n" + " mediump float NdotL;\n" // cos angle between normal and light direction + " mediump float NdotV;\n" // cos angle between normal and view direction + " mediump float NdotH;\n" // cos angle between normal and half vector + " mediump float VdotH;\n" // cos angle between view direction and half vector + " mediump vec3 reflectance0;\n" // full reflectance color (normal incidence angle) + " mediump vec3 reflectance90;\n" // reflectance color at grazing angle + " lowp float alphaRoughness;\n" // roughness mapped to a more linear change in the roughness (proposed by [2]) + "};\n" + + "const float M_PI = 3.141592653589793;\n" + "const float c_MinRoughness = 0.04;\n" + + "vec3 getNormal()\n" + "{\n" + "#ifdef TEXTURE_NORMAL\n" + " lowp vec3 n = texture( uNormalSampler, vUV[uNormalTexCoordIndex] ).rgb;\n" + " n = normalize( vTBN * ( ( 2.0 * n - 1.0 ) * vec3( uNormalScale, uNormalScale, 1.0 ) ) );\n" + "#else\n" + " lowp vec3 n = normalize( vTBN[2].xyz );\n" + "#endif\n" + " return n;\n" + "}\n" + + "vec3 specularReflection( PBRInfo pbrInputs )\n" + "{\n" + " return pbrInputs.reflectance0 + ( pbrInputs.reflectance90 - pbrInputs.reflectance0 ) * pow( clamp( 1.0 - pbrInputs.VdotH, 0.0, 1.0 ), 5.0 );\n" + "}\n" + + "float geometricOcclusion( PBRInfo pbrInputs )\n" + "{\n" + " mediump float NdotL = pbrInputs.NdotL;\n" + " mediump float NdotV = pbrInputs.NdotV;\n" + " lowp float r = pbrInputs.alphaRoughness;\n" + + " lowp float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));\n" + " lowp float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));\n" + " return attenuationL * attenuationV;\n" + "}\n" + + "float microfacetDistribution(PBRInfo pbrInputs)\n" + "{\n" + " mediump float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness;\n" + " lowp float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0;\n" + " return roughnessSq / (M_PI * f * f);\n" + "}\n" + + "vec3 linear( vec3 color )\n" + "{\n" + " return pow(color,vec3(2.2));\n" + "}\n" + + "void main()\n" + "{\n" + // Metallic and Roughness material properties are packed together + // In glTF, these factors can be specified by fixed scalar values + // or from a metallic-roughness map + " lowp float metallic = uMetallicRoughnessFactors.x;\n" + " lowp float perceptualRoughness = uMetallicRoughnessFactors.y;\n" + + // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel. + // This layout intentionally reserves the 'r' channel for (optional) occlusion map data + "#ifdef TEXTURE_METALLICROUGHNESS\n" + " lowp vec4 metrou = texture(uMetallicRoughnessSampler, vUV[uMetallicRoughnessTexCoordIndex]);\n" + " metallic = metrou.b * metallic;\n" + " perceptualRoughness = metrou.g * perceptualRoughness;\n" + "#endif\n" + + " metallic = clamp(metallic, 0.0, 1.0);\n" + " perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);\n" + // Roughness is authored as perceptual roughness; as is convention, + // convert to material roughness by squaring the perceptual roughness [2]. + " lowp float alphaRoughness = perceptualRoughness * perceptualRoughness;\n" + + "#ifdef TEXTURE_BASECOLOR\n" + // The albedo may be defined from a base texture or a flat color + " lowp vec4 baseColor = texture(uBaseColorSampler, vUV[uBaseColorTexCoordIndex]) * uBaseColorFactor;\n" + " baseColor = vec4(linear(baseColor.rgb), baseColor.w);\n" + "#else\n" + " lowp vec4 baseColor = vColor * uBaseColorFactor;\n" + "#endif\n" + + " if( alphaMode == 0 )\n" + " {\n" + " baseColor.w = 1.0;\n" + " }\n" + " else if( alphaMode == 1 )\n" + " {\n" + " if( baseColor.w >= alphaCutoff )" + " {\n" + " baseColor.w = 1.0;\n" + " }\n" + " else\n" + " {\n" + " baseColor.w = 0.0;\n" + " }\n" + " }\n" + + " lowp vec3 f0 = vec3(0.04);\n" + " lowp vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);\n" + " diffuseColor *= ( 1.0 - metallic );\n" + " lowp vec3 specularColor = mix(f0, baseColor.rgb, metallic);\n" + + // Compute reflectance. + " lowp float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);\n" + + // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect. + // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%. + " lowp float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);\n" + " lowp vec3 specularEnvironmentR0 = specularColor.rgb;\n" + " lowp vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;\n" + + " mediump vec3 n = getNormal();\n" // normal at surface point + " mediump vec3 v = normalize(vPositionToCamera);\n" // Vector from surface point to camera + " mediump vec3 l = normalize(vLightDirection);\n" // Vector from light to surface point + " mediump vec3 h = normalize(l+v);\n" // Half vector between both l and v + " mediump vec3 reflection = -normalize(reflect(v, n));\n" + + " mediump float NdotL = clamp(dot(n, l), 0.001, 1.0);\n" + " mediump float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);\n" + " mediump float NdotH = dot(n, h);\n" + " mediump float LdotH = dot(l, h);\n" + " mediump float VdotH = dot(v, h);\n" + + " PBRInfo pbrInputs = PBRInfo(\n" + " NdotL,\n" + " NdotV,\n" + " NdotH,\n" + " VdotH,\n" + " specularEnvironmentR0,\n" + " specularEnvironmentR90,\n" + " alphaRoughness\n" + " );\n" + + // Calculate the shading terms for the microfacet specular shading model + " lowp vec3 color = vec3(0.0);\n" + " if( visLight == 1 )\n" + " {\n" + " lowp vec3 F = specularReflection( pbrInputs );\n" + " lowp float G = geometricOcclusion( pbrInputs );\n" + " lowp float D = microfacetDistribution( pbrInputs );\n" + + // Calculation of analytical lighting contribution + " lowp vec3 diffuseContrib = ( 1.0 - F ) * ( diffuseColor / M_PI );\n" + " lowp vec3 specContrib = F * G * D / ( 4.0 * NdotL * NdotV );\n" + // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law) + " color = NdotL * uLightColor * (diffuseContrib + specContrib);\n" + " }\n" + + "#ifdef TEXTURE_IBL\n" + " lowp float lod = ( perceptualRoughness * uMipmapLevel );\n" + // retrieve a scale and bias to F0. See [1], Figure 3 + " lowp vec3 brdf = linear( texture( ubrdfLUT, vec2( NdotV, 1.0 - perceptualRoughness ) ).rgb );\n" + " lowp vec3 diffuseLight = linear( texture( uDiffuseEnvSampler, n ).rgb );\n" + " lowp vec3 specularLight = linear( textureLod( uSpecularEnvSampler, reflection, lod ).rgb );\n" + + " lowp vec3 diffuse = diffuseLight * diffuseColor * uScaleIBLAmbient.x;\n" + " lowp vec3 specular = specularLight * ( specularColor * brdf.x + brdf.y ) * uScaleIBLAmbient.y;\n" + " color += ( diffuse + specular );\n" + "#endif\n" + + "#ifdef TEXTURE_OCCLUSION\n" + " lowp float ao = texture( uOcclusionSampler, vUV[uOcclusionTexCoordIndex] ).r;\n" + " color = mix( color, color * ao, uOcclusionStrength );\n" + "#endif\n" + + "#ifdef TEXTURE_EMIT\n" + " lowp vec3 emissive = linear( texture( uEmissiveSampler, vUV[uEmissiveTexCoordIndex] ).rgb ) * uEmissiveFactor;\n" + " color += emissive;\n" + "#endif\n" + + " FragColor = vec4( pow( color,vec3( 1.0 / 2.2 ) ), baseColor.a );\n" + "}\n" +}; + +} // namespace internal + +} // namespace Toolkit + +} // namespace Dali + +#endif // DALI_TOOLKIT_INTERNAL_GLTF_SHADER_H \ No newline at end of file diff --git a/dali-toolkit/internal/controls/scene3d-view/scene3d-view-impl.cpp b/dali-toolkit/internal/controls/scene3d-view/scene3d-view-impl.cpp new file mode 100644 index 0000000..3ec462b --- /dev/null +++ b/dali-toolkit/internal/controls/scene3d-view/scene3d-view-impl.cpp @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2018 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. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + +// glTF file extension +const std::string GLTF_EXT( ".gltf" ); + +/** + * cube map face index + */ +const uint32_t CUBEMAP_INDEX_X[2][6] = { { 2, 0, 1, 1, 1, 3 }, { 0, 1, 2, 3, 4, 5 } }; +const uint32_t CUBEMAP_INDEX_Y[2][6] = { { 1, 1, 0, 2, 1, 1 }, { 0, 0, 0, 0, 0, 0 } }; + +}//namespace + +Scene3dView::Scene3dView() + : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ), + mRoot( Actor::New() ), + mShaderArray(), + mCameraActorArray(), + mDefaultCamera( CameraActor::New() ), + mAnimationArray(), + mLightType( Toolkit::Scene3dView::LightType::NONE ), + mLightVector( Vector3::ONE ), + mLightColor( Vector3::ONE ) +{ +} + +Scene3dView::~Scene3dView() +{ +} + +Toolkit::Scene3dView Scene3dView::New( const std::string& filePath ) +{ + Scene3dView* impl = new Scene3dView(); + + Dali::Toolkit::Scene3dView handle = Dali::Toolkit::Scene3dView( *impl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + impl->mFilePath = filePath; + impl->Initialize(); + + return handle; +} + +Toolkit::Scene3dView Scene3dView::New( const std::string& filePath, const std::string& diffuseTexturePath, const std::string& specularTexturePath, Vector4 scaleFactor ) +{ + Scene3dView* impl = new Scene3dView(); + + Dali::Toolkit::Scene3dView handle = Dali::Toolkit::Scene3dView( *impl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + impl->mFilePath = filePath; + impl->SetCubeMap( diffuseTexturePath, specularTexturePath, scaleFactor ); + impl->Initialize(); + + return handle; +} + +bool Scene3dView::CreateScene() +{ + if( std::string::npos != mFilePath.rfind( GLTF_EXT ) ) + { + Internal::Gltf::Loader gltfloader; + return( gltfloader.LoadScene( mFilePath, *this ) ); + } + + return false; +} + +uint32_t Scene3dView::GetAnimationCount() +{ + return mAnimationArray.size(); +} + +bool Scene3dView::PlayAnimation( uint32_t index ) +{ + if( GetAnimationCount() <= index ) + { + return false; + } + + mAnimationArray[index].Play(); + return true; +} + +bool Scene3dView::PlayAnimations() +{ + for( auto&& animation : mAnimationArray ) + { + animation.Play(); + } + + return true; +} + +bool Scene3dView::SetLight( Toolkit::Scene3dView::LightType type, Vector3 lightVector, Vector3 lightColor ) +{ + if( type > Toolkit::Scene3dView::LightType::DIRECTIONAL_LIGHT ) + { + return false; + } + + mLightType = static_cast( + ( mLightType >= Toolkit::Scene3dView::LightType::IMAGE_BASED_LIGHT ) ? + Toolkit::Scene3dView::LightType::IMAGE_BASED_LIGHT + type : + type ); + + mLightVector = lightVector; + mLightColor = lightColor; + + for( auto&& shader : mShaderArray ) + { + shader.RegisterProperty( "uLightType", ( GetLightType() & ~Toolkit::Scene3dView::LightType::IMAGE_BASED_LIGHT ) ); + shader.RegisterProperty( "uLightVector", lightVector ); + shader.RegisterProperty( "uLightColor", lightColor ); + } + + return true; +} + +uint8_t* Scene3dView::GetCroppedBuffer( uint8_t* sourceBuffer, uint32_t bytesPerPixel, uint32_t width, uint32_t height, uint32_t xOffset, uint32_t yOffset, uint32_t xFaceSize, uint32_t yFaceSize ) +{ + uint32_t byteSize = bytesPerPixel * xFaceSize * yFaceSize; + uint8_t* destBuffer = reinterpret_cast( malloc( byteSize + 4u ) ); + + int32_t srcStride = width * bytesPerPixel; + int32_t destStride = xFaceSize * bytesPerPixel; + int32_t srcOffset = xOffset * bytesPerPixel + yOffset * srcStride; + int32_t destOffset = 0; + for( uint16_t row = yOffset; row < yOffset + yFaceSize; ++row ) + { + memcpy( destBuffer + destOffset, sourceBuffer + srcOffset, destStride ); + srcOffset += srcStride; + destOffset += destStride; + } + + return destBuffer; +} + +void Scene3dView::UploadTextureFace( Texture& texture, Devel::PixelBuffer pixelBuffer, uint32_t faceIndex ) +{ + uint8_t* imageBuffer = pixelBuffer.GetBuffer(); + uint32_t bytesPerPixel = Pixel::GetBytesPerPixel( pixelBuffer.GetPixelFormat() ); + uint32_t imageWidth = pixelBuffer.GetWidth(); + uint32_t imageHeight = pixelBuffer.GetHeight(); + + CubeType cubeType = ( imageWidth / 4 == imageHeight / 3 ) ? CROSS_HORIZONTAL : + ( ( imageWidth / 6 == imageHeight ) ? ARRAY_HORIZONTAL : NONE ); + + uint32_t faceSize = 0; + if( cubeType == CROSS_HORIZONTAL ) + { + faceSize = imageWidth / 4; + } + else if( cubeType == ARRAY_HORIZONTAL ) + { + faceSize = imageWidth / 6; + } + else + { + return; + } + + uint32_t xOffset = CUBEMAP_INDEX_X[cubeType][faceIndex] * faceSize; + uint32_t yOffset = CUBEMAP_INDEX_Y[cubeType][faceIndex] * faceSize; + + uint8_t* tempImageBuffer = GetCroppedBuffer( imageBuffer, bytesPerPixel, imageWidth, imageHeight, xOffset, yOffset, faceSize, faceSize ); + PixelData pixelData = PixelData::New( tempImageBuffer, faceSize * faceSize * bytesPerPixel, faceSize, faceSize, pixelBuffer.GetPixelFormat(), PixelData::FREE ); + texture.Upload( pixelData, CubeMapLayer::POSITIVE_X + faceIndex, 0, 0, 0, faceSize, faceSize ); +} + +void Scene3dView::SetCubeMap( const std::string& diffuseTexturePath, const std::string& specularTexturePath, Vector4 scaleFactor ) +{ + mLightType = Toolkit::Scene3dView::LightType::IMAGE_BASED_LIGHT; + + // BRDF texture + std::string imageBrdfUrl = DALI_IMAGE_DIR "brdfLUT.png"; + mBRDFTexture = LoadTexture( imageBrdfUrl.c_str(), true ); + if( !mBRDFTexture ) + { + return; + } + + // Diffuse Cube Map + Devel::PixelBuffer diffusePixelBuffer = LoadImageFromFile( diffuseTexturePath ); + uint32_t diffuseFaceSize = diffusePixelBuffer.GetWidth() / 4; + mDiffuseTexture = Texture::New( TextureType::TEXTURE_CUBE, diffusePixelBuffer.GetPixelFormat(), diffuseFaceSize, diffuseFaceSize ); + for( uint32_t i = 0; i < 6; ++i ) + { + UploadTextureFace( mDiffuseTexture, diffusePixelBuffer, i ); + } + mDiffuseTexture.GenerateMipmaps(); + + // Specular Cube Map + Devel::PixelBuffer specularPixelBuffer = LoadImageFromFile( specularTexturePath ); + uint32_t specularFaceSize = specularPixelBuffer.GetWidth() / 4; + mSpecularTexture = Texture::New( TextureType::TEXTURE_CUBE, specularPixelBuffer.GetPixelFormat(), specularFaceSize, specularFaceSize ); + for( uint32_t i = 0; i < 6; ++i ) + { + UploadTextureFace( mSpecularTexture, specularPixelBuffer, i ); + } + mSpecularTexture.GenerateMipmaps(); + + mIBLScaleFactor = scaleFactor; +} + +bool Scene3dView::SetDefaultCamera( const Dali::Camera::Type type, const float nearPlane, const Vector3 cameraPosition ) +{ + mDefaultCamera.SetParentOrigin( ParentOrigin::CENTER ); + mDefaultCamera.SetAnchorPoint( AnchorPoint::CENTER ); + mDefaultCamera.SetType( type ); + mDefaultCamera.SetNearClippingPlane( nearPlane ); + mDefaultCamera.SetPosition( cameraPosition ); + return true; +} + +void Scene3dView::AddCamera( CameraActor cameraActor ) +{ + mCameraActorArray.push_back( cameraActor ); +} + +void Scene3dView::AddAnimation( Animation animation ) +{ + mAnimationArray.push_back( animation ); +} + +void Scene3dView::AddShader( Shader shader ) +{ + mShaderArray.push_back( shader ); +} + +Actor Scene3dView::GetRoot() +{ + return mRoot; +} + +CameraActor Scene3dView::GetDefaultCamera() +{ + return mDefaultCamera; +} + +uint32_t Scene3dView::GetCameraCount() +{ + return mCameraActorArray.size(); +} + +CameraActor Scene3dView::GetCamera( uint32_t cameraIndex ) +{ + CameraActor cameraActor; + if( cameraIndex < 0 || cameraIndex >= mCameraActorArray.size() ) + { + return cameraActor; + } + cameraActor = mCameraActorArray[cameraIndex]; + return cameraActor; +} + +Toolkit::Scene3dView::LightType Scene3dView::GetLightType() +{ + return mLightType; +} + +Vector3 Scene3dView::GetLightVector() +{ + return mLightVector; +} + +Vector3 Scene3dView::GetLightColor() +{ + return mLightColor; +} + +Vector4 Scene3dView::GetIBLScaleFactor() +{ + return mIBLScaleFactor; +} + +Texture Scene3dView::GetBRDFTexture() +{ + return mBRDFTexture; +} + +Texture Scene3dView::GetSpecularTexture() +{ + return mSpecularTexture; +} + +Texture Scene3dView::GetDiffuseTexture() +{ + return mDiffuseTexture; +} + +Texture Scene3dView::LoadTexture( const char *imageUrl, bool generateMipmaps ) +{ + Texture texture; + + Devel::PixelBuffer pixelBuffer = LoadImageFromFile( imageUrl ); + if( pixelBuffer ) + { + texture = Texture::New( TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight() ); + PixelData pixelData = Devel::PixelBuffer::Convert( pixelBuffer ); + texture.Upload( pixelData ); + + if( generateMipmaps ) + { + texture.GenerateMipmaps(); + } + } + + return texture; +} + +void Scene3dView::OnInitialize() +{ + mRoot.SetParentOrigin( ParentOrigin::CENTER ); + mRoot.SetAnchorPoint( AnchorPoint::CENTER ); + + Layer layer = Layer::New(); + layer.SetBehavior( Layer::LAYER_3D ); + layer.SetParentOrigin( ParentOrigin::CENTER ); + layer.SetAnchorPoint( AnchorPoint::CENTER ); + layer.Add( mRoot ); + + Actor self = Self(); + // Apply some default resizing rules. + self.SetParentOrigin( ParentOrigin::CENTER ); + self.SetAnchorPoint( AnchorPoint::CENTER ); + self.Add( layer ); + + CreateScene(); +} + +}//namespace Internal + +}//namespace Toolkit + +}//namespace Dali + diff --git a/dali-toolkit/internal/controls/scene3d-view/scene3d-view-impl.h b/dali-toolkit/internal/controls/scene3d-view/scene3d-view-impl.h new file mode 100644 index 0000000..afe95fd --- /dev/null +++ b/dali-toolkit/internal/controls/scene3d-view/scene3d-view-impl.h @@ -0,0 +1,276 @@ +#ifndef DALI_TOOLKIT_INTERNAL_SCENE3D_VIEW_H +#define DALI_TOOLKIT_INTERNAL_SCENE3D_VIEW_H + +/* + * Copyright (c) 2018 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. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +class Scene3dView; + +namespace Internal +{ + +namespace Gltf +{ + +class Loader; + +} + +/** + * Scene3dView implementation class + */ +class Scene3dView : public Control +{ +public: + + enum CubeType + { + CROSS_HORIZONTAL = 0, // Cross horizontal style cube map + ARRAY_HORIZONTAL, // array horizontal style cube map + NONE + }; + + + /** + * @copydoc Dali::Toolkit::Scene3dView::Scene3dView + */ + Scene3dView(); + + /** + * @copydoc Dali::Toolkit::Scene3dView::~Scene3dView + */ + virtual ~Scene3dView(); + + /** + * @copydoc Dali::Toolkit::Scene3dView::New( const std::string& filePath ) + */ + static Dali::Toolkit::Scene3dView New( const std::string& filePath ); + + /** + * @copydoc Dali::Toolkit::Scene3dView::New( const std::string& filePath, const std::string& diffuseTexturePath, const std::string& specularTexturePath, Vector4 scaleFactor ) + */ + static Dali::Toolkit::Scene3dView New( const std::string& filePath, const std::string& diffuseTexturePath, const std::string& specularTexturePath, Vector4 scaleFactor ); + + /** + * @copydoc Dali::Toolkit::Scene3dView::CreateScene() + */ + bool CreateScene(); + + /** + * @copydoc Dali::Toolkit::Scene3dView::GetAnimationCount() + */ + uint32_t GetAnimationCount(); + + /** + * @copydoc Dali::Toolkit::Scene3dView::PlayAnimation() + */ + bool PlayAnimation( uint32_t index ); + + /** + * @copydoc Dali::Toolkit::Scene3dView::PlayAnimations() + */ + bool PlayAnimations(); + + /** + * @copydoc Dali::Toolkit::Scene3dView::SetLight( Toolkit::Scene3dView::LightType type, Vector3 lightVector, Vector3 lightColor ) + */ + bool SetLight( Toolkit::Scene3dView::LightType type, Vector3 lightVector, Vector3 lightColor ); + + /** + * @brief Set default CameraActor specified in the each scene format specification. + * Default input values are derived from glTF default camera format. + * with Dali::Camera::Type = Dali::Camera::LOOK_AT_TARGET, + * near clipping plane = 0.1, + * and camera position = Vector3( 0.0, 0.0, 0.0 ). + */ + bool SetDefaultCamera( const Dali::Camera::Type type = Dali::Camera::LOOK_AT_TARGET, const float nearPlane = 0.1, const Vector3 cameraPosition = Vector3( 0.0, 0.0, 0.0 ) ); + + /** + * @brief Add CameraActor loaded from scene format file. + */ + void AddCamera( CameraActor cameraActor ); + + /** + * @brief Add Animation loaded from scene format file. + */ + void AddAnimation( Animation animation ); + + /** + * @brief Add new Shader. + * Actors can share same Shader if they use same properties. + * If a property changes in a shader, then the property of all actors that use the shader change. + */ + void AddShader( Shader shader ); + + /** + * @brief Get Root Actor. + */ + Actor GetRoot(); + + /** + * @copydoc Dali::Toolkit::Scene3dView::GetDefaultCamera() + */ + CameraActor GetDefaultCamera(); + + /** + * @copydoc Dali::Toolkit::Scene3dView::GetCameraCount() + */ + uint32_t GetCameraCount(); + + /** + * @copydoc Dali::Toolkit::Scene3dView::GetCamera( uint32_t cameraIndex ) + */ + CameraActor GetCamera( uint32_t cameraIndex ); + + /** + * @brief Get light type. + */ + Toolkit::Scene3dView::LightType GetLightType(); + + /** + * @brief Get light vector. + * Return light position when light type is LightType::POINT_LIGHT + * Return light direction when light type is LightType::DIRECTIONAL_LIGHT + */ + Vector3 GetLightVector(); + + /** + * @brief Get light color. + */ + Vector3 GetLightColor(); + + /** + * @brief Get Scaling factor of IBL. + */ + Vector4 GetIBLScaleFactor(); + + /** + * @brief Get BRDF Texture. + */ + Texture GetBRDFTexture(); + + /** + * @brief Get diffuse cube map texture. + */ + Texture GetDiffuseTexture(); + + /** + * @brief Get specular cube map texture. + */ + Texture GetSpecularTexture(); + +private: + /** + * @brief Get Cropped image buffer. + * For each direction, Offset + faceSize must be width or height or less then them. + */ + uint8_t* GetCroppedBuffer( uint8_t* sourceBuffer, uint32_t bytesPerPixel, uint32_t width, uint32_t height, uint32_t xOffset, uint32_t yOffset, uint32_t xFaceSize, uint32_t yFaceSize ); + + /** + * @brief Upload cube map texture. + */ + void UploadTextureFace( Texture& texture, Devel::PixelBuffer pixelBuffer, uint32_t faceIndex ); + + /** + * @brief Set diffuse and specular cube map textures. + */ + void SetCubeMap( const std::string& diffuseTexturePath, const std::string& specularTexturePath, Vector4 scaleFactor = Vector4( 1.0, 1.0, 1.0, 1.0 ) ); + + virtual void OnInitialize(); + + + /** + * @brief Load 2D texture. + * @param[in] imageUrl Image URL of the texture. + * @param[in] generateMipmaps If generateMipmaps is true, then generate mipmap of this texture. + * @return Texture loaded from imageUrl. + */ + Texture LoadTexture( const char *imageUrl, bool generateMipmaps ); + +private: + Actor mRoot; // Root actor that contains scene graph + std::string mFilePath; // Full file path of scene file + + std::vector mShaderArray; // Shader Array to change properties of scene such as lighting. + + std::vector mCameraActorArray; // CameraActer array loaded from scene format file. + CameraActor mDefaultCamera; // Default CameraActor for the empty mCameraActorArray. + + std::vector mAnimationArray; // Animation array loaded from scene format file. + + Toolkit::Scene3dView::LightType mLightType; // Light type + Vector3 mLightVector; // Light position when mLightType is LightType::POINT_LIGHT + // Light direction when mLightType is LightType::DIRECTIONAL_LIGHT + Vector3 mLightColor; // Light color + + Vector4 mIBLScaleFactor; // IBL scaling factor for the IBL rendering + Texture mBRDFTexture; // BRDF texture for the PBR rendering + Texture mSpecularTexture; // Specular cube map texture + Texture mDiffuseTexture; // Diffuse cube map texture + +private: + + // Undefined copy constructor. + Scene3dView( const Scene3dView& ); + + // Undefined assignment operator. + Scene3dView& operator=( const Scene3dView& ); +}; + +} // namespace Internal + + // Helpers for public-api forwarding methods +inline const Internal::Scene3dView& GetImpl( const Toolkit::Scene3dView& scene3dView ) +{ + DALI_ASSERT_ALWAYS( scene3dView && "Scene3dView handle is empty" ); + const Dali::RefObject& handle = scene3dView.GetImplementation(); + + return static_cast( handle ); +} + +inline Internal::Scene3dView& GetImpl( Toolkit::Scene3dView& scene3dView ) +{ + DALI_ASSERT_ALWAYS( scene3dView && "Scene3dView handle is empty" ); + + Dali::RefObject& handle = scene3dView.GetImplementation(); + + return static_cast( handle ); +} + +}//namespace Toolkit + +}//namespace Dali + +#endif // DALI_TOOLKIT_INTERNAL_SCENE3D_VIEW_H diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index 64f8251..547df3c 100755 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -90,6 +90,8 @@ toolkit_src_files = \ $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-effect-impl.cpp \ $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-impl.cpp \ $(toolkit_src_dir)/controls/scrollable/scroll-view/scroll-view-page-path-effect-impl.cpp \ + $(toolkit_src_dir)/controls/scene3d-view/scene3d-view-impl.cpp \ + $(toolkit_src_dir)/controls/scene3d-view/gltf-loader.cpp \ $(toolkit_src_dir)/controls/shadow-view/shadow-view-impl.cpp \ $(toolkit_src_dir)/controls/slider/slider-impl.cpp \ $(toolkit_src_dir)/controls/super-blur-view/super-blur-view-impl.cpp \ diff --git a/dali-toolkit/styles/images-common/brdfLUT.png b/dali-toolkit/styles/images-common/brdfLUT.png new file mode 100644 index 0000000..5f6541b Binary files /dev/null and b/dali-toolkit/styles/images-common/brdfLUT.png differ