ADD: Texture cache system improvement 2

This commit is contained in:
2025-12-25 20:08:25 +09:00
parent fae718e9b3
commit 0172996e12
3 changed files with 884 additions and 47 deletions

View File

@@ -26,8 +26,12 @@ Implementation: `src/core/game_api.cpp`
- Instances and animation. - Instances and animation.
- Postprocessing (tonemap, bloom, FXAA). - Postprocessing (tonemap, bloom, FXAA).
- Camera control. - Camera control.
- Lighting (directional, point, spot).
- Volumetrics (clouds, smoke, flame).
- Particle systems.
- Debug drawing.
- Picking and rendergraph pass toggles. - Picking and rendergraph pass toggles.
- Input handling (keyboard, mouse, cursor modes). - Time and statistics.
Typical creation: Typical creation:
@@ -49,10 +53,15 @@ Relevant methods:
- `size_t get_texture_budget() const;` - `size_t get_texture_budget() const;`
- `void set_texture_loads_per_frame(int count);` - `void set_texture_loads_per_frame(int count);`
- `int get_texture_loads_per_frame() const;`
- `void set_texture_upload_budget(size_t bytes);` - `void set_texture_upload_budget(size_t bytes);`
- `size_t get_texture_upload_budget() const;`
- `void set_cpu_source_budget(size_t bytes);` - `void set_cpu_source_budget(size_t bytes);`
- `size_t get_cpu_source_budget() const;`
- `void set_max_upload_dimension(uint32_t dim);` - `void set_max_upload_dimension(uint32_t dim);`
- `uint32_t get_max_upload_dimension() const;`
- `void set_keep_source_bytes(bool keep);` - `void set_keep_source_bytes(bool keep);`
- `bool get_keep_source_bytes() const;`
- `void evict_textures_to_budget();` - `void evict_textures_to_budget();`
At a lower level, `VulkanEngine::query_texture_budget_bytes()` computes a conservative perframe texture budget using VMA heap info and constants in `src/core/config.h`: At a lower level, `VulkanEngine::query_texture_budget_bytes()` computes a conservative perframe texture budget using VMA heap info and constants in `src/core/config.h`:
@@ -63,6 +72,65 @@ At a lower level, `VulkanEngine::query_texture_budget_bytes()` computes a conser
To globally change how aggressive streaming can be, edit these constants in `config.h` and rebuild. Use the `GameAPI::Engine` setters for perscene tuning (e.g. reducing upload bandwidth on lowend machines). To globally change how aggressive streaming can be, edit these constants in `config.h` and rebuild. Use the `GameAPI::Engine` setters for perscene tuning (e.g. reducing upload bandwidth on lowend machines).
#### Texture Loading
```cpp
// Load from file (relative to assets/textures/ or absolute path)
TextureHandle load_texture(const std::string& path, const TextureLoadParams& params = {});
// Load from memory (compressed image data: PNG, JPG, KTX2, etc.)
TextureHandle load_texture_from_memory(const std::vector<uint8_t>& data, const TextureLoadParams& params = {});
// Check if texture is loaded and resident in VRAM
bool is_texture_loaded(TextureHandle handle) const;
// Get internal Vulkan image view (VkImageView) for advanced use
void* get_texture_image_view(TextureHandle handle) const;
// Pin texture to prevent automatic eviction (for UI, critical assets)
void pin_texture(TextureHandle handle);
void unpin_texture(TextureHandle handle);
bool is_texture_pinned(TextureHandle handle) const;
// Unload texture and free VRAM (optional - cache auto-manages)
void unload_texture(TextureHandle handle);
// Create ImGui descriptor set for use with ImGui::Image()
void* create_imgui_texture(TextureHandle handle, void* sampler = nullptr);
void free_imgui_texture(void* imgui_texture_id);
```
**TextureLoadParams:**
```cpp
struct TextureLoadParams
{
bool srgb{false}; // Use sRGB color space (true for albedo/emissive)
bool mipmapped{true}; // Generate mipmap chain
TextureChannels channels{Auto}; // Channel hint (Auto, R, RG, RGBA)
uint32_t mipLevels{0}; // 0 = full chain, otherwise limit to N levels
};
```
**Usage Example:**
```cpp
GameAPI::Engine api(&engine);
// Load UI texture and pin it to prevent eviction
GameAPI::TextureLoadParams params;
params.srgb = true;
params.mipmapped = false; // UI textures don't need mipmaps
TextureHandle uiTex = api.load_texture("ui/button.png", params);
api.pin_texture(uiTex);
// Create ImGui descriptor for rendering
void* imguiId = api.create_imgui_texture(uiTex);
ImGui::Image(imguiId, ImVec2(128, 64));
// Later: cleanup
api.free_imgui_texture(imguiId);
api.unpin_texture(uiTex);
```
### Shadows: Resolution, Quality, and RT Modes ### Shadows: Resolution, Quality, and RT Modes
Shadows are controlled by a combination of: Shadows are controlled by a combination of:
@@ -121,12 +189,101 @@ The following qualityrelated shadow constants also live in `config.h`:
These affect how cascades are distributed and how soft/filtered the resulting shadows are. Changing them is safe but should be tested against your content and FOV ranges. These affect how cascades are distributed and how soft/filtered the resulting shadows are. Changing them is safe but should be tested against your content and FOV ranges.
### IBL (Image-Based Lighting)
**API:**
```cpp
// Load global IBL asynchronously
bool load_global_ibl(const IBLPaths& paths);
// Get/set global IBL paths (does not trigger reload)
IBLPaths get_global_ibl_paths() const;
void set_global_ibl_paths(const IBLPaths& paths);
// Add/remove local IBL volumes
size_t add_ibl_volume(const IBLVolume& volume);
size_t add_ibl_volume(const IBLVolumeD& volume); // double-precision
bool remove_ibl_volume(size_t index);
// Get/set IBL volume properties
bool get_ibl_volume(size_t index, IBLVolume& out) const;
bool set_ibl_volume(size_t index, const IBLVolume& volume);
bool get_ibl_volume(size_t index, IBLVolumeD& out) const; // double-precision
bool set_ibl_volume(size_t index, const IBLVolumeD& volume);
// Query active volume
int get_active_ibl_volume() const; // -1 = global
size_t get_ibl_volume_count() const;
void clear_ibl_volumes();
```
**Structures:**
```cpp
struct IBLPaths
{
std::string specularCube; // .ktx2 specular cubemap
std::string diffuseCube; // .ktx2 diffuse cubemap
std::string brdfLut; // .ktx2 BRDF lookup table
std::string background; // .ktx2 background (optional, falls back to specular)
};
struct IBLVolume
{
glm::vec3 center{0.0f};
glm::vec3 halfExtents{10.0f};
IBLPaths paths;
bool enabled{true};
};
struct IBLVolumeD // double-precision variant
{
glm::dvec3 center{0.0};
glm::vec3 halfExtents{10.0f};
IBLPaths paths;
bool enabled{true};
};
```
**Usage Example:**
```cpp
GameAPI::Engine api(&engine);
// Load global IBL (outdoor environment)
GameAPI::IBLPaths globalIBL;
globalIBL.specularCube = "ibl/outdoor_spec.ktx2";
globalIBL.diffuseCube = "ibl/outdoor_diff.ktx2";
globalIBL.brdfLut = "ibl/brdf_lut.ktx2";
api.load_global_ibl(globalIBL);
// Create local IBL volume for interior (overrides global when camera inside)
GameAPI::IBLVolume interior;
interior.center = glm::vec3(10.0f, 2.0f, -5.0f);
interior.halfExtents = glm::vec3(5.0f, 3.0f, 5.0f);
interior.paths.specularCube = "ibl/indoor_spec.ktx2";
interior.paths.diffuseCube = "ibl/indoor_diff.ktx2";
interior.paths.brdfLut = "ibl/brdf_lut.ktx2";
interior.enabled = true;
size_t idx = api.add_ibl_volume(interior);
// Query which IBL is active
int activeVol = api.get_active_ibl_volume();
if (activeVol == -1)
{
// Using global IBL
}
else
{
// Using local volume at index activeVol
}
```
### Reflections and PostProcessing ### Reflections and PostProcessing
Gameside reflection controls: Gameside reflection controls:
- `void set_ssr_enabled(bool enabled);` - `void set_ssr_enabled(bool enabled);`
- `void set_reflection_mode(ReflectionMode mode);` - `void set_reflection_mode(ReflectionMode mode);`
(`SSROnly`, `SSRPlusRT`, `RTOnly`) (`SSROnly`, `SSRPlusRT`, `RTOnly`)
Tone mapping and bloom: Tone mapping and bloom:
@@ -150,14 +307,17 @@ FXAA:
Camera: Camera:
- `void set_camera_position(const glm::vec3 &position);` - `void set_camera_position(const glm::vec3 &position);`
- `void set_camera_position(const glm::dvec3 &position);` // double-precision
- `glm::vec3 get_camera_position() const;` - `glm::vec3 get_camera_position() const;`
- `glm::dvec3 get_camera_position_d() const;` // double-precision
- `void set_camera_rotation(float pitchDeg, float yawDeg);` - `void set_camera_rotation(float pitchDeg, float yawDeg);`
- `void get_camera_rotation(float &pitchDeg, float &yawDeg) const;` - `void get_camera_rotation(float &pitchDeg, float &yawDeg) const;`
- `void set_camera_fov(float fovDegrees);` - `void set_camera_fov(float fovDegrees);`
- `float get_camera_fov() const;` - `float get_camera_fov() const;`
- `void camera_look_at(const glm::vec3 &target);` - `void camera_look_at(const glm::vec3 &target);`
- `void camera_look_at(const glm::dvec3 &target);` // double-precision
These functions internally manipulate the quaternionbased `Camera::orientation` and `position` in `SceneManager`. They respect the engines `-Z` forward convention. These functions internally manipulate the quaternionbased `Camera::orientation` and `position` in `SceneManager`. They respect the engine's `-Z` forward convention. Double-precision variants allow precise camera positioning in large worlds.
Render resolution scaling: Render resolution scaling:
@@ -171,9 +331,27 @@ This scales the internal draw extent relative to the swapchain and main HDR imag
Picking: Picking:
- `Engine::PickResult get_last_pick() const;` - `Engine::PickResult get_last_pick() const;`
- `Engine::PickResultD get_last_pick_d() const;` // double-precision
- `void set_use_id_buffer_picking(bool use);` - `void set_use_id_buffer_picking(bool use);`
- `bool get_use_id_buffer_picking() const;` - `bool get_use_id_buffer_picking() const;`
**PickResult structure:**
```cpp
struct PickResult
{
bool valid{false};
std::string ownerName;
glm::vec3 worldPosition{0.0f};
};
struct PickResultD // double-precision variant
{
bool valid{false};
std::string ownerName;
glm::dvec3 worldPosition{0.0};
};
```
These mirror `VulkanEngine::get_last_pick()` and `_useIdBufferPicking`, letting you choose between: These mirror `VulkanEngine::get_last_pick()` and `_useIdBufferPicking`, letting you choose between:
- CPU raycast picking (immediate, cheaper VRAM). - CPU raycast picking (immediate, cheaper VRAM).
@@ -369,20 +547,102 @@ Header: `src/core/engine.h`
## Scene & Instances (Actors, Lights, Animations) ## Scene & Instances (Actors, Lights, Animations)
Header: `src/scene/vk_scene.h` Header: `src/scene/vk_scene.h`
Docs: `docs/Scene.md` Docs: `docs/Scene.md`
### Dynamic Mesh/GLTF Instances ### Transform Structures
- Mesh instances The GameAPI provides both single and double-precision transform representations:
- `void addMeshInstance(const std::string &name, std::shared_ptr<MeshAsset> mesh, const glm::mat4 &transform = glm::mat4(1.f), std::optional<BoundsType> boundsType = {});`
- `bool getMeshInstanceTransform(const std::string &name, glm::mat4 &outTransform);` ```cpp
- `bool setMeshInstanceTransform(const std::string &name, const glm::mat4 &transform);` struct Transform
- `bool removeMeshInstance(const std::string &name);` {
- `void clearMeshInstances();` glm::vec3 position{0.0f};
- Typical usage: glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f};
- Spawn primitives or dynamic meshes at runtime (e.g. projectiles, props). glm::vec3 scale{1.0f};
- Use `setMeshInstanceTransform` every frame to move them based on game logic.
glm::mat4 to_matrix() const;
static Transform from_matrix(const glm::mat4& m);
};
struct TransformD // double-precision variant for large worlds
{
glm::dvec3 position{0.0};
glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f};
glm::vec3 scale{1.0f};
glm::mat4 to_matrix() const;
static TransformD from_matrix(const glm::mat4& m);
};
```
Use `TransformD` for positioning objects in large worlds (e.g., space games, flight sims) where single-precision floating point loses sub-meter precision at large coordinates.
### GLTF Instances
**API:**
```cpp
// Add glTF model instance (path relative to assets/models/)
bool add_gltf_instance(const std::string& name,
const std::string& modelPath,
const Transform& transform = {},
bool preloadTextures = true);
bool add_gltf_instance(const std::string& name,
const std::string& modelPath,
const TransformD& transform,
bool preloadTextures = true);
// Add glTF model asynchronously (returns job ID, 0 on failure)
uint32_t add_gltf_instance_async(const std::string& name,
const std::string& modelPath,
const Transform& transform = {},
bool preloadTextures = true);
uint32_t add_gltf_instance_async(const std::string& name,
const std::string& modelPath,
const TransformD& transform,
bool preloadTextures = true);
// Remove glTF instance
bool remove_gltf_instance(const std::string& name);
// Get/set glTF instance transform
bool get_gltf_instance_transform(const std::string& name, Transform& out) const;
bool set_gltf_instance_transform(const std::string& name, const Transform& transform);
bool get_gltf_instance_transform(const std::string& name, TransformD& out) const;
bool set_gltf_instance_transform(const std::string& name, const TransformD& transform);
// Preload textures for an instance
void preload_instance_textures(const std::string& name);
// Clear all dynamic instances
void clear_all_instances();
```
### Primitive Mesh Instances
**API:**
```cpp
// Add primitive mesh instance
bool add_primitive_instance(const std::string& name,
PrimitiveType type,
const Transform& transform = {});
bool add_primitive_instance(const std::string& name,
PrimitiveType type,
const TransformD& transform);
// Remove mesh instance
bool remove_mesh_instance(const std::string& name);
// Get/set mesh instance transform
bool get_mesh_instance_transform(const std::string& name, Transform& out) const;
bool set_mesh_instance_transform(const std::string& name, const Transform& transform);
bool get_mesh_instance_transform(const std::string& name, TransformD& out) const;
bool set_mesh_instance_transform(const std::string& name, const TransformD& transform);
```
**Typical usage:**
- Spawn primitives or dynamic meshes at runtime (e.g. projectiles, props).
- Use `set_mesh_instance_transform` every frame to move them based on game logic.
### Textured Primitives ### Textured Primitives
@@ -488,29 +748,50 @@ api.add_textured_primitive("chrome_sphere", GameAPI::PrimitiveType::Sphere, meta
### Animations (GLTF) ### Animations (GLTF)
- Scenelevel **API:**
- `bool setSceneAnimation(const std::string &sceneName, int animationIndex, bool resetTime = true);` ```cpp
- `bool setSceneAnimation(const std::string &sceneName, const std::string &animationName, bool resetTime = true);` // Set animation by index for a glTF instance (-1 to disable)
- `bool setSceneAnimationLoop(const std::string &sceneName, bool loop);` bool set_instance_animation(const std::string& instanceName, int animationIndex, bool resetTime = true);
- Instancelevel
- `bool setGLTFInstanceAnimation(const std::string &instanceName, int animationIndex, bool resetTime = true);` // Set animation by name for a glTF instance
- `bool setGLTFInstanceAnimation(const std::string &instanceName, const std::string &animationName, bool resetTime = true);` bool set_instance_animation(const std::string& instanceName, const std::string& animationName, bool resetTime = true);
- `bool setGLTFInstanceAnimationLoop(const std::string &instanceName, bool loop);`
- Notes: // Set animation looping for a glTF instance
- All functions return `bool` indicating whether the named scene/instance exists. bool set_instance_animation_loop(const std::string& instanceName, bool loop);
- Animation state is **independent per scene and per instance**: ```
- Each named scene has its own `AnimationState`.
- Each glTF instance has its own `AnimationState`, even when sharing the same `LoadedGLTF`. **Notes:**
- An index `< 0` (e.g. `-1`) disables animation for that scene/instance (pose is frozen at the last evaluated state). - All functions return `bool` indicating whether the named instance exists.
- `SceneManager::update_scene()` advances each active animation state every frame using engine delta time. - Animation state is **independent per instance**:
- Each glTF instance has its own `AnimationState`, even when sharing the same `LoadedGLTF`.
- An index `< 0` (e.g. `-1`) disables animation for that instance (pose is frozen at the last evaluated state).
- `SceneManager::update_scene()` advances each active animation state every frame using engine delta time.
**Usage Example:**
```cpp
GameAPI::Engine api(&engine);
// Play walk animation by index
api.set_instance_animation("player", 0, true); // Reset to start
api.set_instance_animation_loop("player", true);
// Switch to run animation by name
api.set_instance_animation("player", "run", true);
// Stop animation (freeze pose)
api.set_instance_animation("player", -1);
```
### PerInstance Node / Joint Control (NonSkinned) ### PerInstance Node / Joint Control (NonSkinned)
For rigid models and simple joints (e.g. flaps, doors, turrets), you can apply localspace pose offsets to individual glTF nodes per instance: For rigid models and simple "joints" (e.g. flaps, doors, turrets), you can apply localspace pose offsets to individual glTF nodes per instance:
- `bool setGLTFInstanceNodeOffset(const std::string &instanceName, const std::string &nodeName, const glm::mat4 &offset);` **API:**
- `bool clearGLTFInstanceNodeOffset(const std::string &instanceName, const std::string &nodeName);` ```cpp
- `void clearGLTFInstanceNodeOffsets(const std::string &instanceName);` bool set_instance_node_offset(const std::string& instanceName, const std::string& nodeName, const glm::mat4& offset);
bool clear_instance_node_offset(const std::string& instanceName, const std::string& nodeName);
void clear_all_instance_node_offsets(const std::string& instanceName);
```
Typical usage: Typical usage:
@@ -526,20 +807,108 @@ Typical usage:
sceneMgr->setGLTFInstanceNodeOffset("plane01", "LeftAileron", offset); sceneMgr->setGLTFInstanceNodeOffset("plane01", "LeftAileron", offset);
``` ```
### Point Lights ### Lighting - Directional (Sunlight)
- Struct: - `void set_sunlight_direction(const glm::vec3& dir);`
- `struct PointLight { glm::vec3 position; float radius; glm::vec3 color; float intensity; };` - `glm::vec3 get_sunlight_direction() const;`
- API: - `void set_sunlight_color(const glm::vec3& color, float intensity);`
- `void addPointLight(const PointLight &light);` - `glm::vec3 get_sunlight_color() const;`
- `void clearPointLights();` - `float get_sunlight_intensity() const;`
- `size_t getPointLightCount() const;`
- `bool getPointLight(size_t index, PointLight &outLight) const;` ### Lighting - Point Lights
- `bool setPointLight(size_t index, const PointLight &light);`
- `bool removePointLight(size_t index);` **Structs:**
- Typical usage: ```cpp
- On level load, add all static lights. struct PointLight
- At runtime, animate or toggle lights based on gameplay events (e.g. explosions, flickering lamps). {
glm::vec3 position{0.0f};
float radius{10.0f};
glm::vec3 color{1.0f};
float intensity{1.0f};
};
struct PointLightD // double-precision variant
{
glm::dvec3 position{0.0};
float radius{10.0f};
glm::vec3 color{1.0f};
float intensity{1.0f};
};
```
**API:**
- `size_t add_point_light(const PointLight &light);`
- `size_t add_point_light(const PointLightD &light);`
- `bool remove_point_light(size_t index);`
- `bool get_point_light(size_t index, PointLight &out) const;`
- `bool get_point_light(size_t index, PointLightD &out) const;`
- `bool set_point_light(size_t index, const PointLight &light);`
- `bool set_point_light(size_t index, const PointLightD &light);`
- `size_t get_point_light_count() const;`
- `void clear_point_lights();`
**Typical usage:**
- On level load, add all static lights.
- At runtime, animate or toggle lights based on gameplay events (e.g. explosions, flickering lamps).
### Lighting - Spot Lights
**Structs:**
```cpp
struct SpotLight
{
glm::vec3 position{0.0f};
glm::vec3 direction{0.0f, -1.0f, 0.0f};
float radius{10.0f};
glm::vec3 color{1.0f};
float intensity{1.0f};
float inner_angle_deg{15.0f};
float outer_angle_deg{25.0f};
};
struct SpotLightD // double-precision variant
{
glm::dvec3 position{0.0};
glm::vec3 direction{0.0f, -1.0f, 0.0f};
float radius{10.0f};
glm::vec3 color{1.0f};
float intensity{1.0f};
float inner_angle_deg{15.0f};
float outer_angle_deg{25.0f};
};
```
**API:**
- `size_t add_spot_light(const SpotLight &light);`
- `size_t add_spot_light(const SpotLightD &light);`
- `bool remove_spot_light(size_t index);`
- `bool get_spot_light(size_t index, SpotLight &out) const;`
- `bool get_spot_light(size_t index, SpotLightD &out) const;`
- `bool set_spot_light(size_t index, const SpotLight &light);`
- `bool set_spot_light(size_t index, const SpotLightD &light);`
- `size_t get_spot_light_count() const;`
- `void clear_spot_lights();`
**Usage Example:**
```cpp
GameAPI::Engine api(&engine);
// Create a flashlight
GameAPI::SpotLight flashlight;
flashlight.position = glm::vec3(0.0f, 1.5f, 0.0f);
flashlight.direction = glm::vec3(0.0f, 0.0f, -1.0f);
flashlight.radius = 20.0f;
flashlight.color = glm::vec3(1.0f, 0.95f, 0.8f); // Warm white
flashlight.intensity = 50.0f;
flashlight.inner_angle_deg = 10.0f;
flashlight.outer_angle_deg = 25.0f;
size_t idx = api.add_spot_light(flashlight);
// Later: update flashlight direction to follow camera
flashlight.direction = camera_forward;
api.set_spot_light(idx, flashlight);
```
--- ---
@@ -723,3 +1092,416 @@ void draw_gizmo(const PickingSystem::PickInfo& pick,
} }
} }
``` ```
---
## Time and Statistics
Header: `src/core/game_api.h`
### Delta Time
```cpp
// Get delta time in seconds for the current frame (clamped to 0.0-0.1)
float get_delta_time() const;
```
Use this for frame-rate independent movement and animation.
### Engine Statistics
```cpp
struct Stats
{
float frametime{0.0f}; // ms
float drawTime{0.0f}; // ms
float sceneUpdateTime{0.0f}; // ms
int triangleCount{0};
int drawCallCount{0};
};
Stats get_stats() const;
```
**Usage Example:**
```cpp
GameAPI::Engine api(&engine);
// Frame-rate independent movement
float dt = api.get_delta_time();
player_position += velocity * dt;
// Display performance stats
GameAPI::Stats stats = api.get_stats();
fmt::println("FPS: {:.1f} | Tris: {} | Draws: {}",
1000.0f / stats.frametime,
stats.triangleCount,
stats.drawCallCount);
```
---
## Volumetrics (Clouds, Smoke, Flame)
Header: `src/core/game_api.h`
The engine supports GPU-based voxel volumetric rendering for clouds, smoke, and flame effects. Up to 4 independent volumes can be active simultaneously.
### API
```cpp
// Enable/disable volumetrics system
void set_volumetrics_enabled(bool enabled);
bool get_volumetrics_enabled() const;
// Get/set voxel volume settings by index (0-3)
bool get_voxel_volume(size_t index, VoxelVolumeSettings& out) const;
bool set_voxel_volume(size_t index, const VoxelVolumeSettings& settings);
// Get maximum number of voxel volumes
size_t get_max_voxel_volumes() const; // Returns 4
```
### VoxelVolumeSettings Structure
```cpp
struct VoxelVolumeSettings
{
bool enabled{false};
VoxelVolumeType type{VoxelVolumeType::Clouds}; // Clouds, Smoke, Flame
// Volume positioning
bool followCameraXZ{false}; // Follow camera in XZ, offset in Y
bool animateVoxels{true}; // Run voxel advection/update compute
glm::vec3 volumeCenterLocal{0.0f, 2.0f, 0.0f};
glm::vec3 volumeHalfExtents{8.0f, 8.0f, 8.0f};
glm::vec3 volumeVelocityLocal{0.0f, 0.0f, 0.0f}; // Drift when not following camera
// Raymarch/composite controls
float densityScale{1.0f};
float coverage{0.0f}; // 0..1 threshold (higher = emptier)
float extinction{1.0f}; // Absorption/extinction scale
int stepCount{48}; // Raymarch steps
// Voxel grid resolution (cubic)
uint32_t gridResolution{48};
// Voxel animation (advection + injection) parameters
glm::vec3 windVelocityLocal{0.0f, 2.0f, 0.0f}; // Local units/sec (buoyancy)
float dissipation{1.25f}; // Density decay rate (1/sec)
float noiseStrength{1.0f}; // Injection rate
float noiseScale{8.0f}; // Noise frequency in UVW space
float noiseSpeed{1.0f}; // Time scale for injection noise
// Smoke/flame source in normalized volume UVW space
glm::vec3 emitterUVW{0.5f, 0.05f, 0.5f};
float emitterRadius{0.18f}; // Normalized (0..1)
// Shading
glm::vec3 albedo{1.0f, 1.0f, 1.0f}; // Scattering tint (cloud/smoke)
float scatterStrength{1.0f};
glm::vec3 emissionColor{1.0f, 0.6f, 0.25f}; // Flame emissive tint
float emissionStrength{0.0f};
};
```
### Usage Example
```cpp
GameAPI::Engine api(&engine);
// Enable volumetrics
api.set_volumetrics_enabled(true);
// Create a flame effect
GameAPI::VoxelVolumeSettings flame;
flame.enabled = true;
flame.type = GameAPI::VoxelVolumeType::Flame;
flame.volumeCenterLocal = glm::vec3(0.0f, 1.0f, -5.0f);
flame.volumeHalfExtents = glm::vec3(2.0f, 3.0f, 2.0f);
flame.gridResolution = 64;
flame.densityScale = 1.5f;
flame.coverage = 0.3f;
flame.windVelocityLocal = glm::vec3(0.0f, 5.0f, 0.0f); // Upward
flame.emitterUVW = glm::vec3(0.5f, 0.1f, 0.5f);
flame.emitterRadius = 0.2f;
flame.emissionStrength = 2.0f;
flame.emissionColor = glm::vec3(1.0f, 0.5f, 0.1f);
api.set_voxel_volume(0, flame);
// Create cloud layer that follows camera
GameAPI::VoxelVolumeSettings clouds;
clouds.enabled = true;
clouds.type = GameAPI::VoxelVolumeType::Clouds;
clouds.followCameraXZ = true;
clouds.volumeCenterLocal = glm::vec3(0.0f, 50.0f, 0.0f); // Offset in Y
clouds.volumeHalfExtents = glm::vec3(100.0f, 20.0f, 100.0f);
clouds.gridResolution = 128;
clouds.densityScale = 0.8f;
clouds.coverage = 0.5f;
clouds.albedo = glm::vec3(0.9f, 0.95f, 1.0f); // Bluish tint
api.set_voxel_volume(1, clouds);
```
---
## Particle Systems
Header: `src/core/game_api.h`
GPU-accelerated particle systems with flipbook animation, soft particles, and flexible spawning.
### API
```cpp
// Create/destroy particle systems
uint32_t create_particle_system(uint32_t particle_count);
bool destroy_particle_system(uint32_t id);
bool resize_particle_system(uint32_t id, uint32_t new_count);
// Get/set particle system settings
bool get_particle_system(uint32_t id, ParticleSystem& out) const;
bool set_particle_system(uint32_t id, const ParticleSystem& system);
// Query particle systems
std::vector<uint32_t> get_particle_system_ids() const;
uint32_t get_allocated_particles() const;
uint32_t get_free_particles() const;
uint32_t get_max_particles() const;
// Preload VFX textures (e.g., "vfx/flame.ktx2")
void preload_particle_texture(const std::string& assetPath);
```
### ParticleSystem Structure
```cpp
struct ParticleSystem
{
uint32_t id{0};
uint32_t particleCount{0};
bool enabled{true};
bool reset{true};
ParticleBlendMode blendMode{ParticleBlendMode::Additive}; // Additive or Alpha
ParticleParams params{};
// Asset-relative texture paths (e.g., "vfx/flame.ktx2")
std::string flipbookTexture{"vfx/flame.ktx2"};
std::string noiseTexture{"vfx/simplex.ktx2"};
};
struct ParticleParams
{
glm::vec3 emitterPosLocal{0.0f, 0.0f, 0.0f};
float spawnRadius{0.1f};
glm::vec3 emitterDirLocal{0.0f, 1.0f, 0.0f};
float coneAngleDegrees{20.0f};
float minSpeed{2.0f};
float maxSpeed{8.0f};
float minLife{0.5f};
float maxLife{1.5f};
float minSize{0.05f};
float maxSize{0.15f};
float drag{1.0f};
float gravity{0.0f}; // Positive pulls down -Y in local space
glm::vec4 color{1.0f, 0.5f, 0.1f, 1.0f};
// Soft particles (fade near opaque geometry)
float softDepthDistance{0.15f};
// Flipbook animation (atlas layout)
uint32_t flipbookCols{16};
uint32_t flipbookRows{4};
float flipbookFps{30.0f};
float flipbookIntensity{1.0f};
// Noise UV distortion
float noiseScale{6.0f};
float noiseStrength{0.05f};
glm::vec2 noiseScroll{0.0f, 0.0f};
};
```
### Usage Example
```cpp
GameAPI::Engine api(&engine);
// Create fire particle system
uint32_t fireId = api.create_particle_system(4096);
GameAPI::ParticleSystem fire;
fire.id = fireId;
fire.particleCount = 4096;
fire.enabled = true;
fire.blendMode = GameAPI::ParticleBlendMode::Additive;
fire.flipbookTexture = "vfx/flame.ktx2";
fire.noiseTexture = "vfx/simplex.ktx2";
fire.params.emitterPosLocal = glm::vec3(0.0f, 0.0f, -5.0f);
fire.params.spawnRadius = 0.5f;
fire.params.emitterDirLocal = glm::vec3(0.0f, 1.0f, 0.0f);
fire.params.coneAngleDegrees = 15.0f;
fire.params.minSpeed = 2.0f;
fire.params.maxSpeed = 4.0f;
fire.params.minLife = 0.8f;
fire.params.maxLife = 1.5f;
fire.params.minSize = 0.3f;
fire.params.maxSize = 0.6f;
fire.params.gravity = -2.0f; // Upward buoyancy
fire.params.color = glm::vec4(1.0f, 0.7f, 0.3f, 1.0f);
fire.params.flipbookCols = 16;
fire.params.flipbookRows = 4;
fire.params.flipbookFps = 24.0f;
api.set_particle_system(fireId, fire);
// Later: move emitter to follow player
fire.params.emitterPosLocal = player_position;
api.set_particle_system(fireId, fire);
// Reset particles (trigger burst)
fire.reset = true;
api.set_particle_system(fireId, fire);
```
---
## Debug Drawing
Header: `src/core/game_api.h`
Runtime debug visualization for primitives (lines, spheres, boxes, etc.) with optional depth testing and duration.
### Settings API
```cpp
// Enable/disable debug drawing system
void set_debug_draw_enabled(bool enabled);
bool get_debug_draw_enabled() const;
// Control which debug layers are visible (bitmask)
void set_debug_layer_mask(uint32_t mask);
uint32_t get_debug_layer_mask() const;
// Show/hide depth-tested primitives
void set_debug_show_depth_tested(bool show);
bool get_debug_show_depth_tested() const;
// Show/hide overlay (always-on-top) primitives
void set_debug_show_overlay(bool show);
bool get_debug_show_overlay() const;
// Set tessellation quality (segments for circles/spheres)
void set_debug_segments(int segments);
int get_debug_segments() const;
// Clear all debug draw commands
void debug_draw_clear();
```
### Drawing API
All drawing functions support both single and double-precision variants:
```cpp
// Line
void debug_draw_line(const glm::vec3& a, const glm::vec3& b,
const glm::vec4& color = glm::vec4(1.0f),
float duration_seconds = 0.0f,
bool depth_tested = true);
void debug_draw_line(const glm::dvec3& a, const glm::dvec3& b, ...);
// Ray (origin + direction + length)
void debug_draw_ray(const glm::vec3& origin, const glm::vec3& direction, float length,
const glm::vec4& color = glm::vec4(1.0f),
float duration_seconds = 0.0f,
bool depth_tested = true);
void debug_draw_ray(const glm::dvec3& origin, const glm::dvec3& direction, double length, ...);
// AABB (axis-aligned bounding box)
void debug_draw_aabb(const glm::vec3& center, const glm::vec3& half_extents,
const glm::vec4& color = glm::vec4(1.0f),
float duration_seconds = 0.0f,
bool depth_tested = true);
void debug_draw_aabb(const glm::dvec3& center, const glm::vec3& half_extents, ...);
// Sphere
void debug_draw_sphere(const glm::vec3& center, float radius,
const glm::vec4& color = glm::vec4(1.0f),
float duration_seconds = 0.0f,
bool depth_tested = true);
void debug_draw_sphere(const glm::dvec3& center, float radius, ...);
// Capsule (line segment + radius)
void debug_draw_capsule(const glm::vec3& p0, const glm::vec3& p1, float radius,
const glm::vec4& color = glm::vec4(1.0f),
float duration_seconds = 0.0f,
bool depth_tested = true);
void debug_draw_capsule(const glm::dvec3& p0, const glm::dvec3& p1, float radius, ...);
// Circle (center + normal + radius)
void debug_draw_circle(const glm::vec3& center, const glm::vec3& normal, float radius,
const glm::vec4& color = glm::vec4(1.0f),
float duration_seconds = 0.0f,
bool depth_tested = true);
void debug_draw_circle(const glm::dvec3& center, const glm::dvec3& normal, float radius, ...);
// Cone (apex + direction + length + angle)
void debug_draw_cone(const glm::vec3& apex, const glm::vec3& direction,
float length, float angle_degrees,
const glm::vec4& color = glm::vec4(1.0f),
float duration_seconds = 0.0f,
bool depth_tested = true);
void debug_draw_cone(const glm::dvec3& apex, const glm::dvec3& direction,
float length, float angle_degrees, ...);
```
### Usage Example
```cpp
GameAPI::Engine api(&engine);
// Enable debug drawing
api.set_debug_draw_enabled(true);
api.set_debug_segments(32); // Smooth circles/spheres
// Visualize player bounds (persistent, depth-tested)
api.debug_draw_aabb(player_pos, glm::vec3(0.5f, 1.0f, 0.5f),
glm::vec4(0.0f, 1.0f, 0.0f, 1.0f),
0.0f, // Duration 0 = single frame
true); // Depth tested
// Visualize raycast (red ray, always on top, 2 seconds)
api.debug_draw_ray(ray_origin, ray_dir, 100.0f,
glm::vec4(1.0f, 0.0f, 0.0f, 1.0f),
2.0f, // Show for 2 seconds
false); // Always on top
// Visualize trigger volume (transparent sphere)
api.debug_draw_sphere(trigger_pos, trigger_radius,
glm::vec4(1.0f, 1.0f, 0.0f, 0.3f), // Yellow, 30% alpha
0.0f,
true);
// Visualize spot light cone
api.debug_draw_cone(light_pos, light_dir, light_radius, light_angle_deg,
glm::vec4(1.0f, 0.9f, 0.7f, 0.5f),
0.0f,
true);
// One-shot clear (useful for clearing persistent debug viz)
api.debug_draw_clear();
```
**Notes:**
- `duration_seconds = 0.0f`: Draw for a single frame (re-submit each frame for persistent viz).
- `duration_seconds > 0.0f`: Draw for N seconds, then automatically expire.
- `depth_tested = true`: Primitive is occluded by scene geometry.
- `depth_tested = false`: Always on top (overlay mode).
- All primitives support alpha blending via the color's alpha channel.

View File

@@ -16,9 +16,12 @@
#include <glm/gtx/matrix_decompose.hpp> #include <glm/gtx/matrix_decompose.hpp>
#include <glm/gtx/quaternion.hpp> #include <glm/gtx/quaternion.hpp>
#include <cstdint>
#include <filesystem> #include <filesystem>
// ImGui integration for texture display
#include "imgui.h"
#include "imgui_impl_vulkan.h"
namespace GameAPI namespace GameAPI
{ {
@@ -332,6 +335,49 @@ void Engine::unload_texture(TextureHandle handle)
_engine->_textureCache->unload(cacheHandle); _engine->_textureCache->unload(cacheHandle);
} }
void* Engine::create_imgui_texture(TextureHandle handle, void* sampler)
{
if (!_engine || !_engine->_textureCache)
{
return nullptr;
}
auto cacheHandle = static_cast<TextureCache::TextureHandle>(handle);
VkImageView imageView = _engine->_textureCache->image_view(cacheHandle);
if (imageView == VK_NULL_HANDLE)
{
return nullptr;
}
// Use provided sampler or default linear sampler
VkSampler vkSampler = reinterpret_cast<VkSampler>(sampler);
if (vkSampler == VK_NULL_HANDLE && _engine->_context && _engine->_context->samplers)
{
vkSampler = _engine->_context->samplers->defaultLinear();
}
// Create ImGui descriptor set using ImGui_ImplVulkan
VkDescriptorSet descriptorSet = ImGui_ImplVulkan_AddTexture(
vkSampler,
imageView,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
);
return reinterpret_cast<void*>(descriptorSet);
}
void Engine::free_imgui_texture(void* imgui_texture_id)
{
if (imgui_texture_id == nullptr)
{
return;
}
VkDescriptorSet descriptorSet = reinterpret_cast<VkDescriptorSet>(imgui_texture_id);
ImGui_ImplVulkan_RemoveTexture(descriptorSet);
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Shadows // Shadows
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@@ -383,6 +383,15 @@ public:
// This is optional - the cache manages memory automatically // This is optional - the cache manages memory automatically
void unload_texture(TextureHandle handle); void unload_texture(TextureHandle handle);
// Create an ImGui descriptor set for a texture (for use with ImGui::Image())
// Returns ImTextureID (actually VkDescriptorSet) that can be used in ImGui
// The returned descriptor set is managed by ImGui and valid until cleanup
// sampler: VK_NULL_HANDLE uses default linear sampler
void* create_imgui_texture(TextureHandle handle, void* sampler = nullptr);
// Free an ImGui descriptor set created by create_imgui_texture()
void free_imgui_texture(void* imgui_texture_id);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Shadows // Shadows
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------