Improve flex rendering.

RenderableManager is responsible for rendering the "skinned" flexes
which represent the correct "final" look of a flex. This now includes
1D flexes which are rendered as a sequence of capsules (similar to
tendons).

SceneDecorator is responsible for rendering the debug (unskinned)
versions of flexes. This includes the vertices (small spheres for each
vertex), edges (thin cynlinders for each edge), and faces (triangles
for each face, including sublayers).

PiperOrigin-RevId: 951595046
Change-Id: Iebc6fd7a747a602813b6457333bd9fa5915e28d3
This commit is contained in:
Haroon Qureshi
2026-07-21 11:14:47 -07:00
committed by Copybara-Service
parent 7bfa10e1e2
commit a8545ac7cc
6 changed files with 310 additions and 232 deletions
@@ -129,6 +129,7 @@ void SceneDecorator::Update(mjData* data, const mjvOption* vis_option,
mjrf_removeRenderableFromScene(scene_, iter.get());
}
decorations_.clear();
meshes_.clear();
const mat4 clip_from_world =
CalcClipFromWorld(model, data, *camera, viewport);
@@ -143,12 +144,133 @@ void SceneDecorator::Update(mjData* data, const mjvOption* vis_option,
draw_text_at_fn(geom.label, pos.x, pos.y, pos.z);
}
}
if (geom.category != mjCAT_DECOR) {
const mjtGeom geom_type = (mjtGeom)geom.type;
if (geom_type == mjGEOM_NONE || geom_type == mjGEOM_LABEL) {
continue;
}
if (geom.category != mjCAT_DECOR && geom.type != mjGEOM_FLEX) {
continue;
}
const mjtGeom geom_type = (mjtGeom)geom.type;
if (geom_type == mjGEOM_NONE || geom_type == mjGEOM_LABEL) {
if (geom.type == mjGEOM_FLEX) {
// Draw flexes "normally" with skins; no decor/debug rendering needed.
if (mjv_scene_.flexskinopt) {
continue;
}
mjrfMaterial material;
mjrf_defaultMaterial(&material);
material.decor_ux = true;
material.color[0] = model->flex_rgba[4 * geom.objid + 0];
material.color[1] = model->flex_rgba[4 * geom.objid + 1];
material.color[2] = model->flex_rgba[4 * geom.objid + 2];
material.color[3] = model->flex_rgba[4 * geom.objid + 3];
mjrfRenderableParams params;
mjrf_defaultRenderableParams(&params);
const int vertadr = mjv_scene_.flexvertadr[geom.objid];
const int vertnum = mjv_scene_.flexvertnum[geom.objid];
const float radius = model->flex_radius[geom.objid];
if (mjv_scene_.flexvertopt) {
// Use small spheres to represent vertices.
const float rot[] = {1.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f};
const float size[] = {radius, radius, radius};
for (int v = vertadr; v < vertadr + vertnum; ++v) {
auto vertex = CreateRenderable(ctx, params);
mjrf_setRenderableGeomMesh(vertex.get(), mjGEOM_SPHERE, 8, 8, 1);
mjrf_setRenderableMaterial(vertex.get(), &material);
mjrf_setRenderableSize(vertex.get(), size);
mjrf_setRenderableTransform(vertex.get(), mjv_scene_.flexvert + 3*v, rot);
mjrf_addRenderableToScene(scene_, vertex.get());
decorations_.push_back(std::move(vertex));
}
}
if (mjv_scene_.flexedgeopt) {
const int edgeadr = mjv_scene_.flexedgeadr[geom.objid];
const int edgenum = mjv_scene_.flexedgenum[geom.objid];
// Use small thin cylinders to represent the edges.
for (int e = edgeadr; e < edgeadr + edgenum; ++e) {
const float* v1 = mjv_scene_.flexvert + 3 * (vertadr + mjv_scene_.flexedge[2*e]);
const float* v2 = mjv_scene_.flexvert + 3 * (vertadr + mjv_scene_.flexedge[2*e+1]);
const mjtNum vec[3] = {v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2]};
const float pos[3]{
(v1[0] + v2[0]) * 0.5f,
(v1[1] + v2[1]) * 0.5f,
(v1[2] + v2[2]) * 0.5f,
};
mjtNum quat[4];
mju_quatZ2Vec(quat, vec);
mjtNum edgemat[9];
mju_quat2Mat(edgemat, quat);
const float rot[9] = {
static_cast<float>(edgemat[0]),
static_cast<float>(edgemat[1]),
static_cast<float>(edgemat[2]),
static_cast<float>(edgemat[3]),
static_cast<float>(edgemat[4]),
static_cast<float>(edgemat[5]),
static_cast<float>(edgemat[6]),
static_cast<float>(edgemat[7]),
static_cast<float>(edgemat[8]),
};
const float len = static_cast<float>(mju_norm3(vec));
const float size[3] = {radius, radius, len * 0.5f};
auto vertex = CreateRenderable(ctx, params);
mjrf_setRenderableGeomMesh(vertex.get(), mjGEOM_CYLINDER, 1, 8, 1);
mjrf_setRenderableMaterial(vertex.get(), &material);
mjrf_setRenderableSize(vertex.get(), size);
mjrf_setRenderableTransform(vertex.get(), pos, rot);
mjrf_addRenderableToScene(scene_, vertex.get());
decorations_.push_back(std::move(vertex));
}
}
if (mjv_scene_.flexfaceopt && mjv_scene_.flexfaceused[geom.objid]) {
const bool has_uvs = geom.texcoord && geom.matid >= 0;
const int addr = mjv_scene_.flexfaceadr[geom.objid];
const float* positions = mjv_scene_.flexface + (9 * addr);
const float* normals = mjv_scene_.flexnormal + (9 * addr);
const float* uvs =
has_uvs ? mjv_scene_.flextexcoord + (6 * addr) : nullptr;
mjrfMeshData data;
mjrf_defaultMeshData(&data);
data.num_attributes = has_uvs ? 3 : 2;
data.attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
data.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
data.attributes[0].bytes = positions;
data.attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_NORMAL;
data.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
data.attributes[1].bytes = normals;
data.attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
data.attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
data.attributes[2].bytes = uvs;
data.num_vertices = 3 * mjv_scene_.flexfaceused[geom.objid];
data.num_indices = 3 * mjv_scene_.flexfaceused[geom.objid];
data.indices = nullptr;
data.index_type = mjINDEX_TYPE_U32;
data.primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
data.compute_bounds = true;
data.release = nullptr;
data.user_data = nullptr;
meshes_.push_back(CreateMesh(ctx, data));
auto renderable = CreateRenderable(ctx, params);
mjrf_setRenderableMesh(renderable.get(), meshes_.back().get(), 0, 0);
mjrf_setRenderableMaterial(renderable.get(), &material);
mjrf_addRenderableToScene(scene_, renderable.get());
decorations_.push_back(std::move(renderable));
}
continue;
}
@@ -50,10 +50,10 @@ class SceneDecorator {
SceneDecorator& operator=(const SceneDecorator&) = delete;
private:
mjrfContext* ctx_;
mjrfScene* scene_;
ModelObjects* model_objects_;
mjvScene mjv_scene_;
std::vector<UniquePtr<mjrfMesh>> meshes_;
std::vector<UniquePtr<mjrfRenderable>> decorations_;
};
+113 -195
View File
@@ -23,8 +23,8 @@
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <mujoco/mujoco.h>
#include <mujoco/mjrfilament.h>
#include <mujoco/mujoco.h>
#include "engine/engine_vis_visualize.h"
#include "render/filament/mjrfilament_cpp.h"
#include "render/filament/support/filament_util.h"
@@ -53,17 +53,16 @@ struct VertexWithUv {
} // namespace
static void AccumulateNormal(float3* normals, const mjtNum* src_positions,
const int* indices, int i0, int i1, int i2) {
const int idx0 = indices[i0];
const int idx1 = indices[i1];
const int idx2 = indices[i2];
const int* indices) {
const int idx0 = indices[0];
const int idx1 = indices[1];
const int idx2 = indices[2];
const float3 v0 = ReadFloat3(src_positions, idx0);
const float3 v1 = ReadFloat3(src_positions, idx1);
const float3 v2 = ReadFloat3(src_positions, idx2);
const float3 normal = cross(v1 - v0, v2 - v0);
const float3 normal = normalize(cross(v1 - v0, v2 - v0));
normals[idx0] += normal;
normals[idx1] += normal;
@@ -71,27 +70,10 @@ static void AccumulateNormal(float3* normals, const mjtNum* src_positions,
}
template <typename T>
static void AddFlatFace(T* vertices, const mjtNum* src_positions, float radius,
const int* indices, int i0, int i1, int i2) {
const float3 v0 = ReadFloat3(src_positions, indices[i0]);
const float3 v1 = ReadFloat3(src_positions, indices[i1]);
const float3 v2 = ReadFloat3(src_positions, indices[i2]);
const float3 normal = normalize(cross(v1 - v0, v2 - v0));
vertices[0].position = v0 + (radius * normal);
vertices[1].position = v1 + (radius * normal);
vertices[2].position = v2 + (radius * normal);
const float4 orientation = CalculateOrientation(normal);
vertices[0].orientation = orientation;
vertices[1].orientation = orientation;
vertices[2].orientation = orientation;
}
template <typename T>
static void AddSmoothFace(T* vertices, const mjtNum* src_positions,
const float3* src_normals, float radius,
const int* indices, int i0, int i1, int i2) {
static void AddFlexFace(T* vertices, const mjtNum* src_positions,
const float3* src_normals, float radius,
bool flatten_normals, const int* indices, int i0,
int i1, int i2) {
const int idx0 = indices[i0];
const int idx1 = indices[i1];
const int idx2 = indices[i2];
@@ -104,13 +86,20 @@ static void AddSmoothFace(T* vertices, const mjtNum* src_positions,
vertices[1].position = v1 + (radius * src_normals[idx1]);
vertices[2].position = v2 + (radius * src_normals[idx2]);
const float sign = radius > 0 ? 1.f : -1.f;
vertices[0].orientation = CalculateOrientation(sign * src_normals[idx0]);
vertices[1].orientation = CalculateOrientation(sign * src_normals[idx1]);
vertices[2].orientation = CalculateOrientation(sign * src_normals[idx2]);
const float sign = radius >= 0 ? 1.f : -1.f;
if (flatten_normals) {
const float3 flat = normalize(cross(v1 - v0, v2 - v0));
const float4 orientation = CalculateOrientation(sign * flat);
vertices[0].orientation = orientation;
vertices[1].orientation = orientation;
vertices[2].orientation = orientation;
} else {
vertices[0].orientation = CalculateOrientation(sign * src_normals[idx0]);
vertices[1].orientation = CalculateOrientation(sign * src_normals[idx1]);
vertices[2].orientation = CalculateOrientation(sign * src_normals[idx2]);
}
}
// Assumes T::orientation is already set to the "smoothed" normal.
template <typename T>
static void AddSideFace(T* vertices, const mjtNum* src_positions,
const float3* src_normals, float radius,
@@ -157,46 +146,15 @@ static void UpdateBounds(float3* min_pt, float3* max_pt, const T* vertices) {
*max_pt = max(*max_pt, vertices[2].position);
}
static int CalculateVertexCount(const mjModel* model, int flex_id,
int flex_layer, bool smooth_skinning) {
const int dim = model->flex_dim[flex_id];
int num_faces = 0;
if (dim == 1) {
// 1d flexes have no faces.
} else if (smooth_skinning) {
if (dim == 2) {
num_faces += (2 * model->flex_elemnum[flex_id]);
num_faces += (2 * model->flex_shellnum[flex_id]);
} else {
num_faces += model->flex_shellnum[flex_id];
}
} else {
if (dim == 2) {
num_faces += (2 * model->flex_elemnum[flex_id]);
} else {
for (int e = 0; e < model->flex_elemnum[flex_id]; e++) {
if (model->flex_elemlayer[model->flex_elemadr[flex_id] + e] ==
flex_layer) {
num_faces += 4;
}
}
}
}
return num_faces * 3;
}
template <typename T>
static void FillFlexVertices(T* vertices, const mjModel* model,
const mjData* data, int flex_id, float3* min_pt,
float3* max_pt, int flex_layer,
bool smooth_skinning) {
*min_pt = float3(FLT_MAX);
*max_pt = float3(FLT_MIN);
const int num_vertices =
CalculateVertexCount(model, flex_id, flex_layer, smooth_skinning);
float3* max_pt) {
const int dim = model->flex_dim[flex_id];
const int* edata = model->flex_elem + model->flex_elemdataadr[flex_id];
const int* sdata = model->flex_shell + model->flex_shelldataadr[flex_id];
const int* tdata =
model->flex_elemtexcoord + model->flex_elemdataadr[flex_id];
const float radius = (float)model->flex_radius[flex_id];
const bool flat_shading = (bool)model->flex_flatskin[flex_id];
@@ -208,133 +166,87 @@ static void FillFlexVertices(T* vertices, const mjModel* model,
src_uvs = model->flex_texcoord + 2 * model->flex_texcoordadr[flex_id];
}
const int* edata = model->flex_elem + model->flex_elemdataadr[flex_id];
const int* sdata = model->flex_shell + model->flex_shelldataadr[flex_id];
const int* tdata =
model->flex_elemtexcoord + model->flex_elemdataadr[flex_id];
if (dim == 1) {
// 1D - don't render?
return;
// Determine the maximum vertex index.
int max_index = 0;
if (dim == 2) {
for (int e = 0; e < model->flex_elemnum[flex_id]; ++e) {
const int* subindices = edata + (e * 3);
max_index = mjMAX(max_index, subindices[0]);
max_index = mjMAX(max_index, subindices[1]);
max_index = mjMAX(max_index, subindices[2]);
}
} else {
for (int s = 0; s < model->flex_shellnum[flex_id]; ++s) {
const int* subindices = sdata + (s * 3);
max_index = mjMAX(max_index, subindices[0]);
max_index = mjMAX(max_index, subindices[1]);
max_index = mjMAX(max_index, subindices[2]);
}
}
else if (smooth_skinning) {
// Accumulate normals in the `orientation` field.
std::vector<float3> normals(num_vertices, float3(0, 0, 0));
if (dim == 2) {
for (int e = 0; e < model->flex_elemnum[flex_id]; ++e) {
const int* indices = edata + e * (dim + 1);
AccumulateNormal(normals.data(), src_positions, indices, 0, 1, 2);
}
} else {
for (int s = 0; s < model->flex_shellnum[flex_id]; ++s) {
const int* indices = sdata + s * dim;
AccumulateNormal(normals.data(), src_positions, indices, 0, 1, 2);
}
// Accumulate normals.
std::vector<float3> normals(max_index + 1, float3(0, 0, 0));
if (dim == 2) {
for (int e = 0; e < model->flex_elemnum[flex_id]; ++e) {
const int* subindices = edata + (e * 3);
AccumulateNormal(normals.data(), src_positions, subindices);
}
} else {
for (int s = 0; s < model->flex_shellnum[flex_id]; ++s) {
const int* subindices = sdata + (s * 3);
AccumulateNormal(normals.data(), src_positions, subindices);
}
}
// Normalize the accumulated normals.
for (float3& n : normals) {
// Normalize the accumulated normals.
for (float3& n : normals) {
if (length(n) > 1e-16f) {
n = normalize(n);
}
if (dim == 2) {
for (int e = 0; e < model->flex_elemnum[flex_id]; ++e) {
const int* indices = edata + (e * (dim + 1));
const int* tex_indices = tdata + (e * (dim + 1));
if (flat_shading) {
AddFlatFace(vertices, src_positions, radius, indices, 0, 1, 2);
} else {
AddSmoothFace(vertices, src_positions, normals.data(), radius,
indices, 0, 1, 2);
}
AddFaceUvs(vertices, src_uvs, tex_indices, 0, 1, 2);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
if (flat_shading) {
AddFlatFace(vertices, src_positions, -radius, indices, 0, 2, 1);
} else {
AddSmoothFace(vertices, src_positions, normals.data(), -radius,
indices, 0, 2, 1);
}
AddFaceUvs(vertices, src_uvs, tex_indices, 0, 2, 1);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
}
for (int s = 0; s < model->flex_shellnum[flex_id]; ++s) {
const int* indices = sdata + (s * dim);
AddSideFace(vertices, src_positions, normals.data(), radius, indices, 0,
1);
AddFaceUvs(vertices, src_uvs, indices, 0, 1, 1);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
AddSideFace(vertices, src_positions, normals.data(), -radius, indices,
1, 0);
AddFaceUvs(vertices, src_uvs, indices, 1, 0, 0);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
}
} else {
for (int s = 0; s < model->flex_shellnum[flex_id]; ++s) {
const int* indices = sdata + s * dim;
if (flat_shading) {
AddFlatFace(vertices, src_positions, radius, indices, 0, 1, 2);
} else {
AddSmoothFace(vertices, src_positions, normals.data(), radius,
indices, 0, 1, 2);
}
AddFaceUvs(vertices, src_uvs, indices, 0, 1, 2);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
}
n = float3(1, 0, 0);
}
}
// 2D or 3D face: faces from elements, flat normals, texture
else {
for (int e = 0; e < model->flex_elemnum[flex_id]; e++) {
// in 3D, show only elements in selected layer
if (dim == 2 || model->flex_elemlayer[model->flex_elemadr[flex_id] + e] ==
flex_layer) {
const int* edata2 = edata + e * (dim + 1);
const int* tdata2 = tdata + e * (dim + 1);
if (dim == 2) {
for (int e = 0; e < model->flex_elemnum[flex_id]; ++e) {
const int* indices = edata + (e * (dim + 1));
const int* tex_indices = tdata + (e * (dim + 1));
if (dim == 2) {
AddFlatFace(vertices, src_positions, radius, edata2, 0, 1, 2);
AddFaceUvs(vertices, src_uvs, tdata2, 0, 1, 2);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
AddFlexFace(vertices, src_positions, normals.data(), radius,
flat_shading, indices, 0, 1, 2);
AddFaceUvs(vertices, src_uvs, tex_indices, 0, 1, 2);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
AddFlatFace(vertices, src_positions, radius, edata2, 0, 2, 1);
AddFaceUvs(vertices, src_uvs, tdata2, 0, 2, 1);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
} else {
AddFlatFace(vertices, src_positions, radius, edata2, 0, 1, 2);
AddFaceUvs(vertices, src_uvs, tdata2, 0, 1, 2);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
AddFlexFace(vertices, src_positions, normals.data(), -radius,
flat_shading, indices, 0, 2, 1);
AddFaceUvs(vertices, src_uvs, tex_indices, 0, 2, 1);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
}
for (int s = 0; s < model->flex_shellnum[flex_id]; ++s) {
const int* indices = sdata + (s * dim);
AddFlatFace(vertices, src_positions, radius, edata2, 0, 2, 3);
AddFaceUvs(vertices, src_uvs, tdata2, 0, 2, 3);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
AddSideFace(vertices, src_positions, normals.data(), radius, indices, 0,
1);
AddFaceUvs(vertices, src_uvs, indices, 0, 1, 1);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
AddFlatFace(vertices, src_positions, radius, edata2, 0, 3, 1);
AddFaceUvs(vertices, src_uvs, tdata2, 0, 3, 1);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
AddFlatFace(vertices, src_positions, radius, edata2, 1, 3, 2);
AddFaceUvs(vertices, src_uvs, tdata2, 1, 3, 2);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
}
}
AddSideFace(vertices, src_positions, normals.data(), -radius, indices, 1,
0);
AddFaceUvs(vertices, src_uvs, indices, 1, 0, 0);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
}
} else if (dim == 3) {
for (int s = 0; s < model->flex_shellnum[flex_id]; ++s) {
const int* indices = sdata + s * dim;
AddFlexFace(vertices, src_positions, normals.data(), radius,
flat_shading, indices, 0, 1, 2);
AddFaceUvs(vertices, src_uvs, indices, 0, 1, 2);
UpdateBounds(min_pt, max_pt, vertices);
vertices += 3;
}
}
}
@@ -500,13 +412,21 @@ static void SetBounds(mjrfMeshData* mesh_data, const float3& min_pt,
}
UniquePtr<mjrfMesh> CreateFlexMesh(mjrfContext* ctx, const mjModel* model,
const mjData* data, int flex_id,
int flex_layer, bool smooth_skinning,
bool generate_edges,
bool generate_vertices) {
// TODO: generate edges and vertices if requested.
const int num_vertices =
CalculateVertexCount(model, flex_id, flex_layer, smooth_skinning);
const mjData* data, int flex_id) {
const int dim = model->flex_dim[flex_id];
int num_faces = 0;
if (dim == 2) {
num_faces += (2 * model->flex_elemnum[flex_id]);
num_faces += (2 * model->flex_shellnum[flex_id]);
} else if (dim == 3) {
num_faces += model->flex_shellnum[flex_id];
} else {
// 1D flex objects should be rendered as a collection of capsules.
mju_error("Unsupported flex dimension: %d", dim);
}
const int num_vertices = num_faces * 3;
const bool has_uvs = model->flex_texcoordadr[flex_id] >= 0;
mjrfMeshData mesh_data = PrepareMeshData(num_vertices, has_uvs);
@@ -514,19 +434,17 @@ UniquePtr<mjrfMesh> CreateFlexMesh(mjrfContext* ctx, const mjModel* model,
float3 max_pt = float3(FLT_MIN);
if (has_uvs) {
VertexWithUv* vertices = (VertexWithUv*)(mesh_data.user_data);
FillFlexVertices(vertices, model, data, flex_id, &min_pt, &max_pt,
flex_layer, smooth_skinning);
FillFlexVertices(vertices, model, data, flex_id, &min_pt, &max_pt);
} else {
VertexNoUv* vertices = (VertexNoUv*)(mesh_data.user_data);
FillFlexVertices(vertices, model, data, flex_id, &min_pt, &max_pt,
flex_layer, smooth_skinning);
FillFlexVertices(vertices, model, data, flex_id, &min_pt, &max_pt);
}
SetBounds(&mesh_data, min_pt, max_pt);
return CreateMesh(ctx, mesh_data);
}
UniquePtr<mjrfMesh> CreateSkinMesh(mjrfContext* ctx, const mjModel* model,
const mjData* data, int skin_id) {
const mjData* data, int skin_id) {
const int num_vertices = model->skin_vertnum[skin_id];
const bool has_uvs = model->skin_texcoordadr[skin_id] >= 0;
mjrfMeshData mesh_data = PrepareMeshData(num_vertices, has_uvs);
+1 -3
View File
@@ -25,9 +25,7 @@ namespace mujoco {
// Creates a mjrfMesh for the given flex object.
UniquePtr<mjrfMesh> CreateFlexMesh(mjrfContext* ctx, const mjModel* model,
const mjData* data, int flex_id,
int flex_layer, bool smooth_skinning,
bool generate_edges, bool generate_vertices);
const mjData* data, int flex_id);
// Creates a mjrfMesh for the given skin object.
UniquePtr<mjrfMesh> CreateSkinMesh(mjrfContext* ctx, const mjModel* model,
@@ -197,8 +197,10 @@ RenderableManager::~RenderableManager() {
for (auto& renderable : sites_) {
mjrf_removeRenderableFromScene(scene_, renderable.get());
}
for (auto& renderable : flexes_) {
mjrf_removeRenderableFromScene(scene_, renderable.get());
for (auto& parts : flexes_) {
for (auto& renderable : parts) {
mjrf_removeRenderableFromScene(scene_, renderable.get());
}
}
for (auto& renderable : skins_) {
mjrf_removeRenderableFromScene(scene_, renderable.get());
@@ -231,15 +233,29 @@ void RenderableManager::Update(const mjData* data) {
mjrf_setRenderableTransform(sites_[i].get(), pos.v, mat.asArray());
}
for (int i = 0; i < model->nflex; ++i) {
const int flex_layer = vopts_.flex_layer;
const bool smooth_skinning = vopts_.flags[mjVIS_FLEXSKIN];
const bool edges = !smooth_skinning && vopts_.flags[mjVIS_FLEXEDGE];
const bool vertices = !smooth_skinning && vopts_.flags[mjVIS_FLEXVERT];
auto mesh = CreateFlexMesh(ctx, model, data, i, flex_layer, smooth_skinning,
edges, vertices);
mjrf_setRenderableMesh(flexes_[i].get(), mesh.get(), 0, 0);
flex_meshes_[i] = std::move(mesh);
if (vopts_.flags[mjVIS_FLEXSKIN]) {
for (int i = 0; i < model->nflex; ++i) {
const int dim = model->flex_dim[i];
if (dim > 1) {
auto mesh = CreateFlexMesh(ctx, model, data, i);
mjrf_setRenderableMesh(flexes_[i][0].get(), mesh.get(), 0, 0);
flex_meshes_[i] = std::move(mesh);
} else {
const int vertadr = model->flex_vertadr[i];
const int edgeadr = model->flex_edgeadr[i];
const int edgenum = model->flex_edgenum[i];
const float radius = model->flex_radius[i];
auto& edges = flexes_[i];
for (int j = 0; j < edgenum; ++j) {
const int e = j + edgeadr;
const int idx0 = model->flex_edge[2 * e];
const int idx1 = model->flex_edge[2 * e + 1];
const float3 v0 = ReadFloat3(data->flexvert_xpos, vertadr + idx0);
const float3 v1 = ReadFloat3(data->flexvert_xpos, vertadr + idx1);
Connect(edges[j].get(), v0, v1, radius);
}
}
}
}
for (int i = 0; i < model->nskin; ++i) {
@@ -316,11 +332,13 @@ void RenderableManager::Update(const mjData* data) {
island_id = model->tree_dofadr[tree];
}
mjrfMaterial material;
mjrf_getRenderableMaterial(flexes_[i].get(), &material);
material.island_id = island_id;
material.sleep_state = awake ? mjS_AWAKE : mjS_ASLEEP;
mjrf_setRenderableMaterial(flexes_[i].get(), &material);
for (auto& renderable : flexes_[i]) {
mjrfMaterial material;
mjrf_getRenderableMaterial(renderable.get(), &material);
material.island_id = island_id;
material.sleep_state = awake ? mjS_AWAKE : mjS_ASLEEP;
mjrf_setRenderableMaterial(renderable.get(), &material);
}
}
}
}
@@ -340,7 +358,10 @@ mjrfRenderable* RenderableManager::GetRenderable(mjtObj obj_type, int obj_index,
break;
case mjOBJ_FLEX:
if (obj_index >= 0 && obj_index < flexes_.size()) {
return flexes_[obj_index].get();
auto& parts = flexes_[obj_index];
if (sub_index >= 0 && sub_index < parts.size()) {
return parts[sub_index].get();
}
}
break;
case mjOBJ_SKIN:
@@ -449,18 +470,37 @@ void RenderableManager::AddFlexGeoms() {
flexes_.reserve(model->nflex);
flex_meshes_.reserve(model->nflex);
for (int i = 0; i < model->nflex; ++i) {
mjrfRenderableParams params;
mjrf_defaultRenderableParams(&params);
auto renderable = CreateRenderable(ctx, params);
flex_meshes_.emplace_back(nullptr, nullptr);
mjrfMaterial material = GetDefaultMaterial(mjOBJ_FLEX, i);
mjrf_setRenderableMaterial(renderable.get(), &material);
mjrfRenderableParams params;
mjrf_defaultRenderableParams(&params);
auto& parts = flexes_.emplace_back();
const int dim = model->flex_dim[i];
if (dim == 1) {
const float radius = model->flex_radius[i];
const float size[3] = {radius, radius, radius};
for (int j = 0; j < model->flex_edgenum[i]; ++j) {
auto renderable = CreateRenderable(ctx, params);
mjrf_setRenderableGeomMesh(renderable.get(), mjGEOM_CAPSULE, 4, 8, 1);
mjrf_setRenderableSize(renderable.get(), size);
mjrf_setRenderableMaterial(renderable.get(), &material);
parts.push_back(std::move(renderable));
}
} else {
auto renderable = CreateRenderable(ctx, params);
mjrf_setRenderableMaterial(renderable.get(), &material);
parts.push_back(std::move(renderable));
}
if (vopts_.flexgroup[model->flex_group[i]]) {
mjrf_addRenderableToScene(scene_, renderable.get());
for (auto& renderable : parts) {
mjrf_addRenderableToScene(scene_, renderable.get());
}
}
flexes_.emplace_back(std::move(renderable));
flex_meshes_.emplace_back(nullptr, nullptr);
}
}
@@ -950,7 +990,7 @@ void RenderableManager::SetVisibility(mjtObj obj_type, int idx, bool visible) {
case mjOBJ_FLEX:
DetermineVisibilities(ops, vopts_.flexgroup, &vopts_.flags[mjVIS_FLEXSKIN], idx, visible);
for (int i = 0; i < model->nflex; ++i) {
ApplyVisibility(ops, model->flex_group[i], scene_, {&flexes_[i], 1});
ApplyVisibility(ops, model->flex_group[i], scene_, flexes_[i]);
}
break;
case mjOBJ_SKIN:
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_RENDERABLE_MANAGER_H_
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_RENDERABLE_MANAGER_H_
#ifndef MUJOCO_SRC_RENDER_FILAMENT_SUPPORT_RENDERABLE_MANAGER_H_
#define MUJOCO_SRC_RENDER_FILAMENT_SUPPORT_RENDERABLE_MANAGER_H_
#include <array>
#include <unordered_map>
@@ -98,10 +98,10 @@ class RenderableManager {
std::vector<UniquePtr<mjrfRenderable>> geoms_;
std::vector<UniquePtr<mjrfRenderable>> sites_;
std::vector<UniquePtr<mjrfRenderable>> flexes_;
std::vector<UniquePtr<mjrfRenderable>> skins_;
std::unordered_map<int, std::array<UniquePtr<mjrfRenderable>, 2>> slider_cranks_;
std::vector<std::vector<UniquePtr<mjrfRenderable>>> flexes_;
std::vector<std::vector<UniquePtr<mjrfRenderable>>> tendons_;
std::unordered_map<int, std::array<UniquePtr<mjrfRenderable>, 2>> slider_cranks_;
std::vector<UniquePtr<mjrfMesh>> flex_meshes_;
std::vector<UniquePtr<mjrfMesh>> skin_meshes_;
@@ -109,4 +109,4 @@ class RenderableManager {
};
} // namespace mujoco
#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_RENDERABLE_MANAGER_H_
#endif // MUJOCO_SRC_RENDER_FILAMENT_SUPPORT_RENDERABLE_MANAGER_H_