Files
Mujoco_WASM/src/experimental/platform/gui_spec.cc
T
Haroon Qureshi 42d843a0fd Separate mjModel/mjData ImGui table helper from mjSpec ImGui table helper.
There are enough differences between how the two are structured that
having separate implementations makes things easier to implement
despite some minor code duplication.

PiperOrigin-RevId: 875287973
Change-Id: Id49e089e5e1944291ce79e79f8cb71d048a942cc
2026-02-25 12:37:31 -08:00

819 lines
33 KiB
C++

// Copyright 2026 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "experimental/platform/gui_spec.h"
#include <string>
#include <string_view>
#include <imgui.h>
#include <mujoco/mjxmacro.h>
#include <mujoco/mujoco.h>
#include "experimental/platform/imgui_widgets.h"
// Define the mujoco X macros to add fields to the ImGui_DataTable.
// We limit the fields to the ones with a matching element by comparing the
// array size field (e.g. nbody) with the MATCH constexpr value.
#define X(TYPE, NAME, NELEM, SIZE) \
if constexpr (#NELEM == MATCH) table.DataPtr(#NAME, ptr->NAME, index, SIZE);
// Simple wrapper around MJMODEL_POINTERS that prepares values we need for the
// X macro above.
#define MJMODEL_POINTERS_X(M) \
constexpr std::string_view MATCH(#M); \
const auto* ptr = model; \
MJMODEL_POINTERS
// Simple wrapper around MJDATA_POINTERS that prepares values we need for the
// X macro above.
#define MJDATA_POINTERS_X(M) \
constexpr std::string_view MATCH(#M); \
const auto* ptr = data; \
MJDATA_POINTERS
namespace mujoco::platform {
// Returns the index of the element in the spec. This is different from
// mjs_getId which returns the runtime ID of an element.
static int GetElementIndexInSpec(mjsElement* element) {
int n = 0;
mjSpec* spec = mjs_getSpec(element);
mjsElement* iter = mjs_firstElement(spec, element->elemtype);
while (iter) {
if (iter == element) {
return n;
}
iter = mjs_nextElement(spec, iter);
++n;
}
return -1;
}
// Returns a name for the element; either the element has a name, or we
// construct a unique name from the element's id (using mjs_getId) or index
// (using GetElementIndexInSpec).
static std::string ElementName(mjsElement* element) {
const mjString* name = mjs_getName(element);
std::string label = *name;
if (label.empty()) {
int id = mjs_getId(element);
if (id == -1) {
id = GetElementIndexInSpec(element);
}
const char* type_name = mju_type2Str(element->elemtype);
label = "(" + std::string(type_name) + " " + std::to_string(id) + ")";
}
return label;
}
static void AddDeleteButton(mjsElement* element,
const SpecElementCallbackFn& on_delete) {
if (on_delete) {
// Right-align the delete button.
const float button_width = ImGui::CalcTextSize(ICON_FA_TRASH_CAN).x +
ImGui::GetStyle().FramePadding.x * 2.0f;
ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - button_width);
if (ImGui::SmallButton(ICON_FA_TRASH_CAN)) {
on_delete(element);
}
}
}
static void SelectableElement(mjsElement* element,
mjsElement** selected_element,
const SpecElementCallbackFn& on_delete) {
constexpr ImGuiSelectableFlags flags = ImGuiSelectableFlags_AllowOverlap;
const std::string name = ElementName(element);
const bool selected = (element == *selected_element);
if (ImGui::Selectable(name.c_str(), selected, flags)) {
*selected_element = element;
}
if (selected) {
AddDeleteButton(element, on_delete);
}
}
static void BodyChildrenGui(const char* heading, mjtObj type,
mjsElement** element, mjsBody* body,
const SpecElementCallbackFn& on_delete) {
mjsElement* iter = mjs_firstChild(body, type, 0);
if (!iter) {
return;
}
constexpr ImGuiTreeNodeFlags tree_flags =
ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DrawLinesFull;
if (ImGui::TreeNodeEx(heading, tree_flags)) {
while (iter) {
SelectableElement(iter, element, on_delete);
iter = mjs_nextChild(body, iter, 0);
}
ImGui::TreePop();
}
}
static void ElementListGui(const char* heading, mjtObj type,
mjsElement** element, mjSpec* spec,
const SpecElementCallbackFn& on_delete) {
mjsElement* iter = mjs_firstElement(spec, type);
if (!iter) {
return;
}
constexpr ImGuiTreeNodeFlags tree_flags =
ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_Framed;
if (ImGui::TreeNodeEx(heading, tree_flags)) {
while (iter) {
SelectableElement(iter, element, on_delete);
iter = mjs_nextElement(spec, iter);
}
ImGui::TreePop();
}
}
static void BodyTreeGuiRecursive(mjsElement** element, mjsBody* body,
const SpecElementCallbackFn& on_delete) {
const std::string label = ElementName(body->element);
ImGui::PushID(body);
ImGuiTreeNodeFlags flags =
ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_Framed |
ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_AllowOverlap;
if (*element == body->element) {
flags |= ImGuiTreeNodeFlags_Selected;
}
const bool tree_open = ImGui::TreeNodeEx(label.c_str(), flags);
if (ImGui::IsItemClicked()) {
*element = body->element;
}
if (*element == body->element) {
AddDeleteButton(body->element, on_delete);
}
if (tree_open) {
mjsElement* iter = mjs_firstChild(body, mjOBJ_BODY, 0);
while (iter) {
BodyTreeGuiRecursive(element, mjs_asBody(iter), on_delete);
iter = mjs_nextChild(body, iter, 0);
}
BodyChildrenGui("Frames", mjOBJ_FRAME, element, body, on_delete);
BodyChildrenGui("Sites", mjOBJ_SITE, element, body, on_delete);
BodyChildrenGui("Joints", mjOBJ_JOINT, element, body, on_delete);
BodyChildrenGui("Geoms", mjOBJ_GEOM, element, body, on_delete);
BodyChildrenGui("Lights", mjOBJ_LIGHT, element, body, on_delete);
BodyChildrenGui("Cameras", mjOBJ_CAMERA, element, body, on_delete);
ImGui::TreePop();
}
ImGui::PopID();
}
void SpecExplorerGui(mjsElement** element, mjSpec* spec,
const SpecElementCallbackFn& on_delete) {
const ImGuiTreeNodeFlags flags =
ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_Framed;
if (ImGui::TreeNodeEx("Body Tree", flags)) {
mjsElement* root = mjs_firstElement(spec, mjOBJ_BODY);
if (root) {
mjsBody* body = mjs_asBody(root);
if (body) {
BodyTreeGuiRecursive(element, body, on_delete);
}
}
ImGui::TreePop();
}
auto list = [&](const char* heading, mjtObj type) {
ElementListGui(heading, type, element, spec, on_delete);
};
ImGui::PushID(spec);
// Non-tree elements.
if (ImGui::TreeNodeEx("Elements", flags)) {
list("Actuators", mjOBJ_ACTUATOR);
list("Sensors", mjOBJ_SENSOR);
list("Flexes", mjOBJ_FLEX);
list("Tendons", mjOBJ_TENDON);
list("Pair", mjOBJ_PAIR);
list("Exclude", mjOBJ_EXCLUDE);
list("Equality", mjOBJ_EQUALITY);
list("Numeric", mjOBJ_NUMERIC);
list("Text", mjOBJ_TEXT);
list("Tuple", mjOBJ_TUPLE);
list("Key", mjOBJ_KEY);
list("Default", mjOBJ_DEFAULT);
ImGui::TreePop();
}
// Assets.
if (ImGui::TreeNodeEx("Assets", flags)) {
list("Meshes", mjOBJ_MESH);
list("Height Fields", mjOBJ_HFIELD);
list("Skins", mjOBJ_SKIN);
list("Textures", mjOBJ_TEXTURE);
list("Materials", mjOBJ_MATERIAL);
ImGui::TreePop();
}
ImGui::PopID();
}
void ElementSpecGui(const mjSpec* spec, mjsElement* element) {
if (element == nullptr) {
return;
}
ImGui_SpecElementTable table;
switch (element->elemtype) {
case mjOBJ_BODY: {
mjsBody* body = mjs_asBody(element);
table("childclass", body->childclass, "childclass name");
table("pos", body->pos, "frame position");
table("quat", body->quat, "alt", body->alt, "frame orientation");
table("ipos", body->ipos, "inertial frame position");
table("iquat", body->iquat, "ialt", body->ialt, "inertial frame orientation");
table("mass", body->mass, "mass");
table("inertia", body->inertia, "diagonal inertia (in i-frame)");
table("fullinertia", body->fullinertia, "non-axis-aligned inertia matrix");
table("mocap", body->mocap, "is this a mocap body");
table("gravcomp", body->gravcomp, "gravity compensation");
table("explicitinertial", body->explicitinertial, "whether to save the body with explicit inertial clause");
table("sleep", body->sleep, "sleep policy");
table("info", body->info, "message appended to compiler errors");
break;
}
case mjOBJ_JOINT: {
mjsJoint* joint = mjs_asJoint(element);
table("pos", joint->pos, "anchor position");
table("axis", joint->axis, "joint axis");
table("ref", joint->ref, "value at reference configuration: qpos0");
table("align", joint->align, "align free joint with body com (mjtAlignFree)");
table("stiffness", joint->stiffness, "stiffness coefficient");
table("springref", joint->springref, "spring reference value: qpos_spring");
table("springdamper", joint->springdamper, "timeconst, dampratio");
table("limited", joint->limited, "does joint have limits (mjtLimited)");
table("range", joint->range, "joint limits");
table("margin", joint->margin, "margin value for joint limit detection");
table("solref_limit", joint->solref_limit, "solver reference: joint limits");
table("solimp_limit", joint->solimp_limit, "solver impedance: joint limits");
table("actfrclimited", joint->actfrclimited, "are actuator forces on joint limited (mjtLimited)");
table("actfrcrange", joint->actfrcrange, "actuator force limits");
table("armature", joint->armature, "armature inertia (mass for slider)");
table("damping", joint->damping, "damping coefficient");
table("frictionloss", joint->frictionloss, "friction loss");
table("solref_friction", joint->solref_friction, "solver reference: dof friction");
table("solimp_friction", joint->solimp_friction, "solver impedance: dof friction");
table("group", joint->group, "group");
table("actgravcomp", joint->actgravcomp, "is gravcomp force applied via actuators");
table("info", joint->info, "message appended to compiler errors");
break;
}
case mjOBJ_ACTUATOR: {
mjsActuator* actuator = mjs_asActuator(element);
table("gaintype", actuator->gaintype, "gain type");
table("gainprm", actuator->gainprm, "gain parameters");
table("biastype", actuator->biastype, "bias type");
table("biasprm", actuator->biasprm, "bias parameters");
table("dyntype", actuator->dyntype, "dynamics type");
table("dynprm", actuator->dynprm, "dynamics parameters");
table("actdim", actuator->actdim, "number of activation variables");
table("actearly", actuator->actearly, "apply next activations to qfrc");
table("trntype", actuator->trntype, "transmission type");
table("gear", actuator->gear, "length and transmitted force scaling");
table("target", actuator->target, "name of transmission target");
table("refsite", actuator->refsite, "reference site, for site transmission");
table("slidersite", actuator->slidersite, "site defining cylinder, for slider-crank");
table("cranklength", actuator->cranklength, "crank length, for slider-crank");
table("lengthrange", actuator->lengthrange, "transmission length range");
table("inheritrange", actuator->inheritrange, "automatic range setting for position and intvelocity");
table("ctrllimited", actuator->ctrllimited, "are control limits defined (mjtLimited)");
table("ctrlrange", actuator->ctrlrange, "control range");
table("forcelimited", actuator->forcelimited, "are force limits defined (mjtLimited)");
table("forcerange", actuator->forcerange, "force range");
table("actlimited", actuator->actlimited, "are activation limits defined (mjtLimited)");
table("actrange", actuator->actrange, "activation range");
table("group", actuator->group, "group");
table("nsample", actuator->nsample, "number of samples in history buffer");
table("interp", actuator->interp, "interpolation order (0=ZOH, 1=linear, 2=cubic)");
table("delay", actuator->delay, "delay time in seconds; 0: no delay");
table("info", actuator->info, "message appended to compiler errors");
break;
}
case mjOBJ_SENSOR: {
mjsSensor* sensor = mjs_asSensor(element);
table("type", sensor->type, "type of sensor");
table("objtype", sensor->objtype, "type of sensorized object");
table("objname", sensor->objname, "name of sensorized object");
table("reftype", sensor->reftype, "type of referenced object");
table("refname", sensor->refname, "name of referenced object");
table("intprm", sensor->intprm, "integer parameters");
table("datatype", sensor->datatype, "data type for sensor measurement");
table("needstage", sensor->needstage, "compute stage needed to simulate sensor");
table("dim", sensor->dim, "number of scalar outputs");
table("cutoff", sensor->cutoff, "cutoff for real and positive datatypes");
table("noise", sensor->noise, "noise stdev");
table("nsample", sensor->nsample, "number of samples in history buffer");
table("interp", sensor->interp, "interpolation order (0=ZOH, 1=linear, 2=cubic)");
table("delay", sensor->delay, "delay time in seconds");
table("interval", sensor->interval, "[period, time_prev] in seconds");
table("info", sensor->info, "message appended to compiler errors");
break;
}
case mjOBJ_SITE: {
mjsSite* site = mjs_asSite(element);
table("pos", site->pos, "position");
table("quat", site->quat, "alt", site->alt, "orientation");
table("fromto", site->fromto, "alternative for capsule, cylinder, box, ellipsoid");
table("size", site->size, "geom size");
table("type", site->type, "geom type");
table("material", site->material, "name of material");
table("group", site->group, "group");
table("rgba", site->rgba, "rgba when material is omitted");
table("info", site->info, "message appended to compiler errors");
break;
}
case mjOBJ_FRAME: {
mjsFrame* frame = mjs_asFrame(element);
table("childclass", frame->childclass, "childclass name");
table("pos", frame->pos, "position");
table("quat", frame->quat, "alt", frame->alt, "orientation");
table("info", frame->info, "message appended to compiler errors");
break;
}
case mjOBJ_GEOM: {
mjsGeom* geom = mjs_asGeom(element);
table("type", geom->type, "geom type");
table("pos", geom->pos, "position");
table("quat", geom->quat, "alt", geom->alt, "orientation");
table("fromto", geom->fromto, "alternative for capsule, cylinder, box, ellipsoid");
table("size", geom->size, "type-specific size");
table("contype", geom->contype, "contact type");
table("conaffinity", geom->conaffinity, "contact affinity");
table("condim", geom->condim, "contact dimensionality");
table("priority", geom->priority, "contact priority");
table("friction", geom->friction, "one-sided friction coefficients: slide, roll, spin");
table("solmix", geom->solmix, "solver mixing for contact pairs");
table("solref", geom->solref, "solver reference");
table("solimp", geom->solimp, "solver impedance");
table("margin", geom->margin, "margin for contact detection");
table("gap", geom->gap, "include in solver if dist < margin-gap");
table("mass", geom->mass, "used to compute density");
table("density", geom->density, "used to compute mass and inertia from volume or surface");
table("typeinertia", geom->typeinertia, "selects between surface and volume inertia");
table("fluid_ellipsoid", geom->fluid_ellipsoid, "whether ellipsoid-fluid model is active");
table("fluid_coefs", geom->fluid_coefs, "ellipsoid-fluid interaction coefs");
table("material", geom->material, "name of material");
table("rgba", geom->rgba, "rgba when material is omitted");
table("group", geom->group, "group");
table("hfieldname", geom->hfieldname, "heightfield attached to geom");
table("meshname", geom->meshname, "mesh attached to geom");
table("fitscale", geom->fitscale, "scale mesh uniformly");
table("info", geom->info, "message appended to compiler errors");
break;
}
case mjOBJ_LIGHT: {
mjsLight* light = mjs_asLight(element);
table("pos", light->pos, "position");
table("dir", light->dir, "direction");
table("mode", light->mode, "tracking mode");
table("targetbody", light->targetbody, "target body for targeting");
table("active", light->active, "is light active");
table("type", light->type, "type of light");
table("texture", light->texture, "texture name for image lights");
table("castshadow", light->castshadow, "does light cast shadows");
table("bulbradius", light->bulbradius, "bulb radius, for soft shadows");
table("intensity", light->intensity, "intensity, in candelas");
table("range", light->range, "range of effectiveness");
table("attenuation", light->attenuation, "OpenGL attenuation (quadratic model)");
table("cutoff", light->cutoff, "OpenGL cutoff");
table("exponent", light->exponent, "OpenGL exponent");
table("ambient", light->ambient, "ambient color");
table("diffuse", light->diffuse, "diffuse color");
table("specular", light->specular, "specular color");
table("info", light->info, "message appended to compiler errorsx");
break;
}
case mjOBJ_CAMERA: {
mjsCamera* camera = mjs_asCamera(element);
table("pos", camera->pos, "position");
table("quat", camera->quat, "alt", camera->alt, "orientation");
table("mode", camera->mode, "tracking mode");
table("targetbody", camera->targetbody, "target body for tracking/targeting");
table("proj", camera->proj, "camera projection type");
table("resolution", camera->resolution, "resolution (pixel)");
table("output", camera->output, "bit flags for output type");
table("fovy", camera->fovy, "y-field of view");
table("ipd", camera->ipd, "inter-pupillary distance");
table("intrinsic", camera->intrinsic, "camera intrinsics (length)");
table("sensor_size", camera->sensor_size, "sensor size (length)");
table("focal_length", camera->focal_length, "focal length (length)");
table("focal_pixel", camera->focal_pixel, "focal length (pixel)");
table("principal_length", camera->principal_length, "principal point (length)");
table("principal_pixel", camera->principal_pixel, "principal point (pixel)");
table("info", camera->info, "message appended to compiler errors");
break;
}
case mjOBJ_MESH: {
mjsMesh* mesh = mjs_asMesh(element);
table("content_type", mesh->content_type, "content type of file");
table("file", mesh->file, "mesh file");
table("refpos", mesh->refpos, "reference position");
table("refquat", mesh->refquat, "reference orientation");
table("scale", mesh->scale, "rescale mesh");
table("inertia", mesh->inertia, "inertia type (convex, legacy, exact, shell)");
table("smoothnormal", mesh->smoothnormal, "do not exclude large-angle faces from normals");
table("needsdf", mesh->needsdf, "compute sdf from mesh");
table("maxhullvert", mesh->maxhullvert, "maximum vertex count for the convex hull");
table("material", mesh->material, "name of material");
table("info", mesh->info, "message appended to compiler errors");
break;
}
case mjOBJ_HFIELD: {
mjsHField* hfield = mjs_asHField(element);
table("content_type", hfield->content_type, "content type of file");
table("file", hfield->file, "file: (nrow, ncol, [elevation data])");
table("size", hfield->size, "hfield size (ignore referencing geom size)");
table("nrow", hfield->nrow, "number of rows");
table("ncol", hfield->ncol, "number of columns");
table("info", hfield->info, "message appended to compiler errors");
break;
}
case mjOBJ_SKIN: {
mjsSkin* skin = mjs_asSkin(element);
table("file", skin->file, "skin file");
table("material", skin->material, "name of material used for rendering");
table("rgba", skin->rgba, "rgba when material is omitted");
table("inflate", skin->inflate, "inflate in normal direction");
table("group", skin->group, "group for visualization");
table("info", skin->info, "message appended to compiler errors");
break;
}
case mjOBJ_FLEX: {
mjsFlex* flex = mjs_asFlex(element);
table("contype", flex->contype, "contact type");
table("conaffinity", flex->conaffinity, "contact affinity");
table("condim", flex->condim, "contact dimensionality");
table("priority", flex->priority, "contact priority");
table("friction", flex->friction, "one-sided friction coefficients: slide, roll, spin");
table("solmix", flex->solmix, "solver mixing for contact pairs");
table("solref", flex->solref, "solver reference");
table("solimp", flex->solimp, "solver impedance");
table("margin", flex->margin, "margin for contact detection");
table("gap", flex->gap, "include in solver if dist<margin-gap");
table("dim", flex->dim, "element dimensionality");
table("radius", flex->radius, "radius around primitive element");
table("size", flex->size, "vertex bounding box half sizes in qpos0");
table("internal", flex->internal, "enable internal collisions");
table("flatskin", flex->flatskin, "render flex skin with flat shading");
table("selfcollide", flex->selfcollide, "mode for flex self collision");
table("vertcollide", flex->vertcollide, "mode for vertex collision");
table("passive", flex->passive, "mode for passive collisions");
table("activelayers", flex->activelayers, "number of active element layers in 3D");
table("group", flex->group, "group for visualization");
table("edgestiffness", flex->edgestiffness, "edge stiffness");
table("edgedamping", flex->edgedamping, "edge damping");
table("rgba", flex->rgba, "rgba when material is omitted");
table("material", flex->material, "name of material used for rendering");
table("young", flex->young, "Young's modulus");
table("poisson", flex->poisson, "Poisson's ratio");
table("damping", flex->damping, "Rayleigh's damping");
table("thickness", flex->thickness, "thickness (2D only)");
table("elastic2d", flex->elastic2d, "2D passive forces; 0: none, 1: bending, 2: stretching, 3: both");
table("info", flex->info, "message appended to compiler errors");
break;
}
case mjOBJ_TENDON: {
mjsTendon* tendon = mjs_asTendon(element);
table("stiffness", tendon->stiffness, "stiffness coefficient");
table("springlength", tendon->springlength, "spring resting length; {-1, -1}: use qpos_spring");
table("damping", tendon->damping, "damping coefficient");
table("frictionloss", tendon->frictionloss, "friction loss");
table("solref_friction", tendon->solref_friction, "solver reference: tendon friction");
table("solimp_friction", tendon->solimp_friction, "solver impedance: tendon friction");
table("armature", tendon->armature, "inertia associated with tendon velocity");
table("limited", tendon->limited, "does tendon have limits (mjtLimited)");
table("actfrclimited", tendon->actfrclimited, "does tendon have actuator force limits");
table("range", tendon->range, "length limits");
table("actfrcrange", tendon->actfrcrange, "actuator force limits");
table("margin", tendon->margin, "margin value for tendon limit detection");
table("solref_limit", tendon->solref_limit, "solver reference: tendon limits");
table("solimp_limit", tendon->solimp_limit, "solver impedance: tendon limits");
table("material", tendon->material, "name of material for rendering");
table("width", tendon->width, "width for rendering");
table("rgba", tendon->rgba, "rgba when material is omitted");
table("group", tendon->group, "group");
table("info", tendon->info, "message appended to errors");
break;
}
case mjOBJ_TEXTURE: {
mjsTexture* texture = mjs_asTexture(element);
table("type", texture->type, "texture type");
table("colorspace", texture->colorspace, "colorspace");
table("builtin", texture->builtin, "builtin type (mjtBuiltin)");
table("mark", texture->mark, "mark type (mjtMark)");
table("rgb1", texture->rgb1, "first color for builtin");
table("rgb2", texture->rgb2, "second color for builtin");
table("markrgb", texture->markrgb, "mark color");
table("random", texture->random, "probability of random dots");
table("height", texture->height, "height in pixels (square for cube and skybox)");
table("width", texture->width, "width in pixels");
table("nchannel", texture->nchannel, "number of channels");
table("content_type", texture->content_type, "content type of file");
table("file", texture->file, "png file to load; use for all sides of cube");
table("gridsize", texture->gridsize, "size of grid for composite file; (1,1)-repeat");
table("gridlayout", texture->gridlayout, "row-major: L,R,F,B,U,D for faces; . for unused");
table("cubefiles", texture->cubefiles, "different file for each side of the cube");
table("hflip", texture->hflip, "horizontal flip");
table("vflip", texture->vflip, "vertical flip");
table("info", texture->info, "message appended to compiler errors");
break;
}
case mjOBJ_MATERIAL: {
mjsMaterial* material = mjs_asMaterial(element);
table("textures", material->textures, "names of textures (empty: none)");
table("texuniform", material->texuniform, "make texture cube uniform");
table("texrepeat", material->texrepeat, "texture repetition for 2D mapping");
table("emission", material->emission, "emission");
table("specular", material->specular, "specular");
table("shininess", material->shininess, "shininess");
table("reflectance", material->reflectance, "reflectance");
table("metallic", material->metallic, "metallic");
table("roughness", material->roughness, "roughness");
table("rgba", material->rgba, "rgba");
table("info", material->info, "message appended to compiler errors");
break;
}
case mjOBJ_PAIR: {
mjsPair* pair = mjs_asPair(element);
table("geomname1", pair->geomname1, "name of geom 1");
table("geomname2", pair->geomname2, "name of geom 2");
table("condim", pair->condim, "contact dimensionality");
table("solref", pair->solref, "solver reference, normal direction");
table("solreffriction", pair->solreffriction, "solver reference, frictional directions");
table("solimp", pair->solimp, "solver impedance");
table("margin", pair->margin, "margin for contact detection");
table("gap", pair->gap, "include in solver if dist<margin-gap");
table("friction", pair->friction, "full contact friction");
table("info", pair->info, "message appended to errors");
break;
}
case mjOBJ_EQUALITY: {
mjsEquality* equality = mjs_asEquality(element);
table("type", equality->type, "constraint type");
table("data", equality->data, "type-dependent data");
table("active", equality->active, "is equality initially active");
table("name1", equality->name1, "name of object 1");
table("name2", equality->name2, "name of object 2");
table("objtype", equality->objtype, "type of both objects");
table("solref", equality->solref, "solver reference");
table("solimp", equality->solimp, "solver impedance");
table("info", equality->info, "message appended to errors");
break;
}
case mjOBJ_EXCLUDE: {
mjsExclude* exclude = mjs_asExclude(element);
table("bodyname1", exclude->bodyname1, "name of geom 1");
table("bodyname2", exclude->bodyname2, "name of geom 2");
table("info", exclude->info, "message appended to errors");
break;
}
case mjOBJ_NUMERIC: {
mjsNumeric* numeric = mjs_asNumeric(element);
table("data", numeric->data, "initialization data");
table("size", numeric->size, "array size, can be bigger than data size");
table("info", numeric->info, "message appended to errors");
break;
}
case mjOBJ_TEXT: {
mjsText* text = mjs_asText(element);
table("data", text->data, "text string");
table("info", text->info, "message appended to compiler errors");
break;
}
case mjOBJ_TUPLE: {
mjsTuple* tuple = mjs_asTuple(element);
table("objtype", tuple->objtype, "object types");
table("objname", tuple->objname, "object names");
table("objprm", tuple->objprm, "object parameters");
table("info", tuple->info, "message appended to compiler errors");
break;
}
case mjOBJ_KEY: {
mjsKey* key = mjs_asKey(element);
table("time", key->time, "time");
table("qpos", key->qpos, "qpos");
table("qvel", key->qvel, "qvel");
table("act", key->act, "act");
table("mpos", key->mpos, "mocap pos");
table("mquat", key->mquat, "mocap quat");
table("ctrl", key->ctrl, "ctrl");
table("info", key->info, "message appended to compiler errors");
break;
}
case mjOBJ_PLUGIN: {
mjsPlugin* plugin = mjs_asPlugin(element);
table("name", plugin->name, "instance name");
table("plugin_name", plugin->plugin_name, "plugin name");
table("active", plugin->active, "is the plugin active");
table("info", plugin->info, "message appended to compiler errors");
break;
}
default:
// ignore other types
break;
}
}
void ElementModelGui(const mjModel* model, mjsElement* element) {
if (element == nullptr) {
return;
}
ImGui_DataPtrTable table;
const int index = mjs_getId(element);
MJMODEL_POINTERS_PREAMBLE(model);
switch (element->elemtype) {
case mjOBJ_BODY: {
table.SetPrefix("body_");
MJMODEL_POINTERS_X(nbody)
break;
}
case mjOBJ_JOINT: {
table.SetPrefix("jnt_");
MJMODEL_POINTERS_X(njnt)
break;
}
case mjOBJ_ACTUATOR: {
table.SetPrefix("actuator_");
MJMODEL_POINTERS_X(nu)
break;
}
case mjOBJ_SENSOR: {
table.SetPrefix("sensor_");
MJMODEL_POINTERS_X(nsensor)
break;
}
case mjOBJ_SITE: {
table.SetPrefix("site_");
MJMODEL_POINTERS_X(nsite)
break;
}
case mjOBJ_GEOM: {
table.SetPrefix("geom_");
MJMODEL_POINTERS_X(ngeom)
break;
}
case mjOBJ_LIGHT: {
table.SetPrefix("light_");
MJMODEL_POINTERS_X(nlight)
break;
}
case mjOBJ_CAMERA: {
table.SetPrefix("cam_");
MJMODEL_POINTERS_X(ncam)
break;
}
case mjOBJ_MESH: {
table.SetPrefix("mesh_");
MJMODEL_POINTERS_X(nmesh)
break;
}
case mjOBJ_HFIELD: {
table.SetPrefix("hfield_");
MJMODEL_POINTERS_X(nhfield)
break;
}
case mjOBJ_SKIN: {
table.SetPrefix("skin_");
MJMODEL_POINTERS_X(nskin)
break;
}
case mjOBJ_FLEX: {
table.SetPrefix("flex_");
MJMODEL_POINTERS_X(nflex)
break;
}
case mjOBJ_TENDON: {
table.SetPrefix("tendon_");
MJMODEL_POINTERS_X(ntendon)
break;
}
case mjOBJ_TEXTURE: {
table.SetPrefix("tex_");
MJMODEL_POINTERS_X(ntex)
break;
}
case mjOBJ_MATERIAL: {
table.SetPrefix("mat_");
MJMODEL_POINTERS_X(nmat)
break;
}
default:
// ignore other types
break;
}
}
void ElementDataGui(const mjData* data, mjsElement* element) {
if (element == nullptr) {
return;
}
ImGui_DataPtrTable table;
const int index = mjs_getId(element);
switch (element->elemtype) {
case mjOBJ_BODY: {
MJDATA_POINTERS_X(nbody)
break;
}
case mjOBJ_JOINT: {
MJDATA_POINTERS_X(njnt)
break;
}
case mjOBJ_SITE: {
table.SetPrefix("site_");
MJDATA_POINTERS_X(nsite)
break;
}
case mjOBJ_GEOM: {
table.SetPrefix("geom_");
MJDATA_POINTERS_X(ngeom)
break;
}
case mjOBJ_CAMERA: {
table.SetPrefix("cam_");
MJDATA_POINTERS_X(ncam)
break;
}
case mjOBJ_LIGHT: {
table.SetPrefix("light_");
MJDATA_POINTERS_X(nlight)
break;
}
case mjOBJ_SENSOR: {
table.SetPrefix("sensor_");
MJDATA_POINTERS_X(nsensor)
break;
}
case mjOBJ_MESH: {
table.SetPrefix("mesh_");
MJDATA_POINTERS_X(nmesh)
break;
}
case mjOBJ_HFIELD: {
table.SetPrefix("hfield_");
MJDATA_POINTERS_X(nhfield)
break;
}
case mjOBJ_SKIN: {
table.SetPrefix("skin_");
MJDATA_POINTERS_X(nskin)
break;
}
case mjOBJ_FLEX: {
table.SetPrefix("flex_");
MJDATA_POINTERS_X(nflex)
break;
}
case mjOBJ_TENDON: {
table.SetPrefix("ten_");
MJDATA_POINTERS_X(ntendon)
break;
}
case mjOBJ_TEXTURE: {
table.SetPrefix("tex_");
MJDATA_POINTERS_X(ntex)
break;
}
case mjOBJ_MATERIAL: {
table.SetPrefix("mat_");
MJDATA_POINTERS_X(nmat)
break;
}
default:
break;
}
}
} // namespace mujoco::platform