From 767e52fb5b259862c76fbeffaecea15b2edcc075 Mon Sep 17 00:00:00 2001 From: Haroon Qureshi Date: Tue, 17 Feb 2026 02:53:02 -0800 Subject: [PATCH] Fire projectiles when hitting "Ctrl+Shift+Enter". PiperOrigin-RevId: 871215312 Change-Id: Ie22699d5446a49ea6997594b2800cf72840554c1 --- src/experimental/studio/app.cc | 59 +++++++++++++++++++++++++++++++++- src/experimental/studio/app.h | 3 ++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/experimental/studio/app.cc b/src/experimental/studio/app.cc index 8dbfdad8..ec1f4a1f 100644 --- a/src/experimental/studio/app.cc +++ b/src/experimental/studio/app.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -106,7 +107,8 @@ static constexpr std::array kPercentRealTime = { }; // clang-format on -App::App(Config config) : ini_path_(std::move(config.ini_path)) { +App::App(Config config) + : rng_(std::random_device()()), ini_path_(std::move(config.ini_path)) { platform::Window::Config window_config; window_config.renderer_backend = platform::Renderer::GetBackend(); window_config.offscreen_mode = config.offscreen_mode; @@ -702,6 +704,61 @@ void App::HandleKeyboardEvents() { ToggleFlag(vis_options_.geomgroup[4]); } else if (ImGui_IsChordJustPressed(ImGuiKey_5)) { ToggleFlag(vis_options_.geomgroup[5]); + } else if (ImGui_IsChordJustPressed(ImGuiKey_Enter | ImGuiMode_CtrlShift)) { + if (has_spec()) { + spec_op_ = [this]() { + mjsBody* world = mjs_findBody(spec(), "world"); + if (!world) return; + mjsBody* body = mjs_addBody(world, nullptr); + if (!body) return; + mjsJoint* joint = mjs_addJoint(body, nullptr); + if (!joint) return; + mjsGeom* geom = mjs_addGeom(body, nullptr); + if (!geom) return; + + // Set body position slightly in front of the camera. + mjtNum pos[3]; + mjtNum dir[3]; + mjtNum up[3]; + mjv_cameraFrame(pos, dir, up, nullptr, data(), &camera_); + + static int counter = 0; + std::string name = "projectile" + std::to_string(counter++); + mjs_setName(body->element, name.c_str()); + + body->mass = 10.0; + body->pos[0] = pos[0] + dir[0] * 0.2; + body->pos[1] = pos[1] + dir[1] * 0.2; + body->pos[2] = pos[2] + dir[2] * 0.2; + geom->type = mjGEOM_BOX; + geom->size[0] = 0.13365; + geom->size[1] = 0.13365; + geom->size[2] = 0.13365; + geom->rgba[0] = std::uniform_real_distribution(0.3f, 1.0f)(rng_); + geom->rgba[1] = std::uniform_real_distribution(0.3f, 1.0f)(rng_); + geom->rgba[2] = std::uniform_real_distribution(0.3f, 1.0f)(rng_); + geom->rgba[3] = 1.0; + + joint->type = mjJNT_FREE; + + Recompile(); + + // Give the newly added body a velocity in the direction of the camera. + int bodyid = mj_name2id(model(), mjOBJ_BODY, name.c_str()); + if (bodyid >= 0) { + int jntid = model()->body_jntadr[bodyid]; + if (jntid >= 0 && model()->jnt_type[jntid] == mjJNT_FREE) { + int qveladr = model()->jnt_dofadr[jntid]; + if (qveladr >= 0) { + mjtNum speed = 10.0; // Magnitude of the initial velocity. + data()->qvel[qveladr + 0] = dir[0] * speed + up[0]; + data()->qvel[qveladr + 1] = dir[1] * speed + up[1]; + data()->qvel[qveladr + 2] = dir[2] * speed + up[2]; + } + } + } + }; + } } else if (has_model()) { if (ImGui_IsChordJustPressed(ImGuiKey_Escape)) { ui_.camera_idx = diff --git a/src/experimental/studio/app.h b/src/experimental/studio/app.h index c6d6d065..adcfab87 100644 --- a/src/experimental/studio/app.h +++ b/src/experimental/studio/app.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -223,6 +224,8 @@ class App { bool has_model() const { return model_holder_ && model_holder_->model(); } bool has_data() const { return model_holder_ && model_holder_->data(); } + std::mt19937 rng_; + std::string ini_path_; std::string model_name_; // Used if model_kind_ is kModelFromBuffer. std::string model_path_;