sdl_runtime_compiler git · main
SDL3 game for running and compiling code at runtime
C++ 72.3% C 26.2%git clone https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler.gitwget https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler/archive/sdl_runtime_compiler.tar.gzsrc/systems/wall_connection.cpp raw
#include <SDL3/SDL.h>
#include "systems/wall_connection.hpp"
#include "text/handler.hpp"
#include "utils/math.hpp"
#include "entities/camera.hpp"
#include "entities/world.hpp"
#include "rendering/gl_renderer.hpp"
#include "systems/program_system.hpp"
#include "utils/config.hpp"
#include "utils/light.hpp"
#include "wall/wall.hpp"
namespace
Nimbin
{
namespace
Wall
{
void
Connection::begin_Program(const Vec3& pult_pos)
{
_target = Target::Program;
_pult_pos = pult_pos;
_editor = nullptr;
_prog_input.reset();
_prog_last.clear();
}
void
Connection::begin_Editor(const Vec3& pult_pos, const Text::Handler& editor)
{
_target = Target::Editor;
_pult_pos = pult_pos;
_editor = &editor;
_cmd_last.clear();
_cmdline.reset();
}
void
Connection::end()
{
_target = Target::None;
_editor = nullptr;
_prog_input.reset(); _prog_last.clear();
_cmdline. reset(); _cmd_last. clear();
_blink = 0.0;
}
void
Connection::set_ProgramBuffer(const std::string& buffer)
{
if (buffer == _prog_last) return;
rebuild_ProgramPreview(buffer);
_prog_last = buffer;
}
void
Connection::clear_ProgramPreview()
{
_prog_input.reset();
_prog_last. clear();
}
void
Connection::rebuild_ProgramPreview(const std::string& buffer)
{
Wall::Transform tf = Wall::get_Transform(Wall::Role::Program, World::get_BoxScale());
Vec3 pos = tf.position;
pos.y = ProgramSystem::start_y;
_prog_input = Text::make_Token("> " + buffer, Text::Token::depth_wall, 0.003f, pos, Config::Color::white);
_prog_input->yaw = tf.yaw;
}
void
Connection::rebuild_Cmdline(const std::string& text)
{
_cmdline.reset();
if (text.empty()) return;
_cmdline = Text::make_Token(text, Text::Token::depth_wall, 0.0016f, Vec3{0,0,0}, Config::Color::white);
}
void
Connection::rebuild_ShowCmd(const std::string& text)
{
_showcmd.reset();
if (text.empty()) return;
_showcmd = Text::make_Token(text, Text::Token::depth_wall, 0.0016f, Vec3{0,0,0}, Config::Color::white);
}
void
Connection::update(double dt, const Camera& cam, float aspect, float fov_y)
{
if (!is_Active()) return;
_blink += dt;
if (_target == Target::Editor && _editor) {
std::string ml = _editor->modeline();
if (ml != _cmd_last) { rebuild_Cmdline(ml); _cmd_last = ml; }
// Bottom-right "showcmd": the pending operator/count (e.g. "2d", "c").
std::string sc = _editor->vim_ShowCmd();
if (sc != _showcmd_last) { rebuild_ShowCmd(sc); _showcmd_last = sc; }
// Tell the scrolloff how many rows actually fit, so the first/last line
// stays in view when zoomed in. Distance to the editor wall is along x.
const double dist_wall = SDL_fabs(cam.pos.x - _editor->wall_X());
_editor->set_ViewHalfHeight(dist_wall * static_cast<double>(SDL_tanf(fov_y * 0.5f)));
}
const Vec3 up = Math::cross(cam.right, cam.fwd);
const double dist = 2.2;
const double half_h = dist * static_cast<double>(SDL_tanf(fov_y * 0.5f));
const double half_w = half_h * static_cast<double>(aspect);
const double fx = 0.82;
const double fy = 0.78;
const double left = half_w * fx;
const double down = half_h * fy;
Vec3 target {
cam.pos.x + cam.fwd.x * dist - cam.right.x * left - up.x * down,
cam.pos.y + cam.fwd.y * dist - cam.right.y * left - up.y * down,
cam.pos.z + cam.fwd.z * dist - cam.right.z * left - up.z * down,
};
if (!_cmd_pos_init) { _cmd_pos = target; _cmd_pos_init = true; }
const double k = 1.0 - SDL_exp(-6.0 * dt);
_cmd_pos.x += (target.x - _cmd_pos.x) * k;
_cmd_pos.y += (target.y - _cmd_pos.y) * k;
_cmd_pos.z += (target.z - _cmd_pos.z) * k;
// Lower-RIGHT frustum corner for the showcmd (mirror of the cmdline corner).
const double right_off = half_w * 0.72; // a touch inboard so short text stays on-screen
Vec3 sc_target {
cam.pos.x + cam.fwd.x * dist + cam.right.x * right_off - up.x * down,
cam.pos.y + cam.fwd.y * dist + cam.right.y * right_off - up.y * down,
cam.pos.z + cam.fwd.z * dist + cam.right.z * right_off - up.z * down,
};
if (!_showcmd_pos_init) { _showcmd_pos = sc_target; _showcmd_pos_init = true; }
_showcmd_pos.x += (sc_target.x - _showcmd_pos.x) * k;
_showcmd_pos.y += (sc_target.y - _showcmd_pos.y) * k;
_showcmd_pos.z += (sc_target.z - _showcmd_pos.z) * k;
_cmd_yaw = static_cast<float>(SDL_atan2(-cam.fwd.x, -cam.fwd.z));
}
void
Connection::render(GlRenderer& gl, const Camera& cam, const Light& light) const
{
if (_target == Target::Program) {
if (_prog_input) _prog_input->draw(gl, cam, light);
draw_ProgramCursor(gl, cam, light);
draw_Electric (gl, cam);
}
else if (_target == Target::Editor) {
if (_cmdline) {
const double hw = static_cast<double>(_cmdline->half_extent);
_cmdline->pos = Vec3{
_cmd_pos.x + cam.right.x * hw,
_cmd_pos.y + cam.right.y * hw,
_cmd_pos.z + cam.right.z * hw,
};
_cmdline->yaw = _cmd_yaw;
_cmdline->pitch = 0.0f;
gl.clear_Depth();
_cmdline->draw(gl, cam, light);
}
draw_EditorCursor(gl, cam, light);
draw_Electric (gl, cam);
// Bottom-right showcmd: right-align by anchoring its right edge at the
// corner (offset left by its half-width), mirroring the cmdline's left
// anchor on the opposite side.
if (_showcmd) {
const double hw = static_cast<double>(_showcmd->half_extent);
_showcmd->pos = Vec3{
_showcmd_pos.x - cam.right.x * hw,
_showcmd_pos.y - cam.right.y * hw,
_showcmd_pos.z - cam.right.z * hw,
};
_showcmd->yaw = _cmd_yaw;
_showcmd->pitch = 0.0f;
gl.clear_Depth();
_showcmd->draw(gl, cam, light);
}
}
}
void
Connection::draw_ProgramCursor(GlRenderer& gl, const Camera& cam, const Light& light) const
{
if (!_prog_input) return;
if (SDL_fmod(_blink, 0.8) > 0.4) return;
Vec3 c = _prog_input->pos;
c.z = _prog_input->pos.z - (static_cast<double>(_prog_input->half_extent) + 0.15);
Mat4 view = buildViewMat4 (cam);
Mat4 get_Model = buildModelMat4(c, _prog_input->yaw);
Mat4 mvp = Mat4::mul (gl.get_Projection(), Mat4::mul(view, get_Model));
gl.set_Wireframe(false);
gl.set_MVP (get_Model, mvp);
gl.set_NormalMat(get_Model);
gl.set_Light (light);
gl.set_Color (0.6f, 1.0f, 1.0f);
gl.set_Emissive (0.6f, 1.0f, 1.0f, 0.9f);
gl.draw_Box (0.04f, 0.22f, 0.02f);
gl.set_Emissive (0.0f, 0.0f, 0.0f, 0.0f);
}
void
Connection::draw_EditorCursor(GlRenderer& gl, const Camera& cam, const Light& light) const
{
if (!_editor) return;
if (SDL_fmod(_blink, 0.8) > 0.4) return;
const Text::Handler::EditingMetrics m = _editor->editing_Metrics();
if (m.char_w <= 0.0) return;
const int row = _editor->cursor_Row();
const int col = _editor->cursor_Col();
const double row_y = _editor->row_Y(row);
const double cz = m.left_z - (static_cast<double>(col) + 0.5) * m.char_w;
Wall::Transform tf = Wall::get_Transform(Wall::Role::Editor, World::get_BoxScale());
Vec3 c { tf.position.x, row_y - (m.he_y + 0.06), cz };
const Vec3 ref { tf.position.x, row_y, cz };
const float pitch = Wall::tilt_TwardEye(ref, cam.pos);
Mat4 view = buildViewMat4 (cam);
Mat4 get_Model = buildModelMat4(c, tf.yaw, pitch);
Mat4 mvp = Mat4::mul (gl.get_Projection(), Mat4::mul(view, get_Model));
gl.set_Wireframe(false);
gl.set_MVP (get_Model, mvp);
gl.set_NormalMat(get_Model);
gl.set_Light (light);
gl.set_Color (0.6f, 1.0f, 1.0f);
gl.set_Emissive (0.6f, 1.0f, 1.0f, 0.9f);
gl.draw_Box (static_cast<float>(m.char_w * 0.45), 0.015f, 0.03f);
gl.set_Emissive (0.0f, 0.0f, 0.0f, 0.0f);
}
void
Connection::draw_Electric(GlRenderer& gl, const Camera& cam) const
{
const Vec3 a = cam.pos;
const Vec3 b = _pult_pos;
constexpr int SEG = 16;
DynArray<float> pts;
pts.reserve(SEG * 6);
Vec3 prev = a;
for (int i = 1; i <= SEG; i++) {
const double t = static_cast<double>(i) / SEG;
Vec3 p { a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t };
if (i < SEG) {
const double j = 0.18;
p.x += ((rand() % 1000) / 1000.0 - 0.5) * j;
p.y += ((rand() % 1000) / 1000.0 - 0.5) * j;
p.z += ((rand() % 1000) / 1000.0 - 0.5) * j;
}
pts.push_back((float)prev.x); pts.push_back((float)prev.y); pts.push_back((float)prev.z);
pts.push_back((float)p.x); pts.push_back((float)p.y); pts.push_back((float)p.z);
prev = p;
}
Mat4 ident = Mat4::identity();
Mat4 mvp = Mat4::mul(gl.get_Projection(), buildViewMat4(cam));
gl.set_MVP (ident, mvp);
gl.set_NormalMat(ident);
gl.set_Wireframe(true);
gl.set_Color (0.5f, 0.9f, 1.0f);
gl.draw_Lines (pts.data(), static_cast<int>(pts.size()) / 3);
gl.set_Wireframe(false);
}
}
} // namespace Nimbin