diff --git a/doc/includes/references.h b/doc/includes/references.h
index 6c0c1d41..b7a32e40 100644
--- a/doc/includes/references.h
+++ b/doc/includes/references.h
@@ -1170,7 +1170,7 @@ struct mjModel_ {
int* flex_shell; // shell fragment vertex ids (dim per frag) (nflexshelldata x 1)
int* flex_evpair; // (element, vertex) collision pairs (nflexevpair x 2)
mjtNum* flex_vert; // vertex positions in local body frames (nflexvert x 3)
- mjtNum* flex_xvert0; // Cartesian vertex positions in qpos0 (nflexvert x 3)
+ mjtNum* flex_vert0; // vertex positions in qpos0 on [0, 1]^d (nflexvert x 3)
mjtNum* flexedge_length0; // edge lengths in qpos0 (nflexedge x 1)
mjtNum* flexedge_invweight0; // edge inv. weight in qpos0 (nflexedge x 1)
mjtNum* flex_radius; // radius around primitive element (nflex x 1)
diff --git a/include/mujoco/mjmodel.h b/include/mujoco/mjmodel.h
index 7893a8b3..9bea5917 100644
--- a/include/mujoco/mjmodel.h
+++ b/include/mujoco/mjmodel.h
@@ -877,7 +877,7 @@ struct mjModel_ {
int* flex_shell; // shell fragment vertex ids (dim per frag) (nflexshelldata x 1)
int* flex_evpair; // (element, vertex) collision pairs (nflexevpair x 2)
mjtNum* flex_vert; // vertex positions in local body frames (nflexvert x 3)
- mjtNum* flex_xvert0; // Cartesian vertex positions in qpos0 (nflexvert x 3)
+ mjtNum* flex_vert0; // vertex positions in qpos0 on [0, 1]^d (nflexvert x 3)
mjtNum* flexedge_length0; // edge lengths in qpos0 (nflexedge x 1)
mjtNum* flexedge_invweight0; // edge inv. weight in qpos0 (nflexedge x 1)
mjtNum* flex_radius; // radius around primitive element (nflex x 1)
diff --git a/include/mujoco/mjxmacro.h b/include/mujoco/mjxmacro.h
index 3ca67c16..a47a0a32 100644
--- a/include/mujoco/mjxmacro.h
+++ b/include/mujoco/mjxmacro.h
@@ -346,7 +346,7 @@
XMJV( int, flex_shell, nflexshelldata,1 ) \
X ( int, flex_evpair, nflexevpair, 2 ) \
X ( mjtNum, flex_vert, nflexvert, 3 ) \
- X ( mjtNum, flex_xvert0, nflexvert, 3 ) \
+ X ( mjtNum, flex_vert0, nflexvert, 3 ) \
X ( mjtNum, flexedge_length0, nflexedge, 1 ) \
X ( mjtNum, flexedge_invweight0, nflexedge, 1 ) \
XMJV( mjtNum, flex_radius, nflex, 1 ) \
diff --git a/introspect/structs.py b/introspect/structs.py
index 0776273e..c8993c31 100644
--- a/introspect/structs.py
+++ b/introspect/structs.py
@@ -2603,11 +2603,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
array_extent=('nflexvert', 3),
),
StructFieldDecl(
- name='flex_xvert0',
+ name='flex_vert0',
type=PointerType(
inner_type=ValueType(name='mjtNum'),
),
- doc='Cartesian vertex positions in qpos0',
+ doc='vertex positions in qpos0 on [0, 1]^d',
array_extent=('nflexvert', 3),
),
StructFieldDecl(
diff --git a/src/engine/engine_setconst.c b/src/engine/engine_setconst.c
index 64d35ff8..f451353b 100644
--- a/src/engine/engine_setconst.c
+++ b/src/engine/engine_setconst.c
@@ -118,8 +118,19 @@ static void set0(mjModel* m, mjData* d) {
m->light_mode[i] = lightmode[i];
}
+ // compute bounding box coordinates
+ for (int i=0; i < m->nflex; i++) {
+ int bvhadr = m->flex_bvhadr[i];
+ const mjtNum* bvh = d->bvh_aabb_dyn + 6*(bvhadr - m->nbvhstatic);
+ for (int j=0; j < m->nflexvert; j++) {
+ for (int k=0; k < 3; k++) {
+ mjtNum size = 2*(bvh[3+k] - m->flex_radius[i]);
+ m->flex_vert0[3*j+k] = (d->flexvert_xpos[3*j+k] - bvh[k]) / size + 0.5;
+ }
+ }
+ }
+
// copy fields
- mju_copy(m->flex_xvert0, d->flexvert_xpos, 3*m->nflexvert);
mju_copy(m->flexedge_length0, d->flexedge_length, m->nflexedge);
mju_copy(m->tendon_length0, d->ten_length, m->ntendon);
mju_copy(m->actuator_length0, d->actuator_length, m->nu);
diff --git a/test/user/user_flex_test.cc b/test/user/user_flex_test.cc
index 1e2c168d..2f6414d2 100644
--- a/test/user/user_flex_test.cc
+++ b/test/user/user_flex_test.cc
@@ -242,6 +242,45 @@ TEST_F(UserFlexTest, RigidFlex) {
mj_deleteModel(m);
mj_deleteData(d);
}
+TEST_F(UserFlexTest, BoundingBoxCoordinates) {
+ static constexpr char xml[] = R"(
+
+
+
+
+
+ )";
+ std::array error;
+ mjModel* m = LoadModelFromString(xml, error.data(), error.size());
+ ASSERT_THAT(m, NotNull()) << error.data();
+ mjData* d = mj_makeData(m);
+ mj_kinematics(m, d);
+ mj_flex(m, d);
+
+ EXPECT_EQ(m->nflexvert, 5*5*5);
+ EXPECT_EQ(m->nflexelem, 4*4*4*6);
+ EXPECT_EQ(m->flex_dim[0], 3);
+
+ // Cartesian coordinates
+ EXPECT_EQ(d->flexvert_xpos[0], -1);
+ EXPECT_EQ(d->flexvert_xpos[1], -2);
+ EXPECT_EQ(d->flexvert_xpos[2], -3);
+ EXPECT_EQ(d->flexvert_xpos[3*m->nflexvert-3], 3);
+ EXPECT_EQ(d->flexvert_xpos[3*m->nflexvert-2], 2);
+ EXPECT_EQ(d->flexvert_xpos[3*m->nflexvert-1], 1);
+
+ // bounding box coordinates
+ EXPECT_EQ(m->flex_vert0[0], 0);
+ EXPECT_EQ(m->flex_vert0[1], 0);
+ EXPECT_EQ(m->flex_vert0[2], 0);
+ EXPECT_EQ(m->flex_vert0[3*m->nflexvert-3], 1);
+ EXPECT_EQ(m->flex_vert0[3*m->nflexvert-2], 1);
+ EXPECT_EQ(m->flex_vert0[3*m->nflexvert-1], 1);
+
+ mj_deleteModel(m);
+ mj_deleteData(d);
+}
TEST_F(UserFlexTest, LoadMSHBinary_41_Success) {
const std::string xml_path =
@@ -280,14 +319,16 @@ TEST_F(UserFlexTest, LoadMSHSurfaceBinary_41_Success) {
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
+ mj_kinematics(m, d);
+ mj_flex(m, d);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
EXPECT_EQ(m->flex_dim[0], 2);
// first node x y z
- EXPECT_EQ(m->flex_xvert0[0], -0.5 );
- EXPECT_EQ(m->flex_xvert0[1], -0.5 );
- EXPECT_EQ(m->flex_xvert0[2], 0 );
+ EXPECT_EQ(d->flexvert_xpos[0], -0.5 );
+ EXPECT_EQ(d->flexvert_xpos[1], -0.5 );
+ EXPECT_EQ(d->flexvert_xpos[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
@@ -306,14 +347,16 @@ TEST_F(UserFlexTest, LoadMSHSurfaceBinary_22_Success) {
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
+ mj_kinematics(m, d);
+ mj_flex(m, d);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
EXPECT_EQ(m->flex_dim[0], 2);
// first node x y z
- EXPECT_EQ(m->flex_xvert0[0], -0.5 );
- EXPECT_EQ(m->flex_xvert0[1], -0.5 );
- EXPECT_EQ(m->flex_xvert0[2], 0 );
+ EXPECT_EQ(d->flexvert_xpos[0], -0.5 );
+ EXPECT_EQ(d->flexvert_xpos[1], -0.5 );
+ EXPECT_EQ(d->flexvert_xpos[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
@@ -377,14 +420,16 @@ TEST_F(UserFlexTest, LoadMSHSurfaceASCII_41_Success) {
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
+ mj_kinematics(m, d);
+ mj_flex(m, d);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
EXPECT_EQ(m->flex_dim[0], 2);
// first node x y z
- EXPECT_EQ(m->flex_xvert0[0], -0.5 );
- EXPECT_EQ(m->flex_xvert0[1], -0.5 );
- EXPECT_EQ(m->flex_xvert0[2], 0 );
+ EXPECT_EQ(d->flexvert_xpos[0], -0.5 );
+ EXPECT_EQ(d->flexvert_xpos[1], -0.5 );
+ EXPECT_EQ(d->flexvert_xpos[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
@@ -403,14 +448,16 @@ TEST_F(UserFlexTest, LoadMSHSurfaceASCII_22_Success) {
mjModel* m = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
ASSERT_THAT(m, NotNull()) << error.data();
mjData* d = mj_makeData(m);
+ mj_kinematics(m, d);
+ mj_flex(m, d);
EXPECT_EQ(m->nflexvert, 14);
EXPECT_EQ(m->nflexelem, 24);
EXPECT_EQ(m->flex_dim[0], 2);
// first node x y z
- EXPECT_EQ(m->flex_xvert0[0], -0.5 );
- EXPECT_EQ(m->flex_xvert0[1], -0.5 );
- EXPECT_EQ(m->flex_xvert0[2], 0 );
+ EXPECT_EQ(d->flexvert_xpos[0], -0.5 );
+ EXPECT_EQ(d->flexvert_xpos[1], -0.5 );
+ EXPECT_EQ(d->flexvert_xpos[2], 0 );
// first element
EXPECT_EQ(m->flex_elem[0], 9-1 );
diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs
index e963ab1c..262be737 100644
--- a/unity/Runtime/Bindings/MjBindings.cs
+++ b/unity/Runtime/Bindings/MjBindings.cs
@@ -5428,7 +5428,7 @@ public unsafe struct mjModel_ {
public int* flex_shell;
public int* flex_evpair;
public double* flex_vert;
- public double* flex_xvert0;
+ public double* flex_vert0;
public double* flexedge_length0;
public double* flexedge_invweight0;
public double* flex_radius;