Adds built-in superellipsoid mesh.

PiperOrigin-RevId: 791196831
Change-Id: I1d864f6e620870f016377a4d9e2e26d1a23fd77e
This commit is contained in:
Yuval Tassa
2025-08-05 07:01:52 -07:00
committed by Copybara-Service
parent e74cee652f
commit 705ec6d7fb
12 changed files with 253 additions and 137 deletions
+9
View File
@@ -1348,6 +1348,15 @@ The full list of processing steps applied by the compiler to each mesh is as fol
**nvert**: integer >= 3: The number vertices in the polygon. **nvert**: integer >= 3: The number vertices in the polygon.
|br| **radius**: real in [0, 1]: The radius of the top face. |br| **radius**: real in [0, 1]: The radius of the top face.
:at-val:`supersphere` (resolution, e, n)
A generalization of a sphere, also known as a superellipsoid (we use "supersphere" since semiaxis rescaling is
performed by the :ref:`scale<asset-mesh-scale>` attribute). If the **n** and **e** parameters are both 1, the
shape is a sphere. See `here <https://en.wikipedia.org/wiki/Superellipsoid>`__ for the definition of superspheres.
**resolution** integer >= 4: The discretization of both major and minor radii.
|br| **e**: real >= 0: The "east-west" exponent.
|br| **n**: real >= 0: The "north-south" exponent.
:at-val:`supertorus` (resolution, radius, s, t) :at-val:`supertorus` (resolution, radius, s, t)
A generalization of a torus with major radius of 1 and given minor radius. If the **s** and **t** parameters are A generalization of a torus with major radius of 1 and given minor radius. If the **s** and **t** parameters are
both 1, the shape is a torus. See `here <https://en.wikipedia.org/wiki/Supertoroid>`__ for details regarding both 1, the shape is a torus. See `here <https://en.wikipedia.org/wiki/Supertoroid>`__ for details regarding
+1
View File
@@ -1789,6 +1789,7 @@ typedef enum mjtMeshBuiltin_ { // type of built-in procedural mesh
mjMESH_BUILTIN_SPHERE, // sphere mjMESH_BUILTIN_SPHERE, // sphere
mjMESH_BUILTIN_HEMISPHERE, // hemisphere mjMESH_BUILTIN_HEMISPHERE, // hemisphere
mjMESH_BUILTIN_CONE, // cone mjMESH_BUILTIN_CONE, // cone
mjMESH_BUILTIN_SUPERSPHERE, // supersphere
mjMESH_BUILTIN_SUPERTORUS, // supertorus mjMESH_BUILTIN_SUPERTORUS, // supertorus
mjMESH_BUILTIN_WEDGE, // wedge mjMESH_BUILTIN_WEDGE, // wedge
mjMESH_BUILTIN_PLATE, // plate mjMESH_BUILTIN_PLATE, // plate
+1
View File
@@ -75,6 +75,7 @@ typedef enum mjtMeshBuiltin_ { // type of built-in procedural mesh
mjMESH_BUILTIN_SPHERE, // sphere mjMESH_BUILTIN_SPHERE, // sphere
mjMESH_BUILTIN_HEMISPHERE, // hemisphere mjMESH_BUILTIN_HEMISPHERE, // hemisphere
mjMESH_BUILTIN_CONE, // cone mjMESH_BUILTIN_CONE, // cone
mjMESH_BUILTIN_SUPERSPHERE, // supersphere
mjMESH_BUILTIN_SUPERTORUS, // supertorus mjMESH_BUILTIN_SUPERTORUS, // supertorus
mjMESH_BUILTIN_WEDGE, // wedge mjMESH_BUILTIN_WEDGE, // wedge
mjMESH_BUILTIN_PLATE, // plate mjMESH_BUILTIN_PLATE, // plate
+4 -3
View File
@@ -801,9 +801,10 @@ ENUMS: Mapping[str, EnumDecl] = dict([
('mjMESH_BUILTIN_SPHERE', 1), ('mjMESH_BUILTIN_SPHERE', 1),
('mjMESH_BUILTIN_HEMISPHERE', 2), ('mjMESH_BUILTIN_HEMISPHERE', 2),
('mjMESH_BUILTIN_CONE', 3), ('mjMESH_BUILTIN_CONE', 3),
('mjMESH_BUILTIN_SUPERTORUS', 4), ('mjMESH_BUILTIN_SUPERSPHERE', 4),
('mjMESH_BUILTIN_WEDGE', 5), ('mjMESH_BUILTIN_SUPERTORUS', 5),
('mjMESH_BUILTIN_PLATE', 6), ('mjMESH_BUILTIN_WEDGE', 6),
('mjMESH_BUILTIN_PLATE', 7),
]), ]),
)), )),
('mjtBuiltin', ('mjtBuiltin',
+9
View File
@@ -1041,6 +1041,15 @@ PYBIND11_MODULE(_specs, m) {
} }
}, },
py::arg("nedge"), py::arg("radius")); py::arg("nedge"), py::arg("radius"));
mjsMesh.def(
"make_supersphere",
[](raw::MjsMesh* self, int resolution, double e, double n) {
double params[3] = {static_cast<double>(resolution), e, n};
if (mjs_makeMesh(self, mjMESH_BUILTIN_SUPERSPHERE, params, 3)) {
throw pybind11::value_error(mjs_getError(mjs_getSpec(self->element)));
}
},
py::arg("resolution"), py::arg("e"), py::arg("n"));
mjsMesh.def( mjsMesh.def(
"make_supertorus", "make_supertorus",
[](raw::MjsMesh* self, int resolution, double radius, double s, [](raw::MjsMesh* self, int resolution, double radius, double s,
+6 -2
View File
@@ -502,17 +502,21 @@ class SpecsTest(absltest.TestCase):
mesh = spec.add_mesh(name='sphere') mesh = spec.add_mesh(name='sphere')
mesh.make_sphere(subdivision=2) mesh.make_sphere(subdivision=2)
mesh = spec.add_mesh(name='supertoroid') mesh = spec.add_mesh(name='supertorus')
mesh.make_supertorus(resolution=10, radius=0.5, s=1, t=1) mesh.make_supertorus(resolution=10, radius=0.5, s=1, t=1)
mesh = spec.add_mesh(name='supersphere')
mesh.make_supersphere(resolution=20, e=2, n=1)
model = spec.compile() model = spec.compile()
self.assertEqual(model.nmesh, 6) self.assertEqual(model.nmesh, 7)
self.assertEqual(model.mesh_vertnum[0], 25 * 25) self.assertEqual(model.mesh_vertnum[0], 25 * 25)
self.assertEqual(model.mesh_vertnum[1], 10) self.assertEqual(model.mesh_vertnum[1], 10)
self.assertEqual(model.mesh_vertnum[2], 7) self.assertEqual(model.mesh_vertnum[2], 7)
self.assertEqual(model.mesh_vertnum[3], 2 * (4 + 1) * (4 + 2) + 2) self.assertEqual(model.mesh_vertnum[3], 2 * (4 + 1) * (4 + 2) + 2)
self.assertEqual(model.mesh_vertnum[4], 2 + 10 * 4**2) self.assertEqual(model.mesh_vertnum[4], 2 + 10 * 4**2)
self.assertEqual(model.mesh_vertnum[5], 100) self.assertEqual(model.mesh_vertnum[5], 100)
self.assertEqual(model.mesh_vertnum[6], 20 * (20 -1) + 2)
def test_compile_errors_with_line_info(self): def test_compile_errors_with_line_info(self):
spec = mujoco.MjSpec() spec = mujoco.MjSpec()
+24
View File
@@ -542,6 +542,30 @@ int mjs_makeMesh(mjsMesh* mesh, mjtMeshBuiltin builtin, double* params, int npar
return 0; return 0;
} }
case mjMESH_BUILTIN_SUPERSPHERE: {
if (nparams != 3) {
m->SetError(mjCError(0, "Supersphere mesh type requires 3 parameters"));
return -1;
}
int res = static_cast<int>(params[0]);
if (res < 3) {
m->SetError(mjCError(0, "Supersphere resolution must be greater than 2"));
return -1;
}
double e = params[1];
if (e < 0) {
m->SetError(mjCError(0, "Supersphere 'e' cannot be negative"));
return -1;
}
double n = params[2];
if (n < 0) {
m->SetError(mjCError(0, "Supersphere 'n' cannot be negative"));
return -1;
}
meshC->MakeSupersphere(res, e, n);
return 0;
}
case mjMESH_BUILTIN_SUPERTORUS: { case mjMESH_BUILTIN_SUPERTORUS: {
if (nparams != 4) { if (nparams != 4) {
m->SetError(mjCError(0, "Supertorus mesh type requires 4 parameters")); m->SetError(mjCError(0, "Supertorus mesh type requires 4 parameters"));
+62
View File
@@ -2292,6 +2292,68 @@ void mjCMesh::MakeSphere(int subdiv, bool make_faces) {
// make a mesh of a supersphere
void mjCMesh::MakeSupersphere(int res, double e, double n) {
// allocate vertices and faces
int nvert = (res - 1) * res + 2;
int nface = 2 * res * (res - 1);
std::vector<float> vert;
vert.reserve(3 * nvert);
std::vector<int> face;
face.reserve(3 * nface);
// south pole
vert.insert(vert.end(), {0.0f, 0.0f, -1.0f});
// rings
for (int i = 1; i < res; i++) {
double v = -mjPI/2 + i * mjPI / res;
for (int j = 0; j < res; j++) {
double u = -mjPI + j * 2 * mjPI / res;
vert.push_back(aux_c(v, n) * aux_c(u, e));
vert.push_back(aux_c(v, n) * aux_s(u, e));
vert.push_back(aux_s(v, n));
}
}
// north pole
vert.insert(vert.end(), {0.0f, 0.0f, 1.0f});
// south pole faces
for (int j = 0; j < res; j++) {
int v2 = 1 + j;
int v3 = 1 + (j + 1) % res;
face.insert(face.end(), {0, v3, v2});
}
// ring faces
for (int i = 0; i < res - 2; i++) {
for (int j = 0; j < res; j++) {
int v1 = 1 + i * res + j;
int v2 = 1 + i * res + (j + 1) % res;
int v4 = 1 + (i + 1) * res + j;
int v3 = 1 + (i + 1) * res + (j + 1) % res;
face.insert(face.end(), {v1, v2, v4});
face.insert(face.end(), {v2, v3, v4});
}
}
// north pole faces
int north_pole_idx = nvert - 1;
int last_ring_start_idx = 1 + (res - 2) * res;
for (int j = 0; j < res; j++) {
int v1 = last_ring_start_idx + j;
int v2 = last_ring_start_idx + (j + 1) % res;
face.insert(face.end(), {v1, v2, north_pole_idx});
}
// save vertices and faces
mjs_setFloat(spec.uservert, vert.data(), vert.size());
mjs_setInt(spec.userface, face.data(), face.size());
}
// make a mesh of a torus (subsumed by supertorus, kept for reference only) // make a mesh of a torus (subsumed by supertorus, kept for reference only)
void mjCMesh::MakeTorus(int res, double radius) { void mjCMesh::MakeTorus(int res, double radius) {
// allocate vertices and faces // allocate vertices and faces
+1
View File
@@ -1055,6 +1055,7 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
void MakeSphere(int subdiv, bool make_faces); void MakeSphere(int subdiv, bool make_faces);
void MakeTorus(int res, double radius); void MakeTorus(int res, double radius);
void MakeSupertorus(int res, double radius, double s, double t); void MakeSupertorus(int res, double radius, double s, double t);
void MakeSupersphere(int res, double e, double n);
void MakeWedge(int resolution[2], double fov[2], double gamma); void MakeWedge(int resolution[2], double fov[2], double gamma);
void MakeRect(int resolution[2]); void MakeRect(int resolution[2]);
void MakeCone(int nedge, double radius); void MakeCone(int nedge, double radius);
+2 -1
View File
@@ -827,13 +827,14 @@ const mjMap meshinertia_map[4] = {
// mesh builtin type // mesh builtin type
const int meshbuiltin_sz = 7; const int meshbuiltin_sz = 8;
const mjMap meshbuiltin_map[meshbuiltin_sz] = { const mjMap meshbuiltin_map[meshbuiltin_sz] = {
{"none", mjMESH_BUILTIN_NONE}, {"none", mjMESH_BUILTIN_NONE},
{"sphere", mjMESH_BUILTIN_SPHERE}, {"sphere", mjMESH_BUILTIN_SPHERE},
{"hemisphere", mjMESH_BUILTIN_HEMISPHERE}, {"hemisphere", mjMESH_BUILTIN_HEMISPHERE},
{"cone", mjMESH_BUILTIN_CONE}, {"cone", mjMESH_BUILTIN_CONE},
{"supertorus", mjMESH_BUILTIN_SUPERTORUS}, {"supertorus", mjMESH_BUILTIN_SUPERTORUS},
{"supersphere", mjMESH_BUILTIN_SUPERSPHERE},
{"wedge", mjMESH_BUILTIN_WEDGE}, {"wedge", mjMESH_BUILTIN_WEDGE},
{"plate", mjMESH_BUILTIN_PLATE} {"plate", mjMESH_BUILTIN_PLATE}
}; };
+2
View File
@@ -2,6 +2,7 @@
<asset> <asset>
<mesh name="sphere" builtin="sphere" params="2" scale=".8 .8 .8"/> <mesh name="sphere" builtin="sphere" params="2" scale=".8 .8 .8"/>
<mesh name="hemisphere" builtin="hemisphere" params="4" scale=".8 .8 .8"/> <mesh name="hemisphere" builtin="hemisphere" params="4" scale=".8 .8 .8"/>
<mesh name="supersphere" builtin="supersphere" params="50 2 1" scale=".8 .8 .8"/>
<mesh name="toroid" builtin="supertorus" params="50 .5 .42 1.42" scale=".8 .8 .8"/> <mesh name="toroid" builtin="supertorus" params="50 .5 .42 1.42" scale=".8 .8 .8"/>
<mesh name="wedge" builtin="wedge" params="10 10 60 60 0"/> <mesh name="wedge" builtin="wedge" params="10 10 60 60 0"/>
<mesh name="prism3" builtin="cone" params="3 1" scale=".3 .3 .3"/> <mesh name="prism3" builtin="cone" params="3 1" scale=".3 .3 .3"/>
@@ -14,6 +15,7 @@
<light pos="0 0 10"/> <light pos="0 0 10"/>
<geom mesh="sphere" type="mesh" pos="-3 0 0"/> <geom mesh="sphere" type="mesh" pos="-3 0 0"/>
<geom mesh="hemisphere" type="mesh" pos="-1 0 0"/> <geom mesh="hemisphere" type="mesh" pos="-1 0 0"/>
<geom mesh="supersphere" type="mesh" pos="2 0 2"/>
<geom mesh="toroid" type="mesh" pos="-1 0 2"/> <geom mesh="toroid" type="mesh" pos="-1 0 2"/>
<geom mesh="wedge" type="mesh" euler="90 -90 0"/> <geom mesh="wedge" type="mesh" euler="90 -90 0"/>
<geom mesh="prism5" type="mesh" pos="2 0 0" euler="90 0 0"/> <geom mesh="prism5" type="mesh" pos="2 0 0" euler="90 0 0"/>
+4 -3
View File
@@ -502,9 +502,10 @@ public enum mjtMeshBuiltin : int{
mjMESH_BUILTIN_SPHERE = 1, mjMESH_BUILTIN_SPHERE = 1,
mjMESH_BUILTIN_HEMISPHERE = 2, mjMESH_BUILTIN_HEMISPHERE = 2,
mjMESH_BUILTIN_CONE = 3, mjMESH_BUILTIN_CONE = 3,
mjMESH_BUILTIN_SUPERTORUS = 4, mjMESH_BUILTIN_SUPERSPHERE = 4,
mjMESH_BUILTIN_WEDGE = 5, mjMESH_BUILTIN_SUPERTORUS = 5,
mjMESH_BUILTIN_PLATE = 6, mjMESH_BUILTIN_WEDGE = 6,
mjMESH_BUILTIN_PLATE = 7,
} }
public enum mjtBuiltin : int{ public enum mjtBuiltin : int{
mjBUILTIN_NONE = 0, mjBUILTIN_NONE = 0,