Refactor Emscripten bindings generation for MuJoCo structs.

PiperOrigin-RevId: 828550215
Change-Id: Iab0bf3a9fbaca4d7774f89a4d8977e762aa57629
This commit is contained in:
Google DeepMind
2025-11-05 11:13:30 -08:00
committed by Copybara-Service
parent 8104235f8e
commit 3577c98f82
4 changed files with 545 additions and 758 deletions
File diff suppressed because it is too large Load Diff
+13 -7
View File
@@ -58,22 +58,28 @@ class Generator:
))
autogenned_struct_source = []
autogenned_struct_bindings = []
for struct_name in sorted_struct_names:
struct_data = self.structs_to_bind_data[struct_name]
if struct_data.wrapped_source:
autogenned_struct_source.append(
struct_data.wrapped_source + "\n"
)
# Bindings with markers
markers_and_content.append((
f"// INSERT-GENERATED-{struct_data.wrap_name}-BINDINGS",
[l.binding for l in struct_data.wrapped_fields],
))
if struct_data.bindings:
autogenned_struct_bindings.append(struct_data.bindings)
else:
# Bindings with markers
markers_and_content.append((
f"// INSERT-GENERATED-{struct_data.wrap_name}-BINDINGS",
[l.binding for l in struct_data.wrapped_fields],
))
markers_and_content.append((
"// {{ AUTOGENNED_STRUCTS_SOURCE }}",
autogenned_struct_source,
))
markers_and_content.append((
"// {{ AUTOGENNED_STRUCTS_BINDINGS }}",
autogenned_struct_bindings,
))
return markers_and_content
+41 -1
View File
@@ -69,6 +69,9 @@ class WrappedStructData:
# Struct source code
wrapped_source: str
# Struct bindings code
bindings: str = ""
# Whether to use shallow copy for this struct
use_shallow_copy: bool = True
@@ -597,7 +600,7 @@ def build_struct_source(
s = struct_name
w = common.uppercase_first_letter(s)
is_mjs = "Mjs" in w
is_mjs = w.startswith("Mjs")
fields_with_init = _find_fields_with_init(wrapped_fields)
shallow_copy = use_shallow_copy(wrapped_fields)
@@ -660,6 +663,41 @@ def build_struct_source(
return builder.to_string()
def _build_struct_bindings(
struct_name: str,
wrapped_fields: List[WrappedFieldData],
):
"""Builds the C++ bindings for a struct."""
# These structs require specific constructors
# which, for now, are hardcoded in the template file.
if struct_name in [
"mjData",
"mjModel",
"mjvScene",
"mjSpec",
]:
return ""
w = common.uppercase_first_letter(struct_name)
spc = " "
builder = code_builder.CodeBuilder()
builder.line(f"emscripten::class_<{w}>(\"{w}\")")
is_mjs = w.startswith("Mjs")
if not is_mjs:
builder.line(f"{spc}.constructor<>()")
shallow_copy = use_shallow_copy(wrapped_fields)
if shallow_copy and not is_mjs:
builder.line(f"{spc}.function(\"copy\", &{w}::copy, take_ownership())")
for field in wrapped_fields:
if field.binding:
builder.line(f"{spc}{field.binding}")
builder.line(f"{spc};")
return builder.to_string()
def parse_array_extent(
extents: Tuple[Union[str, int], ...], wrapper_name: str, field_name: str
) -> str:
@@ -733,12 +771,14 @@ def generate_wasm_bindings(
struct_name,
wrapped_fields,
)
bindings = _build_struct_bindings(struct_name, wrapped_fields)
wrap_data = WrappedStructData(
wrap_name=wrapped_name,
wrapped_fields=wrapped_fields,
wrapped_header=wrapped_header,
wrapped_source=wrapped_source,
use_shallow_copy=use_shallow_copy(wrapped_fields),
bindings=bindings,
)
wrapped_structs[struct_name] = wrap_data
+1 -228
View File
@@ -598,11 +598,6 @@ std::unique_ptr<MjSpec> parseXMLString(const std::string &xml) {
EMSCRIPTEN_BINDINGS(mujoco_bindings) {
function("parseXMLString", &parseXMLString, take_ownership());
emscripten::class_<MjLROpt>("MjLROpt")
.constructor<>()
.function("copy", &MjLROpt::copy, take_ownership())
// INSERT-GENERATED-MjLROpt-BINDINGS
;
emscripten::class_<MjModel>("MjModel")
.class_function("loadFromXML", &loadFromXML, take_ownership())
.constructor<const MjModel &>()
@@ -613,238 +608,16 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.constructor<const MjModel &, const MjData &>()
// INSERT-GENERATED-MjData-BINDINGS
;
emscripten::class_<MjOption>("MjOption")
.constructor<>()
.function("copy", &MjOption::copy, take_ownership())
// INSERT-GENERATED-MjOption-BINDINGS
;
emscripten::class_<MjStatistic>("MjStatistic")
.constructor<>()
.function("copy", &MjStatistic::copy, take_ownership())
// INSERT-GENERATED-MjStatistic-BINDINGS
;
emscripten::class_<MjVisualGlobal>("MjVisualGlobal")
.constructor<>()
.function("copy", &MjVisualGlobal::copy, take_ownership())
// INSERT-GENERATED-MjVisualGlobal-BINDINGS
;
emscripten::class_<MjVisualQuality>("MjVisualQuality")
.constructor<>()
.function("copy", &MjVisualQuality::copy, take_ownership())
// INSERT-GENERATED-MjVisualQuality-BINDINGS
;
emscripten::class_<MjVisualHeadlight>("MjVisualHeadlight")
.constructor<>()
.function("copy", &MjVisualHeadlight::copy, take_ownership())
// INSERT-GENERATED-MjVisualHeadlight-BINDINGS
;
emscripten::class_<MjVisualMap>("MjVisualMap")
.constructor<>()
.function("copy", &MjVisualMap::copy, take_ownership())
// INSERT-GENERATED-MjVisualMap-BINDINGS
;
emscripten::class_<MjVisualScale>("MjVisualScale")
.constructor<>()
.function("copy", &MjVisualScale::copy, take_ownership())
// INSERT-GENERATED-MjVisualScale-BINDINGS
;
emscripten::class_<MjVisualRgba>("MjVisualRgba")
.constructor<>()
.function("copy", &MjVisualRgba::copy, take_ownership())
// INSERT-GENERATED-MjVisualRgba-BINDINGS
;
emscripten::class_<MjVisual>("MjVisual")
.constructor<>()
.function("copy", &MjVisual::copy, take_ownership())
// INSERT-GENERATED-MjVisual-BINDINGS
;
emscripten::class_<MjSolverStat>("MjSolverStat")
.constructor<>()
.function("copy", &MjSolverStat::copy, take_ownership())
// INSERT-GENERATED-MjSolverStat-BINDINGS
;
emscripten::class_<MjTimerStat>("MjTimerStat")
.constructor<>()
.function("copy", &MjTimerStat::copy, take_ownership())
// INSERT-GENERATED-MjTimerStat-BINDINGS
;
emscripten::class_<MjWarningStat>("MjWarningStat")
.constructor<>()
.function("copy", &MjWarningStat::copy, take_ownership())
// INSERT-GENERATED-MjWarningStat-BINDINGS
;
emscripten::class_<MjContact>("MjContact")
.constructor<>()
.function("copy", &MjContact::copy, take_ownership())
// INSERT-GENERATED-MjContact-BINDINGS
;
emscripten::class_<MjvPerturb>("MjvPerturb")
.constructor<>()
.function("copy", &MjvPerturb::copy, take_ownership())
// INSERT-GENERATED-MjvPerturb-BINDINGS
;
emscripten::class_<MjvCamera>("MjvCamera")
.constructor<>()
.function("copy", &MjvCamera::copy, take_ownership())
// INSERT-GENERATED-MjvCamera-BINDINGS
;
emscripten::class_<MjvGLCamera>("MjvGLCamera")
.constructor<>()
.function("copy", &MjvGLCamera::copy, take_ownership())
// INSERT-GENERATED-MjvGLCamera-BINDINGS
;
emscripten::class_<MjvGeom>("MjvGeom")
.constructor<>()
.function("copy", &MjvGLCamera::copy, take_ownership())
// INSERT-GENERATED-MjvGeom-BINDINGS
;
emscripten::class_<MjvLight>("MjvLight")
.constructor<>()
.function("copy", &MjvLight::copy, take_ownership())
// INSERT-GENERATED-MjvLight-BINDINGS
;
emscripten::class_<MjvOption>("MjvOption")
.constructor<>()
.function("copy", &MjvOption::copy, take_ownership())
// INSERT-GENERATED-MjvOption-BINDINGS
;
emscripten::class_<MjvScene>("MjvScene")
.constructor<>()
.constructor<MjModel *, int>()
// INSERT-GENERATED-MjvScene-BINDINGS
;
emscripten::class_<MjvFigure>("MjvFigure")
.constructor<>()
.function("copy", &MjvFigure::copy, take_ownership())
// INSERT-GENERATED-MjvFigure-BINDINGS
;
emscripten::class_<MjSpec>("MjSpec")
.constructor<const MjSpec &>()
// INSERT-GENERATED-MjSpec-BINDINGS
;
emscripten::class_<MjsElement>("MjsElement")
// INSERT-GENERATED-MjsElement-BINDINGS
;
emscripten::class_<MjsCompiler>("MjsCompiler")
// INSERT-GENERATED-MjsCompiler-BINDINGS
;
emscripten::class_<MjsOrientation>("MjsOrientation")
// INSERT-GENERATED-MjsOrientation-BINDINGS
;
emscripten::class_<MjsBody>("MjsBody")
// INSERT-GENERATED-MjsBody-BINDINGS
;
emscripten::class_<MjsGeom>("MjsGeom")
// INSERT-GENERATED-MjsGeom-BINDINGS
;
emscripten::class_<MjsFrame>("MjsFrame")
// INSERT-GENERATED-MjsFrame-BINDINGS
;
emscripten::class_<MjsJoint>("MjsJoint")
// INSERT-GENERATED-MjsJoint-BINDINGS
;
emscripten::class_<MjsSite>("MjsSite")
// INSERT-GENERATED-MjsSite-BINDINGS
;
emscripten::class_<MjsCamera>("MjsCamera")
// INSERT-GENERATED-MjsCamera-BINDINGS
;
emscripten::class_<MjsLight>("MjsLight")
// INSERT-GENERATED-MjsLight-BINDINGS
;
emscripten::class_<MjsFlex>("MjsFlex")
// INSERT-GENERATED-MjsFlex-BINDINGS
;
emscripten::class_<MjsMesh>("MjsMesh")
// INSERT-GENERATED-MjsMesh-BINDINGS
;
emscripten::class_<MjsHField>("MjsHField")
// INSERT-GENERATED-MjsHField-BINDINGS
;
emscripten::class_<MjsSkin>("MjsSkin")
// INSERT-GENERATED-MjsSkin-BINDINGS
;
emscripten::class_<MjsTexture>("MjsTexture")
// INSERT-GENERATED-MjsTexture-BINDINGS
;
emscripten::class_<MjsMaterial>("MjsMaterial")
// INSERT-GENERATED-MjsMaterial-BINDINGS
;
emscripten::class_<MjsPair>("MjsPair")
// INSERT-GENERATED-MjsPair-BINDINGS
;
emscripten::class_<MjsExclude>("MjsExclude")
// INSERT-GENERATED-MjsExclude-BINDINGS
;
emscripten::class_<MjsEquality>("MjsEquality")
// INSERT-GENERATED-MjsEquality-BINDINGS
;
emscripten::class_<MjsTendon>("MjsTendon")
// INSERT-GENERATED-MjsTendon-BINDINGS
;
emscripten::class_<MjsWrap>("MjsWrap")
// INSERT-GENERATED-MjsWrap-BINDINGS
;
emscripten::class_<MjsActuator>("MjsActuator")
// INSERT-GENERATED-MjsActuator-BINDINGS
;
emscripten::class_<MjsSensor>("MjsSensor")
// INSERT-GENERATED-MjsSensor-BINDINGS
;
emscripten::class_<MjsNumeric>("MjsNumeric")
// INSERT-GENERATED-MjsNumeric-BINDINGS
;
emscripten::class_<MjsText>("MjsText")
// INSERT-GENERATED-MjsText-BINDINGS
;
emscripten::class_<MjsTuple>("MjsTuple")
// INSERT-GENERATED-MjsTuple-BINDINGS
;
emscripten::class_<MjsKey>("MjsKey")
// INSERT-GENERATED-MjsKey-BINDINGS
;
emscripten::class_<MjsDefault>("MjsDefault")
// INSERT-GENERATED-MjsDefault-BINDINGS
;
emscripten::class_<MjsPlugin>("MjsPlugin")
// INSERT-GENERATED-MjsPlugin-BINDINGS
;
emscripten::class_<MjVFS>("MjVFS").constructor<>()
// INSERT-GENERATED-MjVFS-BINDINGS
;
// {{ AUTOGENNED_STRUCTS_BINDINGS }}
// TODO: should be generated in future CLs -- //
emscripten::register_vector<MjSolverStat>("MjSolverStatVec");