Version 2.1.1: Binaries for ARM64, proper macOS bundles, minor bugfixes.
Closes #2 Closes #42 Closes #59 PiperOrigin-RevId: 416794598 Change-Id: I1306df1127d6acecf2323873c0da0910f35d31a0
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
// 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.
|
||||
|
||||
#ifndef MUJOCO_SAMPLE_ARRAY_SAFETY_H_
|
||||
#define MUJOCO_SAMPLE_ARRAY_SAFETY_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
// Provides safe alternatives to the sizeof() operator and standard library functions for handling
|
||||
// null-terminated (C-style) strings in raw char arrays.
|
||||
//
|
||||
// These functions make use of compile-time array sizes to limit read and write operations to within
|
||||
// the array bounds. They are designed to trigger a compile error if the array size cannot be
|
||||
// determined at compile time (e.g. when an array has decayed into a pointer).
|
||||
//
|
||||
// They do not perform runtime bound checks.
|
||||
|
||||
namespace mujoco {
|
||||
namespace sample_util {
|
||||
|
||||
// returns sizeof(arr)
|
||||
// use instead of sizeof() to avoid unintended array-to-pointer decay
|
||||
template <typename T, int N>
|
||||
static constexpr std::size_t sizeof_arr(const T(&arr)[N]) {
|
||||
return sizeof(arr);
|
||||
}
|
||||
|
||||
// like std::strcmp but it will not read beyond the bound of either lhs or rhs
|
||||
template <std::size_t N1, std::size_t N2>
|
||||
static inline int strcmp_arr(const char (&lhs)[N1], const char (&rhs)[N2]) {
|
||||
return std::strncmp(lhs, rhs, std::min(N1, N2));
|
||||
}
|
||||
|
||||
// like std::strlen but it will not read beyond the bound of str
|
||||
// if str is not null-terminated, returns sizeof(str)
|
||||
template <std::size_t N>
|
||||
static inline std::size_t strlen_arr(const char (&str)[N]) {
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
if (str[i] == '\0') {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return N;
|
||||
}
|
||||
|
||||
// like std::sprintf but will not write beyond the bound of dest
|
||||
// dest is guaranteed to be null-terminated
|
||||
template <std::size_t N>
|
||||
static inline int sprintf_arr(char (&dest)[N], const char* format, ...) {
|
||||
std::va_list vargs;
|
||||
va_start(vargs, format);
|
||||
int retval = std::vsnprintf(dest, N, format, vargs);
|
||||
va_end(vargs);
|
||||
return retval;
|
||||
}
|
||||
|
||||
// like std::strcat but will not write beyond the bound of dest
|
||||
// dest is guaranteed to be null-terminated
|
||||
template <std::size_t N>
|
||||
static inline char* strcat_arr(char (&dest)[N], const char* src) {
|
||||
return std::strncat(dest, src, sizeof_arr(dest) - strlen_arr(dest) - 1);
|
||||
}
|
||||
|
||||
// like std::strcpy but won't write beyond the bound of dest
|
||||
// dest is guaranteed to be null-terminated
|
||||
template <std::size_t N>
|
||||
static inline char* strcpy_arr(char (&dest)[N], const char* src) {
|
||||
{
|
||||
std::size_t i = 0;
|
||||
for (; src[i] && i < N - 1; ++i) {
|
||||
dest[i] = src[i];
|
||||
}
|
||||
dest[i] = '\0';
|
||||
}
|
||||
return &dest[0];
|
||||
}
|
||||
|
||||
} // namespace sample_util
|
||||
} // namespace mujoco
|
||||
|
||||
#endif // MUJOCO_SAMPLE_ARRAY_SAFETY_H_
|
||||
+125
-128
@@ -12,12 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "mujoco.h"
|
||||
#include "glfw3.h"
|
||||
#include "stdio.h"
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "GLFW/glfw3.h"
|
||||
#include "mujoco.h"
|
||||
|
||||
// MuJoCo data structures
|
||||
mjModel* m = NULL; // MuJoCo model
|
||||
@@ -36,158 +35,156 @@ double lasty = 0;
|
||||
|
||||
|
||||
// keyboard callback
|
||||
void keyboard(GLFWwindow* window, int key, int scancode, int act, int mods)
|
||||
{
|
||||
// backspace: reset simulation
|
||||
if( act==GLFW_PRESS && key==GLFW_KEY_BACKSPACE )
|
||||
{
|
||||
mj_resetData(m, d);
|
||||
mj_forward(m, d);
|
||||
}
|
||||
void keyboard(GLFWwindow* window, int key, int scancode, int act, int mods) {
|
||||
// backspace: reset simulation
|
||||
if (act==GLFW_PRESS && key==GLFW_KEY_BACKSPACE) {
|
||||
mj_resetData(m, d);
|
||||
mj_forward(m, d);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// mouse button callback
|
||||
void mouse_button(GLFWwindow* window, int button, int act, int mods)
|
||||
{
|
||||
// update button state
|
||||
button_left = (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT)==GLFW_PRESS);
|
||||
button_middle = (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE)==GLFW_PRESS);
|
||||
button_right = (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT)==GLFW_PRESS);
|
||||
void mouse_button(GLFWwindow* window, int button, int act, int mods) {
|
||||
// update button state
|
||||
button_left = (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT)==GLFW_PRESS);
|
||||
button_middle = (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE)==GLFW_PRESS);
|
||||
button_right = (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT)==GLFW_PRESS);
|
||||
|
||||
// update mouse position
|
||||
glfwGetCursorPos(window, &lastx, &lasty);
|
||||
// update mouse position
|
||||
glfwGetCursorPos(window, &lastx, &lasty);
|
||||
}
|
||||
|
||||
|
||||
// mouse move callback
|
||||
void mouse_move(GLFWwindow* window, double xpos, double ypos)
|
||||
{
|
||||
// no buttons down: nothing to do
|
||||
if( !button_left && !button_middle && !button_right )
|
||||
return;
|
||||
void mouse_move(GLFWwindow* window, double xpos, double ypos) {
|
||||
// no buttons down: nothing to do
|
||||
if (!button_left && !button_middle && !button_right) {
|
||||
return;
|
||||
}
|
||||
|
||||
// compute mouse displacement, save
|
||||
double dx = xpos - lastx;
|
||||
double dy = ypos - lasty;
|
||||
lastx = xpos;
|
||||
lasty = ypos;
|
||||
// compute mouse displacement, save
|
||||
double dx = xpos - lastx;
|
||||
double dy = ypos - lasty;
|
||||
lastx = xpos;
|
||||
lasty = ypos;
|
||||
|
||||
// get current window size
|
||||
int width, height;
|
||||
glfwGetWindowSize(window, &width, &height);
|
||||
// get current window size
|
||||
int width, height;
|
||||
glfwGetWindowSize(window, &width, &height);
|
||||
|
||||
// get shift key state
|
||||
bool mod_shift = (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT)==GLFW_PRESS ||
|
||||
glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT)==GLFW_PRESS);
|
||||
// get shift key state
|
||||
bool mod_shift = (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT)==GLFW_PRESS ||
|
||||
glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT)==GLFW_PRESS);
|
||||
|
||||
// determine action based on mouse button
|
||||
mjtMouse action;
|
||||
if( button_right )
|
||||
action = mod_shift ? mjMOUSE_MOVE_H : mjMOUSE_MOVE_V;
|
||||
else if( button_left )
|
||||
action = mod_shift ? mjMOUSE_ROTATE_H : mjMOUSE_ROTATE_V;
|
||||
else
|
||||
action = mjMOUSE_ZOOM;
|
||||
// determine action based on mouse button
|
||||
mjtMouse action;
|
||||
if (button_right) {
|
||||
action = mod_shift ? mjMOUSE_MOVE_H : mjMOUSE_MOVE_V;
|
||||
} else if (button_left) {
|
||||
action = mod_shift ? mjMOUSE_ROTATE_H : mjMOUSE_ROTATE_V;
|
||||
} else {
|
||||
action = mjMOUSE_ZOOM;
|
||||
}
|
||||
|
||||
// move camera
|
||||
mjv_moveCamera(m, action, dx/height, dy/height, &scn, &cam);
|
||||
// move camera
|
||||
mjv_moveCamera(m, action, dx/height, dy/height, &scn, &cam);
|
||||
}
|
||||
|
||||
|
||||
// scroll callback
|
||||
void scroll(GLFWwindow* window, double xoffset, double yoffset)
|
||||
{
|
||||
// emulate vertical mouse motion = 5% of window height
|
||||
mjv_moveCamera(m, mjMOUSE_ZOOM, 0, -0.05*yoffset, &scn, &cam);
|
||||
void scroll(GLFWwindow* window, double xoffset, double yoffset) {
|
||||
// emulate vertical mouse motion = 5% of window height
|
||||
mjv_moveCamera(m, mjMOUSE_ZOOM, 0, -0.05*yoffset, &scn, &cam);
|
||||
}
|
||||
|
||||
|
||||
// main function
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
// check command-line arguments
|
||||
if( argc!=2 )
|
||||
{
|
||||
printf(" USAGE: basic modelfile\n");
|
||||
return 0;
|
||||
int main(int argc, const char** argv) {
|
||||
// check command-line arguments
|
||||
if (argc!=2) {
|
||||
std::printf(" USAGE: basic modelfile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// load and compile model
|
||||
char error[1000] = "Could not load binary model";
|
||||
if (std::strlen(argv[1])>4 && !std::strcmp(argv[1]+std::strlen(argv[1])-4, ".mjb")) {
|
||||
m = mj_loadModel(argv[1], 0);
|
||||
} else {
|
||||
m = mj_loadXML(argv[1], 0, error, 1000);
|
||||
}
|
||||
if (!m) {
|
||||
mju_error_s("Load model error: %s", error);
|
||||
}
|
||||
|
||||
// make data
|
||||
d = mj_makeData(m);
|
||||
|
||||
// init GLFW
|
||||
if (!glfwInit()) {
|
||||
mju_error("Could not initialize GLFW");
|
||||
}
|
||||
|
||||
// create window, make OpenGL context current, request v-sync
|
||||
GLFWwindow* window = glfwCreateWindow(1200, 900, "Demo", NULL, NULL);
|
||||
glfwMakeContextCurrent(window);
|
||||
glfwSwapInterval(1);
|
||||
|
||||
// initialize visualization data structures
|
||||
mjv_defaultCamera(&cam);
|
||||
mjv_defaultOption(&opt);
|
||||
mjv_defaultScene(&scn);
|
||||
mjr_defaultContext(&con);
|
||||
|
||||
// create scene and context
|
||||
mjv_makeScene(m, &scn, 2000);
|
||||
mjr_makeContext(m, &con, mjFONTSCALE_150);
|
||||
|
||||
// install GLFW mouse and keyboard callbacks
|
||||
glfwSetKeyCallback(window, keyboard);
|
||||
glfwSetCursorPosCallback(window, mouse_move);
|
||||
glfwSetMouseButtonCallback(window, mouse_button);
|
||||
glfwSetScrollCallback(window, scroll);
|
||||
|
||||
// run main loop, target real-time simulation and 60 fps rendering
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
// advance interactive simulation for 1/60 sec
|
||||
// Assuming MuJoCo can simulate faster than real-time, which it usually can,
|
||||
// this loop will finish on time for the next frame to be rendered at 60 fps.
|
||||
// Otherwise add a cpu timer and exit this loop when it is time to render.
|
||||
mjtNum simstart = d->time;
|
||||
while (d->time - simstart < 1.0/60.0) {
|
||||
mj_step(m, d);
|
||||
}
|
||||
|
||||
// load and compile model
|
||||
char error[1000] = "Could not load binary model";
|
||||
if( strlen(argv[1])>4 && !strcmp(argv[1]+strlen(argv[1])-4, ".mjb") )
|
||||
m = mj_loadModel(argv[1], 0);
|
||||
else
|
||||
m = mj_loadXML(argv[1], 0, error, 1000);
|
||||
if( !m )
|
||||
mju_error_s("Load model error: %s", error);
|
||||
// get framebuffer viewport
|
||||
mjrRect viewport = {0, 0, 0, 0};
|
||||
glfwGetFramebufferSize(window, &viewport.width, &viewport.height);
|
||||
|
||||
// make data
|
||||
d = mj_makeData(m);
|
||||
// update scene and render
|
||||
mjv_updateScene(m, d, &opt, NULL, &cam, mjCAT_ALL, &scn);
|
||||
mjr_render(viewport, &scn, &con);
|
||||
|
||||
// init GLFW
|
||||
if( !glfwInit() )
|
||||
mju_error("Could not initialize GLFW");
|
||||
// swap OpenGL buffers (blocking call due to v-sync)
|
||||
glfwSwapBuffers(window);
|
||||
|
||||
// create window, make OpenGL context current, request v-sync
|
||||
GLFWwindow* window = glfwCreateWindow(1200, 900, "Demo", NULL, NULL);
|
||||
glfwMakeContextCurrent(window);
|
||||
glfwSwapInterval(1);
|
||||
// process pending GUI events, call GLFW callbacks
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
// initialize visualization data structures
|
||||
mjv_defaultCamera(&cam);
|
||||
mjv_defaultOption(&opt);
|
||||
mjv_defaultScene(&scn);
|
||||
mjr_defaultContext(&con);
|
||||
//free visualization storage
|
||||
mjv_freeScene(&scn);
|
||||
mjr_freeContext(&con);
|
||||
|
||||
// create scene and context
|
||||
mjv_makeScene(m, &scn, 2000);
|
||||
mjr_makeContext(m, &con, mjFONTSCALE_150);
|
||||
// free MuJoCo model and data
|
||||
mj_deleteData(d);
|
||||
mj_deleteModel(m);
|
||||
|
||||
// install GLFW mouse and keyboard callbacks
|
||||
glfwSetKeyCallback(window, keyboard);
|
||||
glfwSetCursorPosCallback(window, mouse_move);
|
||||
glfwSetMouseButtonCallback(window, mouse_button);
|
||||
glfwSetScrollCallback(window, scroll);
|
||||
// terminate GLFW (crashes with Linux NVidia drivers)
|
||||
#if defined(__APPLE__) || defined(_WIN32)
|
||||
glfwTerminate();
|
||||
#endif
|
||||
|
||||
// run main loop, target real-time simulation and 60 fps rendering
|
||||
while( !glfwWindowShouldClose(window) )
|
||||
{
|
||||
// advance interactive simulation for 1/60 sec
|
||||
// Assuming MuJoCo can simulate faster than real-time, which it usually can,
|
||||
// this loop will finish on time for the next frame to be rendered at 60 fps.
|
||||
// Otherwise add a cpu timer and exit this loop when it is time to render.
|
||||
mjtNum simstart = d->time;
|
||||
while( d->time - simstart < 1.0/60.0 )
|
||||
mj_step(m, d);
|
||||
|
||||
// get framebuffer viewport
|
||||
mjrRect viewport = {0, 0, 0, 0};
|
||||
glfwGetFramebufferSize(window, &viewport.width, &viewport.height);
|
||||
|
||||
// update scene and render
|
||||
mjv_updateScene(m, d, &opt, NULL, &cam, mjCAT_ALL, &scn);
|
||||
mjr_render(viewport, &scn, &con);
|
||||
|
||||
// swap OpenGL buffers (blocking call due to v-sync)
|
||||
glfwSwapBuffers(window);
|
||||
|
||||
// process pending GUI events, call GLFW callbacks
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
//free visualization storage
|
||||
mjv_freeScene(&scn);
|
||||
mjr_freeContext(&con);
|
||||
|
||||
// free MuJoCo model and data
|
||||
mj_deleteData(d);
|
||||
mj_deleteModel(m);
|
||||
|
||||
// terminate GLFW (crashes with Linux NVidia drivers)
|
||||
#if defined(__APPLE__) || defined(_WIN32)
|
||||
glfwTerminate();
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
+100
-98
@@ -12,135 +12,137 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "mujoco.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "mujoco.h"
|
||||
|
||||
// help
|
||||
const char helpstring[] =
|
||||
"\n Usage: compile infile outfile\n"
|
||||
" infile can be in mjcf, urdf, mjb format\n"
|
||||
" outfile can be in mjcf, mjb, txt format\n\n"
|
||||
" Example: compile model.xml model.mjb\n";
|
||||
"\n Usage: compile infile outfile\n"
|
||||
" infile can be in mjcf, urdf, mjb format\n"
|
||||
" outfile can be in mjcf, mjb, txt format\n\n"
|
||||
" Example: compile model.xml model.mjb\n";
|
||||
|
||||
|
||||
// deallocate and print message
|
||||
int finish(const char* msg = 0, mjModel* m = 0)
|
||||
{
|
||||
// deallocated everything
|
||||
if( m )
|
||||
mj_deleteModel(m);
|
||||
int finish(const char* msg = 0, mjModel* m = 0) {
|
||||
// deallocated everything
|
||||
if (m) {
|
||||
mj_deleteModel(m);
|
||||
}
|
||||
|
||||
// print message
|
||||
if( msg )
|
||||
printf("%s\n", msg);
|
||||
// print message
|
||||
if (msg) {
|
||||
std::printf("%s\n", msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// possible file types
|
||||
enum
|
||||
{
|
||||
typeUNKNOWN = 0,
|
||||
typeXML,
|
||||
typeMJB,
|
||||
typeTXT
|
||||
enum {
|
||||
typeUNKNOWN = 0,
|
||||
typeXML,
|
||||
typeMJB,
|
||||
typeTXT
|
||||
};
|
||||
|
||||
|
||||
// determine file type
|
||||
int filetype(const char* filename)
|
||||
{
|
||||
// convert to lower case for string comparison
|
||||
char lower[1000];
|
||||
size_t i=0;
|
||||
while( i<strlen(filename) && i<999 )
|
||||
{
|
||||
lower[i] = (char)tolower(filename[i]);
|
||||
i++;
|
||||
}
|
||||
lower[i] = 0;
|
||||
int filetype(const char* filename) {
|
||||
// convert to lower case for string comparison
|
||||
char lower[1000];
|
||||
std::size_t i=0;
|
||||
while (i<std::strlen(filename) && i<999) {
|
||||
lower[i] = (char)tolower(filename[i]);
|
||||
i++;
|
||||
}
|
||||
lower[i] = 0;
|
||||
|
||||
// find last dot
|
||||
int dot = (int)strlen(lower);
|
||||
while( dot>=0 && lower[dot]!='.' )
|
||||
dot--;
|
||||
// find last dot
|
||||
int dot = (int)std::strlen(lower);
|
||||
while (dot>=0 && lower[dot]!='.') {
|
||||
dot--;
|
||||
}
|
||||
|
||||
// no dot found
|
||||
if( dot<0 )
|
||||
return typeUNKNOWN;
|
||||
// no dot found
|
||||
if (dot<0) {
|
||||
return typeUNKNOWN;
|
||||
}
|
||||
|
||||
// check extension
|
||||
if( !strcmp(lower+dot, ".xml") || !strcmp(lower+dot, ".urdf") )
|
||||
return typeXML;
|
||||
else if( !strcmp(lower+dot, ".mjb") )
|
||||
return typeMJB;
|
||||
else if( !strcmp(lower+dot, ".txt") )
|
||||
return typeTXT;
|
||||
else
|
||||
return typeUNKNOWN;
|
||||
// check extension
|
||||
if (!std::strcmp(lower+dot, ".xml") || !std::strcmp(lower+dot, ".urdf")) {
|
||||
return typeXML;
|
||||
} else if (!std::strcmp(lower+dot, ".mjb")) {
|
||||
return typeMJB;
|
||||
} else if (!std::strcmp(lower+dot, ".txt")) {
|
||||
return typeTXT;
|
||||
} else {
|
||||
return typeUNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// main function
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
// model and error
|
||||
mjModel* m = 0;
|
||||
char error[1000];
|
||||
int main(int argc, const char** argv) {
|
||||
// model and error
|
||||
mjModel* m = 0;
|
||||
char error[1000];
|
||||
|
||||
// print help if arguments are missing
|
||||
if( argc!=3 )
|
||||
return finish(helpstring);
|
||||
// print help if arguments are missing
|
||||
if (argc!=3) {
|
||||
return finish(helpstring);
|
||||
}
|
||||
|
||||
// determine file types
|
||||
int type1 = filetype(argv[1]);
|
||||
int type2 = filetype(argv[2]);
|
||||
// determine file types
|
||||
int type1 = filetype(argv[1]);
|
||||
int type2 = filetype(argv[2]);
|
||||
|
||||
// check types
|
||||
if( type1==typeUNKNOWN || type1==typeTXT ||
|
||||
type2==typeUNKNOWN || (type1==typeMJB && type2==typeXML) )
|
||||
return finish("Illegal combination of file formats");
|
||||
// check types
|
||||
if (type1==typeUNKNOWN || type1==typeTXT ||
|
||||
type2==typeUNKNOWN || (type1==typeMJB && type2==typeXML)) {
|
||||
return finish("Illegal combination of file formats");
|
||||
}
|
||||
|
||||
// make sure output file does not exist
|
||||
FILE* fp = fopen(argv[2], "r");
|
||||
if( fp )
|
||||
{
|
||||
fclose(fp);
|
||||
return finish("Output file already exists");
|
||||
// make sure output file does not exist
|
||||
std::FILE* fp = std::fopen(argv[2], "r");
|
||||
if (fp) {
|
||||
std::fclose(fp);
|
||||
return finish("Output file already exists");
|
||||
}
|
||||
|
||||
// load model
|
||||
if (type1==typeXML) {
|
||||
m = mj_loadXML(argv[1], 0, error, 1000);
|
||||
} else {
|
||||
m = mj_loadModel(argv[1], 0);
|
||||
}
|
||||
|
||||
// check error
|
||||
if (!m) {
|
||||
if (type1==typeXML) {
|
||||
return finish(error, 0);
|
||||
} else {
|
||||
return finish("Could not load model", 0);
|
||||
}
|
||||
}
|
||||
|
||||
// load model
|
||||
if( type1==typeXML )
|
||||
m = mj_loadXML(argv[1], 0, error, 1000);
|
||||
else
|
||||
m = mj_loadModel(argv[1], 0);
|
||||
|
||||
// check error
|
||||
if( !m )
|
||||
{
|
||||
if( type1==typeXML )
|
||||
return finish(error, 0);
|
||||
else
|
||||
return finish("Could not load model", 0);
|
||||
// save model
|
||||
if (type2==typeXML) {
|
||||
if (mj_saveLastXML(argv[2], m, error, 1000)) {
|
||||
return finish(error, m);
|
||||
}
|
||||
} else if (type2==typeMJB) {
|
||||
mj_saveModel(m, argv[2], 0, 0);
|
||||
} else {
|
||||
mj_printModel(m, argv[2]);
|
||||
}
|
||||
|
||||
// save model
|
||||
if( type2==typeXML )
|
||||
{
|
||||
if( mj_saveLastXML(argv[2], m, error, 1000) )
|
||||
return finish(error, m);
|
||||
}
|
||||
else if( type2==typeMJB )
|
||||
mj_saveModel(m, argv[2], 0, 0);
|
||||
else
|
||||
mj_printModel(m, argv[2]);
|
||||
|
||||
// finalize
|
||||
return finish("Done", m);
|
||||
// finalize
|
||||
return finish("Done", m);
|
||||
}
|
||||
|
||||
+341
-343
@@ -12,29 +12,32 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "mujoco.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "mujoco.h"
|
||||
|
||||
// enable compilation with and without OpenMP support
|
||||
#if defined(_OPENMP)
|
||||
#include <omp.h>
|
||||
#include <omp.h>
|
||||
#else
|
||||
// omp timer replacement
|
||||
#include <chrono>
|
||||
double omp_get_wtime(void)
|
||||
{
|
||||
static std::chrono::system_clock::time_point _start = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - _start;
|
||||
return elapsed.count();
|
||||
}
|
||||
// omp timer replacement
|
||||
#include <chrono>
|
||||
double omp_get_wtime(void)
|
||||
{
|
||||
static std::chrono::system_clock::time_point _start = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - _start;
|
||||
return elapsed.count();
|
||||
}
|
||||
|
||||
// omp functions used below
|
||||
void omp_set_dynamic(int) {}
|
||||
void omp_set_num_threads(int) {}
|
||||
int omp_get_num_procs(void) {return 1;}
|
||||
// omp functions used below
|
||||
void omp_set_dynamic(int) {}
|
||||
void omp_set_num_threads(int) {}
|
||||
int omp_get_num_procs(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -56,388 +59,383 @@ double eps = 1e-6; // finite-difference epsilon
|
||||
|
||||
|
||||
// worker function for parallel finite-difference computation of derivatives
|
||||
void worker(const mjModel* m, const mjData* dmain, mjData* d, int id)
|
||||
{
|
||||
int nv = m->nv;
|
||||
void worker(const mjModel* m, const mjData* dmain, mjData* d, int id) {
|
||||
int nv = m->nv;
|
||||
|
||||
// allocate stack space for result at center
|
||||
mjMARKSTACK
|
||||
mjtNum* center = mj_stackAlloc(d, nv);
|
||||
mjtNum* warmstart = mj_stackAlloc(d, nv);
|
||||
// allocate stack space for result at center
|
||||
mjMARKSTACK
|
||||
mjtNum* center = mj_stackAlloc(d, nv);
|
||||
mjtNum* warmstart = mj_stackAlloc(d, nv);
|
||||
|
||||
// prepare static schedule: range of derivative columns to be computed by this thread
|
||||
int chunk = (m->nv + nthread-1) / nthread;
|
||||
int istart = id * chunk;
|
||||
int iend = mjMIN(istart + chunk, m->nv);
|
||||
// prepare static schedule: range of derivative columns to be computed by this thread
|
||||
int chunk = (m->nv + nthread-1) / nthread;
|
||||
int istart = id * chunk;
|
||||
int iend = mjMIN(istart + chunk, m->nv);
|
||||
|
||||
// copy state and control from dmain to thread-specific d
|
||||
d->time = dmain->time;
|
||||
// copy state and control from dmain to thread-specific d
|
||||
d->time = dmain->time;
|
||||
mju_copy(d->qpos, dmain->qpos, m->nq);
|
||||
mju_copy(d->qvel, dmain->qvel, m->nv);
|
||||
mju_copy(d->qacc, dmain->qacc, m->nv);
|
||||
mju_copy(d->qacc_warmstart, dmain->qacc_warmstart, m->nv);
|
||||
mju_copy(d->qfrc_applied, dmain->qfrc_applied, m->nv);
|
||||
mju_copy(d->xfrc_applied, dmain->xfrc_applied, 6*m->nbody);
|
||||
mju_copy(d->ctrl, dmain->ctrl, m->nu);
|
||||
|
||||
// run full computation at center point (usually faster than copying dmain)
|
||||
if (isforward) {
|
||||
mj_forward(m, d);
|
||||
|
||||
// extra solver iterations to improve warmstart (qacc) at center point
|
||||
for (int rep=1; rep<nwarmup; rep++) {
|
||||
mj_forwardSkip(m, d, mjSTAGE_VEL, 1);
|
||||
}
|
||||
} else {
|
||||
mj_inverse(m, d);
|
||||
}
|
||||
|
||||
// select output from forward or inverse dynamics
|
||||
mjtNum* output = (isforward ? d->qacc : d->qfrc_inverse);
|
||||
|
||||
// save output for center point and warmstart (needed in forward only)
|
||||
mju_copy(center, output, nv);
|
||||
mju_copy(warmstart, d->qacc_warmstart, nv);
|
||||
|
||||
// select target vector and original vector for force or acceleration derivative
|
||||
mjtNum* target = (isforward ? d->qfrc_applied : d->qacc);
|
||||
const mjtNum* original = (isforward ? dmain->qfrc_applied : dmain->qacc);
|
||||
|
||||
// finite-difference over force or acceleration: skip = mjSTAGE_VEL
|
||||
for (int i=istart; i<iend; i++) {
|
||||
// perturb selected target
|
||||
target[i] += eps;
|
||||
|
||||
// evaluate dynamics, with center warmstart
|
||||
if (isforward) {
|
||||
mju_copy(d->qacc_warmstart, warmstart, m->nv);
|
||||
mj_forwardSkip(m, d, mjSTAGE_VEL, 1);
|
||||
} else {
|
||||
mj_inverseSkip(m, d, mjSTAGE_VEL, 1);
|
||||
}
|
||||
|
||||
// undo perturbation
|
||||
target[i] = original[i];
|
||||
|
||||
// compute column i of derivative 2
|
||||
for (int j=0; j<nv; j++) {
|
||||
deriv[(3*isforward+2)*nv*nv + i + j*nv] = (output[j] - center[j])/eps;
|
||||
}
|
||||
}
|
||||
|
||||
// finite-difference over velocity: skip = mjSTAGE_POS
|
||||
for (int i=istart; i<iend; i++) {
|
||||
// perturb velocity
|
||||
d->qvel[i] += eps;
|
||||
|
||||
// evaluate dynamics, with center warmstart
|
||||
if (isforward) {
|
||||
mju_copy(d->qacc_warmstart, warmstart, m->nv);
|
||||
mj_forwardSkip(m, d, mjSTAGE_POS, 1);
|
||||
} else {
|
||||
mj_inverseSkip(m, d, mjSTAGE_POS, 1);
|
||||
}
|
||||
|
||||
// undo perturbation
|
||||
d->qvel[i] = dmain->qvel[i];
|
||||
|
||||
// compute column i of derivative 1
|
||||
for (int j=0; j<nv; j++) {
|
||||
deriv[(3*isforward+1)*nv*nv + i + j*nv] = (output[j] - center[j])/eps;
|
||||
}
|
||||
}
|
||||
|
||||
// finite-difference over position: skip = mjSTAGE_NONE
|
||||
for (int i=istart; i<iend; i++) {
|
||||
// get joint id for this dof
|
||||
int jid = m->dof_jntid[i];
|
||||
|
||||
// get quaternion address and dof position within quaternion (-1: not in quaternion)
|
||||
int quatadr = -1, dofpos = 0;
|
||||
if (m->jnt_type[jid]==mjJNT_BALL) {
|
||||
quatadr = m->jnt_qposadr[jid];
|
||||
dofpos = i - m->jnt_dofadr[jid];
|
||||
} else if (m->jnt_type[jid]==mjJNT_FREE && i>=m->jnt_dofadr[jid]+3) {
|
||||
quatadr = m->jnt_qposadr[jid] + 3;
|
||||
dofpos = i - m->jnt_dofadr[jid] - 3;
|
||||
}
|
||||
|
||||
// apply quaternion or simple perturbation
|
||||
if (quatadr>=0) {
|
||||
mjtNum angvel[3] = {0, 0, 0};
|
||||
angvel[dofpos] = eps;
|
||||
mju_quatIntegrate(d->qpos+quatadr, angvel, 1);
|
||||
} else {
|
||||
d->qpos[m->jnt_qposadr[jid] + i - m->jnt_dofadr[jid]] += eps;
|
||||
}
|
||||
|
||||
// evaluate dynamics, with center warmstart
|
||||
if (isforward) {
|
||||
mju_copy(d->qacc_warmstart, warmstart, m->nv);
|
||||
mj_forwardSkip(m, d, mjSTAGE_NONE, 1);
|
||||
} else {
|
||||
mj_inverseSkip(m, d, mjSTAGE_NONE, 1);
|
||||
}
|
||||
|
||||
// undo perturbation
|
||||
mju_copy(d->qpos, dmain->qpos, m->nq);
|
||||
mju_copy(d->qvel, dmain->qvel, m->nv);
|
||||
mju_copy(d->qacc, dmain->qacc, m->nv);
|
||||
mju_copy(d->qacc_warmstart, dmain->qacc_warmstart, m->nv);
|
||||
mju_copy(d->qfrc_applied, dmain->qfrc_applied, m->nv);
|
||||
mju_copy(d->xfrc_applied, dmain->xfrc_applied, 6*m->nbody);
|
||||
mju_copy(d->ctrl, dmain->ctrl, m->nu);
|
||||
|
||||
// run full computation at center point (usually faster than copying dmain)
|
||||
if( isforward )
|
||||
{
|
||||
mj_forward(m, d);
|
||||
|
||||
// extra solver iterations to improve warmstart (qacc) at center point
|
||||
for( int rep=1; rep<nwarmup; rep++ )
|
||||
mj_forwardSkip(m, d, mjSTAGE_VEL, 1);
|
||||
// compute column i of derivative 0
|
||||
for (int j=0; j<nv; j++) {
|
||||
deriv[(3*isforward+0)*nv*nv + i + j*nv] = (output[j] - center[j])/eps;
|
||||
}
|
||||
else
|
||||
mj_inverse(m, d);
|
||||
}
|
||||
|
||||
// select output from forward or inverse dynamics
|
||||
mjtNum* output = (isforward ? d->qacc : d->qfrc_inverse);
|
||||
|
||||
// save output for center point and warmstart (needed in forward only)
|
||||
mju_copy(center, output, nv);
|
||||
mju_copy(warmstart, d->qacc_warmstart, nv);
|
||||
|
||||
// select target vector and original vector for force or acceleration derivative
|
||||
mjtNum* target = (isforward ? d->qfrc_applied : d->qacc);
|
||||
const mjtNum* original = (isforward ? dmain->qfrc_applied : dmain->qacc);
|
||||
|
||||
// finite-difference over force or acceleration: skip = mjSTAGE_VEL
|
||||
for( int i=istart; i<iend; i++ )
|
||||
{
|
||||
// perturb selected target
|
||||
target[i] += eps;
|
||||
|
||||
// evaluate dynamics, with center warmstart
|
||||
if( isforward )
|
||||
{
|
||||
mju_copy(d->qacc_warmstart, warmstart, m->nv);
|
||||
mj_forwardSkip(m, d, mjSTAGE_VEL, 1);
|
||||
}
|
||||
else
|
||||
mj_inverseSkip(m, d, mjSTAGE_VEL, 1);
|
||||
|
||||
// undo perturbation
|
||||
target[i] = original[i];
|
||||
|
||||
// compute column i of derivative 2
|
||||
for( int j=0; j<nv; j++ )
|
||||
deriv[(3*isforward+2)*nv*nv + i + j*nv] = (output[j] - center[j])/eps;
|
||||
}
|
||||
|
||||
// finite-difference over velocity: skip = mjSTAGE_POS
|
||||
for( int i=istart; i<iend; i++ )
|
||||
{
|
||||
// perturb velocity
|
||||
d->qvel[i] += eps;
|
||||
|
||||
// evaluate dynamics, with center warmstart
|
||||
if( isforward )
|
||||
{
|
||||
mju_copy(d->qacc_warmstart, warmstart, m->nv);
|
||||
mj_forwardSkip(m, d, mjSTAGE_POS, 1);
|
||||
}
|
||||
else
|
||||
mj_inverseSkip(m, d, mjSTAGE_POS, 1);
|
||||
|
||||
// undo perturbation
|
||||
d->qvel[i] = dmain->qvel[i];
|
||||
|
||||
// compute column i of derivative 1
|
||||
for( int j=0; j<nv; j++ )
|
||||
deriv[(3*isforward+1)*nv*nv + i + j*nv] = (output[j] - center[j])/eps;
|
||||
}
|
||||
|
||||
// finite-difference over position: skip = mjSTAGE_NONE
|
||||
for( int i=istart; i<iend; i++ )
|
||||
{
|
||||
// get joint id for this dof
|
||||
int jid = m->dof_jntid[i];
|
||||
|
||||
// get quaternion address and dof position within quaternion (-1: not in quaternion)
|
||||
int quatadr = -1, dofpos = 0;
|
||||
if( m->jnt_type[jid]==mjJNT_BALL )
|
||||
{
|
||||
quatadr = m->jnt_qposadr[jid];
|
||||
dofpos = i - m->jnt_dofadr[jid];
|
||||
}
|
||||
else if( m->jnt_type[jid]==mjJNT_FREE && i>=m->jnt_dofadr[jid]+3 )
|
||||
{
|
||||
quatadr = m->jnt_qposadr[jid] + 3;
|
||||
dofpos = i - m->jnt_dofadr[jid] - 3;
|
||||
}
|
||||
|
||||
// apply quaternion or simple perturbation
|
||||
if( quatadr>=0 )
|
||||
{
|
||||
mjtNum angvel[3] = {0,0,0};
|
||||
angvel[dofpos] = eps;
|
||||
mju_quatIntegrate(d->qpos+quatadr, angvel, 1);
|
||||
}
|
||||
else
|
||||
d->qpos[m->jnt_qposadr[jid] + i - m->jnt_dofadr[jid]] += eps;
|
||||
|
||||
// evaluate dynamics, with center warmstart
|
||||
if( isforward )
|
||||
{
|
||||
mju_copy(d->qacc_warmstart, warmstart, m->nv);
|
||||
mj_forwardSkip(m, d, mjSTAGE_NONE, 1);
|
||||
}
|
||||
else
|
||||
mj_inverseSkip(m, d, mjSTAGE_NONE, 1);
|
||||
|
||||
// undo perturbation
|
||||
mju_copy(d->qpos, dmain->qpos, m->nq);
|
||||
|
||||
// compute column i of derivative 0
|
||||
for( int j=0; j<nv; j++ )
|
||||
deriv[(3*isforward+0)*nv*nv + i + j*nv] = (output[j] - center[j])/eps;
|
||||
}
|
||||
|
||||
mjFREESTACK
|
||||
mjFREESTACK
|
||||
}
|
||||
|
||||
|
||||
// compute relative L1 norm of residual
|
||||
double relnorm(mjtNum* residual, mjtNum* base, int n)
|
||||
{
|
||||
mjtNum L1res = 0, L1base = 0;
|
||||
for( int i=0; i<n; i++ )
|
||||
{
|
||||
L1res += mju_abs(residual[i]);
|
||||
L1base += mju_abs(base[i]);
|
||||
}
|
||||
double relnorm(mjtNum* residual, mjtNum* base, int n) {
|
||||
mjtNum L1res = 0, L1base = 0;
|
||||
for (int i=0; i<n; i++) {
|
||||
L1res += mju_abs(residual[i]);
|
||||
L1base += mju_abs(base[i]);
|
||||
}
|
||||
|
||||
return (double) mju_log10(mju_max(mjMINVAL,L1res/mju_max(mjMINVAL,L1base)));
|
||||
return (double) mju_log10(mju_max(mjMINVAL, L1res/mju_max(mjMINVAL, L1base)));
|
||||
}
|
||||
|
||||
|
||||
// names of residuals for accuracy check
|
||||
const char* accuracy[8] = {
|
||||
"G2*F2 - I ",
|
||||
"G2 - G2' ",
|
||||
"G1 - G1' ",
|
||||
"F2 - F2' ",
|
||||
"G1 + G2*F1",
|
||||
"G0 + G2*F0",
|
||||
"F1 + F2*G1",
|
||||
"F0 + F2*G0"
|
||||
"G2*F2 - I ",
|
||||
"G2 - G2' ",
|
||||
"G1 - G1' ",
|
||||
"F2 - F2' ",
|
||||
"G1 + G2*F1",
|
||||
"G0 + G2*F0",
|
||||
"F1 + F2*G1",
|
||||
"F0 + F2*G0"
|
||||
};
|
||||
|
||||
|
||||
// check accuracy of derivatives using known mathematical identities
|
||||
void checkderiv(const mjModel* m, mjData* d, mjtNum error[7])
|
||||
{
|
||||
int nv = m->nv;
|
||||
void checkderiv(const mjModel* m, mjData* d, mjtNum error[7]) {
|
||||
int nv = m->nv;
|
||||
|
||||
// allocate space
|
||||
mjMARKSTACK
|
||||
mjtNum* mat = mj_stackAlloc(d, nv*nv);
|
||||
// allocate space
|
||||
mjMARKSTACK
|
||||
mjtNum* mat = mj_stackAlloc(d, nv*nv);
|
||||
|
||||
// get pointers to derivative matrices
|
||||
mjtNum* G0 = deriv; // dinv/dpos
|
||||
mjtNum* G1 = deriv + nv*nv; // dinv/dvel
|
||||
mjtNum* G2 = deriv + 2*nv*nv; // dinv/dacc
|
||||
mjtNum* F0 = deriv + 3*nv*nv; // dacc/dpos
|
||||
mjtNum* F1 = deriv + 4*nv*nv; // dacc/dvel
|
||||
mjtNum* F2 = deriv + 5*nv*nv; // dacc/dfrc
|
||||
// get pointers to derivative matrices
|
||||
mjtNum* G0 = deriv; // dinv/dpos
|
||||
mjtNum* G1 = deriv + nv*nv; // dinv/dvel
|
||||
mjtNum* G2 = deriv + 2*nv*nv; // dinv/dacc
|
||||
mjtNum* F0 = deriv + 3*nv*nv; // dacc/dpos
|
||||
mjtNum* F1 = deriv + 4*nv*nv; // dacc/dvel
|
||||
mjtNum* F2 = deriv + 5*nv*nv; // dacc/dfrc
|
||||
|
||||
// G2*F2 - I
|
||||
mju_mulMatMat(mat, G2, F2, nv, nv, nv);
|
||||
for( int i=0; i<nv; i++ )
|
||||
mat[i*(nv+1)] -= 1;
|
||||
error[0] = relnorm(mat, G2, nv*nv);
|
||||
// G2*F2 - I
|
||||
mju_mulMatMat(mat, G2, F2, nv, nv, nv);
|
||||
for (int i=0; i<nv; i++) {
|
||||
mat[i*(nv+1)] -= 1;
|
||||
}
|
||||
error[0] = relnorm(mat, G2, nv*nv);
|
||||
|
||||
// G2 - G2'
|
||||
mju_transpose(mat, G2, nv, nv);
|
||||
mju_sub(mat, mat, G2, nv*nv);
|
||||
error[1] = relnorm(mat, G2, nv*nv);
|
||||
// G2 - G2'
|
||||
mju_transpose(mat, G2, nv, nv);
|
||||
mju_sub(mat, mat, G2, nv*nv);
|
||||
error[1] = relnorm(mat, G2, nv*nv);
|
||||
|
||||
// G1 - G1'
|
||||
mju_transpose(mat, G1, nv, nv);
|
||||
mju_sub(mat, mat, G1, nv*nv);
|
||||
error[2] = relnorm(mat, G1, nv*nv);
|
||||
// G1 - G1'
|
||||
mju_transpose(mat, G1, nv, nv);
|
||||
mju_sub(mat, mat, G1, nv*nv);
|
||||
error[2] = relnorm(mat, G1, nv*nv);
|
||||
|
||||
// F2 - F2'
|
||||
mju_transpose(mat, F2, nv, nv);
|
||||
mju_sub(mat, mat, F2, nv*nv);
|
||||
error[3] = relnorm(mat, F2, nv*nv);
|
||||
// F2 - F2'
|
||||
mju_transpose(mat, F2, nv, nv);
|
||||
mju_sub(mat, mat, F2, nv*nv);
|
||||
error[3] = relnorm(mat, F2, nv*nv);
|
||||
|
||||
// G1 + G2*F1
|
||||
mju_mulMatMat(mat, G2, F1, nv, nv, nv);
|
||||
mju_addTo(mat, G1, nv*nv);
|
||||
error[4] = relnorm(mat, G1, nv*nv);
|
||||
// G1 + G2*F1
|
||||
mju_mulMatMat(mat, G2, F1, nv, nv, nv);
|
||||
mju_addTo(mat, G1, nv*nv);
|
||||
error[4] = relnorm(mat, G1, nv*nv);
|
||||
|
||||
// G0 + G2*F0
|
||||
mju_mulMatMat(mat, G2, F0, nv, nv, nv);
|
||||
mju_addTo(mat, G0, nv*nv);
|
||||
error[5] = relnorm(mat, G0, nv*nv);
|
||||
// G0 + G2*F0
|
||||
mju_mulMatMat(mat, G2, F0, nv, nv, nv);
|
||||
mju_addTo(mat, G0, nv*nv);
|
||||
error[5] = relnorm(mat, G0, nv*nv);
|
||||
|
||||
// F1 + F2*G1
|
||||
mju_mulMatMat(mat, F2, G1, nv, nv, nv);
|
||||
mju_addTo(mat, F1, nv*nv);
|
||||
error[6] = relnorm(mat, F1, nv*nv);
|
||||
// F1 + F2*G1
|
||||
mju_mulMatMat(mat, F2, G1, nv, nv, nv);
|
||||
mju_addTo(mat, F1, nv*nv);
|
||||
error[6] = relnorm(mat, F1, nv*nv);
|
||||
|
||||
// F0 + F2*G0
|
||||
mju_mulMatMat(mat, F2, G0, nv, nv, nv);
|
||||
mju_addTo(mat, F0, nv*nv);
|
||||
error[7] = relnorm(mat, F0, nv*nv);
|
||||
// F0 + F2*G0
|
||||
mju_mulMatMat(mat, F2, G0, nv, nv, nv);
|
||||
mju_addTo(mat, F0, nv*nv);
|
||||
error[7] = relnorm(mat, F0, nv*nv);
|
||||
|
||||
mjFREESTACK
|
||||
mjFREESTACK
|
||||
}
|
||||
|
||||
|
||||
// main function
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// print help if not enough arguments
|
||||
if( argc<2 )
|
||||
{
|
||||
printf("\n Arguments: modelfile [nthread niter nwarmup nepoch nstep eps]\n\n");
|
||||
return 1;
|
||||
}
|
||||
int main(int argc, char** argv) {
|
||||
// print help if not enough arguments
|
||||
if (argc<2) {
|
||||
std::printf("\n Arguments: modelfile [nthread niter nwarmup nepoch nstep eps]\n\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// default nthread = number of logical cores (usually optimal)
|
||||
nthread = omp_get_num_procs();
|
||||
// default nthread = number of logical cores (usually optimal)
|
||||
nthread = omp_get_num_procs();
|
||||
|
||||
// get numeric command-line arguments
|
||||
if( argc>2 )
|
||||
sscanf(argv[2], "%d", &nthread);
|
||||
if( argc>3 )
|
||||
sscanf(argv[3], "%d", &niter);
|
||||
if( argc>4 )
|
||||
sscanf(argv[4], "%d", &nwarmup);
|
||||
if( argc>5 )
|
||||
sscanf(argv[5], "%d", &nepoch);
|
||||
if( argc>6 )
|
||||
sscanf(argv[6], "%d", &nstep);
|
||||
if( argc>7 )
|
||||
sscanf(argv[7], "%lf", &eps);
|
||||
// get numeric command-line arguments
|
||||
if (argc>2) {
|
||||
std::sscanf(argv[2], "%d", &nthread);
|
||||
}
|
||||
if (argc>3) {
|
||||
std::sscanf(argv[3], "%d", &niter);
|
||||
}
|
||||
if (argc>4) {
|
||||
std::sscanf(argv[4], "%d", &nwarmup);
|
||||
}
|
||||
if (argc>5) {
|
||||
std::sscanf(argv[5], "%d", &nepoch);
|
||||
}
|
||||
if (argc>6) {
|
||||
std::sscanf(argv[6], "%d", &nstep);
|
||||
}
|
||||
if (argc>7) {
|
||||
std::sscanf(argv[7], "%lf", &eps);
|
||||
}
|
||||
|
||||
// check number of threads
|
||||
if( nthread<1 || nthread>MAXTHREAD )
|
||||
{
|
||||
printf("nthread must be between 1 and %d\n", MAXTHREAD);
|
||||
return 1;
|
||||
}
|
||||
// check number of threads
|
||||
if (nthread<1 || nthread>MAXTHREAD) {
|
||||
std::printf("nthread must be between 1 and %d\n", MAXTHREAD);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check number of epochs
|
||||
if( nepoch<1 || nepoch>MAXEPOCH )
|
||||
{
|
||||
printf("nepoch must be between 1 and %d\n", MAXEPOCH);
|
||||
return 1;
|
||||
}
|
||||
// check number of epochs
|
||||
if (nepoch<1 || nepoch>MAXEPOCH) {
|
||||
std::printf("nepoch must be between 1 and %d\n", MAXEPOCH);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// load model
|
||||
mjModel* m = 0;
|
||||
if( strlen(argv[1])>4 && !strcmp(argv[1]+strlen(argv[1])-4, ".mjb") )
|
||||
m = mj_loadModel(argv[1], NULL);
|
||||
else
|
||||
m = mj_loadXML(argv[1], NULL, NULL, 0);
|
||||
if( !m )
|
||||
{
|
||||
printf("Could not load modelfile '%s'\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
// load model
|
||||
mjModel* m = 0;
|
||||
if (std::strlen(argv[1])>4 && !std::strcmp(argv[1]+std::strlen(argv[1])-4, ".mjb")) {
|
||||
m = mj_loadModel(argv[1], NULL);
|
||||
} else {
|
||||
m = mj_loadXML(argv[1], NULL, NULL, 0);
|
||||
}
|
||||
if (!m) {
|
||||
std::printf("Could not load modelfile '%s'\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// print arguments
|
||||
// print arguments
|
||||
#if defined(_OPENMP)
|
||||
printf("\nnthread : %d (OpenMP)\n", nthread);
|
||||
std::printf("\nnthread : %d (OpenMP)\n", nthread);
|
||||
#else
|
||||
printf("\nnthread : %d (serial)\n", nthread);
|
||||
std::printf("\nnthread : %d (serial)\n", nthread);
|
||||
#endif
|
||||
printf("niter : %d\n", niter);
|
||||
printf("nwarmup : %d\n", nwarmup);
|
||||
printf("nepoch : %d\n", nepoch);
|
||||
printf("nstep : %d\n", nstep);
|
||||
printf("eps : %g\n\n", eps);
|
||||
std::printf("niter : %d\n", niter);
|
||||
std::printf("nwarmup : %d\n", nwarmup);
|
||||
std::printf("nepoch : %d\n", nepoch);
|
||||
std::printf("nstep : %d\n", nstep);
|
||||
std::printf("eps : %g\n\n", eps);
|
||||
|
||||
// make mjData: main, per-thread
|
||||
mjData* dmain = mj_makeData(m);
|
||||
mjData* d[MAXTHREAD];
|
||||
for( int n=0; n<nthread; n++ )
|
||||
d[n] = mj_makeData(m);
|
||||
// make mjData: main, per-thread
|
||||
mjData* dmain = mj_makeData(m);
|
||||
mjData* d[MAXTHREAD];
|
||||
for (int n=0; n<nthread; n++) {
|
||||
d[n] = mj_makeData(m);
|
||||
}
|
||||
|
||||
// allocate derivatives
|
||||
deriv = (mjtNum*) mju_malloc(6*sizeof(mjtNum)*m->nv*m->nv);
|
||||
// allocate derivatives
|
||||
deriv = (mjtNum*) mju_malloc(6*sizeof(mjtNum)*m->nv*m->nv);
|
||||
|
||||
// set up OpenMP (if not enabled, this does nothing)
|
||||
omp_set_dynamic(0);
|
||||
omp_set_num_threads(nthread);
|
||||
// set up OpenMP (if not enabled, this does nothing)
|
||||
omp_set_dynamic(0);
|
||||
omp_set_num_threads(nthread);
|
||||
|
||||
// save solver options
|
||||
int save_iterations = m->opt.iterations;
|
||||
mjtNum save_tolerance = m->opt.tolerance;
|
||||
// save solver options
|
||||
int save_iterations = m->opt.iterations;
|
||||
mjtNum save_tolerance = m->opt.tolerance;
|
||||
|
||||
// allocate statistics
|
||||
int nefc = 0;
|
||||
double cputm[MAXEPOCH][2];
|
||||
mjtNum error[MAXEPOCH][8];
|
||||
// allocate statistics
|
||||
int nefc = 0;
|
||||
double cputm[MAXEPOCH][2];
|
||||
mjtNum error[MAXEPOCH][8];
|
||||
|
||||
// run epochs, collect statistics
|
||||
for( int epoch=0; epoch<nepoch; epoch++ )
|
||||
{
|
||||
// set solver options for main simulation
|
||||
m->opt.iterations = save_iterations;
|
||||
m->opt.tolerance = save_tolerance;
|
||||
// run epochs, collect statistics
|
||||
for (int epoch=0; epoch<nepoch; epoch++) {
|
||||
// set solver options for main simulation
|
||||
m->opt.iterations = save_iterations;
|
||||
m->opt.tolerance = save_tolerance;
|
||||
|
||||
// advance main simulation for nstep
|
||||
for( int i=0; i<nstep; i++ )
|
||||
mj_step(m, dmain);
|
||||
|
||||
// count number of active constraints
|
||||
nefc += dmain->nefc;
|
||||
|
||||
// set solver options for finite differences
|
||||
m->opt.iterations = niter;
|
||||
m->opt.tolerance = 0;
|
||||
|
||||
// test forward and inverse
|
||||
for( isforward=0; isforward<2; isforward++ )
|
||||
{
|
||||
// start timer
|
||||
double starttm = omp_get_wtime();
|
||||
|
||||
// run worker threads in parallel if OpenMP is enabled
|
||||
#pragma omp parallel for schedule(static)
|
||||
for( int n=0; n<nthread; n++ )
|
||||
worker(m, dmain, d[n], n);
|
||||
|
||||
// record duration in ms
|
||||
cputm[epoch][isforward] = 1000*(omp_get_wtime() - starttm);
|
||||
}
|
||||
|
||||
// check derivatives
|
||||
checkderiv(m, d[0], error[epoch]);
|
||||
// advance main simulation for nstep
|
||||
for (int i=0; i<nstep; i++) {
|
||||
mj_step(m, dmain);
|
||||
}
|
||||
|
||||
// compute statistics
|
||||
double mcputm[2] = {0,0}, merror[8] = {0,0,0,0,0,0,0,0};
|
||||
for( int epoch=0; epoch<nepoch; epoch++ )
|
||||
{
|
||||
mcputm[0] += cputm[epoch][0];
|
||||
mcputm[1] += cputm[epoch][1];
|
||||
// count number of active constraints
|
||||
nefc += dmain->nefc;
|
||||
|
||||
for( int ie=0; ie<8; ie++ )
|
||||
merror[ie] += error[epoch][ie];
|
||||
// set solver options for finite differences
|
||||
m->opt.iterations = niter;
|
||||
m->opt.tolerance = 0;
|
||||
|
||||
// test forward and inverse
|
||||
for (isforward=0; isforward<2; isforward++) {
|
||||
// start timer
|
||||
double starttm = omp_get_wtime();
|
||||
|
||||
// run worker threads in parallel if OpenMP is enabled
|
||||
#pragma omp parallel for schedule(static)
|
||||
for (int n=0; n<nthread; n++) {
|
||||
worker(m, dmain, d[n], n);
|
||||
}
|
||||
|
||||
// record duration in ms
|
||||
cputm[epoch][isforward] = 1000*(omp_get_wtime() - starttm);
|
||||
}
|
||||
|
||||
// print sizes, timing, accuracy
|
||||
printf("sizes : nv %d, nefc %d\n\n", m->nv, nefc/nepoch);
|
||||
printf("inverse : %.2f ms\n", mcputm[0]/nepoch);
|
||||
printf("forward : %.2f ms\n\n", mcputm[1]/nepoch);
|
||||
printf("accuracy: log10(residual L1 relnorm)\n");
|
||||
printf("------------------------------------\n");
|
||||
for( int ie=0; ie<8; ie++ )
|
||||
printf(" %s : %.2g\n", accuracy[ie], merror[ie]/nepoch);
|
||||
printf("\n");
|
||||
// check derivatives
|
||||
checkderiv(m, d[0], error[epoch]);
|
||||
}
|
||||
|
||||
// shut down
|
||||
mju_free(deriv);
|
||||
mj_deleteData(dmain);
|
||||
for( int n=0; n<nthread; n++ )
|
||||
mj_deleteData(d[n]);
|
||||
mj_deleteModel(m);
|
||||
return 0;
|
||||
// compute statistics
|
||||
double mcputm[2] = {0, 0}, merror[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
for (int epoch=0; epoch<nepoch; epoch++) {
|
||||
mcputm[0] += cputm[epoch][0];
|
||||
mcputm[1] += cputm[epoch][1];
|
||||
|
||||
for (int ie=0; ie<8; ie++) {
|
||||
merror[ie] += error[epoch][ie];
|
||||
}
|
||||
}
|
||||
|
||||
// print sizes, timing, accuracy
|
||||
std::printf("sizes : nv %d, nefc %d\n\n", m->nv, nefc/nepoch);
|
||||
std::printf("inverse : %.2f ms\n", mcputm[0]/nepoch);
|
||||
std::printf("forward : %.2f ms\n\n", mcputm[1]/nepoch);
|
||||
std::printf("accuracy: log10(residual L1 relnorm)\n");
|
||||
std::printf("------------------------------------\n");
|
||||
for (int ie=0; ie<8; ie++) {
|
||||
std::printf(" %s : %.2g\n", accuracy[ie], merror[ie]/nepoch);
|
||||
}
|
||||
std::printf("\n");
|
||||
|
||||
// shut down
|
||||
mju_free(deriv);
|
||||
mj_deleteData(dmain);
|
||||
for (int n=0; n<nthread; n++) {
|
||||
mj_deleteData(d[n]);
|
||||
}
|
||||
mj_deleteModel(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+217
-208
@@ -12,23 +12,25 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "mujoco.h"
|
||||
#include "stdio.h"
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "mujoco.h"
|
||||
|
||||
// select EGL, OSMESA or GLFW
|
||||
#if defined(MJ_EGL)
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/egl.h>
|
||||
#elif defined(MJ_OSMESA)
|
||||
#include <GL/osmesa.h>
|
||||
OSMesaContext ctx;
|
||||
unsigned char buffer[10000000];
|
||||
#include <GL/osmesa.h>
|
||||
OSMesaContext ctx;
|
||||
unsigned char buffer[10000000];
|
||||
#else
|
||||
#include "glfw3.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#endif
|
||||
|
||||
#include "array_safety.h"
|
||||
namespace mju = ::mujoco::sample_util;
|
||||
|
||||
//-------------------------------- global data ------------------------------------------
|
||||
|
||||
@@ -46,266 +48,273 @@ mjrContext con;
|
||||
//-------------------------------- utility functions ------------------------------------
|
||||
|
||||
// load model, init simulation and rendering
|
||||
void initMuJoCo(const char* filename)
|
||||
{
|
||||
// load and compile
|
||||
char error[1000] = "Could not load binary model";
|
||||
if( strlen(filename)>4 && !strcmp(filename+strlen(filename)-4, ".mjb") )
|
||||
m = mj_loadModel(filename, 0);
|
||||
else
|
||||
m = mj_loadXML(filename, 0, error, 1000);
|
||||
if( !m )
|
||||
mju_error_s("Load model error: %s", error);
|
||||
void initMuJoCo(const char* filename) {
|
||||
// load and compile
|
||||
char error[1000] = "Could not load binary model";
|
||||
if (std::strlen(filename)>4 && !std::strcmp(filename+std::strlen(filename)-4, ".mjb")) {
|
||||
m = mj_loadModel(filename, 0);
|
||||
} else {
|
||||
m = mj_loadXML(filename, 0, error, 1000);
|
||||
}
|
||||
if (!m) {
|
||||
mju_error_s("Load model error: %s", error);
|
||||
}
|
||||
|
||||
// make data, run one computation to initialize all fields
|
||||
d = mj_makeData(m);
|
||||
mj_forward(m, d);
|
||||
// make data, run one computation to initialize all fields
|
||||
d = mj_makeData(m);
|
||||
mj_forward(m, d);
|
||||
|
||||
// initialize visualization data structures
|
||||
mjv_defaultCamera(&cam);
|
||||
mjv_defaultOption(&opt);
|
||||
mjv_defaultScene(&scn);
|
||||
mjr_defaultContext(&con);
|
||||
// initialize visualization data structures
|
||||
mjv_defaultCamera(&cam);
|
||||
mjv_defaultOption(&opt);
|
||||
mjv_defaultScene(&scn);
|
||||
mjr_defaultContext(&con);
|
||||
|
||||
// create scene and context
|
||||
mjv_makeScene(m, &scn, 2000);
|
||||
mjr_makeContext(m, &con, 200);
|
||||
// create scene and context
|
||||
mjv_makeScene(m, &scn, 2000);
|
||||
mjr_makeContext(m, &con, 200);
|
||||
|
||||
// center and scale view
|
||||
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;
|
||||
// center and scale view
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// deallocate everything
|
||||
void closeMuJoCo(void)
|
||||
{
|
||||
mj_deleteData(d);
|
||||
mj_deleteModel(m);
|
||||
mjr_freeContext(&con);
|
||||
mjv_freeScene(&scn);
|
||||
void closeMuJoCo(void) {
|
||||
mj_deleteData(d);
|
||||
mj_deleteModel(m);
|
||||
mjr_freeContext(&con);
|
||||
mjv_freeScene(&scn);
|
||||
}
|
||||
|
||||
|
||||
// create OpenGL context/window
|
||||
void initOpenGL(void)
|
||||
{
|
||||
//------------------------ EGL
|
||||
void initOpenGL(void) {
|
||||
//------------------------ EGL
|
||||
#if defined(MJ_EGL)
|
||||
// desired config
|
||||
const EGLint configAttribs[] ={
|
||||
EGL_RED_SIZE, 8,
|
||||
EGL_GREEN_SIZE, 8,
|
||||
EGL_BLUE_SIZE, 8,
|
||||
EGL_ALPHA_SIZE, 8,
|
||||
EGL_DEPTH_SIZE, 24,
|
||||
EGL_STENCIL_SIZE, 8,
|
||||
EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
|
||||
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
|
||||
EGL_NONE
|
||||
};
|
||||
// desired config
|
||||
const EGLint configAttribs[] = {
|
||||
EGL_RED_SIZE, 8,
|
||||
EGL_GREEN_SIZE, 8,
|
||||
EGL_BLUE_SIZE, 8,
|
||||
EGL_ALPHA_SIZE, 8,
|
||||
EGL_DEPTH_SIZE, 24,
|
||||
EGL_STENCIL_SIZE, 8,
|
||||
EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
|
||||
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
// get default display
|
||||
EGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if( eglDpy==EGL_NO_DISPLAY )
|
||||
mju_error_i("Could not get EGL display, error 0x%x\n", eglGetError());
|
||||
// get default display
|
||||
EGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (eglDpy==EGL_NO_DISPLAY) {
|
||||
mju_error_i("Could not get EGL display, error 0x%x\n", eglGetError());
|
||||
}
|
||||
|
||||
// initialize
|
||||
EGLint major, minor;
|
||||
if( eglInitialize(eglDpy, &major, &minor)!=EGL_TRUE )
|
||||
mju_error_i("Could not initialize EGL, error 0x%x\n", eglGetError());
|
||||
// initialize
|
||||
EGLint major, minor;
|
||||
if (eglInitialize(eglDpy, &major, &minor)!=EGL_TRUE) {
|
||||
mju_error_i("Could not initialize EGL, error 0x%x\n", eglGetError());
|
||||
}
|
||||
|
||||
// choose config
|
||||
EGLint numConfigs;
|
||||
EGLConfig eglCfg;
|
||||
if( eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs)!=EGL_TRUE )
|
||||
mju_error_i("Could not choose EGL config, error 0x%x\n", eglGetError());
|
||||
// choose config
|
||||
EGLint numConfigs;
|
||||
EGLConfig eglCfg;
|
||||
if (eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs)!=EGL_TRUE) {
|
||||
mju_error_i("Could not choose EGL config, error 0x%x\n", eglGetError());
|
||||
}
|
||||
|
||||
// bind OpenGL API
|
||||
if( eglBindAPI(EGL_OPENGL_API)!=EGL_TRUE )
|
||||
mju_error_i("Could not bind EGL OpenGL API, error 0x%x\n", eglGetError());
|
||||
// bind OpenGL API
|
||||
if (eglBindAPI(EGL_OPENGL_API)!=EGL_TRUE) {
|
||||
mju_error_i("Could not bind EGL OpenGL API, error 0x%x\n", eglGetError());
|
||||
}
|
||||
|
||||
// create context
|
||||
EGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, NULL);
|
||||
if( eglCtx==EGL_NO_CONTEXT )
|
||||
mju_error_i("Could not create EGL context, error 0x%x\n", eglGetError());
|
||||
// create context
|
||||
EGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, NULL);
|
||||
if (eglCtx==EGL_NO_CONTEXT) {
|
||||
mju_error_i("Could not create EGL context, error 0x%x\n", eglGetError());
|
||||
}
|
||||
|
||||
// make context current, no surface (let OpenGL handle FBO)
|
||||
if( eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx)!=EGL_TRUE )
|
||||
mju_error_i("Could not make EGL context current, error 0x%x\n", eglGetError());
|
||||
// make context current, no surface (let OpenGL handle FBO)
|
||||
if (eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx)!=EGL_TRUE) {
|
||||
mju_error_i("Could not make EGL context current, error 0x%x\n", eglGetError());
|
||||
}
|
||||
|
||||
//------------------------ OSMESA
|
||||
//------------------------ OSMESA
|
||||
#elif defined(MJ_OSMESA)
|
||||
// create context
|
||||
ctx = OSMesaCreateContextExt(GL_RGBA, 24, 8, 8, 0);
|
||||
if( !ctx )
|
||||
mju_error("OSMesa context creation failed");
|
||||
// create context
|
||||
ctx = OSMesaCreateContextExt(GL_RGBA, 24, 8, 8, 0);
|
||||
if (!ctx) {
|
||||
mju_error("OSMesa context creation failed");
|
||||
}
|
||||
|
||||
// make current
|
||||
if( !OSMesaMakeCurrent(ctx, buffer, GL_UNSIGNED_BYTE, 800, 800) )
|
||||
mju_error("OSMesa make current failed");
|
||||
// make current
|
||||
if (!OSMesaMakeCurrent(ctx, buffer, GL_UNSIGNED_BYTE, 800, 800)) {
|
||||
mju_error("OSMesa make current failed");
|
||||
}
|
||||
|
||||
//------------------------ GLFW
|
||||
//------------------------ GLFW
|
||||
#else
|
||||
// init GLFW
|
||||
if( !glfwInit() )
|
||||
mju_error("Could not initialize GLFW");
|
||||
// init GLFW
|
||||
if (!glfwInit()) {
|
||||
mju_error("Could not initialize GLFW");
|
||||
}
|
||||
|
||||
// create invisible window, single-buffered
|
||||
glfwWindowHint(GLFW_VISIBLE, 0);
|
||||
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE);
|
||||
GLFWwindow* window = glfwCreateWindow(800, 800, "Invisible window", NULL, NULL);
|
||||
if( !window )
|
||||
mju_error("Could not create GLFW window");
|
||||
// create invisible window, single-buffered
|
||||
glfwWindowHint(GLFW_VISIBLE, 0);
|
||||
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE);
|
||||
GLFWwindow* window = glfwCreateWindow(800, 800, "Invisible window", NULL, NULL);
|
||||
if (!window) {
|
||||
mju_error("Could not create GLFW window");
|
||||
}
|
||||
|
||||
// make context current
|
||||
glfwMakeContextCurrent(window);
|
||||
// make context current
|
||||
glfwMakeContextCurrent(window);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// close OpenGL context/window
|
||||
void closeOpenGL(void)
|
||||
{
|
||||
//------------------------ EGL
|
||||
void closeOpenGL(void) {
|
||||
//------------------------ EGL
|
||||
#if defined(MJ_EGL)
|
||||
// get current display
|
||||
EGLDisplay eglDpy = eglGetCurrentDisplay();
|
||||
if( eglDpy==EGL_NO_DISPLAY )
|
||||
return;
|
||||
// get current display
|
||||
EGLDisplay eglDpy = eglGetCurrentDisplay();
|
||||
if (eglDpy==EGL_NO_DISPLAY) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get current context
|
||||
EGLContext eglCtx = eglGetCurrentContext();
|
||||
// get current context
|
||||
EGLContext eglCtx = eglGetCurrentContext();
|
||||
|
||||
// release context
|
||||
eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
// release context
|
||||
eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
|
||||
// destroy context if valid
|
||||
if( eglCtx!=EGL_NO_CONTEXT )
|
||||
eglDestroyContext(eglDpy, eglCtx);
|
||||
// destroy context if valid
|
||||
if (eglCtx!=EGL_NO_CONTEXT) {
|
||||
eglDestroyContext(eglDpy, eglCtx);
|
||||
}
|
||||
|
||||
// terminate display
|
||||
eglTerminate(eglDpy);
|
||||
// terminate display
|
||||
eglTerminate(eglDpy);
|
||||
|
||||
//------------------------ OSMESA
|
||||
//------------------------ OSMESA
|
||||
#elif defined(MJ_OSMESA)
|
||||
OSMesaDestroyContext(ctx);
|
||||
OSMesaDestroyContext(ctx);
|
||||
|
||||
//------------------------ GLFW
|
||||
//------------------------ GLFW
|
||||
#else
|
||||
// terminate GLFW (crashes with Linux NVidia drivers)
|
||||
#if defined(__APPLE__) || defined(_WIN32)
|
||||
glfwTerminate();
|
||||
#endif
|
||||
// terminate GLFW (crashes with Linux NVidia drivers)
|
||||
#if defined(__APPLE__) || defined(_WIN32)
|
||||
glfwTerminate();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------- main function ----------------------------------------
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
// check command-line arguments
|
||||
if( argc!=5 )
|
||||
{
|
||||
printf(" USAGE: record modelfile duration fps rgbfile\n");
|
||||
return 0;
|
||||
}
|
||||
int main(int argc, const char** argv) {
|
||||
// check command-line arguments
|
||||
if (argc!=5) {
|
||||
std::printf(" USAGE: record modelfile duration fps rgbfile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// parse numeric arguments
|
||||
double duration = 10, fps = 30;
|
||||
sscanf(argv[2], "%lf", &duration);
|
||||
sscanf(argv[3], "%lf", &fps);
|
||||
// parse numeric arguments
|
||||
double duration = 10, fps = 30;
|
||||
std::sscanf(argv[2], "%lf", &duration);
|
||||
std::sscanf(argv[3], "%lf", &fps);
|
||||
|
||||
// initialize OpenGL and MuJoCo
|
||||
initOpenGL();
|
||||
initMuJoCo(argv[1]);
|
||||
// initialize OpenGL and MuJoCo
|
||||
initOpenGL();
|
||||
initMuJoCo(argv[1]);
|
||||
|
||||
// set rendering to offscreen buffer
|
||||
mjr_setBuffer(mjFB_OFFSCREEN, &con);
|
||||
if( con.currentBuffer!=mjFB_OFFSCREEN )
|
||||
printf("Warning: offscreen rendering not supported, using default/window framebuffer\n");
|
||||
// set rendering to offscreen buffer
|
||||
mjr_setBuffer(mjFB_OFFSCREEN, &con);
|
||||
if (con.currentBuffer!=mjFB_OFFSCREEN) {
|
||||
std::printf("Warning: offscreen rendering not supported, using default/window framebuffer\n");
|
||||
}
|
||||
|
||||
// get size of active renderbuffer
|
||||
mjrRect viewport = mjr_maxViewport(&con);
|
||||
int W = viewport.width;
|
||||
int H = viewport.height;
|
||||
// get size of active renderbuffer
|
||||
mjrRect viewport = mjr_maxViewport(&con);
|
||||
int W = viewport.width;
|
||||
int H = viewport.height;
|
||||
|
||||
// allocate rgb and depth buffers
|
||||
unsigned char* rgb = (unsigned char*)malloc(3*W*H);
|
||||
float* depth = (float*)malloc(sizeof(float)*W*H);
|
||||
if( !rgb || !depth )
|
||||
mju_error("Could not allocate buffers");
|
||||
// allocate rgb and depth buffers
|
||||
unsigned char* rgb = (unsigned char*)std::malloc(3*W*H);
|
||||
float* depth = (float*)std::malloc(sizeof(float)*W*H);
|
||||
if (!rgb || !depth) {
|
||||
mju_error("Could not allocate buffers");
|
||||
}
|
||||
|
||||
// create output rgb file
|
||||
FILE* fp = fopen(argv[4], "wb");
|
||||
if( !fp )
|
||||
mju_error("Could not open rgbfile for writing");
|
||||
// create output rgb file
|
||||
std::FILE* fp = std::fopen(argv[4], "wb");
|
||||
if (!fp) {
|
||||
mju_error("Could not open rgbfile for writing");
|
||||
}
|
||||
|
||||
// main loop
|
||||
double frametime = 0;
|
||||
int framecount = 0;
|
||||
while( d->time<duration )
|
||||
{
|
||||
// render new frame if it is time (or first frame)
|
||||
if( (d->time-frametime)>1/fps || frametime==0 )
|
||||
{
|
||||
// update abstract scene
|
||||
mjv_updateScene(m, d, &opt, NULL, &cam, mjCAT_ALL, &scn);
|
||||
// main loop
|
||||
double frametime = 0;
|
||||
int framecount = 0;
|
||||
while (d->time<duration) {
|
||||
// render new frame if it is time (or first frame)
|
||||
if ((d->time-frametime)>1/fps || frametime==0) {
|
||||
// update abstract scene
|
||||
mjv_updateScene(m, d, &opt, NULL, &cam, mjCAT_ALL, &scn);
|
||||
|
||||
// render scene in offscreen buffer
|
||||
mjr_render(viewport, &scn, &con);
|
||||
// render scene in offscreen buffer
|
||||
mjr_render(viewport, &scn, &con);
|
||||
|
||||
// add time stamp in upper-left corner
|
||||
char stamp[50];
|
||||
sprintf(stamp, "Time = %.3f", d->time);
|
||||
mjr_overlay(mjFONT_NORMAL, mjGRID_TOPLEFT, viewport, stamp, NULL, &con);
|
||||
// add time stamp in upper-left corner
|
||||
char stamp[50];
|
||||
mju::sprintf_arr(stamp, "Time = %.3f", d->time);
|
||||
mjr_overlay(mjFONT_NORMAL, mjGRID_TOPLEFT, viewport, stamp, NULL, &con);
|
||||
|
||||
// read rgb and depth buffers
|
||||
mjr_readPixels(rgb, depth, viewport, &con);
|
||||
// read rgb and depth buffers
|
||||
mjr_readPixels(rgb, depth, viewport, &con);
|
||||
|
||||
// insert subsampled depth image in lower-left corner of rgb image
|
||||
const int NS = 3; // depth image sub-sampling
|
||||
for( int r=0; r<H; r+=NS )
|
||||
for( int c=0; c<W; c+=NS )
|
||||
{
|
||||
int adr = (r/NS)*W + c/NS;
|
||||
rgb[3*adr] = rgb[3*adr+1] = rgb[3*adr+2] =
|
||||
(unsigned char)((1.0f-depth[r*W+c])*255.0f);
|
||||
}
|
||||
|
||||
// write rgb image to file
|
||||
fwrite(rgb, 3, W*H, fp);
|
||||
|
||||
// print every 10 frames: '.' if ok, 'x' if OpenGL error
|
||||
if( ((framecount++)%10)==0 )
|
||||
{
|
||||
if( mjr_getError() )
|
||||
printf("x");
|
||||
else
|
||||
printf(".");
|
||||
}
|
||||
|
||||
// save simulation time
|
||||
frametime = d->time;
|
||||
// insert subsampled depth image in lower-left corner of rgb image
|
||||
const int NS = 3; // depth image sub-sampling
|
||||
for (int r=0; r<H; r+=NS)
|
||||
for (int c=0; c<W; c+=NS) {
|
||||
int adr = (r/NS)*W + c/NS;
|
||||
rgb[3*adr] = rgb[3*adr+1] = rgb[3*adr+2] = (unsigned char)((1.0f-depth[r*W+c])*255.0f);
|
||||
}
|
||||
|
||||
// advance simulation
|
||||
mj_step(m, d);
|
||||
// write rgb image to file
|
||||
std::fwrite(rgb, 3, W*H, fp);
|
||||
|
||||
// print every 10 frames: '.' if ok, 'x' if OpenGL error
|
||||
if (((framecount++)%10)==0) {
|
||||
if (mjr_getError()) {
|
||||
std::printf("x");
|
||||
} else {
|
||||
std::printf(".");
|
||||
}
|
||||
}
|
||||
|
||||
// save simulation time
|
||||
frametime = d->time;
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// close file, free buffers
|
||||
fclose(fp);
|
||||
free(rgb);
|
||||
free(depth);
|
||||
// advance simulation
|
||||
mj_step(m, d);
|
||||
}
|
||||
std::printf("\n");
|
||||
|
||||
// close MuJoCo and OpenGL
|
||||
closeMuJoCo();
|
||||
closeOpenGL();
|
||||
// close file, free buffers
|
||||
std::fclose(fp);
|
||||
std::free(rgb);
|
||||
std::free(depth);
|
||||
|
||||
return 1;
|
||||
// close MuJoCo and OpenGL
|
||||
closeMuJoCo();
|
||||
closeOpenGL();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
+1690
-1641
File diff suppressed because it is too large
Load Diff
Regular → Executable
+159
-133
@@ -12,15 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "mujoco.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
using namespace std;
|
||||
#include "mujoco.h"
|
||||
|
||||
|
||||
// model and per-thread data
|
||||
@@ -35,161 +33,189 @@ double simtime[64];
|
||||
|
||||
|
||||
// timer
|
||||
chrono::system_clock::time_point tm_start;
|
||||
mjtNum gettm(void)
|
||||
{
|
||||
chrono::duration<double> elapsed = chrono::system_clock::now() - tm_start;
|
||||
return elapsed.count();
|
||||
std::chrono::system_clock::time_point tm_start;
|
||||
mjtNum gettm(void) {
|
||||
std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - tm_start;
|
||||
return elapsed.count();
|
||||
}
|
||||
|
||||
|
||||
// deallocate and print message
|
||||
int finish(const char* msg = NULL, mjModel* m = NULL)
|
||||
{
|
||||
// deallocate model
|
||||
if( m )
|
||||
mj_deleteModel(m);
|
||||
int finish(const char* msg = NULL, mjModel* m = NULL) {
|
||||
// deallocate model
|
||||
if (m) {
|
||||
mj_deleteModel(m);
|
||||
}
|
||||
|
||||
// print message
|
||||
if( msg )
|
||||
printf("%s\n", msg);
|
||||
// print message
|
||||
if (msg) {
|
||||
std::printf("%s\n", msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// thread function
|
||||
void simulate(int id, int nstep)
|
||||
{
|
||||
// clear statistics
|
||||
contacts[id] = 0;
|
||||
constraints[id] = 0;
|
||||
void simulate(int id, int nstep, mjtNum ctrlnoise) {
|
||||
// clear statistics
|
||||
contacts[id] = 0;
|
||||
constraints[id] = 0;
|
||||
|
||||
// run and time
|
||||
double start = gettm();
|
||||
for( int i=0; i<nstep; i++ )
|
||||
{
|
||||
// advance simulation
|
||||
mj_step(m, d[id]);
|
||||
// run and time
|
||||
double start = gettm();
|
||||
for (int i=0; i<nstep; i++) {
|
||||
// inject pseuso-random control noise
|
||||
if (ctrlnoise)
|
||||
for (int j=0; j<m->nu; j++) {
|
||||
mjtNum center = 0.0;
|
||||
mjtNum radius = 1.0;
|
||||
mjtNum* range = m->actuator_ctrlrange + 2*j;
|
||||
if (m->actuator_ctrllimited[j]) {
|
||||
center = (range[1] + range[0]) / 2;
|
||||
radius = (range[1] - range[0]) / 2;
|
||||
}
|
||||
radius *= ctrlnoise;
|
||||
d[id]->ctrl[j] = center + radius * (2*mju_Halton(i, j+2) - 1);
|
||||
}
|
||||
|
||||
// accumulate statistics
|
||||
contacts[id] += d[id]->ncon;
|
||||
constraints[id] += d[id]->nefc;
|
||||
}
|
||||
simtime[id] = gettm() - start;
|
||||
// advance simulation
|
||||
mj_step(m, d[id]);
|
||||
|
||||
// accumulate statistics
|
||||
contacts[id] += d[id]->ncon;
|
||||
constraints[id] += d[id]->nefc;
|
||||
}
|
||||
simtime[id] = gettm() - start;
|
||||
}
|
||||
|
||||
|
||||
// main function
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
// print help if arguments are missing
|
||||
if( argc<3 || argc>5 )
|
||||
return finish("\n Usage: testspeed modelfile nstep [nthread [profile]]\n");
|
||||
int main(int argc, const char** argv) {
|
||||
// print help if arguments are missing
|
||||
if (argc<2 || argc>6) {
|
||||
return finish("\n Usage: testspeed modelfile [nstep nthread ctrlnoise profile]\n");
|
||||
}
|
||||
|
||||
// read nstep and nthread
|
||||
int nstep = 0, nthread = 0, profile = 0;
|
||||
if( sscanf(argv[2], "%d", &nstep)!=1 || nstep<=0 )
|
||||
return finish("Invalid nstep argument");
|
||||
if( argc>3 )
|
||||
if( sscanf(argv[3], "%d", &nthread)!=1 )
|
||||
return finish("Invalid nthread argument");
|
||||
if( argc>4 )
|
||||
if( sscanf(argv[4], "%d", &profile)!=1 )
|
||||
return finish("Invalid profile argument");
|
||||
|
||||
// clamp nthread to [1, 64]
|
||||
nthread = mjMAX(1, mjMIN(64, nthread));
|
||||
|
||||
// get filename, determine file type
|
||||
std::string filename(argv[1]);
|
||||
bool binary = (filename.find(".mjb")!=std::string::npos);
|
||||
|
||||
// load model
|
||||
char error[1000] = "Could not load binary model";
|
||||
if( binary )
|
||||
m = mj_loadModel(argv[1], 0);
|
||||
else
|
||||
m = mj_loadXML(argv[1], 0, error, 1000);
|
||||
if( !m )
|
||||
return finish(error);
|
||||
|
||||
// make per-thread data
|
||||
int testkey = mj_name2id(m, mjOBJ_KEY, "test");
|
||||
for( int id=0; id<nthread; id++ )
|
||||
{
|
||||
d[id] = mj_makeData(m);
|
||||
if( !d[id] )
|
||||
return finish("Could not allocate mjData", m);
|
||||
|
||||
// init to keyframe "test" if present
|
||||
if( testkey>=0 )
|
||||
{
|
||||
mju_copy(d[id]->qpos, m->key_qpos + testkey*m->nq, m->nq);
|
||||
mju_copy(d[id]->qvel, m->key_qvel + testkey*m->nv, m->nv);
|
||||
mju_copy(d[id]->act, m->key_act + testkey*m->na, m->na);
|
||||
}
|
||||
// read arguments
|
||||
int nstep = 10000, nthread = 0, profile = 0;
|
||||
// inject small noise by default, to avoid fixed contact state
|
||||
mjtNum ctrlnoise = 0.01;
|
||||
if (argc>2)
|
||||
if (std::sscanf(argv[2], "%d", &nstep)!=1 || nstep<=0) {
|
||||
return finish("Invalid nstep argument");
|
||||
}
|
||||
if (argc>3)
|
||||
if (std::sscanf(argv[3], "%d", &nthread)!=1) {
|
||||
return finish("Invalid nthread argument");
|
||||
}
|
||||
if (argc>4)
|
||||
if (std::sscanf(argv[4], "%lf", &ctrlnoise)!=1) {
|
||||
return finish("Invalid ctrlnoise argument");
|
||||
}
|
||||
if (argc>5)
|
||||
if (std::sscanf(argv[5], "%d", &profile)!=1) {
|
||||
return finish("Invalid profile argument");
|
||||
}
|
||||
|
||||
// install timer callback for profiling if requested
|
||||
tm_start = chrono::system_clock::now();
|
||||
if( profile )
|
||||
mjcb_time = gettm;
|
||||
// clamp ctrlnoise to [0.0, 1.0]
|
||||
ctrlnoise = mjMAX(0.0, mjMIN(ctrlnoise, 1.0));
|
||||
|
||||
// print start
|
||||
if( nthread>1 )
|
||||
printf("\nRunning %d steps per thread at dt = %g ...\n\n", nstep, m->opt.timestep);
|
||||
else
|
||||
printf("\nRunning %d steps at dt = %g ...\n\n", nstep, m->opt.timestep);
|
||||
// clamp nthread to [1, 64]
|
||||
nthread = mjMAX(1, mjMIN(64, nthread));
|
||||
|
||||
// run simulation, record total time
|
||||
thread th[64];
|
||||
double starttime = gettm();
|
||||
for( int id=0; id<nthread; id++ )
|
||||
th[id] = thread(simulate, id, nstep);
|
||||
for( int id=0; id<nthread; id++ )
|
||||
th[id].join();
|
||||
double tottime = gettm() - starttime;
|
||||
// get filename, determine file type
|
||||
std::string filename(argv[1]);
|
||||
bool binary = (filename.find(".mjb")!=std::string::npos);
|
||||
|
||||
// all-thread summary
|
||||
if( nthread>1 )
|
||||
{
|
||||
printf("Summary for all %d threads\n\n", nthread);
|
||||
printf(" Total simulation time : %.2f s\n", tottime);
|
||||
printf(" Total steps per second : %.0f\n", nthread*nstep/tottime);
|
||||
printf(" Total realtime factor : %.2f x\n", nthread*nstep*m->opt.timestep/tottime);
|
||||
printf(" Total time per step : %.4f ms\n\n", 1000*tottime/(nthread*nstep));
|
||||
// load model
|
||||
char error[1000] = "Could not load binary model";
|
||||
if (binary) {
|
||||
m = mj_loadModel(argv[1], 0);
|
||||
} else {
|
||||
m = mj_loadXML(argv[1], 0, error, 1000);
|
||||
}
|
||||
if (!m) {
|
||||
return finish(error);
|
||||
}
|
||||
|
||||
printf("Details for thread 0\n\n");
|
||||
// make per-thread data
|
||||
int testkey = mj_name2id(m, mjOBJ_KEY, "test");
|
||||
for (int id=0; id<nthread; id++) {
|
||||
d[id] = mj_makeData(m);
|
||||
if (!d[id]) {
|
||||
return finish("Could not allocate mjData", m);
|
||||
}
|
||||
|
||||
// details for thread 0
|
||||
printf(" Simulation time : %.2f s\n", simtime[0]);
|
||||
printf(" Steps per second : %.0f\n", nstep/simtime[0]);
|
||||
printf(" Realtime factor : %.2f x\n", nstep*m->opt.timestep/simtime[0]);
|
||||
printf(" Time per step : %.4f ms\n\n", 1000*simtime[0]/nstep);
|
||||
printf(" Contacts per step : %d\n", contacts[0]/nstep);
|
||||
printf(" Constraints per step : %d\n", constraints[0]/nstep);
|
||||
printf(" Degrees of freedom : %d\n\n", m->nv);
|
||||
// init to keyframe "test" if present
|
||||
if (testkey>=0) {
|
||||
mju_copy(d[id]->qpos, m->key_qpos + testkey*m->nq, m->nq);
|
||||
mju_copy(d[id]->qvel, m->key_qvel + testkey*m->nv, m->nv);
|
||||
mju_copy(d[id]->act, m->key_act + testkey*m->na, m->na);
|
||||
}
|
||||
}
|
||||
|
||||
// profiler results for thread 0
|
||||
if( profile )
|
||||
{
|
||||
printf(" Profiler phase (ms per step)\n");
|
||||
mjtNum tstep = d[0]->timer[mjTIMER_STEP].duration/d[0]->timer[mjTIMER_STEP].number;
|
||||
for( int i=0; i<mjNTIMER; i++ )
|
||||
if( d[0]->timer[i].number>0 )
|
||||
{
|
||||
mjtNum istep = d[0]->timer[i].duration/d[0]->timer[i].number;
|
||||
printf(" %16s : %.5f (%6.2f %%)\n", mjTIMERSTRING[i],
|
||||
// install timer callback for profiling if requested
|
||||
tm_start = std::chrono::system_clock::now();
|
||||
if (profile) {
|
||||
mjcb_time = gettm;
|
||||
}
|
||||
|
||||
// print start
|
||||
if (nthread>1) {
|
||||
std::printf("\nRunning %d steps per thread at dt = %g ...\n\n", nstep, m->opt.timestep);
|
||||
} else {
|
||||
std::printf("\nRunning %d steps at dt = %g ...\n\n", nstep, m->opt.timestep);
|
||||
}
|
||||
|
||||
// run simulation, record total time
|
||||
std::thread th[64];
|
||||
double starttime = gettm();
|
||||
for (int id=0; id<nthread; id++) {
|
||||
th[id] = std::thread(simulate, id, nstep, ctrlnoise);
|
||||
}
|
||||
for (int id=0; id<nthread; id++) {
|
||||
th[id].join();
|
||||
}
|
||||
double tottime = gettm() - starttime;
|
||||
|
||||
// all-thread summary
|
||||
if (nthread>1) {
|
||||
std::printf("Summary for all %d threads\n\n", nthread);
|
||||
std::printf(" Total simulation time : %.2f s\n", tottime);
|
||||
std::printf(" Total steps per second : %.0f\n", nthread*nstep/tottime);
|
||||
std::printf(" Total realtime factor : %.2f x\n", nthread*nstep*m->opt.timestep/tottime);
|
||||
std::printf(" Total time per step : %.4f ms\n\n", 1000*tottime/(nthread*nstep));
|
||||
|
||||
std::printf("Details for thread 0\n\n");
|
||||
}
|
||||
|
||||
// details for thread 0
|
||||
std::printf(" Simulation time : %.2f s\n", simtime[0]);
|
||||
std::printf(" Steps per second : %.0f\n", nstep/simtime[0]);
|
||||
std::printf(" Realtime factor : %.2f x\n", nstep*m->opt.timestep/simtime[0]);
|
||||
std::printf(" Time per step : %.4f ms\n\n", 1000*simtime[0]/nstep);
|
||||
std::printf(" Contacts per step : %.2f\n", static_cast<float>(contacts[0])/nstep);
|
||||
std::printf(" Constraints per step : %.2f\n", static_cast<float>(constraints[0])/nstep);
|
||||
std::printf(" Degrees of freedom : %d\n\n", m->nv);
|
||||
|
||||
// profiler results for thread 0
|
||||
if (profile) {
|
||||
printf(" Profiler phase (ms per step)\n");
|
||||
mjtNum tstep = d[0]->timer[mjTIMER_STEP].duration/d[0]->timer[mjTIMER_STEP].number;
|
||||
for (int i=0; i<mjNTIMER; i++)
|
||||
if (d[0]->timer[i].number>0) {
|
||||
mjtNum istep = d[0]->timer[i].duration/d[0]->timer[i].number;
|
||||
std::printf(" %16s : %.5f (%6.2f %%)\n", mjTIMERSTRING[i],
|
||||
1000*istep, 100*istep/tstep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free per-thread data
|
||||
for( int id=0; id<nthread; id++ )
|
||||
mj_deleteData(d[id]);
|
||||
// free per-thread data
|
||||
for (int id=0; id<nthread; id++) {
|
||||
mj_deleteData(d[id]);
|
||||
}
|
||||
|
||||
// finalize
|
||||
return finish();
|
||||
// finalize
|
||||
return finish();
|
||||
}
|
||||
|
||||
Regular → Executable
+107
-111
@@ -12,163 +12,159 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "mujoco.h"
|
||||
#include "mjxmacro.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
#include "mjxmacro.h"
|
||||
#include "mujoco.h"
|
||||
|
||||
using namespace std;
|
||||
#include "array_safety.h"
|
||||
namespace mju = ::mujoco::sample_util;
|
||||
|
||||
static constexpr int kFieldSize = 500;
|
||||
|
||||
// help
|
||||
const char helpstring[] = "\n Usage: testxml modelfile.xml\n";
|
||||
|
||||
|
||||
// deallocate and print message
|
||||
int finish(const char* msg = 0, mjModel* m = 0, mjData* d = 0)
|
||||
{
|
||||
// deallocated everything
|
||||
if( d )
|
||||
mj_deleteData(d);
|
||||
if( m )
|
||||
mj_deleteModel(m);
|
||||
int finish(const char* msg = 0, mjModel* m = 0, mjData* d = 0) {
|
||||
// deallocated everything
|
||||
if (d) {
|
||||
mj_deleteData(d);
|
||||
}
|
||||
if (m) {
|
||||
mj_deleteModel(m);
|
||||
}
|
||||
|
||||
// print message
|
||||
if( msg )
|
||||
printf("%s\n", msg);
|
||||
// print message
|
||||
if (msg) {
|
||||
std::printf("%s\n", msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// return absolute difference if it is below 1, relative difference otherwise
|
||||
static mjtNum _compare(mjtNum val1, mjtNum val2)
|
||||
{
|
||||
mjtNum magnitude = mju_max(mju_abs(val1), mju_abs(val2));
|
||||
static mjtNum _compare(mjtNum val1, mjtNum val2) {
|
||||
mjtNum magnitude = mju_max(mju_abs(val1), mju_abs(val2));
|
||||
|
||||
if( magnitude>1.0 )
|
||||
return mju_abs(val1-val2) / magnitude;
|
||||
else
|
||||
return mju_abs(val1-val2);
|
||||
if (magnitude>1.0) {
|
||||
return mju_abs(val1-val2) / magnitude;
|
||||
} else {
|
||||
return mju_abs(val1-val2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// compare two models, return largest difference and field name
|
||||
mjtNum compareModel(const mjModel* m1, const mjModel* m2, char* field)
|
||||
{
|
||||
int r, c;
|
||||
mjtNum dif, maxdif = 0.0;
|
||||
mjtNum compareModel(const mjModel* m1, const mjModel* m2, char (&field)[kFieldSize]) {
|
||||
int r, c;
|
||||
mjtNum dif, maxdif = 0.0;
|
||||
|
||||
// define symbols corresponding to number of columns (needed in MJMODEL_POINTERS)
|
||||
int nq = m1->nq;
|
||||
int nv = m1->nv;
|
||||
int na = m1->na;
|
||||
int nmocap3 = 3*m1->nmocap;
|
||||
int nmocap4 = 4*m1->nmocap;
|
||||
int nuser_body = m1->nuser_body;
|
||||
int nuser_jnt = m1->nuser_jnt;
|
||||
int nuser_geom = m1->nuser_geom;
|
||||
int nuser_site = m1->nuser_site;
|
||||
int nuser_cam = m1->nuser_cam;
|
||||
int nuser_tendon = m1->nuser_tendon;
|
||||
int nuser_actuator = m1->nuser_actuator;
|
||||
int nuser_sensor = m1->nuser_sensor;
|
||||
// define symbols corresponding to number of columns (needed in MJMODEL_POINTERS)
|
||||
MJMODEL_POINTERS_PREAMBLE(m1);
|
||||
|
||||
// compare ints
|
||||
#define X(name) if(m1->name!=m2->name) {strcpy(field, #name); return 1.0;}
|
||||
// compare ints
|
||||
#define X(name) if(m1->name!=m2->name) {mju::strcpy_arr(field, #name); return 1.0;}
|
||||
|
||||
MJMODEL_INTS
|
||||
#undef X
|
||||
MJMODEL_INTS
|
||||
#undef X
|
||||
|
||||
// compare arrays
|
||||
#define X(type, name, nr, nc) \
|
||||
for( r=0; r<m1->nr; r++ ) \
|
||||
for( c=0; c<nc; c++ ) { \
|
||||
dif = _compare(m1->name[r*nc+c], m2->name[r*nc+c]); \
|
||||
if(dif>maxdif) {maxdif=dif; strcpy(field, #name);} }
|
||||
#define X(type, name, nr, nc) \
|
||||
for( r=0; r<m1->nr; r++ ) \
|
||||
for( c=0; c<nc; c++ ) { \
|
||||
dif = _compare(m1->name[r*nc+c], m2->name[r*nc+c]); \
|
||||
if(dif>maxdif) {maxdif=dif; mju::strcpy_arr(field, #name);} }
|
||||
|
||||
MJMODEL_POINTERS
|
||||
#undef X
|
||||
MJMODEL_POINTERS
|
||||
#undef X
|
||||
|
||||
// compare scalars in mjOption
|
||||
#define X(type, name) \
|
||||
dif = _compare(m1->opt.name, m2->opt.name); \
|
||||
if(dif>maxdif) {maxdif=dif; strcpy(field, #name);}
|
||||
// compare scalars in mjOption
|
||||
#define X(type, name) \
|
||||
dif = _compare(m1->opt.name, m2->opt.name); \
|
||||
if(dif>maxdif) {maxdif=dif; mju::strcpy_arr(field, #name);}
|
||||
|
||||
MJOPTION_SCALARS
|
||||
#undef X
|
||||
MJOPTION_SCALARS
|
||||
#undef X
|
||||
|
||||
// compare arrays in mjOption
|
||||
#define X(name, n) \
|
||||
for( c=0; c<n; c++ ) { \
|
||||
dif = _compare(m1->opt.name[c], m2->opt.name[c]); \
|
||||
if(dif>maxdif) {maxdif=dif; strcpy(field, #name);} }
|
||||
// compare arrays in mjOption
|
||||
#define X(name, n) \
|
||||
for( c=0; c<n; c++ ) { \
|
||||
dif = _compare(m1->opt.name[c], m2->opt.name[c]); \
|
||||
if(dif>maxdif) {maxdif=dif; mju::strcpy_arr(field, #name);} }
|
||||
|
||||
MJOPTION_VECTORS
|
||||
#undef X
|
||||
MJOPTION_VECTORS
|
||||
#undef X
|
||||
|
||||
// mjVisual and mjStatistics ignored for now
|
||||
// mjVisual and mjStatistics ignored for now
|
||||
|
||||
return maxdif;
|
||||
return maxdif;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// main function
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
// print help if arguments are missing
|
||||
if( argc<2 )
|
||||
return finish(helpstring);
|
||||
int main(int argc, const char** argv) {
|
||||
// print help if arguments are missing
|
||||
if (argc<2) {
|
||||
return finish(helpstring);
|
||||
}
|
||||
|
||||
// get filename, check file type
|
||||
std::string filename(argv[1]);
|
||||
if( filename.find(".xml")==std::string::npos )
|
||||
return finish("xml model file is required");
|
||||
// get filename, check file type
|
||||
std::string filename(argv[1]);
|
||||
if (filename.find(".xml")==std::string::npos) {
|
||||
return finish("xml model file is required");
|
||||
}
|
||||
|
||||
// load model
|
||||
char error[1000];
|
||||
mjModel* m = mj_loadXML(argv[1], 0, error, 1000);
|
||||
if( !m )
|
||||
return finish(error);
|
||||
// load model
|
||||
char error[1000];
|
||||
mjModel* m = mj_loadXML(argv[1], 0, error, 1000);
|
||||
if (!m) {
|
||||
return finish(error);
|
||||
}
|
||||
|
||||
// make data
|
||||
mjData* d = mj_makeData(m);
|
||||
if( !d )
|
||||
return finish("Could not allocate mjData", m);
|
||||
// make data
|
||||
mjData* d = mj_makeData(m);
|
||||
if (!d) {
|
||||
return finish("Could not allocate mjData", m);
|
||||
}
|
||||
|
||||
// prepare temp filename in the same directory as original (for asset loading)
|
||||
std::string tempfile;
|
||||
size_t lastpath = filename.find_last_of("/\\");
|
||||
if( lastpath==std::string::npos )
|
||||
tempfile = "_tempfile_.xml";
|
||||
else
|
||||
tempfile = filename.substr(0, lastpath+1) + "_tempfile_.xml";
|
||||
// prepare temp filename in the same directory as original (for asset loading)
|
||||
std::string tempfile;
|
||||
std::size_t lastpath = filename.find_last_of("/\\");
|
||||
if (lastpath==std::string::npos) {
|
||||
tempfile = "_tempfile_.xml";
|
||||
} else {
|
||||
tempfile = filename.substr(0, lastpath+1) + "_tempfile_.xml";
|
||||
}
|
||||
|
||||
// save
|
||||
if( !mj_saveLastXML(tempfile.c_str(), m, error, 1000) )
|
||||
return finish(error, m, d);
|
||||
// save
|
||||
if (!mj_saveLastXML(tempfile.c_str(), m, error, 1000)) {
|
||||
return finish(error, m, d);
|
||||
}
|
||||
|
||||
// load back
|
||||
mjModel* mtemp = mj_loadXML(tempfile.c_str(), 0, error, 100);
|
||||
if( !mtemp )
|
||||
return finish(error, m, d);
|
||||
// load back
|
||||
mjModel* mtemp = mj_loadXML(tempfile.c_str(), 0, error, 100);
|
||||
if (!mtemp) {
|
||||
return finish(error, m, d);
|
||||
}
|
||||
|
||||
// compare
|
||||
char field[500] = "";
|
||||
mjtNum result = compareModel(m, mtemp, field);
|
||||
printf("\nComparison of original and saved model\n");
|
||||
printf(" Max difference : %.3g\n", result);
|
||||
printf(" Field name : %s\n", field);
|
||||
// compare
|
||||
char field[kFieldSize] = "";
|
||||
mjtNum result = compareModel(m, mtemp, field);
|
||||
std::printf("\nComparison of original and saved model\n");
|
||||
std::printf(" Max difference : %.3g\n", result);
|
||||
std::printf(" Field name : %s\n", field);
|
||||
|
||||
// delete temp model and file
|
||||
mj_deleteModel(mtemp);
|
||||
remove(tempfile.c_str());
|
||||
// delete temp model and file
|
||||
mj_deleteModel(mtemp);
|
||||
remove(tempfile.c_str());
|
||||
|
||||
// finalize
|
||||
return finish();
|
||||
// finalize
|
||||
return finish();
|
||||
}
|
||||
|
||||
Executable → Regular
+205
-214
@@ -13,221 +13,216 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "uitools.h"
|
||||
#include "stdio.h"
|
||||
#include "string.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
//-------------------------------- Internal GLFW callbacks ------------------------------
|
||||
|
||||
// update state
|
||||
static void uiUpdateState(GLFWwindow* wnd)
|
||||
{
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
static void uiUpdateState(GLFWwindow* wnd) {
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
|
||||
// mouse buttons
|
||||
state->left = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_LEFT)==GLFW_PRESS);
|
||||
state->right = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_RIGHT)==GLFW_PRESS);
|
||||
state->middle = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_MIDDLE)==GLFW_PRESS);
|
||||
// mouse buttons
|
||||
state->left = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_LEFT)==GLFW_PRESS);
|
||||
state->right = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_RIGHT)==GLFW_PRESS);
|
||||
state->middle = (glfwGetMouseButton(wnd, GLFW_MOUSE_BUTTON_MIDDLE)==GLFW_PRESS);
|
||||
|
||||
// keyboard modifiers
|
||||
state->control = (glfwGetKey(wnd, GLFW_KEY_LEFT_CONTROL)==GLFW_PRESS ||
|
||||
glfwGetKey(wnd, GLFW_KEY_RIGHT_CONTROL)==GLFW_PRESS);
|
||||
state->shift = (glfwGetKey(wnd, GLFW_KEY_LEFT_SHIFT)==GLFW_PRESS ||
|
||||
glfwGetKey(wnd, GLFW_KEY_RIGHT_SHIFT)==GLFW_PRESS);
|
||||
state->alt = (glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS ||
|
||||
glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS);
|
||||
// keyboard modifiers
|
||||
state->control = (glfwGetKey(wnd, GLFW_KEY_LEFT_CONTROL)==GLFW_PRESS ||
|
||||
glfwGetKey(wnd, GLFW_KEY_RIGHT_CONTROL)==GLFW_PRESS);
|
||||
state->shift = (glfwGetKey(wnd, GLFW_KEY_LEFT_SHIFT)==GLFW_PRESS ||
|
||||
glfwGetKey(wnd, GLFW_KEY_RIGHT_SHIFT)==GLFW_PRESS);
|
||||
state->alt = (glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS ||
|
||||
glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS);
|
||||
|
||||
// swap left and right if Alt
|
||||
if( state->alt )
|
||||
{
|
||||
int tmp = state->left;
|
||||
state->left = state->right;
|
||||
state->right = tmp;
|
||||
}
|
||||
// swap left and right if Alt
|
||||
if (state->alt) {
|
||||
int tmp = state->left;
|
||||
state->left = state->right;
|
||||
state->right = tmp;
|
||||
}
|
||||
|
||||
// get mouse position, scale by buffer-to-window ratio
|
||||
double x, y;
|
||||
glfwGetCursorPos(wnd, &x, &y);
|
||||
x *= ptr->buffer2window;
|
||||
y *= ptr->buffer2window;
|
||||
// get mouse position, scale by buffer-to-window ratio
|
||||
double x, y;
|
||||
glfwGetCursorPos(wnd, &x, &y);
|
||||
x *= ptr->buffer2window;
|
||||
y *= ptr->buffer2window;
|
||||
|
||||
// invert y to match OpenGL convention
|
||||
y = state->rect[0].height - y;
|
||||
// invert y to match OpenGL convention
|
||||
y = state->rect[0].height - y;
|
||||
|
||||
// save
|
||||
state->dx = x - state->x;
|
||||
state->dy = y - state->y;
|
||||
state->x = x;
|
||||
state->y = y;
|
||||
// save
|
||||
state->dx = x - state->x;
|
||||
state->dy = y - state->y;
|
||||
state->x = x;
|
||||
state->y = y;
|
||||
|
||||
// find mouse rectangle
|
||||
state->mouserect = mjr_findRect(mju_round(x), mju_round(y),
|
||||
state->nrect-1, state->rect+1) + 1;
|
||||
// find mouse rectangle
|
||||
state->mouserect = mjr_findRect(mju_round(x), mju_round(y), state->nrect-1, state->rect+1) + 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// keyboard
|
||||
static void uiKeyboard(GLFWwindow* wnd, int key, int scancode, int act, int mods)
|
||||
{
|
||||
// release: nothing to do
|
||||
if( act==GLFW_RELEASE )
|
||||
return;
|
||||
static void uiKeyboard(GLFWwindow* wnd, int key, int scancode, int act, int mods) {
|
||||
// release: nothing to do
|
||||
if (act==GLFW_RELEASE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
|
||||
// update state
|
||||
uiUpdateState(wnd);
|
||||
// update state
|
||||
uiUpdateState(wnd);
|
||||
|
||||
// set key info
|
||||
state->type = mjEVENT_KEY;
|
||||
state->key = key;
|
||||
state->keytime = glfwGetTime();
|
||||
// set key info
|
||||
state->type = mjEVENT_KEY;
|
||||
state->key = key;
|
||||
state->keytime = glfwGetTime();
|
||||
|
||||
// application-specific processing
|
||||
ptr->uiEvent(state);
|
||||
// application-specific processing
|
||||
ptr->uiEvent(state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// mouse button
|
||||
static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods)
|
||||
{
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
static void uiMouseButton(GLFWwindow* wnd, int button, int act, int mods) {
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
|
||||
// update state
|
||||
uiUpdateState(wnd);
|
||||
// update state
|
||||
uiUpdateState(wnd);
|
||||
|
||||
// translate button
|
||||
if( button==GLFW_MOUSE_BUTTON_LEFT )
|
||||
button = mjBUTTON_LEFT;
|
||||
else if( button==GLFW_MOUSE_BUTTON_RIGHT )
|
||||
button = mjBUTTON_RIGHT;
|
||||
else
|
||||
button = mjBUTTON_MIDDLE;
|
||||
// translate button
|
||||
if (button==GLFW_MOUSE_BUTTON_LEFT) {
|
||||
button = mjBUTTON_LEFT;
|
||||
} else if (button==GLFW_MOUSE_BUTTON_RIGHT) {
|
||||
button = mjBUTTON_RIGHT;
|
||||
} else {
|
||||
button = mjBUTTON_MIDDLE;
|
||||
}
|
||||
|
||||
// swap left and right if Alt
|
||||
if( glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS ||
|
||||
glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS )
|
||||
{
|
||||
if( button==mjBUTTON_LEFT )
|
||||
button = mjBUTTON_RIGHT;
|
||||
else if( button==mjBUTTON_RIGHT )
|
||||
button = mjBUTTON_LEFT;
|
||||
// swap left and right if Alt
|
||||
if (glfwGetKey(wnd, GLFW_KEY_LEFT_ALT)==GLFW_PRESS ||
|
||||
glfwGetKey(wnd, GLFW_KEY_RIGHT_ALT)==GLFW_PRESS) {
|
||||
if (button==mjBUTTON_LEFT) {
|
||||
button = mjBUTTON_RIGHT;
|
||||
} else if (button==mjBUTTON_RIGHT) {
|
||||
button = mjBUTTON_LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
// press
|
||||
if (act==GLFW_PRESS) {
|
||||
// detect doubleclick: 250 ms
|
||||
if (button==state->button && glfwGetTime()-state->buttontime<0.25) {
|
||||
state->doubleclick = 1;
|
||||
} else {
|
||||
state->doubleclick = 0;
|
||||
}
|
||||
|
||||
// press
|
||||
if( act==GLFW_PRESS )
|
||||
{
|
||||
// detect doubleclick: 250 ms
|
||||
if( button==state->button && glfwGetTime()-state->buttontime<0.25 )
|
||||
state->doubleclick = 1;
|
||||
else
|
||||
state->doubleclick = 0;
|
||||
// set info
|
||||
state->type = mjEVENT_PRESS;
|
||||
state->button = button;
|
||||
state->buttontime = glfwGetTime();
|
||||
|
||||
// set info
|
||||
state->type = mjEVENT_PRESS;
|
||||
state->button = button;
|
||||
state->buttontime = glfwGetTime();
|
||||
|
||||
// start dragging
|
||||
if( state->mouserect )
|
||||
{
|
||||
state->dragbutton = state->button;
|
||||
state->dragrect = state->mouserect;
|
||||
}
|
||||
// start dragging
|
||||
if (state->mouserect) {
|
||||
state->dragbutton = state->button;
|
||||
state->dragrect = state->mouserect;
|
||||
}
|
||||
}
|
||||
|
||||
// release
|
||||
else
|
||||
state->type = mjEVENT_RELEASE;
|
||||
// release
|
||||
else {
|
||||
state->type = mjEVENT_RELEASE;
|
||||
}
|
||||
|
||||
// application-specific processing
|
||||
ptr->uiEvent(state);
|
||||
// application-specific processing
|
||||
ptr->uiEvent(state);
|
||||
|
||||
// stop dragging after application processing
|
||||
if( state->type==mjEVENT_RELEASE )
|
||||
{
|
||||
state->dragrect = 0;
|
||||
state->dragbutton = 0;
|
||||
}
|
||||
// stop dragging after application processing
|
||||
if (state->type==mjEVENT_RELEASE) {
|
||||
state->dragrect = 0;
|
||||
state->dragbutton = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// mouse move
|
||||
static void uiMouseMove(GLFWwindow* wnd, double xpos, double ypos)
|
||||
{
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
static void uiMouseMove(GLFWwindow* wnd, double xpos, double ypos) {
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
|
||||
// no buttons down: nothing to do
|
||||
if( !state->left && !state->right && !state->middle )
|
||||
return;
|
||||
// no buttons down: nothing to do
|
||||
if (!state->left && !state->right && !state->middle) {
|
||||
return;
|
||||
}
|
||||
|
||||
// update state
|
||||
uiUpdateState(wnd);
|
||||
// update state
|
||||
uiUpdateState(wnd);
|
||||
|
||||
// set move info
|
||||
state->type = mjEVENT_MOVE;
|
||||
// set move info
|
||||
state->type = mjEVENT_MOVE;
|
||||
|
||||
// application-specific processing
|
||||
ptr->uiEvent(state);
|
||||
// application-specific processing
|
||||
ptr->uiEvent(state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// scroll
|
||||
static void uiScroll(GLFWwindow* wnd, double xoffset, double yoffset)
|
||||
{
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
static void uiScroll(GLFWwindow* wnd, double xoffset, double yoffset) {
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
|
||||
// update state
|
||||
uiUpdateState(wnd);
|
||||
// update state
|
||||
uiUpdateState(wnd);
|
||||
|
||||
// set scroll info, scale by buffer-to-window ratio
|
||||
state->type = mjEVENT_SCROLL;
|
||||
state->sx = xoffset * ptr->buffer2window;
|
||||
state->sy = yoffset * ptr->buffer2window;
|
||||
// set scroll info, scale by buffer-to-window ratio
|
||||
state->type = mjEVENT_SCROLL;
|
||||
state->sx = xoffset * ptr->buffer2window;
|
||||
state->sy = yoffset * ptr->buffer2window;
|
||||
|
||||
// application-specific processing
|
||||
ptr->uiEvent(state);
|
||||
// application-specific processing
|
||||
ptr->uiEvent(state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// resize
|
||||
static void uiResize(GLFWwindow* wnd, int width, int height)
|
||||
{
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
static void uiResize(GLFWwindow* wnd, int width, int height) {
|
||||
// extract data from user pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*)glfwGetWindowUserPointer(wnd);
|
||||
mjuiState* state = ptr->state;
|
||||
|
||||
// set layout
|
||||
ptr->uiLayout(state);
|
||||
// set layout
|
||||
ptr->uiLayout(state);
|
||||
|
||||
// update state
|
||||
uiUpdateState(wnd);
|
||||
// update state
|
||||
uiUpdateState(wnd);
|
||||
|
||||
// set resize info
|
||||
state->type = mjEVENT_RESIZE;
|
||||
// set resize info
|
||||
state->type = mjEVENT_RESIZE;
|
||||
|
||||
// stop dragging
|
||||
state->dragbutton = 0;
|
||||
state->dragrect = 0;
|
||||
// stop dragging
|
||||
state->dragbutton = 0;
|
||||
state->dragrect = 0;
|
||||
|
||||
// application-specific processing (unless called with 0,0 from uiModify)
|
||||
if( width && height )
|
||||
ptr->uiEvent(state);
|
||||
// application-specific processing (unless called with 0,0 from uiModify)
|
||||
if (width && height) {
|
||||
ptr->uiEvent(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -235,88 +230,84 @@ static void uiResize(GLFWwindow* wnd, int width, int height)
|
||||
//----------------------------------- Public API ----------------------------------------
|
||||
|
||||
// Compute suitable font scale.
|
||||
int uiFontScale(GLFWwindow* wnd)
|
||||
{
|
||||
// compute framebuffer-to-window ratio
|
||||
int width_win, width_buf, height;
|
||||
glfwGetWindowSize(wnd, &width_win, &height);
|
||||
glfwGetFramebufferSize(wnd, &width_buf, &height);
|
||||
double b2w = (double)width_buf / (double)width_win;
|
||||
int uiFontScale(GLFWwindow* wnd) {
|
||||
// compute framebuffer-to-window ratio
|
||||
int width_win, width_buf, height;
|
||||
glfwGetWindowSize(wnd, &width_win, &height);
|
||||
glfwGetFramebufferSize(wnd, &width_buf, &height);
|
||||
double b2w = (double)width_buf / (double)width_win;
|
||||
|
||||
// compute PPI
|
||||
int width_MM, height_MM;
|
||||
glfwGetMonitorPhysicalSize(glfwGetPrimaryMonitor(), &width_MM, &height_MM);
|
||||
int width_vmode = glfwGetVideoMode(glfwGetPrimaryMonitor())->width;
|
||||
double PPI = 25.4 * b2w * (double)width_vmode / (double)width_MM;
|
||||
// compute PPI
|
||||
int width_MM, height_MM;
|
||||
glfwGetMonitorPhysicalSize(glfwGetPrimaryMonitor(), &width_MM, &height_MM);
|
||||
int width_vmode = glfwGetVideoMode(glfwGetPrimaryMonitor())->width;
|
||||
double PPI = 25.4 * b2w * (double)width_vmode / (double)width_MM;
|
||||
|
||||
// estimate font scaling, guard against unrealistic PPI
|
||||
int fs;
|
||||
if( width_buf>width_win )
|
||||
fs = mju_round(b2w * 100);
|
||||
else if( PPI>50 && PPI<350 )
|
||||
fs = mju_round(PPI);
|
||||
else
|
||||
fs = 150;
|
||||
fs = mju_round(fs * 0.02) * 50;
|
||||
fs = mjMIN(300, mjMAX(100, fs));
|
||||
// estimate font scaling, guard against unrealistic PPI
|
||||
int fs;
|
||||
if (width_buf>width_win) {
|
||||
fs = mju_round(b2w * 100);
|
||||
} else if (PPI>50 && PPI<350) {
|
||||
fs = mju_round(PPI);
|
||||
} else {
|
||||
fs = 150;
|
||||
}
|
||||
fs = mju_round(fs * 0.02) * 50;
|
||||
fs = mjMIN(300, mjMAX(100, fs));
|
||||
|
||||
return fs;
|
||||
return fs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Set internal and user-supplied UI callbacks in GLFW window.
|
||||
void uiSetCallback(GLFWwindow* wnd, mjuiState* state,
|
||||
uiEventFn uiEvent, uiLayoutFn uiLayout)
|
||||
{
|
||||
// make container with user-supplied objects and set window pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*) mju_malloc(sizeof(uiUserPointer));
|
||||
ptr->state = state;
|
||||
ptr->uiEvent = uiEvent;
|
||||
ptr->uiLayout = uiLayout;
|
||||
glfwSetWindowUserPointer(wnd, ptr);
|
||||
uiEventFn uiEvent, uiLayoutFn uiLayout) {
|
||||
// make container with user-supplied objects and set window pointer
|
||||
uiUserPointer* ptr = (uiUserPointer*) mju_malloc(sizeof(uiUserPointer));
|
||||
ptr->state = state;
|
||||
ptr->uiEvent = uiEvent;
|
||||
ptr->uiLayout = uiLayout;
|
||||
glfwSetWindowUserPointer(wnd, ptr);
|
||||
|
||||
// compute framebuffer-to-window pixel ratio
|
||||
int width_win, width_buf, height;
|
||||
glfwGetWindowSize(wnd, &width_win, &height);
|
||||
glfwGetFramebufferSize(wnd, &width_buf, &height);
|
||||
ptr->buffer2window = (double)width_buf / (double)width_win;
|
||||
// compute framebuffer-to-window pixel ratio
|
||||
int width_win, width_buf, height;
|
||||
glfwGetWindowSize(wnd, &width_win, &height);
|
||||
glfwGetFramebufferSize(wnd, &width_buf, &height);
|
||||
ptr->buffer2window = (double)width_buf / (double)width_win;
|
||||
|
||||
// set internal callbacks
|
||||
glfwSetKeyCallback(wnd, uiKeyboard);
|
||||
glfwSetCursorPosCallback(wnd, uiMouseMove);
|
||||
glfwSetMouseButtonCallback(wnd, uiMouseButton);
|
||||
glfwSetScrollCallback(wnd, uiScroll);
|
||||
glfwSetWindowSizeCallback(wnd, uiResize);
|
||||
// set internal callbacks
|
||||
glfwSetKeyCallback(wnd, uiKeyboard);
|
||||
glfwSetCursorPosCallback(wnd, uiMouseMove);
|
||||
glfwSetMouseButtonCallback(wnd, uiMouseButton);
|
||||
glfwSetScrollCallback(wnd, uiScroll);
|
||||
glfwSetWindowSizeCallback(wnd, uiResize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Clear UI callbacks in GLFW window.
|
||||
void uiClearCallback(GLFWwindow* wnd)
|
||||
{
|
||||
// clear container
|
||||
if( glfwGetWindowUserPointer(wnd) )
|
||||
{
|
||||
mju_free(glfwGetWindowUserPointer(wnd));
|
||||
glfwSetWindowUserPointer(wnd, NULL);
|
||||
}
|
||||
void uiClearCallback(GLFWwindow* wnd) {
|
||||
// clear container
|
||||
if (glfwGetWindowUserPointer(wnd)) {
|
||||
mju_free(glfwGetWindowUserPointer(wnd));
|
||||
glfwSetWindowUserPointer(wnd, NULL);
|
||||
}
|
||||
|
||||
// clear internal callbacks
|
||||
glfwSetKeyCallback(wnd, NULL);
|
||||
glfwSetCursorPosCallback(wnd, NULL);
|
||||
glfwSetMouseButtonCallback(wnd, NULL);
|
||||
glfwSetScrollCallback(wnd, NULL);
|
||||
glfwSetWindowSizeCallback(wnd, NULL);
|
||||
// clear internal callbacks
|
||||
glfwSetKeyCallback(wnd, NULL);
|
||||
glfwSetCursorPosCallback(wnd, NULL);
|
||||
glfwSetMouseButtonCallback(wnd, NULL);
|
||||
glfwSetScrollCallback(wnd, NULL);
|
||||
glfwSetWindowSizeCallback(wnd, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Modify UI structure.
|
||||
void uiModify(GLFWwindow* wnd, mjUI* ui, mjuiState* state, mjrContext* con)
|
||||
{
|
||||
mjui_resize(ui, con);
|
||||
mjr_addAux(ui->auxid, ui->width, ui->maxheight, ui->spacing.samples, con);
|
||||
uiResize(wnd, 0, 0);
|
||||
mjui_update(-1, -1, ui, state, con);
|
||||
void uiModify(GLFWwindow* wnd, mjUI* ui, mjuiState* state, mjrContext* con) {
|
||||
mjui_resize(ui, con);
|
||||
mjr_addAux(ui->auxid, ui->width, ui->maxheight, ui->spacing.samples, con);
|
||||
uiResize(wnd, 0, 0);
|
||||
mjui_update(-1, -1, ui, state, con);
|
||||
}
|
||||
|
||||
Executable → Regular
+7
-10
@@ -16,14 +16,12 @@
|
||||
#define MUJOCO_UITOOLS_H_
|
||||
|
||||
|
||||
#include "GLFW/glfw3.h"
|
||||
#include "mujoco.h"
|
||||
#include "glfw3.h"
|
||||
|
||||
|
||||
// this is a C-API
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
@@ -32,12 +30,11 @@ typedef void (*uiEventFn)(mjuiState* state);
|
||||
typedef void (*uiLayoutFn)(mjuiState* state);
|
||||
|
||||
// Container for GLFW window pointer.
|
||||
struct _uiUserPointer
|
||||
{
|
||||
mjuiState* state;
|
||||
uiEventFn uiEvent;
|
||||
uiLayoutFn uiLayout;
|
||||
double buffer2window;
|
||||
struct _uiUserPointer {
|
||||
mjuiState* state;
|
||||
uiEventFn uiEvent;
|
||||
uiLayoutFn uiLayout;
|
||||
double buffer2window;
|
||||
};
|
||||
typedef struct _uiUserPointer uiUserPointer;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user