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:
committed by
Copybara-Service
parent
b2281883dd
commit
476e2e909e
@@ -156,7 +156,6 @@ void FilamentContext::Render(const mjrRect& viewport, const mjvScene* scene) {
|
||||
request.viewport = viewport;
|
||||
request.camera = last_camera_;
|
||||
request.enable_ux = (gui_swap_chain_target_ == kWindowSwapChain);
|
||||
request.gui_scale = imgui_bridge_ ? imgui_bridge_->GetScale() : 1.0f;
|
||||
scene_view_->Render(renderer_, request);
|
||||
renderer_->endFrame();
|
||||
}
|
||||
@@ -233,7 +232,6 @@ void FilamentContext::ReadPixels(mjrRect viewport, unsigned char* rgb,
|
||||
request.target = color_target_.get();
|
||||
request.camera = last_camera_;
|
||||
request.enable_ux = (gui_swap_chain_target_ == kOffscreenSwapChain);
|
||||
request.gui_scale = imgui_bridge_ ? imgui_bridge_->GetScale() : 1.0f;
|
||||
scene_view_->Render(renderer_, request);
|
||||
|
||||
const size_t num_bytes = viewport.width * viewport.height * 3;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <math/mat3.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "experimental/filament/filament/material.h"
|
||||
@@ -32,6 +34,9 @@
|
||||
|
||||
namespace mujoco {
|
||||
|
||||
using filament::math::float3;
|
||||
using filament::math::mat3f;
|
||||
|
||||
ImguiBridge::ImguiBridge(ObjectManager* object_mgr, SceneView* scene_view)
|
||||
: object_mgr_(object_mgr), scene_view_(scene_view) {}
|
||||
|
||||
@@ -235,11 +240,7 @@ void ImguiBridge::Update() {
|
||||
const int height = size.y * scale.y;
|
||||
|
||||
auto& renderable = renderables_[renderable_index];
|
||||
if (renderable->GetNumMeshes() == 0) {
|
||||
renderable->AppendMesh(mesh, index_offset, command.ElemCount);
|
||||
} else {
|
||||
renderable->UpdateMesh(0, mesh, index_offset, command.ElemCount);
|
||||
}
|
||||
renderable->SetMesh(mesh, index_offset, command.ElemCount);
|
||||
|
||||
MaterialTextures textures;
|
||||
textures.color = textures_[command.GetTexID()].get();
|
||||
@@ -259,6 +260,8 @@ void ImguiBridge::Update() {
|
||||
properties.scissor[3] = height;
|
||||
}
|
||||
renderable->UpdateMaterial(properties, textures);
|
||||
renderable->SetTransform(
|
||||
{float3{0, 0, 0}, mat3f(), float3(scale.x, scale.y, 1.0f)});
|
||||
|
||||
index_offset += command.ElemCount;
|
||||
++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) {
|
||||
const ImVec2& display_size = ImGui::GetIO().DisplaySize;
|
||||
const float pos_x = display_size.x * ((x + 1) * 0.5f);
|
||||
|
||||
@@ -40,9 +40,6 @@ class ImguiBridge {
|
||||
// synced.
|
||||
void Update();
|
||||
|
||||
// Returns the current ImGui scale factor.
|
||||
float GetScale() const;
|
||||
|
||||
// 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,
|
||||
int height, int bpp);
|
||||
|
||||
@@ -16,20 +16,26 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <math/mat4.h>
|
||||
#include <utils/EntityManager.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "experimental/filament/filament/draw_mode.h"
|
||||
#include "experimental/filament/filament/material.h"
|
||||
#include "experimental/filament/filament/math_util.h"
|
||||
#include "experimental/filament/filament/mesh.h"
|
||||
#include "experimental/filament/filament/object_manager.h"
|
||||
|
||||
namespace mujoco {
|
||||
|
||||
using filament::math::mat4f;
|
||||
|
||||
void DefaultRenderableParams(RenderableParams* params) {
|
||||
params->shading_model = ShadingModel::SceneObject;
|
||||
}
|
||||
@@ -38,68 +44,77 @@ Renderable::Renderable(ObjectManager* object_mgr, const RenderableParams& params
|
||||
: object_mgr_(object_mgr), params_(params) {}
|
||||
|
||||
Renderable::~Renderable() noexcept {
|
||||
while (!entities_.empty()) {
|
||||
RemoveLastEntity();
|
||||
filament::Engine* engine = GetEngine();
|
||||
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) {
|
||||
if (instances_[i] != nullptr) {
|
||||
GetEngine()->destroy(instances_[i]);
|
||||
engine->destroy(instances_[i]);
|
||||
instances_[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Renderable::RemoveLastEntity() {
|
||||
if (entities_.empty()) {
|
||||
return;
|
||||
void Renderable::SetMesh(const Mesh* mesh, int elem_offset, int elem_count) {
|
||||
if (mesh == nullptr) {
|
||||
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();
|
||||
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.");
|
||||
}
|
||||
|
||||
utils::Entity entity = utils::EntityManager::get().create();
|
||||
if (entity.isNull()) {
|
||||
if (elem_count == 0) {
|
||||
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.");
|
||||
}
|
||||
|
||||
filament::VertexBuffer* vertex_buffer = part.mesh->GetFilamentVertexBuffer();
|
||||
filament::IndexBuffer* index_buffer = part.mesh->GetFilamentIndexBuffer();
|
||||
|
||||
filament::RenderableManager::Builder builder(1);
|
||||
builder.geometry(0, mesh->GetPrimitiveType(), vertex_buffer, index_buffer,
|
||||
mesh_info.elem_offset, mesh_info.elem_count);
|
||||
if (mesh->HasBounds()) {
|
||||
builder.boundingBox(mesh->GetBounds());
|
||||
builder.geometry(0, part.mesh->GetPrimitiveType(), vertex_buffer, index_buffer,
|
||||
part.elem_offset, part.elem_count);
|
||||
if (part.mesh->HasBounds()) {
|
||||
builder.boundingBox(part.mesh->GetBounds());
|
||||
} else {
|
||||
builder.culling(false);
|
||||
}
|
||||
@@ -113,56 +128,43 @@ void Renderable::AppendEntity(const MeshInfo& mesh_info) {
|
||||
builder.blendOrder(0, blend_order_);
|
||||
builder.screenSpaceContactShadows(true);
|
||||
|
||||
builder.build(*GetEngine(), entity);
|
||||
builder.build(*GetEngine(), part.entity);
|
||||
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) {
|
||||
if (index < 0 || index >= entities_.size()) {
|
||||
mju_error("Invalid index %d for renderable.", index);
|
||||
void Renderable::SetTransform(const Trs& trs) {
|
||||
transform_ = trs.ToTransform();
|
||||
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,
|
||||
int elem_offset, int elem_count) {
|
||||
if (index == -1) {
|
||||
index = meshes_.size();
|
||||
meshes_.emplace_back();
|
||||
const mat4f& Renderable::GetTransform() const {
|
||||
return transform_;
|
||||
}
|
||||
|
||||
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())) {
|
||||
mju_error("Invalid index %d for renderable.", index);
|
||||
if (!parts_.empty()) {
|
||||
mju_error("Cannot set meshes for renderable with multiple parts.");
|
||||
}
|
||||
|
||||
MeshInfo* mesh_info = &meshes_[index];
|
||||
mesh_info->mesh = mesh;
|
||||
mesh_info->elem_offset = elem_offset;
|
||||
mesh_info->elem_count = elem_count;
|
||||
if (mesh_info->elem_count == 0) {
|
||||
const int total =
|
||||
mesh_info->mesh->GetFilamentIndexBuffer()->getIndexCount();
|
||||
mesh_info->elem_count = total - mesh_info->elem_offset;
|
||||
filament::TransformManager& tm = GetEngine()->getTransformManager();
|
||||
for (int i = 0; i < meshes.size(); ++i) {
|
||||
Part& part = parts_.emplace_back();
|
||||
part.mesh = meshes[i];
|
||||
part.elem_offset = 0;
|
||||
part.elem_count = part.mesh->GetFilamentIndexBuffer()->getIndexCount();
|
||||
InitPartEntity(part);
|
||||
|
||||
tm.setTransform(tm.getInstance(part.entity), transforms[i]);
|
||||
}
|
||||
return *mesh_info;
|
||||
}
|
||||
|
||||
void Renderable::AddToScene(filament::Scene* scene) {
|
||||
@@ -173,8 +175,8 @@ void Renderable::AddToScene(filament::Scene* scene) {
|
||||
// Entities are already added to the scene.
|
||||
return;
|
||||
}
|
||||
for (utils::Entity& entity : entities_) {
|
||||
scene->addEntity(entity);
|
||||
for (Part& part : parts_) {
|
||||
scene->addEntity(part.entity);
|
||||
}
|
||||
assigned_scene_ = scene;
|
||||
}
|
||||
@@ -183,8 +185,8 @@ void Renderable::RemoveFromScene(filament::Scene* scene) {
|
||||
if (assigned_scene_ != scene) {
|
||||
mju_error("Attempting to remove renderable from wrong scene.");
|
||||
}
|
||||
for (utils::Entity& entity : entities_) {
|
||||
scene->remove(entity);
|
||||
for (Part& part : parts_) {
|
||||
scene->remove(part.entity);
|
||||
}
|
||||
assigned_scene_ = nullptr;
|
||||
}
|
||||
@@ -245,8 +247,8 @@ void Renderable::SetDrawMode(DrawMode mode) {
|
||||
filament::MaterialInstance* instance = instances_[static_cast<int>(mode)];
|
||||
if (instance) {
|
||||
filament::RenderableManager& rm = GetEngine()->getRenderableManager();
|
||||
for (utils::Entity& entity : entities_) {
|
||||
filament::RenderableManager::Instance ri = rm.getInstance(entity);
|
||||
for (Part& part : parts_) {
|
||||
filament::RenderableManager::Instance ri = rm.getInstance(part.entity);
|
||||
rm.setMaterialInstanceAt(ri, 0, instance);
|
||||
}
|
||||
}
|
||||
@@ -259,8 +261,8 @@ std::uint8_t Renderable::SetLayerMask(std::uint8_t mask) {
|
||||
layer_mask_ = mask;
|
||||
|
||||
filament::RenderableManager& rm = GetEngine()->getRenderableManager();
|
||||
for (utils::Entity& entity : entities_) {
|
||||
rm.setLayerMask(rm.getInstance(entity), 0xff, layer_mask_);
|
||||
for (Part& part : parts_) {
|
||||
rm.setLayerMask(rm.getInstance(part.entity), 0xff, layer_mask_);
|
||||
}
|
||||
}
|
||||
return prev;
|
||||
@@ -272,8 +274,8 @@ std::uint8_t Renderable::SetPriority(std::uint8_t priority) {
|
||||
priority_ = priority;
|
||||
|
||||
filament::RenderableManager& rm = GetEngine()->getRenderableManager();
|
||||
for (utils::Entity& entity : entities_) {
|
||||
rm.setPriority(rm.getInstance(entity), priority_);
|
||||
for (Part& part : parts_) {
|
||||
rm.setPriority(rm.getInstance(part.entity), priority_);
|
||||
}
|
||||
}
|
||||
return prev;
|
||||
@@ -285,8 +287,8 @@ std::uint16_t Renderable::SetBlendOrder(std::uint16_t blend_order) {
|
||||
blend_order_ = blend_order;
|
||||
|
||||
filament::RenderableManager& rm = GetEngine()->getRenderableManager();
|
||||
for (utils::Entity& entity : entities_) {
|
||||
rm.setBlendOrderAt(rm.getInstance(entity), 0, blend_order_);
|
||||
for (Part& part : parts_) {
|
||||
rm.setBlendOrderAt(rm.getInstance(part.entity), 0, blend_order_);
|
||||
}
|
||||
}
|
||||
return prev;
|
||||
@@ -297,8 +299,8 @@ void Renderable::SetCastShadows(bool cast_shadows) {
|
||||
cast_shadows_ = cast_shadows;
|
||||
|
||||
filament::RenderableManager& rm = GetEngine()->getRenderableManager();
|
||||
for (utils::Entity& entity : entities_) {
|
||||
rm.setCastShadows(rm.getInstance(entity), cast_shadows_);
|
||||
for (Part& part : parts_) {
|
||||
rm.setCastShadows(rm.getInstance(part.entity), cast_shadows_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,8 +310,8 @@ void Renderable::SetReceiveShadows(bool receive_shadows) {
|
||||
receive_shadows_ = receive_shadows;
|
||||
|
||||
filament::RenderableManager& rm = GetEngine()->getRenderableManager();
|
||||
for (utils::Entity& entity : entities_) {
|
||||
rm.setReceiveShadows(rm.getInstance(entity), receive_shadows_);
|
||||
for (Part& part : parts_) {
|
||||
rm.setReceiveShadows(rm.getInstance(part.entity), receive_shadows_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,20 +324,17 @@ void Renderable::SetWireframe(bool wireframe) {
|
||||
wireframe_ = wireframe;
|
||||
|
||||
filament::RenderableManager& rm = GetEngine()->getRenderableManager();
|
||||
for (int i = 0; i < entities_.size(); ++i) {
|
||||
utils::Entity& entity = entities_[i];
|
||||
const Mesh* mesh = meshes_[i].mesh;
|
||||
filament::VertexBuffer* vertex_buffer = mesh->GetFilamentVertexBuffer();
|
||||
filament::IndexBuffer* index_buffer = mesh->GetFilamentIndexBuffer();
|
||||
rm.setGeometryAt(rm.getInstance(entity), 0,
|
||||
wireframe_ ? kWireframeType : mesh->GetPrimitiveType(),
|
||||
vertex_buffer, index_buffer, meshes_[i].elem_offset,
|
||||
meshes_[i].elem_count);
|
||||
for (Part& part : parts_) {
|
||||
filament::VertexBuffer* vertex_buffer = part.mesh->GetFilamentVertexBuffer();
|
||||
filament::IndexBuffer* index_buffer = part.mesh->GetFilamentIndexBuffer();
|
||||
rm.setGeometryAt(rm.getInstance(part.entity), 0,
|
||||
wireframe_ ? kWireframeType : part.mesh->GetPrimitiveType(),
|
||||
vertex_buffer, index_buffer, part.elem_offset,
|
||||
part.elem_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ObjectManager::MaterialType Renderable::GetColorMaterialType() const {
|
||||
if (params_.shading_model == ShadingModel::DecorLines) {
|
||||
return ObjectManager::kUnlitLine;
|
||||
@@ -360,8 +359,8 @@ ObjectManager::MaterialType Renderable::GetColorMaterialType() const {
|
||||
// geometry) and `mesh_texcoordadr` stores the address of the mesh uvs if
|
||||
// it has them.
|
||||
bool has_texcoords = false;
|
||||
if (!meshes_.empty()) {
|
||||
const auto attribs = meshes_[0].mesh->GetVertexAttributes();
|
||||
if (!parts_.empty()) {
|
||||
const auto attribs = parts_[0].mesh->GetVertexAttributes();
|
||||
auto it = std::find(attribs.begin(), attribs.end(),
|
||||
filament::VertexAttribute::UV0);
|
||||
has_texcoords = (it != attribs.end());
|
||||
|
||||
@@ -16,13 +16,16 @@
|
||||
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_RENDERABLE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <math/mat4.h>
|
||||
#include <utils/Entity.h>
|
||||
#include "experimental/filament/filament/draw_mode.h"
|
||||
#include "experimental/filament/filament/material.h"
|
||||
#include "experimental/filament/filament/math_util.h"
|
||||
#include "experimental/filament/filament/mesh.h"
|
||||
#include "experimental/filament/filament/object_manager.h"
|
||||
|
||||
@@ -43,14 +46,20 @@ struct RenderableParams {
|
||||
|
||||
void DefaultRenderableParams(RenderableParams* params);
|
||||
|
||||
// A collection of meshes and a material that, together, define an object that
|
||||
// can be rendered in a scene.
|
||||
// A Renderable is effectively two things: a mesh and a material.
|
||||
//
|
||||
// Meshes can be added to the Renderable either by unique_ptr or raw pointer.
|
||||
// This determines whether or not the Renderable takes ownership of the mesh.
|
||||
// The mesh describes the surface geometry of the object and the material
|
||||
// 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
|
||||
// assigns the same material instance to all of them.
|
||||
// Defining the mesh is easy; just call SetMesh.
|
||||
//
|
||||
// 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 {
|
||||
public:
|
||||
// Default filament values for priority and layer mask.
|
||||
@@ -63,19 +72,21 @@ class Renderable {
|
||||
Renderable(const Renderable&) = delete;
|
||||
Renderable& operator=(const Renderable&) = delete;
|
||||
|
||||
// Appends a mesh to the renderable. The elem_offset and elem_count parameters
|
||||
// can be used to specify a submesh to append. If elem_count is 0, assumes
|
||||
// the entire mesh should be appended.
|
||||
void AppendMesh(const Mesh* mesh, int elem_offset = 0, int elem_count = 0);
|
||||
// Sets the mesh of the renderable. The elem_offset and elem_count parameters
|
||||
// can be used to specify a submesh within the mesh. If elem_count is 0,
|
||||
// assumes the entire mesh should be appended.
|
||||
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
|
||||
// elem_count parameters can be used to specify a submesh to append. If
|
||||
// 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);
|
||||
// Sets the transform of the renderable.
|
||||
void SetTransform(const Trs& trs);
|
||||
|
||||
// Returns the number of meshes that define the renderable.
|
||||
int GetNumMeshes() const { return meshes_.size(); }
|
||||
// Returns the current transform of the renderable.
|
||||
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
|
||||
// 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.
|
||||
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);
|
||||
|
||||
// Updates the parameters for the material.
|
||||
@@ -123,30 +135,15 @@ class Renderable {
|
||||
// Returns the filament Engine managing the renderables.
|
||||
filament::Engine* GetEngine();
|
||||
|
||||
// Returns the underlying filament::entity for the given mesh.
|
||||
utils::Entity operator[](int index) { return entities_[index]; }
|
||||
|
||||
private:
|
||||
struct MeshInfo {
|
||||
struct Part {
|
||||
utils::Entity entity;
|
||||
const Mesh* mesh = nullptr;
|
||||
int elem_offset = 0;
|
||||
int elem_count = 0;
|
||||
};
|
||||
|
||||
// Sets the mesh information for the mesh at the given index. If index is -1,
|
||||
// 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 InitPartEntity(Part& part);
|
||||
|
||||
void AssignMaterial(DrawMode mode, ObjectManager::MaterialType material_type);
|
||||
|
||||
@@ -159,8 +156,9 @@ class Renderable {
|
||||
MaterialTextures material_textures_;
|
||||
DrawMode draw_mode_ = DrawMode::Color;
|
||||
filament::Scene* assigned_scene_ = nullptr;
|
||||
std::vector<utils::Entity> entities_;
|
||||
std::vector<MeshInfo> meshes_;
|
||||
std::vector<Part> parts_;
|
||||
filament::math::mat4f transform_;
|
||||
|
||||
std::uint8_t priority_ = kDefaultPriority;
|
||||
std::uint8_t layer_mask_ = kDefaultLayerMask;
|
||||
std::uint16_t blend_order_ = 0;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <numbers>
|
||||
#include <vector>
|
||||
|
||||
#include <filament/Material.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
@@ -44,30 +45,12 @@ namespace mujoco {
|
||||
using filament::math::float2;
|
||||
using filament::math::float3;
|
||||
using filament::math::float4;
|
||||
using filament::math::mat4;
|
||||
using filament::math::mat4f;
|
||||
|
||||
// An arbitrary scale factor for arrows.
|
||||
static constexpr float kArrowScale = 1.f / 6.f;
|
||||
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.
|
||||
// This is duplicated from engine_vis_visualize.c (re-center infinite plane)
|
||||
// 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);
|
||||
}
|
||||
|
||||
static void AddMesh(Renderable& renderable, ModelObjects* model_objs,
|
||||
int data_id) {
|
||||
static const Mesh* GetMesh(ModelObjects* model_objs, int data_id) {
|
||||
const Mesh* mesh = model_objs->GetMeshBuffer(data_id);
|
||||
if (mesh == nullptr) {
|
||||
mju_error("Unknown mesh %d", data_id);
|
||||
}
|
||||
renderable.AppendMesh(mesh);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static void AddSkinFlexMesh(Renderable& renderable, ModelObjects* model_objs,
|
||||
int objid) {
|
||||
renderable.AppendMesh(model_objs->GetFlexSkinGeomMesh(objid));
|
||||
static const Mesh* GetSkinFlexMesh(ModelObjects* model_objs, int objid) {
|
||||
return model_objs->GetFlexSkinGeomMesh(objid);
|
||||
}
|
||||
|
||||
static void AddHeightField(Renderable& renderable, ModelObjects* model_objs,
|
||||
int hfield_id) {
|
||||
static const Mesh* GetHeightField(ModelObjects* model_objs, int hfield_id) {
|
||||
const Mesh* mesh = model_objs->GetHeightFieldBuffer(hfield_id);
|
||||
if (mesh == nullptr) {
|
||||
mju_error("Unknown height field %d", hfield_id);
|
||||
}
|
||||
renderable.AppendMesh(mesh);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static void AddShape(Renderable& renderable, ModelObjects* model_objs,
|
||||
ModelObjects::ShapeType shape_type) {
|
||||
static const Mesh* GetShape(ModelObjects* model_objs,
|
||||
ModelObjects::ShapeType shape_type) {
|
||||
const Mesh* mesh = model_objs->GetShapeBuffer(shape_type);
|
||||
if (mesh == nullptr) {
|
||||
mju_error("Unknown shape %d", shape_type);
|
||||
}
|
||||
renderable.AppendMesh(mesh);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static void PrepareGeomMeshes(Renderable& renderable, const mjvGeom& geom,
|
||||
const mjvScene* scene,
|
||||
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) {
|
||||
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;
|
||||
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;
|
||||
case mjGEOM_PLANE:
|
||||
AddShape(renderable, model_objects, ModelObjects::kPlane);
|
||||
case mjGEOM_PLANE: {
|
||||
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;
|
||||
}
|
||||
case mjGEOM_SPHERE:
|
||||
AddShape(renderable, model_objects, ModelObjects::kSphere);
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kSphere));
|
||||
transforms.push_back(trs.ToTransform());
|
||||
break;
|
||||
case mjGEOM_ELLIPSOID:
|
||||
AddShape(renderable, model_objects, ModelObjects::kSphere);
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kSphere));
|
||||
transforms.push_back(trs.ToTransform());
|
||||
break;
|
||||
case mjGEOM_BOX:
|
||||
AddShape(renderable, model_objects, ModelObjects::kBox);
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kBox));
|
||||
transforms.push_back(trs.ToTransform());
|
||||
break;
|
||||
case mjGEOM_CAPSULE:
|
||||
AddShape(renderable, model_objects, ModelObjects::kTube);
|
||||
AddShape(renderable, model_objects, ModelObjects::kDome);
|
||||
AddShape(renderable, model_objects, ModelObjects::kDome);
|
||||
case mjGEOM_CAPSULE: {
|
||||
// Capsules are a tube with two domes at the ends.
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kTube));
|
||||
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;
|
||||
case mjGEOM_CYLINDER:
|
||||
AddShape(renderable, model_objects, ModelObjects::kTube);
|
||||
AddShape(renderable, model_objects, ModelObjects::kDisk);
|
||||
AddShape(renderable, model_objects, ModelObjects::kDisk);
|
||||
}
|
||||
case mjGEOM_CYLINDER: {
|
||||
// Cylinders are a tube with two disks at the ends.
|
||||
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;
|
||||
case mjGEOM_ARROW:
|
||||
AddShape(renderable, model_objects, ModelObjects::kTube);
|
||||
AddShape(renderable, model_objects, ModelObjects::kCone);
|
||||
AddShape(renderable, model_objects, ModelObjects::kDisk);
|
||||
}
|
||||
case mjGEOM_ARROW: {
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kTube));
|
||||
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;
|
||||
case mjGEOM_ARROW1:
|
||||
AddShape(renderable, model_objects, ModelObjects::kTube);
|
||||
AddShape(renderable, model_objects, ModelObjects::kCone);
|
||||
AddShape(renderable, model_objects, ModelObjects::kDisk);
|
||||
AddShape(renderable, model_objects, ModelObjects::kDisk);
|
||||
}
|
||||
case mjGEOM_ARROW1: {
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kTube));
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kCone));
|
||||
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;
|
||||
case mjGEOM_ARROW2:
|
||||
AddShape(renderable, model_objects, ModelObjects::kTube);
|
||||
AddShape(renderable, model_objects, ModelObjects::kCone);
|
||||
AddShape(renderable, model_objects, ModelObjects::kCone);
|
||||
AddShape(renderable, model_objects, ModelObjects::kDisk);
|
||||
AddShape(renderable, model_objects, ModelObjects::kDisk);
|
||||
}
|
||||
case mjGEOM_ARROW2: {
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kTube));
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kCone));
|
||||
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 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;
|
||||
}
|
||||
case mjGEOM_LINE:
|
||||
AddShape(renderable, model_objects, ModelObjects::kLine);
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kLine));
|
||||
transforms.push_back(trs.ToTransform());
|
||||
break;
|
||||
case mjGEOM_LINEBOX:
|
||||
AddShape(renderable, model_objects, ModelObjects::kLineBox);
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kLineBox));
|
||||
transforms.push_back(trs.ToTransform());
|
||||
break;
|
||||
case mjGEOM_TRIANGLE:
|
||||
AddShape(renderable, model_objects, ModelObjects::kTriangle);
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kTriangle));
|
||||
transforms.push_back(trs.ToTransform());
|
||||
break;
|
||||
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;
|
||||
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;
|
||||
case mjGEOM_NONE:
|
||||
case mjGEOM_LABEL:
|
||||
@@ -193,124 +314,8 @@ static void PrepareGeomMeshes(Renderable& renderable, const mjvGeom& geom,
|
||||
mju_warning("Unsupported geom type: %d", geom.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void SetGeomTransform(Renderable& renderable, const mjvGeom& geom) {
|
||||
// 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);
|
||||
}
|
||||
renderable.SetMeshes(meshes, transforms);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
renderable.SetWireframe(scene->flags[mjRND_WIREFRAME]);
|
||||
renderable.SetLayerMask(geom.category);
|
||||
if (geom.category == mjCAT_DECOR) {
|
||||
renderable.SetCastShadows(false);
|
||||
renderable.SetReceiveShadows(false);
|
||||
} else {
|
||||
renderable.SetWireframe(scene->flags[mjRND_WIREFRAME]);
|
||||
}
|
||||
|
||||
MaterialTextures textures;
|
||||
@@ -471,10 +478,7 @@ std::unique_ptr<Renderable> CreateGeomRenderable(
|
||||
config.shading_model = shading_model;
|
||||
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);
|
||||
SetGeomTransform(*renderable, geom);
|
||||
UpdateGeomMaterial(*renderable, geom, scene, model_objs, object_mgr, headpos);
|
||||
|
||||
return renderable;
|
||||
|
||||
@@ -277,13 +277,11 @@ void SceneView::Render(filament::Renderer* renderer,
|
||||
|
||||
// Render reflection passes.
|
||||
if (request.draw_mode == DrawMode::Color) {
|
||||
filament::TransformManager& tm = engine_->getTransformManager();
|
||||
for (size_t i = 0; i < reflectives_.size(); ++i) {
|
||||
Renderable* renderable = reflectives_[i];
|
||||
|
||||
// We assume the 0th entity is the reflective entity.
|
||||
const utils::Entity entity = (*renderable)[0];
|
||||
const mat4 transform(tm.getTransform(tm.getInstance(entity)));
|
||||
mat4 transform(renderable->GetTransform());
|
||||
SetupReflectionCamera(transform, camera_, reflect_camera_);
|
||||
|
||||
// Hide reflective surface from its own reflection pass.
|
||||
@@ -305,8 +303,7 @@ void SceneView::Render(filament::Renderer* renderer,
|
||||
|
||||
if (request.enable_ux) {
|
||||
ux_camera_->setProjection(filament::Camera::Projection::ORTHO, 0.0f,
|
||||
viewport.width / request.gui_scale,
|
||||
viewport.height / request.gui_scale, 0.0f, 0.0f,
|
||||
viewport.width, viewport.height, 0.0f, 0.0f,
|
||||
1.0f);
|
||||
ux_view_->setRenderTarget(render_target);
|
||||
renderer->render(ux_view_);
|
||||
|
||||
@@ -69,8 +69,6 @@ class SceneView {
|
||||
RenderTarget* target = nullptr;
|
||||
// Whether or not to render the UX as a separate pass.
|
||||
bool enable_ux = false;
|
||||
// The scale factor to use for UX rendering.
|
||||
float gui_scale = 1.0f;
|
||||
};
|
||||
|
||||
// Renders the scene.
|
||||
|
||||
Reference in New Issue
Block a user