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/pick_system.cpp raw
#include <SDL3/SDL.h>
#include "pick_system.hpp"
#include "entities/world.hpp"
namespace
Nimbin
{
namespace
PickSystem
{
static constexpr double MAX_REACH_XZ = 3.8;
static constexpr double MAX_ANGLE = 0.25;
static double
angScore(const GameObject& obj, const Vec3& eye, const Vec3& fwd,
double max_reach = MAX_REACH_XZ)
{
double he;
if (obj.type == ObjectType::Text)
he = static_cast<double>(static_cast<const Text::Token::Data&>(obj).half_extent);
else
he = obj.shape.half_extents.x;
double cy_ = SDL_cos(obj.yaw);
double sy_ = SDL_sin(obj.yaw);
Vec3 seg_a { obj.pos.x - cy_ * he, obj.pos.y, obj.pos.z + sy_ * he };
Vec3 seg_b { obj.pos.x + cy_ * he, obj.pos.y, obj.pos.z - sy_ * he };
Vec3 seg_dir { seg_b.x - seg_a.x, seg_b.y - seg_a.y, seg_b.z - seg_a.z };
Vec3 w { seg_a.x - eye.x, seg_a.y - eye.y, seg_a.z - eye.z };
double a = Math::dot(fwd, fwd);
double b = Math::dot(fwd, seg_dir);
double c = Math::dot(seg_dir, seg_dir);
double d = Math::dot(fwd, w);
double e = Math::dot(seg_dir, w);
double denom = a*c - b*b;
double seg_t = (denom > 1e-8) ? SDL_clamp((b*d - a*e) / denom, 0.0, 1.0) : 0.0;
Vec3 closest {
seg_a.x + seg_dir.x * seg_t,
seg_a.y + seg_dir.y * seg_t,
seg_a.z + seg_dir.z * seg_t
};
Vec3 dv { closest.x - eye.x, closest.y - eye.y, closest.z - eye.z };
double xz = SDL_sqrt(dv.x*dv.x + dv.z*dv.z);
if (xz > max_reach) return -1.0;
double len = Math::length(dv);
if (len < 0.001) return -1.0;
double t = SDL_clamp(Math::dot(fwd, dv) / len, -1.0, 1.0);
double angle = SDL_acos(t);
return (angle < MAX_ANGLE) ? angle : -1.0;
}
PickResult
pick(const Camera& camera,
const DynArray<Text::TokenPtr>& words,
const DynArray<GameObjectPtr>& scenery,
const DynArray<Text::Handler*>& walls)
{
const Vec3& eye = camera.pos;
const double floor_y = World::get_FloorY();
PickResult result;
double best_angle = MAX_ANGLE;
for (size_t i = 0; i < words.size(); i++) {
const Text::TokenPtr& w = words[i];
if (!w || !w->interactable || w->is_Whitespace()) continue;
if (w->ai.state != Text::Token::Ai::State::Idle)
continue;
if (w->pos.y < floor_y) continue;
double a = angScore(*w, eye, camera.fwd);
if (a >= 0.0 && a < best_angle) {
best_angle = a;
result.kind = PickKind::Word;
result.word_index = i;
}
}
for (const GameObjectPtr& s : scenery) {
if (!s || !s->interactable) continue;
if (s->pos.y < floor_y) continue;
double a = angScore(*s, eye, camera.fwd);
if (a >= 0.0 && a < best_angle) {
best_angle = a;
result.kind = PickKind::Scenery;
result.scenery_obj = s.get();
}
}
for (Text::Handler* h : walls) {
if (!h || h->edit_Active()) continue;
for (const Text::Line& line : h->get_Lines()) {
for (const Text::TokenPtr& tp : line.tokens) {
Text::Token::Data* t = tp.get();
if (!t || !t->interactable || t->is_Whitespace()) continue;
if (t->pos.y < floor_y) continue;
double a = angScore(*t, eye, camera.fwd);
if (a >= 0.0 && a < best_angle) {
best_angle = a;
result.kind = PickKind::WallWord;
result.wall_handler = h;
result.wall_token = t;
}
}
}
}
return result;
}
Text::Token::Data*
pick_WallToken(const Camera& camera, Text::Handler& wall)
{
const Vec3& eye = camera.pos;
Text::Token::Data* best = nullptr;
double best_angle = MAX_ANGLE;
for (const Text::Line& line : wall.get_Lines()) {
for (const Text::TokenPtr& tp : line.tokens) {
Text::Token::Data* t = tp.get();
if (!t) continue;
double a = angScore(*t, eye, camera.fwd, /*max_reach=*/1e18);
if (a >= 0.0 && a < best_angle) {
best_angle = a;
best = t;
}
}
}
return best;
}
} // namespace PickSystem
} // namespace Nimbin