Add function for creating Renderable meshes from mjtGeom types.
The Renderable class now internally knows which built-in meshes to use for a given geom type. It gets these meshes from the ObjectManager which now stores the collection of built-ins based on the nstack/nslice/nquad quality arguments. PiperOrigin-RevId: 911782530 Change-Id: I6698ec3964a5c657f6a9495809cc795eec2e346f
This commit is contained in:
committed by
Copybara-Service
parent
0c05215e18
commit
39c891e358
@@ -29,7 +29,6 @@
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "experimental/filament/filament/builtins.h"
|
||||
#include "experimental/filament/filament/math_util.h"
|
||||
#include "experimental/filament/filament/model_util.h"
|
||||
#include "experimental/filament/render_context_filament.h"
|
||||
@@ -493,19 +492,6 @@ void UpdateSkinFlexMeshData(mjrMeshData* data, const mjModel* model,
|
||||
|
||||
ModelObjects::ModelObjects(const mjModel* model, mjrfContext* ctx)
|
||||
: model_(model), ctx_(ctx) {
|
||||
const int nstack = model->vis.quality.numstacks;
|
||||
const int nslice = model->vis.quality.numslices;
|
||||
const int nquad = model->vis.quality.numquads;
|
||||
shapes_.insert({kLine, CreateLine(ctx_)});
|
||||
shapes_.insert({kBox, CreateBox(ctx_, nquad)});
|
||||
shapes_.insert({kLineBox, CreateLineBox(ctx_)});
|
||||
shapes_.insert({kCone, CreateCone(ctx_, nstack, nslice)});
|
||||
shapes_.insert({kDisk, CreateDisk(ctx_, nslice)});
|
||||
shapes_.insert({kDome, CreateDome(ctx_, nstack / 2, nslice)});
|
||||
shapes_.insert({kTube, CreateTube(ctx_, nstack, nslice)});
|
||||
shapes_.insert({kPlane, CreatePlane(ctx_, nquad)});
|
||||
shapes_.insert({kSphere, CreateSphere(ctx_, nstack, nslice)});
|
||||
shapes_.insert({kTriangle, CreateTriangle(ctx_)});
|
||||
|
||||
for (int i = 0; i < model_->ntex; ++i) {
|
||||
UploadTexture(model_, i);
|
||||
@@ -525,11 +511,6 @@ ModelObjects::ModelObjects(const mjModel* model, mjrfContext* ctx)
|
||||
model_, "filament.phong.emissive_multiplier", emissive_multiplier_);
|
||||
}
|
||||
|
||||
ModelObjects::~ModelObjects() {
|
||||
meshes_.clear();
|
||||
textures_.clear();
|
||||
}
|
||||
|
||||
void ModelObjects::UploadMesh(const mjModel* model, int id) {
|
||||
if (model != model_) {
|
||||
mju_error("Model mismatch.");
|
||||
@@ -641,11 +622,6 @@ const mjrMesh* ModelObjects::GetHeightFieldBuffer(int hfield_id) const {
|
||||
return it != height_fields_.end() ? it->second.get() : nullptr;
|
||||
}
|
||||
|
||||
const mjrMesh* ModelObjects::GetShapeBuffer(ShapeType shape) const {
|
||||
auto it = shapes_.find(shape);
|
||||
return it != shapes_.end() ? it->second.get() : nullptr;
|
||||
}
|
||||
|
||||
const mjrMesh* ModelObjects::GetFlexSkinGeomMesh(int geom_id) const {
|
||||
auto it = dynamic_meshes_.find(geom_id);
|
||||
return it != dynamic_meshes_.end() ? it->second.get() : nullptr;
|
||||
|
||||
@@ -28,21 +28,6 @@ namespace mujoco {
|
||||
class ModelObjects {
|
||||
public:
|
||||
ModelObjects(const mjModel* model, mjrfContext* ctx);
|
||||
~ModelObjects();
|
||||
|
||||
enum ShapeType {
|
||||
kLine,
|
||||
kLineBox,
|
||||
kPlane,
|
||||
kTriangle,
|
||||
kBox,
|
||||
kSphere,
|
||||
kCone,
|
||||
kDisk,
|
||||
kDome,
|
||||
kTube,
|
||||
kNumShapes,
|
||||
};
|
||||
|
||||
void UploadMesh(const mjModel* model, int id);
|
||||
|
||||
@@ -53,7 +38,6 @@ class ModelObjects {
|
||||
void CreateSkinFlexMesh(const mjvScene* scene, const mjvGeom& geom);
|
||||
|
||||
// Returns the cached instance of a filament object created from the mjModel.
|
||||
const mjrMesh* GetShapeBuffer(ShapeType shape) const;
|
||||
const mjrMesh* GetMeshBuffer(int data_id) const;
|
||||
const mjrMesh* GetHeightFieldBuffer(int hfield_id) const;
|
||||
const mjrMesh* GetFlexSkinGeomMesh(int geom_id) const;
|
||||
@@ -73,7 +57,6 @@ class ModelObjects {
|
||||
private:
|
||||
const mjModel* model_ = nullptr;
|
||||
mjrfContext* ctx_ = nullptr;
|
||||
std::unordered_map<ShapeType, UniquePtr<mjrMesh>> shapes_;
|
||||
std::unordered_map<int, UniquePtr<mjrMesh>> meshes_;
|
||||
std::unordered_map<int, UniquePtr<mjrMesh>> convex_hulls_;
|
||||
std::unordered_map<int, UniquePtr<mjrMesh>> height_fields_;
|
||||
|
||||
@@ -18,32 +18,15 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <numbers>
|
||||
#include <vector>
|
||||
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
#include <mujoco/mjvisualize.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "experimental/filament/compat/model_objects.h"
|
||||
#include "experimental/filament/filament/math_util.h"
|
||||
#include "experimental/filament/filament/renderable.h"
|
||||
#include "experimental/filament/render_context_filament.h"
|
||||
#include "experimental/filament/render_context_filament_cpp.h"
|
||||
|
||||
namespace mujoco {
|
||||
|
||||
using filament::math::float2;
|
||||
using filament::math::float3;
|
||||
using filament::math::float4;
|
||||
using filament::math::mat4f;
|
||||
|
||||
// An arbitrary scale factor for arrows.
|
||||
static constexpr float kArrowScale = 1.f / 6.f;
|
||||
static constexpr float kArrowHeadSize = 1.75f;
|
||||
|
||||
// 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.
|
||||
@@ -83,245 +66,103 @@ static const mjrMesh* GetHeightField(ModelObjects* model_objs, int hfield_id) {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static const mjrMesh* GetShape(ModelObjects* model_objs,
|
||||
ModelObjects::ShapeType shape_type) {
|
||||
const mjrMesh* mesh = model_objs->GetShapeBuffer(shape_type);
|
||||
if (mesh == nullptr) {
|
||||
mju_error("Unknown shape %d", shape_type);
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static void PrepareGeomMeshes(mjrRenderable* renderable, const mjvGeom& geom,
|
||||
const mjvScene* scene,
|
||||
ModelObjects* model_objects) {
|
||||
std::vector<const mjrMesh*> meshes;
|
||||
Renderable::GetTransformFn get_transforms;
|
||||
const mjModel* model = model_objects->GetModel();
|
||||
const int nstack = model->vis.quality.numstacks;
|
||||
const int nslice = model->vis.quality.numslices;
|
||||
const int nquad = model->vis.quality.numquads;
|
||||
|
||||
Trs trs = {
|
||||
.translation = ReadFloat3(geom.pos),
|
||||
.rotation = ReadMat3(geom.mat),
|
||||
.size = ReadFloat3(geom.size),
|
||||
};
|
||||
float position[3];
|
||||
std::memcpy(position, &geom.pos, 3 * sizeof(float));
|
||||
float rotation[9];
|
||||
std::memcpy(rotation, &geom.mat, 9 * sizeof(float));
|
||||
float size[3];
|
||||
std::memcpy(size, &geom.size, 3 * sizeof(float));
|
||||
|
||||
switch ((mjtGeom)geom.type) {
|
||||
case mjGEOM_MESH:
|
||||
meshes.push_back(GetMesh(model_objects, geom.dataid));
|
||||
mjrf_setRenderableMesh(renderable, GetMesh(model_objects, geom.dataid), 0, 0);
|
||||
// Ignore size for meshes.
|
||||
trs.size = float3{1.0f, 1.0f, 1.0f};
|
||||
size[0] = 1.f;
|
||||
size[1] = 1.f;
|
||||
size[2] = 1.f;
|
||||
break;
|
||||
case mjGEOM_HFIELD:
|
||||
meshes.push_back(GetHeightField(model_objects, geom.dataid));
|
||||
// Ignore size for height fields.
|
||||
trs.size = float3{1.0f, 1.0f, 1.0f};
|
||||
mjrf_setRenderableMesh(renderable, GetHeightField(model_objects, geom.dataid), 0, 0);
|
||||
// Ignore size for meshes.
|
||||
size[0] = 1.f;
|
||||
size[1] = 1.f;
|
||||
size[2] = 1.f;
|
||||
break;
|
||||
case mjGEOM_PLANE: {
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kPlane));
|
||||
const bool is_infinite = !(trs.size.x > 0 && trs.size.y > 0);
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
|
||||
const bool is_infinite = !(size[0] > 0 && size[1] > 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;
|
||||
size[0] = plane_scale;
|
||||
size[1] = plane_scale;
|
||||
}
|
||||
// Planes only define an xy size, so set the z-dimension to 1.0f.
|
||||
trs.size.z = 1.0f;
|
||||
size[2] = 1.0f;
|
||||
break;
|
||||
}
|
||||
case mjGEOM_SPHERE:
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kSphere));
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
break;
|
||||
case mjGEOM_ELLIPSOID:
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kSphere));
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
break;
|
||||
case mjGEOM_BOX:
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kBox));
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
break;
|
||||
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));
|
||||
|
||||
get_transforms = [](int index, const Trs& trs) {
|
||||
// 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);
|
||||
if (index == 0) {
|
||||
return trs.ToTransform();
|
||||
} else if (index == 1) {
|
||||
// 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});
|
||||
return top;
|
||||
} else if (index == 2) {
|
||||
// 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});
|
||||
return bottom;
|
||||
} else {
|
||||
mju_error("Invalid index for capsule geom: %d (expected [0,2])", index);
|
||||
return trs.ToTransform();
|
||||
}
|
||||
};
|
||||
case mjGEOM_CAPSULE:
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
break;
|
||||
}
|
||||
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));
|
||||
|
||||
get_transforms = [](int index, const Trs& trs) {
|
||||
if (index == 0) {
|
||||
return trs.ToTransform();
|
||||
} else if (index == 1) {
|
||||
// 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);
|
||||
return top;
|
||||
} else if (index == 2) {
|
||||
// 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);
|
||||
return bottom;
|
||||
} else {
|
||||
mju_error("Invalid index for cylinder geom: %d (expected [0,2])", index);
|
||||
return trs.ToTransform();
|
||||
}
|
||||
};
|
||||
case mjGEOM_CYLINDER:
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
break;
|
||||
}
|
||||
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));
|
||||
|
||||
get_transforms = [](int index, const Trs& trs) {
|
||||
mat4f base = mat4f(trs.rotation, trs.translation);
|
||||
base *= mat4f::scaling(float3{1, 1, kArrowScale});
|
||||
base *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
if (index == 0) {
|
||||
return base * mat4f::scaling(trs.size);
|
||||
} else if (index == 1) {
|
||||
mat4f top = base;
|
||||
top *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
top *= mat4f::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
|
||||
return top * mat4f::scaling(trs.size);
|
||||
} else if (index == 2) {
|
||||
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});
|
||||
return top_disk * mat4f::scaling(trs.size);
|
||||
} else if (index == 3) {
|
||||
mat4f bottom = base;
|
||||
bottom *= mat4f::translation(float3{0, 0, -trs.size.z});
|
||||
bottom *= mat4f::rotation(std::numbers::pi, float3{1, 0, 0});
|
||||
return bottom * mat4f::scaling(trs.size);
|
||||
} else {
|
||||
mju_error("Invalid index for arrow geom: %d (expected [0,3])", index);
|
||||
return trs.ToTransform();
|
||||
}
|
||||
};
|
||||
case mjGEOM_ARROW:
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
break;
|
||||
}
|
||||
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));
|
||||
|
||||
get_transforms = [](int index, const Trs& trs) {
|
||||
mat4f base = mat4f(trs.rotation, trs.translation);
|
||||
base *= mat4f::scaling(float3{1, 1, kArrowScale});
|
||||
base *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
if (index == 0) {
|
||||
return base * mat4f::scaling(trs.size);
|
||||
} else if (index == 1) {
|
||||
mat4f top = base;
|
||||
top *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
return top * mat4f::scaling(trs.size);
|
||||
} else if (index == 2) {
|
||||
mat4f bottom = base;
|
||||
bottom *= mat4f::translation(float3{0, 0, -trs.size.z});
|
||||
bottom *= mat4f::rotation(std::numbers::pi, float3{1, 0, 0});
|
||||
return bottom * mat4f::scaling(trs.size);
|
||||
} else {
|
||||
mju_error("Invalid index for arrow1 geom: %d (expected [0,2])", index);
|
||||
return trs.ToTransform();
|
||||
}
|
||||
};
|
||||
case mjGEOM_ARROW1:
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
break;
|
||||
}
|
||||
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));
|
||||
|
||||
get_transforms = [](int index, const Trs& trs) {
|
||||
mat4f base = mat4f(trs.rotation, trs.translation);
|
||||
base *= mat4f::scaling(float3{1, 1, kArrowScale});
|
||||
base *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
if (index == 0) {
|
||||
return base * mat4f::scaling(trs.size);
|
||||
} else if (index == 1) {
|
||||
mat4f top = base;
|
||||
top *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
top *= mat4f::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
|
||||
return top * mat4f::scaling(trs.size);
|
||||
} else if (index == 2) {
|
||||
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});
|
||||
return bottom * mat4f::scaling(trs.size);
|
||||
} else if (index == 3) {
|
||||
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});
|
||||
return top_disk * mat4f::scaling(trs.size);
|
||||
} else if (index == 4) {
|
||||
mat4f bottom_disk = base;
|
||||
bottom_disk *= mat4f::translation(float3{0, 0, -trs.size.z});
|
||||
return bottom_disk * mat4f::scaling(trs.size);
|
||||
} else {
|
||||
mju_error("Invalid index for arrow2 geom: %d (expected [0,4])", index);
|
||||
return trs.ToTransform();
|
||||
}
|
||||
};
|
||||
case mjGEOM_ARROW2:
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
break;
|
||||
}
|
||||
case mjGEOM_LINE:
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kLine));
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
break;
|
||||
case mjGEOM_LINEBOX:
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kLineBox));
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
break;
|
||||
case mjGEOM_TRIANGLE:
|
||||
meshes.push_back(GetShape(model_objects, ModelObjects::kTriangle));
|
||||
mjrf_setRenderableGeomMesh(renderable, (mjtGeom)geom.type, nstack, nslice, nquad);
|
||||
break;
|
||||
case mjGEOM_FLEX:
|
||||
meshes.push_back(GetSkinFlexMesh(model_objects, geom.objid));
|
||||
mjrf_setRenderableMesh(renderable, GetSkinFlexMesh(model_objects, geom.objid), 0, 0);
|
||||
// Flexes are defined in global space.
|
||||
trs = Trs();
|
||||
std::memset(position, 0, sizeof(position));
|
||||
std::memset(rotation, 0, sizeof(rotation));
|
||||
rotation[0] = 1.f;
|
||||
rotation[4] = 1.f;
|
||||
rotation[8] = 1.f;
|
||||
std::memset(size, 0, sizeof(size));
|
||||
break;
|
||||
case mjGEOM_SKIN:
|
||||
meshes.push_back(GetSkinFlexMesh(model_objects, geom.objid));
|
||||
mjrf_setRenderableMesh(renderable, GetSkinFlexMesh(model_objects, geom.objid), 0, 0);
|
||||
// Skins are defined in global space.
|
||||
trs = Trs();
|
||||
std::memset(position, 0, sizeof(position));
|
||||
std::memset(rotation, 0, sizeof(rotation));
|
||||
rotation[0] = 1.f;
|
||||
rotation[4] = 1.f;
|
||||
rotation[8] = 1.f;
|
||||
std::memset(size, 0, sizeof(size));
|
||||
break;
|
||||
case mjGEOM_NONE:
|
||||
case mjGEOM_LABEL:
|
||||
@@ -333,14 +174,6 @@ static void PrepareGeomMeshes(mjrRenderable* renderable, const mjvGeom& geom,
|
||||
break;
|
||||
}
|
||||
|
||||
Renderable::downcast(renderable)->SetMeshes(meshes, get_transforms);
|
||||
|
||||
float position[3];
|
||||
std::memcpy(position, &trs.translation[0], 3 * sizeof(float));
|
||||
float rotation[9];
|
||||
std::memcpy(rotation, &trs.rotation[0], 9 * sizeof(float));
|
||||
float size[3];
|
||||
std::memcpy(size, &trs.size[0], 3 * sizeof(float));
|
||||
mjrf_setRenderableTransform(renderable, position, rotation, size);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,16 +19,16 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <numbers>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
#include "experimental/filament/filament/filament_context.h"
|
||||
#include "experimental/filament/filament/math_util.h"
|
||||
#include "experimental/filament/filament/mesh.h"
|
||||
#include "experimental/filament/render_context_filament.h"
|
||||
#include "experimental/filament/render_context_filament_cpp.h"
|
||||
|
||||
namespace mujoco {
|
||||
|
||||
@@ -65,14 +65,15 @@ class BuiltinBuilder : public mjrMeshData {
|
||||
virtual ~BuiltinBuilder() = default;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
static UniquePtr<mjrMesh> Create(mjrfContext* ctx, Args&&... args) {
|
||||
static std::unique_ptr<Mesh> Create(filament::Engine* engine,
|
||||
Args&&... args) {
|
||||
auto builder = new T(std::forward<Args>(args)...);
|
||||
mjrMeshData* mesh_data = builder->PrepareMeshData();
|
||||
mesh_data->release_callback = +[](void* user_data) {
|
||||
delete static_cast<BuiltinBuilder*>(user_data);
|
||||
};
|
||||
mesh_data->user_data = builder;
|
||||
return CreateMesh(ctx, *mesh_data);
|
||||
return std::make_unique<Mesh>(engine, *mesh_data);
|
||||
}
|
||||
|
||||
mjrMeshData* PrepareMeshData() {
|
||||
@@ -617,44 +618,28 @@ class DomeBuilder : public BuiltinBuilder {
|
||||
}
|
||||
};
|
||||
|
||||
UniquePtr<mjrMesh> CreateLine(mjrfContext* ctx) {
|
||||
return BuiltinBuilder::Create<LineBuilder>(ctx);
|
||||
Builtins::Builtins(filament::Engine* engine, int nstack, int nslice, int nquad) {
|
||||
line_ = BuiltinBuilder::Create<LineBuilder>(engine);
|
||||
plane_ = BuiltinBuilder::Create<PlaneBuilder>(engine, nquad);
|
||||
triangle_ = BuiltinBuilder::Create<TriangleBuilder>(engine);
|
||||
box_ = BuiltinBuilder::Create<BoxBuilder>(engine, nquad);
|
||||
line_box_ = BuiltinBuilder::Create<LineBoxBuilder>(engine);
|
||||
sphere_ = BuiltinBuilder::Create<SphereBuilder>(engine, nstack, nslice);
|
||||
tube_ = BuiltinBuilder::Create<TubeBuilder>(engine, nstack, nslice);
|
||||
disk_ = BuiltinBuilder::Create<DiskBuilder>(engine, nslice);
|
||||
dome_ = BuiltinBuilder::Create<DomeBuilder>(engine, nstack, nslice);
|
||||
cone_ = BuiltinBuilder::Create<ConeBuilder>(engine, nstack, nslice);
|
||||
}
|
||||
|
||||
UniquePtr<mjrMesh> CreatePlane(mjrfContext* ctx, int nquad) {
|
||||
return BuiltinBuilder::Create<PlaneBuilder>(ctx, nquad);
|
||||
}
|
||||
|
||||
UniquePtr<mjrMesh> CreateTriangle(mjrfContext* ctx) {
|
||||
return BuiltinBuilder::Create<TriangleBuilder>(ctx);
|
||||
}
|
||||
|
||||
UniquePtr<mjrMesh> CreateBox(mjrfContext* ctx, int nquad) {
|
||||
return BuiltinBuilder::Create<BoxBuilder>(ctx, nquad);
|
||||
}
|
||||
|
||||
UniquePtr<mjrMesh> CreateLineBox(mjrfContext* ctx) {
|
||||
return BuiltinBuilder::Create<LineBoxBuilder>(ctx);
|
||||
}
|
||||
|
||||
UniquePtr<mjrMesh> CreateSphere(mjrfContext* ctx, int nstack, int nslice) {
|
||||
return BuiltinBuilder::Create<SphereBuilder>(ctx, nstack, nslice);
|
||||
}
|
||||
|
||||
UniquePtr<mjrMesh> CreateTube(mjrfContext* ctx, int nstack, int nslice) {
|
||||
return BuiltinBuilder::Create<TubeBuilder>(ctx, nstack, nslice);
|
||||
}
|
||||
|
||||
UniquePtr<mjrMesh> CreateDisk(mjrfContext* ctx, int nslice) {
|
||||
return BuiltinBuilder::Create<DiskBuilder>(ctx, nslice);
|
||||
}
|
||||
|
||||
UniquePtr<mjrMesh> CreateDome(mjrfContext* ctx, int nstack, int nslice) {
|
||||
return BuiltinBuilder::Create<DomeBuilder>(ctx, nstack, nslice);
|
||||
}
|
||||
|
||||
UniquePtr<mjrMesh> CreateCone(mjrfContext* ctx, int nstack, int nslice) {
|
||||
return BuiltinBuilder::Create<ConeBuilder>(ctx, nstack, nslice);
|
||||
}
|
||||
const Mesh* Builtins::Line() { return line_.get(); }
|
||||
const Mesh* Builtins::LineBox() { return line_box_.get(); }
|
||||
const Mesh* Builtins::Plane() { return plane_.get(); }
|
||||
const Mesh* Builtins::Triangle() { return triangle_.get(); }
|
||||
const Mesh* Builtins::Box() { return box_.get(); }
|
||||
const Mesh* Builtins::Sphere() { return sphere_.get(); }
|
||||
const Mesh* Builtins::Cone() { return cone_.get(); }
|
||||
const Mesh* Builtins::Disk() { return disk_.get(); }
|
||||
const Mesh* Builtins::Dome() { return dome_.get(); }
|
||||
const Mesh* Builtins::Tube() { return tube_.get(); }
|
||||
|
||||
} // namespace mujoco
|
||||
|
||||
@@ -15,22 +15,41 @@
|
||||
#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_BUILTINS_H_
|
||||
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_BUILTINS_H_
|
||||
|
||||
#include "experimental/filament/render_context_filament.h"
|
||||
#include "experimental/filament/render_context_filament_cpp.h"
|
||||
#include <memory>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include "experimental/filament/filament/mesh.h"
|
||||
|
||||
// Generates buffers for built-in shapes.
|
||||
namespace mujoco {
|
||||
|
||||
UniquePtr<mjrMesh> CreateLine(mjrfContext* ctx);
|
||||
UniquePtr<mjrMesh> CreatePlane(mjrfContext* ctx, int nquad);
|
||||
UniquePtr<mjrMesh> CreateTriangle(mjrfContext* ctx);
|
||||
UniquePtr<mjrMesh> CreateBox(mjrfContext* ctx, int nquad);
|
||||
UniquePtr<mjrMesh> CreateLineBox(mjrfContext* ctx);
|
||||
UniquePtr<mjrMesh> CreateSphere(mjrfContext* ctx, int nstack, int nslice);
|
||||
UniquePtr<mjrMesh> CreateTube(mjrfContext* ctx, int nstack, int nslice);
|
||||
UniquePtr<mjrMesh> CreateDisk(mjrfContext* ctx, int nslice);
|
||||
UniquePtr<mjrMesh> CreateDome(mjrfContext* ctx, int nstack, int nslice);
|
||||
UniquePtr<mjrMesh> CreateCone(mjrfContext* ctx, int nstack, int nslice);
|
||||
// A collection of meshes that "built in" to the renderer.
|
||||
class Builtins {
|
||||
public:
|
||||
Builtins(filament::Engine* engine, int nstack, int nslice, int nquad);
|
||||
|
||||
const Mesh* Line();
|
||||
const Mesh* LineBox();
|
||||
const Mesh* Plane();
|
||||
const Mesh* Triangle();
|
||||
const Mesh* Box();
|
||||
const Mesh* Sphere();
|
||||
const Mesh* Cone();
|
||||
const Mesh* Disk();
|
||||
const Mesh* Dome();
|
||||
const Mesh* Tube();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Mesh> line_;
|
||||
std::unique_ptr<Mesh> line_box_;
|
||||
std::unique_ptr<Mesh> plane_;
|
||||
std::unique_ptr<Mesh> triangle_;
|
||||
std::unique_ptr<Mesh> box_;
|
||||
std::unique_ptr<Mesh> sphere_;
|
||||
std::unique_ptr<Mesh> cone_;
|
||||
std::unique_ptr<Mesh> disk_;
|
||||
std::unique_ptr<Mesh> dome_;
|
||||
std::unique_ptr<Mesh> tube_;
|
||||
};
|
||||
|
||||
} // namespace mujoco
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndirectLight.h>
|
||||
@@ -27,6 +28,7 @@
|
||||
#include <filament/Skybox.h>
|
||||
#include <filament/Texture.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "experimental/filament/filament/builtins.h"
|
||||
#include "user/user_resource.h"
|
||||
|
||||
namespace mujoco {
|
||||
@@ -131,6 +133,22 @@ filament::Material* ObjectManager::GetMaterial(MaterialType type) const {
|
||||
return materials_[type];
|
||||
}
|
||||
|
||||
Builtins* ObjectManager::GetBuiltins(int nstack, int nslice, int nquad) {
|
||||
// Assumes nstack, nslice, and nquad are non-negative and less than 2^20.
|
||||
std::uint64_t key = (static_cast<uint64_t>(nstack) << 20) |
|
||||
(static_cast<uint64_t>(nslice) << 40) |
|
||||
static_cast<uint64_t>(nquad);
|
||||
|
||||
auto iter = builtins_.find(key);
|
||||
if (iter == builtins_.end()) {
|
||||
auto builtins = std::make_unique<Builtins>(engine_, nstack, nslice, nquad);
|
||||
Builtins* ptr = builtins.get();
|
||||
builtins_[key] = std::move(builtins);
|
||||
return ptr;
|
||||
}
|
||||
return iter->second.get();
|
||||
}
|
||||
|
||||
const filament::Texture* ObjectManager::GetFallbackTexture(
|
||||
mjtTextureRole role) const {
|
||||
if (role < 0 || role >= mjNTEXROLE) {
|
||||
|
||||
@@ -17,15 +17,18 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndirectLight.h>
|
||||
#include <filament/Skybox.h>
|
||||
#include <filament/Texture.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "experimental/filament/filament/builtins.h"
|
||||
|
||||
namespace mujoco {
|
||||
|
||||
@@ -84,6 +87,9 @@ class ObjectManager {
|
||||
// Returns the fallback Texture with the given role.
|
||||
const filament::Texture* GetFallbackTexture(mjtTextureRole role) const;
|
||||
|
||||
// Returns the built-in mesh collection with the given parameters.
|
||||
Builtins* GetBuiltins(int nstack, int nslice, int nquad);
|
||||
|
||||
// Loads the given asset from the filament resource directory.
|
||||
std::unique_ptr<Asset> LoadAsset(std::string_view filename);
|
||||
|
||||
@@ -97,6 +103,7 @@ class ObjectManager {
|
||||
filament::Engine* engine_ = nullptr;
|
||||
std::array<filament::Material*, kNumMaterials> materials_;
|
||||
std::array<filament::Texture*, mjNTEXROLE> fallback_textures_;
|
||||
std::unordered_map<std::uint64_t, std::unique_ptr<Builtins>> builtins_;
|
||||
filament::Texture* fallback_white_ = nullptr;
|
||||
filament::Texture* fallback_black_ = nullptr;
|
||||
filament::Texture* fallback_normal_ = nullptr;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <numbers>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Material.h>
|
||||
@@ -24,8 +24,12 @@
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
#include <utils/EntityManager.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "experimental/filament/filament/builtins.h"
|
||||
#include "experimental/filament/filament/filament_context.h"
|
||||
#include "experimental/filament/filament/material.h"
|
||||
#include "experimental/filament/filament/math_util.h"
|
||||
@@ -36,8 +40,15 @@
|
||||
|
||||
namespace mujoco {
|
||||
|
||||
using filament::math::float2;
|
||||
using filament::math::float3;
|
||||
using filament::math::float4;
|
||||
using filament::math::mat4f;
|
||||
|
||||
// An arbitrary scale factor for arrows.
|
||||
static constexpr float kArrowScale = 1.f / 6.f;
|
||||
static constexpr float kArrowHeadSize = 1.75f;
|
||||
|
||||
Renderable::Renderable(FilamentContext* ctx, const mjrRenderableParams& params)
|
||||
: object_mgr_(ctx->GetObjectManager()), params_(params) {
|
||||
mjr_defaultMaterialParams(&material_params_);
|
||||
@@ -159,20 +170,12 @@ const mat4f& Renderable::GetTransform() const {
|
||||
return transform_;
|
||||
}
|
||||
|
||||
void Renderable::SetMeshes(std::span<const mjrMesh*> meshes,
|
||||
GetTransformFn get_transform_fn) {
|
||||
if (!parts_.empty()) {
|
||||
mju_error("Cannot set meshes for renderable with multiple parts.");
|
||||
}
|
||||
|
||||
get_transform_fn_ = get_transform_fn;
|
||||
for (int i = 0; i < meshes.size(); ++i) {
|
||||
Part& part = parts_.emplace_back();
|
||||
part.mesh = Mesh::downcast(meshes[i]);
|
||||
part.elem_offset = 0;
|
||||
part.elem_count = part.mesh->GetFilamentIndexBuffer()->getIndexCount();
|
||||
InitPartEntity(part);
|
||||
}
|
||||
void Renderable::AppendMesh(const Mesh* mesh) {
|
||||
Part& part = parts_.emplace_back();
|
||||
part.mesh = Mesh::downcast(mesh);
|
||||
part.elem_offset = 0;
|
||||
part.elem_count = part.mesh->GetFilamentIndexBuffer()->getIndexCount();
|
||||
InitPartEntity(part);
|
||||
}
|
||||
|
||||
void Renderable::AddToScene(filament::Scene* scene) {
|
||||
@@ -410,6 +413,198 @@ ObjectManager::MaterialType Renderable::GetColorMaterialType() const {
|
||||
}
|
||||
}
|
||||
|
||||
void Renderable::SetGeomMesh(mjtGeom type, int nstack, int nslice, int nquad) {
|
||||
Builtins* builtins = object_mgr_->GetBuiltins(nstack, nslice, nquad);
|
||||
|
||||
switch (type) {
|
||||
case mjGEOM_PLANE:
|
||||
AppendMesh(builtins->Plane());
|
||||
break;
|
||||
case mjGEOM_SPHERE:
|
||||
AppendMesh(builtins->Sphere());
|
||||
break;
|
||||
case mjGEOM_ELLIPSOID:
|
||||
AppendMesh(builtins->Sphere());
|
||||
break;
|
||||
case mjGEOM_BOX:
|
||||
AppendMesh(builtins->Box());
|
||||
break;
|
||||
case mjGEOM_CAPSULE:
|
||||
// Capsules are a tube with two domes at the ends.
|
||||
AppendMesh(builtins->Tube());
|
||||
AppendMesh(builtins->Dome());
|
||||
AppendMesh(builtins->Dome());
|
||||
|
||||
get_transform_fn_ = [](int index, const Trs& trs) {
|
||||
// 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);
|
||||
if (index == 0) {
|
||||
return trs.ToTransform();
|
||||
} else if (index == 1) {
|
||||
// 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});
|
||||
return top;
|
||||
} else if (index == 2) {
|
||||
// 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});
|
||||
return bottom;
|
||||
} else {
|
||||
mju_error("Invalid index for capsule geom: %d (expected [0,2])", index);
|
||||
return trs.ToTransform();
|
||||
}
|
||||
};
|
||||
break;
|
||||
case mjGEOM_CYLINDER:
|
||||
// Cylinders are a tube with two disks at the ends.
|
||||
AppendMesh(builtins->Tube());
|
||||
AppendMesh(builtins->Disk());
|
||||
AppendMesh(builtins->Disk());
|
||||
|
||||
get_transform_fn_ = [](int index, const Trs& trs) {
|
||||
if (index == 0) {
|
||||
return trs.ToTransform();
|
||||
} else if (index == 1) {
|
||||
// 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);
|
||||
return top;
|
||||
} else if (index == 2) {
|
||||
// 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);
|
||||
return bottom;
|
||||
} else {
|
||||
mju_error("Invalid index for cylinder geom: %d (expected [0,2])", index);
|
||||
return trs.ToTransform();
|
||||
}
|
||||
};
|
||||
break;
|
||||
case mjGEOM_ARROW:
|
||||
AppendMesh(builtins->Tube());
|
||||
AppendMesh(builtins->Cone());
|
||||
AppendMesh(builtins->Disk());
|
||||
AppendMesh(builtins->Disk());
|
||||
|
||||
get_transform_fn_ = [](int index, const Trs& trs) {
|
||||
mat4f base = mat4f(trs.rotation, trs.translation);
|
||||
base *= mat4f::scaling(float3{1, 1, kArrowScale});
|
||||
base *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
if (index == 0) {
|
||||
return base * mat4f::scaling(trs.size);
|
||||
} else if (index == 1) {
|
||||
mat4f top = base;
|
||||
top *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
top *= mat4f::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
|
||||
return top * mat4f::scaling(trs.size);
|
||||
} else if (index == 2) {
|
||||
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});
|
||||
return top_disk * mat4f::scaling(trs.size);
|
||||
} else if (index == 3) {
|
||||
mat4f bottom = base;
|
||||
bottom *= mat4f::translation(float3{0, 0, -trs.size.z});
|
||||
bottom *= mat4f::rotation(std::numbers::pi, float3{1, 0, 0});
|
||||
return bottom * mat4f::scaling(trs.size);
|
||||
} else {
|
||||
mju_error("Invalid index for arrow geom: %d (expected [0,3])", index);
|
||||
return trs.ToTransform();
|
||||
}
|
||||
};
|
||||
break;
|
||||
case mjGEOM_ARROW1:
|
||||
AppendMesh(builtins->Tube());
|
||||
AppendMesh(builtins->Cone());
|
||||
AppendMesh(builtins->Disk());
|
||||
|
||||
get_transform_fn_ = [](int index, const Trs& trs) {
|
||||
mat4f base = mat4f(trs.rotation, trs.translation);
|
||||
base *= mat4f::scaling(float3{1, 1, kArrowScale});
|
||||
base *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
if (index == 0) {
|
||||
return base * mat4f::scaling(trs.size);
|
||||
} else if (index == 1) {
|
||||
mat4f top = base;
|
||||
top *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
return top * mat4f::scaling(trs.size);
|
||||
} else if (index == 2) {
|
||||
mat4f bottom = base;
|
||||
bottom *= mat4f::translation(float3{0, 0, -trs.size.z});
|
||||
bottom *= mat4f::rotation(std::numbers::pi, float3{1, 0, 0});
|
||||
return bottom * mat4f::scaling(trs.size);
|
||||
} else {
|
||||
mju_error("Invalid index for arrow1 geom: %d (expected [0,2])", index);
|
||||
return trs.ToTransform();
|
||||
}
|
||||
};
|
||||
break;
|
||||
case mjGEOM_ARROW2:
|
||||
AppendMesh(builtins->Tube());
|
||||
AppendMesh(builtins->Cone());
|
||||
AppendMesh(builtins->Cone());
|
||||
AppendMesh(builtins->Disk());
|
||||
AppendMesh(builtins->Disk());
|
||||
|
||||
get_transform_fn_ = [](int index, const Trs& trs) {
|
||||
mat4f base = mat4f(trs.rotation, trs.translation);
|
||||
base *= mat4f::scaling(float3{1, 1, kArrowScale});
|
||||
base *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
if (index == 0) {
|
||||
return base * mat4f::scaling(trs.size);
|
||||
} else if (index == 1) {
|
||||
mat4f top = base;
|
||||
top *= mat4f::translation(float3{0, 0, trs.size.z});
|
||||
top *= mat4f::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f});
|
||||
return top * mat4f::scaling(trs.size);
|
||||
} else if (index == 2) {
|
||||
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});
|
||||
return bottom * mat4f::scaling(trs.size);
|
||||
} else if (index == 3) {
|
||||
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});
|
||||
return top_disk * mat4f::scaling(trs.size);
|
||||
} else if (index == 4) {
|
||||
mat4f bottom_disk = base;
|
||||
bottom_disk *= mat4f::translation(float3{0, 0, -trs.size.z});
|
||||
return bottom_disk * mat4f::scaling(trs.size);
|
||||
} else {
|
||||
mju_error("Invalid index for arrow2 geom: %d (expected [0,4])", index);
|
||||
return trs.ToTransform();
|
||||
}
|
||||
};
|
||||
break;
|
||||
case mjGEOM_LINE:
|
||||
AppendMesh(builtins->Line());
|
||||
break;
|
||||
case mjGEOM_LINEBOX:
|
||||
AppendMesh(builtins->LineBox());
|
||||
break;
|
||||
case mjGEOM_TRIANGLE:
|
||||
AppendMesh(builtins->Triangle());
|
||||
break;
|
||||
default:
|
||||
mju_error("Unsupported geom type: %d", type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
filament::Engine* Renderable::GetEngine() { return object_mgr_->GetEngine(); }
|
||||
|
||||
} // namespace mujoco
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <math/mat4.h>
|
||||
#include <utils/Entity.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "experimental/filament/filament/filament_context.h"
|
||||
#include "experimental/filament/filament/math_util.h"
|
||||
#include "experimental/filament/filament/mesh.h"
|
||||
@@ -59,20 +59,15 @@ class Renderable : public mjrRenderable {
|
||||
// assumes the entire mesh should be appended.
|
||||
void SetMesh(const Mesh* mesh, int elem_offset = 0, int elem_count = 0);
|
||||
|
||||
// Sets the mesh of the renderable based on the given geom type.
|
||||
void SetGeomMesh(mjtGeom type, int nstack, int nslice, int nquad);
|
||||
|
||||
// Sets the transform of the renderable.
|
||||
void SetTransform(const Trs& trs);
|
||||
|
||||
// Returns the current transform of the renderable.
|
||||
const filament::math::mat4f& GetTransform() const;
|
||||
|
||||
// Sets multiple meshes for a renderable. Users can optionally provide a
|
||||
// function that will be used to compute the transform for each (sub)mesh
|
||||
// relative to the transform of the renderable itself. This allows users to
|
||||
// construct compound (but rigid) objects from multiple meshes.
|
||||
using GetTransformFn = std::function<filament::math::mat4f(int, const Trs&)>;
|
||||
void SetMeshes(std::span<const mjrMesh*> meshes,
|
||||
GetTransformFn get_transform = nullptr);
|
||||
|
||||
// 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
|
||||
// layer mask.
|
||||
@@ -128,6 +123,8 @@ class Renderable : public mjrRenderable {
|
||||
}
|
||||
|
||||
private:
|
||||
using GetTransformFn = std::function<filament::math::mat4f(int, const Trs&)>;
|
||||
|
||||
struct Part {
|
||||
utils::Entity entity;
|
||||
const Mesh* mesh = nullptr;
|
||||
@@ -135,6 +132,8 @@ class Renderable : public mjrRenderable {
|
||||
int elem_count = 0;
|
||||
};
|
||||
|
||||
void AppendMesh(const Mesh* mesh);
|
||||
|
||||
void InitPartEntity(Part& part);
|
||||
|
||||
void AssignMaterial(mjrDrawMode mode, ObjectManager::MaterialType material_type);
|
||||
|
||||
@@ -254,6 +254,12 @@ void mjrf_setRenderableMesh(mjrRenderable* renderable, const mjrMesh* mesh,
|
||||
->SetMesh(mujoco::Mesh::downcast(mesh), elem_offset, elem_count);
|
||||
}
|
||||
|
||||
void mjrf_setRenderableGeomMesh(mjrRenderable* renderable, mjtGeom type,
|
||||
int nstack, int nslice, int nquad) {
|
||||
mujoco::Renderable::downcast(renderable)->SetGeomMesh(type, nstack, nslice,
|
||||
nquad);
|
||||
}
|
||||
|
||||
void mjrf_setRenderableMaterial(mjrRenderable* renderable,
|
||||
const mjrMaterialParams* params,
|
||||
const mjrMaterialTextures* textures) {
|
||||
@@ -265,9 +271,9 @@ void mjrf_setRenderableTransform(mjrRenderable* renderable,
|
||||
const float rotation[9], const float size[3]) {
|
||||
const filament::math::float3 fposition{position[0], position[1], position[2]};
|
||||
const filament::math::float3 fsize{size[0], size[1], size[2]};
|
||||
const filament::math::mat3f frotation{rotation[0], rotation[1], rotation[2],
|
||||
rotation[3], rotation[4], rotation[5],
|
||||
rotation[6], rotation[7], rotation[8]};
|
||||
const filament::math::mat3f frotation{rotation[0], rotation[3], rotation[6],
|
||||
rotation[1], rotation[4], rotation[7],
|
||||
rotation[2], rotation[5], rotation[8]};
|
||||
mujoco::Renderable::downcast(renderable)
|
||||
->SetTransform({fposition, frotation, fsize});
|
||||
}
|
||||
|
||||
@@ -500,6 +500,12 @@ mjrLightType mjrf_getLightType(const mjrLight* light);
|
||||
void mjrf_setRenderableMesh(mjrRenderable* renderable, const mjrMesh* mesh,
|
||||
int elem_offset, int elem_count);
|
||||
|
||||
// Sets the mesh of the renderable to a built-in mesh based on the geom type.
|
||||
// Note: using the same parameters (nstack, nslice, nquad) will have better
|
||||
// performance as the internal mesh data can be shared across renderables.
|
||||
void mjrf_setRenderableGeomMesh(mjrRenderable* renderable, mjtGeom type,
|
||||
int nstack, int nslice, int nquad);
|
||||
|
||||
// Sets the material properties and textures of the renderable.
|
||||
void mjrf_setRenderableMaterial(mjrRenderable* renderable,
|
||||
const mjrMaterialParams* params,
|
||||
|
||||
Reference in New Issue
Block a user