Add vertex-based flex constraints (dim=2).

PiperOrigin-RevId: 859552050
Change-Id: I61d4b9dc40f041c5b930e5f8810a803e8bccb87a
This commit is contained in:
Alessio Quaglino
2026-01-22 04:54:11 -08:00
committed by Copybara-Service
parent 2cc2e579f1
commit 54dd623650
15 changed files with 705 additions and 22 deletions
+9 -1
View File
@@ -274,6 +274,8 @@ struct mjData_ {
mjtNum* flexelem_aabb; // flex element bounding boxes (center, size) (nflexelem x 6)
mjtNum* flexedge_J; // flex edge Jacobian (nJfe x 1)
mjtNum* flexedge_length; // flex edge lengths (nflexedge x 1)
mjtNum* flexvert_J; // flex vertex Jacobian (nJfv x 2)
mjtNum* flexvert_length; // flex vertex lengths (nflexvert x 2)
mjtNum* bvh_aabb_dyn; // global bounding box (center, size) (nbvhdynamic x 6)
// computed by mj_fwdPosition/mj_tendon
@@ -1034,6 +1036,7 @@ struct mjModel_ {
int nflexevpair; // number of element-vertex pairs in all flexes
int nflextexcoord; // number of vertices with texture coordinates
int nJfe; // number of non-zeros in sparse flexedge Jacobian matrix
int nJfv; // number of non-zeros in sparse flexvert Jacobian matrix
int nmesh; // number of meshes
int nmeshvert; // number of vertices in all meshes
int nmeshnormal; // number of normals in all meshes
@@ -1328,12 +1331,13 @@ struct mjModel_ {
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)
mjtNum* flex_size; // vertex bounding box half sizes in qpos0 (nflex x 3)
mjtNum* flex_stiffness; // finite element stiffness matrix (nflexelem x 21)
mjtNum* flex_bending; // bending stiffness (nflexedge x 17)
mjtNum* flex_damping; // Rayleigh's damping coefficient (nflex x 1)
mjtNum* flex_edgestiffness; // edge stiffness (nflex x 1)
mjtNum* flex_edgedamping; // edge damping (nflex x 1)
mjtByte* flex_edgeequality; // is edge equality constraint defined (nflex x 1)
int* flex_edgeequality; // 0: none, 1: edges, 2: vertices (nflex x 1)
mjtByte* flex_rigid; // are all vertices in the same body (nflex x 1)
mjtByte* flexedge_rigid; // are both edge vertices in same body (nflexedge x 1)
mjtByte* flex_centered; // are all vertex coordinates (0,0,0) (nflex x 1)
@@ -1343,6 +1347,9 @@ struct mjModel_ {
int* flexedge_J_rownnz; // number of non-zeros in Jacobian row (nflexedge x 1)
int* flexedge_J_rowadr; // row start address in colind array (nflexedge x 1)
int* flexedge_J_colind; // column indices in sparse Jacobian (nJfe x 1)
int* flexvert_J_rownnz; // number of non-zeros in Jacobian row (nflexvert x 2)
int* flexvert_J_rowadr; // row start address in colind array (nflexvert x 2)
int* flexvert_J_colind; // column indices in sparse Jacobian (nJfv x 2)
float* flex_rgba; // rgba when material is omitted (nflex x 4)
float* flex_texcoord; // vertex texture coordinates (nflextexcoord x 2)
@@ -2186,6 +2193,7 @@ typedef struct mjsFlex_ { // flex specification
// other properties
int dim; // element dimensionality
double radius; // radius around primitive element
double size[3]; // vertex bounding box half sizes in qpos0
mjtByte internal; // enable internal collisions
mjtByte flatskin; // render flex skin with flat shading
int selfcollide; // mode for flex self collision
+2
View File
@@ -308,6 +308,8 @@ struct mjData_ {
mjtNum* flexelem_aabb; // flex element bounding boxes (center, size) (nflexelem x 6)
mjtNum* flexedge_J; // flex edge Jacobian (nJfe x 1)
mjtNum* flexedge_length; // flex edge lengths (nflexedge x 1)
mjtNum* flexvert_J; // flex vertex Jacobian (nJfv x 2)
mjtNum* flexvert_length; // flex vertex lengths (nflexvert x 2)
mjtNum* bvh_aabb_dyn; // global bounding box (center, size) (nbvhdynamic x 6)
// computed by mj_fwdPosition/mj_tendon
+6 -1
View File
@@ -702,6 +702,7 @@ struct mjModel_ {
int nflexevpair; // number of element-vertex pairs in all flexes
int nflextexcoord; // number of vertices with texture coordinates
int nJfe; // number of non-zeros in sparse flexedge Jacobian matrix
int nJfv; // number of non-zeros in sparse flexvert Jacobian matrix
int nmesh; // number of meshes
int nmeshvert; // number of vertices in all meshes
int nmeshnormal; // number of normals in all meshes
@@ -996,12 +997,13 @@ struct mjModel_ {
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)
mjtNum* flex_size; // vertex bounding box half sizes in qpos0 (nflex x 3)
mjtNum* flex_stiffness; // finite element stiffness matrix (nflexelem x 21)
mjtNum* flex_bending; // bending stiffness (nflexedge x 17)
mjtNum* flex_damping; // Rayleigh's damping coefficient (nflex x 1)
mjtNum* flex_edgestiffness; // edge stiffness (nflex x 1)
mjtNum* flex_edgedamping; // edge damping (nflex x 1)
mjtByte* flex_edgeequality; // is edge equality constraint defined (nflex x 1)
int* flex_edgeequality; // 0: none, 1: edges, 2: vertices (nflex x 1)
mjtByte* flex_rigid; // are all vertices in the same body (nflex x 1)
mjtByte* flexedge_rigid; // are both edge vertices in same body (nflexedge x 1)
mjtByte* flex_centered; // are all vertex coordinates (0,0,0) (nflex x 1)
@@ -1011,6 +1013,9 @@ struct mjModel_ {
int* flexedge_J_rownnz; // number of non-zeros in Jacobian row (nflexedge x 1)
int* flexedge_J_rowadr; // row start address in colind array (nflexedge x 1)
int* flexedge_J_colind; // column indices in sparse Jacobian (nJfe x 1)
int* flexvert_J_rownnz; // number of non-zeros in Jacobian row (nflexvert x 2)
int* flexvert_J_rowadr; // row start address in colind array (nflexvert x 2)
int* flexvert_J_colind; // column indices in sparse Jacobian (nJfv x 2)
float* flex_rgba; // rgba when material is omitted (nflex x 4)
float* flex_texcoord; // vertex texture coordinates (nflextexcoord x 2)
+1
View File
@@ -438,6 +438,7 @@ typedef struct mjsFlex_ { // flex specification
// other properties
int dim; // element dimensionality
double radius; // radius around primitive element
double size[3]; // vertex bounding box half sizes in qpos0
mjtByte internal; // enable internal collisions
mjtByte flatskin; // render flex skin with flat shading
int selfcollide; // mode for flex self collision
+8 -1
View File
@@ -97,6 +97,7 @@
X( nflexevpair ) \
X( nflextexcoord ) \
X( nJfe ) \
X( nJfv ) \
X( nmesh ) \
X( nmeshvert ) \
X( nmeshnormal ) \
@@ -386,12 +387,13 @@
X ( mjtNum, flexedge_length0, nflexedge, 1 ) \
X ( mjtNum, flexedge_invweight0, nflexedge, 1 ) \
X ( mjtNum, flex_radius, nflex, 1 ) \
X ( mjtNum, flex_size, nflex, 3 ) \
X ( mjtNum, flex_stiffness, nflexelem, 21 ) \
X ( mjtNum, flex_bending, nflexedge, 17 ) \
X ( mjtNum, flex_damping, nflex, 1 ) \
X ( mjtNum, flex_edgestiffness, nflex, 1 ) \
X ( mjtNum, flex_edgedamping, nflex, 1 ) \
X ( mjtByte, flex_edgeequality, nflex, 1 ) \
X ( int, flex_edgeequality, nflex, 1 ) \
X ( mjtByte, flex_rigid, nflex, 1 ) \
X ( mjtByte, flexedge_rigid, nflexedge, 1 ) \
X ( mjtByte, flex_centered, nflex, 1 ) \
@@ -401,6 +403,9 @@
X ( int, flexedge_J_rownnz, nflexedge, 1 ) \
X ( int, flexedge_J_rowadr, nflexedge, 1 ) \
X ( int, flexedge_J_colind, nJfe, 1 ) \
X ( int, flexvert_J_rownnz, nflexvert, 2 ) \
X ( int, flexvert_J_rowadr, nflexvert, 2 ) \
X ( int, flexvert_J_colind, nJfv, 2 ) \
X ( float, flex_rgba, nflex, 4 ) \
X ( float, flex_texcoord, nflextexcoord, 2 )
@@ -736,6 +741,8 @@
X ( mjtNum, flexelem_aabb, nflexelem, 6 ) \
X ( mjtNum, flexedge_J, nJfe, 1 ) \
X ( mjtNum, flexedge_length, nflexedge, 1 ) \
X ( mjtNum, flexvert_J, nJfv, 2 ) \
X ( mjtNum, flexvert_length, nflexvert, 2 ) \
X ( mjtNum, bvh_aabb_dyn, nbvhdynamic, 6 ) \
X ( int, ten_wrapadr, ntendon, 1 ) \
X ( int, ten_wrapnum, ntendon, 1 ) \
+63 -2
View File
@@ -997,6 +997,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='int'),
doc='number of non-zeros in sparse flexedge Jacobian matrix',
),
StructFieldDecl(
name='nJfv',
type=ValueType(name='int'),
doc='number of non-zeros in sparse flexvert Jacobian matrix',
),
StructFieldDecl(
name='nmesh',
type=ValueType(name='int'),
@@ -2880,6 +2885,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
doc='radius around primitive element',
array_extent=('nflex',),
),
StructFieldDecl(
name='flex_size',
type=PointerType(
inner_type=ValueType(name='mjtNum'),
),
doc='vertex bounding box half sizes in qpos0',
array_extent=('nflex', 3),
),
StructFieldDecl(
name='flex_stiffness',
type=PointerType(
@@ -2923,9 +2936,9 @@ STRUCTS: Mapping[str, StructDecl] = dict([
StructFieldDecl(
name='flex_edgeequality',
type=PointerType(
inner_type=ValueType(name='mjtByte'),
inner_type=ValueType(name='int'),
),
doc='is edge equality constraint defined',
doc='0: none, 1: edges, 2: vertices',
array_extent=('nflex',),
),
StructFieldDecl(
@@ -3000,6 +3013,30 @@ STRUCTS: Mapping[str, StructDecl] = dict([
doc='column indices in sparse Jacobian',
array_extent=('nJfe',),
),
StructFieldDecl(
name='flexvert_J_rownnz',
type=PointerType(
inner_type=ValueType(name='int'),
),
doc='number of non-zeros in Jacobian row',
array_extent=('nflexvert', 2),
),
StructFieldDecl(
name='flexvert_J_rowadr',
type=PointerType(
inner_type=ValueType(name='int'),
),
doc='row start address in colind array',
array_extent=('nflexvert', 2),
),
StructFieldDecl(
name='flexvert_J_colind',
type=PointerType(
inner_type=ValueType(name='int'),
),
doc='column indices in sparse Jacobian',
array_extent=('nJfv', 2),
),
StructFieldDecl(
name='flex_rgba',
type=PointerType(
@@ -5609,6 +5646,22 @@ STRUCTS: Mapping[str, StructDecl] = dict([
doc='flex edge lengths',
array_extent=('nflexedge',),
),
StructFieldDecl(
name='flexvert_J',
type=PointerType(
inner_type=ValueType(name='mjtNum'),
),
doc='flex vertex Jacobian',
array_extent=('nJfv', 2),
),
StructFieldDecl(
name='flexvert_length',
type=PointerType(
inner_type=ValueType(name='mjtNum'),
),
doc='flex vertex lengths',
array_extent=('nflexvert', 2),
),
StructFieldDecl(
name='bvh_aabb_dyn',
type=PointerType(
@@ -7879,6 +7932,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='double'),
doc='radius around primitive element',
),
StructFieldDecl(
name='size',
type=ArrayType(
inner_type=ValueType(name='double'),
extents=(3,),
),
doc='vertex bounding box half sizes in qpos0',
),
StructFieldDecl(
name='internal',
type=ValueType(name='mjtByte'),
+319
View File
@@ -537,6 +537,7 @@ void mj_updateDynamicBVH(const mjModel* m, mjData* d, int bvhadr, int bvhnum) {
void mj_flex(const mjModel* m, mjData* d) {
int nv = m->nv;
int* rowadr = m->flexedge_J_rowadr, *rownnz = m->flexedge_J_rownnz;
int* vrowadr = m->flexvert_J_rowadr, *vrownnz = m->flexvert_J_rownnz;
// skip if no flexes
if (!m->nflex) {
@@ -659,6 +660,9 @@ void mj_flex(const mjModel* m, mjData* d) {
// clear Jacobian
mju_zeroInt(rowadr, m->nflexedge);
mju_zeroInt(rownnz, m->nflexedge);
mju_zeroInt(vrowadr, 2*m->nflexvert);
mju_zeroInt(vrownnz, 2*m->nflexvert);
mju_zero(d->flexvert_J, 2*m->nJfv);
// compute lengths and Jacobians of edges
for (int f=0; f < m->nflex; f++) {
@@ -715,6 +719,321 @@ void mj_flex(const mjModel* m, mjData* d) {
rownnz[ebase+e] = NV;
mju_copyInt(m->flexedge_J_colind + rowadr[ebase+e], chain, NV);
}
// if dim=2 and constraints are active we use the vertex-based constraint defined in
// Chen, Kry, and Vouga, "Locking-free Simulation of Isometric Thin Plates", 2019.
if (m->flex_dim[f] == 2 && m->flex_edgeequality[f] == 2) {
int nvert = m->flex_vertnum[f];
mjtNum edge1[3], edge2[3], normal[3];
mjtNum quat[4], mat[9];
int t_adr, t0, t1, t2;
mj_markStack(d);
int* buf_ind = mjSTACKALLOC(d, nv, int);
// compute normal from first element
t_adr = m->flex_elemdataadr[f];
t0 = m->flex_elem[t_adr];
t1 = m->flex_elem[t_adr+1];
t2 = m->flex_elem[t_adr+2];
mju_sub3(edge1, m->flex_vert0 + 3*(vbase+t1), m->flex_vert0 + 3*(vbase+t0));
mju_sub3(edge2, m->flex_vert0 + 3*(vbase+t2), m->flex_vert0 + 3*(vbase+t0));
mji_cross(normal, edge1, edge2);
mju_normalize3(normal);
// compute rotation to Z
mju_quatZ2Vec(quat, normal);
mju_quat2Mat(mat, quat);
// compute edge vectors
mjtNum* edge_dx = mjSTACKALLOC(d, 3*m->flex_edgenum[f], mjtNum);
mjtNum* edge_dy = mjSTACKALLOC(d, 3*m->flex_edgenum[f], mjtNum);
for (int e=0; e < m->flex_edgenum[f]; e++) {
int v1 = m->flex_edge[2*(ebase+e)];
int v2 = m->flex_edge[2*(ebase+e)+1];
mjtNum dx3[3];
mju_sub3(dx3, m->flex_vert0 + 3*(vbase+v2), m->flex_vert0 + 3*(vbase+v1));
dx3[0] *= 2*m->flex_size[3*f+0];
dx3[1] *= 2*m->flex_size[3*f+1];
dx3[2] *= 2*m->flex_size[3*f+2];
mji_mulMatTVec3(edge_dx+3*e, mat, dx3);
if (mju_abs((edge_dx+3*e)[2]) > mjMINVAL) {
mjERROR("flex vertices are not in the same plane"); // SHOULD NOT OCCUR
}
mju_sub3(edge_dy+3*e, d->flexvert_xpos+3*(vbase+v2), d->flexvert_xpos+3*(vbase+v1));
}
// build vertex adjacency list
int* v_edge_cnt = mjSTACKALLOC(d, nvert, int);
int* v_edge_adr = mjSTACKALLOC(d, nvert, int);
int* adj_edges = mjSTACKALLOC(d, 2*m->flex_edgenum[f], int);
mju_zeroInt(v_edge_cnt, nvert);
for (int e = 0; e < m->flex_edgenum[f]; ++e) {
v_edge_cnt[m->flex_edge[2*(ebase+e)+0]]++;
v_edge_cnt[m->flex_edge[2*(ebase+e)+1]]++;
}
int total_adj_edges = 0;
for (int v = 0; v < nvert; ++v) {
v_edge_adr[v] = total_adj_edges;
total_adj_edges += v_edge_cnt[v];
}
int* v_edge_fill = mjSTACKALLOC(d, nvert, int);
mju_zeroInt(v_edge_fill, nvert);
for (int e = 0; e < m->flex_edgenum[f]; ++e) {
int v1 = m->flex_edge[2*(ebase+e)+0];
int v2 = m->flex_edge[2*(ebase+e)+1];
adj_edges[v_edge_adr[v1] + v_edge_fill[v1]] = e;
v_edge_fill[v1]++;
adj_edges[v_edge_adr[v2] + v_edge_fill[v2]] = e;
v_edge_fill[v2]++;
}
mjtNum* F_vert = mjSTACKALLOC(d, 6*nvert, mjtNum);
mjtNum* Binv_vert = mjSTACKALLOC(d, 4*nvert, mjtNum);
// compute averaged Cauchy strain tensors for each vertex
for (int v=0; v < nvert; v++) {
mjtNum A[6] = {0}, B[4] = {0};
int k, edge_idx;
for (k=0; k<v_edge_cnt[v]; k++) {
edge_idx = adj_edges[v_edge_adr[v]+k];
mjtNum* dx = edge_dx+3*edge_idx;
mjtNum* dy = edge_dy+3*edge_idx;
// get mass of neighbor vertex
mjtNum weight = 1.0;
int v1 = m->flex_edge[2 * (ebase + edge_idx)];
int v2 = m->flex_edge[2 * (ebase + edge_idx) + 1];
int neighbor_v = (v == v1) ? v2 : v1;
int b_neighbor = m->flex_vertbodyid[vbase + neighbor_v];
if (b_neighbor >= 0) {
weight = m->body_mass[b_neighbor];
if (weight < mjMINVAL) weight = mjMINVAL;
}
// accumulate A += w * dy * dx', B += w * dx * dx'
for (int row=0; row < 3; row++) {
for (int col=0; col < 2; col++) {
A[2 * row + col] += weight * dy[row] * dx[col];
}
}
for (int row=0; row < 2; row++) {
for (int col=0; col < 2; col++) {
B[2 * row + col] += weight * dx[row] * dx[col];
}
}
}
int vadr = vbase+v;
mjtNum* F = F_vert + 6*v;
mjtNum* Binv = Binv_vert + 4*v;
mjtNum cauchy[2][2];
// compute Binv = B^-1
mjtNum det = B[0]*B[3] - B[1]*B[2];
if (mju_abs(det) < mjMINVAL) {
mju_zero(Binv, 4);
} else {
mjtNum invdet = 1/det;
Binv[0] = B[3]*invdet;
Binv[1] = -B[1]*invdet;
Binv[2] = -B[2]*invdet;
Binv[3] = B[0]*invdet;
}
// compute deformation gradient F = A * Binv
mju_mulMatMat(F, A, Binv, 3, 2, 2);
// compute Cauchy strain tensor F^T F
cauchy[0][0] = F[0]*F[0] + F[2]*F[2] + F[4]*F[4];
cauchy[0][1] = F[0]*F[1] + F[2]*F[3] + F[4]*F[5];
cauchy[1][0] = F[1]*F[0] + F[3]*F[2] + F[5]*F[4];
cauchy[1][1] = F[1]*F[1] + F[3]*F[3] + F[5]*F[5];
// compute tensor invariants
d->flexvert_length[2*vadr+0] = cauchy[0][0] + cauchy[1][1] - 2;
d->flexvert_length[2*vadr+1] = cauchy[0][0] * cauchy[1][1] -
cauchy[0][1] * cauchy[1][0] - 1;
}
// 1st pass: compute vrownnz
int* chain1 = mjSTACKALLOC(d, nv, int);
int* chain2 = mjSTACKALLOC(d, nv, int);
// determine start address for this flex
int v0_base = 2*vbase;
int current_adr = 0;
if (v0_base > 0) {
current_adr = vrowadr[v0_base - 1] + vrownnz[v0_base - 1];
}
vrowadr[v0_base] = current_adr;
for (int v=0; v<nvert; ++v) {
// clear buf_ind
mju_zeroInt(buf_ind, nv);
int current_nnz = 0;
for (int i=0; i<v_edge_cnt[v]; ++i) {
int e = adj_edges[v_edge_adr[v]+i];
int v1 = m->flex_edge[2*(ebase+e)];
int v2 = m->flex_edge[2*(ebase+e)+1];
// chains from edge e
int b1 = m->flex_vertbodyid[vbase+v1];
int b2 = m->flex_vertbodyid[vbase+v2];
int NV1 = mj_bodyChain(m, b1, chain1);
int NV2 = mj_bodyChain(m, b2, chain2);
for (int j=0; j<NV1; ++j) {
if (!buf_ind[chain1[j]]) {
buf_ind[chain1[j]] = 1;
current_nnz++;
}
}
for (int j=0; j<NV2; ++j) {
if (!buf_ind[chain2[j]]) {
buf_ind[chain2[j]] = 1;
current_nnz++;
}
}
}
int row0 = 2*(vbase+v);
int row1 = 2*(vbase+v)+1;
vrownnz[row0] = vrownnz[row1] = current_nnz;
// set rowadr for next rows
vrowadr[row1] = vrowadr[row0] + current_nnz;
if (row1 + 1 < 2*m->nflexvert) {
vrowadr[row1+1] = vrowadr[row1] + current_nnz;
}
// fill colind
int count = 0;
for (int j=0; j<nv; j++) {
if (buf_ind[j]) {
m->flexvert_J_colind[vrowadr[row0]+count] = j;
m->flexvert_J_colind[vrowadr[row1]+count] = j;
count++;
}
}
}
// 2nd pass: clear Jacobian and assemble vertex by vertex
mjtNum* J0_dense = mjSTACKALLOC(d, nv, mjtNum);
mjtNum* J1_dense = mjSTACKALLOC(d, nv, mjtNum);
mjtNum dI1dy1[3], dI1dy2[3], FB[6];
mjtNum dI2dy1[3], dI2dy2[3];
mjtNum cauchy[4], adj[4], Fadj[6], FadjBinv[6], dI2dy[3];
for (int v=0; v<nvert; v++) {
mju_zero(J0_dense, nv);
mju_zero(J1_dense, nv);
mjtNum* F = F_vert + 6*v;
mjtNum* Binv = Binv_vert + 4*v;
// precompute for I1
mju_mulMatMat(FB, F, Binv, 3, 2, 2);
// precompute for I2
cauchy[0] = F[0]*F[0] + F[2]*F[2] + F[4]*F[4]; // c00
cauchy[1] = F[0]*F[1] + F[2]*F[3] + F[4]*F[5]; // c01
cauchy[3] = F[1]*F[1] + F[3]*F[3] + F[5]*F[5]; // c11
adj[0] = cauchy[3];
adj[1] = -cauchy[1];
adj[2] = -cauchy[1];
adj[3] = cauchy[0];
mju_mulMatMat(Fadj, F, adj, 3, 2, 2);
mju_mulMatMat(FadjBinv, Fadj, Binv, 3, 2, 2);
for (int i=0; i<v_edge_cnt[v]; ++i) {
int e = adj_edges[v_edge_adr[v]+i];
int v1 = m->flex_edge[2*(ebase+e)];
int v2 = m->flex_edge[2*(ebase+e)+1];
// reuse precomputed edge vector
mjtNum* dx = edge_dx + 3 * e;
// get mass of neighbor vertex
mjtNum weight = 1.0;
int neighbor_v = (v == v1) ? v2 : v1;
int b_neighbor = m->flex_vertbodyid[vbase + neighbor_v];
if (b_neighbor >= 0) {
weight = m->body_mass[b_neighbor];
if (weight < mjMINVAL) weight = mjMINVAL;
}
// dI1/dy1, dI1/dy2 (scaled by weight)
mju_mulMatVec(dI1dy1, FB, dx, 3, 2);
mju_scl3(dI1dy1, dI1dy1, -2 * weight);
mju_scl3(dI1dy2, dI1dy1, -1); // dI1dy2 = -dI1dy1
// dI2/dy1, dI2/dy2 (scaled by weight)
mju_mulMatVec(dI2dy, FadjBinv, dx, 3, 2);
mju_scl3(dI2dy1, dI2dy, -2 * weight);
mju_scl3(dI2dy2, dI2dy1, -1); // dI2dy2 = -dI2dy1
// get endpoint Jacobians
int b1 = m->flex_vertbodyid[vbase+v1];
int b2 = m->flex_vertbodyid[vbase+v2];
int NV1 = mj_bodyChain(m, b1, chain1);
mj_jacSparse(m, d, jac1, NULL, d->flexvert_xpos + 3*(vbase+v1), b1, NV1, chain1);
int NV2 = mj_bodyChain(m, b2, chain2);
mj_jacSparse(m, d, jac2, NULL, d->flexvert_xpos + 3*(vbase+v2), b2, NV2, chain2);
// accumulate dense Jacobians for vertex v
for (int j=0; j<NV1; j++) {
J0_dense[chain1[j]] += dI1dy1[0]*jac1[j] + dI1dy1[1]*jac1[j+NV1] + dI1dy1[2]*jac1[j+2*NV1];
}
for (int j=0; j<NV2; j++) {
J0_dense[chain2[j]] += dI1dy2[0]*jac2[j] + dI1dy2[1]*jac2[j+NV2] + dI1dy2[2]*jac2[j+2*NV2];
}
for (int j=0; j<NV1; j++) {
J1_dense[chain1[j]] += dI2dy1[0]*jac1[j] + dI2dy1[1]*jac1[j+NV1] + dI2dy1[2]*jac1[j+2*NV1];
}
for (int j=0; j<NV2; j++) {
J1_dense[chain2[j]] += dI2dy2[0]*jac2[j] + dI2dy2[1]*jac2[j+NV2] + dI2dy2[2]*jac2[j+2*NV2];
}
}
// copy to sparse flexvert_J
int row0 = 2*(vbase+v);
int nnz0 = vrownnz[row0];
for (int j=0; j<nnz0; j++) {
d->flexvert_J[vrowadr[row0]+j] += J0_dense[m->flexvert_J_colind[vrowadr[row0]+j]];
}
int row1 = 2*(vbase+v)+1;
int nnz1 = vrownnz[row1];
for (int j = 0; j < nnz1; j++) {
d->flexvert_J[vrowadr[row1] + j] +=
J1_dense[m->flexvert_J_colind[vrowadr[row1] + j]];
}
// mass scaling: scale constraint by sqrt(mass) to improve condition
// number
int b = m->flex_vertbodyid[vbase + v];
if (b >= 0) {
mjtNum mass = m->body_mass[b];
if (mass > mjMINVAL) {
mjtNum scale = mju_sqrt(mass);
d->flexvert_length[2 * (vbase + v) + 0] *= scale;
d->flexvert_length[2 * (vbase + v) + 1] *= scale;
nnz0 = vrownnz[row0];
for (int j = 0; j < nnz0; j++) {
d->flexvert_J[vrowadr[row0] + j] *= scale;
}
nnz1 = vrownnz[row1];
for (int j = 0; j < nnz1; j++) {
d->flexvert_J[vrowadr[row1] + j] *= scale;
}
}
}
}
mj_freeStack(d);
}
}
mj_freeStack(d);
+5 -4
View File
@@ -220,7 +220,7 @@ void mj_makeModel(mjModel** dest,
int nbvhstatic, int nbvhdynamic, int noct, int njnt, int ntree,
int nM, int nB, int nC, int nD, int ngeom, int nsite, int ncam, int nlight,
int nflex, int nflexnode, int nflexvert, int nflexedge, int nflexelem, int nflexelemdata,
int nflexelemedge, int nflexshelldata, int nflexevpair, int nflextexcoord, int nJfe,
int nflexelemedge, int nflexshelldata, int nflexevpair, int nflextexcoord, int nJfe, int nJfv,
int nmesh, int nmeshvert, int nmeshnormal, int nmeshtexcoord, int nmeshface,
int nmeshgraph, int nmeshpoly, int nmeshpolyvert, int nmeshpolymap, int nskin, int nskinvert,
int nskintexvert, int nskinface,
@@ -279,6 +279,7 @@ void mj_makeModel(mjModel** dest,
m->nflexevpair = nflexevpair;
m->nflextexcoord = nflextexcoord;
m->nJfe = nJfe;
m->nJfv = nJfv;
m->nmesh = nmesh;
m->nmeshvert = nmeshvert;
m->nmeshnormal = nmeshnormal;
@@ -406,8 +407,8 @@ mjModel* mj_copyModel(mjModel* dest, const mjModel* src) {
src->nbvhstatic, src->nbvhdynamic, src->noct, src->njnt, src->ntree,
src->nM, src->nB, src->nC, src->nD, src->ngeom, src->nsite, src->ncam,
src->nlight, src->nflex, src->nflexnode, src->nflexvert, src->nflexedge,
src->nflexelem, src->nflexelemdata, src->nflexelemedge,
src->nflexshelldata, src->nflexevpair, src->nflextexcoord, src->nJfe, src->nmesh,
src->nflexelem, src->nflexelemdata, src->nflexelemedge, src->nflexshelldata,
src->nflexevpair, src->nflextexcoord, src->nJfe, src->nJfv, src->nmesh,
src->nmeshvert, src->nmeshnormal, src->nmeshtexcoord, src->nmeshface,
src->nmeshgraph, src->nmeshpoly, src->nmeshpolyvert, src->nmeshpolymap,
src->nskin, src->nskinvert, src->nskintexvert, src->nskinface,
@@ -598,7 +599,7 @@ mjModel* mj_loadModelBuffer(const void* buffer, int buffer_sz) {
ints[49], ints[50], ints[51], ints[52], ints[53], ints[54], ints[55],
ints[56], ints[57], ints[58], ints[59], ints[60], ints[61], ints[62],
ints[63], ints[64], ints[65], ints[66], ints[67], ints[68], ints[69],
ints[70], ints[71], ints[72], ints[73], ints[74], ints[75]);
ints[70], ints[71], ints[72], ints[73], ints[74], ints[75], ints[76]);
// read mjModel mjtSize fields
mjtSize sizes[8];
+9 -8
View File
@@ -51,14 +51,15 @@ void mj_makeModel(mjModel** dest,
int nq, int nv, int nu, int na, int nbody, int nbvh, int nbvhstatic, int nbvhdynamic, int noct,
int njnt, int ntree, int nM, int nB, int nC, int nD, int ngeom, int nsite, int ncam, int nlight,
int nflex, int nflexnode, int nflexvert, int nflexedge, int nflexelem, int nflexelemdata,
int nJfe, int nflexelemedge, int nflexshelldata, int nflexevpair, int nflextexcoord, int nmesh,
int nmeshvert, int nmeshnormal, int nmeshtexcoord, int nmeshface, int nmeshgraph, int nmeshpoly,
int nmeshpolyvert, int nmeshpolymap, int nskin, int nskinvert, int nskintexvert, int nskinface,
int nskinbone, int nskinbonevert, int nhfield, int nhfielddata, int ntex, int ntexdata,
int nmat, int npair, int nexclude, int neq, int ntendon, int nwrap, int nsensor, int nnumeric,
int nnumericdata, int ntext, int ntextdata, int ntuple, int ntupledata, int nkey, int nmocap,
int nplugin, int npluginattr, int nuser_body, int nuser_jnt, int nuser_geom, int nuser_site,
int nuser_cam, int nuser_tendon, int nuser_actuator, int nuser_sensor, int nnames, int npaths);
int nflexelemedge, int nflexshelldata, int nflexevpair, int nflextexcoord, int nJfe, int nJfv,
int nmesh, int nmeshvert, int nmeshnormal, int nmeshtexcoord, int nmeshface, int nmeshgraph,
int nmeshpoly, int nmeshpolyvert, int nmeshpolymap, int nskin, int nskinvert, int nskintexvert,
int nskinface, int nskinbone, int nskinbonevert, int nhfield, int nhfielddata, int ntex,
int ntexdata, int nmat, int npair, int nexclude, int neq, int ntendon, int nwrap, int nsensor,
int nnumeric, int nnumericdata, int ntext, int ntextdata, int ntuple, int ntupledata, int nkey,
int nmocap, int nplugin, int npluginattr, int nuser_body, int nuser_jnt, int nuser_geom,
int nuser_site, int nuser_cam, int nuser_tendon, int nuser_actuator, int nuser_sensor,
int nnames, int npaths);
// copy mjModel; allocate new if dest is NULL
MJAPI mjModel* mj_copyModel(mjModel* dest, const mjModel* src);
+8 -2
View File
@@ -4392,10 +4392,16 @@ void mjCFlex::Compile(const mjVFS* vfs) {
// compute bounding box coordinates
vert0_.assign(3*nvert, 0);
const mjtNum* bvh = tree.Bvh().data();
size[0] = bvh[3] - radius;
size[1] = bvh[4] - radius;
size[2] = bvh[5] - radius;
for (int j=0; j < nvert; j++) {
for (int k=0; k < 3; k++) {
double size = 2*(bvh[k+3] - radius);
vert0_[3*j+k] = (vertxpos[3*j+k] - bvh[k]) / size + 0.5;
if (size[k] > mjMINVAL) {
vert0_[3*j+k] = (vertxpos[3*j+k] - bvh[k]) / (2*size[k]) + 0.5;
} else {
vert0_[3*j+k] = 0.5;
}
}
}
+29 -1
View File
@@ -1155,6 +1155,7 @@ void mjCModel::Clear() {
nflexevpair = 0;
nflextexcoord = 0;
nJfe = 0;
nJfv = 0;
nmeshvert = 0;
nmeshnormal = 0;
nmeshtexcoord = 0;
@@ -2166,6 +2167,31 @@ void mjCModel::SetSizes() {
nJfe += b->dofnum;
}
}
// compute nJfv
std::vector<std::vector<int>> adj(flexes_[i]->nvert);
for (const auto& edge : flexes_[i]->edge) {
adj[edge.first].push_back(edge.second);
adj[edge.second].push_back(edge.first);
}
for (int j=0; j < flexes_[i]->nvert; j++) {
std::unordered_set<int> vert_bodies;
vert_bodies.insert(flexes_[i]->vertbodyid[j]);
for (int neighbor : adj[j]) {
vert_bodies.insert(flexes_[i]->vertbodyid[neighbor]);
}
std::unordered_set<mjCBody*> bodies_in_jac;
for (int body_id : vert_bodies) {
mjCBody* b = bodies_[body_id];
while (b) {
bodies_in_jac.insert(b);
b = b->parent;
}
}
for (mjCBody* b : bodies_in_jac) {
nJfv += b->dofnum;
}
}
}
// mesh counts
@@ -3288,6 +3314,7 @@ void mjCModel::CopyObjects(mjModel* m) {
mjuu_copyvec(m->flex_solref + mjNREF * i, pfl->solref, mjNREF);
mjuu_copyvec(m->flex_solimp + mjNIMP * i, pfl->solimp, mjNIMP);
m->flex_radius[i] = (mjtNum)pfl->radius;
mjuu_copyvec(m->flex_size + 3 * i, pfl->size, 3);
mjuu_copyvec(m->flex_friction + 3 * i, pfl->friction, 3);
m->flex_margin[i] = (mjtNum)pfl->margin;
m->flex_gap[i] = (mjtNum)pfl->gap;
@@ -3363,6 +3390,7 @@ void mjCModel::CopyObjects(mjModel* m) {
m->flex_edgeequality[i] = 1;
break;
}
// TODO: support flex_edgeequality = 2
}
// copy bvh data (flex aabb computed dynamically in mjData)
@@ -4856,7 +4884,7 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
mj_makeModel(&m,
nq, nv, nu, na, nbody, nbvh, nbvhstatic, nbvhdynamic, noct, njnt, ntree, nM, nB, nC,
nD, ngeom, nsite, ncam, nlight, nflex, nflexnode, nflexvert, nflexedge, nflexelem,
nflexelemdata, nflexelemedge, nflexshelldata, nflexevpair, nflextexcoord, nJfe,
nflexelemdata, nflexelemedge, nflexshelldata, nflexevpair, nflextexcoord, nJfe, nJfv,
nmesh, nmeshvert, nmeshnormal, nmeshtexcoord, nmeshface, nmeshgraph, nmeshpoly,
nmeshpolyvert, nmeshpolymap, nskin, nskinvert, nskintexvert, nskinface, nskinbone,
nskinbonevert, nhfield, nhfielddata, ntex, ntexdata, nmat, npair, nexclude,
+2 -1
View File
@@ -99,7 +99,8 @@ class mjCModel_ : public mjsElement {
int nflexshelldata; // number of shell fragment vertex ids in all flexes
int nflexevpair; // number of element-vertex pairs in all flexes
int nflextexcoord; // number of vertex texture coordinates in all flexes
int nJfe; // number of non-zeros in sparse flex constraint Jacobian
int nJfe; // number of non-zeros in sparse flex edge constraint Jacobian
int nJfv; // number of non-zeros in sparse flex vertex constraint Jacobian
int nmeshvert; // number of vertices in all meshes
int nmeshnormal; // number of normals in all meshes
int nmeshtexcoord; // number of texture coordinates in all meshes
+201
View File
@@ -41,6 +41,7 @@ using ::testing::Eq;
using ::testing::Pointwise;
using ::testing::DoubleNear;
using ::testing::NotNull;
using ::testing::Not;
using CoreSmoothTest = MujocoTest;
@@ -876,5 +877,205 @@ TEST_F(CoreSmoothTest, FactorIs) {
mj_deleteModel(m);
}
TEST_F(CoreSmoothTest, FlexVertLengthScaling) {
constexpr char xml[] = R"(
<mujoco>
<option jacobian="sparse"/>
<worldbody>
<flexcomp name="g" type="grid" count="3 3 1" spacing=".5 .5 .5" dim="2" radius=".05">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
)";
char error[1024];
mjModel* m = LoadModelFromString(xml, error, sizeof(error));
ASSERT_THAT(m, NotNull()) << error;
mjData* d = mj_makeData(m);
// check that nJfv is correct:
// corner vertices: 2 * (1+3) * 3 + 2 * (1+2) * 3 = 42
// edge vertices: 4 * (1+4) * 3 = 60
// center vertex: 1 * (1+6) * 3 = 21
// nJfv = 42 + 60 + 21 = 123
EXPECT_EQ(m->nJfv, 123);
// Set edge equality to 2
m->flex_edgeequality[0] = 2;
// Run kinematics to populate xpos/xmat initially
mj_fwdKinematics(m, d);
// Check invariants for scale=1
// The constraints should be satisfied
int nvert = m->flex_vertnum[0];
ASSERT_EQ(nvert, 9);
for (int i=0; i < nvert; i++) {
EXPECT_NEAR(d->flexvert_length[2*i+0], 0.0, 1e-5);
EXPECT_NEAR(d->flexvert_length[2*i+1], 0.0, 1e-5);
}
// set qvel to rigid rotation
ASSERT_EQ(m->nv, 3*nvert);
mju_zero(d->qvel, m->nv);
for (int i=0; i < nvert; i++) {
const mjtNum* p = d->xpos + 3*m->flex_vertbodyid[i];
d->qvel[3*i+0] = -p[1];
d->qvel[3*i+1] = p[0];
d->qvel[3*i+2] = 1.0;
}
// check that Jacobian times velocity is zero for rigid body motion
vector<mjtNum> Jv(2*nvert, 0);
for (int i=0; i < 2*nvert; i++) {
int row_start = m->flexvert_J_rowadr[i];
int row_nnz = m->flexvert_J_rownnz[i];
for (int j=0; j < row_nnz; j++) {
Jv[i] += d->flexvert_J[row_start + j] *
d->qvel[m->flexvert_J_colind[row_start + j]];
}
}
EXPECT_THAT(Jv, Each(DoubleNear(0.0, 1e-9)));
// check sparsity pattern
int corners[] = {0, 2, 6, 8};
int edges[] = {1, 3, 5, 7};
int center[] = {4};
for (int i : corners) {
EXPECT_EQ(m->flexvert_J_rownnz[2*i+0], (i == 0 || i == 8) ? 12 : 9);
EXPECT_EQ(m->flexvert_J_rownnz[2*i+1], (i == 0 || i == 8) ? 12 : 9);
}
for (int i : edges) {
EXPECT_EQ(m->flexvert_J_rownnz[2*i+0], 15);
EXPECT_EQ(m->flexvert_J_rownnz[2*i+1], 15);
}
for (int i : center) {
EXPECT_EQ(m->flexvert_J_rownnz[2*i+0], 21);
EXPECT_EQ(m->flexvert_J_rownnz[2*i+1], 21);
}
// check rowadr
EXPECT_EQ(m->flexvert_J_rowadr[0], 0);
for (int i=1; i < 2*nvert; i++) {
EXPECT_EQ(m->flexvert_J_rowadr[i],
m->flexvert_J_rowadr[i-1] + m->flexvert_J_rownnz[i-1]);
}
// check that colind are sorted and unique
int nnzJ = 0;
for (int i = 0; i < 2*nvert; i++) {
nnzJ += m->flexvert_J_rownnz[i];
}
EXPECT_EQ(nnzJ, 2*m->nJfv);
for (int i=0; i < 2*nvert; i++) {
int row_start = m->flexvert_J_rowadr[i];
int row_nnz = m->flexvert_J_rownnz[i];
for (int j=0; j < row_nnz-1; j++) {
EXPECT_LE(m->flexvert_J_colind[row_start+j],
m->flexvert_J_colind[row_start+j+1]);
}
}
// Finite-difference check for flexvert_J
auto fd_check = [&](double tolerance) {
std::vector<mjtNum> qpos0(m->nq);
mju_copy(qpos0.data(), d->qpos, m->nq);
mj_kinematics(m, d);
mj_flex(m, d);
mjtNum eps = 1e-7;
int nflexvert = m->flex_vertnum[0];
std::vector<mjtNum> jac_fd(2 * nflexvert * m->nv);
std::vector<mjtNum> qpos_backup(m->nq);
mju_copy(qpos_backup.data(), d->qpos, m->nq);
for (int i=0; i < m->nv; ++i) {
std::vector<mjtNum> qvel(m->nv, 0);
qvel[i] = 1.0;
// plus
mju_copy(d->qpos, qpos_backup.data(), m->nq);
mj_integratePos(m, d->qpos, qvel.data(), eps);
mj_kinematics(m, d);
mj_flex(m, d);
std::vector<mjtNum> L_plus(2 * nflexvert);
for (int e = 0; e < 2 * nflexvert; ++e) {
L_plus[e] = d->flexvert_length[e];
}
// minus
mju_copy(d->qpos, qpos_backup.data(), m->nq);
mj_integratePos(m, d->qpos, qvel.data(), -eps);
mj_kinematics(m, d);
mj_flex(m, d);
std::vector<mjtNum> L_minus(2 * nflexvert);
for (int e = 0; e < 2 * nflexvert; ++e) {
L_minus[e] = d->flexvert_length[e];
}
for (int e = 0; e < 2 * nflexvert; ++e) {
jac_fd[e*m->nv + i] = (L_plus[e] - L_minus[e]) / (2*eps);
}
}
mju_copy(d->qpos, qpos_backup.data(), m->nq);
mj_kinematics(m, d);
mj_flex(m, d);
// Compare with analytic
std::vector<mjtNum> jac_analytic(2 * nflexvert * m->nv);
mju_zero(jac_analytic.data(), 2 * nflexvert * m->nv);
for (int e = 0; e < 2 * nflexvert; ++e) {
int row_start = m->flexvert_J_rowadr[e];
int row_nnz = m->flexvert_J_rownnz[e];
for (int i = 0; i < row_nnz; ++i) {
jac_analytic[e*m->nv + m->flexvert_J_colind[row_start+i]] =
d->flexvert_J[row_start+i];
}
}
EXPECT_THAT(jac_analytic, Not(Each(Eq(0))));
EXPECT_THAT(jac_analytic, Pointwise(DoubleNear(tolerance), jac_fd));
mju_copy(d->qpos, qpos0.data(), m->nq);
mj_kinematics(m, d);
mj_flex(m, d);
};
fd_check(5e-5);
// Set qpos to put flex in scale=2 configuration.
for (int i=0; i < nvert; i++) {
d->qpos[3*i+0] = d->xpos[3*(i+1)+0];
d->qpos[3*i+1] = d->xpos[3*(i+1)+1];
d->qpos[3*i+2] = d->xpos[3*(i+1)+2];
}
mj_fwdKinematics(m, d);
// Get mass scaling factor
mjtNum scale = 1.0;
int b = m->flex_vertbodyid[0];
if (b >= 0 && m->body_mass[b] > mjMINVAL) {
scale = mju_sqrt(m->body_mass[b]);
}
// Check invariants for scale=2
// F should be [2, 2]. C = F'F = 4I.
// Strain E = C - I = 3I.
// Invariant 0: Trace(E) = 3 + 3 = 6
// Invariant 1: Det(C) - 1 = 4 * 4 - 1 = 15
for (int i=0; i < nvert; i++) {
EXPECT_NEAR(d->flexvert_length[2 * i + 0], 6.0 * scale, 1e-5);
EXPECT_NEAR(d->flexvert_length[2 * i + 1], 15.0 * scale, 1e-5);
}
// Perturb z-positions so configuration is not flat
for (int i=0; i < nvert; i++) {
d->qpos[3*i+2] += 0.01 * (i%2 ? 1 : -1);
}
fd_check(5e-5);
mj_deleteData(d);
mj_deleteModel(m);
}
} // namespace
} // namespace mujoco
+8 -1
View File
@@ -4983,6 +4983,8 @@ public unsafe struct mjData_ {
public double* flexelem_aabb;
public double* flexedge_J;
public double* flexedge_length;
public double* flexvert_J;
public double* flexvert_length;
public double* bvh_aabb_dyn;
public int* ten_wrapadr;
public int* ten_wrapnum;
@@ -5314,6 +5316,7 @@ public unsafe struct mjModel_ {
public int nflexevpair;
public int nflextexcoord;
public int nJfe;
public int nJfv;
public int nmesh;
public int nmeshvert;
public int nmeshnormal;
@@ -5572,12 +5575,13 @@ public unsafe struct mjModel_ {
public double* flexedge_length0;
public double* flexedge_invweight0;
public double* flex_radius;
public double* flex_size;
public double* flex_stiffness;
public double* flex_bending;
public double* flex_damping;
public double* flex_edgestiffness;
public double* flex_edgedamping;
public byte* flex_edgeequality;
public int* flex_edgeequality;
public byte* flex_rigid;
public byte* flexedge_rigid;
public byte* flex_centered;
@@ -5587,6 +5591,9 @@ public unsafe struct mjModel_ {
public int* flexedge_J_rownnz;
public int* flexedge_J_rowadr;
public int* flexedge_J_colind;
public int* flexvert_J_rownnz;
public int* flexvert_J_rowadr;
public int* flexvert_J_colind;
public float* flex_rgba;
public float* flex_texcoord;
public int* mesh_vertadr;
+35
View File
@@ -2157,6 +2157,9 @@ struct MjsFlex {
void set_radius(double value) {
ptr_->radius = value;
}
emscripten::val size() const {
return emscripten::val(emscripten::typed_memory_view(3, ptr_->size));
}
mjtByte internal() const {
return ptr_->internal;
}
@@ -3585,6 +3588,12 @@ struct MjModel {
void set_nJfe(int value) {
ptr_->nJfe = value;
}
int nJfv() const {
return ptr_->nJfv;
}
void set_nJfv(int value) {
ptr_->nJfv = value;
}
int nmesh() const {
return ptr_->nmesh;
}
@@ -4521,6 +4530,9 @@ struct MjModel {
emscripten::val flex_radius() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->nflex, ptr_->flex_radius));
}
emscripten::val flex_size() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->nflex * 3, ptr_->flex_size));
}
emscripten::val flex_stiffness() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->nflexelem * 21, ptr_->flex_stiffness));
}
@@ -4566,6 +4578,15 @@ struct MjModel {
emscripten::val flexedge_J_colind() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->nJfe, ptr_->flexedge_J_colind));
}
emscripten::val flexvert_J_rownnz() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->nflexvert * 2, ptr_->flexvert_J_rownnz));
}
emscripten::val flexvert_J_rowadr() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->nflexvert * 2, ptr_->flexvert_J_rowadr));
}
emscripten::val flexvert_J_colind() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->nJfv * 2, ptr_->flexvert_J_colind));
}
emscripten::val flex_rgba() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->nflex * 4, ptr_->flex_rgba));
}
@@ -6306,6 +6327,12 @@ struct MjData {
emscripten::val flexedge_length() const {
return emscripten::val(emscripten::typed_memory_view(model->nflexedge, ptr_->flexedge_length));
}
emscripten::val flexvert_J() const {
return emscripten::val(emscripten::typed_memory_view(model->nJfv * 2, ptr_->flexvert_J));
}
emscripten::val flexvert_length() const {
return emscripten::val(emscripten::typed_memory_view(model->nflexvert * 2, ptr_->flexvert_length));
}
emscripten::val bvh_aabb_dyn() const {
return emscripten::val(emscripten::typed_memory_view(model->nbvhdynamic * 6, ptr_->bvh_aabb_dyn));
}
@@ -11019,6 +11046,8 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("flexedge_length", &MjData::flexedge_length)
.property("flexedge_velocity", &MjData::flexedge_velocity)
.property("flexelem_aabb", &MjData::flexelem_aabb)
.property("flexvert_J", &MjData::flexvert_J)
.property("flexvert_length", &MjData::flexvert_length)
.property("flexvert_xpos", &MjData::flexvert_xpos)
.property("geom_xmat", &MjData::geom_xmat)
.property("geom_xpos", &MjData::geom_xpos)
@@ -11334,6 +11363,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("flex_shell", &MjModel::flex_shell)
.property("flex_shelldataadr", &MjModel::flex_shelldataadr)
.property("flex_shellnum", &MjModel::flex_shellnum)
.property("flex_size", &MjModel::flex_size)
.property("flex_solimp", &MjModel::flex_solimp)
.property("flex_solmix", &MjModel::flex_solmix)
.property("flex_solref", &MjModel::flex_solref)
@@ -11351,6 +11381,9 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("flexedge_invweight0", &MjModel::flexedge_invweight0)
.property("flexedge_length0", &MjModel::flexedge_length0)
.property("flexedge_rigid", &MjModel::flexedge_rigid)
.property("flexvert_J_colind", &MjModel::flexvert_J_colind)
.property("flexvert_J_rowadr", &MjModel::flexvert_J_rowadr)
.property("flexvert_J_rownnz", &MjModel::flexvert_J_rownnz)
.property("geom_aabb", &MjModel::geom_aabb)
.property("geom_bodyid", &MjModel::geom_bodyid)
.property("geom_conaffinity", &MjModel::geom_conaffinity)
@@ -11477,6 +11510,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("nC", &MjModel::nC, &MjModel::set_nC, reference())
.property("nD", &MjModel::nD, &MjModel::set_nD, reference())
.property("nJfe", &MjModel::nJfe, &MjModel::set_nJfe, reference())
.property("nJfv", &MjModel::nJfv, &MjModel::set_nJfv, reference())
.property("nJmom", &MjModel::nJmom, &MjModel::set_nJmom, reference())
.property("nM", &MjModel::nM, &MjModel::set_nM, reference())
.property("na", &MjModel::na, &MjModel::set_na, reference())
@@ -12048,6 +12082,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("radius", &MjsFlex::radius, &MjsFlex::set_radius, reference())
.property("rgba", &MjsFlex::rgba)
.property("selfcollide", &MjsFlex::selfcollide, &MjsFlex::set_selfcollide, reference())
.property("size", &MjsFlex::size)
.property("solimp", &MjsFlex::solimp)
.property("solmix", &MjsFlex::solmix, &MjsFlex::set_solmix, reference())
.property("solref", &MjsFlex::solref)