From 74c7303abb26d25114e08a3a23c3a53d641d50fa Mon Sep 17 00:00:00 2001
From: Ian Chen <ichen@openrobotics.org>
Date: Tue, 5 Sep 2023 21:46:33 -0700
Subject: [PATCH 1/2] enable sky

Signed-off-by: Ian Chen <ichen@openrobotics.org>
---
 .../rendering/ogre2/Ogre2WideAngleCamera.hh   | 10 ++
 ogre2/src/Ogre2Scene.cc                       |  9 ++
 ogre2/src/Ogre2WideAngleCamera.cc             | 88 ++++++++++++++++-
 test/integration/sky.cc                       | 97 +++++++++++++++++++
 4 files changed, 203 insertions(+), 1 deletion(-)

diff --git a/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh b/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh
index 688f07540..44e3c28b5 100644
--- a/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh
+++ b/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh
@@ -110,6 +110,13 @@ namespace gz
           std::function<void(const unsigned char *, unsigned int, unsigned int,
           unsigned int, const std::string &)>  _subscriber) override;
 
+      /// \brief Set the background material of this camera
+      /// \param[in] _material Material to set the background to
+      public: virtual void SetBackgroundMaterial(MaterialPtr _material);
+
+      /// \brief Get the background material of this camera
+      /// \return background material
+      public: virtual MaterialPtr BackgroundMaterial() const;
 
       /// \brief Returns the workspace name for the final pass
       /// that stitches all faces.
@@ -179,6 +186,9 @@ namespace gz
       protected: void SetupMSAA(Ogre::CompositorManager2 *_ogreCompMgr,
                                 uint8_t _msaa);
 
+      /// \brief Update the background material
+      private: virtual void UpdateBackgroundMaterial();
+
       /// \brief Saves the CompositorPassSceneDef of each of the 6 passes
       /// defined in WideAngleCamera.compositor data file for later
       /// manipulation.
diff --git a/ogre2/src/Ogre2Scene.cc b/ogre2/src/Ogre2Scene.cc
index 3bdb74b59..17b870cc2 100644
--- a/ogre2/src/Ogre2Scene.cc
+++ b/ogre2/src/Ogre2Scene.cc
@@ -1096,6 +1096,8 @@ WideAngleCameraPtr Ogre2Scene::CreateWideAngleCameraImpl(const unsigned int _id,
 {
   Ogre2WideAngleCameraPtr camera(new Ogre2WideAngleCamera);
   bool result = this->InitObject(camera, _id, _name);
+  if (this->backgroundMaterial)
+    camera->SetBackgroundMaterial(this->backgroundMaterial);
   return (result) ? camera : nullptr;
 }
 
@@ -1535,6 +1537,13 @@ void Ogre2Scene::SetSkyEnabled(bool _enabled)
     {
       camera->SetBackgroundMaterial(skyboxMat);
     }
+    else
+    {
+      auto wideAngleCamera =
+          std::dynamic_pointer_cast<Ogre2WideAngleCamera>(sensor);
+      if (wideAngleCamera)
+        wideAngleCamera->SetBackgroundMaterial(skyboxMat);
+    }
   }
   this->dataPtr->skyEnabled = _enabled;
 }
diff --git a/ogre2/src/Ogre2WideAngleCamera.cc b/ogre2/src/Ogre2WideAngleCamera.cc
index cb70297bd..2c9b25114 100644
--- a/ogre2/src/Ogre2WideAngleCamera.cc
+++ b/ogre2/src/Ogre2WideAngleCamera.cc
@@ -32,6 +32,7 @@
 #include <Compositor/OgreCompositorWorkspace.h>
 #include <Compositor/OgreCompositorWorkspaceListener.h>
 #include <Compositor/Pass/PassQuad/OgreCompositorPassQuad.h>
+#include <Compositor/Pass/PassQuad/OgreCompositorPassQuadDef.h>
 #include <Compositor/Pass/PassScene/OgreCompositorPassSceneDef.h>
 #include <OgreDepthBuffer.h>
 #include <OgreImage2.h>
@@ -150,6 +151,16 @@ class gz::rendering::Ogre2WideAngleCamera::Implementation
   /// \brief See Ogre2WideAngleCameraWorkspaceListenerPrivate
   public: Ogre2WideAngleCameraWorkspaceListenerPrivate workspaceListener;
 
+  /// \brief Name of sky box material
+  public: const std::string kSkyboxMaterialName = "SkyBox";
+
+  /// \brief Background material of the render target
+  public: MaterialPtr backgroundMaterial;
+
+  /// \brief Flag to indicate if the render target background material has
+  /// changed
+  public: bool backgroundMaterialDirty = false;
+
   explicit Implementation(gz::rendering::Ogre2WideAngleCamera &_owner) :
     workspaceListener(_owner)
   {
@@ -216,6 +227,13 @@ void Ogre2WideAngleCamera::PreRender()
 {
   BaseCamera::PreRender();
 
+  if (this->dataPtr->backgroundMaterialDirty)
+  {
+    this->UpdateBackgroundMaterial();
+    this->RemoveAllRenderPasses();
+    this->DestroyTextures();
+  }
+
   {
     auto thisAsCameraPtr =
       std::dynamic_pointer_cast<Camera>(this->shared_from_this());
@@ -621,6 +639,26 @@ void Ogre2WideAngleCamera::CreateWorkspaceDefinition(bool _withMsaa)
   const IdString cubemapPassNodeName =
     _withMsaa ? "WideAngleCameraCubemapPassMsaa" : "WideAngleCameraCubemapPass";
 
+  bool validBackground = this->dataPtr->backgroundMaterial &&
+      !this->dataPtr->backgroundMaterial->EnvironmentMap().empty();
+
+  // render background, e.g. sky, after opaque stuff
+  if (validBackground)
+  {
+    Ogre::CompositorNodeDef *nodeDef = ogreCompMgr->getNodeDefinitionNonConst(
+        cubemapPassNodeName);
+    Ogre::CompositorTargetDef *target0 = nodeDef->getTargetPass(0);
+
+    // quad pass
+    Ogre::CompositorPassQuadDef *passQuad =
+        static_cast<Ogre::CompositorPassQuadDef *>(
+        target0->addPass(Ogre::PASS_QUAD));
+    passQuad->mMaterialName = this->dataPtr->kSkyboxMaterialName + "_"
+        + this->Name();
+    passQuad->mFrustumCorners =
+        Ogre::CompositorPassQuadDef::CAMERA_DIRECTION;
+  }
+
   for (uint32_t faceIdx = 0u; faceIdx < kWideAngleNumCubemapFaces; ++faceIdx)
   {
     const std::string wsDefName = this->WorkspaceDefinitionName(faceIdx);
@@ -969,7 +1007,7 @@ void Ogre2WideAngleCamera::CreateWideAngleTexture()
 
   if (msaa > 1u)
   {
-    SetupMSAA(ogreCompMgr, msaa);
+    this->SetupMSAA(ogreCompMgr, msaa);
   }
 
   this->CreateFacesWorkspaces(msaa > 1u);
@@ -1364,3 +1402,51 @@ void Ogre2WideAngleCameraWorkspaceListenerPrivate::passPreExecute(
     this->owner.PrepareForFinalPass(pass);
   }
 }
+
+//////////////////////////////////////////////////
+void Ogre2WideAngleCamera::SetBackgroundMaterial(MaterialPtr _material)
+{
+  this->dataPtr->backgroundMaterial = _material;
+  this->dataPtr->backgroundMaterialDirty = true;
+}
+
+//////////////////////////////////////////////////
+MaterialPtr Ogre2WideAngleCamera::BackgroundMaterial() const
+{
+  return this->dataPtr->backgroundMaterial;
+}
+
+//////////////////////////////////////////////////
+void Ogre2WideAngleCamera::UpdateBackgroundMaterial()
+{
+  if (!this->dataPtr->backgroundMaterialDirty)
+    return;
+
+  bool validBackground = this->dataPtr->backgroundMaterial &&
+      !this->dataPtr->backgroundMaterial->EnvironmentMap().empty();
+
+  if (validBackground)
+  {
+    Ogre::MaterialManager &matManager = Ogre::MaterialManager::getSingleton();
+    std::string skyMatName = this->dataPtr->kSkyboxMaterialName + "_"
+        + this->Name();
+    auto mat = matManager.getByName(skyMatName);
+    if (!mat)
+    {
+      auto skyboxMat = matManager.getByName(this->dataPtr->kSkyboxMaterialName);
+      if (!skyboxMat)
+      {
+        gzerr << "Unable to find skybox material" << std::endl;
+        return;
+      }
+      mat = skyboxMat->clone(skyMatName);
+    }
+    Ogre::TextureUnitState *texUnit =
+        mat->getTechnique(0u)->getPass(0u)->getTextureUnitState(0u);
+    texUnit->setTextureName(this->dataPtr->backgroundMaterial->EnvironmentMap(),
+        Ogre::TextureTypes::TypeCube);
+    texUnit->setHardwareGammaEnabled(false);
+  }
+
+  this->dataPtr->backgroundMaterialDirty = false;
+}
diff --git a/test/integration/sky.cc b/test/integration/sky.cc
index 0462e74f7..153e8150a 100644
--- a/test/integration/sky.cc
+++ b/test/integration/sky.cc
@@ -25,6 +25,9 @@
 #include "gz/rendering/Image.hh"
 #include "gz/rendering/PixelFormat.hh"
 #include "gz/rendering/Scene.hh"
+#include "gz/rendering/WideAngleCamera.hh"
+
+#include <gz/utils/ExtraTestMacros.hh>
 
 using namespace gz;
 using namespace rendering;
@@ -86,6 +89,100 @@ TEST_F(SkyTest, Sky)
   unsigned int gSkySum = 0u;
   unsigned int bSkySum = 0u;
 
+  for (unsigned int i = 0; i < height; ++i)
+  {
+    for (unsigned int j = 0; j < step; j += channelCount)
+    {
+      unsigned int idx = i * step + j;
+      rSum += data[idx];
+      gSum += data[idx + 1];
+      bSum += data[idx + 2];
+
+      rSkySum += dataSky[idx];
+      gSkySum += dataSky[idx + 1];
+      bSkySum += dataSky[idx + 2];
+    }
+  }
+
+  // sky disabled - red background
+  EXPECT_GT(rSum, 0u);
+  EXPECT_EQ(0u, gSum);
+  EXPECT_EQ(0u, bSum);
+
+  // sky enabled - blue should be the dominant color
+  EXPECT_GT(rSkySum, 0u);
+  EXPECT_GT(gSkySum, 0u);
+  EXPECT_GT(bSkySum, 0u);
+  EXPECT_GT(bSkySum, gSkySum);
+  EXPECT_GT(bSkySum, rSkySum);
+
+  // Clean up
+  engine->DestroyScene(scene);
+}
+
+/////////////////////////////////////////////////
+TEST_F(SkyTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(WideAngleCamera))
+{
+  CHECK_SUPPORTED_ENGINE("ogre2");
+
+  // add resources in build dir
+  engine->AddResourcePath(
+      common::joinPaths(std::string(PROJECT_BUILD_PATH), "src"));
+
+  ScenePtr scene = engine->CreateScene("scene");
+  ASSERT_NE(nullptr, scene);
+  scene->SetAmbientLight(0.3, 0.3, 0.3);
+
+  scene->SetBackgroundColor(1.0, 0.0, 0.0);
+
+  VisualPtr root = scene->RootVisual();
+  ASSERT_NE(nullptr, root);
+
+  // create  camera
+  auto camera = scene->CreateWideAngleCamera("WideAngleCamera");
+  ASSERT_NE(nullptr, camera);
+
+  CameraLens lens;
+  lens.SetCustomMappingFunction(1.05, 4.0, AFT_TAN, 1.0, 0.0);
+  lens.SetType(MFT_CUSTOM);
+  lens.SetCutOffAngle(GZ_PI);
+
+  camera->SetLens(lens);
+  camera->SetHFOV(2.6);
+  camera->SetImageWidth(100);
+  camera->SetImageHeight(100);
+  camera->SetAspectRatio(1.333);
+  camera->SetLocalPosition(0.0, 0.0, 0.0);
+
+  // look up into the sky
+  camera->SetLocalRotation(math::Quaterniond(0, -GZ_PI/2.0, 0));
+  root->AddChild(camera);
+
+  // capture original image with red background
+  Image image = camera->CreateImage();
+  camera->Capture(image);
+
+  // Enable sky
+  scene->SetSkyEnabled(true);
+
+  // capture image with sky enabled
+  Image imageSky = camera->CreateImage();
+  camera->Capture(imageSky);
+
+  // Compare image pixels
+  unsigned char *data = image.Data<unsigned char>();
+  unsigned char *dataSky = imageSky.Data<unsigned char>();
+  unsigned int height = camera->ImageHeight();
+  unsigned int width = camera->ImageWidth();
+  unsigned int channelCount = PixelUtil::ChannelCount(camera->ImageFormat());
+  unsigned int step = width * channelCount;
+
+  unsigned int rSum = 0u;
+  unsigned int gSum = 0u;
+  unsigned int bSum = 0u;
+  unsigned int rSkySum = 0u;
+  unsigned int gSkySum = 0u;
+  unsigned int bSkySum = 0u;
 
   for (unsigned int i = 0; i < height; ++i)
   {

From e4687bbf479d9a90162ca29231a6127b861f1050 Mon Sep 17 00:00:00 2001
From: Ian Chen <ichen@openrobotics.org>
Date: Wed, 27 Sep 2023 23:02:58 +0000
Subject: [PATCH 2/2] remove virtual

Signed-off-by: Ian Chen <ichen@openrobotics.org>
---
 ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh b/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh
index 44e3c28b5..2979ddec7 100644
--- a/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh
+++ b/ogre2/include/gz/rendering/ogre2/Ogre2WideAngleCamera.hh
@@ -112,11 +112,11 @@ namespace gz
 
       /// \brief Set the background material of this camera
       /// \param[in] _material Material to set the background to
-      public: virtual void SetBackgroundMaterial(MaterialPtr _material);
+      public: void SetBackgroundMaterial(MaterialPtr _material);
 
       /// \brief Get the background material of this camera
       /// \return background material
-      public: virtual MaterialPtr BackgroundMaterial() const;
+      public: MaterialPtr BackgroundMaterial() const;
 
       /// \brief Returns the workspace name for the final pass
       /// that stitches all faces.
@@ -187,7 +187,7 @@ namespace gz
                                 uint8_t _msaa);
 
       /// \brief Update the background material
-      private: virtual void UpdateBackgroundMaterial();
+      private: void UpdateBackgroundMaterial();
 
       /// \brief Saves the CompositorPassSceneDef of each of the 6 passes
       /// defined in WideAngleCamera.compositor data file for later