ADD: Texture cache system improvement 2
This commit is contained in:
868
docs/GameAPI.md
868
docs/GameAPI.md
@@ -26,8 +26,12 @@ Implementation: `src/core/game_api.cpp`
|
|||||||
- Instances and animation.
|
- Instances and animation.
|
||||||
- Post‑processing (tonemap, bloom, FXAA).
|
- Post‑processing (tonemap, bloom, FXAA).
|
||||||
- Camera control.
|
- Camera control.
|
||||||
|
- Lighting (directional, point, spot).
|
||||||
|
- Volumetrics (clouds, smoke, flame).
|
||||||
|
- Particle systems.
|
||||||
|
- Debug drawing.
|
||||||
- Picking and render‑graph pass toggles.
|
- Picking and render‑graph 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 per‑frame 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 per‑frame 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 per‑scene tuning (e.g. reducing upload bandwidth on low‑end machines).
|
To globally change how aggressive streaming can be, edit these constants in `config.h` and rebuild. Use the `GameAPI::Engine` setters for per‑scene tuning (e.g. reducing upload bandwidth on low‑end 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,6 +189,95 @@ The following quality‑related 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 Post‑Processing
|
### Reflections and Post‑Processing
|
||||||
|
|
||||||
Game‑side reflection controls:
|
Game‑side reflection controls:
|
||||||
@@ -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 quaternion‑based `Camera::orientation` and `position` in `SceneManager`. They respect the engine’s `-Z` forward convention.
|
These functions internally manipulate the quaternion‑based `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).
|
||||||
@@ -372,17 +550,99 @@ Header: `src/core/engine.h`
|
|||||||
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)
|
||||||
|
|
||||||
- Scene‑level
|
**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);
|
||||||
- Instance‑level
|
|
||||||
- `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`.
|
|
||||||
|
**Notes:**
|
||||||
|
- All functions return `bool` indicating whether the named instance exists.
|
||||||
|
- Animation state is **independent per instance**:
|
||||||
- Each glTF instance has its own `AnimationState`, even when sharing the same `LoadedGLTF`.
|
- Each glTF instance has its own `AnimationState`, even when sharing the same `LoadedGLTF`.
|
||||||
- An index `< 0` (e.g. `-1`) disables animation for that scene/instance (pose is frozen at the last evaluated state).
|
- 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.
|
- `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);
|
||||||
|
```
|
||||||
|
|
||||||
### Per‑Instance Node / Joint Control (Non‑Skinned)
|
### Per‑Instance Node / Joint Control (Non‑Skinned)
|
||||||
|
|
||||||
For rigid models and simple “joints” (e.g. flaps, doors, turrets), you can apply local‑space pose offsets to individual glTF nodes per instance:
|
For rigid models and simple "joints" (e.g. flaps, doors, turrets), you can apply local‑space 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.
|
||||||
|
|||||||
@@ -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
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user