2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 using Tizen.NUI.BaseComponents;
21 using Tizen.NUI.Constants;
22 using Tizen.NUI.Scene3D;
23 using System.Collections.Generic;
25 class Scene3DSample : NUIApplication
27 static string IMAGE_DIR = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "image/";
28 static string MODEL_DIR = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "model/";
35 Animation mModelAnimation;
36 bool mModelLoadFinished;
38 // Note : This motion data works well only if model is MorthStressTest!
39 MotionData mStaticMotionData;
40 MotionData mStaticRevertMotionData;
41 MotionData mAnimateMotionData;
42 Animation mMotionAnimation;
43 const int modelMotionAnimationDurationMilliseconds = 2000; // milliseconds
45 Animation mModelRotateAnimation;
46 const int modelRotateAnimationDurationMilliseconds = 10000; // milliseconds
48 private bool mMutex = false; // Lock key event during some transition / Change informations
50 #region Model list define
52 * Copyright 2021 Analytical Graphics, Inc.
53 * CC-BY 4.0 https://creativecommons.org/licenses/by/4.0/
55 private static readonly List<string> ModelUrlList = new List<string>()
57 // Model reference : https://sketchfab.com/models/b81008d513954189a063ff901f7abfe4
58 // Get from KhronosGroup https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/DamagedHelmet
59 "DamagedHelmet/DamagedHelmet.gltf",
61 //Get from KhronosGroup https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/MorphStressTest
62 "MorphStressTest/MorphStressTest.gltf",
64 // Get from KhronosGroup https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/2CylinderEngine
65 "2CylinderEngine/2CylinderEngine_e.gltf",
67 // Get from KhronosGroup https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/ToyCar
70 //Get from KhronosGroup https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated
71 "BoxAnimated/BoxAnimated.gltf",
73 private int currentModelIndex = 0;
76 #region Image based light list define
77 private static readonly List<(string, string)> IBLUrlList = new List<(string, string)>
79 ("forest_diffuse_cubemap.png", "forest_specular_cubemap.png"),
80 ("papermill_E_diffuse-64.ktx", "papermill_pmrem.ktx"),
81 ("Irradiance.ktx", "Radiance.ktx"),
83 private int currentIBLIndex = 0;
84 private float IBLFactor = 0.7f;
87 #region Camera list define
88 private class CameraInfo
90 public Vector3 Position { get; set; } = Vector3.Zero;
91 public Rotation Orientation { get; set; } = null;
92 public Radian Fov { get; set; } = null;
93 public float Near { get; set; } = 0.5f;
94 public float Far { get; set; } = 50.0f;
96 private static readonly List<CameraInfo> CameraInfoList = new List<CameraInfo>()
98 // -Z front and -Y up.
100 Position = new Vector3(0.0f, 0.0f, 5.55f),
101 // Basic camera : +Z front and -Y up
102 // After YAxis 180 rotate : -Z front and -Y up
103 // Note : Default camera has YAxis 180.0f rotation even we didn't setup.
104 Orientation = new Rotation(new Radian(new Degree(180.0f)), Vector3.YAxis),
105 Fov = new Radian(new Degree(45.0f)),
110 // +Y front and +X up.
112 Position = new Vector3(0.0f, -3.95f, 0.0f),
113 // Rotate by XAxis first, and then rotate by YAxis
114 // Basic camera : +Z front and -Y up
115 // After XAxis -90 rotate : +Y front and +Z up
116 // After YAxis 90 rotate : +Y front and +X up
117 Orientation = new Rotation(new Radian(new Degree(90.0f)), Vector3.YAxis) *
118 new Rotation(new Radian(new Degree(-90.0f)), Vector3.XAxis),
119 Fov = new Radian(new Degree(70.0f)),
124 uint currentCameraIndex = 0u;
125 List<Tizen.NUI.Scene3D.Camera> additionalCameraList;
126 const int cameraAnimationDurationMilliseconds = 2000; // milliseconds
129 protected void CreateSceneView()
131 mSceneView = new SceneView()
133 SizeWidth = mWindowSize.Width,
134 SizeHeight = mWindowSize.Height,
137 PivotPoint = PivotPoint.TopLeft,
138 ParentOrigin = ParentOrigin.TopLeft,
139 PositionUsesPivotPoint = true,
142 mSceneView.CameraTransitionFinished += (o, e) =>
150 mSceneView.ResourcesLoaded += (o, e) =>
152 Tizen.Log.Error("NUI", $"IBL image loaded done\n");
159 SetupSceneViewCamera(mSceneView);
161 mWindow.Add(mSceneView);
163 private void SetupSceneViewCamera(SceneView sceneView)
165 int cameraCount = CameraInfoList.Count;
166 for (int i = 0; i < cameraCount; ++i)
168 var cameraInfo = CameraInfoList[i];
169 Tizen.NUI.Scene3D.Camera camera;
172 // Default camera setting
173 // Note : SceneView always have 1 default camera.
174 camera = sceneView.GetCamera(0u);
178 // Additional camera setting (top view camera).
179 camera = new Tizen.NUI.Scene3D.Camera();
180 sceneView.AddCamera(camera);
182 camera.PositionX = cameraInfo?.Position?.X ?? 0.0f;
183 camera.PositionY = cameraInfo?.Position?.Y ?? 0.0f;
184 camera.PositionZ = cameraInfo?.Position?.Z ?? 0.0f;
185 camera.Orientation = cameraInfo?.Orientation ?? new Rotation(new Radian(new Degree(180.0f)), Vector3.YAxis);
187 camera.NearPlaneDistance = cameraInfo?.Near ?? 0.5f;
188 camera.FarPlaneDistance = cameraInfo?.Far ?? 50.0f;
189 camera.FieldOfView = cameraInfo?.Fov ?? new Radian(new Degree(45.0f));
193 protected void CreateModel(string modelUrl)
202 // Release old animation if exist
203 if (mModelAnimation != null)
205 mModelAnimation.Stop();
206 mModelAnimation.Dispose();
207 mModelAnimation = null;
210 // Release old camera if exist
211 if (additionalCameraList != null)
213 if (currentCameraIndex >= CameraInfoList.Count)
215 currentCameraIndex = 0u;
216 Tizen.Log.Error("NUI", $"Use camera [{currentCameraIndex}]\n");
217 mSceneView.SelectCamera(currentCameraIndex);
219 foreach (var additionalCamera in additionalCameraList)
221 mSceneView.RemoveCamera(additionalCamera);
222 additionalCamera.Dispose();
224 additionalCameraList = null;
227 mModelLoadFinished = false;
229 mModel = new Model(MODEL_DIR + modelUrl)
233 mModel.ResourcesLoaded += (s, e) =>
235 Model model = s as Model;
237 // You can play animation if the animation exists.
238 if (model.GetAnimationCount() > 0u)
240 mModelAnimation = model.GetAnimation(0u);
241 if (mModelAnimation != null)
243 mModelAnimation.Looping = true;
244 mModelAnimation.Play();
247 // You can apply camera properties if the camera parameter exists.
248 if (model.GetCameraCount() > 0u)
250 additionalCameraList = new List<Tizen.NUI.Scene3D.Camera>();
251 bool firstSucceededCamera = true;
252 for (uint i = 0; i < model.GetCameraCount(); ++i)
254 Tizen.NUI.Scene3D.Camera additionalCamera = new Tizen.NUI.Scene3D.Camera();
255 // If we success to make additional camera from model, Add that camera into sceneview, and select that.
256 if (model.ApplyCamera(i, additionalCamera))
258 mSceneView.AddCamera(additionalCamera);
259 if (firstSucceededCamera)
261 currentCameraIndex = mSceneView.GetCameraCount() - 1u;
263 Tizen.Log.Error("NUI", $"Use additional camera [{currentCameraIndex}]\n");
264 mSceneView.SelectCamera(currentCameraIndex);
265 firstSucceededCamera = false;
267 additionalCameraList.Add(additionalCamera);
271 Tizen.Log.Error("NUI", $"Error! camera at [{i}] have some problem\n");
272 additionalCamera.Dispose();
276 Tizen.Log.Error("NUI", $"{model.Name} size : {model.Size.Width}, {model.Size.Height}, {model.Size.Depth}\n");
277 Tizen.Log.Error("NUI", $"Animation count {model.GetAnimationCount()} , Camera count {model.GetCameraCount()}\n");
279 // Auto rotate model only if it don't have camera.
280 if (mModel.GetCameraCount() == 0u)
282 mModelRotateAnimation.Play();
285 mModelLoadFinished = true;
293 mModelRotateAnimation = new Animation(modelRotateAnimationDurationMilliseconds);
294 mModelRotateAnimation.AnimateBy(mModel, "Orientation", new Rotation(new Radian(new Degree(360.0f)), Vector3.YAxis));
296 mModelRotateAnimation.Looping = true;
298 mSceneView.Add(mModel);
303 // Note : This motion data works well only if model is MorthStressTest!
304 private void CreateMotionData()
306 mStaticMotionData = new MotionData();
307 mStaticRevertMotionData = new MotionData();
308 mAnimateMotionData = new MotionData();
310 mStaticMotionData.MotionValues = new List<(MotionIndex, MotionValue)>
313 new MotionTransformIndex()
315 ModelNodeId = new PropertyKey("Main"),
316 TransformType = MotionTransformIndex.TransformTypes.Orientation,
320 Value = new PropertyValue(new Rotation(new Radian(new Degree(-45.0f)), Vector3.ZAxis)),
324 mStaticRevertMotionData.MotionValues = new List<(MotionIndex, MotionValue)>
327 new MotionTransformIndex()
329 ModelNodeId = new PropertyKey("Main"),
330 TransformType = MotionTransformIndex.TransformTypes.Orientation,
334 Value = new PropertyValue(new Rotation(new Radian(new Degree(0.0f)), Vector3.ZAxis)),
338 new MotionTransformIndex()
340 ModelNodeId = new PropertyKey("Main"),
341 TransformType = MotionTransformIndex.TransformTypes.Scale,
345 Value = new PropertyValue(Vector3.One),
350 mAnimateMotionData.MotionValues = new List<(MotionIndex, MotionValue)>()
353 new MotionTransformIndex()
355 ModelNodeId = new PropertyKey("Main"),
356 TransformType = MotionTransformIndex.TransformTypes.Scale,
360 Value = new PropertyValue(new Vector3(0.5f, 1.5f, 1.0f)),
364 for (int i = 0; i < 8; ++i)
366 MotionIndex index = new BlendShapeIndex()
368 ModelNodeId = new PropertyKey("Main"),
369 BlendShapeId = new PropertyKey(i),
371 MotionValue value = new MotionValue()
373 KeyFramesValue = new KeyFrames()
375 value.KeyFramesValue.Add(0.0f, 0.0f);
376 value.KeyFramesValue.Add(1.0f, 1.0f * ((float)Math.Abs(i - 3.5f) + 0.5f) / 4.0f);
378 mAnimateMotionData.MotionValues.Add(ValueTuple.Create(index, value));
382 void SetupIBLimage(string specularUrl, string diffuseUrl, float iblFactor)
384 mSceneView.SetImageBasedLightSource(IMAGE_DIR + specularUrl, IMAGE_DIR + diffuseUrl, iblFactor);
389 void OnKeyEvent(object source, Window.KeyEventArgs e)
391 // Skip interaction when some resources are changing now.
396 if (e.Key.State == Key.StateType.Down)
398 switch (e.Key.KeyPressedName)
411 currentCameraIndex++;
412 if (currentCameraIndex >= mSceneView.GetCameraCount())
414 currentCameraIndex = 0;
417 Tizen.Log.Error("NUI", $"Use camera [{currentCameraIndex}]\n");
418 mSceneView.CameraTransition(currentCameraIndex, cameraAnimationDurationMilliseconds);
427 if (currentModelIndex >= ModelUrlList.Count)
429 currentModelIndex = 0;
432 Tizen.Log.Error("NUI", $"Create model [{currentModelIndex}]\n");
433 CreateModel(ModelUrlList[currentModelIndex]);
439 if (currentIBLIndex >= IBLUrlList.Count)
444 Tizen.Log.Error("NUI", $"Use Light image [{currentIBLIndex}]\n");
445 SetupIBLimage(IBLUrlList[currentIBLIndex].Item1, IBLUrlList[currentIBLIndex].Item2, IBLFactor);
450 if (mModelRotateAnimation.State == Animation.States.Playing)
452 mModelRotateAnimation.Pause();
456 mModelRotateAnimation.Play();
462 if (mModelAnimation?.State == Animation.States.Playing)
464 if (mModel != null && mModelLoadFinished)
466 mMotionAnimation = mModel.GenerateMotionDataAnimation(mAnimateMotionData, modelMotionAnimationDurationMilliseconds);
468 if (mMotionAnimation != null)
470 // Stop original model animation
471 mModelAnimation.Stop();
473 mModel.SetMotionData(mStaticMotionData);
474 mMotionAnimation.Looping = true;
475 mMotionAnimation.BlendPoint = 0.25f;
476 mMotionAnimation.Play();
477 Tizen.Log.Error("NUI", $"Animate pre-defined motion data!\n");
487 else if (e.Key.State == Key.StateType.Up)
489 if (mModelAnimation?.State == Animation.States.Stopped)
491 if (mMotionAnimation != null)
493 mMotionAnimation.Stop();
494 mMotionAnimation.Dispose();
495 mMotionAnimation = null;
497 // Revert motion data
498 mModel.SetMotionData(mStaticRevertMotionData);
500 // Replay original model animation
501 mModelAnimation.Play();
507 public void Activate()
509 mWindow = Window.Instance;
510 mWindow.BackgroundColor = Color.DarkOrchid;
511 mWindowSize = mWindow.WindowSize;
513 mWindow.Resized += (o, e) =>
515 mWindowSize = mWindow.WindowSize;
516 mSceneView.Size = new Size(mWindowSize);
519 mWindow.KeyEvent += OnKeyEvent;
521 // Create motion data for MorphStressTest.gltf
525 SetupIBLimage(IBLUrlList[currentIBLIndex].Item1, IBLUrlList[currentIBLIndex].Item2, IBLFactor);
526 CreateModel(ModelUrlList[currentModelIndex]);
530 global::System.GC.Collect();
531 global::System.GC.WaitForPendingFinalizers();
532 global::System.GC.Collect();
535 public void Deactivate()
539 private void DestroyScene()
543 protected override void OnCreate()
545 // Up call to the Base class first
551 /// The main entry point for the application.
553 [STAThread] // Forces app to use one thread to access NUI
554 static void Main(string[] args)
556 Scene3DSample example = new Scene3DSample();