Use separate SceneView for main scene and UX.

Refactor FilamentContext to allow multiple render requests to
be submitted onto a single RenderTarget. The main scene and the
ux scene are then submitted as two separate requests that share
the same target, resulting in a single, composited render.

PiperOrigin-RevId: 904324801
Change-Id: I7c47ec4f8d159320612fa33384c01e8db3414daa
This commit is contained in:
Haroon Qureshi
2026-04-23 02:22:53 -07:00
committed by Copybara-Service
parent c41ed42407
commit 57ad7463b1
11 changed files with 244 additions and 195 deletions
@@ -36,6 +36,7 @@
#include <mujoco/mujoco.h> #include <mujoco/mujoco.h>
#include "experimental/filament/filament/filament_platform_factory.h" #include "experimental/filament/filament/filament_platform_factory.h"
#include "experimental/filament/filament/object_manager.h" #include "experimental/filament/filament/object_manager.h"
#include "experimental/filament/filament/render_target.h"
#include "experimental/filament/filament/scene_view.h" #include "experimental/filament/filament/scene_view.h"
#include "experimental/filament/render_context_filament.h" #include "experimental/filament/render_context_filament.h"
@@ -81,77 +82,91 @@ FilamentContext::~FilamentContext() {
FilamentContext::FrameHandle FilamentContext::Render( FilamentContext::FrameHandle FilamentContext::Render(
std::span<const RenderRequest> requests, std::span<const RenderRequest> requests,
std::span<const ReadPixelsRequest> read_requests) { std::span<const ReadPixelsRequest> read_requests) {
if (requests.size() != 1) {
mju_error("Only one render request is supported for now.");
}
if (read_requests.size() > 1) { if (read_requests.size() > 1) {
mju_error("Only one read request is supported for now."); mju_error("Only one read request is supported for now.");
} }
const RenderRequest& request = requests[0]; bool render_began = false;
RenderTarget* current_target = nullptr;
if (request.target == nullptr) { for (const RenderRequest& request : requests) {
if (!read_requests.empty()) { if (request.target != current_target && render_began) {
mju_error("Cannot read pixels from the window.");
}
if constexpr (UTILS_HAS_THREADING) {
// Wait until previous frame is completed before requesting a new frame.
engine_->flushAndWait();
}
// If the window size has changed, we need to reacquire the swap chain.
if (request.width != window_width_ || request.height != window_height_) {
if (window_width_ != 0 && window_height_ != 0) {
engine_->destroy(window_swap_chain_);
window_swap_chain_ = engine_->createSwapChain(config_.native_window);
}
window_width_ = request.width;
window_height_ = request.height;
}
SceneView::RenderRequest scene_view_request;
scene_view_request.draw_mode = request.draw_mode;
scene_view_request.viewport = {0, 0, request.width, request.height};
scene_view_request.camera = request.camera;
scene_view_request.enable_ux = request.draw_ux;
if (renderer_->beginFrame(window_swap_chain_)) {
request.scene->Render(renderer_, scene_view_request);
renderer_->endFrame(); renderer_->endFrame();
render_began = false;
} }
current_target = request.target;
if (current_target == nullptr) {
if (!read_requests.empty()) {
mju_error("Cannot read pixels from the window.");
}
if constexpr (UTILS_HAS_THREADING) {
// Wait until previous frame is completed before requesting a new frame.
engine_->flushAndWait();
}
// If the window size has changed, we need to reacquire the swap chain.
if (request.width != window_width_ || request.height != window_height_) {
if (window_width_ != 0 && window_height_ != 0) {
engine_->destroy(window_swap_chain_);
window_swap_chain_ = engine_->createSwapChain(config_.native_window);
}
window_width_ = request.width;
window_height_ = request.height;
}
if (!render_began) {
render_began = renderer_->beginFrame(window_swap_chain_);
}
if (render_began) {
SceneView::RenderRequest scene_view_request;
scene_view_request.draw_mode = request.draw_mode;
scene_view_request.viewport = {0, 0, request.width, request.height};
scene_view_request.camera = request.camera;
request.scene->Render(renderer_, scene_view_request);
}
} else {
if (read_requests.empty()) {
mju_error(
"Rendering to a render target without a read request is pointless.");
}
const ReadPixelsRequest& read_request = read_requests[0];
if (read_request.num_bytes == 0) {
mju_error("Output buffer size is zero.");
}
if (!render_began) {
render_began = renderer_->beginFrame(offscreen_swap_chain_);
}
if (render_began) {
SceneView::RenderRequest scene_view_request;
scene_view_request.draw_mode = request.draw_mode;
scene_view_request.viewport = {0, 0, request.width, request.height};
scene_view_request.camera = request.camera;
scene_view_request.target = request.target;
request.scene->Render(renderer_, scene_view_request);
request.target->ReadColorPixels(renderer_, read_request.output,
read_request.num_bytes);
}
}
}
if (render_began) {
renderer_->endFrame();
render_began = false;
if constexpr (!UTILS_HAS_THREADING) { if constexpr (!UTILS_HAS_THREADING) {
engine_->execute(); engine_->execute();
} }
} else { }
if (read_requests.empty()) {
mju_error(
"Rendering to a render target without a read request is pointless.");
}
const ReadPixelsRequest& read_request = read_requests[0]; if (!read_requests.empty()) {
if (read_request.num_bytes == 0) {
mju_error("Output buffer size is zero.");
}
if (renderer_->beginFrame(offscreen_swap_chain_)) {
SceneView::RenderRequest scene_view_request;
scene_view_request.draw_mode = request.draw_mode;
scene_view_request.viewport = {0, 0, request.width, request.height};
scene_view_request.camera = request.camera;
scene_view_request.enable_ux = request.draw_ux;
scene_view_request.target = request.target;
request.scene->Render(renderer_, scene_view_request);
request.target->ReadColorPixels(renderer_, read_request.output,
read_request.num_bytes);
renderer_->endFrame();
}
engine_->flushAndWait(); engine_->flushAndWait();
if (read_request.read_completed_callback) { if (read_requests[0].read_completed_callback) {
read_request.read_completed_callback(read_request.user_data); read_requests[0].read_completed_callback(read_requests[0].user_data);
} }
} }
return ++frame_counter_; return ++frame_counter_;
} }
@@ -59,10 +59,6 @@ class FilamentContext {
// will be rendered to the window (as previously configured in // will be rendered to the window (as previously configured in
// mjrFilamentConfig::native_window). // mjrFilamentConfig::native_window).
RenderTarget* target = nullptr; RenderTarget* target = nullptr;
// Whether or not to include the UX scene in the render. (The SceneView
// stores both a main simulation scene and the UX scene.)
bool draw_ux = true;
}; };
// Information needed to read pixels from a render target. // Information needed to read pixels from a render target.
@@ -37,8 +37,13 @@ namespace mujoco {
using filament::math::float3; using filament::math::float3;
using filament::math::mat3f; using filament::math::mat3f;
ImguiBridge::ImguiBridge(ObjectManager* object_mgr, SceneView* scene_view) ImguiBridge::ImguiBridge(ObjectManager* object_mgr)
: object_mgr_(object_mgr), scene_view_(scene_view) {} : object_mgr_(object_mgr) {
scene_view_ = std::make_unique<SceneView>(object_mgr_->GetEngine());
scene_view_->DisableShadows();
scene_view_->DisableReflections();
scene_view_->DisablePostProcessing();
}
ImguiBridge::~ImguiBridge() { PrepareRenderables(0); } ImguiBridge::~ImguiBridge() { PrepareRenderables(0); }
@@ -279,10 +284,10 @@ void ImguiBridge::PrepareRenderables(int count) {
r->SetCastShadows(false); r->SetCastShadows(false);
r->SetReceiveShadows(false); r->SetReceiveShadows(false);
r->SetBlendOrder(static_cast<std::uint16_t>(renderables_.size())); r->SetBlendOrder(static_cast<std::uint16_t>(renderables_.size()));
scene_view_->AddToUxScene(r.get()); scene_view_->AddToScene(r.get());
} }
while (renderables_.size() > count) { while (renderables_.size() > count) {
scene_view_->RemoveFromUxScene(renderables_.back().get()); scene_view_->RemoveFromScene(renderables_.back().get());
renderables_.pop_back(); renderables_.pop_back();
} }
} }
@@ -29,10 +29,10 @@
namespace mujoco { namespace mujoco {
// Manages Renderables that will be added a SceneView's UX scene. // Creates and manages a SceneView using data read from ImGui.
class ImguiBridge { class ImguiBridge {
public: public:
ImguiBridge(ObjectManager* object_mgr, SceneView* scene_view); explicit ImguiBridge(ObjectManager* object_mgr);
~ImguiBridge(); ~ImguiBridge();
// Prepares the Renderables using data from the current ImGui state. This // Prepares the Renderables using data from the current ImGui state. This
@@ -40,6 +40,9 @@ class ImguiBridge {
// synced. // synced.
void Update(); void Update();
// Returns the managed UX scene.
SceneView* GetSceneView() const { return scene_view_.get(); }
// Uploads texture to be used with ImGui's Image and ImageButton functions. // Uploads texture to be used with ImGui's Image and ImageButton functions.
uintptr_t UploadImage(uintptr_t tex_id, const uint8_t* pixels, int width, uintptr_t UploadImage(uintptr_t tex_id, const uint8_t* pixels, int width,
int height, int bpp); int height, int bpp);
@@ -57,7 +60,7 @@ class ImguiBridge {
void DestroyTexture(ImTextureData* data); void DestroyTexture(ImTextureData* data);
ObjectManager* object_mgr_ = nullptr; ObjectManager* object_mgr_ = nullptr;
SceneView* scene_view_ = nullptr; std::unique_ptr<SceneView> scene_view_;
std::vector<std::unique_ptr<Renderable>> renderables_; std::vector<std::unique_ptr<Renderable>> renderables_;
std::vector<std::unique_ptr<Mesh>> meshes_; std::vector<std::unique_ptr<Mesh>> meshes_;
std::unordered_map<uintptr_t, std::unique_ptr<Texture>> textures_; std::unordered_map<uintptr_t, std::unique_ptr<Texture>> textures_;
@@ -14,6 +14,7 @@
#include "experimental/filament/filament/mjr_filament_renderer.h" #include "experimental/filament/filament/mjr_filament_renderer.h"
#include <cstddef>
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
@@ -28,7 +29,6 @@
#include "experimental/filament/filament/model_util.h" #include "experimental/filament/filament/model_util.h"
#include "experimental/filament/filament/render_target.h" #include "experimental/filament/filament/render_target.h"
#include "experimental/filament/filament/scene_bridge.h" #include "experimental/filament/filament/scene_bridge.h"
#include "experimental/filament/filament/scene_view.h"
#include "experimental/filament/filament/texture.h" #include "experimental/filament/filament/texture.h"
#include "experimental/filament/render_context_filament.h" #include "experimental/filament/render_context_filament.h"
@@ -39,11 +39,31 @@ MjrFilamentRenderer::MjrFilamentRenderer(const mjrFilamentConfig* config)
} }
void MjrFilamentRenderer::Init(const mjModel* model) { void MjrFilamentRenderer::Init(const mjModel* model) {
scene_view_ = std::make_unique<SceneView>(GetEngine()); scene_bridge_ = std::make_unique<SceneBridge>(GetObjectManager(), model);
scene_bridge_ = std::make_unique<SceneBridge>(GetObjectManager(), imgui_bridge_ = std::make_unique<ImguiBridge>(GetObjectManager());
scene_view_.get(), model);
imgui_bridge_ = render_requests_[0].scene = scene_bridge_->GetSceneView();
std::make_unique<ImguiBridge>(GetObjectManager(), scene_view_.get()); render_requests_[0].draw_mode = DrawMode::Color;
render_requests_[1].scene = imgui_bridge_->GetSceneView();
render_requests_[1].draw_mode = DrawMode::Color;
// The UX camera is a fixed orthographic camera. We only need to change the
// width/height based on the viewport per frame.
render_requests_[1].camera.orthographic = true;
render_requests_[1].camera.pos[0] = 0.0f;
render_requests_[1].camera.pos[1] = 0.0f;
render_requests_[1].camera.pos[2] = 1.0f;
render_requests_[1].camera.forward[0] = 0.0f;
render_requests_[1].camera.forward[1] = 0.0f;
render_requests_[1].camera.forward[2] = -1.0f;
render_requests_[1].camera.up[0] = 0.0f;
render_requests_[1].camera.up[1] = 1.0f;
render_requests_[1].camera.up[2] = 0.0f;
render_requests_[1].camera.frustum_top = 0.0f;
render_requests_[1].camera.frustum_near = 0.0f;
render_requests_[1].camera.frustum_far = 1.0f;
SetClearColor(ReadElement(model, "filament.clearColor", SetClearColor(ReadElement(model, "filament.clearColor",
filament::math::float4(0, 0, 0, 1))); filament::math::float4(0, 0, 0, 1)));
@@ -53,46 +73,45 @@ void MjrFilamentRenderer::Render(const mjrRect& viewport, const mjvScene* scene)
scene_bridge_->Update(viewport, scene); scene_bridge_->Update(viewport, scene);
// Update the UX renderable entity after processing the scene in case there // Update the UX renderable entity after processing the scene in case there
// are any elements in the scene which generate UX draw calls (e.g. labels). // are any elements in the scene which generate UX draw calls (e.g. labels).
if (imgui_bridge_ && gui_swap_chain_target_ == scene_swap_chain_target_) { if (mode_ != FrameBufferMode::OffScreen) {
// Prepare the filament Renderable that contains the GUI draw commands. We
// must call this function even if we do not plan on rendering the GUI to
// ensure the ImGui state is updated.
imgui_bridge_->Update(); imgui_bridge_->Update();
} }
last_render_mode_ = DrawMode::Color;
if (scene->flags[mjRND_SEGMENT]) { if (scene->flags[mjRND_SEGMENT]) {
last_render_mode_ = DrawMode::Segmentation; render_requests_[0].draw_mode = DrawMode::Segmentation;
} else if (scene->flags[mjRND_DEPTH]) { } else if (scene->flags[mjRND_DEPTH]) {
last_render_mode_ = DrawMode::Depth; render_requests_[0].draw_mode = DrawMode::Depth;
} else {
render_requests_[0].draw_mode = DrawMode::Color;
} }
last_camera_ = mjv_averageCamera(scene->camera, scene->camera + 1);
if (scene_swap_chain_target_ == kWindowSwapChain) { render_requests_[0].width = viewport.width;
RenderRequest request; render_requests_[0].height = viewport.height;
request.scene = scene_view_.get(); render_requests_[1].width = viewport.width;
request.draw_mode = last_render_mode_; render_requests_[1].height = viewport.height;
request.camera = last_camera_;
request.draw_ux = (gui_swap_chain_target_ == kWindowSwapChain); render_requests_[0].camera = mjv_averageCamera(scene->camera, scene->camera + 1);
request.width = viewport.width; render_requests_[1].camera.frustum_center = viewport.width / 2.0f;
request.height = viewport.height; render_requests_[1].camera.frustum_width = viewport.width / 2.0f;
FilamentContext::Render({&request, 1}); render_requests_[1].camera.frustum_bottom = viewport.height;
if (mode_ == FrameBufferMode::Window) {
render_requests_[0].target = nullptr;
render_requests_[1].target = nullptr;
FilamentContext::Render(render_requests_);
} }
} }
void MjrFilamentRenderer::SetFrameBuffer(int framebuffer) { void MjrFilamentRenderer::SetFrameBuffer(int framebuffer) {
switch (framebuffer) { switch (framebuffer) {
case mjFB_WINDOW: case mjFB_WINDOW:
scene_swap_chain_target_ = kWindowSwapChain; mode_ = FrameBufferMode::Window;
gui_swap_chain_target_ = kWindowSwapChain;
break; break;
case mjFB_OFFSCREEN: case mjFB_OFFSCREEN:
scene_swap_chain_target_ = kOffscreenSwapChain; mode_ = FrameBufferMode::OffScreen;
gui_swap_chain_target_ = kWindowSwapChain;
break; break;
case 2: // No official constant fo this. case 2: // No official constant fo this.
scene_swap_chain_target_ = kOffscreenSwapChain; mode_ = FrameBufferMode::OffScreenWithGui;
gui_swap_chain_target_ = kOffscreenSwapChain;
break; break;
default: default:
mju_error("Invalid framebuffer mode: %d", framebuffer); mju_error("Invalid framebuffer mode: %d", framebuffer);
@@ -101,53 +120,62 @@ void MjrFilamentRenderer::SetFrameBuffer(int framebuffer) {
void MjrFilamentRenderer::ReadPixels(mjrRect viewport, unsigned char* rgb, void MjrFilamentRenderer::ReadPixels(mjrRect viewport, unsigned char* rgb,
float* depth) { float* depth) {
if (scene_swap_chain_target_ != kOffscreenSwapChain) { if (mode_ == FrameBufferMode::Window) {
mju_error("ReadPixels is only supported for offscreen rendering."); mju_error("ReadPixels is only supported for offscreen rendering.");
} }
RenderRequest request; render_requests_[0].width = viewport.width;
request.scene = scene_view_.get(); render_requests_[0].height = viewport.height;
request.camera = last_camera_; render_requests_[1].width = viewport.width;
request.draw_ux = (gui_swap_chain_target_ == kOffscreenSwapChain); render_requests_[1].height = viewport.height;
request.width = viewport.width;
request.height = viewport.height;
if (rgb) { if (rgb) {
request.draw_mode = last_render_mode_;
RenderTargetConfig config; RenderTargetConfig config;
DefaultRenderTargetConfig(&config); DefaultRenderTargetConfig(&config);
config.color_format = mjPIXEL_FORMAT_RGB8; config.color_format = mjPIXEL_FORMAT_RGB8;
config.depth_format = mjPIXEL_FORMAT_DEPTH32F; config.depth_format = mjPIXEL_FORMAT_DEPTH32F;
auto target = std::make_unique<RenderTarget>(GetEngine(), config); auto target = std::make_unique<RenderTarget>(GetEngine(), config);
target->Prepare(request.width, request.height); target->Prepare(viewport.width, viewport.height);
request.target = target.get(); render_requests_[0].target = target.get();
render_requests_[1].target = target.get();
const size_t num_requests =
(mode_ == FrameBufferMode::OffScreenWithGui) ? 2 : 1;
ReadPixelsRequest read_request; ReadPixelsRequest read_request;
read_request.output = rgb; read_request.output = rgb;
read_request.num_bytes = viewport.width * viewport.height * 3; read_request.num_bytes = viewport.width * viewport.height * 3;
const FrameHandle frame = const FrameHandle frame = FilamentContext::Render(
FilamentContext::Render({&request, 1}, {&read_request, 1}); {&render_requests_[0], num_requests}, {&read_request, 1});
FilamentContext::WaitForFrame(frame); FilamentContext::WaitForFrame(frame);
render_requests_[0].target = nullptr;
render_requests_[1].target = nullptr;
} }
if (depth) { if (depth) {
request.draw_mode = DrawMode::Depth;
RenderTargetConfig config; RenderTargetConfig config;
DefaultRenderTargetConfig(&config); DefaultRenderTargetConfig(&config);
config.color_format = mjPIXEL_FORMAT_R32F; config.color_format = mjPIXEL_FORMAT_R32F;
config.depth_format = mjPIXEL_FORMAT_DEPTH32F; config.depth_format = mjPIXEL_FORMAT_DEPTH32F;
auto target = std::make_unique<RenderTarget>(GetEngine(), config); auto target = std::make_unique<RenderTarget>(GetEngine(), config);
target->Prepare(request.width, request.height); target->Prepare(viewport.width, viewport.height);
request.target = target.get(); render_requests_[0].target = target.get();
render_requests_[1].target = target.get();
DrawMode last_draw_mode = render_requests_[0].draw_mode;
render_requests_[0].draw_mode = DrawMode::Depth;
ReadPixelsRequest read_request; ReadPixelsRequest read_request;
read_request.output = reinterpret_cast<uint8_t*>(depth); read_request.output = reinterpret_cast<uint8_t*>(depth);
read_request.num_bytes = viewport.width * viewport.height * sizeof(float); read_request.num_bytes = viewport.width * viewport.height * sizeof(float);
const FrameHandle frame = const FrameHandle frame =
FilamentContext::Render({&request, 1}, {&read_request, 1}); FilamentContext::Render({&render_requests_[0], 1}, {&read_request, 1});
FilamentContext::WaitForFrame(frame); FilamentContext::WaitForFrame(frame);
render_requests_[0].target = nullptr;
render_requests_[1].target = nullptr;
render_requests_[0].draw_mode = last_draw_mode;
} }
} }
@@ -175,10 +203,7 @@ void MjrFilamentRenderer::UploadHeightField(const mjModel* model, int id) {
uintptr_t MjrFilamentRenderer::UploadGuiImage(uintptr_t tex_id, uintptr_t MjrFilamentRenderer::UploadGuiImage(uintptr_t tex_id,
const uint8_t* pixels, int width, const uint8_t* pixels, int width,
int height, int bpp) { int height, int bpp) {
if (imgui_bridge_) { return imgui_bridge_->UploadImage(tex_id, pixels, width, height, bpp);
return imgui_bridge_->UploadImage(tex_id, pixels, width, height, bpp);
}
return 0;
} }
void MjrFilamentRenderer::UpdateGui() { DrawGui(scene_bridge_.get()); } void MjrFilamentRenderer::UpdateGui() { DrawGui(scene_bridge_.get()); }
@@ -25,7 +25,6 @@
#include "experimental/filament/filament/filament_context.h" #include "experimental/filament/filament/filament_context.h"
#include "experimental/filament/filament/imgui_bridge.h" #include "experimental/filament/filament/imgui_bridge.h"
#include "experimental/filament/filament/scene_bridge.h" #include "experimental/filament/filament/scene_bridge.h"
#include "experimental/filament/filament/scene_view.h"
#include "experimental/filament/render_context_filament.h" #include "experimental/filament/render_context_filament.h"
namespace mujoco { namespace mujoco {
@@ -71,16 +70,14 @@ class MjrFilamentRenderer : public FilamentContext {
MjrFilamentRenderer& operator=(const MjrFilamentRenderer&) = delete; MjrFilamentRenderer& operator=(const MjrFilamentRenderer&) = delete;
private: private:
enum SwapChainType { enum class FrameBufferMode {
kWindowSwapChain, Window,
kOffscreenSwapChain, OffScreen,
OffScreenWithGui,
}; };
DrawMode last_render_mode_ = DrawMode::Color; FrameBufferMode mode_ = FrameBufferMode::Window;
mjvGLCamera last_camera_; RenderRequest render_requests_[2];
SwapChainType scene_swap_chain_target_ = kWindowSwapChain;
SwapChainType gui_swap_chain_target_ = kWindowSwapChain;
std::unique_ptr<SceneView> scene_view_;
std::unique_ptr<SceneBridge> scene_bridge_; std::unique_ptr<SceneBridge> scene_bridge_;
std::unique_ptr<ImguiBridge> imgui_bridge_; std::unique_ptr<ImguiBridge> imgui_bridge_;
}; };
@@ -87,9 +87,9 @@ static std::unique_ptr<Texture> CreateFallbackIndirectLightTexture(
return texture; return texture;
} }
SceneBridge::SceneBridge(ObjectManager* object_mgr, SceneView* scene_view, SceneBridge::SceneBridge(ObjectManager* object_mgr, const mjModel* model)
const mjModel* model) : object_mgr_(object_mgr) {
: scene_view_(scene_view), object_mgr_(object_mgr) { scene_view_ = std::make_unique<SceneView>(object_mgr_->GetEngine());
model_objects_ = model_objects_ =
std::make_unique<ModelObjects>(model, object_mgr_->GetEngine()); std::make_unique<ModelObjects>(model, object_mgr_->GetEngine());
@@ -354,8 +354,16 @@ filament::math::mat4 CalculateClipFromWorld(const mjrRect& viewport,
} }
void SceneBridge::Update(const mjrRect& viewport, const mjvScene* scene) { void SceneBridge::Update(const mjrRect& viewport, const mjvScene* scene) {
filament::View* view = scene_view_->GetDefaultRenderView(); if (scene->flags[mjRND_SHADOW]) {
view->setShadowingEnabled(scene->flags[mjRND_SHADOW] ? true : false); scene_view_->EnableShadows();
} else {
scene_view_->DisableShadows();
}
if (scene->flags[mjRND_REFLECTION]) {
scene_view_->EnableReflections();
} else {
scene_view_->DisableReflections();
}
mjtNum hpos[3], hfwd[3]; mjtNum hpos[3], hfwd[3];
float headpos[3], gazedir[3]; float headpos[3], gazedir[3];
@@ -25,7 +25,6 @@
#include <mujoco/mjvisualize.h> #include <mujoco/mjvisualize.h>
#include <mujoco/mujoco.h> #include <mujoco/mujoco.h>
#include "experimental/filament/filament/light.h" #include "experimental/filament/filament/light.h"
#include "experimental/filament/filament/material.h"
#include "experimental/filament/filament/model_objects.h" #include "experimental/filament/filament/model_objects.h"
#include "experimental/filament/filament/object_manager.h" #include "experimental/filament/filament/object_manager.h"
#include "experimental/filament/filament/renderable.h" #include "experimental/filament/filament/renderable.h"
@@ -37,8 +36,7 @@ namespace mujoco {
// Manages all mjModel data and updates a SceneView using an mjvScene. // Manages all mjModel data and updates a SceneView using an mjvScene.
class SceneBridge { class SceneBridge {
public: public:
SceneBridge(ObjectManager* object_mgr, SceneView* scene_view, SceneBridge(ObjectManager* object_mgr, const mjModel* model);
const mjModel* model);
~SceneBridge(); ~SceneBridge();
// Updates the environment light using the KTX image at the given path. // Updates the environment light using the KTX image at the given path.
@@ -56,7 +54,8 @@ class SceneBridge {
void UploadTexture(const mjModel* model, int id); void UploadTexture(const mjModel* model, int id);
void UploadHeightField(const mjModel* model, int id); void UploadHeightField(const mjModel* model, int id);
SceneView* GetSceneView() const { return scene_view_; } // Returns the managed scene.
SceneView* GetSceneView() const { return scene_view_.get(); }
SceneBridge(const SceneBridge&) = delete; SceneBridge(const SceneBridge&) = delete;
SceneBridge& operator=(const SceneBridge&) = delete; SceneBridge& operator=(const SceneBridge&) = delete;
@@ -69,7 +68,7 @@ class SceneBridge {
std::optional<filament::math::float3> ClipFromWorld( std::optional<filament::math::float3> ClipFromWorld(
const filament::math::float3& pos) const; const filament::math::float3& pos) const;
SceneView* scene_view_ = nullptr; std::unique_ptr<SceneView> scene_view_;
ObjectManager* object_mgr_ = nullptr; ObjectManager* object_mgr_ = nullptr;
std::unique_ptr<ModelObjects> model_objects_; std::unique_ptr<ModelObjects> model_objects_;
std::unique_ptr<Light> fallback_ibl_; std::unique_ptr<Light> fallback_ibl_;
@@ -351,7 +351,6 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom,
const mjModel* model = model_objs->GetModel(); const mjModel* model = model_objs->GetModel();
const bool use_segid_color = scene->flags[mjRND_IDCOLOR]; const bool use_segid_color = scene->flags[mjRND_IDCOLOR];
const bool enable_reflection = scene->flags[mjRND_REFLECTION];
MaterialParams params; MaterialParams params;
params.color = ReadFloat4(geom.rgba); params.color = ReadFloat4(geom.rgba);
if (geom.type == mjGEOM_PLANE) { if (geom.type == mjGEOM_PLANE) {
@@ -361,8 +360,7 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom,
params.reflective = false; params.reflective = false;
} else { } else {
renderable.SetReceiveShadows(true); renderable.SetReceiveShadows(true);
params.reflective = params.reflective = geom.reflectance > 0 && params.color.a == 1.0f;
enable_reflection && geom.reflectance > 0 && params.color.a == 1.0f;
} }
} }
renderable.SetLayerMask(geom.category); renderable.SetLayerMask(geom.category);
@@ -127,9 +127,7 @@ static void SetupReflectionCamera(const mat4& surface_xform,
SceneView::SceneView(filament::Engine* engine) : engine_(engine) { SceneView::SceneView(filament::Engine* engine) : engine_(engine) {
scene_ = engine->createScene(); scene_ = engine->createScene();
ux_scene_ = engine->createScene();
camera_ = engine->createCamera(utils::EntityManager::get().create()); camera_ = engine->createCamera(utils::EntityManager::get().create());
ux_camera_ = engine->createCamera(utils::EntityManager::get().create());
reflect_camera_ = engine->createCamera(utils::EntityManager::get().create()); reflect_camera_ = engine->createCamera(utils::EntityManager::get().create());
for (auto& view : views_) { for (auto& view : views_) {
@@ -139,12 +137,6 @@ SceneView::SceneView(filament::Engine* engine) : engine_(engine) {
view->setVisibleLayers(0xff, mjCAT_ALL); view->setVisibleLayers(0xff, mjCAT_ALL);
} }
ux_view_ = engine->createView();
ux_view_->setScene(ux_scene_);
ux_view_->setCamera(ux_camera_);
ux_view_->setPostProcessingEnabled(false);
ux_view_->setShadowingEnabled(false);
reflect_view_ = engine->createView(); reflect_view_ = engine->createView();
reflect_view_->setScene(scene_); reflect_view_->setScene(scene_);
reflect_view_->setCamera(reflect_camera_); reflect_view_->setCamera(reflect_camera_);
@@ -172,22 +164,16 @@ SceneView::~SceneView() {
for (auto& renderable : renderables_) { for (auto& renderable : renderables_) {
renderable->RemoveFromScene(scene_); renderable->RemoveFromScene(scene_);
} }
for (auto& renderable : ux_renderables_) {
renderable->RemoveFromScene(ux_scene_);
}
lights_.clear(); lights_.clear();
renderables_.clear(); renderables_.clear();
reflect_targets_.clear(); reflect_targets_.clear();
engine_->destroyCameraComponent(reflect_camera_->getEntity()); engine_->destroyCameraComponent(reflect_camera_->getEntity());
engine_->destroy(reflect_view_); engine_->destroy(reflect_view_);
engine_->destroyCameraComponent(ux_camera_->getEntity());
engine_->destroy(ux_view_);
engine_->destroyCameraComponent(camera_->getEntity()); engine_->destroyCameraComponent(camera_->getEntity());
if (color_grading_) { if (color_grading_) {
engine_->destroy(color_grading_); engine_->destroy(color_grading_);
} }
engine_->destroy(scene_); engine_->destroy(scene_);
engine_->destroy(ux_scene_);
for (auto& view : views_) { for (auto& view : views_) {
engine_->destroy(view); engine_->destroy(view);
} }
@@ -224,18 +210,6 @@ void SceneView::RemoveFromScene(Renderable* renderable) {
} }
} }
void SceneView::AddToUxScene(Renderable* renderable) {
if (ux_renderables_.insert(renderable).second) {
renderable->AddToScene(ux_scene_);
}
}
void SceneView::RemoveFromUxScene(Renderable* renderable) {
if (ux_renderables_.erase(renderable)) {
renderable->RemoveFromScene(ux_scene_);
}
}
void SceneView::AddToScene(filament::Skybox* skybox) { void SceneView::AddToScene(filament::Skybox* skybox) {
skybox_ = skybox; skybox_ = skybox;
scene_->setSkybox(skybox); scene_->setSkybox(skybox);
@@ -255,7 +229,6 @@ void SceneView::Render(filament::Renderer* renderer,
for (auto& view : views_) { for (auto& view : views_) {
view->setViewport(viewport); view->setViewport(viewport);
} }
ux_view_->setViewport(viewport);
reflect_view_->setViewport(viewport); reflect_view_->setViewport(viewport);
SetupCamera(request.camera, viewport, camera_); SetupCamera(request.camera, viewport, camera_);
@@ -276,7 +249,7 @@ void SceneView::Render(filament::Renderer* renderer,
} }
// Render reflection passes. // Render reflection passes.
if (request.draw_mode == DrawMode::Color) { if (request.draw_mode == DrawMode::Color && reflections_enabled_) {
for (size_t i = 0; i < reflectives_.size(); ++i) { for (size_t i = 0; i < reflectives_.size(); ++i) {
Renderable* renderable = reflectives_[i]; Renderable* renderable = reflectives_[i];
@@ -301,15 +274,6 @@ void SceneView::Render(filament::Renderer* renderer,
renderer->render(view); renderer->render(view);
view->setRenderTarget(nullptr); view->setRenderTarget(nullptr);
if (request.enable_ux) {
ux_camera_->setProjection(filament::Camera::Projection::ORTHO, 0.0f,
viewport.width, viewport.height, 0.0f, 0.0f,
1.0f);
ux_view_->setRenderTarget(render_target);
renderer->render(ux_view_);
ux_view_->setRenderTarget(nullptr);
}
if (request.target) { if (request.target) {
view->setMultiSampleAntiAliasingOptions(options); view->setMultiSampleAntiAliasingOptions(options);
} }
@@ -335,9 +299,11 @@ void SceneView::AddReflectiveRenderable(Renderable* renderable) {
auto& target = reflect_targets_[index]; auto& target = reflect_targets_[index];
target->Prepare(viewport.width, viewport.height); target->Prepare(viewport.width, viewport.height);
MaterialTextures textures = renderable->GetMaterialTextures(); if (reflections_enabled_) {
textures.reflection = target->GetColorTexture(); MaterialTextures textures = renderable->GetMaterialTextures();
renderable->UpdateMaterial(renderable->GetMaterialParams(), textures); textures.reflection = target->GetColorTexture();
renderable->UpdateMaterial(renderable->GetMaterialParams(), textures);
}
} }
void SceneView::SetColorGradingOptions(const ColorGradingOptions& opts) { void SceneView::SetColorGradingOptions(const ColorGradingOptions& opts) {
@@ -353,6 +319,43 @@ void SceneView::SetColorGradingOptions(const ColorGradingOptions& opts) {
color_grading_options_ = opts; color_grading_options_ = opts;
} }
void SceneView::EnableShadows() {
views_[kNormalIndex]->setShadowingEnabled(true);
}
void SceneView::DisableShadows() {
views_[kNormalIndex]->setShadowingEnabled(false);
}
void SceneView::EnableReflections() {
reflections_enabled_ = true;
for (int i = 0; i < reflectives_.size(); ++i) {
Renderable* renderable = reflectives_[i];
MaterialTextures textures = renderable->GetMaterialTextures();
textures.reflection = reflect_targets_[i]->GetColorTexture();
renderable->UpdateMaterial(renderable->GetMaterialParams(), textures);
}
}
void SceneView::DisableReflections() {
reflections_enabled_ = false;
for (Renderable* renderable : reflectives_) {
MaterialTextures textures = renderable->GetMaterialTextures();
textures.reflection = nullptr;
renderable->UpdateMaterial(renderable->GetMaterialParams(), textures);
}
}
void SceneView::EnablePostProcessing() {
views_[kNormalIndex]->setPostProcessingEnabled(true);
}
void SceneView::DisablePostProcessing() {
views_[kNormalIndex]->setPostProcessingEnabled(false);
}
filament::View* SceneView::GetDefaultRenderView() { filament::View* SceneView::GetDefaultRenderView() {
return views_[kNormalIndex]; return views_[kNormalIndex];
} }
+14 -14
View File
@@ -38,8 +38,7 @@ namespace mujoco {
// //
// The filament Scene is populated with the objects (e.g. lights, renderables, // The filament Scene is populated with the objects (e.g. lights, renderables,
// skybox, etc.). It manages multiple views to support a variety of draw modes // skybox, etc.). It manages multiple views to support a variety of draw modes
// (e.g. normal, depth, segmentation, etc.) as well as reflective surfaces. It // (e.g. normal, depth, segmentation, etc.) as well as reflective surfaces.
// also manages a separate scene and view for UX rendering.
class SceneView { class SceneView {
public: public:
SceneView(filament::Engine* engine); SceneView(filament::Engine* engine);
@@ -53,10 +52,6 @@ class SceneView {
void AddToScene(filament::Skybox* skybox); void AddToScene(filament::Skybox* skybox);
void RemoveFromScene(filament::Skybox* skybox); void RemoveFromScene(filament::Skybox* skybox);
// Adds/removes entities from the UX scene, which is rendered separately.
void AddToUxScene(Renderable* renderable);
void RemoveFromUxScene(Renderable* renderable);
// Parameters for rendering the scene. // Parameters for rendering the scene.
struct RenderRequest { struct RenderRequest {
// The draw mode (e.g. normal, depth, segmentation) to render. // The draw mode (e.g. normal, depth, segmentation) to render.
@@ -67,8 +62,6 @@ class SceneView {
mjvGLCamera camera; mjvGLCamera camera;
// An optional render target into which the scene will be rendered. // An optional render target into which the scene will be rendered.
RenderTarget* target = nullptr; RenderTarget* target = nullptr;
// Whether or not to render the UX as a separate pass.
bool enable_ux = false;
}; };
// Renders the scene. // Renders the scene.
@@ -77,6 +70,18 @@ class SceneView {
// Returns the filament Engine managing the scene. // Returns the filament Engine managing the scene.
filament::Engine* GetEngine() const { return engine_; } filament::Engine* GetEngine() const { return engine_; }
// Enables/disables shadows for the default render view.
void EnableShadows();
void DisableShadows();
// Enables/disables reflections for the default render view.
void EnableReflections();
void DisableReflections();
// Enables/disables post processing for the default render view.
void EnablePostProcessing();
void DisablePostProcessing();
// Returns the underlying filament View that is used for normal rendering. // Returns the underlying filament View that is used for normal rendering.
// Callers can update rendering settings (e.g. post processing) directly. // Callers can update rendering settings (e.g. post processing) directly.
filament::View* GetDefaultRenderView(); filament::View* GetDefaultRenderView();
@@ -95,7 +100,6 @@ class SceneView {
filament::Engine* engine_ = nullptr; filament::Engine* engine_ = nullptr;
filament::Scene* scene_ = nullptr; filament::Scene* scene_ = nullptr;
filament::Scene* ux_scene_ = nullptr;
filament::Camera* camera_ = nullptr; filament::Camera* camera_ = nullptr;
filament::ColorGrading* color_grading_ = nullptr; filament::ColorGrading* color_grading_ = nullptr;
ColorGradingOptions color_grading_options_; ColorGradingOptions color_grading_options_;
@@ -106,16 +110,12 @@ class SceneView {
std::unordered_set<Renderable*> renderables_; std::unordered_set<Renderable*> renderables_;
filament::Skybox* skybox_ = nullptr; filament::Skybox* skybox_ = nullptr;
// Custom view for UX.
filament::View* ux_view_ = nullptr;
filament::Camera* ux_camera_ = nullptr;
std::unordered_set<Renderable*> ux_renderables_;
// Custom view and camera for reflective surfaces. // Custom view and camera for reflective surfaces.
filament::View* reflect_view_ = nullptr; filament::View* reflect_view_ = nullptr;
filament::Camera* reflect_camera_ = nullptr; filament::Camera* reflect_camera_ = nullptr;
// The list of reflective renderables and their corresponding render targets. // The list of reflective renderables and their corresponding render targets.
bool reflections_enabled_ = true;
std::vector<Renderable*> reflectives_; std::vector<Renderable*> reflectives_;
std::vector<std::unique_ptr<RenderTarget>> reflect_targets_; std::vector<std::unique_ptr<RenderTarget>> reflect_targets_;
}; };