ADD: cloud, particle API
This commit is contained in:
@@ -401,6 +401,7 @@ IBLManager::AsyncResult IBLManager::pump_async()
|
||||
|
||||
PreparedIBLData data{};
|
||||
bool success = false;
|
||||
std::string error;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state->mutex);
|
||||
if (!state->resultReady)
|
||||
@@ -409,12 +410,17 @@ IBLManager::AsyncResult IBLManager::pump_async()
|
||||
}
|
||||
data = std::move(state->readyData);
|
||||
success = state->resultSuccess;
|
||||
error = std::move(state->lastError);
|
||||
state->resultReady = false;
|
||||
}
|
||||
|
||||
out.completed = true;
|
||||
if (!success)
|
||||
{
|
||||
if (!error.empty())
|
||||
{
|
||||
fmt::println("[IBL] async load failed: {}", error);
|
||||
}
|
||||
out.success = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -1051,7 +1051,7 @@ void VulkanEngine::draw()
|
||||
|
||||
if (newVolume != _activeIBLVolume)
|
||||
{
|
||||
const IBLPaths *paths = nullptr;
|
||||
IBLPaths *paths = nullptr;
|
||||
if (newVolume >= 0)
|
||||
{
|
||||
paths = &_iblVolumes[newVolume].paths;
|
||||
@@ -1067,17 +1067,27 @@ void VulkanEngine::draw()
|
||||
|
||||
if (paths && !alreadyPendingForTarget)
|
||||
{
|
||||
if (_iblManager->load_async(*paths))
|
||||
IBLPaths resolved = *paths;
|
||||
if (_assetManager)
|
||||
{
|
||||
if (!resolved.specularCube.empty()) resolved.specularCube = _assetManager->assetPath(resolved.specularCube);
|
||||
if (!resolved.diffuseCube.empty()) resolved.diffuseCube = _assetManager->assetPath(resolved.diffuseCube);
|
||||
if (!resolved.brdfLut2D.empty()) resolved.brdfLut2D = _assetManager->assetPath(resolved.brdfLut2D);
|
||||
if (!resolved.background2D.empty()) resolved.background2D = _assetManager->assetPath(resolved.background2D);
|
||||
}
|
||||
*paths = resolved;
|
||||
|
||||
if (_iblManager->load_async(resolved))
|
||||
{
|
||||
_pendingIBLRequest.active = true;
|
||||
_pendingIBLRequest.targetVolume = newVolume;
|
||||
_pendingIBLRequest.paths = *paths;
|
||||
_pendingIBLRequest.paths = resolved;
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::println("[Engine] Warning: failed to enqueue IBL load for {} (specular='{}')",
|
||||
(newVolume >= 0) ? "volume" : "global environment",
|
||||
paths->specularCube);
|
||||
resolved.specularCube);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,18 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
static IBLPaths resolve_ibl_paths(VulkanEngine *eng, const IBLPaths &paths)
|
||||
{
|
||||
IBLPaths out = paths;
|
||||
if (!eng || !eng->_assetManager) return out;
|
||||
|
||||
if (!out.specularCube.empty()) out.specularCube = eng->_assetManager->assetPath(out.specularCube);
|
||||
if (!out.diffuseCube.empty()) out.diffuseCube = eng->_assetManager->assetPath(out.diffuseCube);
|
||||
if (!out.brdfLut2D.empty()) out.brdfLut2D = eng->_assetManager->assetPath(out.brdfLut2D);
|
||||
if (!out.background2D.empty()) out.background2D = eng->_assetManager->assetPath(out.background2D);
|
||||
return out;
|
||||
}
|
||||
|
||||
static void ui_window(VulkanEngine *eng)
|
||||
{
|
||||
if (!eng || !eng->_window) return;
|
||||
@@ -639,6 +651,35 @@ namespace
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Volume %zu", i);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Delete"))
|
||||
{
|
||||
const int idx = static_cast<int>(i);
|
||||
if (eng->_activeIBLVolume == idx)
|
||||
{
|
||||
eng->_activeIBLVolume = -1;
|
||||
}
|
||||
else if (eng->_activeIBLVolume > idx)
|
||||
{
|
||||
eng->_activeIBLVolume -= 1;
|
||||
}
|
||||
|
||||
if (eng->_pendingIBLRequest.active)
|
||||
{
|
||||
if (eng->_pendingIBLRequest.targetVolume == idx)
|
||||
{
|
||||
eng->_pendingIBLRequest.active = false;
|
||||
}
|
||||
else if (eng->_pendingIBLRequest.targetVolume > idx)
|
||||
{
|
||||
eng->_pendingIBLRequest.targetVolume -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
eng->_iblVolumes.erase(eng->_iblVolumes.begin() + idx);
|
||||
ImGui::PopID();
|
||||
break;
|
||||
}
|
||||
ImGui::Checkbox("Enabled", &vol.enabled);
|
||||
{
|
||||
double c[3] = {vol.center_world.x, vol.center_world.y, vol.center_world.z};
|
||||
@@ -680,6 +721,7 @@ namespace
|
||||
{
|
||||
if (eng->_iblManager && vol.enabled)
|
||||
{
|
||||
vol.paths = resolve_ibl_paths(eng, vol.paths);
|
||||
if (eng->_iblManager->load_async(vol.paths))
|
||||
{
|
||||
eng->_pendingIBLRequest.active = true;
|
||||
@@ -691,6 +733,7 @@ namespace
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Set As Global IBL"))
|
||||
{
|
||||
vol.paths = resolve_ibl_paths(eng, vol.paths);
|
||||
eng->_globalIBLPaths = vol.paths;
|
||||
if (eng->_iblManager)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "core/pipeline/manager.h"
|
||||
#include "render/passes/tonemap.h"
|
||||
#include "render/passes/fxaa.h"
|
||||
#include "render/passes/particles.h"
|
||||
#include "render/renderpass.h"
|
||||
#include "core/picking/picking_system.h"
|
||||
#include "scene/vk_scene.h"
|
||||
@@ -1415,6 +1416,293 @@ Stats Engine::get_stats() const
|
||||
return s;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Volumetrics (Cloud/Smoke/Flame)
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void Engine::set_volumetrics_enabled(bool enabled)
|
||||
{
|
||||
if (!_engine || !_engine->_context) return;
|
||||
_engine->_context->enableVolumetrics = enabled;
|
||||
}
|
||||
|
||||
bool Engine::get_volumetrics_enabled() const
|
||||
{
|
||||
if (!_engine || !_engine->_context) return false;
|
||||
return _engine->_context->enableVolumetrics;
|
||||
}
|
||||
|
||||
bool Engine::get_voxel_volume(size_t index, VoxelVolumeSettings& out) const
|
||||
{
|
||||
if (!_engine || !_engine->_context) return false;
|
||||
if (index >= EngineContext::MAX_VOXEL_VOLUMES) return false;
|
||||
|
||||
const auto& src = _engine->_context->voxelVolumes[index];
|
||||
|
||||
out.enabled = src.enabled;
|
||||
out.type = static_cast<VoxelVolumeType>(src.type);
|
||||
out.followCameraXZ = src.followCameraXZ;
|
||||
out.animateVoxels = src.animateVoxels;
|
||||
out.volumeCenterLocal = src.volumeCenterLocal;
|
||||
out.volumeHalfExtents = src.volumeHalfExtents;
|
||||
out.volumeVelocityLocal = src.volumeVelocityLocal;
|
||||
out.densityScale = src.densityScale;
|
||||
out.coverage = src.coverage;
|
||||
out.extinction = src.extinction;
|
||||
out.stepCount = src.stepCount;
|
||||
out.gridResolution = src.gridResolution;
|
||||
out.windVelocityLocal = src.windVelocityLocal;
|
||||
out.dissipation = src.dissipation;
|
||||
out.noiseStrength = src.noiseStrength;
|
||||
out.noiseScale = src.noiseScale;
|
||||
out.noiseSpeed = src.noiseSpeed;
|
||||
out.emitterUVW = src.emitterUVW;
|
||||
out.emitterRadius = src.emitterRadius;
|
||||
out.albedo = src.albedo;
|
||||
out.scatterStrength = src.scatterStrength;
|
||||
out.emissionColor = src.emissionColor;
|
||||
out.emissionStrength = src.emissionStrength;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Engine::set_voxel_volume(size_t index, const VoxelVolumeSettings& settings)
|
||||
{
|
||||
if (!_engine || !_engine->_context) return false;
|
||||
if (index >= EngineContext::MAX_VOXEL_VOLUMES) return false;
|
||||
|
||||
auto& dst = _engine->_context->voxelVolumes[index];
|
||||
|
||||
dst.enabled = settings.enabled;
|
||||
dst.type = static_cast<::VoxelVolumeType>(settings.type);
|
||||
dst.followCameraXZ = settings.followCameraXZ;
|
||||
dst.animateVoxels = settings.animateVoxels;
|
||||
dst.volumeCenterLocal = settings.volumeCenterLocal;
|
||||
dst.volumeHalfExtents = settings.volumeHalfExtents;
|
||||
dst.volumeVelocityLocal = settings.volumeVelocityLocal;
|
||||
dst.densityScale = settings.densityScale;
|
||||
dst.coverage = settings.coverage;
|
||||
dst.extinction = settings.extinction;
|
||||
dst.stepCount = settings.stepCount;
|
||||
dst.gridResolution = settings.gridResolution;
|
||||
dst.windVelocityLocal = settings.windVelocityLocal;
|
||||
dst.dissipation = settings.dissipation;
|
||||
dst.noiseStrength = settings.noiseStrength;
|
||||
dst.noiseScale = settings.noiseScale;
|
||||
dst.noiseSpeed = settings.noiseSpeed;
|
||||
dst.emitterUVW = settings.emitterUVW;
|
||||
dst.emitterRadius = settings.emitterRadius;
|
||||
dst.albedo = settings.albedo;
|
||||
dst.scatterStrength = settings.scatterStrength;
|
||||
dst.emissionColor = settings.emissionColor;
|
||||
dst.emissionStrength = settings.emissionStrength;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Engine::get_max_voxel_volumes() const
|
||||
{
|
||||
return EngineContext::MAX_VOXEL_VOLUMES;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Particle Systems
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
uint32_t Engine::create_particle_system(uint32_t particle_count)
|
||||
{
|
||||
if (!_engine || !_engine->_renderPassManager) return 0;
|
||||
|
||||
ParticlePass* particlePass = _engine->_renderPassManager->getPass<ParticlePass>();
|
||||
if (!particlePass) return 0;
|
||||
|
||||
return particlePass->create_system(particle_count);
|
||||
}
|
||||
|
||||
bool Engine::destroy_particle_system(uint32_t id)
|
||||
{
|
||||
if (!_engine || !_engine->_renderPassManager) return false;
|
||||
|
||||
ParticlePass* particlePass = _engine->_renderPassManager->getPass<ParticlePass>();
|
||||
if (!particlePass) return false;
|
||||
|
||||
return particlePass->destroy_system(id);
|
||||
}
|
||||
|
||||
bool Engine::resize_particle_system(uint32_t id, uint32_t new_count)
|
||||
{
|
||||
if (!_engine || !_engine->_renderPassManager) return false;
|
||||
|
||||
ParticlePass* particlePass = _engine->_renderPassManager->getPass<ParticlePass>();
|
||||
if (!particlePass) return false;
|
||||
|
||||
return particlePass->resize_system(id, new_count);
|
||||
}
|
||||
|
||||
bool Engine::get_particle_system(uint32_t id, ParticleSystem& out) const
|
||||
{
|
||||
if (!_engine || !_engine->_renderPassManager) return false;
|
||||
|
||||
ParticlePass* particlePass = _engine->_renderPassManager->getPass<ParticlePass>();
|
||||
if (!particlePass) return false;
|
||||
|
||||
const auto& systems = particlePass->systems();
|
||||
for (const auto& sys : systems)
|
||||
{
|
||||
if (sys.id == id)
|
||||
{
|
||||
out.id = sys.id;
|
||||
out.particleCount = sys.count;
|
||||
out.enabled = sys.enabled;
|
||||
out.reset = sys.reset;
|
||||
out.blendMode = static_cast<ParticleBlendMode>(sys.blend);
|
||||
out.flipbookTexture = sys.flipbook_texture;
|
||||
out.noiseTexture = sys.noise_texture;
|
||||
|
||||
// Copy parameters
|
||||
const auto& p = sys.params;
|
||||
out.params.emitterPosLocal = p.emitter_pos_local;
|
||||
out.params.spawnRadius = p.spawn_radius;
|
||||
out.params.emitterDirLocal = p.emitter_dir_local;
|
||||
out.params.coneAngleDegrees = p.cone_angle_degrees;
|
||||
out.params.minSpeed = p.min_speed;
|
||||
out.params.maxSpeed = p.max_speed;
|
||||
out.params.minLife = p.min_life;
|
||||
out.params.maxLife = p.max_life;
|
||||
out.params.minSize = p.min_size;
|
||||
out.params.maxSize = p.max_size;
|
||||
out.params.drag = p.drag;
|
||||
out.params.gravity = p.gravity;
|
||||
out.params.color = p.color;
|
||||
out.params.softDepthDistance = p.soft_depth_distance;
|
||||
out.params.flipbookCols = p.flipbook_cols;
|
||||
out.params.flipbookRows = p.flipbook_rows;
|
||||
out.params.flipbookFps = p.flipbook_fps;
|
||||
out.params.flipbookIntensity = p.flipbook_intensity;
|
||||
out.params.noiseScale = p.noise_scale;
|
||||
out.params.noiseStrength = p.noise_strength;
|
||||
out.params.noiseScroll = p.noise_scroll;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Engine::set_particle_system(uint32_t id, const ParticleSystem& system)
|
||||
{
|
||||
if (!_engine || !_engine->_renderPassManager) return false;
|
||||
|
||||
ParticlePass* particlePass = _engine->_renderPassManager->getPass<ParticlePass>();
|
||||
if (!particlePass) return false;
|
||||
|
||||
auto& systems = particlePass->systems();
|
||||
for (auto& sys : systems)
|
||||
{
|
||||
if (sys.id == id)
|
||||
{
|
||||
sys.enabled = system.enabled;
|
||||
sys.reset = system.reset;
|
||||
sys.blend = static_cast<ParticlePass::BlendMode>(system.blendMode);
|
||||
sys.flipbook_texture = system.flipbookTexture;
|
||||
sys.noise_texture = system.noiseTexture;
|
||||
|
||||
// Copy parameters
|
||||
auto& p = sys.params;
|
||||
p.emitter_pos_local = system.params.emitterPosLocal;
|
||||
p.spawn_radius = system.params.spawnRadius;
|
||||
p.emitter_dir_local = system.params.emitterDirLocal;
|
||||
p.cone_angle_degrees = system.params.coneAngleDegrees;
|
||||
p.min_speed = system.params.minSpeed;
|
||||
p.max_speed = system.params.maxSpeed;
|
||||
p.min_life = system.params.minLife;
|
||||
p.max_life = system.params.maxLife;
|
||||
p.min_size = system.params.minSize;
|
||||
p.max_size = system.params.maxSize;
|
||||
p.drag = system.params.drag;
|
||||
p.gravity = system.params.gravity;
|
||||
p.color = system.params.color;
|
||||
p.soft_depth_distance = system.params.softDepthDistance;
|
||||
p.flipbook_cols = system.params.flipbookCols;
|
||||
p.flipbook_rows = system.params.flipbookRows;
|
||||
p.flipbook_fps = system.params.flipbookFps;
|
||||
p.flipbook_intensity = system.params.flipbookIntensity;
|
||||
p.noise_scale = system.params.noiseScale;
|
||||
p.noise_strength = system.params.noiseStrength;
|
||||
p.noise_scroll = system.params.noiseScroll;
|
||||
|
||||
// Preload textures if changed
|
||||
if (!sys.flipbook_texture.empty())
|
||||
{
|
||||
particlePass->preload_vfx_texture(sys.flipbook_texture);
|
||||
}
|
||||
if (!sys.noise_texture.empty())
|
||||
{
|
||||
particlePass->preload_vfx_texture(sys.noise_texture);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> Engine::get_particle_system_ids() const
|
||||
{
|
||||
std::vector<uint32_t> ids;
|
||||
|
||||
if (!_engine || !_engine->_renderPassManager) return ids;
|
||||
|
||||
ParticlePass* particlePass = _engine->_renderPassManager->getPass<ParticlePass>();
|
||||
if (!particlePass) return ids;
|
||||
|
||||
const auto& systems = particlePass->systems();
|
||||
ids.reserve(systems.size());
|
||||
for (const auto& sys : systems)
|
||||
{
|
||||
ids.push_back(sys.id);
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
uint32_t Engine::get_allocated_particles() const
|
||||
{
|
||||
if (!_engine || !_engine->_renderPassManager) return 0;
|
||||
|
||||
ParticlePass* particlePass = _engine->_renderPassManager->getPass<ParticlePass>();
|
||||
if (!particlePass) return 0;
|
||||
|
||||
return particlePass->allocated_particles();
|
||||
}
|
||||
|
||||
uint32_t Engine::get_free_particles() const
|
||||
{
|
||||
if (!_engine || !_engine->_renderPassManager) return 0;
|
||||
|
||||
ParticlePass* particlePass = _engine->_renderPassManager->getPass<ParticlePass>();
|
||||
if (!particlePass) return 0;
|
||||
|
||||
return particlePass->free_particles();
|
||||
}
|
||||
|
||||
uint32_t Engine::get_max_particles() const
|
||||
{
|
||||
return ParticlePass::k_max_particles;
|
||||
}
|
||||
|
||||
void Engine::preload_particle_texture(const std::string& assetPath)
|
||||
{
|
||||
if (!_engine || !_engine->_renderPassManager) return;
|
||||
|
||||
ParticlePass* particlePass = _engine->_renderPassManager->getPass<ParticlePass>();
|
||||
if (!particlePass) return;
|
||||
|
||||
particlePass->preload_vfx_texture(assetPath);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Picking / Selection
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -42,6 +42,21 @@ enum class TonemapOperator : int
|
||||
ACES = 1
|
||||
};
|
||||
|
||||
// Voxel volume type (cloud/smoke/flame)
|
||||
enum class VoxelVolumeType : uint32_t
|
||||
{
|
||||
Clouds = 0,
|
||||
Smoke = 1,
|
||||
Flame = 2
|
||||
};
|
||||
|
||||
// Particle blend mode
|
||||
enum class ParticleBlendMode : uint32_t
|
||||
{
|
||||
Additive = 0, // Additive blending (for fire, sparks, etc.)
|
||||
Alpha = 1 // Alpha blending with depth sorting
|
||||
};
|
||||
|
||||
// Primitive geometry types
|
||||
enum class PrimitiveType
|
||||
{
|
||||
@@ -107,6 +122,107 @@ struct SpotLightD
|
||||
float outer_angle_deg{25.0f};
|
||||
};
|
||||
|
||||
// Voxel volumetric settings (cloud/smoke/flame)
|
||||
struct VoxelVolumeSettings
|
||||
{
|
||||
bool enabled{false};
|
||||
VoxelVolumeType type{VoxelVolumeType::Clouds};
|
||||
|
||||
// If true, volume follows camera XZ and volumeCenterLocal is treated as offset
|
||||
// If false, volumeCenterLocal is absolute render-local space
|
||||
bool followCameraXZ{false};
|
||||
|
||||
// If true, run voxel advection/update compute pass every frame
|
||||
bool animateVoxels{true};
|
||||
|
||||
// Volume AABB in render-local space
|
||||
glm::vec3 volumeCenterLocal{0.0f, 2.0f, 0.0f};
|
||||
glm::vec3 volumeHalfExtents{8.0f, 8.0f, 8.0f};
|
||||
|
||||
// Optional volume drift (applied only when followCameraXZ == false)
|
||||
glm::vec3 volumeVelocityLocal{0.0f, 0.0f, 0.0f};
|
||||
|
||||
// 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 (add buoyancy here)
|
||||
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-ish)
|
||||
|
||||
// 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};
|
||||
};
|
||||
|
||||
// Particle system parameters
|
||||
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};
|
||||
|
||||
// Fade particles near opaque geometry intersections (0 disables)
|
||||
float softDepthDistance{0.15f};
|
||||
|
||||
// Flipbook sampling (atlas layout and animation)
|
||||
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};
|
||||
};
|
||||
|
||||
// Particle system settings
|
||||
struct ParticleSystem
|
||||
{
|
||||
uint32_t id{0};
|
||||
uint32_t particleCount{0};
|
||||
bool enabled{true};
|
||||
bool reset{true};
|
||||
ParticleBlendMode blendMode{ParticleBlendMode::Additive};
|
||||
ParticleParams params{};
|
||||
|
||||
// Asset-relative texture paths (e.g., "vfx/flame.ktx2")
|
||||
// Empty string disables the texture
|
||||
std::string flipbookTexture{"vfx/flame.ktx2"};
|
||||
std::string noiseTexture{"vfx/simplex.ktx2"};
|
||||
};
|
||||
|
||||
// IBL (Image-Based Lighting) paths
|
||||
struct IBLPaths
|
||||
{
|
||||
@@ -496,6 +612,51 @@ public:
|
||||
|
||||
Stats get_stats() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Volumetrics (Cloud/Smoke/Flame)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// 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;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Particle Systems
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// Create a new particle system (returns system ID, 0 on failure)
|
||||
uint32_t create_particle_system(uint32_t particle_count);
|
||||
|
||||
// Destroy a particle system by ID
|
||||
bool destroy_particle_system(uint32_t id);
|
||||
|
||||
// Resize a particle system (reallocates particle count)
|
||||
bool resize_particle_system(uint32_t id, uint32_t new_count);
|
||||
|
||||
// Get particle system settings by ID
|
||||
bool get_particle_system(uint32_t id, ParticleSystem& out) const;
|
||||
|
||||
// Set particle system settings by ID
|
||||
bool set_particle_system(uint32_t id, const ParticleSystem& system);
|
||||
|
||||
// Get all particle system IDs
|
||||
std::vector<uint32_t> get_particle_system_ids() const;
|
||||
|
||||
// Get particle pool statistics
|
||||
uint32_t get_allocated_particles() const;
|
||||
uint32_t get_free_particles() const;
|
||||
uint32_t get_max_particles() const;
|
||||
|
||||
// Preload a VFX texture (e.g., "vfx/flame.ktx2")
|
||||
void preload_particle_texture(const std::string& assetPath);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Picking / Selection
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@@ -119,21 +119,6 @@ void SceneManager::init(EngineContext *context)
|
||||
sceneData.sunlightDirection = glm::vec4(-0.2f, -1.0f, -0.3f, 1.0f);
|
||||
sceneData.sunlightColor = glm::vec4(1.0f, 1.0f, 1.0f, 3.0f);
|
||||
|
||||
// Seed a couple of default point lights for quick testing.
|
||||
PointLight warmKey{};
|
||||
warmKey.position_world = WorldVec3(0.0, 0.0, 0.0);
|
||||
warmKey.radius = 25.0f;
|
||||
warmKey.color = glm::vec3(1.0f, 0.95f, 0.8f);
|
||||
warmKey.intensity = 15.0f;
|
||||
addPointLight(warmKey);
|
||||
|
||||
PointLight coolFill{};
|
||||
coolFill.position_world = WorldVec3(-10.0, 4.0, 10.0);
|
||||
coolFill.radius = 20.0f;
|
||||
coolFill.color = glm::vec3(0.6f, 0.7f, 1.0f);
|
||||
coolFill.intensity = 10.0f;
|
||||
addPointLight(coolFill);
|
||||
|
||||
_camera_position_local = world_to_local(mainCamera.position_world, _origin_world);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user