192 lines
5.5 KiB
C++
192 lines
5.5 KiB
C++
|
|
// Copyright 2021 DeepMind Technologies Limited
|
||
|
|
//
|
||
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
|
// you may not use this file except in compliance with the License.
|
||
|
|
// You may obtain a copy of the License at
|
||
|
|
//
|
||
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
//
|
||
|
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
|
// See the License for the specific language governing permissions and
|
||
|
|
// limitations under the License.
|
||
|
|
|
||
|
|
#include <cstdio>
|
||
|
|
#include <cstdlib>
|
||
|
|
#include <cstring>
|
||
|
|
|
||
|
|
#include <GLFW/glfw3.h>
|
||
|
|
#include <mujoco/mujoco.h>
|
||
|
|
|
||
|
|
// MuJoCo data structures
|
||
|
|
mjModel* m = NULL; // MuJoCo model
|
||
|
|
mjData* d = NULL; // MuJoCo data
|
||
|
|
mjvCamera cam; // abstract camera
|
||
|
|
mjvOption opt; // visualization options
|
||
|
|
mjvScene scn; // abstract scene
|
||
|
|
mjrContext con; // custom GPU context
|
||
|
|
|
||
|
|
// mouse interaction
|
||
|
|
bool button_left = false;
|
||
|
|
bool button_middle = false;
|
||
|
|
bool button_right = false;
|
||
|
|
double lastx = 0;
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// 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);
|
||
|
|
|
||
|
|
// 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;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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 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;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// main function
|
||
|
|
int main(int argc, const char** argv) {
|
||
|
|
// check command-line arguments
|
||
|
|
if (argc!=2) {
|
||
|
|
std::printf(" USAGE: basic modelfile\n");
|
||
|
|
return EXIT_FAILURE;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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("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);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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 EXIT_SUCCESS;
|
||
|
|
}
|