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:
committed by
Copybara-Service
parent
d3166cb630
commit
ba9a65031b
@@ -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.
|
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:
|
||||||
|
|
||||||
mjrfMeshData
|
mjrfMeshData
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
Binary data used for creating a :ref:`mesh<mjrfMesh>`.
|
Binary data for a :ref:`mesh<mjrfMesh>`.
|
||||||
|
|
||||||
.. mujoco-include:: mjrfMeshData
|
.. mujoco-include:: mjrfMeshData
|
||||||
|
|
||||||
|
|||||||
@@ -3249,14 +3249,14 @@ Returns the height of the texture.
|
|||||||
Returns the sampler type (mjrSamplerType) used by the texture.
|
Returns the sampler type (mjrSamplerType) used by the texture.
|
||||||
[returns: mjrSamplerType]
|
[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:
|
.. _mjrf_createMesh:
|
||||||
|
|
||||||
@@ -3265,7 +3265,7 @@ Initializes the mjrfMeshData to default values.
|
|||||||
|
|
||||||
.. mujoco-include:: mjrf_createMesh
|
.. mujoco-include:: mjrf_createMesh
|
||||||
|
|
||||||
Creates a mesh with the given data.
|
Creates an empty mesh with the given config.
|
||||||
|
|
||||||
.. _mjrf_destroyMesh:
|
.. _mjrf_destroyMesh:
|
||||||
|
|
||||||
@@ -3276,6 +3276,24 @@ Creates a mesh with the given data.
|
|||||||
|
|
||||||
Destroys the mesh.
|
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 <#mjrf_defaultSceneParams>`__
|
`mjrf_defaultSceneParams <#mjrf_defaultSceneParams>`__
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ Engine
|
|||||||
- :ref:`mju_round` now breaks ties away from zero rather than towards :math:`+\infty`. This only affects
|
- :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.
|
negative half-integers, e.g. ``mju_round(-2.5)`` now returns -3 rather than -2.
|
||||||
- Removed unneeded `mjvScene` argument from :ref:`mjv_moveCamera`.
|
- 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
|
.. admonition:: Breaking ABI changes
|
||||||
:class: caution
|
:class: caution
|
||||||
|
|||||||
@@ -1425,7 +1425,6 @@ typedef struct mjrRendererInfo_ { // active renderer identity
|
|||||||
const char* backend; // graphics backend: opengl, vulkan; empty if uninitialized
|
const char* backend; // graphics backend: opengl, vulkan; empty if uninitialized
|
||||||
} mjrRendererInfo;
|
} mjrRendererInfo;
|
||||||
typedef struct mjrVertexAttribute_ { // vertex attribute format specification
|
typedef struct mjrVertexAttribute_ { // vertex attribute format specification
|
||||||
const void* bytes; // vertex data
|
|
||||||
int usage; // position, normal, etc [mjrVertexAttributeUsage]
|
int usage; // position, normal, etc [mjrVertexAttributeUsage]
|
||||||
int type; // float3, ubyte4, etc. [mjrVertexAttributeType]
|
int type; // float3, ubyte4, etc. [mjrVertexAttributeType]
|
||||||
} mjrVertexAttribute;
|
} mjrVertexAttribute;
|
||||||
@@ -1580,15 +1579,20 @@ typedef struct mjrfTextureData_ {
|
|||||||
mjrfCallback release; // callback when data has finished uploading
|
mjrfCallback release; // callback when data has finished uploading
|
||||||
void* user_data; // user data for release callback
|
void* user_data; // user data for release callback
|
||||||
} mjrfTextureData;
|
} mjrfTextureData;
|
||||||
typedef struct mjrfMeshData_ {
|
typedef struct mjrfMeshConfig_ {
|
||||||
mjtSize num_vertices; // number of vertices; all vertex attributes share this size
|
mjtSize max_vertices; // maximum number of vertices
|
||||||
int num_attributes; // number of attributes defined
|
mjtSize max_indices; // maximum number of indices
|
||||||
|
int num_attributes; // number of defined attributes
|
||||||
mjrVertexAttribute attributes[mjMAX_VERTEX_ATTRIBUTES]; // per-vertex attribute information
|
mjrVertexAttribute attributes[mjMAX_VERTEX_ATTRIBUTES]; // per-vertex attribute information
|
||||||
mjtBool interleaved; // true if vertex attributes are interleaved
|
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 index_type; // index data format (e.g. UINT16 or UINT32) [mjrIndexType]
|
||||||
int primitive_type; // index interpretation (e.g. TRIANGLES, etc.) [mjrMeshPrimitiveType]
|
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
|
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_min[3]; // min/max bounds; assume unset if bounds_min == bounds_max
|
||||||
float bounds_max[3];
|
float bounds_max[3];
|
||||||
@@ -3422,9 +3426,11 @@ void mjrf_setTextureData(mjrfTexture* texture, const mjrfTextureData* data);
|
|||||||
int mjrf_getTextureWidth(const mjrfTexture* texture);
|
int mjrf_getTextureWidth(const mjrfTexture* texture);
|
||||||
int mjrf_getTextureHeight(const mjrfTexture* texture);
|
int mjrf_getTextureHeight(const mjrfTexture* texture);
|
||||||
int mjrf_getTextureSamplerType(const mjrfTexture* texture);
|
int mjrf_getTextureSamplerType(const mjrfTexture* texture);
|
||||||
void mjrf_defaultMeshData(mjrfMeshData* data);
|
void mjrf_defaultMeshConfig(mjrfMeshConfig* config);
|
||||||
mjrfMesh* mjrf_createMesh(mjrfContext* ctx, const mjrfMeshData* data);
|
mjrfMesh* mjrf_createMesh(mjrfContext* ctx, const mjrfMeshConfig* config);
|
||||||
void mjrf_destroyMesh(mjrfMesh* mesh);
|
void mjrf_destroyMesh(mjrfMesh* mesh);
|
||||||
|
void mjrf_defaultMeshData(mjrfMeshData* data);
|
||||||
|
void mjrf_setMeshData(mjrfMesh* mesh, const mjrfMeshData* data);
|
||||||
void mjrf_defaultSceneParams(mjrfSceneParams* params);
|
void mjrf_defaultSceneParams(mjrfSceneParams* params);
|
||||||
mjrfScene* mjrf_createScene(mjrfContext* ctx, const mjrfSceneParams* params);
|
mjrfScene* mjrf_createScene(mjrfContext* ctx, const mjrfSceneParams* params);
|
||||||
void mjrf_destroyScene(mjrfScene* scene);
|
void mjrf_destroyScene(mjrfScene* scene);
|
||||||
|
|||||||
@@ -124,7 +124,6 @@ typedef struct mjrRendererInfo_ { // active renderer identity
|
|||||||
|
|
||||||
|
|
||||||
typedef struct mjrVertexAttribute_ { // vertex attribute format specification
|
typedef struct mjrVertexAttribute_ { // vertex attribute format specification
|
||||||
const void* bytes; // vertex data
|
|
||||||
int usage; // position, normal, etc [mjrVertexAttributeUsage]
|
int usage; // position, normal, etc [mjrVertexAttributeUsage]
|
||||||
int type; // float3, ubyte4, etc. [mjrVertexAttributeType]
|
int type; // float3, ubyte4, etc. [mjrVertexAttributeType]
|
||||||
} mjrVertexAttribute;
|
} mjrVertexAttribute;
|
||||||
|
|||||||
@@ -182,16 +182,32 @@ int mjrf_getTextureSamplerType(const mjrfTexture* texture);
|
|||||||
// Maximum number of vertex attributes in a mesh.
|
// Maximum number of vertex attributes in a mesh.
|
||||||
enum { mjMAX_VERTEX_ATTRIBUTES = 16 };
|
enum { mjMAX_VERTEX_ATTRIBUTES = 16 };
|
||||||
|
|
||||||
// Binary data used for creating a mesh (mjrfMesh).
|
// Parameters describing a mesh (mjrfMesh).
|
||||||
typedef struct mjrfMeshData_ {
|
typedef struct mjrfMeshConfig_ {
|
||||||
mjtSize num_vertices; // number of vertices; all vertex attributes share this size
|
mjtSize max_vertices; // maximum number of vertices
|
||||||
int num_attributes; // number of attributes defined
|
mjtSize max_indices; // maximum number of indices
|
||||||
|
int num_attributes; // number of defined attributes
|
||||||
mjrVertexAttribute attributes[mjMAX_VERTEX_ATTRIBUTES]; // per-vertex attribute information
|
mjrVertexAttribute attributes[mjMAX_VERTEX_ATTRIBUTES]; // per-vertex attribute information
|
||||||
mjtBool interleaved; // true if vertex attributes are interleaved
|
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 index_type; // index data format (e.g. UINT16 or UINT32) [mjrIndexType]
|
||||||
int primitive_type; // index interpretation (e.g. TRIANGLES, etc.) [mjrMeshPrimitiveType]
|
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
|
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_min[3]; // min/max bounds; assume unset if bounds_min == bounds_max
|
||||||
float bounds_max[3];
|
float bounds_max[3];
|
||||||
@@ -202,11 +218,8 @@ typedef struct mjrfMeshData_ {
|
|||||||
// Initializes the mjrfMeshData to default values.
|
// Initializes the mjrfMeshData to default values.
|
||||||
void mjrf_defaultMeshData(mjrfMeshData* data);
|
void mjrf_defaultMeshData(mjrfMeshData* data);
|
||||||
|
|
||||||
// Creates a mesh with the given data.
|
// Uploads the given mesh data to the mesh.
|
||||||
mjrfMesh* mjrf_createMesh(mjrfContext* ctx, const mjrfMeshData* data);
|
void mjrf_setMeshData(mjrfMesh* mesh, const mjrfMeshData* data);
|
||||||
|
|
||||||
// Destroys the mesh.
|
|
||||||
void mjrf_destroyMesh(mjrfMesh* mesh);
|
|
||||||
|
|
||||||
// Parameters for creating a scene (mjrfScene).
|
// Parameters for creating a scene (mjrfScene).
|
||||||
typedef struct mjrfSceneParams_ {
|
typedef struct mjrfSceneParams_ {
|
||||||
|
|||||||
@@ -11332,13 +11332,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([
|
|||||||
name='mjrVertexAttribute',
|
name='mjrVertexAttribute',
|
||||||
declname='struct mjrVertexAttribute_',
|
declname='struct mjrVertexAttribute_',
|
||||||
fields=(
|
fields=(
|
||||||
StructFieldDecl(
|
|
||||||
name='bytes',
|
|
||||||
type=PointerType(
|
|
||||||
inner_type=ValueType(name='void', is_const=True),
|
|
||||||
),
|
|
||||||
doc='vertex data',
|
|
||||||
),
|
|
||||||
StructFieldDecl(
|
StructFieldDecl(
|
||||||
name='usage',
|
name='usage',
|
||||||
type=ValueType(name='int'),
|
type=ValueType(name='int'),
|
||||||
|
|||||||
@@ -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,
|
py::class_<raw::MjrVertexAttribute> mjrVertexAttribute(m,
|
||||||
"MjrVertexAttribute");
|
"MjrVertexAttribute");
|
||||||
mjrVertexAttribute.def(py::init([](int usage, int type) {
|
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);
|
py::arg("usage") = 0, py::arg("type") = 0);
|
||||||
mjrVertexAttribute.def("__copy__", [](const raw::MjrVertexAttribute& other) {
|
mjrVertexAttribute.def("__copy__", [](const raw::MjrVertexAttribute& other) {
|
||||||
|
|||||||
@@ -243,27 +243,35 @@ void SceneDecorator::Update(mjData* data, const mjvOption* vis_option,
|
|||||||
const float* uvs =
|
const float* uvs =
|
||||||
has_uvs ? mjv_scene_.flextexcoord + (6 * addr) : nullptr;
|
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;
|
mjrfMeshData data;
|
||||||
mjrf_defaultMeshData(&data);
|
mjrf_defaultMeshData(&data);
|
||||||
data.num_attributes = has_uvs ? 3 : 2;
|
data.vertices[0] = positions;
|
||||||
data.attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
|
data.vertices[1] = normals;
|
||||||
data.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
|
data.vertices[2] = uvs;
|
||||||
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_vertices = 3 * mjv_scene_.flexfaceused[geom.objid];
|
||||||
data.num_indices = 3 * mjv_scene_.flexfaceused[geom.objid];
|
data.num_indices = 3 * mjv_scene_.flexfaceused[geom.objid];
|
||||||
data.indices = nullptr;
|
data.indices = nullptr;
|
||||||
data.index_type = mjINDEX_TYPE_U32;
|
|
||||||
data.primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
|
|
||||||
data.compute_bounds = true;
|
data.compute_bounds = true;
|
||||||
data.release = nullptr;
|
data.release = nullptr;
|
||||||
data.user_data = 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);
|
auto renderable = CreateRenderable(ctx, params);
|
||||||
mjrf_setRenderableMesh(renderable.get(), meshes_.back().get(), 0, 0);
|
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,
|
SceneObjects::SceneObjects(mjrfContext* ctx) : ctx_(ctx) {}
|
||||||
const mjvScene* scene, const mjvGeom& geom) {
|
|
||||||
|
bool SceneObjects::CreateSkinFlexMesh(const mjvScene* scene,
|
||||||
|
const mjModel* model,
|
||||||
|
const mjvGeom& geom) {
|
||||||
auto positions = GetPositions(model, scene, geom);
|
auto positions = GetPositions(model, scene, geom);
|
||||||
if (positions.empty()) {
|
if (positions.empty()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -119,41 +122,44 @@ static bool UpdateSkinFlexMeshData(mjrfMeshData* data, const mjModel* model,
|
|||||||
num_indices = 3 * scene->flexfaceused[geom.objid];
|
num_indices = 3 * scene->flexfaceused[geom.objid];
|
||||||
}
|
}
|
||||||
|
|
||||||
data->num_attributes = uvs.data() ? 3 : 2;
|
mjrfMeshConfig config;
|
||||||
data->attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
|
mjrf_defaultMeshConfig(&config);
|
||||||
data->attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
|
config.num_attributes = uvs.data() ? 3 : 2;
|
||||||
data->attributes[0].bytes = positions.data();
|
config.attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
|
||||||
data->attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_NORMAL;
|
config.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
|
||||||
data->attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
|
config.attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_NORMAL;
|
||||||
data->attributes[1].bytes = normals.data();
|
config.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
|
||||||
data->attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
|
config.attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
|
||||||
data->attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
|
config.attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
|
||||||
data->attributes[2].bytes = uvs.data();
|
config.max_vertices = positions.size() / 3;
|
||||||
data->num_vertices = positions.size() / 3;
|
config.max_indices = num_indices;
|
||||||
data->num_indices = num_indices;
|
config.index_type = mjINDEX_TYPE_U32;
|
||||||
data->indices = indices.data();
|
config.primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
SceneObjects::SceneObjects(mjrfContext* ctx) : ctx_(ctx) {}
|
|
||||||
|
|
||||||
bool SceneObjects::CreateSkinFlexMesh(const mjvScene* scene,
|
|
||||||
const mjModel* model,
|
|
||||||
const mjvGeom& geom) {
|
|
||||||
mjrfMeshData data;
|
mjrfMeshData data;
|
||||||
mjrf_defaultMeshData(&data);
|
mjrf_defaultMeshData(&data);
|
||||||
if (!UpdateSkinFlexMeshData(&data, model, scene, geom)) {
|
data.vertices[0] = positions.data();
|
||||||
return false;
|
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) {
|
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) {
|
} 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 {
|
} else {
|
||||||
mju_error("Unsupported dynamic mesh type: %d", geom.type);
|
mju_error("Unsupported dynamic mesh type: %d", geom.type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,27 +219,32 @@ void ImguiBridge::Update() {
|
|||||||
for (int n = 0; n < commands->CmdListsCount; ++n) {
|
for (int n = 0; n < commands->CmdListsCount; ++n) {
|
||||||
const ImDrawList* cmds = commands->CmdLists[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;
|
mjrfMeshData data;
|
||||||
mjrf_defaultMeshData(&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.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.num_indices = cmds->IdxBuffer.Size;
|
||||||
data.indices = cmds->IdxBuffer.Data;
|
data.indices = cmds->IdxBuffer.Data;
|
||||||
data.index_type = mjINDEX_TYPE_U16;
|
mjrf_setMeshData(mesh, &data);
|
||||||
data.primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
|
|
||||||
meshes_.push_back(CreateMesh(ctx_, data));
|
|
||||||
|
|
||||||
const mjrfMesh* mesh = meshes_.back().get();
|
|
||||||
|
|
||||||
int index_offset = 0;
|
int index_offset = 0;
|
||||||
for (const ImDrawCmd& command : cmds->CmdBuffer) {
|
for (const ImDrawCmd& command : cmds->CmdBuffer) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <math/vec3.h>
|
#include <math/vec3.h>
|
||||||
#include <math/vec4.h>
|
#include <math/vec4.h>
|
||||||
#include <mujoco/mjrfilament.h>
|
#include <mujoco/mjrfilament.h>
|
||||||
|
#include <mujoco/mujoco.h>
|
||||||
#include "render/filament/core/mesh.h"
|
#include "render/filament/core/mesh.h"
|
||||||
#include "render/filament/support/filament_util.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;
|
return kNumIndicesPerQuad * num_quads_per_axis * num_quads_per_axis;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BuiltinBuilder : public mjrfMeshData {
|
class BuiltinBuilder {
|
||||||
public:
|
public:
|
||||||
BuiltinBuilder() { mjrf_defaultMeshData(this); }
|
BuiltinBuilder() {}
|
||||||
virtual ~BuiltinBuilder() = default;
|
virtual ~BuiltinBuilder() = default;
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
static std::unique_ptr<Mesh> Create(filament::Engine* engine,
|
static std::unique_ptr<Mesh> Create(filament::Engine* engine,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
auto builder = new T(std::forward<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);
|
delete static_cast<BuiltinBuilder*>(user_data);
|
||||||
};
|
};
|
||||||
mesh_data->user_data = builder;
|
data.user_data = builder;
|
||||||
return std::make_unique<Mesh>(engine, *mesh_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
mjrfMeshData* PrepareMeshData() {
|
auto mesh = std::make_unique<Mesh>(engine, config);
|
||||||
// Update the `mjrfMeshData` fields.
|
mesh->Upload(data);
|
||||||
num_attributes = 2;
|
return mesh;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SetBounds(const float3& min, const float3& max) {
|
void SetBounds(const float3& min, const float3& max) {
|
||||||
bounds_min[0] = min.x;
|
bounds_min_ = min;
|
||||||
bounds_min[1] = min.y;
|
bounds_max_ = max;
|
||||||
bounds_min[2] = min.z;
|
|
||||||
bounds_max[0] = max.x;
|
|
||||||
bounds_max[1] = max.y;
|
|
||||||
bounds_max[2] = max.z;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int primitive_type_ = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
|
||||||
std::vector<float3> positions_;
|
std::vector<float3> positions_;
|
||||||
std::vector<float4> orientations_;
|
std::vector<float4> orientations_;
|
||||||
std::vector<uint16_t> indices_;
|
std::vector<uint16_t> indices_;
|
||||||
|
float3 bounds_min_ = {0, 0, 0};
|
||||||
|
float3 bounds_max_ = {0, 0, 0};
|
||||||
};
|
};
|
||||||
|
|
||||||
class LineBuilder : public BuiltinBuilder {
|
class LineBuilder : public BuiltinBuilder {
|
||||||
public:
|
public:
|
||||||
LineBuilder() {
|
LineBuilder() {
|
||||||
primitive_type = mjMESH_PRIMITIVE_TYPE_LINES;
|
primitive_type_ = mjMESH_PRIMITIVE_TYPE_LINES;
|
||||||
|
|
||||||
positions_.reserve(2);
|
positions_.reserve(2);
|
||||||
positions_.emplace_back(0, 0, 0);
|
positions_.emplace_back(0, 0, 0);
|
||||||
@@ -183,7 +194,7 @@ class TriangleBuilder : public BuiltinBuilder {
|
|||||||
class LineBoxBuilder : public BuiltinBuilder {
|
class LineBoxBuilder : public BuiltinBuilder {
|
||||||
public:
|
public:
|
||||||
explicit LineBoxBuilder() {
|
explicit LineBoxBuilder() {
|
||||||
primitive_type = mjMESH_PRIMITIVE_TYPE_LINES;
|
primitive_type_ = mjMESH_PRIMITIVE_TYPE_LINES;
|
||||||
|
|
||||||
positions_.reserve(8);
|
positions_.reserve(8);
|
||||||
positions_.emplace_back(-1.0f, -1.0f, -1.0f);
|
positions_.emplace_back(-1.0f, -1.0f, -1.0f);
|
||||||
|
|||||||
+152
-126
@@ -102,21 +102,43 @@ int FillSequence(std::byte* buffer, std::size_t num_bytes) {
|
|||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh::Mesh(filament::Engine* engine, const mjrfMeshData& data)
|
Mesh::Mesh(filament::Engine* engine, const mjrfMeshConfig& config)
|
||||||
: engine_(engine), shared_state_(std::make_shared<SharedState>()) {
|
: engine_(engine),
|
||||||
type_ = data.primitive_type == mjMESH_PRIMITIVE_TYPE_TRIANGLES
|
config_(config),
|
||||||
? filament::RenderableManager::PrimitiveType::TRIANGLES
|
shared_state_(std::make_shared<SharedState>()) {
|
||||||
: filament::RenderableManager::PrimitiveType::LINES;
|
// Perform some validation on the config.
|
||||||
|
const mjrVertexAttribute* positions = nullptr;
|
||||||
// If the user has provided a release callback, then we need to ensure we
|
const mjrVertexAttribute* normals = nullptr;
|
||||||
// call is when filament is done with the mesh data.
|
const mjrVertexAttribute* tangents = nullptr;
|
||||||
if (data.release) {
|
for (int i = 0; i < config_.num_attributes; ++i) {
|
||||||
shared_state_->callbacks.push_back([=]() { data.release(data.user_data); });
|
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);
|
InitVertexBuffer();
|
||||||
BuildIndexBuffer(data);
|
InitIndexBuffer();
|
||||||
UpdateBounds(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh::~Mesh() {
|
Mesh::~Mesh() {
|
||||||
@@ -129,9 +151,84 @@ Mesh::~Mesh() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::BuildVertexBuffer(const mjrfMeshData& data) {
|
void Mesh::Upload(const mjrfMeshData& data) {
|
||||||
if (data.num_vertices == 0) {
|
// If the user has provided a release callback, then we need to ensure we
|
||||||
mju_error("mjrfMeshData has no vertices.");
|
// 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.
|
// 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
|
if (config_.interleaved) {
|
||||||
// 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);
|
|
||||||
int total_vertex_size = 0;
|
int total_vertex_size = 0;
|
||||||
for (int i = 0; i < data.num_attributes; ++i) {
|
for (int i = 0; i < config_.num_attributes; ++i) {
|
||||||
total_vertex_size += VertexAttributeTypeSize(data.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_);
|
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,
|
vertex_buffer_->setBufferAt(*engine_, 0,
|
||||||
{bytes, nbytes, callback, user_data});
|
{bytes, nbytes, callback, user_data});
|
||||||
} else {
|
} 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.
|
// Assign the individual data buffers.
|
||||||
for (int i = 0; i < data.num_attributes; ++i) {
|
for (int i = 0; i < config_.num_attributes; ++i) {
|
||||||
const mjrVertexAttribute& attrib = data.attributes[i];
|
const mjrVertexAttribute& attrib = config_.attributes[i];
|
||||||
const void* bytes = attrib.bytes;
|
const void* bytes = data.vertices[i];
|
||||||
size_t nbytes = data.num_vertices * VertexAttributeTypeSize(attrib);
|
size_t nbytes = data.num_vertices * VertexAttributeTypeSize(attrib);
|
||||||
if (attrib.usage == mjVERTEX_ATTRIBUTE_USAGE_NORMAL) {
|
if (attrib.usage == mjVERTEX_ATTRIBUTE_USAGE_NORMAL) {
|
||||||
// Replace normals with orientations.
|
// Replace normals with orientations.
|
||||||
nbytes = data.num_vertices * sizeof(float4);
|
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_);
|
auto* user_data = new std::shared_ptr<SharedState>(shared_state_);
|
||||||
vertex_buffer_->setBufferAt(*engine_, i,
|
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) {
|
if (data.num_indices == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (data.num_indices != config_.max_indices) {
|
||||||
|
mju_error("Index count does not match config.");
|
||||||
|
}
|
||||||
|
|
||||||
const int element_size =
|
const int element_size =
|
||||||
data.index_type == mjINDEX_TYPE_U16 ? sizeof(uint16_t) : sizeof(uint32_t);
|
config_.index_type == mjINDEX_TYPE_U16 ? sizeof(uint16_t) : sizeof(uint32_t);
|
||||||
const int num_bytes = data.num_indices * element_size;
|
const int num_bytes = config_.max_indices * element_size;
|
||||||
|
|
||||||
// If indices == 0 and num_indices > 0, then the user is specifying that the
|
// 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, ...
|
// 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];
|
std::byte* sequence = new std::byte[num_bytes];
|
||||||
shared_state_->callbacks.push_back([=]() { delete[] sequence; });
|
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);
|
FillSequence<uint16_t>(sequence, num_bytes);
|
||||||
} else {
|
} else {
|
||||||
FillSequence<uint32_t>(sequence, num_bytes);
|
FillSequence<uint32_t>(sequence, num_bytes);
|
||||||
}
|
}
|
||||||
indices = sequence;
|
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
|
// We don't worry about setting a release callback here because the release
|
||||||
// callback for the vertex buffer will call release_callbacks_.
|
// callback for the vertex buffer will call release_callbacks_.
|
||||||
filament::backend::BufferDescriptor desc(indices, num_bytes);
|
filament::backend::BufferDescriptor desc(indices, num_bytes);
|
||||||
index_buffer_->setBuffer(*engine_, std::move(desc));
|
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) {
|
void Mesh::UpdateBounds(const mjrfMeshData& data) {
|
||||||
float3 bounds_min = ReadFloat3(data.bounds_min);
|
float3 bounds_min = ReadFloat3(data.bounds_min);
|
||||||
float3 bounds_max = ReadFloat3(data.bounds_max);
|
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_min = float3(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||||
bounds_max = 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.");
|
mju_error("mjrfMeshData has no positions.");
|
||||||
}
|
}
|
||||||
const float* 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) {
|
for (int i = 0; i < data.num_vertices; ++i) {
|
||||||
const float3 position = ReadFloat3(positions, 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 {
|
filament::IndexBuffer* Mesh::GetFilamentIndexBuffer() const {
|
||||||
return index_buffer_;
|
return index_buffer_;
|
||||||
}
|
}
|
||||||
@@ -345,13 +366,18 @@ filament::VertexBuffer* Mesh::GetFilamentVertexBuffer() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filament::RenderableManager::PrimitiveType Mesh::GetPrimitiveType() 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 {
|
bool Mesh::HasVertexAttribute(mjrVertexAttributeUsage attrib) const {
|
||||||
auto fattrib = GetUsage(mjrVertexAttribute{.usage = attrib});
|
for (int i = 0; i < config_.num_attributes; ++i) {
|
||||||
auto it = std::find(attributes_.begin(), attributes_.end(), fattrib);
|
if (config_.attributes[i].usage == attrib) {
|
||||||
return it != attributes_.end();
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mesh::HasBounds() const { return bounds_.has_value(); }
|
bool Mesh::HasBounds() const { return bounds_.has_value(); }
|
||||||
|
|||||||
@@ -37,12 +37,15 @@ namespace mujoco {
|
|||||||
class Mesh : public mjrfMesh {
|
class Mesh : public mjrfMesh {
|
||||||
public:
|
public:
|
||||||
// Creates a Mesh from the given MeshData.
|
// Creates a Mesh from the given MeshData.
|
||||||
Mesh(filament::Engine* engine, const mjrfMeshData& data);
|
Mesh(filament::Engine* engine, const mjrfMeshConfig& config);
|
||||||
~Mesh();
|
~Mesh();
|
||||||
|
|
||||||
Mesh(const Mesh&) = delete;
|
Mesh(const Mesh&) = delete;
|
||||||
Mesh& operator=(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.
|
// Returns the filament IndexBuffer for the mesh.
|
||||||
filament::IndexBuffer* GetFilamentIndexBuffer() const;
|
filament::IndexBuffer* GetFilamentIndexBuffer() const;
|
||||||
|
|
||||||
@@ -67,29 +70,31 @@ class Mesh : public mjrfMesh {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void BuildVertexBuffer(const mjrfMeshData& data);
|
void InitVertexBuffer();
|
||||||
void BuildIndexBuffer(const mjrfMeshData& data);
|
void InitIndexBuffer();
|
||||||
|
|
||||||
|
void UpdateVertexBuffer(const mjrfMeshData& data);
|
||||||
|
void UpdateIndexBuffer(const mjrfMeshData& data);
|
||||||
void UpdateBounds(const mjrfMeshData& data);
|
void UpdateBounds(const mjrfMeshData& data);
|
||||||
|
|
||||||
filament::math::float4* BuildOrientationsFromNormals(
|
filament::math::float4* BuildOrientationsFromNormals(int num_vertices,
|
||||||
int num_vertices, const mjrVertexAttribute& normals);
|
const void* normals);
|
||||||
|
|
||||||
void ReleaseResources();
|
void ReleaseResources();
|
||||||
|
|
||||||
filament::Engine* engine_ = nullptr;
|
filament::Engine* engine_ = nullptr;
|
||||||
|
mjrfMeshConfig config_;
|
||||||
|
|
||||||
filament::IndexBuffer* index_buffer_ = nullptr;
|
filament::IndexBuffer* index_buffer_ = nullptr;
|
||||||
filament::VertexBuffer* vertex_buffer_ = nullptr;
|
filament::VertexBuffer* vertex_buffer_ = nullptr;
|
||||||
filament::RenderableManager::PrimitiveType type_ =
|
|
||||||
filament::RenderableManager::PrimitiveType::TRIANGLES;
|
|
||||||
std::optional<filament::Box> bounds_;
|
std::optional<filament::Box> bounds_;
|
||||||
|
|
||||||
struct SharedState {
|
struct SharedState {
|
||||||
std::vector<std::function<void()>> callbacks;
|
std::vector<std::function<void()>> callbacks;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
bool called = false;
|
bool called = false;
|
||||||
};
|
};
|
||||||
std::shared_ptr<SharedState> shared_state_;
|
std::shared_ptr<SharedState> shared_state_;
|
||||||
std::array<filament::VertexAttribute, mjMAX_VERTEX_ATTRIBUTES> attributes_;
|
|
||||||
int num_attributes_ = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ void mjrf_defaultTextureConfig(mjrfTextureConfig* config) {
|
|||||||
memset(config, 0, sizeof(mjrfTextureConfig));
|
memset(config, 0, sizeof(mjrfTextureConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mjrf_defaultMeshConfig(mjrfMeshConfig* config) {
|
||||||
|
memset(config, 0, sizeof(mjrfMeshConfig));
|
||||||
|
}
|
||||||
|
|
||||||
void mjrf_defaultMeshData(mjrfMeshData* data) {
|
void mjrf_defaultMeshData(mjrfMeshData* data) {
|
||||||
memset(data, 0, sizeof(mjrfMeshData));
|
memset(data, 0, sizeof(mjrfMeshData));
|
||||||
}
|
}
|
||||||
@@ -151,9 +155,9 @@ void mjrf_destroyTexture(mjrfTexture* texture) {
|
|||||||
delete mujoco::Texture::downcast(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(),
|
return new mujoco::Mesh(mujoco::FilamentContext::downcast(ctx)->GetEngine(),
|
||||||
*data);
|
*config);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mjrf_destroyMesh(mjrfMesh* mesh) { delete mujoco::Mesh::downcast(mesh); }
|
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);
|
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) {
|
int mjrf_getTextureWidth(const mjrfTexture* texture) {
|
||||||
return mujoco::Texture::downcast(texture)->GetWidth();
|
return mujoco::Texture::downcast(texture)->GetWidth();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ inline UniquePtr<mjrfTexture> CreateTexture(mjrfContext* ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline UniquePtr<mjrfMesh> CreateMesh(mjrfContext* ctx,
|
inline UniquePtr<mjrfMesh> CreateMesh(mjrfContext* ctx,
|
||||||
const mjrfMeshData& data) {
|
const mjrfMeshConfig& config) {
|
||||||
mjrfMesh* mesh = mjrf_createMesh(ctx, &data);
|
mjrfMesh* mesh = mjrf_createMesh(ctx, &config);
|
||||||
return UniquePtr<mjrfMesh>(mesh, mjrf_destroyMesh);
|
return UniquePtr<mjrfMesh>(mesh, mjrf_destroyMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -362,43 +362,47 @@ static void FillSkinVertices(T* vertices, const mjModel* model,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static mjrfMeshData PrepareMeshData(int num_vertices, bool has_uvs) {
|
static void PrepareMeshData(mjrfMeshConfig* config, mjrfMeshData* data,
|
||||||
mjrfMeshData mesh_data;
|
int num_vertices, bool has_uvs) {
|
||||||
mjrf_defaultMeshData(&mesh_data);
|
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) {
|
if (has_uvs) {
|
||||||
const int nbytes = sizeof(VertexWithUv) * num_vertices;
|
const int nbytes = sizeof(VertexWithUv) * num_vertices;
|
||||||
mesh_data.user_data = new char[nbytes];
|
data->user_data = new char[nbytes];
|
||||||
std::memset(mesh_data.user_data, 0, nbytes);
|
std::memset(data->user_data, 0, nbytes);
|
||||||
} else {
|
} else {
|
||||||
const int nbytes = sizeof(VertexNoUv) * num_vertices;
|
const int nbytes = sizeof(VertexNoUv) * num_vertices;
|
||||||
mesh_data.user_data = new char[nbytes];
|
data->user_data = new char[nbytes];
|
||||||
std::memset(mesh_data.user_data, 0, nbytes);
|
std::memset(data->user_data, 0, nbytes);
|
||||||
}
|
}
|
||||||
mesh_data.release = [](void* user_data) {
|
data->release = [](void* user_data) {
|
||||||
delete[] (char*)(user_data);
|
delete[] (char*)(user_data);
|
||||||
};
|
};
|
||||||
|
|
||||||
char* buf = reinterpret_cast<char*>(mesh_data.user_data);
|
char* buf = reinterpret_cast<char*>(data->user_data);
|
||||||
mesh_data.num_vertices = num_vertices;
|
data->num_vertices = num_vertices;
|
||||||
mesh_data.num_attributes = has_uvs ? 3 : 2;
|
data->vertices[0] = buf;
|
||||||
mesh_data.interleaved = true;
|
data->vertices[1] = buf + sizeof(float[3]);
|
||||||
mesh_data.attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
|
|
||||||
mesh_data.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
|
|
||||||
mesh_data.attributes[0].bytes = buf;
|
|
||||||
mesh_data.attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_TANGENTS;
|
|
||||||
mesh_data.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4;
|
|
||||||
mesh_data.attributes[1].bytes = buf + sizeof(float[3]);
|
|
||||||
if (has_uvs) {
|
if (has_uvs) {
|
||||||
mesh_data.attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
|
data->vertices[2] = buf + sizeof(float[7]);
|
||||||
mesh_data.attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
|
|
||||||
mesh_data.attributes[2].bytes = buf + sizeof(float[7]);
|
|
||||||
}
|
}
|
||||||
mesh_data.primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
|
data->num_indices = num_vertices;
|
||||||
mesh_data.num_indices = num_vertices;
|
data->indices = nullptr;
|
||||||
mesh_data.index_type = mjINDEX_TYPE_U32;
|
|
||||||
mesh_data.indices = nullptr;
|
|
||||||
return mesh_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetBounds(mjrfMeshData* mesh_data, const float3& min_pt,
|
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 int num_vertices = num_faces * 3;
|
||||||
const bool has_uvs = model->flex_texcoordadr[flex_id] >= 0;
|
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 min_pt = float3(FLT_MAX);
|
||||||
float3 max_pt = float3(FLT_MIN);
|
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);
|
FillFlexVertices(vertices, model, data, flex_id, &min_pt, &max_pt);
|
||||||
}
|
}
|
||||||
SetBounds(&mesh_data, 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,
|
UniquePtr<mjrfMesh> CreateSkinMesh(mjrfContext* ctx, const mjModel* model,
|
||||||
const mjData* data, int skin_id) {
|
const mjData* data, int skin_id) {
|
||||||
const int num_vertices = model->skin_vertnum[skin_id];
|
const int num_vertices = model->skin_vertnum[skin_id];
|
||||||
const bool has_uvs = model->skin_texcoordadr[skin_id] >= 0;
|
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 min_pt = float3(FLT_MAX);
|
||||||
float3 max_pt = float3(FLT_MIN);
|
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.num_indices = 3 * model->skin_facenum[skin_id];
|
||||||
mesh_data.indices = model->skin_face + 3 * model->skin_faceadr[skin_id];
|
mesh_data.indices = model->skin_face + 3 * model->skin_faceadr[skin_id];
|
||||||
SetBounds(&mesh_data, 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GatherSpatialTendonPoints(const mjModel* model, const mjData* data,
|
void GatherSpatialTendonPoints(const mjModel* model, const mjData* data,
|
||||||
|
|||||||
@@ -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,
|
static void UpdateMeshData(mjrfMeshConfig* config, mjrfMeshData* data,
|
||||||
MeshType mesh_type) {
|
const mjModel* model, int id, MeshType mesh_type) {
|
||||||
if (!IsValidIndex(model, id, mesh_type)) {
|
if (!IsValidIndex(model, id, mesh_type)) {
|
||||||
mju_error("Invalid index %d for type %d", id, mesh_type);
|
mju_error("Invalid index %d for type %d", id, mesh_type);
|
||||||
return;
|
return;
|
||||||
@@ -357,24 +357,29 @@ static void UpdateMeshData(mjrfMeshData* data, const mjModel* model, int id,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
|
config->max_vertices = num_vertices;
|
||||||
data->num_vertices = num_vertices;
|
config->max_indices = num_vertices;
|
||||||
data->num_indices = data->num_vertices;
|
config->primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES;
|
||||||
data->indices = nullptr;
|
config->index_type = num_vertices >= std::numeric_limits<uint16_t>::max()
|
||||||
data->index_type = data->num_vertices >= std::numeric_limits<uint16_t>::max()
|
? mjINDEX_TYPE_U32
|
||||||
? mjINDEX_TYPE_U32
|
: mjINDEX_TYPE_U16;
|
||||||
: mjINDEX_TYPE_U16;
|
config->num_attributes = has_uvs ? 3 : 2;
|
||||||
data->num_attributes = has_uvs ? 3 : 2;
|
config->attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
|
||||||
data->attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION;
|
config->attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
|
||||||
data->attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3;
|
config->attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_TANGENTS;
|
||||||
data->attributes[0].bytes = builder->positions.data();
|
config->attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4;
|
||||||
data->attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_TANGENTS;
|
|
||||||
data->attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4;
|
|
||||||
data->attributes[1].bytes = builder->orientations.data();
|
|
||||||
if (has_uvs) {
|
if (has_uvs) {
|
||||||
data->attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
|
config->attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV;
|
||||||
data->attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
|
config->attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2;
|
||||||
data->attributes[2].bytes = builder->uvs.data();
|
}
|
||||||
|
|
||||||
|
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[0] = builder->bounds_min.x;
|
||||||
data->bounds_min[1] = builder->bounds_min.y;
|
data->bounds_min[1] = builder->bounds_min.y;
|
||||||
@@ -415,16 +420,27 @@ void ModelObjects::UploadMesh(const mjModel* model, int id) {
|
|||||||
meshes_.erase(id);
|
meshes_.erase(id);
|
||||||
convex_hulls_.erase(id);
|
convex_hulls_.erase(id);
|
||||||
|
|
||||||
|
mjrfMeshConfig config;
|
||||||
|
mjrf_defaultMeshConfig(&config);
|
||||||
mjrfMeshData data;
|
mjrfMeshData data;
|
||||||
mjrf_defaultMeshData(&data);
|
mjrf_defaultMeshData(&data);
|
||||||
UpdateMeshData(&data, model, id, MeshType::kNormal);
|
UpdateMeshData(&config, &data, model, id, MeshType::kNormal);
|
||||||
meshes_.insert_or_assign(id, CreateMesh(ctx_, data));
|
|
||||||
|
auto mesh = CreateMesh(ctx_, config);
|
||||||
|
mjrf_setMeshData(mesh.get(), &data);
|
||||||
|
meshes_.insert_or_assign(id, std::move(mesh));
|
||||||
|
|
||||||
if (model->mesh_graphadr[id] >= 0) {
|
if (model->mesh_graphadr[id] >= 0) {
|
||||||
|
mjrfMeshConfig convex_hull_config;
|
||||||
|
mjrf_defaultMeshConfig(&convex_hull_config);
|
||||||
mjrfMeshData convex_hull_data;
|
mjrfMeshData convex_hull_data;
|
||||||
mjrf_defaultMeshData(&convex_hull_data);
|
mjrf_defaultMeshData(&convex_hull_data);
|
||||||
UpdateMeshData(&convex_hull_data, model, id, MeshType::kConvexHull);
|
UpdateMeshData(&convex_hull_config, &convex_hull_data, model, id,
|
||||||
convex_hulls_.insert_or_assign(id, CreateMesh(ctx_, convex_hull_data));
|
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);
|
height_fields_.erase(id);
|
||||||
|
|
||||||
|
mjrfMeshConfig config;
|
||||||
|
mjrf_defaultMeshConfig(&config);
|
||||||
mjrfMeshData data;
|
mjrfMeshData data;
|
||||||
mjrf_defaultMeshData(&data);
|
mjrf_defaultMeshData(&data);
|
||||||
UpdateMeshData(&data, model, id, MeshType::kHeightField);
|
UpdateMeshData(&config, &data, model, id, MeshType::kHeightField);
|
||||||
height_fields_.insert_or_assign(id, CreateMesh(ctx_, data));
|
|
||||||
|
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 {
|
const mjrfMesh* ModelObjects::GetMesh(int data_id) const {
|
||||||
|
|||||||
@@ -6240,7 +6240,6 @@ public unsafe struct mjrRendererInfo_ {
|
|||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public unsafe struct mjrVertexAttribute_ {
|
public unsafe struct mjrVertexAttribute_ {
|
||||||
public void* bytes;
|
|
||||||
public int usage;
|
public int usage;
|
||||||
public int type;
|
public int type;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user