Allow mesh data to be reuploaded without having to recreate the mesh.

Splits mjrfMeshData into config+data parts.

PiperOrigin-RevId: 952042545
Change-Id: I19a572a0f591fa0d33bbc2c32b974efc075310f4
This commit is contained in:
Haroon Qureshi
2026-07-22 04:55:59 -07:00
committed by Copybara-Service
parent d3166cb630
commit ba9a65031b
19 changed files with 469 additions and 321 deletions
+11 -1
View File
@@ -1452,12 +1452,22 @@ culling. Alternatively, the bounds can be computed at runtime (though there is a
are provided (or calculated), then frustum culling will not be performed.
.. _mjrfMeshConfig:
mjrfMeshConfig
~~~~~~~~~~~~~~~~~
Parameters for creating a :ref:`mesh<mjrfMesh>`.
.. mujoco-include:: mjrfMeshConfig
.. _mjrfMeshData:
mjrfMeshData
~~~~~~~~~~~~
Binary data used for creating a :ref:`mesh<mjrfMesh>`.
Binary data for a :ref:`mesh<mjrfMesh>`.
.. mujoco-include:: mjrfMeshData
+24 -6
View File
@@ -3249,14 +3249,14 @@ Returns the height of the texture.
Returns the sampler type (mjrSamplerType) used by the texture.
[returns: mjrSamplerType]
.. _mjrf_defaultMeshData:
.. _mjrf_defaultMeshConfig:
`mjrf_defaultMeshData <#mjrf_defaultMeshData>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`mjrf_defaultMeshConfig <#mjrf_defaultMeshConfig>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mjrf_defaultMeshData
.. mujoco-include:: mjrf_defaultMeshConfig
Initializes the mjrfMeshData to default values.
Initializes the mjrfMeshConfig to default values.
.. _mjrf_createMesh:
@@ -3265,7 +3265,7 @@ Initializes the mjrfMeshData to default values.
.. mujoco-include:: mjrf_createMesh
Creates a mesh with the given data.
Creates an empty mesh with the given config.
.. _mjrf_destroyMesh:
@@ -3276,6 +3276,24 @@ Creates a mesh with the given data.
Destroys the mesh.
.. _mjrf_defaultMeshData:
`mjrf_defaultMeshData <#mjrf_defaultMeshData>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mjrf_defaultMeshData
Initializes the mjrfMeshData to default values.
.. _mjrf_setMeshData:
`mjrf_setMeshData <#mjrf_setMeshData>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mjrf_setMeshData
Uploads the given mesh data to the mesh.
.. _mjrf_defaultSceneParams:
`mjrf_defaultSceneParams <#mjrf_defaultSceneParams>`__
+3
View File
@@ -66,6 +66,9 @@ Engine
- :ref:`mju_round` now breaks ties away from zero rather than towards :math:`+\infty`. This only affects
negative half-integers, e.g. ``mju_round(-2.5)`` now returns -3 rather than -2.
- Removed unneeded `mjvScene` argument from :ref:`mjv_moveCamera`.
- Split up :ref:`mjrfMeshData` into `mjrfMeshData` and `mjrfMeshConfig` to allow reuploading of mesh data without
having to recreate the mesh object. Introduces :ref:`mjrfDefaultMeshConfig` and :ref:`mjrfSetMeshData` functions.
- Removed `bytes` field from :ref:`mjrVertexAttribute`.
.. admonition:: Breaking ABI changes
:class: caution
+14 -8
View File
@@ -1425,7 +1425,6 @@ typedef struct mjrRendererInfo_ { // active renderer identity
const char* backend; // graphics backend: opengl, vulkan; empty if uninitialized
} mjrRendererInfo;
typedef struct mjrVertexAttribute_ { // vertex attribute format specification
const void* bytes; // vertex data
int usage; // position, normal, etc [mjrVertexAttributeUsage]
int type; // float3, ubyte4, etc. [mjrVertexAttributeType]
} mjrVertexAttribute;
@@ -1580,15 +1579,20 @@ typedef struct mjrfTextureData_ {
mjrfCallback release; // callback when data has finished uploading
void* user_data; // user data for release callback
} mjrfTextureData;
typedef struct mjrfMeshData_ {
mjtSize num_vertices; // number of vertices; all vertex attributes share this size
int num_attributes; // number of attributes defined
typedef struct mjrfMeshConfig_ {
mjtSize max_vertices; // maximum number of vertices
mjtSize max_indices; // maximum number of indices
int num_attributes; // number of defined attributes
mjrVertexAttribute attributes[mjMAX_VERTEX_ATTRIBUTES]; // per-vertex attribute information
mjtBool interleaved; // true if vertex attributes are interleaved
mjtSize num_indices; // number of indices
const void* indices; // indices data array
int index_type; // index data format (e.g. UINT16 or UINT32) [mjrIndexType]
int primitive_type; // index interpretation (e.g. TRIANGLES, etc.) [mjrMeshPrimitiveType]
} mjrfMeshConfig;
typedef struct mjrfMeshData_ {
mjtSize num_vertices; // number of vertices
const void* vertices[mjMAX_VERTEX_ATTRIBUTES]; // per-vertex attribute data arrays
mjtSize num_indices; // number of indices
const void* indices; // indices data array
mjtBool compute_bounds; // if true, compute bounds from vertex positions
float bounds_min[3]; // min/max bounds; assume unset if bounds_min == bounds_max
float bounds_max[3];
@@ -3422,9 +3426,11 @@ void mjrf_setTextureData(mjrfTexture* texture, const mjrfTextureData* data);
int mjrf_getTextureWidth(const mjrfTexture* texture);
int mjrf_getTextureHeight(const mjrfTexture* texture);
int mjrf_getTextureSamplerType(const mjrfTexture* texture);
void mjrf_defaultMeshData(mjrfMeshData* data);
mjrfMesh* mjrf_createMesh(mjrfContext* ctx, const mjrfMeshData* data);
void mjrf_defaultMeshConfig(mjrfMeshConfig* config);
mjrfMesh* mjrf_createMesh(mjrfContext* ctx, const mjrfMeshConfig* config);
void mjrf_destroyMesh(mjrfMesh* mesh);
void mjrf_defaultMeshData(mjrfMeshData* data);
void mjrf_setMeshData(mjrfMesh* mesh, const mjrfMeshData* data);
void mjrf_defaultSceneParams(mjrfSceneParams* params);
mjrfScene* mjrf_createScene(mjrfContext* ctx, const mjrfSceneParams* params);
void mjrf_destroyScene(mjrfScene* scene);
-1
View File
@@ -124,7 +124,6 @@ typedef struct mjrRendererInfo_ { // active renderer identity
typedef struct mjrVertexAttribute_ { // vertex attribute format specification
const void* bytes; // vertex data
int usage; // position, normal, etc [mjrVertexAttributeUsage]
int type; // float3, ubyte4, etc. [mjrVertexAttributeType]
} mjrVertexAttribute;
+24 -11
View File
@@ -182,16 +182,32 @@ int mjrf_getTextureSamplerType(const mjrfTexture* texture);
// Maximum number of vertex attributes in a mesh.
enum { mjMAX_VERTEX_ATTRIBUTES = 16 };
// Binary data used for creating a mesh (mjrfMesh).
typedef struct mjrfMeshData_ {
mjtSize num_vertices; // number of vertices; all vertex attributes share this size
int num_attributes; // number of attributes defined
// Parameters describing a mesh (mjrfMesh).
typedef struct mjrfMeshConfig_ {
mjtSize max_vertices; // maximum number of vertices
mjtSize max_indices; // maximum number of indices
int num_attributes; // number of defined attributes
mjrVertexAttribute attributes[mjMAX_VERTEX_ATTRIBUTES]; // per-vertex attribute information
mjtBool interleaved; // true if vertex attributes are interleaved
mjtSize num_indices; // number of indices
const void* indices; // indices data array
int index_type; // index data format (e.g. UINT16 or UINT32) [mjrIndexType]
int primitive_type; // index interpretation (e.g. TRIANGLES, etc.) [mjrMeshPrimitiveType]
} mjrfMeshConfig;
// Initializes the mjrfMeshConfig to default values.
void mjrf_defaultMeshConfig(mjrfMeshConfig* config);
// Creates an empty mesh with the given config.
mjrfMesh* mjrf_createMesh(mjrfContext* ctx, const mjrfMeshConfig* config);
// Destroys the mesh.
void mjrf_destroyMesh(mjrfMesh* mesh);
// Binary data used for creating a mesh (mjrfMesh).
typedef struct mjrfMeshData_ {
mjtSize num_vertices; // number of vertices
const void* vertices[mjMAX_VERTEX_ATTRIBUTES]; // per-vertex attribute data arrays
mjtSize num_indices; // number of indices
const void* indices; // indices data array
mjtBool compute_bounds; // if true, compute bounds from vertex positions
float bounds_min[3]; // min/max bounds; assume unset if bounds_min == bounds_max
float bounds_max[3];
@@ -202,11 +218,8 @@ typedef struct mjrfMeshData_ {
// Initializes the mjrfMeshData to default values.
void mjrf_defaultMeshData(mjrfMeshData* data);
// Creates a mesh with the given data.
mjrfMesh* mjrf_createMesh(mjrfContext* ctx, const mjrfMeshData* data);
// Destroys the mesh.
void mjrf_destroyMesh(mjrfMesh* mesh);
// Uploads the given mesh data to the mesh.
void mjrf_setMeshData(mjrfMesh* mesh, const mjrfMeshData* data);
// Parameters for creating a scene (mjrfScene).
typedef struct mjrfSceneParams_ {
-7
View File
@@ -11332,13 +11332,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([
name='mjrVertexAttribute',
declname='struct mjrVertexAttribute_',
fields=(
StructFieldDecl(
name='bytes',
type=PointerType(
inner_type=ValueType(name='void', is_const=True),
),
doc='vertex data',
),
StructFieldDecl(
name='usage',
type=ValueType(name='int'),
+1 -1
View File
@@ -1078,7 +1078,7 @@ This is useful for example when the MJB is not available as a file on disk.)"));
py::class_<raw::MjrVertexAttribute> mjrVertexAttribute(m,
"MjrVertexAttribute");
mjrVertexAttribute.def(py::init([](int usage, int type) {
return raw::MjrVertexAttribute{nullptr, usage, type};
return raw::MjrVertexAttribute{usage, type};
}),
py::arg("usage") = 0, py::arg("type") = 0);
mjrVertexAttribute.def("__copy__", [](const raw::MjrVertexAttribute& other) {
@@ -243,27 +243,35 @@ void SceneDecorator::Update(mjData* data, const mjvOption* vis_option,
const float* uvs =
has_uvs ? mjv_scene_.flextexcoord + (6 * addr) : nullptr;
mjrfMeshConfig config;
mjrf_defaultMeshConfig(&config);
config.num_attributes = has_uvs ? 3 : 2;
config.max_vertices = 3 * mjv_scene_.flexfaceused[geom.objid];
config.max_indices = 3 * mjv_scene_.flexfaceused[geom.objid];
config.attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
config.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
config.attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_NORMAL;
config.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
config.attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
config.attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
config.index_type = mjINDEX_TYPE_U32;
config.primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
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.vertices[0] = positions;
data.vertices[1] = normals;
data.vertices[2] = 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 mesh = CreateMesh(ctx, config);
mjrf_setMeshData(mesh.get(), &data);
meshes_.push_back(std::move(mesh));
auto renderable = CreateRenderable(ctx, params);
mjrf_setRenderableMesh(renderable.get(), meshes_.back().get(), 0, 0);
@@ -103,8 +103,11 @@ static std::span<const int> GetIndices(const mjModel* model,
}
}
static bool UpdateSkinFlexMeshData(mjrfMeshData* data, const mjModel* model,
const mjvScene* scene, const mjvGeom& geom) {
SceneObjects::SceneObjects(mjrfContext* ctx) : ctx_(ctx) {}
bool SceneObjects::CreateSkinFlexMesh(const mjvScene* scene,
const mjModel* model,
const mjvGeom& geom) {
auto positions = GetPositions(model, scene, geom);
if (positions.empty()) {
return false;
@@ -119,41 +122,44 @@ static bool UpdateSkinFlexMeshData(mjrfMeshData* data, const mjModel* model,
num_indices = 3 * scene->flexfaceused[geom.objid];
}
data->num_attributes = 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->num_vertices = positions.size() / 3;
data->num_indices = 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;
return true;
}
mjrfMeshConfig config;
mjrf_defaultMeshConfig(&config);
config.num_attributes = uvs.data() ? 3 : 2;
config.attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
config.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
config.attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_NORMAL;
config.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
config.attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
config.attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
config.max_vertices = positions.size() / 3;
config.max_indices = num_indices;
config.index_type = mjINDEX_TYPE_U32;
config.primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
SceneObjects::SceneObjects(mjrfContext* ctx) : ctx_(ctx) {}
bool SceneObjects::CreateSkinFlexMesh(const mjvScene* scene,
const mjModel* model,
const mjvGeom& geom) {
mjrfMeshData data;
mjrf_defaultMeshData(&data);
if (!UpdateSkinFlexMeshData(&data, model, scene, geom)) {
return false;
}
data.vertices[0] = positions.data();
data.vertices[1] = normals.data();
data.vertices[2] = uvs.data();
data.num_vertices = positions.size() / 3;
data.num_indices = num_indices;
data.indices = indices.data();
data.compute_bounds = true;
data.release = nullptr;
data.user_data = nullptr;
if (geom.type == mjGEOM_FLEX) {
flexes_.insert_or_assign(geom.objid, CreateMesh(ctx_, data));
auto iter = flexes_.find(geom.objid);
if (iter == flexes_.end()) {
iter = flexes_.insert({geom.objid, CreateMesh(ctx_, config)}).first;
}
mjrf_setMeshData(iter->second.get(), &data);
} else if (geom.type == mjGEOM_SKIN) {
skins_.insert_or_assign(geom.objid, CreateMesh(ctx_, data));
auto iter = skins_.find(geom.objid);
if (iter == skins_.end()) {
iter = skins_.insert({geom.objid, CreateMesh(ctx_, config)}).first;
}
mjrf_setMeshData(iter->second.get(), &data);
} else {
mju_error("Unsupported dynamic mesh type: %d", geom.type);
}
+21 -16
View File
@@ -219,27 +219,32 @@ void ImguiBridge::Update() {
for (int n = 0; n < commands->CmdListsCount; ++n) {
const ImDrawList* cmds = commands->CmdLists[n];
mjrfMeshConfig config;
mjrf_defaultMeshConfig(&config);
config.num_attributes = 3;
config.attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
config.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
config.attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
config.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
config.attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_COLOR;
config.attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_UBYTE4;
config.max_vertices = cmds->VtxBuffer.Size;
config.max_indices = cmds->IdxBuffer.Size;
config.interleaved = true;
config.index_type = mjINDEX_TYPE_U16;
config.primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
meshes_.push_back(CreateMesh(ctx_, config));
mjrfMesh* mesh = meshes_.back().get();
mjrfMeshData data;
mjrf_defaultMeshData(&data);
data.num_attributes = 3;
data.attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
data.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
data.attributes[0].bytes = cmds->VtxBuffer.Data;
data.attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
data.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
data.attributes[1].bytes = cmds->VtxBuffer.Data + sizeof(float) * 2;
data.attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_COLOR;
data.attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_UBYTE4;
data.attributes[2].bytes = cmds->VtxBuffer.Data + sizeof(float) * 4;
data.interleaved = true;
data.num_vertices = cmds->VtxBuffer.Size;
data.vertices[0] = cmds->VtxBuffer.Data;
data.vertices[1] = cmds->VtxBuffer.Data + sizeof(float) * 2;
data.vertices[2] = cmds->VtxBuffer.Data + sizeof(float) * 4;
data.num_indices = cmds->IdxBuffer.Size;
data.indices = cmds->IdxBuffer.Data;
data.index_type = mjINDEX_TYPE_U16;
data.primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
meshes_.push_back(CreateMesh(ctx_, data));
const mjrfMesh* mesh = meshes_.back().get();
mjrf_setMeshData(mesh, &data);
int index_offset = 0;
for (const ImDrawCmd& command : cmds->CmdBuffer) {
+41 -30
View File
@@ -27,6 +27,7 @@
#include <math/vec3.h>
#include <math/vec4.h>
#include <mujoco/mjrfilament.h>
#include <mujoco/mujoco.h>
#include "render/filament/core/mesh.h"
#include "render/filament/support/filament_util.h"
@@ -59,59 +60,69 @@ static std::size_t NumIndicesPerSide(int num_quads_per_axis) {
return kNumIndicesPerQuad * num_quads_per_axis * num_quads_per_axis;
}
class BuiltinBuilder : public mjrfMeshData {
class BuiltinBuilder {
public:
BuiltinBuilder() { mjrf_defaultMeshData(this); }
BuiltinBuilder() {}
virtual ~BuiltinBuilder() = default;
template <typename T, typename... Args>
static std::unique_ptr<Mesh> Create(filament::Engine* engine,
Args&&... args) {
auto builder = new T(std::forward<Args>(args)...);
mjrfMeshData* mesh_data = builder->PrepareMeshData();
mesh_data->release = +[](void* user_data) {
mjrfMeshConfig config;
mjrf_defaultMeshConfig(&config);
config.max_vertices = builder->positions_.size();
config.max_indices = builder->indices_.size();
config.index_type = mjINDEX_TYPE_U16;
config.primitive_type = builder->primitive_type_;
config.num_attributes = 2;
config.attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
config.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
config.attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_TANGENTS;
config.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4;
mjrfMeshData data;
mjrf_defaultMeshData(&data);
data.num_vertices = builder->positions_.size();
data.vertices[0] = builder->positions_.data();
data.vertices[1] = builder->orientations_.data();
data.num_indices = builder->indices_.size();
data.indices = builder->indices_.data();
data.bounds_min[0] = builder->bounds_min_.x;
data.bounds_min[1] = builder->bounds_min_.y;
data.bounds_min[2] = builder->bounds_min_.z;
data.bounds_max[0] = builder->bounds_max_.x;
data.bounds_max[1] = builder->bounds_max_.y;
data.bounds_max[2] = builder->bounds_max_.z;
data.release = +[](void* user_data) {
delete static_cast<BuiltinBuilder*>(user_data);
};
mesh_data->user_data = builder;
return std::make_unique<Mesh>(engine, *mesh_data);
}
data.user_data = builder;
mjrfMeshData* PrepareMeshData() {
// Update the `mjrfMeshData` fields.
num_attributes = 2;
attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
attributes[0].bytes = reinterpret_cast<const void*>(positions_.data());
attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_TANGENTS;
attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4;
attributes[1].bytes = reinterpret_cast<const void*>(orientations_.data());
num_vertices = positions_.size();
indices = indices_.data();
num_indices = indices_.size();
index_type = mjINDEX_TYPE_U16;
return this;
auto mesh = std::make_unique<Mesh>(engine, config);
mesh->Upload(data);
return mesh;
}
protected:
void SetBounds(const float3& min, const float3& max) {
bounds_min[0] = min.x;
bounds_min[1] = min.y;
bounds_min[2] = min.z;
bounds_max[0] = max.x;
bounds_max[1] = max.y;
bounds_max[2] = max.z;
bounds_min_ = min;
bounds_max_ = max;
}
int primitive_type_ = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
std::vector<float3> positions_;
std::vector<float4> orientations_;
std::vector<uint16_t> indices_;
float3 bounds_min_ = {0, 0, 0};
float3 bounds_max_ = {0, 0, 0};
};
class LineBuilder : public BuiltinBuilder {
public:
LineBuilder() {
primitive_type = mjMESH_PRIMITIVE_TYPE_LINES;
primitive_type_ = mjMESH_PRIMITIVE_TYPE_LINES;
positions_.reserve(2);
positions_.emplace_back(0, 0, 0);
@@ -183,7 +194,7 @@ class TriangleBuilder : public BuiltinBuilder {
class LineBoxBuilder : public BuiltinBuilder {
public:
explicit LineBoxBuilder() {
primitive_type = mjMESH_PRIMITIVE_TYPE_LINES;
primitive_type_ = mjMESH_PRIMITIVE_TYPE_LINES;
positions_.reserve(8);
positions_.emplace_back(-1.0f, -1.0f, -1.0f);
+152 -126
View File
@@ -102,21 +102,43 @@ int FillSequence(std::byte* buffer, std::size_t num_bytes) {
return num;
}
Mesh::Mesh(filament::Engine* engine, const mjrfMeshData& data)
: engine_(engine), shared_state_(std::make_shared<SharedState>()) {
type_ = data.primitive_type == mjMESH_PRIMITIVE_TYPE_TRIANGLES
? filament::RenderableManager::PrimitiveType::TRIANGLES
: filament::RenderableManager::PrimitiveType::LINES;
// If the user has provided a release callback, then we need to ensure we
// call is when filament is done with the mesh data.
if (data.release) {
shared_state_->callbacks.push_back([=]() { data.release(data.user_data); });
Mesh::Mesh(filament::Engine* engine, const mjrfMeshConfig& config)
: engine_(engine),
config_(config),
shared_state_(std::make_shared<SharedState>()) {
// Perform some validation on the config.
const mjrVertexAttribute* positions = nullptr;
const mjrVertexAttribute* normals = nullptr;
const mjrVertexAttribute* tangents = nullptr;
for (int i = 0; i < config_.num_attributes; ++i) {
if (config_.attributes[i].usage == mjVERTEX_ATTRIBUTE_USAGE_POSITION) {
positions = &config_.attributes[i];
} else if (config_.attributes[i].usage == mjVERTEX_ATTRIBUTE_USAGE_NORMAL) {
normals = &config_.attributes[i];
} else if (config_.attributes[i].usage == mjVERTEX_ATTRIBUTE_USAGE_TANGENTS) {
tangents = &config_.attributes[i];
}
}
if (config_.max_vertices == 0) {
mju_error("Mesh has no vertices.");
}
if (!positions) {
mju_error("Mesh has no positions.");
}
if (config_.attributes[0].usage != mjVERTEX_ATTRIBUTE_USAGE_POSITION) {
mju_error("Positions must be the first attribute.");
}
if (normals && tangents) {
mju_error("Mesh has both normals and tangents.");
}
if (normals && config_.interleaved) {
// We need to build orientations from normals and so we require each
// attribute to be in a separate buffer.
mju_error("Cannot support normals with interleaved vertex attributes.");
}
BuildVertexBuffer(data);
BuildIndexBuffer(data);
UpdateBounds(data);
InitVertexBuffer();
InitIndexBuffer();
}
Mesh::~Mesh() {
@@ -129,9 +151,84 @@ Mesh::~Mesh() {
}
}
void Mesh::BuildVertexBuffer(const mjrfMeshData& data) {
if (data.num_vertices == 0) {
mju_error("mjrfMeshData has no vertices.");
void Mesh::Upload(const mjrfMeshData& data) {
// If the user has provided a release callback, then we need to ensure we
// call is when filament is done with the mesh data.
if (data.release) {
shared_state_->callbacks.push_back([=]() { data.release(data.user_data); });
}
UpdateVertexBuffer(data);
UpdateIndexBuffer(data);
UpdateBounds(data);
}
void Mesh::InitVertexBuffer() {
filament::VertexBuffer::Builder vb_builder;
vb_builder.vertexCount(config_.max_vertices);
if (config_.interleaved) {
// For an interleaved vertex buffer, we will create a single buffer which
// contains the data in the order specified by the attributes array,
// starting from the first attribute's payload.
vb_builder.bufferCount(1);
int total_vertex_size = 0;
for (int i = 0; i < config_.num_attributes; ++i) {
total_vertex_size += VertexAttributeTypeSize(config_.attributes[i]);
}
// We assume the buffer is tightly packed with no padding between
// attributes. As such, the stride is equal to the total vertex size and
// each offset is the sum of the sizes of the preceding attributes.
int offset = 0;
for (int i = 0; i < config_.num_attributes; ++i) {
const mjrVertexAttribute& attrib = config_.attributes[i];
const filament::VertexAttribute usage = GetUsage(attrib);
filament::VertexBuffer::AttributeType type = GetType(attrib);
vb_builder.attribute(usage, 0, type, offset, total_vertex_size);
if (usage == filament::VertexAttribute::COLOR) {
vb_builder.normalized(usage);
}
offset += VertexAttributeTypeSize(attrib);
}
vertex_buffer_ = vb_builder.build(*engine_);
} else {
// For a non-interleaved vertex buffer, we assign a separate buffer to each
// attribute.
vb_builder.bufferCount(config_.num_attributes);
for (int i = 0; i < config_.num_attributes; ++i) {
const mjrVertexAttribute& attrib = config_.attributes[i];
const filament::VertexAttribute usage = GetUsage(attrib);
filament::VertexBuffer::AttributeType type = GetType(attrib);
if (attrib.usage == mjVERTEX_ATTRIBUTE_USAGE_NORMAL) {
// We will replace normals with orientations.
type = filament::VertexBuffer::AttributeType::FLOAT4;
}
vb_builder.attribute(usage, i, type);
if (usage == filament::VertexAttribute::COLOR) {
vb_builder.normalized(usage);
}
}
vertex_buffer_ = vb_builder.build(*engine_);
}
}
void Mesh::InitIndexBuffer() {
if (config_.max_indices == 0) {
return;
}
filament::IndexBuffer::Builder ib_builder;
ib_builder.indexCount(config_.max_indices);
ib_builder.bufferType(config_.index_type == mjINDEX_TYPE_U16
? filament::IndexBuffer::IndexType::USHORT
: filament::IndexBuffer::IndexType::UINT);
index_buffer_ = ib_builder.build(*engine_);
}
void Mesh::UpdateVertexBuffer(const mjrfMeshData& data) {
if (config_.max_vertices != data.num_vertices) {
mju_error("Vertex count does not match config.");
}
// The filament BufferDescriptor callback for releasing the memory.
@@ -151,100 +248,27 @@ void Mesh::BuildVertexBuffer(const mjrfMeshData& data) {
}
};
// Pointers to specific attributes in the mesh data, used for additional
// validation and processing.
const mjrVertexAttribute* positions = nullptr;
const mjrVertexAttribute* normals = nullptr;
const mjrVertexAttribute* tangents = nullptr;
for (int i = 0; i < data.num_attributes; ++i) {
if (data.attributes[i].usage == mjVERTEX_ATTRIBUTE_USAGE_POSITION) {
positions = &data.attributes[i];
} else if (data.attributes[i].usage == mjVERTEX_ATTRIBUTE_USAGE_NORMAL) {
normals = &data.attributes[i];
} else if (data.attributes[i].usage == mjVERTEX_ATTRIBUTE_USAGE_TANGENTS) {
tangents = &data.attributes[i];
}
}
if (!positions) {
mju_error("mjrfMeshData has no positions.");
}
if (data.attributes[0].usage != mjVERTEX_ATTRIBUTE_USAGE_POSITION) {
mju_error("Positions must be the first attribute.");
}
if (normals && tangents) {
mju_error("mjrfMeshData has both normals and tangents.");
}
if (normals && data.interleaved) {
// We need to build orientations from normals and so we require each
// attribute to be in a separate buffer.
mju_error("Cannot support normals with interleaved vertex attributes.");
}
// Build the vertex buffer.
filament::VertexBuffer::Builder vb_builder;
vb_builder.vertexCount(data.num_vertices);
if (data.interleaved) {
// For an interleaved vertex buffer, we will create a single buffer which
// contains the data in the order specified by the attributes array,
// starting from the first attribute's payload.
vb_builder.bufferCount(1);
if (config_.interleaved) {
int total_vertex_size = 0;
for (int i = 0; i < data.num_attributes; ++i) {
total_vertex_size += VertexAttributeTypeSize(data.attributes[i]);
for (int i = 0; i < config_.num_attributes; ++i) {
total_vertex_size += VertexAttributeTypeSize(config_.attributes[i]);
}
const void* bytes = data.attributes[0].bytes;
const size_t nbytes = data.num_vertices * total_vertex_size;
// We assume the buffer is tightly packed with no padding between
// attributes. As such, the stride is equal to the total vertex size and
// each offset is the sum of the sizes of the preceding attributes.
int offset = 0;
for (int i = 0; i < data.num_attributes; ++i) {
const mjrVertexAttribute& attrib = data.attributes[i];
const filament::VertexAttribute usage = GetUsage(attrib);
filament::VertexBuffer::AttributeType type = GetType(attrib);
vb_builder.attribute(usage, 0, type, offset, total_vertex_size);
if (usage == filament::VertexAttribute::COLOR) {
vb_builder.normalized(usage);
}
offset += VertexAttributeTypeSize(attrib);
attributes_[i] = usage;
}
vertex_buffer_ = vb_builder.build(*engine_);
auto* user_data = new std::shared_ptr<SharedState>(shared_state_);
const void* bytes = data.vertices[0];
const size_t nbytes = data.num_vertices * total_vertex_size;
vertex_buffer_->setBufferAt(*engine_, 0,
{bytes, nbytes, callback, user_data});
} else {
// For a non-interleaved vertex buffer, we assign a separate buffer to each
// attribute.
vb_builder.bufferCount(data.num_attributes);
for (int i = 0; i < data.num_attributes; ++i) {
const mjrVertexAttribute& attrib = data.attributes[i];
const filament::VertexAttribute usage = GetUsage(attrib);
filament::VertexBuffer::AttributeType type = GetType(attrib);
if (attrib.usage == mjVERTEX_ATTRIBUTE_USAGE_NORMAL) {
// We will replace normals with orientations.
type = filament::VertexBuffer::AttributeType::FLOAT4;
}
vb_builder.attribute(usage, i, type);
if (usage == filament::VertexAttribute::COLOR) {
vb_builder.normalized(usage);
}
attributes_[i] = usage;
}
num_attributes_ = data.num_attributes;
vertex_buffer_ = vb_builder.build(*engine_);
// Assign the individual data buffers.
for (int i = 0; i < data.num_attributes; ++i) {
const mjrVertexAttribute& attrib = data.attributes[i];
const void* bytes = attrib.bytes;
for (int i = 0; i < config_.num_attributes; ++i) {
const mjrVertexAttribute& attrib = config_.attributes[i];
const void* bytes = data.vertices[i];
size_t nbytes = data.num_vertices * VertexAttributeTypeSize(attrib);
if (attrib.usage == mjVERTEX_ATTRIBUTE_USAGE_NORMAL) {
// Replace normals with orientations.
nbytes = data.num_vertices * sizeof(float4);
bytes = BuildOrientationsFromNormals(data.num_vertices, attrib);
bytes = BuildOrientationsFromNormals(data.num_vertices, bytes);
}
auto* user_data = new std::shared_ptr<SharedState>(shared_state_);
vertex_buffer_->setBufferAt(*engine_, i,
@@ -253,14 +277,17 @@ void Mesh::BuildVertexBuffer(const mjrfMeshData& data) {
}
}
void Mesh::BuildIndexBuffer(const mjrfMeshData& data) {
void Mesh::UpdateIndexBuffer(const mjrfMeshData& data) {
if (data.num_indices == 0) {
return;
}
if (data.num_indices != config_.max_indices) {
mju_error("Index count does not match config.");
}
const int element_size =
data.index_type == mjINDEX_TYPE_U16 ? sizeof(uint16_t) : sizeof(uint32_t);
const int num_bytes = data.num_indices * element_size;
config_.index_type == mjINDEX_TYPE_U16 ? sizeof(uint16_t) : sizeof(uint32_t);
const int num_bytes = config_.max_indices * element_size;
// If indices == 0 and num_indices > 0, then the user is specifying that the
// vertices are provided "in order", i.e. the indices are 0, 1, 2, 3, ...
@@ -270,37 +297,19 @@ void Mesh::BuildIndexBuffer(const mjrfMeshData& data) {
std::byte* sequence = new std::byte[num_bytes];
shared_state_->callbacks.push_back([=]() { delete[] sequence; });
if (data.index_type == mjINDEX_TYPE_U16) {
if (config_.index_type == mjINDEX_TYPE_U16) {
FillSequence<uint16_t>(sequence, num_bytes);
} else {
FillSequence<uint32_t>(sequence, num_bytes);
}
indices = sequence;
}
filament::IndexBuffer::Builder ib_builder;
ib_builder.indexCount(data.num_indices);
ib_builder.bufferType(data.index_type == mjINDEX_TYPE_U16
? filament::IndexBuffer::IndexType::USHORT
: filament::IndexBuffer::IndexType::UINT);
index_buffer_ = ib_builder.build(*engine_);
// We don't worry about setting a release callback here because the release
// callback for the vertex buffer will call release_callbacks_.
filament::backend::BufferDescriptor desc(indices, num_bytes);
index_buffer_->setBuffer(*engine_, std::move(desc));
}
float4* Mesh::BuildOrientationsFromNormals(int num_vertices,
const mjrVertexAttribute& normals) {
float4* orientations = new float4[num_vertices];
shared_state_->callbacks.push_back([=]() { delete[] orientations; });
const float* normals_ptr = reinterpret_cast<const float*>(normals.bytes);
for (int i = 0; i < num_vertices; ++i) {
orientations[i] = CalculateOrientation(ReadFloat3(normals_ptr, i));
}
return orientations;
}
void Mesh::UpdateBounds(const mjrfMeshData& data) {
float3 bounds_min = ReadFloat3(data.bounds_min);
float3 bounds_max = ReadFloat3(data.bounds_max);
@@ -310,11 +319,11 @@ void Mesh::UpdateBounds(const mjrfMeshData& data) {
bounds_min = float3(FLT_MAX, FLT_MAX, FLT_MAX);
bounds_max = float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
if (data.attributes[0].usage != mjVERTEX_ATTRIBUTE_USAGE_POSITION) {
if (config_.attributes[0].usage != mjVERTEX_ATTRIBUTE_USAGE_POSITION) {
mju_error("mjrfMeshData has no positions.");
}
const float* positions =
reinterpret_cast<const float*>(data.attributes[0].bytes);
reinterpret_cast<const float*>(data.vertices[0]);
for (int i = 0; i < data.num_vertices; ++i) {
const float3 position = ReadFloat3(positions, i);
@@ -336,6 +345,18 @@ void Mesh::ReleaseResources() {
}
}
float4* Mesh::BuildOrientationsFromNormals(int num_vertices,
const void* normals) {
float4* orientations = new float4[num_vertices];
shared_state_->callbacks.push_back([=]() { delete[] orientations; });
const float* normals_ptr = reinterpret_cast<const float*>(normals);
for (int i = 0; i < num_vertices; ++i) {
orientations[i] = CalculateOrientation(ReadFloat3(normals_ptr, i));
}
return orientations;
}
filament::IndexBuffer* Mesh::GetFilamentIndexBuffer() const {
return index_buffer_;
}
@@ -345,13 +366,18 @@ filament::VertexBuffer* Mesh::GetFilamentVertexBuffer() const {
}
filament::RenderableManager::PrimitiveType Mesh::GetPrimitiveType() const {
return type_;
return config_.primitive_type == mjMESH_PRIMITIVE_TYPE_TRIANGLES
? filament::RenderableManager::PrimitiveType::TRIANGLES
: filament::RenderableManager::PrimitiveType::LINES;
}
bool Mesh::HasVertexAttribute(mjrVertexAttributeUsage attrib) const {
auto fattrib = GetUsage(mjrVertexAttribute{.usage = attrib});
auto it = std::find(attributes_.begin(), attributes_.end(), fattrib);
return it != attributes_.end();
for (int i = 0; i < config_.num_attributes; ++i) {
if (config_.attributes[i].usage == attrib) {
return true;
}
}
return false;
}
bool Mesh::HasBounds() const { return bounds_.has_value(); }
+14 -9
View File
@@ -37,12 +37,15 @@ namespace mujoco {
class Mesh : public mjrfMesh {
public:
// Creates a Mesh from the given MeshData.
Mesh(filament::Engine* engine, const mjrfMeshData& data);
Mesh(filament::Engine* engine, const mjrfMeshConfig& config);
~Mesh();
Mesh(const Mesh&) = delete;
Mesh& operator=(const Mesh&) = delete;
// Uploads the given MeshData to the mesh.
void Upload(const mjrfMeshData& data);
// Returns the filament IndexBuffer for the mesh.
filament::IndexBuffer* GetFilamentIndexBuffer() const;
@@ -67,29 +70,31 @@ class Mesh : public mjrfMesh {
}
private:
void BuildVertexBuffer(const mjrfMeshData& data);
void BuildIndexBuffer(const mjrfMeshData& data);
void InitVertexBuffer();
void InitIndexBuffer();
void UpdateVertexBuffer(const mjrfMeshData& data);
void UpdateIndexBuffer(const mjrfMeshData& data);
void UpdateBounds(const mjrfMeshData& data);
filament::math::float4* BuildOrientationsFromNormals(
int num_vertices, const mjrVertexAttribute& normals);
filament::math::float4* BuildOrientationsFromNormals(int num_vertices,
const void* normals);
void ReleaseResources();
filament::Engine* engine_ = nullptr;
mjrfMeshConfig config_;
filament::IndexBuffer* index_buffer_ = nullptr;
filament::VertexBuffer* vertex_buffer_ = nullptr;
filament::RenderableManager::PrimitiveType type_ =
filament::RenderableManager::PrimitiveType::TRIANGLES;
std::optional<filament::Box> bounds_;
struct SharedState {
std::vector<std::function<void()>> callbacks;
std::mutex mutex;
bool called = false;
};
std::shared_ptr<SharedState> shared_state_;
std::array<filament::VertexAttribute, mjMAX_VERTEX_ATTRIBUTES> attributes_;
int num_attributes_ = 0;
};
} // namespace mujoco
+10 -2
View File
@@ -62,6 +62,10 @@ void mjrf_defaultTextureConfig(mjrfTextureConfig* config) {
memset(config, 0, sizeof(mjrfTextureConfig));
}
void mjrf_defaultMeshConfig(mjrfMeshConfig* config) {
memset(config, 0, sizeof(mjrfMeshConfig));
}
void mjrf_defaultMeshData(mjrfMeshData* data) {
memset(data, 0, sizeof(mjrfMeshData));
}
@@ -151,9 +155,9 @@ void mjrf_destroyTexture(mjrfTexture* texture) {
delete mujoco::Texture::downcast(texture);
}
mjrfMesh* mjrf_createMesh(mjrfContext* ctx, const mjrfMeshData* data) {
mjrfMesh* mjrf_createMesh(mjrfContext* ctx, const mjrfMeshConfig* config) {
return new mujoco::Mesh(mujoco::FilamentContext::downcast(ctx)->GetEngine(),
*data);
*config);
}
void mjrf_destroyMesh(mjrfMesh* mesh) { delete mujoco::Mesh::downcast(mesh); }
@@ -202,6 +206,10 @@ void mjrf_setTextureData(mjrfTexture* texture, const mjrfTextureData* data) {
mujoco::Texture::downcast(texture)->Upload(*data);
}
void mjrf_setMeshData(mjrfMesh* mesh, const mjrfMeshData* data) {
mujoco::Mesh::downcast(mesh)->Upload(*data);
}
int mjrf_getTextureWidth(const mjrfTexture* texture) {
return mujoco::Texture::downcast(texture)->GetWidth();
}
+2 -2
View File
@@ -38,8 +38,8 @@ inline UniquePtr<mjrfTexture> CreateTexture(mjrfContext* ctx,
}
inline UniquePtr<mjrfMesh> CreateMesh(mjrfContext* ctx,
const mjrfMeshData& data) {
mjrfMesh* mesh = mjrf_createMesh(ctx, &data);
const mjrfMeshConfig& config) {
mjrfMesh* mesh = mjrf_createMesh(ctx, &config);
return UniquePtr<mjrfMesh>(mesh, mjrf_destroyMesh);
}
+47 -30
View File
@@ -362,43 +362,47 @@ static void FillSkinVertices(T* vertices, const mjModel* model,
}
}
static mjrfMeshData PrepareMeshData(int num_vertices, bool has_uvs) {
mjrfMeshData mesh_data;
mjrf_defaultMeshData(&mesh_data);
static void PrepareMeshData(mjrfMeshConfig* config, mjrfMeshData* data,
int num_vertices, bool has_uvs) {
mjrf_defaultMeshConfig(config);
config->max_vertices = num_vertices;
config->num_attributes = has_uvs ? 3 : 2;
config->interleaved = true;
config->primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
config->max_indices = num_vertices;
config->index_type = mjINDEX_TYPE_U32;
config->attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
config->attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
config->attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_TANGENTS;
config->attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4;
if (has_uvs) {
config->attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
config->attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
}
mjrf_defaultMeshData(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);
data->user_data = new char[nbytes];
std::memset(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);
data->user_data = new char[nbytes];
std::memset(data->user_data, 0, nbytes);
}
mesh_data.release = [](void* user_data) {
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]);
char* buf = reinterpret_cast<char*>(data->user_data);
data->num_vertices = num_vertices;
data->vertices[0] = buf;
data->vertices[1] = 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]);
data->vertices[2] = 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;
data->num_indices = num_vertices;
data->indices = nullptr;
}
static void SetBounds(mjrfMeshData* mesh_data, const float3& min_pt,
@@ -428,7 +432,10 @@ UniquePtr<mjrfMesh> CreateFlexMesh(mjrfContext* ctx, const mjModel* model,
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);
mjrfMeshConfig mesh_config;
mjrfMeshData mesh_data;
PrepareMeshData(&mesh_config, &mesh_data, num_vertices, has_uvs);
float3 min_pt = float3(FLT_MAX);
float3 max_pt = float3(FLT_MIN);
@@ -440,14 +447,21 @@ UniquePtr<mjrfMesh> CreateFlexMesh(mjrfContext* ctx, const mjModel* model,
FillFlexVertices(vertices, model, data, flex_id, &min_pt, &max_pt);
}
SetBounds(&mesh_data, min_pt, max_pt);
return CreateMesh(ctx, mesh_data);
auto mesh = CreateMesh(ctx, mesh_config);
mjrf_setMeshData(mesh.get(), &mesh_data);
return mesh;
}
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);
mjrfMeshConfig mesh_config;
mjrfMeshData mesh_data;
PrepareMeshData(&mesh_config, &mesh_data, num_vertices, has_uvs);
float3 min_pt = float3(FLT_MAX);
float3 max_pt = float3(FLT_MIN);
@@ -461,7 +475,10 @@ UniquePtr<mjrfMesh> CreateSkinMesh(mjrfContext* ctx, const mjModel* model,
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);
auto mesh = CreateMesh(ctx, mesh_config);
mjrf_setMeshData(mesh.get(), &mesh_data);
return mesh;
}
void GatherSpatialTendonPoints(const mjModel* model, const mjData* data,
+46 -25
View File
@@ -329,8 +329,8 @@ static int GetNumVertices(const mjModel* model, int id, MeshType mesh_type) {
}
}
static void UpdateMeshData(mjrfMeshData* data, const mjModel* model, int id,
MeshType mesh_type) {
static void UpdateMeshData(mjrfMeshConfig* config, mjrfMeshData* data,
const mjModel* model, int id, MeshType mesh_type) {
if (!IsValidIndex(model, id, mesh_type)) {
mju_error("Invalid index %d for type %d", id, mesh_type);
return;
@@ -357,24 +357,29 @@ static void UpdateMeshData(mjrfMeshData* data, const mjModel* model, int id,
break;
}
data->primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
data->num_vertices = num_vertices;
data->num_indices = data->num_vertices;
data->indices = nullptr;
data->index_type = data->num_vertices >= std::numeric_limits<uint16_t>::max()
? mjINDEX_TYPE_U32
: mjINDEX_TYPE_U16;
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 = builder->positions.data();
data->attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_TANGENTS;
data->attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4;
data->attributes[1].bytes = builder->orientations.data();
config->max_vertices = num_vertices;
config->max_indices = num_vertices;
config->primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
config->index_type = num_vertices >= std::numeric_limits<uint16_t>::max()
? mjINDEX_TYPE_U32
: mjINDEX_TYPE_U16;
config->num_attributes = has_uvs ? 3 : 2;
config->attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
config->attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
config->attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_TANGENTS;
config->attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4;
if (has_uvs) {
data->attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
data->attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
data->attributes[2].bytes = builder->uvs.data();
config->attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
config->attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
}
data->num_vertices = num_vertices;
data->num_indices = num_vertices;
data->indices = nullptr;
data->vertices[0] = builder->positions.data();
data->vertices[1] = builder->orientations.data();
if (has_uvs) {
data->vertices[2] = builder->uvs.data();
}
data->bounds_min[0] = builder->bounds_min.x;
data->bounds_min[1] = builder->bounds_min.y;
@@ -415,16 +420,27 @@ void ModelObjects::UploadMesh(const mjModel* model, int id) {
meshes_.erase(id);
convex_hulls_.erase(id);
mjrfMeshConfig config;
mjrf_defaultMeshConfig(&config);
mjrfMeshData data;
mjrf_defaultMeshData(&data);
UpdateMeshData(&data, model, id, MeshType::kNormal);
meshes_.insert_or_assign(id, CreateMesh(ctx_, data));
UpdateMeshData(&config, &data, model, id, MeshType::kNormal);
auto mesh = CreateMesh(ctx_, config);
mjrf_setMeshData(mesh.get(), &data);
meshes_.insert_or_assign(id, std::move(mesh));
if (model->mesh_graphadr[id] >= 0) {
mjrfMeshConfig convex_hull_config;
mjrf_defaultMeshConfig(&convex_hull_config);
mjrfMeshData convex_hull_data;
mjrf_defaultMeshData(&convex_hull_data);
UpdateMeshData(&convex_hull_data, model, id, MeshType::kConvexHull);
convex_hulls_.insert_or_assign(id, CreateMesh(ctx_, convex_hull_data));
UpdateMeshData(&convex_hull_config, &convex_hull_data, model, id,
MeshType::kConvexHull);
auto convex_hull = CreateMesh(ctx_, convex_hull_config);
mjrf_setMeshData(convex_hull.get(), &convex_hull_data);
convex_hulls_.insert_or_assign(id, std::move(convex_hull));
}
}
@@ -484,10 +500,15 @@ void ModelObjects::UploadHeightField(const mjModel* model, int id) {
height_fields_.erase(id);
mjrfMeshConfig config;
mjrf_defaultMeshConfig(&config);
mjrfMeshData data;
mjrf_defaultMeshData(&data);
UpdateMeshData(&data, model, id, MeshType::kHeightField);
height_fields_.insert_or_assign(id, CreateMesh(ctx_, data));
UpdateMeshData(&config, &data, model, id, MeshType::kHeightField);
auto mesh = CreateMesh(ctx_, config);
mjrf_setMeshData(mesh.get(), &data);
height_fields_.insert_or_assign(id, std::move(mesh));
}
const mjrfMesh* ModelObjects::GetMesh(int data_id) const {
-1
View File
@@ -6240,7 +6240,6 @@ public unsafe struct mjrRendererInfo_ {
[StructLayout(LayoutKind.Sequential)]
public unsafe struct mjrVertexAttribute_ {
public void* bytes;
public int usage;
public int type;
}