Add Skybox in SceneView 03/283303/12
authorseungho <sbsh.baek@samsung.com>
Fri, 21 Oct 2022 16:24:00 +0000 (01:24 +0900)
committerseungho <sbsh.baek@samsung.com>
Wed, 2 Nov 2022 06:15:05 +0000 (15:15 +0900)
Change-Id: I37483af74efe3f0514d00cd1c7bc103066d455ee
Signed-off-by: seungho <sbsh.baek@samsung.com>
automated-tests/src/dali-scene3d/utc-Dali-SceneView.cpp
dali-scene3d/internal/controls/scene-view/scene-view-impl.cpp
dali-scene3d/internal/controls/scene-view/scene-view-impl.h
dali-scene3d/internal/graphics/shaders/skybox-shader.frag [new file with mode: 0644]
dali-scene3d/internal/graphics/shaders/skybox-shader.vert [new file with mode: 0644]
dali-scene3d/public-api/controls/scene-view/scene-view.cpp
dali-scene3d/public-api/controls/scene-view/scene-view.h

index 6996ec6..a98e430 100644 (file)
@@ -586,3 +586,67 @@ int UtcDaliSceneViewResourceReady(void)
 
   END_TEST;
 }
+
+int UtcDaliSceneViewSetSkybox(void)
+{
+  ToolkitTestApplication application;
+
+  gResourceReadyCalled = false;
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
+  view.ResourceReadySignal().Connect(OnResourceReady);
+  // SceneView::IsResourceReady() returns true by default.
+  DALI_TEST_EQUALS(view.IsResourceReady(), true, TEST_LOCATION);
+
+  // Sanity check
+  DALI_TEST_CHECK(!gResourceReadyCalled);
+
+  application.GetScene().Add(view);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(view.IsResourceReady(), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
+
+  gResourceReadyCalled = false;
+
+  view.SetSkybox(TEST_SPECULAR_TEXTURE);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewSetSkyboxIntensity(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
+
+  float intensity = 0.5f;
+  DALI_TEST_EQUALS(view.GetSkyboxIntensity(), 1.0f, TEST_LOCATION);
+
+  view.SetSkyboxIntensity(intensity);
+  DALI_TEST_EQUALS(view.GetSkyboxIntensity(), intensity, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewSetSkyboxOrientation(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
+
+  Dali::Quaternion orientation = Dali::Quaternion(Radian(0.5f), Vector3::YAXIS);
+  view.SetSkyboxOrientation(orientation);
+  DALI_TEST_EQUALS(view.GetSkyboxOrientation(), orientation, TEST_LOCATION);
+
+  END_TEST;
+}
index 593daf0..d19010f 100644 (file)
 #include <dali/integration-api/debug.h>
 #include <dali/public-api/object/type-registry-helper.h>
 #include <dali/public-api/object/type-registry.h>
+#include <string_view>
 
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/controls/model/model-impl.h>
+#include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
 #include <dali-scene3d/public-api/loader/cube-map-loader.h>
 
 #include <dali/integration-api/debug.h>
@@ -60,11 +62,100 @@ DALI_TYPE_REGISTRATION_END()
 Property::Index   RENDERING_BUFFER    = Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX + 1;
 constexpr int32_t DEFAULT_ORIENTATION = 0;
 
+static constexpr std::string_view SKYBOX_INTENSITY_STRING = "uIntensity";
+
+Dali::Actor CreateSkybox(const std::string& skyboxUrl)
+{
+  struct Vertex
+  {
+    Vector3 aPosition;
+  };
+
+  Vertex skyboxVertices[] = {
+    // back
+    {Vector3(-1.0f, 1.0f, -1.0f)},
+    {Vector3(-1.0f, -1.0f, -1.0f)},
+    {Vector3(1.0f, -1.0f, -1.0f)},
+    {Vector3(1.0f, -1.0f, -1.0f)},
+    {Vector3(1.0f, 1.0f, -1.0f)},
+    {Vector3(-1.0f, 1.0f, -1.0f)},
+
+    // left
+    {Vector3(-1.0f, -1.0f, 1.0f)},
+    {Vector3(-1.0f, -1.0f, -1.0f)},
+    {Vector3(-1.0f, 1.0f, -1.0f)},
+    {Vector3(-1.0f, 1.0f, -1.0f)},
+    {Vector3(-1.0f, 1.0f, 1.0f)},
+    {Vector3(-1.0f, -1.0f, 1.0f)},
+
+    // right
+    {Vector3(1.0f, -1.0f, -1.0f)},
+    {Vector3(1.0f, -1.0f, 1.0f)},
+    {Vector3(1.0f, 1.0f, 1.0f)},
+    {Vector3(1.0f, 1.0f, 1.0f)},
+    {Vector3(1.0f, 1.0f, -1.0f)},
+    {Vector3(1.0f, -1.0f, -1.0f)},
+
+    // front
+    {Vector3(-1.0f, -1.0f, 1.0f)},
+    {Vector3(-1.0f, 1.0f, 1.0f)},
+    {Vector3(1.0f, 1.0f, 1.0f)},
+    {Vector3(1.0f, 1.0f, 1.0f)},
+    {Vector3(1.0f, -1.0f, 1.0f)},
+    {Vector3(-1.0f, -1.0f, 1.0f)},
+
+    // botton
+    {Vector3(-1.0f, 1.0f, -1.0f)},
+    {Vector3(1.0f, 1.0f, -1.0f)},
+    {Vector3(1.0f, 1.0f, 1.0f)},
+    {Vector3(1.0f, 1.0f, 1.0f)},
+    {Vector3(-1.0f, 1.0f, 1.0f)},
+    {Vector3(-1.0f, 1.0f, -1.0f)},
+
+    // top
+    {Vector3(-1.0f, -1.0f, -1.0f)},
+    {Vector3(-1.0f, -1.0f, 1.0f)},
+    {Vector3(1.0f, -1.0f, -1.0f)},
+    {Vector3(1.0f, -1.0f, -1.0f)},
+    {Vector3(-1.0f, -1.0f, 1.0f)},
+    {Vector3(1.0f, -1.0f, 1.0f)}};
+
+  Dali::Shader       shaderSkybox  = Shader::New(SHADER_SKYBOX_SHADER_VERT.data(), SHADER_SKYBOX_SHADER_FRAG.data());
+  Dali::VertexBuffer vertexBuffer  = Dali::VertexBuffer::New(Property::Map().Add("aPosition", Property::VECTOR3));
+  vertexBuffer.SetData(skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex));
+
+  Dali::Geometry skyboxGeometry = Geometry::New();
+  skyboxGeometry.AddVertexBuffer(vertexBuffer);
+  skyboxGeometry.SetType(Geometry::TRIANGLES);
+
+  Dali::Texture    skyboxTexture   = Dali::Scene3D::Loader::LoadCubeMap(skyboxUrl);
+  Dali::TextureSet skyboxTextures = TextureSet::New();
+  skyboxTextures.SetTexture(0, skyboxTexture);
+
+  Dali::Renderer skyboxRenderer = Renderer::New(skyboxGeometry, shaderSkybox);
+  skyboxRenderer.SetTextures(skyboxTextures);
+  skyboxRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2.0f);
+  // Enables the depth test.
+  skyboxRenderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);
+  // The fragment shader will run only is those pixels that have the max depth value.
+  skyboxRenderer.SetProperty(Renderer::Property::DEPTH_FUNCTION, DepthFunction::LESS_EQUAL);
+
+  Dali::Actor skyboxActor = Actor::New();
+  skyboxActor.SetProperty(Dali::Actor::Property::NAME, "SkyBox");
+  skyboxActor.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  skyboxActor.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  skyboxActor.AddRenderer(skyboxRenderer);
+  return skyboxActor;
+}
+
 } // anonymous namespace
 
 SceneView::SceneView()
 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
-  mWindowOrientation(DEFAULT_ORIENTATION)
+  mWindowOrientation(DEFAULT_ORIENTATION),
+  mSkybox(),
+  mSkyboxOrientation(Quaternion()),
+  mSkyboxIntensity(1.0f)
 {
 }
 
@@ -213,7 +304,10 @@ void SceneView::SetImageBasedLightSource(const std::string& diffuseUrl, const st
     }
   }
   mIBLResourceReady = true;
-  Control::SetResourceReady(false);
+  if(IsResourceReady())
+  {
+    Control::SetResourceReady(false);
+  }
 }
 
 void SceneView::SetImageBasedLightScaleFactor(float scaleFactor)
@@ -247,6 +341,63 @@ bool SceneView::IsUsingFramebuffer() const
   return mUseFrameBuffer;
 }
 
+void SceneView::SetSkybox(const std::string& skyboxUrl)
+{
+  mSkyboxResourceReady = false;
+  if(mSkybox)
+  {
+    mSkybox.Unparent();
+    mSkybox.Reset();
+  }
+  mSkybox = CreateSkybox(skyboxUrl);
+  SetSkyboxIntensity(mSkyboxIntensity);
+  SetSkyboxOrientation(mSkyboxOrientation);
+  if(mRootLayer)
+  {
+    mRootLayer.Add(mSkybox);
+  }
+
+  mSkyboxResourceReady = true;
+  if(IsResourceReady())
+  {
+    Control::SetResourceReady(false);
+  }
+}
+
+void SceneView::SetSkyboxIntensity(float intensity)
+{
+  mSkyboxIntensity = intensity;
+  if(intensity < 0)
+  {
+    DALI_LOG_ERROR("Intensity should be greater than or equal to 0.\n");
+    mSkyboxIntensity = 0.0f;
+  }
+
+  if(mSkybox)
+  {
+    mSkybox.RegisterProperty(SKYBOX_INTENSITY_STRING.data(), mSkyboxIntensity);
+  }
+}
+
+float SceneView::GetSkyboxIntensity() const
+{
+  return mSkyboxIntensity;
+}
+
+void SceneView::SetSkyboxOrientation(const Quaternion& orientation)
+{
+  mSkyboxOrientation = orientation;
+  if(mSkybox)
+  {
+    mSkybox.SetProperty(Dali::Actor::Property::ORIENTATION, orientation);
+  }
+}
+
+Quaternion SceneView::GetSkyboxOrientation() const
+{
+  return mSkyboxOrientation;
+}
+
 ///////////////////////////////////////////////////////////
 //
 // Private methods
@@ -344,7 +495,7 @@ void SceneView::OnRelayout(const Vector2& size, RelayoutContainer& container)
 
 bool SceneView::IsResourceReady() const
 {
-  return mIBLResourceReady;
+  return mIBLResourceReady & mSkyboxResourceReady;
 }
 
 void SceneView::UpdateCamera(CameraActor camera)
index 548bfee..480ffb0 100644 (file)
@@ -136,6 +136,31 @@ public:
    */
   bool IsUsingFramebuffer() const;
 
+  /**
+   * @copydoc SceneView::SetSkybox()
+   */
+  void SetSkybox(const std::string& skyboxUrl);
+
+  /**
+   * @copydoc SceneView::SetSkyboxIntensity()
+   */
+  void SetSkyboxIntensity(float intensity);
+
+  /**
+   * @copydoc SceneView::GetSkyboxIntensity()
+   */
+  float GetSkyboxIntensity() const;
+
+  /**
+   * @copydoc SceneView::SetSkyboxOrientation()
+   */
+  void SetSkyboxOrientation(const Quaternion& orientation);
+
+  /**
+   * @copydoc SceneView::GetSkyboxOrientation()
+   */
+  Quaternion GetSkyboxOrientation() const;
+
 protected:
   /**
    * @brief Constructs a new SceneView.
@@ -229,12 +254,16 @@ private:
   Dali::RenderTask            mRenderTask;
   Layer                       mRootLayer;
   int32_t                     mWindowOrientation;
+  Dali::Actor                 mSkybox;
+  Quaternion                  mSkyboxOrientation;
+  float                       mSkyboxIntensity{1.0f};
 
   Dali::Texture mSpecularTexture;
   Dali::Texture mDiffuseTexture;
   float         mIblScaleFactor{1.0f};
   bool          mUseFrameBuffer{false};
   bool          mIBLResourceReady{true};
+  bool          mSkyboxResourceReady{true};
 
   // TODO : Light Source
 };
diff --git a/dali-scene3d/internal/graphics/shaders/skybox-shader.frag b/dali-scene3d/internal/graphics/shaders/skybox-shader.frag
new file mode 100644 (file)
index 0000000..2a6024d
--- /dev/null
@@ -0,0 +1,9 @@
+uniform samplerCube   uSkyBoxTexture;
+uniform mediump float uIntensity;
+varying mediump vec3  vTexCoord;
+
+void main()
+{
+  mediump vec4 texColor = textureCube(uSkyBoxTexture, vTexCoord) * uIntensity;
+  gl_FragColor = texColor;
+}
\ No newline at end of file
diff --git a/dali-scene3d/internal/graphics/shaders/skybox-shader.vert b/dali-scene3d/internal/graphics/shaders/skybox-shader.vert
new file mode 100644 (file)
index 0000000..944bbb0
--- /dev/null
@@ -0,0 +1,21 @@
+attribute mediump vec3 aPosition;
+uniform   mediump mat4 uModelView;
+uniform   mediump mat4 uModelMatrix;
+uniform   mediump mat4 uViewMatrix;
+uniform   mediump mat4 uProjection;
+uniform   mediump mat4 uMvpMatrix;
+varying   mediump vec3 vTexCoord;
+
+void main()
+{
+  vTexCoord.x = aPosition.x;
+  vTexCoord.y = -aPosition.y; // convert to GL coords
+  vTexCoord.z = aPosition.z;
+
+  mediump vec4 vertexPosition = vec4(aPosition, 1.0);
+  vec4 clipSpacePosition = uProjection * mat4(mat3(uModelView)) * vertexPosition;
+  // Writes 1.0, the maximum depth value, into the depth buffer.
+  // This is an optimization to avoid running the fragment shader
+  // for the pixels hidden by the scene's objects.
+  gl_Position = clipSpacePosition.xyww;
+}
\ No newline at end of file
index 1aea71c..bc41050 100644 (file)
@@ -127,6 +127,31 @@ bool SceneView::IsUsingFramebuffer() const
   return GetImpl(*this).IsUsingFramebuffer();
 }
 
+void SceneView::SetSkybox(const std::string& skyboxUrl)
+{
+  GetImpl(*this).SetSkybox(skyboxUrl);
+}
+
+void SceneView::SetSkyboxIntensity(float intensity)
+{
+  GetImpl(*this).SetSkyboxIntensity(intensity);
+}
+
+float SceneView::GetSkyboxIntensity() const
+{
+  return GetImpl(*this).GetSkyboxIntensity();
+}
+
+void SceneView::SetSkyboxOrientation(const Quaternion& orientation)
+{
+  GetImpl(*this).SetSkyboxOrientation(orientation);
+}
+
+Quaternion SceneView::GetSkyboxOrientation() const
+{
+  return GetImpl(*this).GetSkyboxOrientation();
+}
+
 } // namespace Scene3D
 
 } // namespace Dali
index fce3b19..9c72d9c 100644 (file)
@@ -315,6 +315,51 @@ public:
    */
   bool IsUsingFramebuffer() const;
 
+  /**
+   * @brief Sets Skybox for this scene.
+   * Skybox texture is asynchronously loaded. When loading is finished, ResourceReady is emitted.
+   *
+   * @SINCE_2_2.0
+   * @param[in] skyboxUrl Cube map image url for skybox.
+   */
+  void SetSkybox(const std::string& skyboxUrl);
+
+  /**
+   * @brief Sets Skybox intensity.
+   * The skybox intensity is multiplied to the color of skybox texture.
+   * Default value is 1.0f.
+   *
+   * @SINCE_2_2.0
+   * @note Intensity should be positive value.
+   * @param[in] intensity Intensity value to be multiplied to the cube map color
+   */
+  void SetSkyboxIntensity(float intensity);
+
+  /**
+   * @brief Gets Skybox intensity.
+   * Default value is 1.0f.
+   *
+   * @SINCE_2_2.0
+   * @return skybox intensity
+   */
+  float GetSkyboxIntensity() const;
+
+  /**
+   * @brief Sets Orientation of Skybox.
+   *
+   * @SINCE_2_2.0
+   * @param[in] orientation Quaternion for orientation of Skybox.
+   */
+  void SetSkyboxOrientation(const Quaternion& orientation);
+
+  /**
+   * @brief Gets Skybox orientation.
+   *
+   * @SINCE_2_2.0
+   * @return skybox orientation
+   */
+  Quaternion GetSkyboxOrientation() const;
+
 public: // Not intended for application developers
   /// @cond internal
   /**