Add mjv_copyModel to copy mjModel, skipping large arrays not required for abstract visualization.

This functionality is meant for fast copying of `mjModel` when synchronizing the visualization state, as in the Python passive viewer.

PiperOrigin-RevId: 699199743
Change-Id: I13a5160063eb09139ec1aee9c4969bc1c6f547f1
This commit is contained in:
Yuval Tassa
2024-11-22 09:37:18 -08:00
committed by Copybara-Service
parent 74dcd51d83
commit 1d64362adc
9 changed files with 147 additions and 32 deletions
+9
View File
@@ -2163,6 +2163,15 @@ Update entire scene given model state.
Update entire scene from a scene state, return the number of new mjWARN_VGEOMFULL warnings.
.. _mjv_copyModel:
`mjv_copyModel <#mjv_copyModel>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mjv_copyModel
Copy mjModel, skip large arrays not required for abstract visualization.
.. _mjv_defaultSceneState:
`mjv_defaultSceneState <#mjv_defaultSceneState>`__
+1
View File
@@ -3347,6 +3347,7 @@ void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt,
int mjv_updateSceneFromState(const mjvSceneState* scnstate, const mjvOption* opt,
const mjvPerturb* pert, mjvCamera* cam, int catmask,
mjvScene* scn);
void mjv_copyModel(mjModel* dest, const mjModel* src);
void mjv_defaultSceneState(mjvSceneState* scnstate);
void mjv_makeSceneState(const mjModel* m, const mjData* d,
mjvSceneState* scnstate, int maxgeom);
+14 -9
View File
@@ -378,13 +378,13 @@
X ( mjtNum, mesh_scale, nmesh, 3 ) \
X ( mjtNum, mesh_pos, nmesh, 3 ) \
X ( mjtNum, mesh_quat, nmesh, 4 ) \
X ( float, mesh_vert, nmeshvert, 3 ) \
X ( float, mesh_normal, nmeshnormal, 3 ) \
X ( float, mesh_texcoord, nmeshtexcoord, 2 ) \
X ( int, mesh_face, nmeshface, 3 ) \
X ( int, mesh_facenormal, nmeshface, 3 ) \
X ( int, mesh_facetexcoord, nmeshface, 3 ) \
X ( int, mesh_graph, nmeshgraph, 1 ) \
XNV ( float, mesh_vert, nmeshvert, 3 ) \
XNV ( float, mesh_normal, nmeshnormal, 3 ) \
XNV ( float, mesh_texcoord, nmeshtexcoord, 2 ) \
XNV ( int, mesh_face, nmeshface, 3 ) \
XNV ( int, mesh_facenormal, nmeshface, 3 ) \
XNV ( int, mesh_facetexcoord, nmeshface, 3 ) \
XNV ( int, mesh_graph, nmeshgraph, 1 ) \
XMJV( int, mesh_pathadr, nmesh, 1 ) \
XMJV( int, skin_matid, nskin, 1 ) \
XMJV( int, skin_group, nskin, 1 ) \
@@ -412,14 +412,14 @@
X ( int, hfield_nrow, nhfield, 1 ) \
X ( int, hfield_ncol, nhfield, 1 ) \
X ( int, hfield_adr, nhfield, 1 ) \
X ( float, hfield_data, nhfielddata, 1 ) \
XNV ( float, hfield_data, nhfielddata, 1 ) \
XMJV( int, hfield_pathadr, nhfield, 1 ) \
X ( int, tex_type, ntex, 1 ) \
X ( int, tex_height, ntex, 1 ) \
X ( int, tex_width, ntex, 1 ) \
X ( int, tex_nchannel, ntex, 1 ) \
X ( int, tex_adr, ntex, 1 ) \
X ( mjtByte, tex_data, ntexdata, 1 ) \
XNV ( mjtByte, tex_data, ntexdata, 1 ) \
XMJV( int, tex_pathadr, ntex, 1 ) \
XMJV( int, mat_texid, nmat, mjNTEXROLE ) \
XMJV( mjtByte, mat_texuniform, nmat, 1 ) \
@@ -779,4 +779,9 @@
// redefine X to expand to nothing, and XMJV to do what's required
#define XMJV X
// alias XNV to be the same as X
// to obtain only X macros for fields that are relevant for mjvScene creation,
// redefine XNV to expand to nothing
#define XNV X
#endif // MUJOCO_MJXMACRO_H_
+3
View File
@@ -692,6 +692,9 @@ MJAPI int mjv_updateSceneFromState(const mjvSceneState* scnstate, const mjvOptio
const mjvPerturb* pert, mjvCamera* cam, int catmask,
mjvScene* scn);
// Copy mjModel, skip large arrays not required for abstract visualization.
MJAPI void mjv_copyModel(mjModel* dest, const mjModel* src);
// Set default scene state.
MJAPI void mjv_defaultSceneState(mjvSceneState* scnstate);
+20
View File
@@ -4550,6 +4550,26 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
),
doc='Update entire scene from a scene state, return the number of new mjWARN_VGEOMFULL warnings.', # pylint: disable=line-too-long
)),
('mjv_copyModel',
FunctionDecl(
name='mjv_copyModel',
return_type=ValueType(name='void'),
parameters=(
FunctionParameterDecl(
name='dest',
type=PointerType(
inner_type=ValueType(name='mjModel'),
),
),
FunctionParameterDecl(
name='src',
type=PointerType(
inner_type=ValueType(name='mjModel', is_const=True),
),
),
),
doc='Copy mjModel, skip large arrays not required for abstract visualization.', # pylint: disable=line-too-long
)),
('mjv_defaultSceneState',
FunctionDecl(
name='mjv_defaultSceneState',
+57 -23
View File
@@ -625,10 +625,10 @@ void mj_makeModel(mjModel** dest,
}
}
// copy mjModel, if dest==NULL create new model
mjModel* mj_copyModel(mjModel* dest, const mjModel* src) {
void* save_bufptr;
// allocate new model if needed
if (!dest) {
mj_makeModel(&dest,
@@ -658,13 +658,13 @@ mjModel* mj_copyModel(mjModel* dest, const mjModel* src) {
mjERROR("dest and src models have different buffer size");
}
// save buffer ptr, copy everything, restore buffer and other pointers
save_bufptr = dest->buffer;
// save buffer ptr, copy struct, restore buffer and other pointers
void* save_bufptr = dest->buffer;
*dest = *src;
dest->buffer = save_bufptr;
mj_setPtrModel(dest);
// copy buffer
// copy buffer contents
{
MJMODEL_POINTERS_PREAMBLE(src)
#define X(type, name, nr, nc) \
@@ -678,6 +678,38 @@ mjModel* mj_copyModel(mjModel* dest, const mjModel* src) {
// copy mjModel, skip large arrays not required for abstract visualization
void mjv_copyModel(mjModel* dest, const mjModel* src) {
// check sizes
if (dest->nbuffer != src->nbuffer) {
mjERROR("dest and src models have different buffer size");
}
// save buffer ptr, copy struct, restore buffer and other pointers
void* save_bufptr = dest->buffer;
*dest = *src;
dest->buffer = save_bufptr;
mj_setPtrModel(dest);
// redefine XNV to do nothing
#undef XNV
#define XNV(type, name, nr, nc)
// copy buffer contents, skipping arrays marked XNV
{
MJMODEL_POINTERS_PREAMBLE(src)
#define X(type, name, nr, nc) \
memcpy((char*)dest->name, (const char*)src->name, sizeof(type)*(src->nr)*nc);
MJMODEL_POINTERS
#undef X
}
// redefine XNV to be the same as X
#undef XNV
#define XNV X
}
// save model to binary file, or memory buffer of szbuf>0
void mj_saveModel(const mjModel* m, const char* filename, void* buffer, int buffer_sz) {
FILE* fp = 0;
@@ -1401,26 +1433,28 @@ mjData* mj_copyData(mjData* dest, const mjModel* m, const mjData* src) {
#undef X
}
// copy arena memory
#undef MJ_D
#define MJ_D(n) (src->n)
#undef MJ_M
#define MJ_M(n) (m->n)
#define X(type, name, nr, nc) \
if (src->name) { \
dest->name = (type*)((char*)dest->arena + PTRDIFF(src->name, src->arena)); \
ASAN_UNPOISON_MEMORY_REGION(dest->name, sizeof(type)*nr*nc); \
memcpy((char*)dest->name, (const char*)src->name, sizeof(type)*nr*nc); \
} else { \
dest->name = NULL; \
}
// copy arena memory
#undef MJ_D
#define MJ_D(n) (src->n)
#undef MJ_M
#define MJ_M(n) (m->n)
#define X(type, name, nr, nc) \
if (src->name) { \
dest->name = (type*)((char*)dest->arena + PTRDIFF(src->name, src->arena)); \
ASAN_UNPOISON_MEMORY_REGION(dest->name, sizeof(type) * nr * nc); \
memcpy((char*)dest->name, (const char*)src->name, sizeof(type) * nr * nc); \
} else { \
dest->name = NULL; \
}
MJDATA_ARENA_POINTERS
#undef X
#undef MJ_M
#define MJ_M(n) n
#undef MJ_D
#define MJ_D(n) n
#undef X
#undef MJ_M
#define MJ_M(n) n
#undef MJ_D
#define MJ_D(n) n
// restore contact pointer
dest->contact = dest->arena;
+3
View File
@@ -69,6 +69,9 @@ void mj_makeModel(mjModel** dest,
// copy mjModel; allocate new if dest is NULL
MJAPI mjModel* mj_copyModel(mjModel* dest, const mjModel* src);
// copy mjModel, skip large arrays not required for abstract visualization
MJAPI void mjv_copyModel(mjModel* dest, const mjModel* src);
// save model to binary file
MJAPI void mj_saveModel(const mjModel* m, const char* filename, void* buffer, int buffer_sz);
+37
View File
@@ -193,6 +193,43 @@ TEST_F(EngineIoTest, MakeDataResetsAllArenaPointerSizes) {
mj_deleteModel(model);
}
TEST_F(EngineIoTest, MjvCopyModel) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<mesh name="tet" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
</asset>
<worldbody>
<geom type="mesh" mesh="tet"/>
</worldbody>
</mujoco>
)";
char error[1024];
mjModel* model1 = LoadModelFromString(xml, error, sizeof(error));
ASSERT_THAT(model1, NotNull()) << error;
mjModel* model2 = mj_copyModel(nullptr, model1);
ASSERT_THAT(model2, NotNull()) << error;
model1->mesh_vert[0] = 0.1;
model1->geom_rgba[0] = 0.2;
mj_copyModel(model2, model1);
EXPECT_FLOAT_EQ(model2->mesh_vert[0], 0.1);
EXPECT_FLOAT_EQ(model2->geom_rgba[0], 0.2);
model1->mesh_vert[0] = 0.3;
model1->geom_rgba[0] = 0.4;
mjv_copyModel(model2, model1);
EXPECT_FLOAT_EQ(model2->mesh_vert[0], 0.1); // unchanged
EXPECT_FLOAT_EQ(model2->geom_rgba[0], 0.4);
// mj_deleteData(data);
mj_deleteModel(model2);
mj_deleteModel(model1);
}
using ValidateReferencesTest = MujocoTest;
TEST_F(ValidateReferencesTest, BodyReferences) {
+3
View File
@@ -6940,6 +6940,9 @@ public static unsafe extern void mjv_updateScene(mjModel_* m, mjData_* d, mjvOpt
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern int mjv_updateSceneFromState(mjvSceneState_* scnstate, mjvOption_* opt, mjvPerturb_* pert, mjvCamera_* cam, int catmask, mjvScene_* scn);
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void mjv_copyModel(mjModel_* dest, mjModel_* src);
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void mjv_defaultSceneState(mjvSceneState_* scnstate);