debbd7c916
PiperOrigin-RevId: 798177495 Change-Id: I5d95446dd7f7f84052b60cb378acdd856c1e4d50
484 lines
12 KiB
C
484 lines
12 KiB
C
// Copyright 2021 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 "engine/engine_vis_init.h"
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include <mujoco/mjmacro.h>
|
|
#include <mujoco/mjmodel.h>
|
|
#include <mujoco/mjvisualize.h>
|
|
#include "engine/engine_array_safety.h"
|
|
#include "engine/engine_util_errmem.h"
|
|
#include "engine/engine_util_misc.h"
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning (disable: 4305) // disable MSVC warning: truncation from 'double' to 'float'
|
|
#endif
|
|
|
|
|
|
//--------------------------------- Strings --------------------------------------------------------
|
|
|
|
// label names
|
|
const char* mjLABELSTRING[mjNLABEL] = {
|
|
"None",
|
|
"Body",
|
|
"Joint",
|
|
"Geom",
|
|
"Site",
|
|
"Camera",
|
|
"Light",
|
|
"Tendon",
|
|
"Actuator",
|
|
"Constraint",
|
|
"Flex",
|
|
"Skin",
|
|
"Selection",
|
|
"SelPoint",
|
|
"Contact",
|
|
"ContactForce",
|
|
"Island"
|
|
};
|
|
|
|
|
|
// frame names
|
|
const char* mjFRAMESTRING[mjNFRAME] = {
|
|
"None",
|
|
"Body",
|
|
"Geom",
|
|
"Site",
|
|
"Camera",
|
|
"Light",
|
|
"Contact",
|
|
"World"
|
|
};
|
|
|
|
|
|
// visual options: {name, initial value, shortcut}
|
|
const char* mjVISSTRING[mjNVISFLAG][3] = {
|
|
{"Convex Hull", "0", "H"},
|
|
{"Texture", "1", "X"},
|
|
{"Joint", "0", "J"},
|
|
{"Camera", "0", "Q"},
|
|
{"Actuator", "0", "U"},
|
|
{"Activation", "0", ","},
|
|
{"Light", "0", "Z"},
|
|
{"Tendon", "1", "V"},
|
|
{"Range Finder", "1", "Y"},
|
|
{"Equality", "0", "E"},
|
|
{"Inertia", "0", "I"},
|
|
{"Scale Inertia", "0", "'"},
|
|
{"Perturb Force", "0", "B"},
|
|
{"Perturb Object", "1", "O"},
|
|
{"Contact Point", "0", "C"},
|
|
{"Island", "0", "N"},
|
|
{"Contact Force", "0", "F"},
|
|
{"Contact Split", "0", "P"},
|
|
{"Transparent", "0", "T"},
|
|
{"Auto Connect", "0", "A"},
|
|
{"Center of Mass", "0", "M"},
|
|
{"Select Point", "0", ""},
|
|
{"Static Body", "1", "D"},
|
|
{"Skin", "1", ";"},
|
|
{"Flex Vert", "0", ""},
|
|
{"Flex Edge", "1", ""},
|
|
{"Flex Face", "0", ""},
|
|
{"Flex Skin", "1", ""},
|
|
{"Body Tree", "0", "`"},
|
|
{"Mesh Tree", "0", "\\"},
|
|
{"SDF iters", "0", ""}
|
|
};
|
|
|
|
|
|
// render options: {name, initial value, shortcut}
|
|
const char* mjRNDSTRING[mjNRNDFLAG][3] = {
|
|
{"Shadow", "1", "S"},
|
|
{"Wireframe", "0", "W"},
|
|
{"Reflection", "1", "R"},
|
|
{"Additive", "0", "L"},
|
|
{"Skybox", "1", "K"},
|
|
{"Fog", "0", "G"},
|
|
{"Haze", "1", "/"},
|
|
{"Segment", "0", ","},
|
|
{"Id Color", "0", ""},
|
|
{"Cull Face", "1", ""}
|
|
};
|
|
|
|
|
|
|
|
//--------------------------------- Implementation -------------------------------------------------
|
|
|
|
|
|
// allocate and init abstract scene
|
|
void mjv_makeScene(const mjModel* m, mjvScene* scn, int maxgeom) {
|
|
// free previous
|
|
mjv_freeScene(scn);
|
|
|
|
// allocate geom buffers
|
|
if (maxgeom > 0) {
|
|
// allocate
|
|
scn->maxgeom = maxgeom;
|
|
scn->geoms = (mjvGeom*) mju_malloc(maxgeom*sizeof(mjvGeom));
|
|
scn->geomorder = (int*) mju_malloc(maxgeom*sizeof(int));
|
|
|
|
// check allocation
|
|
if (!scn->geoms || !scn->geomorder) {
|
|
mjERROR("could not allocate geom buffers");
|
|
}
|
|
}
|
|
|
|
// set default OpenGL options
|
|
for (int i=0; i < mjNRNDFLAG; i++) {
|
|
scn->flags[i] = (mjRNDSTRING[i][1][0] == '1');
|
|
}
|
|
|
|
// set default model transformation
|
|
scn->scale = 1;
|
|
scn->rotate[0] = 1;
|
|
|
|
// set number of flexes
|
|
scn->nflex = m ? m->nflex : 0;
|
|
|
|
// allocate flex data
|
|
if (scn->nflex) {
|
|
int nflex = scn->nflex;
|
|
|
|
// allocate fixed
|
|
scn->flexedgeadr = (int*) mju_malloc(nflex*sizeof(int));
|
|
scn->flexedgenum = (int*) mju_malloc(nflex*sizeof(int));
|
|
scn->flexvertadr = (int*) mju_malloc(nflex*sizeof(int));
|
|
scn->flexvertnum = (int*) mju_malloc(nflex*sizeof(int));
|
|
scn->flexfaceadr = (int*) mju_malloc(nflex*sizeof(int));
|
|
scn->flexfacenum = (int*) mju_malloc(nflex*sizeof(int));
|
|
scn->flexfaceused= (int*) mju_malloc(nflex*sizeof(int));
|
|
scn->flexedge = (int*) mju_malloc(2*m->nflexedge*sizeof(int));
|
|
scn->flexvert = (float*) mju_malloc(3*m->nflexvert*sizeof(float));
|
|
|
|
// count max number of flex faces to be rendered (depending on vis options)
|
|
int nface = 0;
|
|
for (int f=0; f < nflex; f++) {
|
|
// 1D : 0
|
|
if (m->flex_dim[f] == 0) {
|
|
scn->flexfacenum[f] = 0;
|
|
}
|
|
|
|
// 2D: 2*fragments + 2*elements
|
|
else if (m->flex_dim[f] == 2) {
|
|
scn->flexfacenum[f] = 2*m->flex_shellnum[f] + 2*m->flex_elemnum[f];
|
|
}
|
|
|
|
// 3D: max(fragments, 4*maxlayer)
|
|
else {
|
|
// find number of elements in biggest layer
|
|
int maxlayer = 0, layer = 0, nlayer = 1;
|
|
while (nlayer) {
|
|
// count elements in this layer
|
|
nlayer = 0;
|
|
for (int e=0; e < m->flex_elemnum[f]; e++) {
|
|
if (m->flex_elemlayer[m->flex_elemadr[f]+e] == layer) {
|
|
nlayer++;
|
|
}
|
|
}
|
|
|
|
// accumulate max over layers, advance layer
|
|
maxlayer = mjMAX(maxlayer, nlayer);
|
|
layer++;
|
|
}
|
|
|
|
scn->flexfacenum[f] = mjMAX(m->flex_shellnum[f], 4*maxlayer);
|
|
}
|
|
|
|
// accumulate over flexes
|
|
nface += scn->flexfacenum[f];
|
|
}
|
|
|
|
// allocate face-related
|
|
scn->flexface = nface ? (float*) mju_malloc(9*nface*sizeof(float)) : NULL;
|
|
scn->flexnormal = nface ? (float*) mju_malloc(9*nface*sizeof(float)) : NULL;
|
|
scn->flextexcoord = nface ? (float*) mju_malloc(6*nface*sizeof(float)) : NULL;
|
|
|
|
// check allocation
|
|
if (!scn->flexedgeadr ||
|
|
!scn->flexedgenum ||
|
|
!scn->flexfaceadr ||
|
|
!scn->flexfacenum ||
|
|
!scn->flexfaceused||
|
|
!scn->flexvertadr ||
|
|
!scn->flexvertnum ||
|
|
!scn->flexedge ||
|
|
!scn->flexvert ||
|
|
(nface && !scn->flexface) ||
|
|
(nface && !scn->flexnormal) ||
|
|
(nface && !scn->flextexcoord)) {
|
|
mju_error("Could not allocate flex buffers");
|
|
}
|
|
|
|
// copy constant edge and vertex data
|
|
memcpy(scn->flexedgeadr, m->flex_edgeadr, nflex*sizeof(int));
|
|
memcpy(scn->flexedgenum, m->flex_edgenum, nflex*sizeof(int));
|
|
memcpy(scn->flexvertadr, m->flex_vertadr, nflex*sizeof(int));
|
|
memcpy(scn->flexvertnum, m->flex_vertnum, nflex*sizeof(int));
|
|
memcpy(scn->flexedge, m->flex_edge, 2*m->nflexedge*sizeof(int));
|
|
|
|
// compute flexfaceadr
|
|
for (int f=0; f < nflex; f++) {
|
|
scn->flexfaceadr[f] = f == 0 ? 0 : scn->flexfaceadr[f-1]+scn->flexfacenum[f-1];
|
|
}
|
|
}
|
|
|
|
// set number of skins
|
|
if (m) {
|
|
scn->nskin = m->nskin;
|
|
} else {
|
|
scn->nskin = 0;
|
|
}
|
|
|
|
// allocate skin data
|
|
if (scn->nskin) {
|
|
int nskin = m->nskin;
|
|
|
|
// allocate
|
|
scn->skinfacenum = (int*) mju_malloc(nskin*sizeof(int));
|
|
scn->skinvertadr = (int*) mju_malloc(nskin*sizeof(int));
|
|
scn->skinvertnum = (int*) mju_malloc(nskin*sizeof(int));
|
|
scn->skinvert = (float*) mju_malloc(3*m->nskinvert*sizeof(float));
|
|
scn->skinnormal = (float*) mju_malloc(3*m->nskinvert*sizeof(float));
|
|
|
|
// check allocation
|
|
if (!scn->skinfacenum ||
|
|
!scn->skinvertadr ||
|
|
!scn->skinvertnum ||
|
|
!scn->skinvert ||
|
|
!scn->skinnormal) {
|
|
mjERROR("could not allocate skin buffers");
|
|
}
|
|
|
|
// copy constant data
|
|
mju_copyInt(scn->skinfacenum, m->skin_facenum, nskin);
|
|
mju_copyInt(scn->skinvertadr, m->skin_vertadr, nskin);
|
|
mju_copyInt(scn->skinvertnum, m->skin_vertnum, nskin);
|
|
}
|
|
|
|
// mjvGeom, mjvLight, mjvGLCamera objects are invalid
|
|
}
|
|
|
|
|
|
|
|
// free abstract scene
|
|
void mjv_freeScene(mjvScene* scn) {
|
|
// free buffers allocated by mjv_makeScene
|
|
mju_free(scn->geoms);
|
|
mju_free(scn->geomorder);
|
|
|
|
mju_free(scn->flexedgeadr);
|
|
mju_free(scn->flexedgenum);
|
|
mju_free(scn->flexvertadr);
|
|
mju_free(scn->flexvertnum);
|
|
mju_free(scn->flexfaceadr);
|
|
mju_free(scn->flexfacenum);
|
|
mju_free(scn->flexfaceused);
|
|
mju_free(scn->flexedge);
|
|
mju_free(scn->flexvert);
|
|
mju_free(scn->flexface);
|
|
mju_free(scn->flexnormal);
|
|
mju_free(scn->flextexcoord);
|
|
|
|
mju_free(scn->skinfacenum);
|
|
mju_free(scn->skinvertadr);
|
|
mju_free(scn->skinvertnum);
|
|
mju_free(scn->skinvert);
|
|
mju_free(scn->skinnormal);
|
|
|
|
// clear data structure
|
|
mjv_defaultScene(scn);
|
|
}
|
|
|
|
|
|
|
|
// set default scene
|
|
void mjv_defaultScene(mjvScene* scn) {
|
|
memset(scn, 0, sizeof(mjvScene));
|
|
}
|
|
|
|
|
|
|
|
// set default visualization options
|
|
void mjv_defaultOption(mjvOption* vopt) {
|
|
vopt->label = mjLABEL_NONE;
|
|
vopt->frame = mjFRAME_NONE;
|
|
|
|
for (int i=0; i < mjNGROUP; i++) {
|
|
int state = (i < 3 ? 1 : 0);
|
|
vopt->geomgroup[i] = state;
|
|
vopt->sitegroup[i] = state;
|
|
vopt->jointgroup[i] = state;
|
|
vopt->tendongroup[i] = state;
|
|
vopt->actuatorgroup[i] = state;
|
|
vopt->flexgroup[i] = state;
|
|
vopt->skingroup[i] = state;
|
|
}
|
|
|
|
for (int i=0; i < mjNVISFLAG; i++) {
|
|
vopt->flags[i] = (mjVISSTRING[i][1][0] == '1');
|
|
}
|
|
|
|
vopt->bvh_depth = 1;
|
|
vopt->flex_layer = 0;
|
|
}
|
|
|
|
|
|
|
|
// set default camera
|
|
void mjv_defaultCamera(mjvCamera* cam) {
|
|
memset(cam, 0, sizeof(mjvCamera));
|
|
|
|
cam->type = mjCAMERA_FREE;
|
|
cam->fixedcamid = -1;
|
|
cam->trackbodyid = -1;
|
|
cam->distance = 2;
|
|
cam->azimuth = 90;
|
|
cam->elevation = -45;
|
|
}
|
|
|
|
|
|
|
|
// set default free camera
|
|
void mjv_defaultFreeCamera(const mjModel* m, mjvCamera* cam) {
|
|
memset(cam, 0, sizeof(mjvCamera));
|
|
|
|
cam->type = mjCAMERA_FREE;
|
|
cam->fixedcamid = -1;
|
|
cam->trackbodyid = -1;
|
|
cam->lookat[0] = m->stat.center[0];
|
|
cam->lookat[1] = m->stat.center[1];
|
|
cam->lookat[2] = m->stat.center[2];
|
|
cam->distance = 1.5 * m->stat.extent;
|
|
cam->azimuth = m->vis.global.azimuth;
|
|
cam->elevation = m->vis.global.elevation;
|
|
cam->orthographic = m->vis.global.orthographic;
|
|
}
|
|
|
|
|
|
|
|
// set default perturbation
|
|
void mjv_defaultPerturb(mjvPerturb* pert) {
|
|
memset(pert, 0, sizeof(mjvPerturb));
|
|
|
|
pert->flexselect = -1;
|
|
pert->skinselect = -1;
|
|
pert->refquat[0] = 1;
|
|
pert->scale = 1;
|
|
}
|
|
|
|
|
|
|
|
// predefined line colors
|
|
static const float _linergb[8][3] = {
|
|
{1.0, 0.3, 0.3},
|
|
{0.1, 1.0, 0.1},
|
|
{0.3, 0.3, 1.0},
|
|
{0.1, 1.0, 1.0},
|
|
{1.0, 0.2, 1.0},
|
|
{1.0, 1.0, 0.1},
|
|
{1.0, 0.6, 0.2},
|
|
{0.6, 0.7, 0.4}
|
|
};
|
|
|
|
|
|
|
|
// set default figure
|
|
void mjv_defaultFigure(mjvFigure* fig) {
|
|
// set everything to zero
|
|
memset(fig, 0, sizeof(mjvFigure));
|
|
|
|
// disable highlight
|
|
fig->highlightid = -1;
|
|
|
|
// set enable flags
|
|
fig->flg_legend = 1;
|
|
fig->flg_ticklabel[0] = 1;
|
|
fig->flg_ticklabel[1] = 1;
|
|
fig->flg_extend = 1;
|
|
|
|
// set style
|
|
fig->linewidth = 3;
|
|
fig->gridwidth = 1;
|
|
fig->gridsize[0] = 2;
|
|
fig->gridsize[1] = 2;
|
|
fig->gridrgb[0] = 0.4f;
|
|
fig->gridrgb[1] = 0.4f;
|
|
fig->gridrgb[2] = 0.4f;
|
|
fig->figurergba[3] = 1;
|
|
fig->panergba[3] = 1;
|
|
fig->legendrgba[3] = 0.3f;
|
|
fig->textrgb[0] = 1;
|
|
fig->textrgb[1] = 1;
|
|
fig->textrgb[2] = 1;
|
|
fig->range[0][0] = 0;
|
|
fig->range[0][1] = 1;
|
|
fig->range[1][0] = 0;
|
|
fig->range[1][1] = 1;
|
|
mjSTRNCPY(fig->xformat, "%.0f");
|
|
mjSTRNCPY(fig->yformat, "%.2g");
|
|
mjSTRNCPY(fig->minwidth, "XXX");
|
|
|
|
// set line colors
|
|
for (int n=0; n < mjMAXLINE; n++) {
|
|
// predefined colors
|
|
if (n < 8) {
|
|
fig->linergb[n][0] = _linergb[n][0];
|
|
fig->linergb[n][1] = _linergb[n][1];
|
|
fig->linergb[n][2] = _linergb[n][2];
|
|
}
|
|
|
|
// automatically generated colors: Halton sequence
|
|
else {
|
|
fig->linergb[n][0] = 0.1f + 0.8f*mju_Halton(n, 2);
|
|
fig->linergb[n][1] = 0.1f + 0.8f*mju_Halton(n, 3);
|
|
fig->linergb[n][2] = 0.1f + 0.8f*mju_Halton(n, 5);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// compute rbound for mjvGeom
|
|
float mjv_rbound(const mjvGeom* geom) {
|
|
// model geom: return
|
|
if (geom->objtype == mjOBJ_GEOM) {
|
|
return geom->modelrbound;
|
|
}
|
|
|
|
// compute rbound according to type
|
|
const float* s = geom->size;
|
|
switch ((mjtGeom) geom->type) {
|
|
case mjGEOM_SPHERE:
|
|
return s[0];
|
|
|
|
case mjGEOM_CAPSULE:
|
|
return (s[0]+s[2]);
|
|
|
|
case mjGEOM_CYLINDER:
|
|
return sqrtf(s[0]*s[0] + s[2]*s[2]);
|
|
|
|
case mjGEOM_BOX:
|
|
return sqrtf(s[0]*s[0] + s[1]*s[1] + s[2]*s[2]);
|
|
break;
|
|
|
|
default: // not accurate for arrows, but they are not transparent
|
|
return mjMAX(s[0], mjMAX(s[1], s[2]));
|
|
}
|
|
}
|