Implement RenderableManager.

Similar to LightManager, the RenderableManager manages
renderables based on an mjModel and its mjData.

PiperOrigin-RevId: 941510745
Change-Id: I97b44cc857f136aa7c90ceb85a42e299fc759b73
This commit is contained in:
Haroon Qureshi
2026-07-02 00:02:24 -07:00
committed by Copybara-Service
parent c6c3ec3149
commit fb259a5edd
7 changed files with 1750 additions and 3 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ void mj_updateSleepInit(const mjModel* m, mjData* d, int flg_staticawake);
MJAPI void mj_updateSleep(const mjModel* m, mjData* d);
// return the first tree in the sleep cycle that starts at i, -1 if error
int mj_sleepCycle(const int* tree_asleep, int ntree, int i);
MJAPI int mj_sleepCycle(const int* tree_asleep, int ntree, int i);
// wake tree i and its related island cycle, return number of woke trees
MJAPI int mj_wakeIsland(int* tree_asleep, int ntree, int i, int wakeval,
+2 -2
View File
@@ -69,8 +69,8 @@ MJAPI void mjv_cameraFrustum(float zver[2], float zhor[2], float zclip[2], cons
MJAPI int mjv_isCatenary(const mjModel* m, const mjData* d, int i, mjtNum* length);
// computes points along a catenary curve
int mjv_catenary(const mjtNum x0[3], const mjtNum x1[3], const mjtNum gravity[3], mjtNum length,
mjtNum* catenary, int ncatenary);
MJAPI int mjv_catenary(const mjtNum x0[3], const mjtNum x1[3], const mjtNum gravity[3],
mjtNum length, mjtNum* catenary, int ncatenary);
// convert HSV to RGB
MJAPI void hsv2rgb(float *RGB, float H, float S, float V);
+4
View File
@@ -57,8 +57,12 @@ target_sources(${MUJOCO_FILAMENT_TARGET_NAME}
support/filament_util.cc
support/light_manager.h
support/light_manager.cc
support/mesh_util.h
support/mesh_util.cc
support/model_objects.h
support/model_objects.cc
support/renderable_manager.h
support/renderable_manager.cc
)
target_include_directories(${MUJOCO_FILAMENT_TARGET_NAME}
+589
View File
@@ -0,0 +1,589 @@
// Copyright 2026 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "render/filament/support/mesh_util.h"
#include <cfloat>
#include <cstddef>
#include <cstring>
#include <vector>
#include <math/TVecHelpers.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <mujoco/mujoco.h>
#include <mujoco/mjrfilament.h>
#include "engine/engine_vis_visualize.h"
#include "render/filament/mjrfilament_cpp.h"
#include "render/filament/support/filament_util.h"
namespace mujoco {
using filament::math::float2;
using filament::math::float3;
using filament::math::float4;
// Vertex types that can be used to fill in mesh data using generic functions.
namespace {
struct VertexNoUv {
float3 position;
float4 orientation;
static constexpr bool kHasUv = false;
};
struct VertexWithUv {
float3 position;
float4 orientation;
float2 uv;
static constexpr bool kHasUv = true;
};
} // 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 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);
normals[idx0] += normal;
normals[idx1] += normal;
normals[idx2] += normal;
}
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) {
const int idx0 = indices[i0];
const int idx1 = indices[i1];
const int idx2 = indices[i2];
const float3 v0 = ReadFloat3(src_positions, idx0);
const float3 v1 = ReadFloat3(src_positions, idx1);
const float3 v2 = ReadFloat3(src_positions, idx2);
vertices[0].position = v0 + (radius * src_normals[idx0]);
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]);
}
// 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,
const int* indices, int i0, int i1) {
const int idx0 = indices[i0];
const int idx1 = indices[i1];
const float3 v0 = ReadFloat3(src_positions, idx0);
const float3 v1 = ReadFloat3(src_positions, idx1);
const float3 v01 = v1 - v0;
float3 normal = normalize(cross(v01, src_normals[idx1]));
if (radius < 0) {
normal = -normal;
}
vertices[0].position = v0 + radius * src_normals[idx0];
vertices[1].position = v1 - radius * src_normals[idx1];
vertices[2].position = v1 + radius * src_normals[idx1];
vertices[0].orientation = CalculateOrientation(normal);
vertices[1].orientation = vertices[0].orientation;
vertices[2].orientation = vertices[0].orientation;
}
template <typename T>
static void AddFaceUvs(T* vertices, const float* src_uvs, const int* indices,
int t0, int t1, int t2) {
if constexpr (T::kHasUv) {
if (src_uvs) {
vertices[0].uv = ReadFloat2(src_uvs, indices[t0]);
vertices[1].uv = ReadFloat2(src_uvs, indices[t1]);
vertices[2].uv = ReadFloat2(src_uvs, indices[t2]);
}
}
}
template <typename T>
static void UpdateBounds(float3* min_pt, float3* max_pt, const T* vertices) {
*min_pt = min(*min_pt, vertices[0].position);
*max_pt = max(*max_pt, vertices[0].position);
*min_pt = min(*min_pt, vertices[1].position);
*max_pt = max(*max_pt, vertices[1].position);
*min_pt = min(*min_pt, vertices[2].position);
*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);
const int dim = model->flex_dim[flex_id];
const float radius = (float)model->flex_radius[flex_id];
const bool flat_shading = (bool)model->flex_flatskin[flex_id];
const mjtNum* src_positions =
data->flexvert_xpos + 3 * model->flex_vertadr[flex_id];
const float* src_uvs = nullptr;
if (model->flex_texcoordadr[flex_id] >= 0) {
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;
}
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);
}
}
// Normalize the accumulated normals.
for (float3& n : normals) {
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;
}
}
}
// 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) {
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;
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;
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;
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;
}
}
}
}
}
template <typename T>
static void FillSkinVertices(T* vertices, const mjModel* model,
const mjData* data, int skin_id, float3* min_pt,
float3* max_pt) {
const int vertadr = model->skin_vertadr[skin_id];
const int faceadr = model->skin_faceadr[skin_id];
const int facenum = model->skin_facenum[skin_id];
const int boneadr = model->skin_boneadr[skin_id];
const int bonenum = model->skin_bonenum[skin_id];
// Accumulate positions from all bones.
for (int bone_idx = boneadr; bone_idx < boneadr + bonenum; ++bone_idx) {
mjtNum bind_pos[3] = {(mjtNum)model->skin_bonebindpos[3 * bone_idx + 0],
(mjtNum)model->skin_bonebindpos[3 * bone_idx + 1],
(mjtNum)model->skin_bonebindpos[3 * bone_idx + 2]};
mjtNum bind_quat[4] = {(mjtNum)model->skin_bonebindquat[4 * bone_idx + 0],
(mjtNum)model->skin_bonebindquat[4 * bone_idx + 1],
(mjtNum)model->skin_bonebindquat[4 * bone_idx + 2],
(mjtNum)model->skin_bonebindquat[4 * bone_idx + 3]};
const int body_id = model->skin_bonebodyid[bone_idx];
const mjtNum* body_quat = data->xquat + 4 * body_id;
const mjtNum* body_pos = data->xpos + 3 * body_id;
// Apply the bone's current pose to the bind pose.
mjtNum neg_bind_quat[4];
mju_negQuat(neg_bind_quat, bind_quat);
mjtNum quat[4];
mju_mulQuat(quat, body_quat, neg_bind_quat);
mjtNum rotate[9];
mju_quat2Mat(rotate, quat);
mjtNum translate[3];
mju_mulMatVec3(translate, rotate, bind_pos);
mju_sub3(translate, body_pos, translate);
// Apply the bone's position to all vertices "connected" to the bone by
// the weighting of the bone to the vertex.
const int bonevertadr = model->skin_bonevertadr[bone_idx];
const int bonevertnum = model->skin_bonevertnum[bone_idx];
for (int i = bonevertadr; i < bonevertadr + bonevertnum; ++i) {
const int vertex_id = model->skin_bonevertid[i];
const mjtNum base_pos[3] = {
(mjtNum)model->skin_vert[3 * (vertadr + vertex_id) + 0],
(mjtNum)model->skin_vert[3 * (vertadr + vertex_id) + 1],
(mjtNum)model->skin_vert[3 * (vertadr + vertex_id) + 2],
};
mjtNum unweighted_pos[3];
mju_mulMatVec3(unweighted_pos, rotate, base_pos);
mju_addTo3(unweighted_pos, translate);
const float weight = model->skin_bonevertweight[i];
vertices[vertex_id].position.x += weight * (float)unweighted_pos[0];
vertices[vertex_id].position.y += weight * (float)unweighted_pos[1];
vertices[vertex_id].position.z += weight * (float)unweighted_pos[2];
}
}
// Compute normals for each face. For now, we'll store the normals in the
// xyz components of the orientation field.
for (int i = faceadr; i < faceadr + facenum; ++i) {
const int i0 = model->skin_face[(3 * i) + 0];
const int i1 = model->skin_face[(3 * i) + 1];
const int i2 = model->skin_face[(3 * i) + 2];
T& v0 = vertices[i0];
T& v1 = vertices[i1];
T& v2 = vertices[i2];
const float3 vec01 = v1.position - v0.position;
const float3 vec02 = v2.position - v0.position;
const float4 normal = float4(cross(vec01, vec02), 0);
v0.orientation += normal;
v1.orientation += normal;
v2.orientation += normal;
}
const float* uvs = nullptr;
if (model->skin_texcoordadr[skin_id] >= 0) {
const int uaddr = model->skin_texcoordadr[skin_id];
uvs = model->skin_texcoord + (2 * uaddr);
}
// Perform final adjustments/corrections on all the vertices.
const float inflate = model->skin_inflate[skin_id];
const size_t num_vertices = model->skin_vertnum[skin_id];
for (int i = 0; i < num_vertices; ++i) {
// Ensure normals are normalized.
vertices[i].orientation = normalize(vertices[i].orientation);
// Inflate the vertex position in direction of normal (if applicable).
if (inflate != 0.0f) {
vertices[i].position += inflate * vertices[i].orientation.xyz;
}
// Convert the normals into orientations.
vertices[i].orientation = CalculateOrientation(vertices[i].orientation.xyz);
// Assign uvs (if applicable).
if constexpr (T::kHasUv) {
vertices[i].uv = ReadFloat2(uvs, i);
}
// Calculate the bounds of the vertex buffer.
*min_pt = min(*min_pt, vertices[i].position);
*max_pt = max(*max_pt, vertices[i].position);
}
}
static mjrfMeshData PrepareMeshData(int num_vertices, bool has_uvs) {
mjrfMeshData mesh_data;
mjrf_defaultMeshData(&mesh_data);
if (has_uvs) {
const int nbytes = sizeof(VertexWithUv) * num_vertices;
mesh_data.user_data = new char[nbytes];
std::memset(mesh_data.user_data, 0, nbytes);
} else {
const int nbytes = sizeof(VertexNoUv) * num_vertices;
mesh_data.user_data = new char[nbytes];
std::memset(mesh_data.user_data, 0, nbytes);
}
mesh_data.release = [](void* user_data) {
delete[] (char*)(user_data);
};
char* buf = reinterpret_cast<char*>(mesh_data.user_data);
mesh_data.num_vertices = num_vertices;
mesh_data.num_attributes = has_uvs ? 3 : 2;
mesh_data.interleaved = true;
mesh_data.attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
mesh_data.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
mesh_data.attributes[0].bytes = buf;
mesh_data.attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_TANGENTS;
mesh_data.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4;
mesh_data.attributes[1].bytes = buf + sizeof(float[3]);
if (has_uvs) {
mesh_data.attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
mesh_data.attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
mesh_data.attributes[2].bytes = buf + sizeof(float[7]);
}
mesh_data.primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
mesh_data.num_indices = num_vertices;
mesh_data.index_type = mjINDEX_TYPE_U32;
mesh_data.indices = nullptr;
return mesh_data;
}
static void SetBounds(mjrfMeshData* mesh_data, const float3& min_pt,
const float3& max_pt) {
mesh_data->bounds_min[0] = min_pt.x;
mesh_data->bounds_min[1] = min_pt.y;
mesh_data->bounds_min[2] = min_pt.z;
mesh_data->bounds_max[0] = max_pt.x;
mesh_data->bounds_max[1] = max_pt.y;
mesh_data->bounds_max[2] = max_pt.z;
}
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 bool has_uvs = model->flex_texcoordadr[flex_id] >= 0;
mjrfMeshData mesh_data = PrepareMeshData(num_vertices, has_uvs);
float3 min_pt = float3(FLT_MAX);
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);
} else {
VertexNoUv* vertices = (VertexNoUv*)(mesh_data.user_data);
FillFlexVertices(vertices, model, data, flex_id, &min_pt, &max_pt,
flex_layer, smooth_skinning);
}
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 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);
float3 min_pt = float3(FLT_MAX);
float3 max_pt = float3(FLT_MIN);
if (has_uvs) {
VertexWithUv* vertices = (VertexWithUv*)(mesh_data.user_data);
FillSkinVertices(vertices, model, data, skin_id, &min_pt, &max_pt);
} else {
VertexNoUv* vertices = (VertexNoUv*)(mesh_data.user_data);
FillSkinVertices(vertices, model, data, skin_id, &min_pt, &max_pt);
}
mesh_data.num_indices = 3 * model->skin_facenum[skin_id];
mesh_data.indices = model->skin_face + 3 * model->skin_faceadr[skin_id];
SetBounds(&mesh_data, min_pt, max_pt);
return CreateMesh(ctx, mesh_data);
}
void GatherSpatialTendonPoints(const mjModel* model, const mjData* data,
int tendon_id, std::vector<float4>& points) {
mjtNum length = 0.f;
const bool is_catenary = mjv_isCatenary(model, data, tendon_id, &length);
if (is_catenary) {
const int max_segments = mjMIN(model->vis.quality.numslices + 1, 100);
mjtNum x0[3];
mju_copy3(x0, data->wrap_xpos + 3 * data->ten_wrapadr[tendon_id] + 0);
mjtNum x1[3];
mju_copy3(x1, data->wrap_xpos + 3 * data->ten_wrapadr[tendon_id] + 3);
const float width = model->tendon_width[tendon_id];
mjtNum pts[3 * 100];
const int npoints =
mjv_catenary(x0, x1, model->opt.gravity, length, pts, max_segments);
for (int j = 0; j < npoints - 1; ++j) {
points.emplace_back(ReadFloat3(pts, j), width);
points.emplace_back(ReadFloat3(pts, j + 1), width);
}
} else {
const int adr = data->ten_wrapadr[tendon_id];
const int num = data->ten_wrapnum[tendon_id];
for (int j = adr; j < adr + num - 1; j++) {
if (data->wrap_obj[j] == -2 || data->wrap_obj[j + 1] == -2) {
continue;
}
float width = model->tendon_width[tendon_id];
if (data->wrap_obj[j] >= 0 && data->wrap_obj[j + 1] >= 0) {
width *= 0.5;
}
points.emplace_back(ReadFloat3(data->wrap_xpos, j + 0), width);
points.emplace_back(ReadFloat3(data->wrap_xpos, j + 1), width);
}
}
}
} // namespace mujoco
+45
View File
@@ -0,0 +1,45 @@
// Copyright 2026 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MUJOCO_SRC_RENDER_FILAMENT_SUPPORT_MESH_UTIL_H_
#define MUJOCO_SRC_RENDER_FILAMENT_SUPPORT_MESH_UTIL_H_
#include <vector>
#include <math/vec4.h>
#include <mujoco/mujoco.h>
#include <mujoco/mjrfilament.h>
#include "render/filament/mjrfilament_cpp.h"
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);
// Creates a mjrfMesh for the given skin object.
UniquePtr<mjrfMesh> CreateSkinMesh(mjrfContext* ctx, const mjModel* model,
const mjData* data, int skin_id);
// Populates the set of points that define the given tendon. Points are
// added in pairs, representing the start and end of a segment of the tendon.
// The w-component of the point stores the width/radius of the tendon.
void GatherSpatialTendonPoints(const mjModel* model, const mjData* data,
int tendon_id,
std::vector<filament::math::float4>& points);
} // namespace mujoco
#endif // MUJOCO_SRC_RENDER_FILAMENT_SUPPORT_MESH_UTIL_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,100 @@
// Copyright 2026 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// 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_
#include <optional>
#include <vector>
#include <math/vec4.h>
#include <mujoco/mjdata.h>
#include <mujoco/mjrfilament.h>
#include <mujoco/mjvisualize.h>
#include <mujoco/mujoco.h>
#include "render/filament/mjrfilament_cpp.h"
#include "render/filament/support/model_objects.h"
namespace mujoco {
// Manages Renderable entities for an mjrfScene.
class RenderableManager {
public:
// Populates the mjrScene with the renderables from the model.
RenderableManager(mjrfContext* ctx, mjrfScene* scene,
ModelObjects* model_objects);
~RenderableManager();
// Updates the state of the renderables in the scene.
void Update(const mjData* data);
// Returns the renderable corresponding to the given model object.
mjrfRenderable* GetRenderable(mjtObj obj_type, int obj_index);
// Returns the default material (as defined in the mjModel) for the given
// object. Useful if you want to "reset" the material of a renderable back
// to its default.
mjrfMaterial GetDefaultMaterial(mjtObj obj_type, int obj_index);
// Marks the given object as "selected", unmarking any previously selected
// object.
void SelectObject(mjtObj obj_type, int obj_index);
// Sets the visibility of all renderables of the given type. If `group` is
// specified, only applies to renderables in that group.
void SetVisibility(mjtObj obj_type, bool visible,
std::optional<int> group = std::nullopt);
// Applies the visualization options to the renderables in the scene.
void Apply(const mjvOption& vopts);
private:
void AddGeomGeoms();
void AddSiteGeoms();
void AddFlexGeoms();
void AddSkinGeoms();
void AddSliderCrankGeoms();
void UpdateSpatialTendons(const mjData* data, int tendon_id);
void AppendSegmentToTendon(int tendon_id);
void RemoveSegmentFromTendon(int tendon_id);
void UpdateSliderCranks(const mjData* data, int actuator_id, int index);
int GetSegmentationId(mjtObj obj_type, int obj_index);
mjrfRenderable* GetSelectedRenderable();
mjrfContext* ctx_;
mjrfScene* scene_;
ModelObjects* model_objects_;
mjvOption vopts_;
std::vector<UniquePtr<mjrfRenderable>> geoms_;
std::vector<UniquePtr<mjrfRenderable>> sites_;
std::vector<UniquePtr<mjrfRenderable>> flexes_;
std::vector<UniquePtr<mjrfRenderable>> skins_;
std::vector<UniquePtr<mjrfRenderable>> sliders_;
std::vector<UniquePtr<mjrfRenderable>> cranks_;
std::vector<std::vector<UniquePtr<mjrfRenderable>>> tendons_;
std::vector<UniquePtr<mjrfMesh>> flex_meshes_;
std::vector<UniquePtr<mjrfMesh>> skin_meshes_;
std::vector<filament::math::float4> point_cache_;
mjtObj selected_obj_type_ = mjOBJ_UNKNOWN;
int selected_obj_index_ = -1;
};
} // namespace mujoco
#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_RENDERABLE_MANAGER_H_