Files
Faset_Engine/include/faset/render/visibility.hpp
T

78 lines
3.2 KiB
C++

#pragma once
#include <faset/render/renderer.hpp>
#include <cstddef>
#include <cstdint>
#include <span>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace faset::render {
struct Bounds {
Vec3 min{};
Vec3 max{};
};
// Mesh bounds are computed from all vertices; world bounds transform all eight
// corners of the local AABB, including rotation, reflection and nonuniform scale.
// Empty or nonfinite geometry/matrices are rejected instead of being culled.
Bounds local_bounds(const Mesh& mesh);
Bounds transformed_bounds(const Mesh& mesh, const Mat4& model);
struct InstanceUpdate {
std::uint32_t slot{};
std::uint64_t generation{};
Mat4 previous_model{identity};
Bounds previous_bounds{};
bool previous_valid{};
};
// One update per logical instance per rendered frame. finish_frame() promotes the
// current state to *rendered* history and retires keys absent from that frame.
// Reordering the input never renumbers live instances. Mesh changes retain the
// slot but advance its generation; slot reuse also advances its generation.
class InstanceTracker {
public:
// Prefer this overload for engine-owned meshes. Retaining the owner prevents
// allocator address reuse from impersonating the previously rendered mesh.
InstanceUpdate update(std::string_view key, std::shared_ptr<const Mesh> mesh_identity,
const Mat4& model, const Bounds& world_bounds,
std::string_view view_id);
// Non-owning overload for meshes whose lifetime is guaranteed by the caller.
InstanceUpdate update(std::string_view key, const Mesh* mesh_identity, const Mat4& model,
const Bounds& world_bounds, std::string_view view_id);
void finish_frame();
void invalidate_view(std::string_view view_id);
private:
struct Record {
std::uint32_t slot{};
std::uint64_t generation{};
const Mesh* mesh{};
std::shared_ptr<const Mesh> mesh_owner{};
Mat4 current_model{identity}, previous_model{identity};
Bounds current_bounds{}, previous_bounds{};
std::string current_view, previous_view;
std::uint64_t seen_frame{};
bool committed{};
};
std::unordered_map<std::string, Record> records_;
std::vector<std::uint32_t> free_slots_;
std::vector<std::uint64_t> slot_generations_;
std::unordered_set<std::string> invalid_views_;
std::uint64_t frame_{1};
};
// thresholds[i] is the projected-pixel boundary between levels i and i+1,
// ordered strictly high-to-low. Moving toward a coarser level requires size <
// threshold*(1-hysteresis); toward a finer level requires size >
// threshold*(1+hysteresis). SIZE_MAX means no prior level and uses raw thresholds.
// available is optional; when given it has thresholds.size()+1 entries, with 0
// marking missing prepared geometry. Nearest available wins; ties prefer finer.
std::size_t select_lod(float projected_pixels, std::size_t previous_level,
std::span<const float> thresholds, float hysteresis,
std::span<const std::uint8_t> available = {});
} // namespace faset::render