diff --git a/Changelog.md b/Changelog.md index 0082c5f50..8878ad5f6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -56,6 +56,48 @@ ### Ignition Rendering 4.X +### Ignition Rendering 4.8.0 (2021-06-18) + +1. relax gaussian test tolerance + * [Pull request #344](https://github.com/ignitionrobotics/ign-rendering/pull/344) + +1. recreate node only when needed + * [Pull request #342](https://github.com/ignitionrobotics/ign-rendering/pull/342) + +1. Backport memory fixes found by ASAN + * [Pull request #340](https://github.com/ignitionrobotics/ign-rendering/pull/340) + +1. Re-enable part of depth camera test on macOS + * [Pull request #335](https://github.com/ignitionrobotics/ign-rendering/pull/335) + * [Pull request #347](https://github.com/ignitionrobotics/ign-rendering/pull/347) + +1. Fix depth alpha + * [Pull request #316](https://github.com/ignitionrobotics/ign-rendering/pull/316) + +1. Fix floating point precision bug handling alpha channel (#332) + * [Pull request #333](https://github.com/ignitionrobotics/ign-rendering/pull/333) + +1. Include MoveTo Helper class to ign-rendering + * [Pull request #311](https://github.com/ignitionrobotics/ign-rendering/pull/311) + +1. Remove `tools/code_check` and update codecov + * [Pull request #321](https://github.com/ignitionrobotics/ign-rendering/pull/321) + +1. [OGRE 1.x] Uniform buffer shader support + * [Pull request #294](https://github.com/ignitionrobotics/ign-rendering/pull/294) + +1. Helper function to get a scene + * [Pull request #320](https://github.com/ignitionrobotics/ign-rendering/pull/320) + +1. Reduce lidar data discretization + * [Pull request #296](https://github.com/ignitionrobotics/ign-rendering/pull/296) + +1. Prevent console warnings when multiple texture coordinates are present + * [Pull request #301](https://github.com/ignitionrobotics/ign-rendering/pull/301) + +1. Added command line argument to pick version of Ogre + * [Pull request #277](https://github.com/ignitionrobotics/ign-rendering/pull/277) + ### Ignition Rendering 4.7.0 (2021-03-17) 1. Enable depth write in particles example diff --git a/examples/custom_shaders_uniforms/CMakeLists.txt b/examples/custom_shaders_uniforms/CMakeLists.txt index 9909e78be..5fc59e542 100644 --- a/examples/custom_shaders_uniforms/CMakeLists.txt +++ b/examples/custom_shaders_uniforms/CMakeLists.txt @@ -5,7 +5,7 @@ include_directories(SYSTEM ${PROJECT_BINARY_DIR} ) -find_package(ignition-rendering4) +find_package(ignition-rendering6) set(TARGET_THIRD_PARTY_DEPENDS "") diff --git a/examples/visualization_demo/CMakeLists.txt b/examples/visualization_demo/CMakeLists.txt new file mode 100644 index 000000000..21474cd5d --- /dev/null +++ b/examples/visualization_demo/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +project(ignition-rendering-visualization-demo) + +find_package(ignition-rendering6) + +if (APPLE OR UNIX) + find_package(GLUT REQUIRED) + include_directories(SYSTEM ${GLUT_INCLUDE_DIRS}) + link_directories(${GLUT_LIBRARY_DIRS}) + + find_package(OpenGL REQUIRED) + include_directories(SYSTEM ${OpenGL_INCLUDE_DIRS}) + link_directories(${OpenGL_LIBRARY_DIRS}) + + set(TARGET_THIRD_PARTY_DEPENDS + ${TARGET_THIRD_PARTY_DEPENDS} + ${OPENGL_LIBRARIES} + ${GLUT_LIBRARIES} + ) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") +endif() + +if (NOT APPLE) + find_package(GLEW REQUIRED) + if (WIN32) + set(TARGET_THIRD_PARTY_DEPENDS + ${TARGET_THIRD_PARTY_DEPENDS} + GLEW::glew + ) + else () + set(TARGET_THIRD_PARTY_DEPENDS + ${TARGET_THIRD_PARTY_DEPENDS} + GLEW + ) + endif() +endif() + +if (WIN32) + find_package(FreeGLUT REQUIRED) + set(TARGET_THIRD_PARTY_DEPENDS ${TARGET_THIRD_PARTY_DEPENDS} FreeGLUT::freeglut) +endif() + +add_executable(visualization_demo Main.cc GlutWindow.cc) + +target_link_libraries(visualization_demo + ${IGNITION-RENDERING_LIBRARIES} + ${TARGET_THIRD_PARTY_DEPENDS} +) diff --git a/examples/visualization_demo/GlutWindow.cc b/examples/visualization_demo/GlutWindow.cc new file mode 100644 index 000000000..5aae62c70 --- /dev/null +++ b/examples/visualization_demo/GlutWindow.cc @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * 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. + * + */ + +#if __APPLE__ + #include + #include + #include +#elif _WIN32 + #define NOMINMAX + // Must include this before GL/gl.h + #include + #include + // OpenGL utilities header file + #include + #include "Wingdi.h" +#else + #include + #include + #include +#endif + +#if !defined(__APPLE__) && !defined(_WIN32) + #include +#endif + +#include + +#include +#include +#include +#include + +#include "GlutWindow.hh" + +#define KEY_ESC 27 +#define KEY_TAB 9 + +////////////////////////////////////////////////// +unsigned int imgw = 0; +unsigned int imgh = 0; + +std::vector g_cameras; +ir::CameraPtr g_camera; +ir::CameraPtr g_currCamera; +unsigned int g_cameraIndex = 0; +ir::ImagePtr g_image; + +bool g_initContext = false; + +#if __APPLE__ + CGLContextObj g_context; + CGLContextObj g_glutContext; +#elif _WIN32 + HGLRC g_context = 0; + HDC g_display = 0; + HGLRC g_glutContext = 0; + HDC g_glutDisplay = 0; +#else + GLXContext g_context; + Display *g_display; + GLXDrawable g_drawable; + GLXContext g_glutContext; + Display *g_glutDisplay; + GLXDrawable g_glutDrawable; +#endif + +double g_offset = 0.0; + +////////////////////////////////////////////////// +void updateCameras() +{ + double angle = g_offset / 2 * M_PI; + double x = sin(angle) * 3.0 + 3.0; + double y = cos(angle) * 3.0; + for (ir::CameraPtr camera : g_cameras) + { + camera->SetLocalPosition(x, y, 0.0); + } + + g_offset += 0.0005; +} + +////////////////////////////////////////////////// +void displayCB() +{ +#if __APPLE__ + CGLSetCurrentContext(g_context); +#elif _WIN32 + if(!wglMakeCurrent(g_display, g_context)) + { + std::cerr << "Not able to wglMakeCurrent" << '\n'; + exit(-1); + } +#else + if (g_display) + { + glXMakeCurrent(g_display, g_drawable, g_context); + } +#endif + + g_cameras[g_cameraIndex]->Capture(*g_image); + +#if __APPLE__ + CGLSetCurrentContext(g_glutContext); +#elif _WIN32 + wglMakeCurrent(g_glutDisplay, g_glutContext); +#else + glXMakeCurrent(g_glutDisplay, g_glutDrawable, g_glutContext); +#endif + + unsigned char *data = g_image->Data(); + + glClearColor(0.5, 0.5, 0.5, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glPixelZoom(1, -1); + glRasterPos2f(-1, 1); + glDrawPixels(imgw, imgh, GL_RGB, GL_UNSIGNED_BYTE, data); + + glutSwapBuffers(); + updateCameras(); +} + +////////////////////////////////////////////////// +void idleCB() +{ + glutPostRedisplay(); +} + +////////////////////////////////////////////////// +void keyboardCB(unsigned char _key, int, int) +{ + if (_key == KEY_ESC || _key == 'q' || _key == 'Q') + { + exit(0); + } + else if (_key == KEY_TAB) + { + g_cameraIndex = (g_cameraIndex + 1) % g_cameras.size(); + } +} + +////////////////////////////////////////////////// +void initCamera(ir::CameraPtr _camera) +{ + g_camera = _camera; + imgw = g_camera->ImageWidth(); + imgh = g_camera->ImageHeight(); + ir::Image image = g_camera->CreateImage(); + g_image = std::make_shared(image); + g_camera->Capture(*g_image); +} + +////////////////////////////////////////////////// +void initContext() +{ + glutInitDisplayMode(GLUT_DOUBLE); + glutInitWindowPosition(0, 0); + glutInitWindowSize(imgw, imgh); + glutCreateWindow("Visualization Demo"); + glutDisplayFunc(displayCB); + glutIdleFunc(idleCB); + glutKeyboardFunc(keyboardCB); +} + +////////////////////////////////////////////////// +void printUsage() +{ + std::cout << "===============================" << std::endl; + std::cout << " TAB - Switch render engines " << std::endl; + std::cout << " ESC - Exit " << std::endl; + std::cout << "===============================" << std::endl; +} + +////////////////////////////////////////////////// +void run(std::vector _cameras) +{ + if (_cameras.empty()) + { + ignerr << "No cameras found. Scene will not be rendered" << std::endl; + return; + } + +#if __APPLE__ + g_context = CGLGetCurrentContext(); +#elif _WIN32 + g_context = wglGetCurrentContext(); + g_display = wglGetCurrentDC(); +#else + g_context = glXGetCurrentContext(); + g_display = glXGetCurrentDisplay(); + g_drawable = glXGetCurrentDrawable(); +#endif + + g_cameras = _cameras; + initCamera(_cameras[0]); + initContext(); + printUsage(); + +#if __APPLE__ + g_glutContext = CGLGetCurrentContext(); +#elif _WIN32 + g_glutContext = wglGetCurrentContext(); + g_glutDisplay = wglGetCurrentDC(); +#else + g_glutDisplay = glXGetCurrentDisplay(); + g_glutDrawable = glXGetCurrentDrawable(); + g_glutContext = glXGetCurrentContext(); +#endif + + glutMainLoop(); +} diff --git a/examples/visualization_demo/GlutWindow.hh b/examples/visualization_demo/GlutWindow.hh new file mode 100644 index 000000000..2c636d1a9 --- /dev/null +++ b/examples/visualization_demo/GlutWindow.hh @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * 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. + * + */ +#ifndef IGNITION_RENDERING_EXAMPLES_VISUALIZATION_DEMO_GLUTWINDOW_HH_ +#define IGNITION_RENDERING_EXAMPLES_VISUALIZATION_DEMO_GLUTWINDOW_HH_ + +#include +#include "ignition/rendering/RenderTypes.hh" + +namespace ir = ignition::rendering; + +/// \brief Run the demo and render the scene from the cameras +/// \param[in] _cameras Cameras in the scene +void run(std::vector _cameras); + +#endif diff --git a/examples/visualization_demo/Main.cc b/examples/visualization_demo/Main.cc new file mode 100644 index 000000000..68540c77f --- /dev/null +++ b/examples/visualization_demo/Main.cc @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * 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. + * + */ + +#if defined(__APPLE__) + #include + #include +#elif _WIN32 + #define NOMINMAX + // Must include this before GL/gl.h + #include + #include + // OpenGL utilities header file + #include + // OpenGL utilities header file + #include +#else + #include + #include + #include +#endif + +#include +#include + +#include +#include + +#include "GlutWindow.hh" + +using namespace ignition; +using namespace rendering; + +////////////////////////////////////////////////// +void buildScene(ScenePtr _scene) +{ + // initialize _scene + _scene->SetAmbientLight(0.3, 0.3, 0.3); + VisualPtr root = _scene->RootVisual(); + + // create directional light + DirectionalLightPtr light0 = _scene->CreateDirectionalLight(); + light0->SetDirection(-0.5, 0.5, -1); + light0->SetDiffuseColor(0.5, 0.5, 0.5); + light0->SetSpecularColor(0.5, 0.5, 0.5); + root->AddChild(light0); + + // create point light + PointLightPtr light1 = _scene->CreatePointLight(); + light1->SetDiffuseColor(0.5, 0.5, 0.5); + light1->SetSpecularColor(0.5, 0.5, 0.5); + light1->SetLocalPosition(5, -5, 10); + root->AddChild(light1); + + // create point light + PointLightPtr light2 = _scene->CreatePointLight(); + light2->SetDiffuseColor(0.5, 0.5, 0.5); + light2->SetSpecularColor(0.5, 0.5, 0.5); + light2->SetLocalPosition(3, 5, 5); + root->AddChild(light2); + + // create green material + MaterialPtr green = _scene->CreateMaterial(); + green->SetAmbient(0.0, 0.5, 0.0); + green->SetDiffuse(0.0, 0.7, 0.0); + green->SetSpecular(0.5, 0.5, 0.5); + green->SetShininess(50); + green->SetReflectivity(0); + + // create center visual + VisualPtr center = _scene->CreateVisual(); + center->AddGeometry(_scene->CreateSphere()); + center->SetLocalPosition(3, 0, 0); + center->SetLocalScale(0.1, 0.1, 0.1); + center->SetMaterial(green); + root->AddChild(center); + + // create red material + MaterialPtr red = _scene->CreateMaterial(); + red->SetAmbient(0.5, 0.0, 0.0); + red->SetDiffuse(1.0, 0.0, 0.0); + red->SetSpecular(0.5, 0.5, 0.5); + red->SetShininess(50); + red->SetReflectivity(0); + red->SetRenderOrder(3); + + // create sphere visual + VisualPtr sphere = _scene->CreateVisual(); + sphere->AddGeometry(_scene->CreateSphere()); + sphere->SetOrigin(0.0, -0.5, 0.0); + sphere->SetLocalPosition(3, -1, 0); + sphere->SetLocalRotation(0, 0, 0); + sphere->SetLocalScale(1, 1, 1); + sphere->SetMaterial(red); + sphere->SetWireframe(true); + root->AddChild(sphere); + + // create white material + MaterialPtr white = _scene->CreateMaterial(); + white->SetAmbient(0.5, 0.5, 0.5); + white->SetDiffuse(0.8, 0.8, 0.8); + white->SetReceiveShadows(true); + white->SetReflectivity(0); + white->SetRenderOrder(0); + + // create plane visual + VisualPtr plane = _scene->CreateVisual(); + plane->AddGeometry(_scene->CreatePlane()); + plane->SetLocalScale(5, 8, 1); + plane->SetLocalPosition(3, 0, -0.5); + plane->SetMaterial(white); + root->AddChild(plane); + + // create inertia visual + InertiaVisualPtr inertiaVisual = _scene->CreateInertiaVisual(); + ignition::math::MassMatrix3d massMatrix(1.0, {0.1, 0.1, 0.1}, {0.0, 0.0, 0.0}); + ignition::math::Pose3d p(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + ignition::math::Inertiald inertial{massMatrix, p}; + inertiaVisual->SetInertial(inertial); + inertiaVisual->SetLocalPosition(3.2, 1.5, 0); + root->AddChild(inertiaVisual); + + // create camera + CameraPtr camera = _scene->CreateCamera("camera"); + camera->SetLocalPosition(0.0, 0.0, 0.0); + camera->SetLocalRotation(0.0, 0.0, 0.0); + camera->SetImageWidth(800); + camera->SetImageHeight(600); + camera->SetAntiAliasing(2); + camera->SetAspectRatio(1.333); + camera->SetHFOV(IGN_PI / 2); + root->AddChild(camera); + + // track target + camera->SetTrackTarget(center); +} + +////////////////////////////////////////////////// +CameraPtr createCamera(const std::string &_engineName) +{ + // create and populate scene + RenderEngine *engine = rendering::engine(_engineName); + if (!engine) + { + std::cout << "Engine '" << _engineName + << "' is not supported" << std::endl; + return CameraPtr(); + } + ScenePtr scene = engine->CreateScene("scene"); + buildScene(scene); + + // return camera sensor + SensorPtr sensor = scene->SensorByName("camera"); + return std::dynamic_pointer_cast(sensor); +} + +////////////////////////////////////////////////// +int main(int _argc, char** _argv) +{ + glutInit(&_argc, _argv); + + // Expose engine name to command line because we can't instantiate both + // ogre and ogre2 at the same time + std::string engine("ogre"); + if (_argc > 1) + { + engine = _argv[1]; + } + + common::Console::SetVerbosity(4); + std::vector engineNames; + std::vector cameras; + + engineNames.push_back(engine); + engineNames.push_back("optix"); + + for (auto engineName : engineNames) + { + try + { + CameraPtr camera = createCamera(engineName); + if (camera) + { + cameras.push_back(camera); + } + } + catch (...) + { + std::cerr << "Error starting up: " << engineName << std::endl; + } + } + run(cameras); + return 0; +} diff --git a/include/ignition/rendering/InertiaVisual.hh b/include/ignition/rendering/InertiaVisual.hh new file mode 100644 index 000000000..2b233aef0 --- /dev/null +++ b/include/ignition/rendering/InertiaVisual.hh @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * 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. + * + */ +#ifndef IGNITION_RENDERING_INERTIAVISUAL_HH_ +#define IGNITION_RENDERING_INERTIAVISUAL_HH_ + +#include +#include "ignition/rendering/config.hh" +#include "ignition/rendering/Object.hh" +#include "ignition/rendering/RenderTypes.hh" +#include "ignition/rendering/Visual.hh" + +namespace ignition +{ + namespace rendering + { + inline namespace IGNITION_RENDERING_VERSION_NAMESPACE { + + /// \class InertiaVisual InertiaVisual.hh + /// ignition/rendering/InertiaVisual.hh + /// \brief Represents a inertia visual + class IGNITION_RENDERING_VISIBLE InertiaVisual : + public virtual Visual + { + /// \brief Destructor + public: virtual ~InertiaVisual() {} + + /// \brief Set the inertial component of the visual + /// \param[in] _inertial Inertial component of the visual + public: virtual void SetInertial( + const ignition::math::Inertiald &_inertial) = 0; + + /// \brief Load the Inertia visual from its pose and scale + /// \param[in] _pose Pose of the Inertia visual + /// \param[in] _scale Scale factor of the box visual + public: virtual void Load(const ignition::math::Pose3d &_pose, + const ignition::math::Vector3d &_scale) = 0; + + /// \brief Get the box visual + /// \return Pointer to the box visual + public: virtual VisualPtr BoxVisual() const = 0; + }; + } + } +} +#endif diff --git a/include/ignition/rendering/RenderTypes.hh b/include/ignition/rendering/RenderTypes.hh index 23ff5e202..b332cc6b6 100644 --- a/include/ignition/rendering/RenderTypes.hh +++ b/include/ignition/rendering/RenderTypes.hh @@ -59,6 +59,7 @@ namespace ignition class Grid; class Heightmap; class Image; + class InertiaVisual; class Light; class LightVisual; class JointVisual; @@ -149,6 +150,10 @@ namespace ignition /// \brief Shared pointer to Image typedef shared_ptr ImagePtr; + /// \def InertiaVisualPtr + /// \def Shared pointer to InertiaVisual + typedef shared_ptr InertiaVisualPtr; + /// \def LightPtr /// \brief Shared pointer to Light typedef shared_ptr LightPtr; diff --git a/include/ignition/rendering/Scene.hh b/include/ignition/rendering/Scene.hh index b61404580..e1e915096 100644 --- a/include/ignition/rendering/Scene.hh +++ b/include/ignition/rendering/Scene.hh @@ -823,6 +823,35 @@ namespace ignition public: virtual GizmoVisualPtr CreateGizmoVisual( unsigned int _id, const std::string &_name) = 0; + /// \brief Create new inertia visual. A unique ID and name will + /// automatically be assigned to the inertia visual. + /// \return The created inertia visual + public: virtual InertiaVisualPtr CreateInertiaVisual() = 0; + + /// \brief Create new inertia visual with the given ID. A unique name + /// will automatically be assigned to the visual. If the given ID is + /// already in use, NULL will be returned. + /// \param[in] _id ID of the new inertia visual + /// \return The created light visual + public: virtual InertiaVisualPtr CreateInertiaVisual( + unsigned int _id) = 0; + + /// \brief Create new inertia visual with the given name. A unique ID + /// will automatically be assigned to the visual. If the given name is + /// already in use, NULL will be returned. + /// \param[in] _name Name of the new inertia visual + /// \return The created light visual + public: virtual InertiaVisualPtr CreateInertiaVisual( + const std::string &_name) = 0; + + /// \brief Create new inertia visual with the given name. If either the + /// given ID or name is already in use, NULL will be returned. + /// \param[in] _id ID of the new inertia visual + /// \param[in] _name Name of the new inertia visual + /// \return The created inertia visual + public: virtual InertiaVisualPtr CreateInertiaVisual( + unsigned int _id, const std::string &_name) = 0; + /// \brief Create new light visual. A unique ID and name will /// automatically be assigned to the light visual. /// \return The created light visual diff --git a/include/ignition/rendering/base/BaseInertiaVisual.hh b/include/ignition/rendering/base/BaseInertiaVisual.hh new file mode 100644 index 000000000..9be984395 --- /dev/null +++ b/include/ignition/rendering/base/BaseInertiaVisual.hh @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * 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. + * + */ +#ifndef IGNITION_RENDERING_BASE_BASEINERTIAVISUAL_HH_ +#define IGNITION_RENDERING_BASE_BASEINERTIAVISUAL_HH_ + +#include "ignition/common/Console.hh" + +#include "ignition/rendering/base/BaseObject.hh" +#include "ignition/rendering/base/BaseRenderTypes.hh" +#include "ignition/rendering/InertiaVisual.hh" +#include "ignition/rendering/Scene.hh" + +namespace ignition +{ + namespace rendering + { + inline namespace IGNITION_RENDERING_VERSION_NAMESPACE { + // + /// \brief Base implementation of an inertia visual + template + class BaseInertiaVisual : + public virtual InertiaVisual, + public virtual T + { + /// \brief Constructor + protected: BaseInertiaVisual(); + + /// \brief Destructor + public: virtual ~BaseInertiaVisual(); + + // Documentation inherited. + protected: virtual void Init() override; + + // Documentation inherited. + protected: virtual void PreRender() override; + + // Documentation inherited. + public: virtual void SetInertial( + const ignition::math::Inertiald &_inertial) override; + + // Documentation inherited. + public: virtual void Load(const ignition::math::Pose3d &, + const ignition::math::Vector3d &) override; + + // Documentation inherited + public: virtual VisualPtr BoxVisual() const override; + }; + + ////////////////////////////////////////////////// + template + BaseInertiaVisual::BaseInertiaVisual() + { + } + + ////////////////////////////////////////////////// + template + BaseInertiaVisual::~BaseInertiaVisual() + { + } + + ///////////////////////////////////////////////// + template + void BaseInertiaVisual::PreRender() + { + T::PreRender(); + } + + ////////////////////////////////////////////////// + template + void BaseInertiaVisual::Init() + { + T::Init(); + } + + ////////////////////////////////////////////////// + template + void BaseInertiaVisual::SetInertial( + const ignition::math::Inertiald &_inertial) + { + auto xyz = _inertial.Pose().Pos(); + auto q = _inertial.Pose().Rot(); + + // Use ignition::math::MassMatrix3 to compute + // equivalent box size and rotation + auto m = _inertial.MassMatrix(); + ignition::math::Vector3d boxScale; + ignition::math::Quaterniond boxRot; + if (!m.EquivalentBox(boxScale, boxRot)) + { + // Invalid inertia, load with default scale + ignlog << "The link is static or has unrealistic " + << "inertia, so the equivalent inertia box will not be shown.\n"; + } + else + { + // Apply additional rotation by boxRot + this->Load(ignition::math::Pose3d(xyz, q * boxRot), boxScale); + } + } + + ////////////////////////////////////////////////// + template + void BaseInertiaVisual::Load(const ignition::math::Pose3d &, + const ignition::math::Vector3d &) + { + // no op + } + + ////////////////////////////////////////////////// + template + VisualPtr BaseInertiaVisual::BoxVisual() const + { + return nullptr; + } + } + } +} +#endif diff --git a/include/ignition/rendering/base/BaseScene.hh b/include/ignition/rendering/base/BaseScene.hh index 31670265b..d47276408 100644 --- a/include/ignition/rendering/base/BaseScene.hh +++ b/include/ignition/rendering/base/BaseScene.hh @@ -265,6 +265,12 @@ namespace ignition public: virtual PointLightPtr CreatePointLight(unsigned int _id, const std::string &_name) override; + /// \brief Implementation for creating Inertia visual. + /// \param[in] _id Unique id + /// \param[in] _name Name of inertia visual + protected: virtual InertiaVisualPtr CreateInertiaVisualImpl( + unsigned int _id, const std::string &_name) = 0; + /// \brief Implementation for creating Light visual. /// \param[in] _id Unique id /// \param[in] _name Name of light visual @@ -360,6 +366,21 @@ namespace ignition public: virtual AxisVisualPtr CreateAxisVisual(unsigned int _id, const std::string &_name) override; + // Documentation inherited + public: virtual InertiaVisualPtr CreateInertiaVisual() override; + + // Documentation inherited + public: virtual InertiaVisualPtr CreateInertiaVisual(unsigned int _id) + override; + + // Documentation inherited + public: virtual InertiaVisualPtr CreateInertiaVisual( + const std::string &_name) override; + + // Documentation inherited + public: virtual InertiaVisualPtr CreateInertiaVisual(unsigned int _id, + const std::string &_name) override; + // Documentation inherited public: virtual LightVisualPtr CreateLightVisual() override; diff --git a/ogre/include/ignition/rendering/ogre/OgreInertiaVisual.hh b/ogre/include/ignition/rendering/ogre/OgreInertiaVisual.hh new file mode 100644 index 000000000..75b0c78eb --- /dev/null +++ b/ogre/include/ignition/rendering/ogre/OgreInertiaVisual.hh @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * 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. + * +*/ + +#ifndef IGNITION_RENDERING_OGRE_OGREINERTIAVISUAL_HH_ +#define IGNITION_RENDERING_OGRE_OGREINERTIAVISUAL_HH_ + +#include + +#include "ignition/rendering/base/BaseInertiaVisual.hh" +#include "ignition/rendering/ogre/OgreIncludes.hh" +#include "ignition/rendering/ogre/OgreMaterial.hh" +#include "ignition/rendering/ogre/OgreVisual.hh" + +namespace Ogre +{ + class MovableObject; +} + +namespace ignition +{ + namespace rendering + { + inline namespace IGNITION_RENDERING_VERSION_NAMESPACE { + + // Forward declaration + class OgreInertiaVisualPrivate; + + class IGNITION_RENDERING_OGRE_VISIBLE OgreInertiaVisual : + public BaseInertiaVisual + { + /// \brief Constructor + protected: OgreInertiaVisual(); + + /// \brief Destructor + public: virtual ~OgreInertiaVisual(); + + // Documentation inherited. + public: virtual void Init() override; + + // Documentation inherited. + public: virtual void PreRender() override; + + // Documentation inherited. + public: Ogre::MovableObject *OgreObject() const; + + /// \brief Load the Inertia visual from its pose and scale + /// \param[in] _pose Pose of the Inertia visual + /// \param[in] _scale Scale factor of the box visual + public: void Load(const ignition::math::Pose3d &_pose, + const ignition::math::Vector3d &_scale) override; + + /// \brief Get the box visual + /// \return Pointer to the box visual + public: VisualPtr BoxVisual() const override; + + // Documentation inherited. + public: virtual MaterialPtr Material() const; + + // Documentation inherited. + public: virtual void SetMaterial( + MaterialPtr _material, bool _unique) override; + + /// \brief Set material to line geometry. + /// \param[in] _material Ogre material. + protected: virtual void SetMaterialImpl(OgreMaterialPtr _material); + + private: friend class OgreScene; + + /// \brief Private data class + private: std::unique_ptr dataPtr; + }; + } + } +} +#endif diff --git a/ogre/include/ignition/rendering/ogre/OgreRenderTypes.hh b/ogre/include/ignition/rendering/ogre/OgreRenderTypes.hh index 3743b77de..40736ab43 100644 --- a/ogre/include/ignition/rendering/ogre/OgreRenderTypes.hh +++ b/ogre/include/ignition/rendering/ogre/OgreRenderTypes.hh @@ -37,6 +37,7 @@ namespace ignition class OgreGpuRays; class OgreGrid; class OgreHeightmap; + class OgreInertiaVisual; class OgreJointVisual; class OgreLight; class OgreLightVisual; @@ -86,6 +87,7 @@ namespace ignition typedef shared_ptr OgreGpuRaysPtr; typedef shared_ptr OgreGridPtr; typedef shared_ptr OgreHeightmapPtr; + typedef shared_ptr OgreInertiaVisualPtr; typedef shared_ptr OgreJointVisualPtr; typedef shared_ptr OgreLightPtr; typedef shared_ptr OgreLightVisualPtr; diff --git a/ogre/include/ignition/rendering/ogre/OgreScene.hh b/ogre/include/ignition/rendering/ogre/OgreScene.hh index fca6a5c2d..9d1282682 100644 --- a/ogre/include/ignition/rendering/ogre/OgreScene.hh +++ b/ogre/include/ignition/rendering/ogre/OgreScene.hh @@ -79,6 +79,9 @@ namespace ignition unsigned int _id, const std::string &_name) override; // Documentation inherited + protected: virtual InertiaVisualPtr CreateInertiaVisualImpl( + unsigned int _id, const std::string &_name) override; + protected: virtual LightVisualPtr CreateLightVisualImpl(unsigned int _id, const std::string &_name) override; diff --git a/ogre/src/OgreInertiaVisual.cc b/ogre/src/OgreInertiaVisual.cc new file mode 100644 index 000000000..d95571bc7 --- /dev/null +++ b/ogre/src/OgreInertiaVisual.cc @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * 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 "ignition/rendering/ogre/OgreInertiaVisual.hh" +#include "ignition/rendering/ogre/OgreDynamicLines.hh" + +#include "ignition/rendering/ogre/OgreScene.hh" + +class ignition::rendering::OgreInertiaVisualPrivate +{ + /// \brief Grid materal + public: OgreMaterialPtr material = nullptr; + + /// \brief Ogre renderable used to render the cross lines. + public: std::shared_ptr crossLines = nullptr; + + /// \brief Box visual + public: VisualPtr boxVis = nullptr; +}; + +using namespace ignition; +using namespace rendering; + +////////////////////////////////////////////////// +OgreInertiaVisual::OgreInertiaVisual() + : dataPtr(new OgreInertiaVisualPrivate) +{ +} + +////////////////////////////////////////////////// +OgreInertiaVisual::~OgreInertiaVisual() +{ +} + +////////////////////////////////////////////////// +void OgreInertiaVisual::PreRender() +{ +} + +////////////////////////////////////////////////// +void OgreInertiaVisual::Init() +{ + BaseInertiaVisual::Init(); +} + +////////////////////////////////////////////////// +Ogre::MovableObject *OgreInertiaVisual::OgreObject() const +{ + std::shared_ptr mv = + std::dynamic_pointer_cast(this->dataPtr->crossLines); + return mv.get(); +} + +////////////////////////////////////////////////// +void OgreInertiaVisual::Load(const ignition::math::Pose3d &_pose, + const ignition::math::Vector3d &_scale) +{ + if (!this->dataPtr->crossLines) + { + this->dataPtr->crossLines = std::shared_ptr( + new OgreDynamicLines(MT_LINE_LIST)); + this->ogreNode->attachObject(this->OgreObject()); + MaterialPtr inertiaVisualMaterial = + this->Scene()->Material("Default/TransGreen"); + this->SetMaterial(inertiaVisualMaterial, false); + } + + if (!this->dataPtr->boxVis) + { + this->dataPtr->boxVis = this->Scene()->CreateVisual(); + this->dataPtr->boxVis->AddGeometry(this->Scene()->CreateBox()); + this->dataPtr->boxVis->SetMaterial("Default/TransPurple"); + this->AddChild(this->dataPtr->boxVis); + } + + // Inertia position indicator + ignition::math::Vector3d p1(0, 0, -2*_scale.Z()); + ignition::math::Vector3d p2(0, 0, 2*_scale.Z()); + ignition::math::Vector3d p3(0, -2*_scale.Y(), 0); + ignition::math::Vector3d p4(0, 2*_scale.Y(), 0); + ignition::math::Vector3d p5(-2*_scale.X(), 0, 0); + ignition::math::Vector3d p6(2*_scale.X(), 0, 0); + p1 = _pose.Rot().RotateVector(p1); + p2 = _pose.Rot().RotateVector(p2); + p3 = _pose.Rot().RotateVector(p3); + p4 = _pose.Rot().RotateVector(p4); + p5 = _pose.Rot().RotateVector(p5); + p6 = _pose.Rot().RotateVector(p6); + p1 += _pose.Pos(); + p2 += _pose.Pos(); + p3 += _pose.Pos(); + p4 += _pose.Pos(); + p5 += _pose.Pos(); + p6 += _pose.Pos(); + + this->dataPtr->crossLines->AddPoint(p1); + this->dataPtr->crossLines->AddPoint(p2); + this->dataPtr->crossLines->AddPoint(p3); + this->dataPtr->crossLines->AddPoint(p4); + this->dataPtr->crossLines->AddPoint(p5); + this->dataPtr->crossLines->AddPoint(p6); + + this->dataPtr->crossLines->Update(); + this->ogreNode->setVisible(true); + + this->dataPtr->boxVis->SetLocalScale(_scale); + this->dataPtr->boxVis->SetLocalPosition(_pose.Pos()); + this->dataPtr->boxVis->SetLocalRotation(_pose.Rot()); +} + +////////////////////////////////////////////////// +VisualPtr OgreInertiaVisual::BoxVisual() const +{ + return this->dataPtr->boxVis; +} + +////////////////////////////////////////////////// +void OgreInertiaVisual::SetMaterial(MaterialPtr _material, bool _unique) +{ + _material = (_unique) ? _material->Clone() : _material; + + OgreMaterialPtr derived = + std::dynamic_pointer_cast(_material); + + if (!derived) + { + ignerr << "Cannot assign material created by another render-engine" + << std::endl; + + return; + } + + this->SetMaterialImpl(derived); +} + +////////////////////////////////////////////////// +void OgreInertiaVisual::SetMaterialImpl(OgreMaterialPtr _material) +{ + std::string materialName = _material->Name(); + Ogre::MaterialPtr ogreMaterial = _material->Material(); + +// OGRE 1.10.7 +#if (OGRE_VERSION <= ((1 << 16) | (10 << 8) | 7)) + this->dataPtr->crossLines->setMaterial(materialName); +#else + this->dataPtr->crossLines->setMaterial(ogreMaterial); +#endif + this->dataPtr->material = _material; + + this->dataPtr->material->SetReceiveShadows(false); + this->dataPtr->material->SetLightingEnabled(false); +} + +////////////////////////////////////////////////// +MaterialPtr OgreInertiaVisual::Material() const +{ + return this->dataPtr->material; +} diff --git a/ogre/src/OgreScene.cc b/ogre/src/OgreScene.cc index ea13d4ae8..e3351befb 100644 --- a/ogre/src/OgreScene.cc +++ b/ogre/src/OgreScene.cc @@ -29,6 +29,7 @@ #include "ignition/rendering/ogre/OgreGrid.hh" #include "ignition/rendering/ogre/OgreHeightmap.hh" #include "ignition/rendering/ogre/OgreIncludes.hh" +#include "ignition/rendering/ogre/OgreInertiaVisual.hh" #include "ignition/rendering/ogre/OgreLidarVisual.hh" #include "ignition/rendering/ogre/OgreLightVisual.hh" #include "ignition/rendering/ogre/OgreMarker.hh" @@ -375,6 +376,15 @@ PointLightPtr OgreScene::CreatePointLightImpl(unsigned int _id, return (result) ? light : nullptr; } +////////////////////////////////////////////////// +InertiaVisualPtr OgreScene::CreateInertiaVisualImpl(unsigned int _id, + const std::string &_name) +{ + OgreInertiaVisualPtr visual(new OgreInertiaVisual); + bool result = this->InitObject(visual, _id, _name); + return (result) ? visual : nullptr; +} + ////////////////////////////////////////////////// LightVisualPtr OgreScene::CreateLightVisualImpl(unsigned int _id, const std::string &_name) diff --git a/ogre2/include/ignition/rendering/ogre2/Ogre2InertiaVisual.hh b/ogre2/include/ignition/rendering/ogre2/Ogre2InertiaVisual.hh new file mode 100644 index 000000000..d18511c1c --- /dev/null +++ b/ogre2/include/ignition/rendering/ogre2/Ogre2InertiaVisual.hh @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * 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. + * + */ +#ifndef IGNITION_RENDERING_OGRE2_OGRE2INERTIAVISUAL_HH_ +#define IGNITION_RENDERING_OGRE2_OGRE2INERTIAVISUAL_HH_ + +#include + +#include "ignition/rendering/base/BaseInertiaVisual.hh" +#include "ignition/rendering/ogre2/Ogre2Visual.hh" + +namespace Ogre +{ + class MovableObject; +} + +namespace ignition +{ + namespace rendering + { + inline namespace IGNITION_RENDERING_VERSION_NAMESPACE { + + // Forward declaration + class Ogre2InertiaVisualPrivate; + + /// \brief Ogre2.x implementation of the inertia visual class + class IGNITION_RENDERING_OGRE2_VISIBLE Ogre2InertiaVisual : + public BaseInertiaVisual + { + /// \brief Constructor + protected: Ogre2InertiaVisual(); + + /// \brief Destructor + public: virtual ~Ogre2InertiaVisual(); + + // Documentation inherited. + public: virtual void Init() override; + + // Documentation inherited. + public: virtual void PreRender() override; + + // Documentation inherited. + protected: virtual void Destroy() override; + + /// \brief Load the Inertia visual from its pose and scale + /// \param[in] _pose Pose of the Inertia visual + /// \param[in] _scale Scale factor of the box visual + public: void Load(const ignition::math::Pose3d &_pose, + const ignition::math::Vector3d &_scale) override; + + /// \brief Get the box visual + /// \return Pointer to the box visual + public: VisualPtr BoxVisual() const override; + + // Documentation inherited. + public: virtual MaterialPtr Material() const; + + // Documentation inherited. + public: virtual void SetMaterial( + MaterialPtr _material, bool _unique) override; + + /// \brief Set material to line geometry. + /// \param[in] _material Ogre material. + protected: virtual void SetMaterialImpl(Ogre2MaterialPtr _material); + + private: friend class Ogre2Scene; + + /// \brief Private data class + private: std::unique_ptr dataPtr; + }; + } + } +} +#endif diff --git a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTarget.hh b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTarget.hh index c6aec49cc..32672db24 100644 --- a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTarget.hh +++ b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTarget.hh @@ -115,9 +115,31 @@ namespace ignition /// \see Camera::SetShadowsNodeDefDirty public: void SetShadowsNodeDefDirty(); - /// \brief Get a pointer to the ogre render target + /// \brief Get a pointer to the ogre render target containing + /// the results of the render (implemented separately + /// to avoid breaking ABI of the pure virtual function) + protected: Ogre::RenderTarget *RenderTargetImpl() const; + + /// \brief Get a pointer to the ogre render target containing + /// the results of the render public: virtual Ogre::RenderTarget *RenderTarget() const = 0; + /// \brief Returns true if this is a render window + /// TODO(anyone): this function should be virtual. + /// We didn't do it to preserve ABI. + /// Look in commit history for '#Ogre2IsRenderWindowABI' to + /// see changes made and revert + public: bool IsRenderWindow() const; + + // Documentation inherited + public: unsigned int GLIdImpl() const; + + /// \brief Destroy the render texture + protected: void DestroyTargetImpl(); + + /// \brief Build the render texture + protected: void BuildTargetImpl(); + /// \brief Get visibility mask for the viewport associated with this /// render target /// \return Visibility mask @@ -128,12 +150,23 @@ namespace ignition /// \param[in] _mask Visibility mask public: virtual void SetVisibilityMask(uint32_t _mask); + /// \brief Deprecated. Use other overloads. + public: static IGN_DEPRECATED(5) void UpdateRenderPassChain( + Ogre::CompositorWorkspace *_workspace, + const std::string &_workspaceDefName, + const std::string &_baseNode, const std::string &_finalNode, + const std::vector &_renderPasses, + bool _recreateNodes); + /// \brief Update the render pass chain public: static void UpdateRenderPassChain( Ogre::CompositorWorkspace *_workspace, const std::string &_workspaceDefName, const std::string &_baseNode, const std::string &_finalNode, - const std::vector &_renderPasses, bool _recreateNodes); + const std::vector &_renderPasses, + bool _recreateNodes, + Ogre::Texture *(*_ogreTextures)[2], + bool _isRenderWindow); /// \brief Update the background color protected: virtual void UpdateBackgroundColor(); @@ -168,6 +201,10 @@ namespace ignition /// \sa BaseRenderTarget::Rebuild() protected: void RebuildMaterial(); + /// Calls Ogre2RenderTexture::SetOgreTexture if appropiate to ensure + /// Ogre2RenderTexture::ogreTexture always has our outputs + protected: void SyncOgreTextureVars(); + /// \brief Pointer to the internal ogre camera protected: Ogre::Camera *ogreCamera = nullptr; @@ -187,7 +224,11 @@ namespace ignition /// \brief a material used by for the render target protected: MaterialPtr material; - /// \brief Helper class that applies the material to the render target + /// \brief Unused. Kept for ABI reasons. + /// + /// Just in case we set this value to + /// Ogre2RenderTargetPrivate::materialApplicator[0] which is what + /// most client applications may want. protected: Ogre2RenderTargetMaterialPtr materialApplicator; /// \brief Flag to indicate if the render target color has changed @@ -229,7 +270,9 @@ namespace ignition // Documentation inherited public: virtual unsigned int GLId() const override; - // Documentation inherited. + // Documentation inherited + // TODO(anyone): this function should be removed. + // We didn't do it to preserve ABI. public: virtual Ogre::RenderTarget *RenderTarget() const override; // Documentation inherited. @@ -241,8 +284,20 @@ namespace ignition /// \brief Build the render texture protected: virtual void BuildTarget(); - /// \brief Pointer to the internal ogre render texture object - protected: Ogre::Texture *ogreTexture = nullptr; + /// \brief Do not call this function directly. + /// + /// It's used to keep ABI compatibility to sync ogreTexture + /// with the internal pointer from our base class. + /// \param[in] _ogreTexture texture from + /// Ogre2RenderTargetPrivate::ogreTexture[1] + public: void SetOgreTexture(Ogre::Texture *_ogreTexture); + + /// \brief Unused. Kept for ABI reasons. + /// + /// Just in case we set this value to + /// Ogre2RenderTargetPrivate::ogreTexture[1] which is what most client + /// applications may want. + protected: IGN_DEPRECATED(5) Ogre::Texture * ogreTexture = nullptr; /// \brief Make scene our friend so it can create a ogre2 render texture private: friend class Ogre2Scene; @@ -261,6 +316,12 @@ namespace ignition // Documentation inherited. public: virtual void Destroy() override; + // TODO(anyone): this function should be virtual. + // We didn't do it to preserve ABI. + // Looks in commit history for '#Ogre2IsRenderWindowABI' to + // see changes made and revert + public: bool IsRenderWindow() const; + // Documentation inherited. public: virtual Ogre::RenderTarget *RenderTarget() const override; diff --git a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTargetMaterial.hh b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTargetMaterial.hh index 624c01ba0..57bfaae61 100644 --- a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTargetMaterial.hh +++ b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTargetMaterial.hh @@ -96,6 +96,9 @@ namespace ignition Ogre::Material *_originalMaterial, uint16_t _lodIndex, const Ogre::Renderable *_rend) override; + /// \brief Returns this->renderTarget == _renderTarget + public: bool IsSameRenderTarget(Ogre::RenderTarget *_renderTarget); + /// \brief scene manager responsible for rendering private: Ogre::SceneManager *scene = nullptr; diff --git a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTypes.hh b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTypes.hh index 115c9d69b..65885dd1e 100644 --- a/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTypes.hh +++ b/ogre2/include/ignition/rendering/ogre2/Ogre2RenderTypes.hh @@ -38,6 +38,7 @@ namespace ignition class Ogre2GizmoVisual; class Ogre2GpuRays; class Ogre2Grid; + class Ogre2InertiaVisual; class Ogre2Light; class Ogre2LightVisual; class Ogre2LidarVisual; @@ -83,6 +84,7 @@ namespace ignition typedef shared_ptr Ogre2GizmoVisualPtr; typedef shared_ptr Ogre2GpuRaysPtr; typedef shared_ptr Ogre2GridPtr; + typedef shared_ptr Ogre2InertiaVisualPtr; typedef shared_ptr Ogre2LightPtr; typedef shared_ptr Ogre2LightVisualPtr; typedef shared_ptr Ogre2LidarVisualPtr; diff --git a/ogre2/include/ignition/rendering/ogre2/Ogre2Scene.hh b/ogre2/include/ignition/rendering/ogre2/Ogre2Scene.hh index e4d05a2f9..2570e857e 100644 --- a/ogre2/include/ignition/rendering/ogre2/Ogre2Scene.hh +++ b/ogre2/include/ignition/rendering/ogre2/Ogre2Scene.hh @@ -119,6 +119,10 @@ namespace ignition // Documentation inherited protected: virtual bool InitImpl() override; + // Documentation inherited + protected: virtual InertiaVisualPtr CreateInertiaVisualImpl( + unsigned int _id, const std::string &_name) override; + // Documentation inherited protected: virtual LightVisualPtr CreateLightVisualImpl(unsigned int _id, const std::string &_name) override; diff --git a/ogre2/src/Ogre2DepthCamera.cc b/ogre2/src/Ogre2DepthCamera.cc index c45457558..60e6b9510 100644 --- a/ogre2/src/Ogre2DepthCamera.cc +++ b/ogre2/src/Ogre2DepthCamera.cc @@ -104,7 +104,7 @@ class ignition::rendering::Ogre2DepthCameraPrivate public: Ogre::CompositorWorkspace *ogreCompositorWorkspace = nullptr; /// \brief Output texture with depth and color data - public: Ogre::TexturePtr ogreDepthTexture; + public: Ogre::TexturePtr ogreDepthTexture[2]; /// \brief Dummy render texture for the depth data public: RenderTexturePtr depthTexture; @@ -297,10 +297,13 @@ void Ogre2DepthCamera::Destroy() Ogre::CompositorManager2 *ogreCompMgr = ogreRoot->getCompositorManager2(); // remove depth texture, material, compositor - if (this->dataPtr->ogreDepthTexture) + for( size_t i = 0u; i < 2u; ++i ) { - Ogre::TextureManager::getSingleton().remove( - this->dataPtr->ogreDepthTexture->getName()); + if (this->dataPtr->ogreDepthTexture[i]) + { + Ogre::TextureManager::getSingleton().remove( + this->dataPtr->ogreDepthTexture[i]->getName()); + } } if (this->dataPtr->ogreCompositorWorkspace) { @@ -588,41 +591,11 @@ void Ogre2DepthCamera::CreateDepthTexture() this->dataPtr->ogreCompositorBaseNodeDef = baseNodeDefName; Ogre::CompositorNodeDef *baseNodeDef = ogreCompMgr->addNodeDefinition(baseNodeDefName); - Ogre::TextureDefinitionBase::TextureDefinition *rt0TexDef = - baseNodeDef->addTextureDefinition("rt0"); - rt0TexDef->textureType = Ogre::TEX_TYPE_2D; - rt0TexDef->width = 0; - rt0TexDef->height = 0; - rt0TexDef->depth = 1; - rt0TexDef->numMipmaps = 0; - rt0TexDef->widthFactor = 1; - rt0TexDef->heightFactor = 1; - rt0TexDef->formatList = {Ogre::PF_FLOAT32_RGBA}; - rt0TexDef->fsaa = 0; - rt0TexDef->uav = false; - rt0TexDef->automipmaps = false; - rt0TexDef->hwGammaWrite = Ogre::TextureDefinitionBase::BoolFalse; - rt0TexDef->depthBufferId = Ogre::DepthBuffer::POOL_INVALID; - rt0TexDef->depthBufferFormat = Ogre::PF_UNKNOWN; - rt0TexDef->fsaaExplicitResolve = false; - - Ogre::TextureDefinitionBase::TextureDefinition *rt1TexDef = - baseNodeDef->addTextureDefinition("rt1"); - rt1TexDef->textureType = Ogre::TEX_TYPE_2D; - rt1TexDef->width = 0; - rt1TexDef->height = 0; - rt1TexDef->depth = 1; - rt1TexDef->numMipmaps = 0; - rt1TexDef->widthFactor = 1; - rt1TexDef->heightFactor = 1; - rt1TexDef->formatList = {Ogre::PF_FLOAT32_RGBA}; - rt1TexDef->fsaa = 0; - rt1TexDef->uav = false; - rt1TexDef->automipmaps = false; - rt1TexDef->hwGammaWrite = Ogre::TextureDefinitionBase::BoolFalse; - rt1TexDef->depthBufferId = Ogre::DepthBuffer::POOL_INVALID; - rt1TexDef->depthBufferFormat = Ogre::PF_UNKNOWN; - rt1TexDef->fsaaExplicitResolve = false; + + baseNodeDef->addTextureSourceName( + "rt0", 0u, Ogre::TextureDefinitionBase::TEXTURE_INPUT); + baseNodeDef->addTextureSourceName( + "rt1", 1u, Ogre::TextureDefinitionBase::TEXTURE_INPUT); Ogre::TextureDefinitionBase::TextureDefinition *depthTexDef = baseNodeDef->addTextureDefinition("depthTexture"); @@ -853,10 +826,10 @@ void Ogre2DepthCamera::CreateDepthTexture() Ogre::CompositorNodeDef *finalNodeDef = ogreCompMgr->addNodeDefinition(finalNodeDefName); - // output texture - finalNodeDef->addTextureSourceName("rt_output", 0, + finalNodeDef->addTextureSourceName("rt_input", 0, Ogre::TextureDefinitionBase::TEXTURE_INPUT); - finalNodeDef->addTextureSourceName("rt_input", 1, + // output texture + finalNodeDef->addTextureSourceName("rt_output", 1, Ogre::TextureDefinitionBase::TEXTURE_INPUT); finalNodeDef->setNumTargetPass(1); @@ -892,8 +865,9 @@ void Ogre2DepthCamera::CreateDepthTexture() Ogre::CompositorWorkspaceDef *workDef = ogreCompMgr->addWorkspaceDefinition(wsDefName); - workDef->connect(baseNodeDefName, 0, finalNodeDefName, 1); - workDef->connectExternal(0, finalNodeDefName, 0); + workDef->connectExternal(0, baseNodeDefName, 0); + workDef->connectExternal(1, baseNodeDefName, 1); + workDef->connect(baseNodeDefName, finalNodeDefName); } Ogre::CompositorWorkspaceDef *wsDef = ogreCompMgr->getWorkspaceDefinition(wsDefName); @@ -905,12 +879,19 @@ void Ogre2DepthCamera::CreateDepthTexture() } // create render texture - these textures pack the range data - this->dataPtr->ogreDepthTexture = - Ogre::TextureManager::getSingleton().createManual( - this->Name() + "_depth", "General", Ogre::TEX_TYPE_2D, - this->ImageWidth(), this->ImageHeight(), 1, 0, - Ogre::PF_FLOAT32_RGBA, Ogre::TU_RENDERTARGET, - 0, false, 0, Ogre::BLANKSTRING, false, true); + for( size_t i = 0u; i < 2u; ++i ) + { + this->dataPtr->ogreDepthTexture[i] = + Ogre::TextureManager::getSingleton().createManual( + this->Name() + "_depth" + std::to_string(i), "General", + Ogre::TEX_TYPE_2D, this->ImageWidth(), this->ImageHeight(), 1, 0, + Ogre::PF_FLOAT32_RGBA, Ogre::TU_RENDERTARGET, + 0, false, 0, Ogre::BLANKSTRING, false, true); + + Ogre::RenderTarget *rt = + this->dataPtr->ogreDepthTexture[i]->getBuffer()->getRenderTarget(); + rt->setDepthBufferPool(Ogre::DepthBuffer::POOL_INVALID); + } CreateWorkspaceInstance(); } @@ -922,13 +903,19 @@ void Ogre2DepthCamera::CreateWorkspaceInstance() auto ogreRoot = engine->OgreRoot(); Ogre::CompositorManager2 *ogreCompMgr = ogreRoot->getCompositorManager2(); - Ogre::RenderTarget *rt = - this->dataPtr->ogreDepthTexture->getBuffer()->getRenderTarget(); + Ogre::CompositorChannelVec externalTargets(2u); + for( size_t i = 0u; i < 2u; ++i ) + { + externalTargets[i].target = + this->dataPtr->ogreDepthTexture[i]->getBuffer()->getRenderTarget(); + externalTargets[i].textures.push_back(this->dataPtr->ogreDepthTexture[i]); + } // create compositor worksspace this->dataPtr->ogreCompositorWorkspace = ogreCompMgr->addWorkspace(this->scene->OgreSceneManager(), - rt, this->ogreCamera, this->dataPtr->ogreCompositorWorkspaceDef, false); + externalTargets, this->ogreCamera, + this->dataPtr->ogreCompositorWorkspaceDef, false); // add the listener Ogre::CompositorNode *node = @@ -963,12 +950,18 @@ void Ogre2DepthCamera::Render() ////////////////////////////////////////////////// void Ogre2DepthCamera::PreRender() { - if (!this->dataPtr->ogreDepthTexture) + if (!this->dataPtr->ogreDepthTexture[0]) this->CreateDepthTexture(); if (!this->dataPtr->ogreCompositorWorkspace) this->CreateWorkspaceInstance(); + Ogre::Texture *rawDepthTextures[2] = + { + this->dataPtr->ogreDepthTexture[0].get(), + this->dataPtr->ogreDepthTexture[1].get() + }; + // update depth camera render passes Ogre2RenderTarget::UpdateRenderPassChain( this->dataPtr->ogreCompositorWorkspace, @@ -976,7 +969,16 @@ void Ogre2DepthCamera::PreRender() this->dataPtr->ogreCompositorBaseNodeDef, this->dataPtr->ogreCompositorFinalNodeDef, this->dataPtr->renderPasses, - this->dataPtr->renderPassDirty); + this->dataPtr->renderPassDirty, + &rawDepthTextures, + false); + + if (rawDepthTextures[0] != this->dataPtr->ogreDepthTexture[0].get()) + { + std::swap( this->dataPtr->ogreDepthTexture[0], + this->dataPtr->ogreDepthTexture[1] ); + } + for (auto &pass : this->dataPtr->renderPasses) pass->PreRender(); @@ -1028,7 +1030,7 @@ void Ogre2DepthCamera::PostRender() 1, imageFormat, this->dataPtr->depthBuffer); // blit data from gpu to cpu - auto rt = this->dataPtr->ogreDepthTexture->getBuffer()->getRenderTarget(); + auto rt = this->dataPtr->ogreDepthTexture[1]->getBuffer()->getRenderTarget(); rt->copyContentsToMemory(dstBox, Ogre::RenderTarget::FB_AUTO); if (!this->dataPtr->depthImage) diff --git a/ogre2/src/Ogre2DynamicRenderable.cc b/ogre2/src/Ogre2DynamicRenderable.cc index c300c5d6f..90ffbedd6 100644 --- a/ogre2/src/Ogre2DynamicRenderable.cc +++ b/ogre2/src/Ogre2DynamicRenderable.cc @@ -130,6 +130,16 @@ void Ogre2DynamicRenderable::Destroy() this->dataPtr->sceneManager->destroyItem(this->dataPtr->ogreItem); this->dataPtr->ogreItem = nullptr; + // remove mesh from mesh manager + if (this->dataPtr->subMesh && + Ogre::MeshManager::getSingleton().resourceExists( + this->dataPtr->subMesh->mParent->getName())) + { + Ogre::MeshManager::getSingleton().remove( + this->dataPtr->subMesh->mParent->getName()); + this->dataPtr->subMesh = nullptr; + } + if (this->dataPtr->material && this->dataPtr->ownsMaterial) { this->dataPtr->scene->DestroyMaterial(this->dataPtr->material); @@ -150,11 +160,16 @@ void Ogre2DynamicRenderable::DestroyBuffer() if (!vaoManager) return; - if (this->dataPtr->vertexBuffer) - vaoManager->destroyVertexBuffer(this->dataPtr->vertexBuffer); - - if (this->dataPtr->vao) - vaoManager->destroyVertexArrayObject(this->dataPtr->vao); + if (this->dataPtr->subMesh) + { + if (!this->dataPtr->subMesh->mVao[Ogre::VpNormal].empty()) + { + this->dataPtr->subMesh->destroyVaos( + this->dataPtr->subMesh->mVao[Ogre::VpNormal], vaoManager); + } + if (!this->dataPtr->subMesh->mVao[Ogre::VpShadow].empty()) + this->dataPtr->subMesh->mVao[Ogre::VpShadow].clear(); + } this->dataPtr->vertexBuffer = nullptr; this->dataPtr->vao = nullptr; diff --git a/ogre2/src/Ogre2InertiaVisual.cc b/ogre2/src/Ogre2InertiaVisual.cc new file mode 100644 index 000000000..3a623a279 --- /dev/null +++ b/ogre2/src/Ogre2InertiaVisual.cc @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * 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 "ignition/rendering/ogre2/Ogre2InertiaVisual.hh" +#include "ignition/rendering/ogre2/Ogre2Material.hh" +#include "ignition/rendering/ogre2/Ogre2DynamicRenderable.hh" + +#ifdef _MSC_VER + #pragma warning(push, 0) +#endif +#include +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +using namespace ignition; +using namespace rendering; + +class ignition::rendering::Ogre2InertiaVisualPrivate +{ + /// \brief inertia visual materal + public: Ogre2MaterialPtr material = nullptr; + + /// \brief Ogre renderable used to render the cross lines. + public: std::shared_ptr crossLines = nullptr; + + /// \brief Box visual + public: VisualPtr boxVis = nullptr; +}; + +////////////////////////////////////////////////// +Ogre2InertiaVisual::Ogre2InertiaVisual() + : dataPtr(new Ogre2InertiaVisualPrivate) +{ +} + +////////////////////////////////////////////////// +Ogre2InertiaVisual::~Ogre2InertiaVisual() +{ +} + +////////////////////////////////////////////////// +void Ogre2InertiaVisual::PreRender() +{ +} + +////////////////////////////////////////////////// +void Ogre2InertiaVisual::Init() +{ + BaseInertiaVisual::Init(); +} + +////////////////////////////////////////////////// +void Ogre2InertiaVisual::Destroy() +{ + if (this->dataPtr->boxVis != nullptr) + { + this->dataPtr->boxVis->Destroy(); + this->dataPtr->boxVis.reset(); + } + + if (this->dataPtr->crossLines) + { + this->dataPtr->crossLines->Destroy(); + this->dataPtr->crossLines.reset(); + } + + if (this->dataPtr->material && this->Scene()) + { + this->Scene()->DestroyMaterial(this->dataPtr->material); + this->dataPtr->material.reset(); + } +} + +////////////////////////////////////////////////// +void Ogre2InertiaVisual::Load(const ignition::math::Pose3d &_pose, + const ignition::math::Vector3d &_scale) +{ + if (!this->dataPtr->crossLines) + { + this->dataPtr->crossLines.reset( + new Ogre2DynamicRenderable(this->Scene())); + this->ogreNode->attachObject(this->dataPtr->crossLines->OgreObject()); + } + + if (!this->dataPtr->boxVis) + { + this->dataPtr->boxVis = this->Scene()->CreateVisual(); + this->dataPtr->boxVis->AddGeometry(this->Scene()->CreateBox()); + this->dataPtr->boxVis->SetMaterial("Default/TransPurple"); + this->AddChild(this->dataPtr->boxVis); + } + + // Clear any previous data from the grid and update + this->dataPtr->crossLines->Clear(); + this->dataPtr->crossLines->Update(); + + this->dataPtr->crossLines->SetOperationType(MarkerType::MT_LINE_LIST); + if (this->dataPtr->material == nullptr) + { + MaterialPtr defaultMat = + this->Scene()->Material("Default/TransGreen")->Clone(); + this->SetMaterial(defaultMat, false); + } + + // Inertia position indicator + ignition::math::Vector3d p1(0, 0, -2*_scale.Z()); + ignition::math::Vector3d p2(0, 0, 2*_scale.Z()); + ignition::math::Vector3d p3(0, -2*_scale.Y(), 0); + ignition::math::Vector3d p4(0, 2*_scale.Y(), 0); + ignition::math::Vector3d p5(-2*_scale.X(), 0, 0); + ignition::math::Vector3d p6(2*_scale.X(), 0, 0); + p1 = _pose.Rot().RotateVector(p1); + p2 = _pose.Rot().RotateVector(p2); + p3 = _pose.Rot().RotateVector(p3); + p4 = _pose.Rot().RotateVector(p4); + p5 = _pose.Rot().RotateVector(p5); + p6 = _pose.Rot().RotateVector(p6); + p1 += _pose.Pos(); + p2 += _pose.Pos(); + p3 += _pose.Pos(); + p4 += _pose.Pos(); + p5 += _pose.Pos(); + p6 += _pose.Pos(); + + this->dataPtr->crossLines->AddPoint(p1); + this->dataPtr->crossLines->AddPoint(p2); + this->dataPtr->crossLines->AddPoint(p3); + this->dataPtr->crossLines->AddPoint(p4); + this->dataPtr->crossLines->AddPoint(p5); + this->dataPtr->crossLines->AddPoint(p6); + + this->dataPtr->crossLines->Update(); + + this->dataPtr->boxVis->SetLocalScale(_scale); + this->dataPtr->boxVis->SetLocalPosition(_pose.Pos()); + this->dataPtr->boxVis->SetLocalRotation(_pose.Rot()); +} + +////////////////////////////////////////////////// +VisualPtr Ogre2InertiaVisual::BoxVisual() const +{ + return this->dataPtr->boxVis; +} + +////////////////////////////////////////////////// +void Ogre2InertiaVisual::SetMaterial(MaterialPtr _material, bool _unique) +{ + _material = (_unique) ? _material->Clone() : _material; + + Ogre2MaterialPtr derived = + std::dynamic_pointer_cast(_material); + + if (!derived) + { + ignerr << "Cannot assign material created by another render-engine" + << std::endl; + + return; + } + + this->dataPtr->crossLines->SetMaterial(_material, false); + this->SetMaterialImpl(derived); +} + +////////////////////////////////////////////////// +void Ogre2InertiaVisual::SetMaterialImpl(Ogre2MaterialPtr _material) +{ + Ogre::MaterialPtr ogreMaterial = _material->Material(); + this->dataPtr->material = _material; +} + +////////////////////////////////////////////////// +MaterialPtr Ogre2InertiaVisual::Material() const +{ + return this->dataPtr->material; +} diff --git a/ogre2/src/Ogre2Node.cc b/ogre2/src/Ogre2Node.cc index 63a43a936..ec8a5d158 100644 --- a/ogre2/src/Ogre2Node.cc +++ b/ogre2/src/Ogre2Node.cc @@ -64,6 +64,9 @@ Ogre::SceneNode *Ogre2Node::Node() const ////////////////////////////////////////////////// void Ogre2Node::Destroy() { + if (!ogreNode) + return; + BaseNode::Destroy(); Ogre::SceneManager *ogreSceneManager = this->scene->OgreSceneManager(); ogreSceneManager->destroySceneNode(this->ogreNode); diff --git a/ogre2/src/Ogre2RenderTarget.cc b/ogre2/src/Ogre2RenderTarget.cc index d677c14bb..3b97602e6 100644 --- a/ogre2/src/Ogre2RenderTarget.cc +++ b/ogre2/src/Ogre2RenderTarget.cc @@ -100,6 +100,17 @@ class ignition::rendering::Ogre2RenderTargetPrivate /// \brief Name of shadow compositor node public: const std::string kShadowNodeName = "PbsMaterialsShadowNode"; + + /// \brief Helper class that applies the material to the render target + Ogre2RenderTargetMaterialPtr materialApplicator[2]; + + /// \brief Pointer to the internal ogre render texture objects + /// There's two because we ping pong postprocessing effects + /// and the final result is always in ogreTexture[1] + /// RenderWindows may have a 3rd texture which is the + /// actual window + /// + Ogre::Texture *ogreTexture[2] = {nullptr, nullptr}; }; using namespace ignition; @@ -153,42 +164,10 @@ void Ogre2RenderTarget::BuildCompositor() Ogre::CompositorNodeDef *nodeDef = ogreCompMgr->addNodeDefinition(nodeDefName); - // Input texture - Ogre::TextureDefinitionBase::TextureDefinition *rt0TexDef = - nodeDef->addTextureDefinition("rt0"); - rt0TexDef->textureType = Ogre::TEX_TYPE_2D; - rt0TexDef->width = 0; - rt0TexDef->height = 0; - rt0TexDef->depth = 1; - rt0TexDef->numMipmaps = 0; - rt0TexDef->widthFactor = 1; - rt0TexDef->heightFactor = 1; - rt0TexDef->formatList = {Ogre::PF_R8G8B8}; - rt0TexDef->fsaa = 0; - rt0TexDef->uav = false; - rt0TexDef->automipmaps = false; - rt0TexDef->hwGammaWrite = Ogre::TextureDefinitionBase::BoolTrue; - rt0TexDef->depthBufferId = Ogre::DepthBuffer::POOL_DEFAULT; - rt0TexDef->depthBufferFormat = Ogre::PF_UNKNOWN; - rt0TexDef->fsaaExplicitResolve = false; - - Ogre::TextureDefinitionBase::TextureDefinition *rt1TexDef = - nodeDef->addTextureDefinition("rt1"); - rt1TexDef->textureType = Ogre::TEX_TYPE_2D; - rt1TexDef->width = 0; - rt1TexDef->height = 0; - rt1TexDef->depth = 1; - rt1TexDef->numMipmaps = 0; - rt1TexDef->widthFactor = 1; - rt1TexDef->heightFactor = 1; - rt1TexDef->formatList = {Ogre::PF_R8G8B8}; - rt1TexDef->fsaa = 0; - rt1TexDef->uav = false; - rt1TexDef->automipmaps = false; - rt1TexDef->hwGammaWrite = Ogre::TextureDefinitionBase::BoolTrue; - rt1TexDef->depthBufferId = Ogre::DepthBuffer::POOL_DEFAULT; - rt1TexDef->depthBufferFormat = Ogre::PF_UNKNOWN; - rt1TexDef->fsaaExplicitResolve = false; + nodeDef->addTextureSourceName( + "rt0", 0u, Ogre::TextureDefinitionBase::TEXTURE_INPUT); + nodeDef->addTextureSourceName( + "rt1", 1u, Ogre::TextureDefinitionBase::TEXTURE_INPUT); nodeDef->setNumTargetPass(2); Ogre::CompositorTargetDef *rt0TargetDef = @@ -233,9 +212,9 @@ void Ogre2RenderTarget::BuildCompositor() this->dataPtr->kFinalNodeName; Ogre::CompositorNodeDef *finalNodeDef = ogreCompMgr->addNodeDefinition(finalNodeDefName); - finalNodeDef->addTextureSourceName("rt_output", 0, + finalNodeDef->addTextureSourceName("rtN", 0, Ogre::TextureDefinitionBase::TEXTURE_INPUT); - finalNodeDef->addTextureSourceName("rtN", 1, + finalNodeDef->addTextureSourceName("rt_output", 1, Ogre::TextureDefinitionBase::TEXTURE_INPUT); finalNodeDef->setNumTargetPass(2); @@ -258,17 +237,44 @@ void Ogre2RenderTarget::BuildCompositor() passScene->mIncludeOverlays = true; passScene->mFirstRQ = 254; passScene->mLastRQ = 255; - } + } Ogre::CompositorWorkspaceDef *workDef = ogreCompMgr->addWorkspaceDefinition(wsDefName); - workDef->connectExternal(0, finalNodeDefName, 0); - workDef->connect(nodeDefName, 0, finalNodeDefName, 1); + + workDef->connectExternal(0, nodeDefName, 0); + workDef->connectExternal(1, nodeDefName, 1); + + if (!this->IsRenderWindow()) + { + workDef->connect(nodeDefName, finalNodeDefName); + } + else + { + // connect the last render pass to the final compositor node + // but only input, since output goes to the render window + workDef->connect(nodeDefName, 0, finalNodeDefName, 0); + workDef->connectExternal(2, finalNodeDefName, 1); + } } + auto &manager = Ogre::TextureManager::getSingleton(); + Ogre::CompositorChannelVec externalTargets(2u); + for( size_t i = 0u; i < 2u; ++i ) + { + // Connect them in reverse order + const size_t srcIdx = 2u - i - 1u; + externalTargets[i].target = + this->dataPtr->ogreTexture[srcIdx]->getBuffer()->getRenderTarget(); + externalTargets[i].textures.push_back( + manager.getByName(this->dataPtr->ogreTexture[srcIdx]->getName())); + } + + this->SyncOgreTextureVars(); + this->ogreCompositorWorkspace = ogreCompMgr->addWorkspace(this->scene->OgreSceneManager(), - this->RenderTarget(), this->ogreCamera, + externalTargets, this->ogreCamera, this->ogreCompositorWorkspaceDefName, false); this->dataPtr->rtListener = new Ogre2RenderTargetCompositorListener(this); @@ -281,6 +287,27 @@ void Ogre2RenderTarget::DestroyCompositor() if (!this->ogreCompositorWorkspace) return; + // Restore the original order so that this->ogreTexture[1] is the one with + // FSAA (which we need for BuildCompositor to connect correctly) + const Ogre::CompositorChannelVec &externalTargets = + this->ogreCompositorWorkspace->getExternalRenderTargets(); + for( size_t i = 0u; i < 2u; ++i ) + { + const size_t srcIdx = (2u - i - 1u); + this->dataPtr->ogreTexture[srcIdx] = + externalTargets[i].textures.front().get(); + } + this->SyncOgreTextureVars(); + + if (this->dataPtr->materialApplicator[0] && + this->dataPtr->materialApplicator[0]->IsSameRenderTarget( + this->dataPtr->ogreTexture[0]->getBuffer()->getRenderTarget())) + { + std::swap( this->dataPtr->materialApplicator[0], + this->dataPtr->materialApplicator[1] ); + } + this->materialApplicator = this->dataPtr->materialApplicator[0]; + auto engine = Ogre2RenderEngine::Instance(); auto ogreRoot = engine->OgreRoot(); Ogre::CompositorManager2 *ogreCompMgr = ogreRoot->getCompositorManager2(); @@ -427,6 +454,106 @@ void Ogre2RenderTarget::Render() // engine->OgreRoot()->getRenderSystem()->_update(); } +////////////////////////////////////////////////// +bool Ogre2RenderTarget::IsRenderWindow() const +{ + const Ogre2RenderWindow *asWindow = + dynamic_cast(this); + if (asWindow) + return true; + + return false; +} + +////////////////////////////////////////////////// +void Ogre2RenderTarget::DestroyTargetImpl() +{ + if (nullptr == this->dataPtr->ogreTexture[0]) + return; + + this->DestroyCompositor(); + + auto &manager = Ogre::TextureManager::getSingleton(); + + this->materialApplicator.reset(); + + for( size_t i = 0u; i < 2u; ++i ) + { + manager.unload(this->dataPtr->ogreTexture[i]->getName()); + manager.remove(this->dataPtr->ogreTexture[i]->getName()); + + // TODO(anyone) there is memory leak when a render texture is destroyed. + // The RenderSystem::_cleanupDepthBuffers method used in ogre1 does not + // seem to work in ogre2 + + this->dataPtr->materialApplicator[i].reset(); + this->dataPtr->ogreTexture[i] = nullptr; + } + + this->SyncOgreTextureVars(); +} + +////////////////////////////////////////////////// +void Ogre2RenderTarget::BuildTargetImpl() +{ + Ogre::TextureManager &manager = Ogre::TextureManager::getSingleton(); + Ogre::PixelFormat ogreFormat = Ogre2Conversions::Convert(this->format); + + // check if target fsaa is supported + unsigned int fsaa = 0; + std::vector fsaaLevels = + Ogre2RenderEngine::Instance()->FSAALevels(); + unsigned int targetFSAA = this->antiAliasing; + auto const it = std::find(fsaaLevels.begin(), fsaaLevels.end(), targetFSAA); + if (it != fsaaLevels.end()) + { + fsaa = targetFSAA; + } + else + { + // output warning but only do it once + static bool ogre2FSAAWarn = false; + if (ogre2FSAAWarn) + { + ignwarn << "Anti-aliasing level of '" << this->antiAliasing << "' " + << "is not supported. Setting to 0" << std::endl; + ogre2FSAAWarn = true; + } + } + + for( size_t i = 0u; i < 2u; ++i ) + { + // Ogre 2 PBS expects gamma correction to be enabled + // Only the second target uses FSAA. + // Note: It's not guaranteed the 2nd target will remain + // the one using FSAA + this->dataPtr->ogreTexture[i] = (manager.createManual( + this->name + std::to_string(i), "General", + Ogre::TEX_TYPE_2D, this->width, this->height, 0, ogreFormat, + Ogre::TU_RENDERTARGET, 0, true, i == 1u ? fsaa : 0)).get(); + } + + this->SyncOgreTextureVars(); +} + +////////////////////////////////////////////////// +unsigned int Ogre2RenderTarget::GLIdImpl() const +{ + if (!this->dataPtr->ogreTexture[0]) + return 0; + + GLuint texId; + this->dataPtr->ogreTexture[1]->getCustomAttribute("GLID", &texId); + + return static_cast(texId); +} + +////////////////////////////////////////////////// +Ogre::RenderTarget *Ogre2RenderTarget::RenderTargetImpl() const +{ + return this->dataPtr->ogreTexture[1]->getBuffer()->getRenderTarget(); +} + ////////////////////////////////////////////////// uint32_t Ogre2RenderTarget::VisibilityMask() const { @@ -498,17 +625,45 @@ void Ogre2RenderTarget::UpdateRenderPassChain() this->dataPtr->kBaseNodeName, this->ogreCompositorWorkspaceDefName + "/" + this->dataPtr->kFinalNodeName, - this->renderPasses, this->renderPassDirty); + this->renderPasses, + this->renderPassDirty, + &this->dataPtr->ogreTexture, + this->IsRenderWindow()); + + // this->dataPtr->ogreTexture[0] may have changed + if (this->dataPtr->materialApplicator[0] && + this->dataPtr->materialApplicator[0]->IsSameRenderTarget( + this->dataPtr->ogreTexture[0]->getBuffer()->getRenderTarget())) + { + std::swap( this->dataPtr->materialApplicator[0], + this->dataPtr->materialApplicator[1] ); + } + this->materialApplicator = this->dataPtr->materialApplicator[0]; + + this->SyncOgreTextureVars(); this->renderPassDirty = false; } +////////////////////////////////////////////////// +void Ogre2RenderTarget::UpdateRenderPassChain( + Ogre::CompositorWorkspace * /*_workspace*/, + const std::string & /*_workspaceDefName*/, + const std::string & /*_baseNode*/, const std::string & /*_finalNode*/, + const std::vector & /*_renderPasses*/, + bool /*_recreateNodes*/) +{ + ignwarn << "Warning: This Ogre2RenderTarget::UpdateRenderPassChain " + << "overload is deprecated" << std::endl; +} + ////////////////////////////////////////////////// void Ogre2RenderTarget::UpdateRenderPassChain( Ogre::CompositorWorkspace *_workspace, const std::string &_workspaceDefName, const std::string &_baseNode, const std::string &_finalNode, const std::vector &_renderPasses, - bool _recreateNodes) + bool _recreateNodes, Ogre::Texture *(*_ogreTextures)[2], + bool _isRenderWindow) { if (!_workspace || _workspaceDefName.empty() || _baseNode.empty() || _finalNode.empty() || _renderPasses.empty()) @@ -532,10 +687,10 @@ void Ogre2RenderTarget::UpdateRenderPassChain( ogre2RenderPass->OgreCompositorNodeDefinitionName()); // check if we need to create all nodes or just update the connections. - // if node does not exist then it means it has not been added to the - // chain yet, in which case, we need to recreate the nodes and - // connections - if (!node) + // if node does not exist then it means it either has not been added to + // the chain yet or it was removed because it was disabled. + // In both cases, we need to recreate the nodes and connections + if (!node && ogre2RenderPass->IsEnabled()) { _recreateNodes = true; } @@ -564,7 +719,7 @@ void Ogre2RenderTarget::UpdateRenderPassChain( // the first node is the base scene pass node std::string outNodeDefName = _baseNode; // the final compositor node - std::string finalNodeDefName = _finalNode; + const std::string finalNodeDefName = _finalNode; std::string inNodeDefName; // if new nodes need to be added then clear everything, @@ -574,6 +729,8 @@ void Ogre2RenderTarget::UpdateRenderPassChain( else workspaceDef->clearAllInterNodeConnections(); + int numActiveNodes = 0; + // chain the render passes by connecting all the ogre compositor nodes // in between the base scene pass node and the final compositor node for (const auto &pass : _renderPasses) @@ -587,18 +744,43 @@ void Ogre2RenderTarget::UpdateRenderPassChain( { workspaceDef->connect(outNodeDefName, inNodeDefName); outNodeDefName = inNodeDefName; + ++numActiveNodes; + } + } + + workspaceDef->connectExternal(0, _baseNode, 0); + workspaceDef->connectExternal(1, _baseNode, 1); + + if (!_isRenderWindow) + { + // connect the last render pass to the final compositor node + workspaceDef->connect(outNodeDefName, finalNodeDefName); + + // We must ensure the output is always in ogreTextures[1] + const bool bMustSwapRts = (numActiveNodes & 0x01) == 0u; + + const Ogre::CompositorChannelVec &externalTargets = + _workspace->getExternalRenderTargets(); + for( size_t i = 0u; i < 2u; ++i ) + { + const size_t srcIdx = bMustSwapRts ? (2u - i - 1u) : i; + (*_ogreTextures)[srcIdx] = externalTargets[i].textures.front().get(); } } + else + { + // connect the last render pass to the final compositor node + // but only input, since output goes to the render window + workspaceDef->connect(outNodeDefName, 0, finalNodeDefName, 0); + workspaceDef->connectExternal(2, _finalNode, 1); + } - // connect the last render pass to the final compositor node - workspaceDef->connect(outNodeDefName, 0, finalNodeDefName, 1); // if new node definitions were added then recreate all the compositor nodes, // otherwise update the connections if (_recreateNodes) { // clearAll requires the output to be connected again. - workspaceDef->connectExternal(0, finalNodeDefName, 0); _workspace->recreateAllNodes(); } else @@ -651,18 +833,36 @@ void Ogre2RenderTarget::RebuildMaterial() Ogre::MaterialPtr matPtr = ogreMaterial->Material(); Ogre::SceneManager *sceneMgr = this->scene->OgreSceneManager(); - Ogre::RenderTarget *target = this->RenderTarget(); - this->materialApplicator.reset(new Ogre2RenderTargetMaterial( - sceneMgr, target, matPtr.get())); + for( size_t i = 0u; i < 2u; ++i ) + { + Ogre::RenderTarget *target = + this->dataPtr->ogreTexture[i]->getBuffer()->getRenderTarget(); + this->dataPtr->materialApplicator[i].reset( + new Ogre2RenderTargetMaterial(sceneMgr, target, matPtr.get())); + } + + this->materialApplicator = this->dataPtr->materialApplicator[0]; } } +////////////////////////////////////////////////// +void Ogre2RenderTarget::SyncOgreTextureVars() +{ + Ogre2RenderTexture *asRenderTexture = + dynamic_cast(this); + if (asRenderTexture) + asRenderTexture->SetOgreTexture(this->dataPtr->ogreTexture[1]); +} + ////////////////////////////////////////////////// // Ogre2RenderTexture ////////////////////////////////////////////////// +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" Ogre2RenderTexture::Ogre2RenderTexture() { } +#pragma GCC diagnostic pop ////////////////////////////////////////////////// Ogre2RenderTexture::~Ogre2RenderTexture() @@ -675,13 +875,6 @@ void Ogre2RenderTexture::Destroy() this->DestroyTarget(); } -////////////////////////////////////////////////// -Ogre::RenderTarget *Ogre2RenderTexture::RenderTarget() const -{ - return this->ogreTexture->getBuffer()->getRenderTarget(); -} - - ////////////////////////////////////////////////// void Ogre2RenderTexture::RebuildTarget() { @@ -692,64 +885,19 @@ void Ogre2RenderTexture::RebuildTarget() ////////////////////////////////////////////////// void Ogre2RenderTexture::DestroyTarget() { - if (nullptr == this->ogreTexture) - return; - - auto &manager = Ogre::TextureManager::getSingleton(); - manager.unload(this->ogreTexture->getName()); - manager.remove(this->ogreTexture->getName()); - - // TODO(anyone) there is memory leak when a render texture is destroyed. - // The RenderSystem::_cleanupDepthBuffers method used in ogre1 does not - // seem to work in ogre2 - - this->ogreTexture = nullptr; + Ogre2RenderTarget::DestroyTargetImpl(); } ////////////////////////////////////////////////// void Ogre2RenderTexture::BuildTarget() { - Ogre::TextureManager &manager = Ogre::TextureManager::getSingleton(); - Ogre::PixelFormat ogreFormat = Ogre2Conversions::Convert(this->format); - - // check if target fsaa is supported - unsigned int fsaa = 0; - std::vector fsaaLevels = - Ogre2RenderEngine::Instance()->FSAALevels(); - unsigned int targetFSAA = this->antiAliasing; - auto const it = std::find(fsaaLevels.begin(), fsaaLevels.end(), targetFSAA); - if (it != fsaaLevels.end()) - { - fsaa = targetFSAA; - } - else - { - // output warning but only do it once - static bool ogre2FSAAWarn = false; - if (ogre2FSAAWarn) - { - ignwarn << "Anti-aliasing level of '" << this->antiAliasing << "' " - << "is not supported. Setting to 0" << std::endl; - ogre2FSAAWarn = true; - } - } - - // Ogre 2 PBS expects gamma correction to be enabled - this->ogreTexture = (manager.createManual(this->name, "General", - Ogre::TEX_TYPE_2D, this->width, this->height, 0, ogreFormat, - Ogre::TU_RENDERTARGET, 0, true, fsaa)).get(); + Ogre2RenderTarget::BuildTargetImpl(); } ////////////////////////////////////////////////// unsigned int Ogre2RenderTexture::GLId() const { - if (!this->ogreTexture) - return 0; - - GLuint texId; - this->ogreTexture->getCustomAttribute("GLID", &texId); - - return static_cast(texId); + return Ogre2RenderTarget::GLIdImpl(); } ////////////////////////////////////////////////// @@ -764,6 +912,21 @@ void Ogre2RenderTexture::PostRender() Ogre2RenderTarget::PostRender(); } +////////////////////////////////////////////////// +Ogre::RenderTarget *Ogre2RenderTexture::RenderTarget() const +{ + return Ogre2RenderTarget::RenderTargetImpl(); +} + +////////////////////////////////////////////////// +void Ogre2RenderTexture::SetOgreTexture(Ogre::Texture *_ogreTexture) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + this->ogreTexture = _ogreTexture; +#pragma GCC diagnostic pop +} + ////////////////////////////////////////////////// // Ogre2RenderWindow ////////////////////////////////////////////////// @@ -776,6 +939,12 @@ Ogre2RenderWindow::~Ogre2RenderWindow() { } +////////////////////////////////////////////////// +bool Ogre2RenderWindow::IsRenderWindow() const +{ + return true; +} + ////////////////////////////////////////////////// Ogre::RenderTarget *Ogre2RenderWindow::RenderTarget() const { diff --git a/ogre2/src/Ogre2RenderTargetMaterial.cc b/ogre2/src/Ogre2RenderTargetMaterial.cc index 3d416a2e7..bcea1ae90 100644 --- a/ogre2/src/Ogre2RenderTargetMaterial.cc +++ b/ogre2/src/Ogre2RenderTargetMaterial.cc @@ -75,3 +75,10 @@ Ogre::Technique *Ogre2RenderTargetMaterial::handleSchemeNotFound( } return nullptr; } + +////////////////////////////////////////////////// +bool Ogre2RenderTargetMaterial::IsSameRenderTarget( + Ogre::RenderTarget *_renderTarget) +{ + return this->renderTarget == _renderTarget; +} diff --git a/ogre2/src/Ogre2Scene.cc b/ogre2/src/Ogre2Scene.cc index 59f53be0e..0a9141bf8 100644 --- a/ogre2/src/Ogre2Scene.cc +++ b/ogre2/src/Ogre2Scene.cc @@ -27,6 +27,7 @@ #include "ignition/rendering/ogre2/Ogre2GizmoVisual.hh" #include "ignition/rendering/ogre2/Ogre2GpuRays.hh" #include "ignition/rendering/ogre2/Ogre2Grid.hh" +#include "ignition/rendering/ogre2/Ogre2InertiaVisual.hh" #include "ignition/rendering/ogre2/Ogre2Light.hh" #include "ignition/rendering/ogre2/Ogre2LightVisual.hh" #include "ignition/rendering/ogre2/Ogre2LidarVisual.hh" @@ -739,6 +740,15 @@ AxisVisualPtr Ogre2Scene::CreateAxisVisualImpl(unsigned int _id, return (result) ? visual : nullptr; } +////////////////////////////////////////////////// +InertiaVisualPtr Ogre2Scene::CreateInertiaVisualImpl(unsigned int _id, + const std::string &_name) +{ + Ogre2InertiaVisualPtr visual(new Ogre2InertiaVisual); + bool result = this->InitObject(visual, _id, _name); + return (result) ? visual : nullptr; +} + ////////////////////////////////////////////////// LightVisualPtr Ogre2Scene::CreateLightVisualImpl(unsigned int _id, const std::string &_name) diff --git a/ogre2/src/Ogre2SelectionBuffer.cc b/ogre2/src/Ogre2SelectionBuffer.cc index 9221706d7..767ad6521 100644 --- a/ogre2/src/Ogre2SelectionBuffer.cc +++ b/ogre2/src/Ogre2SelectionBuffer.cc @@ -192,9 +192,12 @@ void Ogre2SelectionBuffer::CreateRTTBuffer() const_cast(scenePass)->mVisibilityMask = IGN_VISIBILITY_SELECTABLE; - // buffer to store render texture data - size_t bufferSize = Ogre::PixelUtil::getMemorySize(width, height, 1, format); + // buffer to store render texture data. Ensure it's at least 4 bytes + size_t bufferSize = std::max( + Ogre::PixelUtil::getMemorySize(width, height, 1, format), + 4u); this->dataPtr->buffer = new uint8_t[bufferSize]; + memset(this->dataPtr->buffer, 0, 4u); this->dataPtr->pixelBox = new Ogre::PixelBox(width, height, 1, format, this->dataPtr->buffer); } diff --git a/ogre2/src/media/materials/programs/depth_camera_final_fs.glsl b/ogre2/src/media/materials/programs/depth_camera_final_fs.glsl index ca3be0d0c..ef5eec270 100644 --- a/ogre2/src/media/materials/programs/depth_camera_final_fs.glsl +++ b/ogre2/src/media/materials/programs/depth_camera_final_fs.glsl @@ -31,10 +31,18 @@ uniform float far; uniform float min; uniform float max; +uniform vec4 texResolution; + void main() { float tolerance = 1e-6; - vec4 p = texture(inputTexture, inPs.uv0); + + // Note: We use texelFetch because p.a contains an uint32 and sampling + // (even w/ point filtering) causes p.a to loss information (e.g. + // values close to 0 get rounded to 0) + // + // See https://github.com/ignitionrobotics/ign-rendering/issues/332 + vec4 p = texelFetch(inputTexture, ivec2(inPs.uv0 *texResolution.xy), 0); vec3 point = p.xyz; diff --git a/ogre2/src/media/materials/scripts/depth_camera.material b/ogre2/src/media/materials/scripts/depth_camera.material index e0b87e166..602c69505 100644 --- a/ogre2/src/media/materials/scripts/depth_camera.material +++ b/ogre2/src/media/materials/scripts/depth_camera.material @@ -88,6 +88,8 @@ fragment_program DepthCameraFinalFS glsl default_params { param_named inputTexture int 0 + + param_named_auto texResolution texture_size 0 } } diff --git a/src/GaussianNoisePass_TEST.cc b/src/GaussianNoisePass_TEST.cc index aa885d0b6..61c73b6d9 100644 --- a/src/GaussianNoisePass_TEST.cc +++ b/src/GaussianNoisePass_TEST.cc @@ -85,8 +85,9 @@ void GaussianNoisePassTest::GaussianNoise(const std::string &_renderEngine) double biasStdDev = 0.007; noisePass->SetBiasStdDev(biasStdDev); // expect bias to be within 3-sigma - EXPECT_LE(std::fabs(noisePass->Bias()), biasMean + biasStdDev*3); - EXPECT_GE(std::fabs(noisePass->Bias()), biasMean - biasStdDev*3); + // Note, tol relaxed to 4-sigma to fix flaky test + EXPECT_LE(std::fabs(noisePass->Bias()), biasMean + biasStdDev*4); + EXPECT_GE(std::fabs(noisePass->Bias()), biasMean - biasStdDev*4); } ///////////////////////////////////////////////// diff --git a/src/Image.cc b/src/Image.cc index 74ed14fb6..a2ad9cc43 100644 --- a/src/Image.cc +++ b/src/Image.cc @@ -19,6 +19,16 @@ using namespace ignition; using namespace rendering; +////////////////////////////////////////////////// +template +struct ArrayDeleter +{ + void operator () (T const * p) + { + delete [] p; + } +}; + ////////////////////////////////////////////////// Image::Image(unsigned int _width, unsigned int _height, PixelFormat _format) : @@ -27,7 +37,7 @@ Image::Image(unsigned int _width, unsigned int _height, { this->format = PixelUtil::Sanitize(_format); unsigned int size = this->MemorySize(); - this->data = DataPtr(new unsigned char[size]); + this->data = DataPtr(new unsigned char[size], ArrayDeleter()); } ////////////////////////////////////////////////// diff --git a/src/InertiaVisual_TEST.cc b/src/InertiaVisual_TEST.cc new file mode 100644 index 000000000..0d958cadf --- /dev/null +++ b/src/InertiaVisual_TEST.cc @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * 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 "test_config.h" // NOLINT(build/include) + +#include "ignition/rendering/InertiaVisual.hh" +#include "ignition/rendering/RenderEngine.hh" +#include "ignition/rendering/RenderingIface.hh" +#include "ignition/rendering/Scene.hh" + +using namespace ignition; +using namespace rendering; + +class InertiaVisualTest : public testing::Test, + public testing::WithParamInterface +{ + /// \brief Test basic API + public: void InertiaVisual(const std::string &_renderEngine); +}; + +///////////////////////////////////////////////// +void InertiaVisualTest::InertiaVisual(const std::string &_renderEngine) +{ + RenderEngine *engine = rendering::engine(_renderEngine); + if (!engine) + { + igndbg << "Engine '" << _renderEngine + << "' is not supported" << std::endl; + return; + } + + ScenePtr scene = engine->CreateScene("scene"); + + // create visual + InertiaVisualPtr inertiaVisual = scene->CreateInertiaVisual(); + ASSERT_NE(nullptr, inertiaVisual); + + // check initial values + EXPECT_EQ(nullptr, inertiaVisual->BoxVisual()); + + ignition::math::MassMatrix3d massMatrix( + 2.0, {2.0, 1.5, 1.0}, {0.0, 0.0, 0.0}); + ignition::math::Pose3d p(0.0, 1.0, 2.5, 1.0, 0.4, 0.4); + ignition::math::Inertiald inertial; + + inertiaVisual->SetInertial(inertial); + EXPECT_EQ(nullptr, inertiaVisual->BoxVisual()); + + inertial.SetMassMatrix(massMatrix); + inertial.SetPose(p); + inertiaVisual->SetInertial(inertial); + EXPECT_NE(nullptr, inertiaVisual->BoxVisual()); + + // Clean up + engine->DestroyScene(scene); + rendering::unloadEngine(engine->Name()); +} + +///////////////////////////////////////////////// +TEST_P(InertiaVisualTest, InertiaVisual) +{ + InertiaVisual(GetParam()); +} + +INSTANTIATE_TEST_CASE_P(Visual, InertiaVisualTest, + RENDER_ENGINE_VALUES, + ignition::rendering::PrintToStringParam()); + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/base/BaseScene.cc b/src/base/BaseScene.cc index 76094afb8..b2fd44596 100644 --- a/src/base/BaseScene.cc +++ b/src/base/BaseScene.cc @@ -27,6 +27,7 @@ #include "ignition/rendering/ArrowVisual.hh" #include "ignition/rendering/AxisVisual.hh" +#include "ignition/rendering/InertiaVisual.hh" #include "ignition/rendering/LidarVisual.hh" #include "ignition/rendering/LightVisual.hh" #include "ignition/rendering/Camera.hh" @@ -918,6 +919,36 @@ AxisVisualPtr BaseScene::CreateAxisVisual(unsigned int _id, return (result) ? visual : nullptr; } +////////////////////////////////////////////////// +InertiaVisualPtr BaseScene::CreateInertiaVisual() +{ + unsigned int objId = this->CreateObjectId(); + return this->CreateInertiaVisual(objId); +} + +////////////////////////////////////////////////// +InertiaVisualPtr BaseScene::CreateInertiaVisual(unsigned int _id) +{ + std::string objName = this->CreateObjectName(_id, "InertiaVisual"); + return this->CreateInertiaVisual(_id, objName); +} + +////////////////////////////////////////////////// +InertiaVisualPtr BaseScene::CreateInertiaVisual(const std::string &_name) +{ + unsigned int objId = this->CreateObjectId(); + return this->CreateInertiaVisual(objId, _name); +} + +////////////////////////////////////////////////// +InertiaVisualPtr BaseScene::CreateInertiaVisual(unsigned int _id, + const std::string &_name) +{ + InertiaVisualPtr visual = this->CreateInertiaVisualImpl(_id, _name); + bool result = this->RegisterVisual(visual); + return (result) ? visual : nullptr; +} + ////////////////////////////////////////////////// LightVisualPtr BaseScene::CreateLightVisual() { @@ -1350,6 +1381,16 @@ void BaseScene::CreateMaterials() material->SetReceiveShadows(false); material->SetLightingEnabled(false); + material = this->CreateMaterial("Default/TransPurple"); + material->SetAmbient(1.0, 0.0, 1.0); + material->SetDiffuse(1.0, 0.0, 1.0); + material->SetEmissive(1.0, 0.0, 1.0); + material->SetTransparency(0.5); + material->SetCastShadows(false); + material->SetReceiveShadows(false); + material->SetLightingEnabled(false); + material->SetDepthWriteEnabled(false); + material = this->CreateMaterial("Default/White"); material->SetAmbient(1.0, 1.0, 1.0); material->SetDiffuse(1.0, 1.0, 1.0); diff --git a/test/integration/depth_camera.cc b/test/integration/depth_camera.cc index c014d5985..211c5ba72 100644 --- a/test/integration/depth_camera.cc +++ b/test/integration/depth_camera.cc @@ -254,6 +254,8 @@ void DepthCameraTest::DepthCameraBoxes( unsigned int ma = *mrgba >> 0 & 0xFF; EXPECT_EQ(0u, mr); EXPECT_EQ(0u, mg); + // Note: If it fails here, it may be this problem again: + // https://github.com/ignitionrobotics/ign-rendering/issues/332 EXPECT_GT(mb, 0u); // Far left and right points should be red (background color) @@ -453,6 +455,8 @@ void DepthCameraTest::DepthCameraBoxes( unsigned int a = *rgba >> 0 & 0xFF; EXPECT_EQ(0u, r); EXPECT_EQ(0u, g); + // Note: If it fails here, it may be this problem again: + // https://github.com/ignitionrobotics/ign-rendering/issues/332 EXPECT_GT(b, 0u); EXPECT_EQ(255u, a); } @@ -762,20 +766,12 @@ void DepthCameraTest::DepthCameraParticles( ignition::rendering::unloadEngine(engine->Name()); } -#ifdef __APPLE__ -TEST_P(DepthCameraTest, DISABLED_DepthCameraBoxes) -#else TEST_P(DepthCameraTest, DepthCameraBoxes) -#endif { DepthCameraBoxes(GetParam()); } -#ifdef __APPLE__ -TEST_P(DepthCameraTest, DISABLED_DepthCameraParticles) -#else TEST_P(DepthCameraTest, DepthCameraParticles) -#endif { DepthCameraParticles(GetParam()); } diff --git a/tutorials/04_lightmap.md b/tutorials/04_lightmap.md index fa20e4a67..e298cd4ee 100644 --- a/tutorials/04_lightmap.md +++ b/tutorials/04_lightmap.md @@ -26,7 +26,7 @@ To import a model go to `file` > `import` > (choose file type of model) The next step is to create UV unwraps for your model. In Blender usually the first UVmap for any mesh created is for PBR textures and the second UV channel is for light map information. This secondary UV set is different from the UVs used to map things like color maps or normal maps because each polygon surface is receiving different lighting from all other polygons so each UV coordinate also needs to be unique. -For a fast automatic light map unwrap hit F3 and then search for “Lightmap pack” +For a fast automatic light map unwrap hit F3 and then search for “Lightmap pack”. Make sure to the “margin” is set to 1 for max distance between faces (reduces shadow bleeding during bake) @image html img/lightmap_unwrap.png @@ -41,9 +41,9 @@ If the texture is black use the color picker and select the UV island and it sho @image html img/lightmap_new_texture.png -### Step 4: Material shader +### Step 4: Material setup / Baking -In order to have the texture show we must assign it to a new material. With your model selected open the Shader tab and select `Add` > `Image` > `Image texture`. This will create a new image texture node. +In order to have the texture show the baked lighting we must assign it to a new material. With your model selected open the Shader tab and select `Add` > `Image` > `Image texture`. This will create a new image texture node. * Add an image texture * Connect the color from the image node to the base color of the material color (principled BSDF) @@ -52,16 +52,21 @@ After this step add another image texture in the shader editor @image html img/lightmap_material_shader.png -### Step 5: Render/Bake - -When baking we have the option to either bake in all the lighting, both indirect and direct, or just the indirect lighting, also known as global illumination or bounced lighting, and use realtime lights in Ignition for the direct lighting and shadows. With the latter method we get sharper lighting and shadow detail as well as more accurate lighting on our dynamic objects however performance will be impacted by having more real time lights. Even with all the lighting baked it’s still a good idea to have one realtime light such as a large point light or directional light works particularly well in order to enhance the effects of the physically based materials. +@image html img/lightmap_bake.jpg Now that the material node is set up, open the render properties and make sure that the cycles renderer active in the render engines drop down. -Scroll down to where it says bake, and then with the second image node selected (MeshLightBake step 4) hit bake. Blender should now start baking the light map for the model. +Scroll down to where it says bake, and then with the second image node selected (MeshLightBake) hit bake. Blender should now start baking the light map for the model. -@image html img/lightmap_new_texture.png +### Step 5: Show Baked Lighting + +After the bake is finished you should have a separate light map of your model. +* To see it applied real time in your scene, go back to the hypershade / materials editor. Click on the Image texture that has the baked light map information and connect it to the base color. + +@image html img/lightmap_material_connection.jpg -The above image shows the model with baked in lighting (Blender Viewport Shading turned on). +Model with baked in lighting (Blender Viewport Shading turned on): + +@image html img/lightmap_rendered.png ### Step 6: Test/Export @@ -69,12 +74,16 @@ Light maps are great for working with bigger scenes. With baked-in lighting, com To export light map image go to `UV editor` > `Image` > `Save as` -Lightmap baking applied to a more complex scene: +@image html img/lightmap_depot_saved.jpg + +Light map baking applied to a more complex scene: @image html img/lightmap_depot_render.png ## Using the light map in Ignition +When baking we have the option to either bake in all the lighting, both indirect and direct, or just the indirect lighting, also known as global illumination or bounced lighting, and use real time lights in Ignition for the direct lighting and shadows. With the latter method we get sharper lighting and shadow detail as well as more accurate lighting on our dynamic objects however performance will be impacted by having more real time lights. Even with all the lighting baked it’s still a good idea to have one real time light such as a large point light or directional light works particularly well in order to enhance the effects of the physically based materials. + Lightmaps can be applied to a mesh in Ignition the same way as other texture maps. Create an `ignition::rendering::Material` and specify a light map texture by calling [SetLightMap](https://ignitionrobotics.org/api/rendering/5.0/classignition_1_1rendering_1_1Material.html#addc6eb6206e0a17ab82aeaea543e8c71). Recall that when creating the light map UV texture in Step 2, we typically use a secondary UV set for light maps. Make sure to specify the index of the light map UV set as the second argument to this function. diff --git a/tutorials/img/lightmap_bake.jpg b/tutorials/img/lightmap_bake.jpg new file mode 100644 index 000000000..84993a8d6 Binary files /dev/null and b/tutorials/img/lightmap_bake.jpg differ diff --git a/tutorials/img/lightmap_depot_saved.jpg b/tutorials/img/lightmap_depot_saved.jpg new file mode 100644 index 000000000..dc71dd95c Binary files /dev/null and b/tutorials/img/lightmap_depot_saved.jpg differ diff --git a/tutorials/img/lightmap_material_connection.jpg b/tutorials/img/lightmap_material_connection.jpg new file mode 100644 index 000000000..f075e3ab6 Binary files /dev/null and b/tutorials/img/lightmap_material_connection.jpg differ diff --git a/tutorials/img/lightmap_rendered.png b/tutorials/img/lightmap_rendered.png new file mode 100644 index 000000000..4825b52d2 Binary files /dev/null and b/tutorials/img/lightmap_rendered.png differ