Fix segfault in FuseStatic when body contains cameras or lights
FuseStatic deletes static (joint-less) bodies and reassigns their children to the parent body. Geoms and sites were reassigned, but cameras and lights were not, leaving dangling body pointers that crash in mjCCamera::Compile() / mjCLight::Compile(). Add camera and light reassignment in FuseStatic and FuseReindex.
This commit is contained in:
+20
-1
@@ -4138,6 +4138,8 @@ void mjCModel::FuseReindex(mjCBody* body) {
|
|||||||
makelistid(joints_, body->joints);
|
makelistid(joints_, body->joints);
|
||||||
makelistid(geoms_, body->geoms);
|
makelistid(geoms_, body->geoms);
|
||||||
makelistid(sites_, body->sites);
|
makelistid(sites_, body->sites);
|
||||||
|
makelistid(cameras_, body->cameras);
|
||||||
|
makelistid(lights_, body->lights);
|
||||||
|
|
||||||
// process children recursively
|
// process children recursively
|
||||||
for (int i=0; i < body->bodies.size(); i++) {
|
for (int i=0; i < body->bodies.size(); i++) {
|
||||||
@@ -4265,10 +4267,25 @@ void mjCModel::FuseStatic(void) {
|
|||||||
mju_error("Internal error: FuseStatic: body not found");
|
mju_error("Internal error: FuseStatic: body not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------- assign geoms and sites to parent, change frames
|
//------------- assign geoms, sites, cameras, lights to parent, change frames
|
||||||
|
|
||||||
ReassignChild(par->geoms, body->geoms, par, body);
|
ReassignChild(par->geoms, body->geoms, par, body);
|
||||||
ReassignChild(par->sites, body->sites, par, body);
|
ReassignChild(par->sites, body->sites, par, body);
|
||||||
|
ReassignChild(par->cameras, body->cameras, par, body);
|
||||||
|
|
||||||
|
// lights have dir instead of quat, so handle separately
|
||||||
|
for (int j=0; j < body->lights.size(); j++) {
|
||||||
|
body->lights[j]->body = par;
|
||||||
|
par->lights.push_back(body->lights[j]);
|
||||||
|
|
||||||
|
// transform pos into parent frame
|
||||||
|
double qunit[4] = {1, 0, 0, 0};
|
||||||
|
changeframe(body->lights[j]->pos, qunit, body->pos, body->quat);
|
||||||
|
|
||||||
|
// rotate dir into parent frame
|
||||||
|
mjuu_rotVecQuat(body->lights[j]->dir, body->lights[j]->dir, body->quat);
|
||||||
|
}
|
||||||
|
body->lights.clear();
|
||||||
|
|
||||||
//------------- remove from global body list, reduce global counts
|
//------------- remove from global body list, reduce global counts
|
||||||
|
|
||||||
@@ -4300,6 +4317,8 @@ void mjCModel::FuseStatic(void) {
|
|||||||
joints_.clear();
|
joints_.clear();
|
||||||
geoms_.clear();
|
geoms_.clear();
|
||||||
sites_.clear();
|
sites_.clear();
|
||||||
|
cameras_.clear();
|
||||||
|
lights_.clear();
|
||||||
FuseReindex(bodies_[0]);
|
FuseReindex(bodies_[0]);
|
||||||
|
|
||||||
// recompute parent contype, conaffinity, and margin
|
// recompute parent contype, conaffinity, and margin
|
||||||
|
|||||||
@@ -668,6 +668,53 @@ TEST_F(FuseStaticTest, FuseStaticForceSensorReferencedBody) {
|
|||||||
mj_deleteModel(m);
|
mj_deleteModel(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(FuseStaticTest, FuseStaticCameraInBody) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<compiler fusestatic="true"/>
|
||||||
|
<worldbody>
|
||||||
|
<body>
|
||||||
|
<joint axis="1 0 0"/>
|
||||||
|
<geom size="0.5"/>
|
||||||
|
<body pos="1 0 0">
|
||||||
|
<site name="site1"/>
|
||||||
|
<camera name="cam1"/>
|
||||||
|
</body>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
std::array<char, 1024> error;
|
||||||
|
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
|
||||||
|
ASSERT_THAT(m, NotNull()) << error.data();
|
||||||
|
EXPECT_EQ(m->nbody, 2) << "Static body should be fused";
|
||||||
|
EXPECT_EQ(m->ncam, 1);
|
||||||
|
mj_deleteModel(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FuseStaticTest, FuseStaticLightInBody) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<compiler fusestatic="true"/>
|
||||||
|
<worldbody>
|
||||||
|
<body>
|
||||||
|
<joint axis="1 0 0"/>
|
||||||
|
<geom size="0.5"/>
|
||||||
|
<body pos="1 0 0">
|
||||||
|
<light name="light1" dir="0 0 -1"/>
|
||||||
|
</body>
|
||||||
|
</body>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
std::array<char, 1024> error;
|
||||||
|
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
|
||||||
|
ASSERT_THAT(m, NotNull()) << error.data();
|
||||||
|
EXPECT_EQ(m->nbody, 2) << "Static body should be fused";
|
||||||
|
EXPECT_EQ(m->nlight, 1);
|
||||||
|
mj_deleteModel(m);
|
||||||
|
}
|
||||||
|
|
||||||
// ------------- test discardvisual --------------------------------------------
|
// ------------- test discardvisual --------------------------------------------
|
||||||
|
|
||||||
using DiscardVisualTest = MujocoTest;
|
using DiscardVisualTest = MujocoTest;
|
||||||
|
|||||||
Reference in New Issue
Block a user