Add built-in hemisphere mesh.

PiperOrigin-RevId: 791148546
Change-Id: Ic9ff0c9b0f5fa4fd22d5f7f848f150cf74d378ba
This commit is contained in:
Yuval Tassa
2025-08-05 04:15:27 -07:00
committed by Copybara-Service
parent a5a6379f83
commit 5220767457
8 changed files with 179 additions and 2 deletions
+18
View File
@@ -506,6 +506,24 @@ int mjs_makeMesh(mjsMesh* mesh, mjtMeshBuiltin builtin, double* params, int npar
mjCMesh* meshC = static_cast<mjCMesh*>(mesh->element);
mjCModel* m = meshC->model;
switch (builtin) {
case mjMESH_BUILTIN_HEMISPHERE: {
if (nparams != 1) {
m->SetError(mjCError(0, "Hemisphere mesh type requires 1 parameter"));
return -1;
}
int subdiv = static_cast<int>(params[0]);
if (subdiv < 0) {
m->SetError(mjCError(0, "Hemisphere subdivision cannot be negative"));
return -1;
}
if (subdiv > 10) {
m->SetError(mjCError(0, "Hemisphere subdivision cannot be greater than 10"));
return -1;
}
meshC->MakeHemisphere(subdiv, /*make_faces*/ true, /*make_cap*/ true);
return 0;
}
case mjMESH_BUILTIN_WEDGE: {
if (nparams != 5) {
m->SetError(mjCError(0, "Wedge builtin mesh types require 5 parameters"));
+122
View File
@@ -2056,6 +2056,128 @@ void mjCMesh::CopyGraph() {
// make a mesh of a hemisphere
void mjCMesh::MakeHemisphere(int res, bool make_faces, bool make_cap) {
constexpr double kNorthPole[3] = {0, 0, 1};
constexpr double kEquator[4][3] = {
{1, 0, 0},
{0, 1, 0},
{-1, 0, 0},
{0, -1, 0},
};
// allocate vertices
int nvert = 1 + 2 * (res + 1) * (res + 2);
nvert += make_cap && make_faces; // add center vertex for bottom cap faces
std::vector<float> vert(3 * nvert);
// north pole
vert[0] = kNorthPole[0];
vert[1] = kNorthPole[1];
vert[2] = kNorthPole[2];
// iterate through rows from north pole to equator, compute vertices
int v = 1;
for (int row = 0; row <= res; row++) {
// iterate through the four sides
for (int side = 0; side < 4; side++) {
double factor = static_cast<double>(row + 1) / (res + 1);
double start[3], end[3];
// start and end points of current arc
for (int i = 0; i < 3; i++) {
start[i] = kNorthPole[i] + factor * (kEquator[side][i] - kNorthPole[i]);
end[i] = kNorthPole[i] + factor * (kEquator[(side + 1) % 4][i] - kNorthPole[i]);
}
// step size for interpolation along the arc
double delta[3];
for (int i = 0; i < 3; i++) {
delta[i] = (end[i] - start[i]) / (row + 1);
}
// interpolate points along the arc
for (int i = 0; i < row + 1; i++) {
double p[3];
for (int j = 0; j < 3; j++) {
p[j] = start[j] + i * delta[j];
}
// normalize point to lie on hemisphere surface
double norm = std::sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
vert[3 * v + 0] = p[0] / norm;
vert[3 * v + 1] = p[1] / norm;
vert[3 * v + 2] = p[2] / norm;
v++;
}
}
}
// optional center vertex for bottom cap (for symmetry)
if (make_faces && make_cap) {
vert[3 * (nvert - 1) + 0] = 0;
vert[3 * (nvert - 1) + 1] = 0;
vert[3 * (nvert - 1) + 2] = 0;
}
// save vertices
mjs_setFloat(spec.uservert, vert.data(), 3 * nvert);
if (make_faces) {
// allocate faces
int nface = 4 * (res + 1) * (res + 1);
nface += make_cap * (4 * (res + 1)); // bottom cap faces
std::vector<int> face(3 * nface);
// faces connected to north pole
int f = 0;
face[f++] = 0; face[f++] = 1; face[f++] = 2;
face[f++] = 0; face[f++] = 2; face[f++] = 3;
face[f++] = 0; face[f++] = 3; face[f++] = 4;
face[f++] = 0; face[f++] = 4; face[f++] = 1;
// faces on the hemisphere from north pole to equator
for (int row = 0; row < res; row++) {
const int start_curr = 2 * row * (row + 1) + 1;
const int count_curr = 4 * (row + 1);
const int start_next = start_curr + count_curr;
const int count_next = 4 * (row + 2);
for (int side = 0; side < 4; side++) {
for (int i = 0; i < row + 2; i++) {
const int v_curr = i + (row + 1) * side;
const int v_next = i + (row + 2) * side;
face[f++] = start_curr + v_curr % count_curr;
face[f++] = start_next + v_next % count_next;
face[f++] = start_next + (v_next + 1) % count_next;
if (i < row + 1) {
face[f++] = start_curr + v_curr % count_curr;
face[f++] = start_next + (v_next + 1) % count_next;
face[f++] = start_curr + (v_curr + 1) % count_curr;
}
}
}
}
if (make_cap) {
// add faces for the bottom cap
const int start = 2 * res * (res + 1) + 1;
const int count = 4 * (res + 1);
for (int i = 0; i < count; i++) {
face[f++] = start + i;
face[f++] = nvert - 1;
face[f++] = start + (i + 1) % count;
}
}
// save faces
mjs_setInt(spec.userface, face.data(), 3 * nface);
}
}
// make a mesh of a spherical wedge
void mjCMesh::MakeWedge(int resolution[2], double fov[2], double gamma) {
std::vector<double> x_edges(resolution[0] + 1, 0);
+1
View File
@@ -1051,6 +1051,7 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
void NameSpace(const mjCModel* m);
// make a mesh of a predefined shape
void MakeHemisphere(int res, bool make_faces, bool make_cap);
void MakeWedge(int resolution[2], double fov[2], double gamma);
void MakeRect(int resolution[2]);
void MakeCone(int nedge, double radius);