From a8545ac7cc34dbc9f169c04e81dfd56cb6c8fdcc Mon Sep 17 00:00:00 2001 From: Haroon Qureshi Date: Tue, 21 Jul 2026 11:14:47 -0700 Subject: [PATCH] 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 --- .../filament/compat/scene_decorator.cc | 128 +++++++- .../filament/compat/scene_decorator.h | 2 +- src/render/filament/support/mesh_util.cc | 308 +++++++----------- src/render/filament/support/mesh_util.h | 4 +- .../filament/support/renderable_manager.cc | 90 +++-- .../filament/support/renderable_manager.h | 10 +- 6 files changed, 310 insertions(+), 232 deletions(-) diff --git a/src/experimental/filament/compat/scene_decorator.cc b/src/experimental/filament/compat/scene_decorator.cc index b7bea6d3..04968afd 100644 --- a/src/experimental/filament/compat/scene_decorator.cc +++ b/src/experimental/filament/compat/scene_decorator.cc @@ -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(¶ms); + + 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(edgemat[0]), + static_cast(edgemat[1]), + static_cast(edgemat[2]), + static_cast(edgemat[3]), + static_cast(edgemat[4]), + static_cast(edgemat[5]), + static_cast(edgemat[6]), + static_cast(edgemat[7]), + static_cast(edgemat[8]), + }; + + const float len = static_cast(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; } diff --git a/src/experimental/filament/compat/scene_decorator.h b/src/experimental/filament/compat/scene_decorator.h index 508700e5..c69ab1f1 100644 --- a/src/experimental/filament/compat/scene_decorator.h +++ b/src/experimental/filament/compat/scene_decorator.h @@ -50,10 +50,10 @@ class SceneDecorator { SceneDecorator& operator=(const SceneDecorator&) = delete; private: - mjrfContext* ctx_; mjrfScene* scene_; ModelObjects* model_objects_; mjvScene mjv_scene_; + std::vector> meshes_; std::vector> decorations_; }; diff --git a/src/render/filament/support/mesh_util.cc b/src/render/filament/support/mesh_util.cc index d72ac09a..fcc3a7b1 100644 --- a/src/render/filament/support/mesh_util.cc +++ b/src/render/filament/support/mesh_util.cc @@ -23,8 +23,8 @@ #include #include #include -#include #include +#include #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 -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 -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 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 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 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 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 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 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 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); diff --git a/src/render/filament/support/mesh_util.h b/src/render/filament/support/mesh_util.h index baf97050..577db417 100644 --- a/src/render/filament/support/mesh_util.h +++ b/src/render/filament/support/mesh_util.h @@ -25,9 +25,7 @@ namespace mujoco { // Creates a mjrfMesh for the given flex object. UniquePtr 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 CreateSkinMesh(mjrfContext* ctx, const mjModel* model, diff --git a/src/render/filament/support/renderable_manager.cc b/src/render/filament/support/renderable_manager.cc index b64b16bc..6827ef10 100644 --- a/src/render/filament/support/renderable_manager.cc +++ b/src/render/filament/support/renderable_manager.cc @@ -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(¶ms); - 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(¶ms); + + 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: diff --git a/src/render/filament/support/renderable_manager.h b/src/render/filament/support/renderable_manager.h index 2958d559..1f170756 100644 --- a/src/render/filament/support/renderable_manager.h +++ b/src/render/filament/support/renderable_manager.h @@ -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 #include @@ -98,10 +98,10 @@ class RenderableManager { std::vector> geoms_; std::vector> sites_; - std::vector> flexes_; std::vector> skins_; - std::unordered_map, 2>> slider_cranks_; + std::vector>> flexes_; std::vector>> tendons_; + std::unordered_map, 2>> slider_cranks_; std::vector> flex_meshes_; std::vector> 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_