Split SceneObjects out from ModelObjects.

PiperOrigin-RevId: 930960334
Change-Id: I7958a8240a90e2eb7fb87e58e5bea561459a7695
This commit is contained in:
Haroon Qureshi
2026-06-11 23:35:40 -07:00
committed by Copybara-Service
parent 8d42473ed0
commit a37389ccfb
9 changed files with 261 additions and 153 deletions
+2
View File
@@ -29,6 +29,8 @@ target_sources(${MUJOCO_FILAMENT_EXPERIMENTAL_TARGET_NAME}
compat/scene_bridge.h
compat/scene_geom_util.cc
compat/scene_geom_util.h
compat/scene_objects.cc
compat/scene_objects.h
)
if(MUJOCO_USE_FILAMENT_MJR_COMPAT)
target_sources(${MUJOCO_FILAMENT_EXPERIMENTAL_TARGET_NAME}
@@ -16,11 +16,9 @@
#include <algorithm>
#include <cfloat>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <memory>
#include <span>
#include <utility>
#include <vector>
@@ -331,77 +329,6 @@ static int GetNumVertices(const mjModel* model, int id, MeshType mesh_type) {
}
}
static std::span<const float> GetPositions(const mjModel* model,
const mjvScene* scene,
const mjvGeom& geom) {
if (geom.type == mjGEOM_FLEX) {
const int num = 9 * scene->flexfaceused[geom.objid];
const int addr = scene->flexfaceadr[geom.objid];
const float* ptr = scene->flexface + (9 * addr);
return {ptr, static_cast<size_t>(num)};
} else {
const int num = 3 * scene->skinvertnum[geom.objid];
const int addr = scene->skinvertadr[geom.objid];
const float* ptr = scene->skinvert + (3 * addr);
return {ptr, static_cast<size_t>(num)};
}
}
static std::span<const float> GetNormals(const mjModel* model,
const mjvScene* scene,
const mjvGeom& geom) {
if (geom.type == mjGEOM_FLEX) {
const int num = 9 * scene->flexfaceused[geom.objid];
const int addr = scene->flexfaceadr[geom.objid];
const float* ptr = scene->flexnormal + (9 * addr);
return {ptr, static_cast<size_t>(num)};
} else {
const int num = 3 * scene->skinvertnum[geom.objid];
const int addr = scene->skinvertadr[geom.objid];
const float* ptr = scene->skinnormal + (3 * addr);
return {ptr, static_cast<size_t>(num)};
}
}
static std::span<const float> GetUvs(const mjModel* model,
const mjvScene* scene,
const mjvGeom& geom) {
if (geom.type == mjGEOM_FLEX) {
if (geom.texcoord && geom.matid >= 0) {
const int num = 6 * scene->flexfaceused[geom.objid];
const int addr = scene->flexfaceadr[geom.objid];
const float* ptr = scene->flextexcoord + (6 * addr);
return {ptr, static_cast<size_t>(num)};
} else {
const float* ptr = nullptr;
return {ptr, 0};
}
} else {
if (model->skin_texcoordadr[geom.objid] >= 0) {
const int num = 3 * scene->skinvertnum[geom.objid];
const int addr = model->skin_texcoordadr[geom.objid];
const float* ptr = model->skin_texcoord + (2 * addr);
return {ptr, static_cast<size_t>(num)};
} else {
const float* ptr = nullptr;
return {ptr, 0};
}
}
}
static std::span<const int> GetIndices(const mjModel* model,
const mjvScene* scene,
const mjvGeom& geom) {
if (geom.type == mjGEOM_FLEX) {
const int* ptr = nullptr;
return {ptr, 0};
} else {
const int num = 3 * model->skin_facenum[geom.objid];
const int* ptr = model->skin_face + 3 * model->skin_faceadr[geom.objid];
return {ptr, static_cast<size_t>(num)};
}
}
static void UpdateMeshData(mjrfMeshData* data, const mjModel* model, int id,
MeshType mesh_type) {
if (!IsValidIndex(model, id, mesh_type)) {
@@ -457,38 +384,6 @@ static void UpdateMeshData(mjrfMeshData* data, const mjModel* model, int id,
data->bounds_max[2] = builder->bounds_max.z;
}
void UpdateSkinFlexMeshData(mjrfMeshData* data, const mjModel* model,
const mjvScene* scene, const mjvGeom& geom) {
auto positions = GetPositions(model, scene, geom);
auto normals = GetNormals(model, scene, geom);
auto uvs = GetUvs(model, scene, geom);
auto indices = GetIndices(model, scene, geom);
int num_indices = indices.size();
if (num_indices == 0 && geom.type == mjGEOM_FLEX) {
num_indices = 3 * scene->flexfaceused[geom.objid];
}
data->nattributes = uvs.data() ? 3 : 2;
data->attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
data->attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
data->attributes[0].bytes = positions.data();
data->attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_NORMAL;
data->attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
data->attributes[1].bytes = normals.data();
data->attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
data->attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
data->attributes[2].bytes = uvs.data();
data->nvertices = positions.size() / 3;
data->nindices = num_indices;
data->indices = indices.data();
data->index_type = mjINDEX_TYPE_U32;
data->primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
data->compute_bounds = true;
data->release = nullptr;
data->user_data = nullptr;
}
ModelObjects::ModelObjects(const mjModel* model, mjrfContext* ctx)
: model_(model), ctx_(ctx) {
@@ -595,19 +490,6 @@ void ModelObjects::UploadHeightField(const mjModel* model, int id) {
height_fields_.insert_or_assign(id, CreateMesh(ctx_, data));
}
void ModelObjects::CreateSkinFlexMesh(const mjvScene* scene, const mjvGeom& geom) {
mjrfMeshData data;
mjrf_defaultMeshData(&data);
UpdateSkinFlexMeshData(&data, model_, scene, geom);
if (geom.type == mjGEOM_FLEX) {
flexes_.insert_or_assign(geom.objid, CreateMesh(ctx_, data));
} else if (geom.type == mjGEOM_SKIN) {
skins_.insert_or_assign(geom.objid, CreateMesh(ctx_, data));
} else {
mju_error("Unsupported dynamic mesh type: %d", geom.type);
}
}
const mjrfMesh* ModelObjects::GetMesh(int data_id) const {
// As defined by mjv_updateScene:
// original mesh: mesh_id * 2
@@ -630,22 +512,6 @@ const mjrfMesh* ModelObjects::GetHeightField(int hfield_id) const {
return nullptr;
}
const mjrfMesh* ModelObjects::GetFlexMesh(int geom_id) const {
if (auto it = flexes_.find(geom_id); it != flexes_.end()) {
return it->second.get();
}
mju_error("Unknown flex mesh %d", geom_id);
return nullptr;
}
const mjrfMesh* ModelObjects::GetSkinMesh(int geom_id) const {
if (auto it = skins_.find(geom_id); it != skins_.end()) {
return it->second.get();
}
mju_error("Unknown skin mesh %d", geom_id);
return nullptr;
}
const mjrfTexture* ModelObjects::GetTexture(int tex_id) const {
if (auto it = textures_.find(tex_id); it != textures_.end()) {
return it->second.get();
@@ -24,33 +24,46 @@
namespace mujoco {
// Creates and owns various filament objects based on the mjModel.
// Creates and owns meshes and textures read from an mjModel.
class ModelObjects {
public:
ModelObjects(const mjModel* model, mjrfContext* ctx);
// Uploads a new mesh from the model with the given id.
void UploadMesh(const mjModel* model, int id);
// Uploads a new texture from the model with the given id.
void UploadTexture(const mjModel* model, int id);
// Uploads a new height field from the model with the given id.
void UploadHeightField(const mjModel* model, int id);
void CreateSkinFlexMesh(const mjvScene* scene, const mjvGeom& geom);
// Returns the cached instance of a filament object created from the mjModel.
// Returns the mjModel mesh with the given data_id. The data_id is the
// mesh_id * 2 of the mesh in the mjModel.
const mjrfMesh* GetMesh(int data_id) const;
// Returns the mjModel convex hulll mesh with the given data_id. The data_id
// is the (mesh_id * 2) + 1 of the mesh in the mjModel.
const mjrfMesh* GetConvexHull(int data_id) const;
// Returns the mjModel height field mesh with the given id.
const mjrfMesh* GetHeightField(int hfield_id) const;
const mjrfMesh* GetSkinMesh(int geom_id) const;
const mjrfMesh* GetFlexMesh(int geom_id) const;
// Returns the mjModel texture with the given id.
const mjrfTexture* GetTexture(int tex_id) const;
// Returns the skybox texture in the mjModel.
const mjrfTexture* GetSkyboxTexture() const;
// Returns the mjModel from which the Model Objects are created.
const mjModel* GetModel() const { return model_; }
// Returns the multipliers used for mapping legacy material properties to
// filament material properties.
float GetSpecularMultiplier() const { return specular_multiplier_; }
float GetShininessMultiplier() const { return shininess_multiplier_; }
float GetEmissiveMultiplier() const { return emissive_multiplier_; }
const mjModel* GetModel() const { return model_; }
ModelObjects(const ModelObjects&) = delete;
ModelObjects& operator=(const ModelObjects&) = delete;
@@ -60,8 +73,6 @@ class ModelObjects {
std::unordered_map<int, UniquePtr<mjrfMesh>> meshes_;
std::unordered_map<int, UniquePtr<mjrfMesh>> convex_hulls_;
std::unordered_map<int, UniquePtr<mjrfMesh>> height_fields_;
std::unordered_map<int, UniquePtr<mjrfMesh>> skins_;
std::unordered_map<int, UniquePtr<mjrfMesh>> flexes_;
std::unordered_map<int, UniquePtr<mjrfTexture>> textures_;
float specular_multiplier_ = 0.2f;
float shininess_multiplier_ = 0.1f;
@@ -28,6 +28,7 @@
#include "experimental/filament/compat/light_manager.h"
#include "experimental/filament/compat/model_objects.h"
#include "experimental/filament/compat/scene_geom_util.h"
#include "experimental/filament/compat/scene_objects.h"
#include "render/filament/mjrfilament.h"
#include "render/filament/mjrfilament_cpp.h"
#include "render/filament/support/filament_util.h"
@@ -45,6 +46,7 @@ SceneBridge::SceneBridge(mjrfContext* ctx, const mjModel* model)
mjrf_defaultSceneParams(&params);
scene_ = CreateScene(ctx_, params);
model_objects_ = std::make_unique<ModelObjects>(model, ctx_);
scene_objects_ = std::make_unique<SceneObjects>(ctx_);
mjrf_configureSceneFromModel(scene_.get(), model);
@@ -124,11 +126,12 @@ void SceneBridge::Update(const mjrRect& viewport, const mjvScene* scene) {
}
if (geom->type == mjGEOM_FLEX || geom->type == mjGEOM_SKIN) {
model_objects_->CreateSkinFlexMesh(scene, *geom);
scene_objects_->CreateSkinFlexMesh(scene, model_objects_->GetModel(),
*geom);
}
UniquePtr<mjrfRenderable> renderable =
CreateGeomRenderable(*geom, ctx_, model_objects_.get(), scene->flags);
UniquePtr<mjrfRenderable> renderable = CreateGeomRenderable(
*geom, ctx_, model_objects_.get(), scene_objects_.get(), scene->flags);
mjrf_addRenderableToScene(scene_.get(), renderable.get());
renderables_.push_back(std::move(renderable));
@@ -26,6 +26,7 @@
#include <mujoco/mujoco.h>
#include "experimental/filament/compat/light_manager.h"
#include "experimental/filament/compat/model_objects.h"
#include "experimental/filament/compat/scene_objects.h"
#include "render/filament/mjrfilament.h"
#include "render/filament/mjrfilament_cpp.h"
@@ -64,6 +65,7 @@ class SceneBridge {
mjrfContext* ctx_ = nullptr;
std::unique_ptr<ModelObjects> model_objects_;
std::unique_ptr<SceneObjects> scene_objects_;
std::unique_ptr<LightManager> light_manager_;
mjrCamera camera_;
DrawTextAtFn draw_text_callback_;
@@ -22,6 +22,7 @@
#include <mujoco/mjvisualize.h>
#include <mujoco/mujoco.h>
#include "experimental/filament/compat/model_objects.h"
#include "experimental/filament/compat/scene_objects.h"
#include "render/filament/mjrfilament.h"
#include "render/filament/mjrfilament_cpp.h"
@@ -41,7 +42,8 @@ static float GetPlaneTileSize(const mjModel* model, int matid,
}
static void PrepareGeomMeshes(mjrfRenderable* renderable, const mjvGeom& geom,
ModelObjects* model_objs) {
ModelObjects* model_objs,
SceneObjects* scene_objs) {
const mjModel* model = model_objs->GetModel();
const int nstack = model->vis.quality.numstacks;
const int nslice = model->vis.quality.numslices;
@@ -113,7 +115,7 @@ static void PrepareGeomMeshes(mjrfRenderable* renderable, const mjvGeom& geom,
mjrf_setRenderableSize(renderable, geom.size);
break;
case mjGEOM_FLEX:
mjrf_setRenderableMesh(renderable, model_objs->GetFlexMesh(geom.objid), 0, 0);
mjrf_setRenderableMesh(renderable, scene_objs->GetFlexMesh(geom.objid), 0, 0);
// Flexes are defined in global space.
std::memset(position, 0, sizeof(position));
std::memset(rotation, 0, sizeof(rotation));
@@ -122,7 +124,7 @@ static void PrepareGeomMeshes(mjrfRenderable* renderable, const mjvGeom& geom,
rotation[8] = 1.f;
break;
case mjGEOM_SKIN:
mjrf_setRenderableMesh(renderable, model_objs->GetSkinMesh(geom.objid), 0, 0);
mjrf_setRenderableMesh(renderable, scene_objs->GetSkinMesh(geom.objid), 0, 0);
// Skins are defined in global space.
std::memset(position, 0, sizeof(position));
std::memset(rotation, 0, sizeof(rotation));
@@ -271,11 +273,11 @@ static void UpdateGeomMaterial(mjrfRenderable* renderable, const mjvGeom& geom,
UniquePtr<mjrfRenderable> CreateGeomRenderable(
const mjvGeom& geom, mjrfContext* ctx, ModelObjects* model_objs,
const mjtByte render_flags[mjNRNDFLAG]) {
SceneObjects* scene_objs, const mjtByte render_flags[mjNRNDFLAG]) {
mjrfRenderableParams params;
mjrf_defaultRenderableParams(&params);
auto renderable = CreateRenderable(ctx, params);
PrepareGeomMeshes(renderable.get(), geom, model_objs);
PrepareGeomMeshes(renderable.get(), geom, model_objs, scene_objs);
UpdateGeomMaterial(renderable.get(), geom, model_objs, render_flags);
return renderable;
}
@@ -18,6 +18,7 @@
#include <mujoco/mjvisualize.h>
#include <mujoco/mujoco.h>
#include "experimental/filament/compat/model_objects.h"
#include "experimental/filament/compat/scene_objects.h"
#include "render/filament/mjrfilament.h"
#include "render/filament/mjrfilament_cpp.h"
@@ -26,7 +27,7 @@ namespace mujoco {
// Creates a Renderable from the given mjvGeom.
UniquePtr<mjrfRenderable> CreateGeomRenderable(
const mjvGeom& geom, mjrfContext* ctx, ModelObjects* model_objs,
const mjtByte render_flags[mjNRNDFLAG]);
SceneObjects* scene_objs, const mjtByte render_flags[mjNRNDFLAG]);
} // namespace mujoco
@@ -0,0 +1,170 @@
// 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 "experimental/filament/compat/scene_objects.h"
#include <cstddef>
#include <memory>
#include <span>
#include <utility>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <mujoco/mujoco.h>
#include "render/filament/mjrfilament.h"
#include "render/filament/mjrfilament_cpp.h"
namespace mujoco {
using filament::math::float2;
using filament::math::float3;
using filament::math::float4;
static std::span<const float> GetPositions(const mjModel* model,
const mjvScene* scene,
const mjvGeom& geom) {
if (geom.type == mjGEOM_FLEX) {
const int num = 9 * scene->flexfaceused[geom.objid];
const int addr = scene->flexfaceadr[geom.objid];
const float* ptr = scene->flexface + (9 * addr);
return {ptr, static_cast<size_t>(num)};
} else {
const int num = 3 * scene->skinvertnum[geom.objid];
const int addr = scene->skinvertadr[geom.objid];
const float* ptr = scene->skinvert + (3 * addr);
return {ptr, static_cast<size_t>(num)};
}
}
static std::span<const float> GetNormals(const mjModel* model,
const mjvScene* scene,
const mjvGeom& geom) {
if (geom.type == mjGEOM_FLEX) {
const int num = 9 * scene->flexfaceused[geom.objid];
const int addr = scene->flexfaceadr[geom.objid];
const float* ptr = scene->flexnormal + (9 * addr);
return {ptr, static_cast<size_t>(num)};
} else {
const int num = 3 * scene->skinvertnum[geom.objid];
const int addr = scene->skinvertadr[geom.objid];
const float* ptr = scene->skinnormal + (3 * addr);
return {ptr, static_cast<size_t>(num)};
}
}
static std::span<const float> GetUvs(const mjModel* model,
const mjvScene* scene,
const mjvGeom& geom) {
if (geom.type == mjGEOM_FLEX) {
if (geom.texcoord && geom.matid >= 0) {
const int num = 6 * scene->flexfaceused[geom.objid];
const int addr = scene->flexfaceadr[geom.objid];
const float* ptr = scene->flextexcoord + (6 * addr);
return {ptr, static_cast<size_t>(num)};
} else {
const float* ptr = nullptr;
return {ptr, 0};
}
} else {
if (model->skin_texcoordadr[geom.objid] >= 0) {
const int num = 3 * scene->skinvertnum[geom.objid];
const int addr = model->skin_texcoordadr[geom.objid];
const float* ptr = model->skin_texcoord + (2 * addr);
return {ptr, static_cast<size_t>(num)};
} else {
const float* ptr = nullptr;
return {ptr, 0};
}
}
}
static std::span<const int> GetIndices(const mjModel* model,
const mjvScene* scene,
const mjvGeom& geom) {
if (geom.type == mjGEOM_FLEX) {
const int* ptr = nullptr;
return {ptr, 0};
} else {
const int num = 3 * model->skin_facenum[geom.objid];
const int* ptr = model->skin_face + 3 * model->skin_faceadr[geom.objid];
return {ptr, static_cast<size_t>(num)};
}
}
static void UpdateSkinFlexMeshData(mjrfMeshData* data, const mjModel* model,
const mjvScene* scene, const mjvGeom& geom) {
auto positions = GetPositions(model, scene, geom);
auto normals = GetNormals(model, scene, geom);
auto uvs = GetUvs(model, scene, geom);
auto indices = GetIndices(model, scene, geom);
int num_indices = indices.size();
if (num_indices == 0 && geom.type == mjGEOM_FLEX) {
num_indices = 3 * scene->flexfaceused[geom.objid];
}
data->nattributes = uvs.data() ? 3 : 2;
data->attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
data->attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
data->attributes[0].bytes = positions.data();
data->attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_NORMAL;
data->attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
data->attributes[1].bytes = normals.data();
data->attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
data->attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
data->attributes[2].bytes = uvs.data();
data->nvertices = positions.size() / 3;
data->nindices = num_indices;
data->indices = indices.data();
data->index_type = mjINDEX_TYPE_U32;
data->primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
data->compute_bounds = true;
data->release = nullptr;
data->user_data = nullptr;
}
SceneObjects::SceneObjects(mjrfContext* ctx) : ctx_(ctx) {}
void SceneObjects::CreateSkinFlexMesh(const mjvScene* scene,
const mjModel* model,
const mjvGeom& geom) {
mjrfMeshData data;
mjrf_defaultMeshData(&data);
UpdateSkinFlexMeshData(&data, model, scene, geom);
if (geom.type == mjGEOM_FLEX) {
flexes_.insert_or_assign(geom.objid, CreateMesh(ctx_, data));
} else if (geom.type == mjGEOM_SKIN) {
skins_.insert_or_assign(geom.objid, CreateMesh(ctx_, data));
} else {
mju_error("Unsupported dynamic mesh type: %d", geom.type);
}
}
const mjrfMesh* SceneObjects::GetFlexMesh(int geom_id) const {
if (auto it = flexes_.find(geom_id); it != flexes_.end()) {
return it->second.get();
}
mju_error("Unknown flex mesh %d", geom_id);
return nullptr;
}
const mjrfMesh* SceneObjects::GetSkinMesh(int geom_id) const {
if (auto it = skins_.find(geom_id); it != skins_.end()) {
return it->second.get();
}
mju_error("Unknown skin mesh %d", geom_id);
return nullptr;
}
} // namespace mujoco
@@ -0,0 +1,51 @@
// 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_SCENE_OBJECTS_H_
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_OBJECTS_H_
#include <unordered_map>
#include <mujoco/mjmodel.h>
#include <mujoco/mujoco.h>
#include "render/filament/mjrfilament.h"
#include "render/filament/mjrfilament_cpp.h"
namespace mujoco {
// Creates and owns meshes read from an mjvScene.
class SceneObjects {
public:
explicit SceneObjects(mjrfContext* ctx);
// Creates a skin or flex mesh from the given geom in the mjvScene.
void CreateSkinFlexMesh(const mjvScene* scene, const mjModel* model,
const mjvGeom& geom);
// Returns the mesh for the given geom id, as created by CreateSkinFlexMesh.
const mjrfMesh* GetSkinMesh(int geom_id) const;
const mjrfMesh* GetFlexMesh(int geom_id) const;
SceneObjects(const SceneObjects&) = delete;
SceneObjects& operator=(const SceneObjects&) = delete;
private:
mjrfContext* ctx_ = nullptr;
std::unordered_map<int, UniquePtr<mjrfMesh>> skins_;
std::unordered_map<int, UniquePtr<mjrfMesh>> flexes_;
};
} // namespace mujoco
#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_OBJECTS_H_