Allow Transforms to be set directly on Renderable.

Also allow multiple Meshes to be assigned at once rather than one at a
time.

This simplifies both the usage and implementation of Renderable.

PiperOrigin-RevId: 902665683
Change-Id: I23ff365a54fd6a3814ed3452b1e094ae9f698a1b
This commit is contained in:
Haroon Qureshi
2026-04-20 09:05:43 -07:00
committed by Copybara-Service
parent b2281883dd
commit 476e2e909e
8 changed files with 343 additions and 353 deletions
@@ -156,7 +156,6 @@ void FilamentContext::Render(const mjrRect& viewport, const mjvScene* scene) {
request.viewport = viewport; request.viewport = viewport;
request.camera = last_camera_; request.camera = last_camera_;
request.enable_ux = (gui_swap_chain_target_ == kWindowSwapChain); request.enable_ux = (gui_swap_chain_target_ == kWindowSwapChain);
request.gui_scale = imgui_bridge_ ? imgui_bridge_->GetScale() : 1.0f;
scene_view_->Render(renderer_, request); scene_view_->Render(renderer_, request);
renderer_->endFrame(); renderer_->endFrame();
} }
@@ -233,7 +232,6 @@ void FilamentContext::ReadPixels(mjrRect viewport, unsigned char* rgb,
request.target = color_target_.get(); request.target = color_target_.get();
request.camera = last_camera_; request.camera = last_camera_;
request.enable_ux = (gui_swap_chain_target_ == kOffscreenSwapChain); request.enable_ux = (gui_swap_chain_target_ == kOffscreenSwapChain);
request.gui_scale = imgui_bridge_ ? imgui_bridge_->GetScale() : 1.0f;
scene_view_->Render(renderer_, request); scene_view_->Render(renderer_, request);
const size_t num_bytes = viewport.width * viewport.height * 3; const size_t num_bytes = viewport.width * viewport.height * 3;
@@ -21,6 +21,8 @@
#include <vector> #include <vector>
#include <imgui.h> #include <imgui.h>
#include <math/mat3.h>
#include <math/vec3.h>
#include <math/vec4.h> #include <math/vec4.h>
#include <mujoco/mujoco.h> #include <mujoco/mujoco.h>
#include "experimental/filament/filament/material.h" #include "experimental/filament/filament/material.h"
@@ -32,6 +34,9 @@
namespace mujoco { namespace mujoco {
using filament::math::float3;
using filament::math::mat3f;
ImguiBridge::ImguiBridge(ObjectManager* object_mgr, SceneView* scene_view) ImguiBridge::ImguiBridge(ObjectManager* object_mgr, SceneView* scene_view)
: object_mgr_(object_mgr), scene_view_(scene_view) {} : object_mgr_(object_mgr), scene_view_(scene_view) {}
@@ -235,11 +240,7 @@ void ImguiBridge::Update() {
const int height = size.y * scale.y; const int height = size.y * scale.y;
auto& renderable = renderables_[renderable_index]; auto& renderable = renderables_[renderable_index];
if (renderable->GetNumMeshes() == 0) { renderable->SetMesh(mesh, index_offset, command.ElemCount);
renderable->AppendMesh(mesh, index_offset, command.ElemCount);
} else {
renderable->UpdateMesh(0, mesh, index_offset, command.ElemCount);
}
MaterialTextures textures; MaterialTextures textures;
textures.color = textures_[command.GetTexID()].get(); textures.color = textures_[command.GetTexID()].get();
@@ -259,6 +260,8 @@ void ImguiBridge::Update() {
properties.scissor[3] = height; properties.scissor[3] = height;
} }
renderable->UpdateMaterial(properties, textures); renderable->UpdateMaterial(properties, textures);
renderable->SetTransform(
{float3{0, 0, 0}, mat3f(), float3(scale.x, scale.y, 1.0f)});
index_offset += command.ElemCount; index_offset += command.ElemCount;
++renderable_index; ++renderable_index;
@@ -284,10 +287,6 @@ void ImguiBridge::PrepareRenderables(int count) {
} }
} }
float ImguiBridge::GetScale() const {
return ImGui::GetIO().DisplayFramebufferScale.x;
}
static ImVec2 ClipSpaceToWindowCoordinates(float x, float y) { static ImVec2 ClipSpaceToWindowCoordinates(float x, float y) {
const ImVec2& display_size = ImGui::GetIO().DisplaySize; const ImVec2& display_size = ImGui::GetIO().DisplaySize;
const float pos_x = display_size.x * ((x + 1) * 0.5f); const float pos_x = display_size.x * ((x + 1) * 0.5f);
@@ -40,9 +40,6 @@ class ImguiBridge {
// synced. // synced.
void Update(); void Update();
// Returns the current ImGui scale factor.
float GetScale() const;
// 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);
+106 -107
View File
@@ -16,20 +16,26 @@
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <span>
#include <filament/Engine.h> #include <filament/Engine.h>
#include <filament/Material.h> #include <filament/Material.h>
#include <filament/RenderableManager.h> #include <filament/RenderableManager.h>
#include <filament/Scene.h> #include <filament/Scene.h>
#include <filament/TransformManager.h>
#include <math/mat4.h>
#include <utils/EntityManager.h> #include <utils/EntityManager.h>
#include <mujoco/mujoco.h> #include <mujoco/mujoco.h>
#include "experimental/filament/filament/draw_mode.h" #include "experimental/filament/filament/draw_mode.h"
#include "experimental/filament/filament/material.h" #include "experimental/filament/filament/material.h"
#include "experimental/filament/filament/math_util.h"
#include "experimental/filament/filament/mesh.h" #include "experimental/filament/filament/mesh.h"
#include "experimental/filament/filament/object_manager.h" #include "experimental/filament/filament/object_manager.h"
namespace mujoco { namespace mujoco {
using filament::math::mat4f;
void DefaultRenderableParams(RenderableParams* params) { void DefaultRenderableParams(RenderableParams* params) {
params->shading_model = ShadingModel::SceneObject; params->shading_model = ShadingModel::SceneObject;
} }
@@ -38,68 +44,77 @@ Renderable::Renderable(ObjectManager* object_mgr, const RenderableParams& params
: object_mgr_(object_mgr), params_(params) {} : object_mgr_(object_mgr), params_(params) {}
Renderable::~Renderable() noexcept { Renderable::~Renderable() noexcept {
while (!entities_.empty()) { filament::Engine* engine = GetEngine();
RemoveLastEntity(); utils::EntityManager& em = utils::EntityManager::get();
for (Part& part : parts_) {
if (assigned_scene_) {
assigned_scene_->remove(part.entity);
}
engine->destroy(part.entity);
em.destroy(part.entity);
} }
for (int i = 0; i < kNumDrawModes; ++i) { for (int i = 0; i < kNumDrawModes; ++i) {
if (instances_[i] != nullptr) { if (instances_[i] != nullptr) {
GetEngine()->destroy(instances_[i]); engine->destroy(instances_[i]);
instances_[i] = nullptr; instances_[i] = nullptr;
} }
} }
} }
void Renderable::RemoveLastEntity() { void Renderable::SetMesh(const Mesh* mesh, int elem_offset, int elem_count) {
if (entities_.empty()) { if (mesh == nullptr) {
return; mju_error("Cannot set mesh to nullptr.");
} }
utils::EntityManager& em = utils::EntityManager::get();
utils::Entity entity = entities_.back();
if (assigned_scene_) {
assigned_scene_->remove(entity);
}
GetEngine()->destroy(entity);
em.destroy(entity);
entities_.pop_back();
meshes_.pop_back();
}
void Renderable::UpdateMesh(int index, const Mesh* mesh, int elem_offset,
int elem_count) {
MeshInfo& mesh_info = SetMesh(index, mesh, elem_offset, elem_count);
UpdateEntity(index, mesh_info);
}
void Renderable::AppendMesh(const Mesh* mesh, int elem_offset, int elem_count) {
MeshInfo& mesh_info = SetMesh(-1, mesh, elem_offset, elem_count);
AppendEntity(mesh_info);
}
void Renderable::AppendEntity(const MeshInfo& mesh_info) {
const Mesh* mesh = mesh_info.mesh;
filament::VertexBuffer* vertex_buffer = mesh->GetFilamentVertexBuffer(); filament::VertexBuffer* vertex_buffer = mesh->GetFilamentVertexBuffer();
if (vertex_buffer == nullptr) { if (vertex_buffer == nullptr) {
mju_error("Invalid (null) vertex buffer."); mju_error("Invalid (null) vertex buffer.");
} }
filament::IndexBuffer* index_buffer = mesh->GetFilamentIndexBuffer(); filament::IndexBuffer* index_buffer = mesh->GetFilamentIndexBuffer();
if (index_buffer == nullptr) { if (index_buffer == nullptr) {
mju_error("Invalid (null) index buffer."); mju_error("Invalid (null) index buffer.");
} }
utils::Entity entity = utils::EntityManager::get().create(); if (elem_count == 0) {
if (entity.isNull()) { elem_count = index_buffer->getIndexCount() - elem_offset;
}
if (parts_.empty()) {
Part& part = parts_.emplace_back();
part.mesh = mesh;
part.elem_offset = elem_offset;
part.elem_count = elem_count;
InitPartEntity(part);
} else if (parts_.size() == 1) {
Part& part = parts_[0];
part.mesh = mesh;
part.elem_offset = elem_offset;
part.elem_count = elem_count;
filament::RenderableManager& rm = GetEngine()->getRenderableManager();
rm.setGeometryAt(rm.getInstance(part.entity), 0,
part.mesh->GetPrimitiveType(), vertex_buffer, index_buffer,
part.elem_offset, part.elem_count);
} else {
mju_error("Cannot set mesh for renderable with multiple parts.");
}
}
void Renderable::InitPartEntity(Part& part) {
part.entity = utils::EntityManager::get().create();
if (part.entity.isNull()) {
mju_error("Failed to create entity."); mju_error("Failed to create entity.");
} }
filament::VertexBuffer* vertex_buffer = part.mesh->GetFilamentVertexBuffer();
filament::IndexBuffer* index_buffer = part.mesh->GetFilamentIndexBuffer();
filament::RenderableManager::Builder builder(1); filament::RenderableManager::Builder builder(1);
builder.geometry(0, mesh->GetPrimitiveType(), vertex_buffer, index_buffer, builder.geometry(0, part.mesh->GetPrimitiveType(), vertex_buffer, index_buffer,
mesh_info.elem_offset, mesh_info.elem_count); part.elem_offset, part.elem_count);
if (mesh->HasBounds()) { if (part.mesh->HasBounds()) {
builder.boundingBox(mesh->GetBounds()); builder.boundingBox(part.mesh->GetBounds());
} else { } else {
builder.culling(false); builder.culling(false);
} }
@@ -113,56 +128,43 @@ void Renderable::AppendEntity(const MeshInfo& mesh_info) {
builder.blendOrder(0, blend_order_); builder.blendOrder(0, blend_order_);
builder.screenSpaceContactShadows(true); builder.screenSpaceContactShadows(true);
builder.build(*GetEngine(), entity); builder.build(*GetEngine(), part.entity);
if (assigned_scene_) { if (assigned_scene_) {
assigned_scene_->addEntity(entity); assigned_scene_->addEntity(part.entity);
} }
entities_.push_back(entity);
} }
void Renderable::UpdateEntity(int index, const MeshInfo& mesh_info) { void Renderable::SetTransform(const Trs& trs) {
if (index < 0 || index >= entities_.size()) { transform_ = trs.ToTransform();
mju_error("Invalid index %d for renderable.", index); filament::TransformManager& tm = GetEngine()->getTransformManager();
for (Part& part : parts_) {
tm.setTransform(tm.getInstance(part.entity), transform_);
} }
utils::Entity entity = entities_[index];
const Mesh* mesh = mesh_info.mesh;
filament::VertexBuffer* vertex_buffer = mesh->GetFilamentVertexBuffer();
if (vertex_buffer == nullptr) {
mju_error("Invalid (null) vertex buffer.");
}
filament::IndexBuffer* index_buffer = mesh->GetFilamentIndexBuffer();
if (index_buffer == nullptr) {
mju_error("Invalid (null) index buffer.");
}
filament::RenderableManager& rm = GetEngine()->getRenderableManager();
rm.setGeometryAt(rm.getInstance(entity), 0, mesh->GetPrimitiveType(),
vertex_buffer, index_buffer, mesh_info.elem_offset,
mesh_info.elem_count);
} }
Renderable::MeshInfo& Renderable::SetMesh(int index, const Mesh* mesh, const mat4f& Renderable::GetTransform() const {
int elem_offset, int elem_count) { return transform_;
if (index == -1) { }
index = meshes_.size();
meshes_.emplace_back(); void Renderable::SetMeshes(std::span<const Mesh*> meshes,
std::span<const mat4f> transforms) {
if (meshes.size() != transforms.size()) {
mju_error("Number of meshes does not match number of transforms.");
} }
if (index < 0 || index >= static_cast<int>(meshes_.size())) { if (!parts_.empty()) {
mju_error("Invalid index %d for renderable.", index); mju_error("Cannot set meshes for renderable with multiple parts.");
} }
MeshInfo* mesh_info = &meshes_[index]; filament::TransformManager& tm = GetEngine()->getTransformManager();
mesh_info->mesh = mesh; for (int i = 0; i < meshes.size(); ++i) {
mesh_info->elem_offset = elem_offset; Part& part = parts_.emplace_back();
mesh_info->elem_count = elem_count; part.mesh = meshes[i];
if (mesh_info->elem_count == 0) { part.elem_offset = 0;
const int total = part.elem_count = part.mesh->GetFilamentIndexBuffer()->getIndexCount();
mesh_info->mesh->GetFilamentIndexBuffer()->getIndexCount(); InitPartEntity(part);
mesh_info->elem_count = total - mesh_info->elem_offset;
tm.setTransform(tm.getInstance(part.entity), transforms[i]);
} }
return *mesh_info;
} }
void Renderable::AddToScene(filament::Scene* scene) { void Renderable::AddToScene(filament::Scene* scene) {
@@ -173,8 +175,8 @@ void Renderable::AddToScene(filament::Scene* scene) {
// Entities are already added to the scene. // Entities are already added to the scene.
return; return;
} }
for (utils::Entity& entity : entities_) { for (Part& part : parts_) {
scene->addEntity(entity); scene->addEntity(part.entity);
} }
assigned_scene_ = scene; assigned_scene_ = scene;
} }
@@ -183,8 +185,8 @@ void Renderable::RemoveFromScene(filament::Scene* scene) {
if (assigned_scene_ != scene) { if (assigned_scene_ != scene) {
mju_error("Attempting to remove renderable from wrong scene."); mju_error("Attempting to remove renderable from wrong scene.");
} }
for (utils::Entity& entity : entities_) { for (Part& part : parts_) {
scene->remove(entity); scene->remove(part.entity);
} }
assigned_scene_ = nullptr; assigned_scene_ = nullptr;
} }
@@ -245,8 +247,8 @@ void Renderable::SetDrawMode(DrawMode mode) {
filament::MaterialInstance* instance = instances_[static_cast<int>(mode)]; filament::MaterialInstance* instance = instances_[static_cast<int>(mode)];
if (instance) { if (instance) {
filament::RenderableManager& rm = GetEngine()->getRenderableManager(); filament::RenderableManager& rm = GetEngine()->getRenderableManager();
for (utils::Entity& entity : entities_) { for (Part& part : parts_) {
filament::RenderableManager::Instance ri = rm.getInstance(entity); filament::RenderableManager::Instance ri = rm.getInstance(part.entity);
rm.setMaterialInstanceAt(ri, 0, instance); rm.setMaterialInstanceAt(ri, 0, instance);
} }
} }
@@ -259,8 +261,8 @@ std::uint8_t Renderable::SetLayerMask(std::uint8_t mask) {
layer_mask_ = mask; layer_mask_ = mask;
filament::RenderableManager& rm = GetEngine()->getRenderableManager(); filament::RenderableManager& rm = GetEngine()->getRenderableManager();
for (utils::Entity& entity : entities_) { for (Part& part : parts_) {
rm.setLayerMask(rm.getInstance(entity), 0xff, layer_mask_); rm.setLayerMask(rm.getInstance(part.entity), 0xff, layer_mask_);
} }
} }
return prev; return prev;
@@ -272,8 +274,8 @@ std::uint8_t Renderable::SetPriority(std::uint8_t priority) {
priority_ = priority; priority_ = priority;
filament::RenderableManager& rm = GetEngine()->getRenderableManager(); filament::RenderableManager& rm = GetEngine()->getRenderableManager();
for (utils::Entity& entity : entities_) { for (Part& part : parts_) {
rm.setPriority(rm.getInstance(entity), priority_); rm.setPriority(rm.getInstance(part.entity), priority_);
} }
} }
return prev; return prev;
@@ -285,8 +287,8 @@ std::uint16_t Renderable::SetBlendOrder(std::uint16_t blend_order) {
blend_order_ = blend_order; blend_order_ = blend_order;
filament::RenderableManager& rm = GetEngine()->getRenderableManager(); filament::RenderableManager& rm = GetEngine()->getRenderableManager();
for (utils::Entity& entity : entities_) { for (Part& part : parts_) {
rm.setBlendOrderAt(rm.getInstance(entity), 0, blend_order_); rm.setBlendOrderAt(rm.getInstance(part.entity), 0, blend_order_);
} }
} }
return prev; return prev;
@@ -297,8 +299,8 @@ void Renderable::SetCastShadows(bool cast_shadows) {
cast_shadows_ = cast_shadows; cast_shadows_ = cast_shadows;
filament::RenderableManager& rm = GetEngine()->getRenderableManager(); filament::RenderableManager& rm = GetEngine()->getRenderableManager();
for (utils::Entity& entity : entities_) { for (Part& part : parts_) {
rm.setCastShadows(rm.getInstance(entity), cast_shadows_); rm.setCastShadows(rm.getInstance(part.entity), cast_shadows_);
} }
} }
} }
@@ -308,8 +310,8 @@ void Renderable::SetReceiveShadows(bool receive_shadows) {
receive_shadows_ = receive_shadows; receive_shadows_ = receive_shadows;
filament::RenderableManager& rm = GetEngine()->getRenderableManager(); filament::RenderableManager& rm = GetEngine()->getRenderableManager();
for (utils::Entity& entity : entities_) { for (Part& part : parts_) {
rm.setReceiveShadows(rm.getInstance(entity), receive_shadows_); rm.setReceiveShadows(rm.getInstance(part.entity), receive_shadows_);
} }
} }
} }
@@ -322,20 +324,17 @@ void Renderable::SetWireframe(bool wireframe) {
wireframe_ = wireframe; wireframe_ = wireframe;
filament::RenderableManager& rm = GetEngine()->getRenderableManager(); filament::RenderableManager& rm = GetEngine()->getRenderableManager();
for (int i = 0; i < entities_.size(); ++i) { for (Part& part : parts_) {
utils::Entity& entity = entities_[i]; filament::VertexBuffer* vertex_buffer = part.mesh->GetFilamentVertexBuffer();
const Mesh* mesh = meshes_[i].mesh; filament::IndexBuffer* index_buffer = part.mesh->GetFilamentIndexBuffer();
filament::VertexBuffer* vertex_buffer = mesh->GetFilamentVertexBuffer(); rm.setGeometryAt(rm.getInstance(part.entity), 0,
filament::IndexBuffer* index_buffer = mesh->GetFilamentIndexBuffer(); wireframe_ ? kWireframeType : part.mesh->GetPrimitiveType(),
rm.setGeometryAt(rm.getInstance(entity), 0, vertex_buffer, index_buffer, part.elem_offset,
wireframe_ ? kWireframeType : mesh->GetPrimitiveType(), part.elem_count);
vertex_buffer, index_buffer, meshes_[i].elem_offset,
meshes_[i].elem_count);
} }
} }
} }
ObjectManager::MaterialType Renderable::GetColorMaterialType() const { ObjectManager::MaterialType Renderable::GetColorMaterialType() const {
if (params_.shading_model == ShadingModel::DecorLines) { if (params_.shading_model == ShadingModel::DecorLines) {
return ObjectManager::kUnlitLine; return ObjectManager::kUnlitLine;
@@ -360,8 +359,8 @@ ObjectManager::MaterialType Renderable::GetColorMaterialType() const {
// geometry) and `mesh_texcoordadr` stores the address of the mesh uvs if // geometry) and `mesh_texcoordadr` stores the address of the mesh uvs if
// it has them. // it has them.
bool has_texcoords = false; bool has_texcoords = false;
if (!meshes_.empty()) { if (!parts_.empty()) {
const auto attribs = meshes_[0].mesh->GetVertexAttributes(); const auto attribs = parts_[0].mesh->GetVertexAttributes();
auto it = std::find(attribs.begin(), attribs.end(), auto it = std::find(attribs.begin(), attribs.end(),
filament::VertexAttribute::UV0); filament::VertexAttribute::UV0);
has_texcoords = (it != attribs.end()); has_texcoords = (it != attribs.end());
+36 -38
View File
@@ -16,13 +16,16 @@
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_RENDERABLE_H_ #define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_RENDERABLE_H_
#include <cstdint> #include <cstdint>
#include <span>
#include <vector> #include <vector>
#include <filament/Engine.h> #include <filament/Engine.h>
#include <filament/Scene.h> #include <filament/Scene.h>
#include <math/mat4.h>
#include <utils/Entity.h> #include <utils/Entity.h>
#include "experimental/filament/filament/draw_mode.h" #include "experimental/filament/filament/draw_mode.h"
#include "experimental/filament/filament/material.h" #include "experimental/filament/filament/material.h"
#include "experimental/filament/filament/math_util.h"
#include "experimental/filament/filament/mesh.h" #include "experimental/filament/filament/mesh.h"
#include "experimental/filament/filament/object_manager.h" #include "experimental/filament/filament/object_manager.h"
@@ -43,14 +46,20 @@ struct RenderableParams {
void DefaultRenderableParams(RenderableParams* params); void DefaultRenderableParams(RenderableParams* params);
// A collection of meshes and a material that, together, define an object that // A Renderable is effectively two things: a mesh and a material.
// can be rendered in a scene.
// //
// Meshes can be added to the Renderable either by unique_ptr or raw pointer. // The mesh describes the surface geometry of the object and the material
// This determines whether or not the Renderable takes ownership of the mesh. // describes how that surface interacts with light (i.e. the color of each point
// on the surface).
// //
// Internally, the Renderable creates a filament::Entity for each mesh and // Defining the mesh is easy; just call SetMesh.
// assigns the same material instance to all of them. //
// Defining a Material happens in two stages. First, the user specifies the
// ShadingModel to use for Rendering. This describes the overall intent of
// how the Renderable will appear (e.g. lit, unlit, wireframe, etc.). Next,
// the user specifies the MaterialParams and MaterialTextures to use with the
// ShadingModel. Its these properties that ultimately define the actual material
// of the Renderable.
class Renderable { class Renderable {
public: public:
// Default filament values for priority and layer mask. // Default filament values for priority and layer mask.
@@ -63,19 +72,21 @@ class Renderable {
Renderable(const Renderable&) = delete; Renderable(const Renderable&) = delete;
Renderable& operator=(const Renderable&) = delete; Renderable& operator=(const Renderable&) = delete;
// Appends a mesh to the renderable. The elem_offset and elem_count parameters // Sets the mesh of the renderable. The elem_offset and elem_count parameters
// can be used to specify a submesh to append. If elem_count is 0, assumes // can be used to specify a submesh within the mesh. If elem_count is 0,
// the entire mesh should be appended. // assumes the entire mesh should be appended.
void AppendMesh(const Mesh* mesh, int elem_offset = 0, int elem_count = 0); void SetMesh(const Mesh* mesh, int elem_offset = 0, int elem_count = 0);
// Replaces the mesh at the index with a new mesh. The elem_offset and // Sets the transform of the renderable.
// elem_count parameters can be used to specify a submesh to append. If void SetTransform(const Trs& trs);
// elem_count is 0, assumes the entire mesh should be appended.
void UpdateMesh(int index, const Mesh* mesh, int elem_offset = 0,
int elem_count = 0);
// Returns the number of meshes that define the renderable. // Returns the current transform of the renderable.
int GetNumMeshes() const { return meshes_.size(); } const filament::math::mat4f& GetTransform() const;
// Sets multiple meshes for a renderable. Each mesh is assigned a specific
// transform to allow for assembly of compound shapes.
void SetMeshes(std::span<const Mesh*> meshes,
std::span<const filament::math::mat4f> transforms);
// Sets the layer mask for the managed filament Entities. Layer masks can be // Sets the layer mask for the managed filament Entities. Layer masks can be
// used to show/hide the renderable in different views. Returns the previous // used to show/hide the renderable in different views. Returns the previous
@@ -107,7 +118,8 @@ class Renderable {
// Removes the renderable from the given filament Scene. // Removes the renderable from the given filament Scene.
void RemoveFromScene(filament::Scene* scene); void RemoveFromScene(filament::Scene* scene);
// Sets the material instance for all managed entities. // Further defines the material of the renderable. Only applies to renderables
// with a SceneObject shading model.
void SetDrawMode(DrawMode mode); void SetDrawMode(DrawMode mode);
// Updates the parameters for the material. // Updates the parameters for the material.
@@ -123,30 +135,15 @@ class Renderable {
// Returns the filament Engine managing the renderables. // Returns the filament Engine managing the renderables.
filament::Engine* GetEngine(); filament::Engine* GetEngine();
// Returns the underlying filament::entity for the given mesh.
utils::Entity operator[](int index) { return entities_[index]; }
private: private:
struct MeshInfo { struct Part {
utils::Entity entity;
const Mesh* mesh = nullptr; const Mesh* mesh = nullptr;
int elem_offset = 0; int elem_offset = 0;
int elem_count = 0; int elem_count = 0;
}; };
// Sets the mesh information for the mesh at the given index. If index is -1, void InitPartEntity(Part& part);
// a new mesh will be appended to the renderable.
MeshInfo& SetMesh(int index, const Mesh* mesh, int elem_offset,
int elem_count);
// Appends a new filament::Entity to the renderable, configured to use the
// given mesh.
void AppendEntity(const MeshInfo& mesh_info);
// Updates the filament::Entity at the given index to use the given mesh.
void UpdateEntity(int index, const MeshInfo& mesh_info);
// Removes the last filament::Entity from the renderable.
void RemoveLastEntity();
void AssignMaterial(DrawMode mode, ObjectManager::MaterialType material_type); void AssignMaterial(DrawMode mode, ObjectManager::MaterialType material_type);
@@ -159,8 +156,9 @@ class Renderable {
MaterialTextures material_textures_; MaterialTextures material_textures_;
DrawMode draw_mode_ = DrawMode::Color; DrawMode draw_mode_ = DrawMode::Color;
filament::Scene* assigned_scene_ = nullptr; filament::Scene* assigned_scene_ = nullptr;
std::vector<utils::Entity> entities_; std::vector<Part> parts_;
std::vector<MeshInfo> meshes_; filament::math::mat4f transform_;
std::uint8_t priority_ = kDefaultPriority; std::uint8_t priority_ = kDefaultPriority;
std::uint8_t layer_mask_ = kDefaultLayerMask; std::uint8_t layer_mask_ = kDefaultLayerMask;
std::uint16_t blend_order_ = 0; std::uint16_t blend_order_ = 0;
@@ -18,6 +18,7 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <numbers> #include <numbers>
#include <vector>
#include <filament/Material.h> #include <filament/Material.h>
#include <filament/RenderableManager.h> #include <filament/RenderableManager.h>
@@ -44,30 +45,12 @@ namespace mujoco {
using filament::math::float2; using filament::math::float2;
using filament::math::float3; using filament::math::float3;
using filament::math::float4; using filament::math::float4;
using filament::math::mat4; using filament::math::mat4f;
// An arbitrary scale factor for arrows. // An arbitrary scale factor for arrows.
static constexpr float kArrowScale = 1.f / 6.f; static constexpr float kArrowScale = 1.f / 6.f;
static constexpr float kArrowHeadSize = 1.75f; static constexpr float kArrowHeadSize = 1.75f;
// Some built-in geometries are actually composed of multiple simple shapes. A
// capsule, for example, is a open-ended tube with two dome ends. We use these
// constants to help identify which entity (by index) represents which part of
// the overall shape.
static constexpr int kCapsuleTopDome = 1;
static constexpr int kCapsuleBottomDome = 2;
static constexpr int kCylinderTopDisk = 1;
static constexpr int kCylinderBottomDisk = 2;
static constexpr int kArrow0Cone = 1;
static constexpr int kArrow0ConeDisk = 2;
static constexpr int kArrow0BottomDisk = 3;
static constexpr int kArrow1Cone = 1;
static constexpr int kArrow1BottomDisk = 2;
static constexpr int kArrow2TopCone = 1;
static constexpr int kArrow2BottomCone = 2;
static constexpr int kArrow2TopConeDisk = 3;
static constexpr int kArrow2BottomConeDisk = 4;
// Returns the tile size for infinite plane texture alignment. // Returns the tile size for infinite plane texture alignment.
// This is duplicated from engine_vis_visualize.c (re-center infinite plane) // This is duplicated from engine_vis_visualize.c (re-center infinite plane)
// to ensure UV scaling matches the re-centering increments. // to ensure UV scaling matches the re-centering increments.
@@ -87,102 +70,240 @@ static bool IsBehind(const float* headpos, const float* pos, const float* mat) {
0.0f); 0.0f);
} }
static void AddMesh(Renderable& renderable, ModelObjects* model_objs, static const Mesh* GetMesh(ModelObjects* model_objs, int data_id) {
int data_id) {
const Mesh* mesh = model_objs->GetMeshBuffer(data_id); const Mesh* mesh = model_objs->GetMeshBuffer(data_id);
if (mesh == nullptr) { if (mesh == nullptr) {
mju_error("Unknown mesh %d", data_id); mju_error("Unknown mesh %d", data_id);
} }
renderable.AppendMesh(mesh); return mesh;
} }
static void AddSkinFlexMesh(Renderable& renderable, ModelObjects* model_objs, static const Mesh* GetSkinFlexMesh(ModelObjects* model_objs, int objid) {
int objid) { return model_objs->GetFlexSkinGeomMesh(objid);
renderable.AppendMesh(model_objs->GetFlexSkinGeomMesh(objid));
} }
static void AddHeightField(Renderable& renderable, ModelObjects* model_objs, static const Mesh* GetHeightField(ModelObjects* model_objs, int hfield_id) {
int hfield_id) {
const Mesh* mesh = model_objs->GetHeightFieldBuffer(hfield_id); const Mesh* mesh = model_objs->GetHeightFieldBuffer(hfield_id);
if (mesh == nullptr) { if (mesh == nullptr) {
mju_error("Unknown height field %d", hfield_id); mju_error("Unknown height field %d", hfield_id);
} }
renderable.AppendMesh(mesh); return mesh;
} }
static void AddShape(Renderable& renderable, ModelObjects* model_objs, static const Mesh* GetShape(ModelObjects* model_objs,
ModelObjects::ShapeType shape_type) { ModelObjects::ShapeType shape_type) {
const Mesh* mesh = model_objs->GetShapeBuffer(shape_type); const Mesh* mesh = model_objs->GetShapeBuffer(shape_type);
if (mesh == nullptr) { if (mesh == nullptr) {
mju_error("Unknown shape %d", shape_type); mju_error("Unknown shape %d", shape_type);
} }
renderable.AppendMesh(mesh); return mesh;
} }
static void PrepareGeomMeshes(Renderable& renderable, const mjvGeom& geom, static void PrepareGeomMeshes(Renderable& renderable, const mjvGeom& geom,
const mjvScene* scene, const mjvScene* scene,
ModelObjects* model_objects) { ModelObjects* model_objects) {
std::vector<const Mesh*> meshes;
std::vector<mat4f> transforms;
Trs trs = {
.translation = ReadFloat3(geom.pos),
.rotation = ReadMat3(geom.mat),
.size = ReadFloat3(geom.size),
};
switch ((mjtGeom)geom.type) { switch ((mjtGeom)geom.type) {
case mjGEOM_MESH: case mjGEOM_MESH:
AddMesh(renderable, model_objects, geom.dataid); meshes.push_back(GetMesh(model_objects, geom.dataid));
// Ignore size for meshes.
transforms.push_back(mat4f(trs.rotation, trs.translation));
break; break;
case mjGEOM_HFIELD: case mjGEOM_HFIELD:
AddHeightField(renderable, model_objects, geom.dataid); meshes.push_back(GetHeightField(model_objects, geom.dataid));
// Ignore size for height fields.
transforms.push_back(mat4f(trs.rotation, trs.translation));
break; break;
case mjGEOM_PLANE: case mjGEOM_PLANE: {
AddShape(renderable, model_objects, ModelObjects::kPlane); meshes.push_back(GetShape(model_objects, ModelObjects::kPlane));
const bool is_infinite = !(trs.size.x > 0 && trs.size.y > 0);
if (is_infinite) {
// Infinite planes are scaled to match the tile size used by
// re-centering in engine_vis_visualize.c.
const float plane_scale = static_cast<float>(mjMAXPLANEGRID) / 2.0f;
trs.size.x = plane_scale;
trs.size.y = plane_scale;
}
// Planes only define an xy size, so set the z-dimension to 1.0f.
trs.size.z = 1.0f;
transforms.push_back(trs.ToTransform());
break; break;
}
case mjGEOM_SPHERE: case mjGEOM_SPHERE:
AddShape(renderable, model_objects, ModelObjects::kSphere); meshes.push_back(GetShape(model_objects, ModelObjects::kSphere));
transforms.push_back(trs.ToTransform());
break; break;
case mjGEOM_ELLIPSOID: case mjGEOM_ELLIPSOID:
AddShape(renderable, model_objects, ModelObjects::kSphere); meshes.push_back(GetShape(model_objects, ModelObjects::kSphere));
transforms.push_back(trs.ToTransform());
break; break;
case mjGEOM_BOX: case mjGEOM_BOX:
AddShape(renderable, model_objects, ModelObjects::kBox); meshes.push_back(GetShape(model_objects, ModelObjects::kBox));
transforms.push_back(trs.ToTransform());
break; break;
case mjGEOM_CAPSULE: case mjGEOM_CAPSULE: {
AddShape(renderable, model_objects, ModelObjects::kTube); // Capsules are a tube with two domes at the ends.
AddShape(renderable, model_objects, ModelObjects::kDome); meshes.push_back(GetShape(model_objects, ModelObjects::kTube));
AddShape(renderable, model_objects, ModelObjects::kDome); meshes.push_back(GetShape(model_objects, ModelObjects::kDome));
meshes.push_back(GetShape(model_objects, ModelObjects::kDome));
transforms.push_back(trs.ToTransform());
// We apply an inverse scale to the domes to counteract the capsule's
// overall scale so that the domes remain spherical in shape.
const float xz_size = 0.5f * (trs.size.x + trs.size.y);
// Move the first dome to the top of the capsule.
mat4f top = mat4f(trs.rotation, trs.translation);
top *= mat4f::translation(float3{0, 0, trs.size.z});
top *= mat4f::scaling(float3{trs.size.x, trs.size.y, xz_size});
transforms.push_back(top);
// Move the second dome to the bottom of the capsule and rotate it 180
// degrees so that it's facing the right way.
mat4f bottom = mat4f(trs.rotation, trs.translation);
bottom *= mat4f::translation(float3{0, 0, -trs.size.z});
bottom *= mat4f::rotation(std::numbers::pi, float3{1, 0, 0});
bottom *= mat4f::scaling(float3{trs.size.x, trs.size.y, xz_size});
transforms.push_back(bottom);
break; break;
case mjGEOM_CYLINDER: }
AddShape(renderable, model_objects, ModelObjects::kTube); case mjGEOM_CYLINDER: {
AddShape(renderable, model_objects, ModelObjects::kDisk); // Cylinders are a tube with two disks at the ends.
AddShape(renderable, model_objects, ModelObjects::kDisk); meshes.push_back(GetShape(model_objects, ModelObjects::kTube));
meshes.push_back(GetShape(model_objects, ModelObjects::kDisk));
meshes.push_back(GetShape(model_objects, ModelObjects::kDisk));
transforms.push_back(trs.ToTransform());
// Move the first disk to the top of the cylinder.
mat4f top = mat4f(trs.rotation, trs.translation);
top *= mat4f::translation(float3{0, 0, trs.size.z});
top *= mat4f::scaling(trs.size);
transforms.push_back(top);
// Move the second disk to the bottom of the cylinder. Rotate the disk
// 180 degrees so that the normals point outwards.
mat4f bottom = mat4f(trs.rotation, trs.translation);
bottom *= mat4f::translation(float3{0, 0, -trs.size.z});
bottom *= mat4f::rotation(std::numbers::pi, float3{1, 0, 0});
bottom *= mat4f::scaling(trs.size);
transforms.push_back(bottom);
break; break;
case mjGEOM_ARROW: }
AddShape(renderable, model_objects, ModelObjects::kTube); case mjGEOM_ARROW: {
AddShape(renderable, model_objects, ModelObjects::kCone); meshes.push_back(GetShape(model_objects, ModelObjects::kTube));
AddShape(renderable, model_objects, ModelObjects::kDisk); meshes.push_back(GetShape(model_objects, ModelObjects::kCone));
meshes.push_back(GetShape(model_objects, ModelObjects::kDisk));
meshes.push_back(GetShape(model_objects, ModelObjects::kDisk));
mat4f base = mat4f(trs.rotation, trs.translation);
base *= mat4f::scaling(float3{1, 1, kArrowScale});
base *= mat4f::translation(float3{0, 0, trs.size.z});
transforms.push_back(base * mat4f::scaling(trs.size));
mat4f top = base;
top *= mat4f::translation(float3{0, 0, trs.size.z});
top *= mat4f::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
transforms.push_back(top * mat4f::scaling(trs.size));
mat4f top_disk = base;
top_disk *= mat4f::translation(float3{0, 0, trs.size.z});
top_disk *= mat4f::rotation(std::numbers::pi, float3{1, 0, 0});
top_disk *= mat4f::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
transforms.push_back(top_disk * mat4f::scaling(trs.size));
mat4f bottom = base;
bottom *= mat4f::translation(float3{0, 0, -trs.size.z});
bottom *= mat4f::rotation(std::numbers::pi, float3{1, 0, 0});
transforms.push_back(bottom * mat4f::scaling(trs.size));
break; break;
case mjGEOM_ARROW1: }
AddShape(renderable, model_objects, ModelObjects::kTube); case mjGEOM_ARROW1: {
AddShape(renderable, model_objects, ModelObjects::kCone); meshes.push_back(GetShape(model_objects, ModelObjects::kTube));
AddShape(renderable, model_objects, ModelObjects::kDisk); meshes.push_back(GetShape(model_objects, ModelObjects::kCone));
AddShape(renderable, model_objects, ModelObjects::kDisk); meshes.push_back(GetShape(model_objects, ModelObjects::kDisk));
mat4f base = mat4f(trs.rotation, trs.translation);
base *= mat4f::scaling(float3{1, 1, kArrowScale});
base *= mat4f::translation(float3{0, 0, trs.size.z});
transforms.push_back(base * mat4f::scaling(trs.size));
mat4f top = base;
top *= mat4f::translation(float3{0, 0, trs.size.z});
transforms.push_back(top * mat4f::scaling(trs.size));
mat4f bottom = base;
bottom *= mat4f::translation(float3{0, 0, -trs.size.z});
bottom *= mat4f::rotation(std::numbers::pi, float3{1, 0, 0});
transforms.push_back(bottom * mat4f::scaling(trs.size));
break; break;
case mjGEOM_ARROW2: }
AddShape(renderable, model_objects, ModelObjects::kTube); case mjGEOM_ARROW2: {
AddShape(renderable, model_objects, ModelObjects::kCone); meshes.push_back(GetShape(model_objects, ModelObjects::kTube));
AddShape(renderable, model_objects, ModelObjects::kCone); meshes.push_back(GetShape(model_objects, ModelObjects::kCone));
AddShape(renderable, model_objects, ModelObjects::kDisk); meshes.push_back(GetShape(model_objects, ModelObjects::kCone));
AddShape(renderable, model_objects, ModelObjects::kDisk); meshes.push_back(GetShape(model_objects, ModelObjects::kDisk));
meshes.push_back(GetShape(model_objects, ModelObjects::kDisk));
mat4f base = mat4f(trs.rotation, trs.translation);
base *= mat4f::scaling(float3{1, 1, kArrowScale});
base *= mat4f::translation(float3{0, 0, trs.size.z});
transforms.push_back(base * mat4f::scaling(trs.size));
mat4f top = base;
top *= mat4f::translation(float3{0, 0, trs.size.z});
top *= mat4f::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
transforms.push_back(top * mat4f::scaling(trs.size));
mat4f bottom = base;
bottom *= mat4f::translation(float3{0, 0, -trs.size.z});
bottom *= mat4f::rotation(std::numbers::pi, float3{1, 0, 0});
bottom *= mat4f::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
transforms.push_back(bottom * mat4f::scaling(trs.size));
mat4f top_disk = base;
top_disk *= mat4f::translation(float3{0, 0, trs.size.z});
top_disk *= mat4f::rotation(std::numbers::pi, float3{1, 0, 0});
top_disk *= mat4f::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
transforms.push_back(top_disk * mat4f::scaling(trs.size));
mat4f bottom_disk = base;
bottom_disk *= mat4f::translation(float3{0, 0, -trs.size.z});
transforms.push_back(bottom_disk * mat4f::scaling(trs.size));
break; break;
}
case mjGEOM_LINE: case mjGEOM_LINE:
AddShape(renderable, model_objects, ModelObjects::kLine); meshes.push_back(GetShape(model_objects, ModelObjects::kLine));
transforms.push_back(trs.ToTransform());
break; break;
case mjGEOM_LINEBOX: case mjGEOM_LINEBOX:
AddShape(renderable, model_objects, ModelObjects::kLineBox); meshes.push_back(GetShape(model_objects, ModelObjects::kLineBox));
transforms.push_back(trs.ToTransform());
break; break;
case mjGEOM_TRIANGLE: case mjGEOM_TRIANGLE:
AddShape(renderable, model_objects, ModelObjects::kTriangle); meshes.push_back(GetShape(model_objects, ModelObjects::kTriangle));
transforms.push_back(trs.ToTransform());
break; break;
case mjGEOM_FLEX: case mjGEOM_FLEX:
AddSkinFlexMesh(renderable, model_objects, geom.objid); meshes.push_back(GetSkinFlexMesh(model_objects, geom.objid));
// Flexes are defined in global space.
transforms.push_back(mat4f());
break; break;
case mjGEOM_SKIN: case mjGEOM_SKIN:
AddSkinFlexMesh(renderable, model_objects, geom.objid); meshes.push_back(GetSkinFlexMesh(model_objects, geom.objid));
// Skins are defined in global space.
transforms.push_back(mat4f());
break; break;
case mjGEOM_NONE: case mjGEOM_NONE:
case mjGEOM_LABEL: case mjGEOM_LABEL:
@@ -193,124 +314,8 @@ static void PrepareGeomMeshes(Renderable& renderable, const mjvGeom& geom,
mju_warning("Unsupported geom type: %d", geom.type); mju_warning("Unsupported geom type: %d", geom.type);
break; break;
} }
}
static void SetGeomTransform(Renderable& renderable, const mjvGeom& geom) { renderable.SetMeshes(meshes, transforms);
// Flex and skin geometries are in global space.
if (geom.type == mjGEOM_FLEX || geom.type == mjGEOM_SKIN) {
return;
}
mat4 transform = mat4(ReadMat3(geom.mat), ReadFloat3(geom.pos));
renderable.SetLayerMask(geom.category);
float3 size = ReadFloat3(geom.size);
filament::TransformManager& tm =
renderable.GetEngine()->getTransformManager();
for (int j = 0; j < renderable.GetNumMeshes(); ++j) {
const utils::Entity& entity = renderable[j];
// Update object transform.
mat4 entity_transform = transform;
// Some built-in drawables are composed of multiple entities. For example,
// capsules are a combination of a open tube and two dome end caps.
if (geom.type == mjGEOM_CYLINDER) {
// Cylinders are a tube with two disks at the ends. The "bottom" disk is
// rotated so that the normals point outwards.
if (j == kCylinderTopDisk) {
entity_transform *= mat4::translation(float3{0, 0, size.z});
} else if (j == kCylinderBottomDisk) {
entity_transform *= mat4::translation(float3{0, 0, -size.z});
entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0});
}
} else if (geom.type == mjGEOM_CAPSULE) {
// Capsules are a tube with two domes at the ends. We apply an inverse
// scale to the domes to "counteract" the capsule's overall scale so that
// the domes remain spherical in shape.
const float xz_size = 0.5f * (size.x + size.y);
if (j == kCapsuleTopDome) {
entity_transform *= mat4::translation(float3{0, 0, size.z});
entity_transform *= mat4::scaling(float3{1, 1, xz_size / size.z});
} else if (j == kCapsuleBottomDome) {
entity_transform *= mat4::translation(float3{0, 0, -size.z});
entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0});
entity_transform *= mat4::scaling(float3{1, 1, xz_size / size.z});
}
} else if (geom.type == mjGEOM_ARROW) {
// An arrow is a tube with a cone at the end and a disk cap at the other
// end. Because the cone head's base is larger than the tube, an extra
// disk is added to the base of the cone. This disk is rotated such that
// its normal points outwards.
entity_transform *= mat4::scaling(float3{1, 1, kArrowScale});
entity_transform *= mat4::translation(float3{0, 0, size.z});
if (j == kArrow0Cone) {
entity_transform *= mat4::translation(float3{0, 0, size.z});
entity_transform *=
mat4::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
} else if (j == kArrow0ConeDisk) {
entity_transform *= mat4::translation(float3{0, 0, size.z});
entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0});
entity_transform *=
mat4::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
} else if (j == kArrow0BottomDisk) {
entity_transform *= mat4::translation(float3{0, 0, -size.z});
entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0});
}
} else if (geom.type == mjGEOM_ARROW1) {
// An arrow1 is a tube with a cone at the end and a disk cap at the other
// end.
entity_transform *= mat4::scaling(float3{1, 1, kArrowScale});
entity_transform *= mat4::translation(float3{0, 0, size.z});
if (j == kArrow1Cone) {
entity_transform *= mat4::translation(float3{0, 0, size.z});
} else if (j == kArrow1BottomDisk) {
entity_transform *= mat4::translation(float3{0, 0, -size.z});
entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0});
}
} else if (geom.type == mjGEOM_ARROW2) {
// An arrow2 is a tube with a cone at both ends. Like the standard arrow,
// an extra disk is added to the base of each cone.
entity_transform *= mat4::scaling(float3{1, 1, kArrowScale});
entity_transform *= mat4::translation(float3{0, 0, size.z});
if (j == kArrow2TopCone) {
entity_transform *= mat4::translation(float3{0, 0, size.z});
entity_transform *=
mat4::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
} else if (j == kArrow2BottomCone) {
entity_transform *= mat4::translation(float3{0, 0, -size.z});
entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0});
entity_transform *=
mat4::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
} else if (j == kArrow2TopConeDisk) {
entity_transform *= mat4::translation(float3{0, 0, size.z});
entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0});
entity_transform *=
mat4::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
} else if (j == kArrow2BottomConeDisk) {
entity_transform *= mat4::translation(float3{0, 0, -size.z});
entity_transform *=
mat4::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
}
}
if (geom.type == mjGEOM_PLANE) {
const bool is_infinite = !(size.x > 0 && size.y > 0);
if (is_infinite) {
// Infinite planes are scaled to match the tile size used by
// re-centering in engine_vis_visualize.c.
const float plane_scale = static_cast<float>(mjMAXPLANEGRID) / 2.0f;
entity_transform *=
mat4::scaling(float3{plane_scale, plane_scale, 1.0f});
} else {
// Regular planes are scaled by geom.size.
entity_transform *= mat4::scaling(float3{size.x, size.y, 1.0f});
}
} else if (geom.type != mjGEOM_MESH && geom.type != mjGEOM_HFIELD) {
entity_transform *= mat4::scaling(size);
}
tm.setTransform(tm.getInstance(entity), entity_transform);
}
} }
static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom, static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom,
@@ -334,10 +339,12 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom,
enable_reflection && geom.reflectance > 0 && params.color.a == 1.0f; enable_reflection && geom.reflectance > 0 && params.color.a == 1.0f;
} }
} }
renderable.SetWireframe(scene->flags[mjRND_WIREFRAME]); renderable.SetLayerMask(geom.category);
if (geom.category == mjCAT_DECOR) { if (geom.category == mjCAT_DECOR) {
renderable.SetCastShadows(false); renderable.SetCastShadows(false);
renderable.SetReceiveShadows(false); renderable.SetReceiveShadows(false);
} else {
renderable.SetWireframe(scene->flags[mjRND_WIREFRAME]);
} }
MaterialTextures textures; MaterialTextures textures;
@@ -471,10 +478,7 @@ std::unique_ptr<Renderable> CreateGeomRenderable(
config.shading_model = shading_model; config.shading_model = shading_model;
auto renderable = std::make_unique<Renderable>(object_mgr, config); auto renderable = std::make_unique<Renderable>(object_mgr, config);
// The order of these calls is important. e.g. We need to create the filament
// renderable entities before we can set their transform.
PrepareGeomMeshes(*renderable, geom, scene, model_objs); PrepareGeomMeshes(*renderable, geom, scene, model_objs);
SetGeomTransform(*renderable, geom);
UpdateGeomMaterial(*renderable, geom, scene, model_objs, object_mgr, headpos); UpdateGeomMaterial(*renderable, geom, scene, model_objs, object_mgr, headpos);
return renderable; return renderable;
@@ -277,13 +277,11 @@ void SceneView::Render(filament::Renderer* renderer,
// Render reflection passes. // Render reflection passes.
if (request.draw_mode == DrawMode::Color) { if (request.draw_mode == DrawMode::Color) {
filament::TransformManager& tm = engine_->getTransformManager();
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];
// We assume the 0th entity is the reflective entity. // We assume the 0th entity is the reflective entity.
const utils::Entity entity = (*renderable)[0]; mat4 transform(renderable->GetTransform());
const mat4 transform(tm.getTransform(tm.getInstance(entity)));
SetupReflectionCamera(transform, camera_, reflect_camera_); SetupReflectionCamera(transform, camera_, reflect_camera_);
// Hide reflective surface from its own reflection pass. // Hide reflective surface from its own reflection pass.
@@ -305,8 +303,7 @@ void SceneView::Render(filament::Renderer* renderer,
if (request.enable_ux) { if (request.enable_ux) {
ux_camera_->setProjection(filament::Camera::Projection::ORTHO, 0.0f, ux_camera_->setProjection(filament::Camera::Projection::ORTHO, 0.0f,
viewport.width / request.gui_scale, viewport.width, viewport.height, 0.0f, 0.0f,
viewport.height / request.gui_scale, 0.0f, 0.0f,
1.0f); 1.0f);
ux_view_->setRenderTarget(render_target); ux_view_->setRenderTarget(render_target);
renderer->render(ux_view_); renderer->render(ux_view_);
@@ -69,8 +69,6 @@ class SceneView {
RenderTarget* target = nullptr; RenderTarget* target = nullptr;
// Whether or not to render the UX as a separate pass. // Whether or not to render the UX as a separate pass.
bool enable_ux = false; bool enable_ux = false;
// The scale factor to use for UX rendering.
float gui_scale = 1.0f;
}; };
// Renders the scene. // Renders the scene.