Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / src/systems/wall_connection.cpp

sdl_runtime_compiler git · main

SDL3 game for running and compiling code at runtime

sdl3 c++ compiler dlopen cmake · first commit 2026-04-19 · last commit 2026-07-03 (3 months ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/sdl-runtime-compiler

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.gz
src/systems/wall_connection.cpp 9.2 KB · 312 lines 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