ADD: planet quadtree texture boilerplate
This commit is contained in:
@@ -669,6 +669,26 @@ std::shared_ptr<GLTFMaterial> AssetManager::createMaterialFromConstants(
|
|||||||
return createMaterial(pass, res);
|
return createMaterial(pass, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkImageView AssetManager::fallback_checkerboard_view() const
|
||||||
|
{
|
||||||
|
return (_engine) ? _engine->_errorCheckerboardImage.imageView : VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageView AssetManager::fallback_white_view() const
|
||||||
|
{
|
||||||
|
return (_engine) ? _engine->_whiteImage.imageView : VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageView AssetManager::fallback_flat_normal_view() const
|
||||||
|
{
|
||||||
|
return (_engine) ? _engine->_flatNormalImage.imageView : VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageView AssetManager::fallback_black_view() const
|
||||||
|
{
|
||||||
|
return (_engine) ? _engine->_blackImage.imageView : VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<MeshAsset> AssetManager::getMesh(const std::string &name) const
|
std::shared_ptr<MeshAsset> AssetManager::getMesh(const std::string &name) const
|
||||||
{
|
{
|
||||||
auto it = _meshCache.find(name);
|
auto it = _meshCache.find(name);
|
||||||
|
|||||||
@@ -120,6 +120,12 @@ public:
|
|||||||
const GLTFMetallic_Roughness::MaterialConstants &constants,
|
const GLTFMetallic_Roughness::MaterialConstants &constants,
|
||||||
MaterialPass pass = MaterialPass::MainColor);
|
MaterialPass pass = MaterialPass::MainColor);
|
||||||
|
|
||||||
|
// Access engine-provided fallback textures for procedural systems.
|
||||||
|
VkImageView fallback_checkerboard_view() const;
|
||||||
|
VkImageView fallback_white_view() const;
|
||||||
|
VkImageView fallback_flat_normal_view() const;
|
||||||
|
VkImageView fallback_black_view() const;
|
||||||
|
|
||||||
const AssetPaths &paths() const { return _locator.paths(); }
|
const AssetPaths &paths() const { return _locator.paths(); }
|
||||||
void setPaths(const AssetPaths &p) { _locator.setPaths(p); }
|
void setPaths(const AssetPaths &p) { _locator.setPaths(p); }
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ void SamplerManager::init(DeviceManager *deviceManager)
|
|||||||
sampl.minFilter = VK_FILTER_LINEAR;
|
sampl.minFilter = VK_FILTER_LINEAR;
|
||||||
vkCreateSampler(_deviceManager->device(), &sampl, nullptr, &_defaultSamplerLinear);
|
vkCreateSampler(_deviceManager->device(), &sampl, nullptr, &_defaultSamplerLinear);
|
||||||
|
|
||||||
|
// Linear clamp-to-edge (useful for tiled textures)
|
||||||
|
VkSamplerCreateInfo clampEdge = sampl;
|
||||||
|
clampEdge.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||||
|
clampEdge.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||||
|
clampEdge.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||||
|
vkCreateSampler(_deviceManager->device(), &clampEdge, nullptr, &_linearClampEdge);
|
||||||
|
|
||||||
// Shadow linear clamp sampler (border=white)
|
// Shadow linear clamp sampler (border=white)
|
||||||
VkSamplerCreateInfo sh = sampl;
|
VkSamplerCreateInfo sh = sampl;
|
||||||
sh.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
sh.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||||
@@ -60,4 +67,10 @@ void SamplerManager::cleanup()
|
|||||||
vkDestroySampler(_deviceManager->device(), _shadowLinearClamp, nullptr);
|
vkDestroySampler(_deviceManager->device(), _shadowLinearClamp, nullptr);
|
||||||
_shadowLinearClamp = VK_NULL_HANDLE;
|
_shadowLinearClamp = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_linearClampEdge)
|
||||||
|
{
|
||||||
|
vkDestroySampler(_deviceManager->device(), _linearClampEdge, nullptr);
|
||||||
|
_linearClampEdge = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public:
|
|||||||
VkSampler defaultLinear() const { return _defaultSamplerLinear; }
|
VkSampler defaultLinear() const { return _defaultSamplerLinear; }
|
||||||
VkSampler defaultNearest() const { return _defaultSamplerNearest; }
|
VkSampler defaultNearest() const { return _defaultSamplerNearest; }
|
||||||
VkSampler shadowLinearClamp() const { return _shadowLinearClamp; }
|
VkSampler shadowLinearClamp() const { return _shadowLinearClamp; }
|
||||||
|
VkSampler linearClampEdge() const { return _linearClampEdge; }
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -21,4 +22,5 @@ private:
|
|||||||
VkSampler _defaultSamplerLinear = VK_NULL_HANDLE;
|
VkSampler _defaultSamplerLinear = VK_NULL_HANDLE;
|
||||||
VkSampler _defaultSamplerNearest = VK_NULL_HANDLE;
|
VkSampler _defaultSamplerNearest = VK_NULL_HANDLE;
|
||||||
VkSampler _shadowLinearClamp = VK_NULL_HANDLE;
|
VkSampler _shadowLinearClamp = VK_NULL_HANDLE;
|
||||||
|
VkSampler _linearClampEdge = VK_NULL_HANDLE;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <core/assets/manager.h>
|
#include <core/assets/manager.h>
|
||||||
#include <render/materials.h>
|
#include <render/materials.h>
|
||||||
#include <render/primitives.h>
|
#include <render/primitives.h>
|
||||||
|
#include <core/pipeline/sampler.h>
|
||||||
#include <scene/planet/cubesphere.h>
|
#include <scene/planet/cubesphere.h>
|
||||||
#include <scene/tangent_space.h>
|
#include <scene/tangent_space.h>
|
||||||
#include <scene/vk_scene.h>
|
#include <scene/vk_scene.h>
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
|
|
||||||
@@ -77,6 +79,79 @@ void PlanetSystem::init(EngineContext *context)
|
|||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlanetSystem::cleanup()
|
||||||
|
{
|
||||||
|
if (!_context)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureCache *textures = _context->textures;
|
||||||
|
if (textures)
|
||||||
|
{
|
||||||
|
for (EarthPatch &p : _earth_patches)
|
||||||
|
{
|
||||||
|
if (p.material_instance.materialSet != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
textures->unwatchSet(p.material_instance.materialSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceManager *rm = _context->getResources();
|
||||||
|
if (rm)
|
||||||
|
{
|
||||||
|
for (EarthPatch &p : _earth_patches)
|
||||||
|
{
|
||||||
|
if (p.vertex_buffer.buffer != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
rm->destroy_buffer(p.vertex_buffer);
|
||||||
|
p.vertex_buffer = {};
|
||||||
|
p.vertex_buffer_address = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_earth_patch_index_buffer.buffer != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
rm->destroy_buffer(_earth_patch_index_buffer);
|
||||||
|
_earth_patch_index_buffer = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_earth_patch_material_constants_buffer.buffer != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
rm->destroy_buffer(_earth_patch_material_constants_buffer);
|
||||||
|
_earth_patch_material_constants_buffer = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_earth_patch_material_allocator_initialized)
|
||||||
|
{
|
||||||
|
if (DeviceManager *device = _context->getDevice())
|
||||||
|
{
|
||||||
|
_earth_patch_material_allocator.destroy_pools(device->device());
|
||||||
|
}
|
||||||
|
_earth_patch_material_allocator_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_earth_patch_material_layout != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
if (DeviceManager *device = _context->getDevice())
|
||||||
|
{
|
||||||
|
vkDestroyDescriptorSetLayout(device->device(), _earth_patch_material_layout, nullptr);
|
||||||
|
}
|
||||||
|
_earth_patch_material_layout = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
_earth_patch_lookup.clear();
|
||||||
|
_earth_patch_lru.clear();
|
||||||
|
_earth_patch_free.clear();
|
||||||
|
_earth_patches.clear();
|
||||||
|
|
||||||
|
_earth_patch_index_count = 0;
|
||||||
|
_earth_patch_index_resolution = 0;
|
||||||
|
_earth_patch_frame_stamp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
const PlanetSystem::PlanetBody *PlanetSystem::get_body(BodyID id) const
|
const PlanetSystem::PlanetBody *PlanetSystem::get_body(BodyID id) const
|
||||||
{
|
{
|
||||||
size_t i = static_cast<size_t>(id);
|
size_t i = static_cast<size_t>(id);
|
||||||
@@ -182,6 +257,7 @@ void PlanetSystem::ensure_earth_patch_index_buffer()
|
|||||||
if (_earth_patch_index_buffer.buffer != VK_NULL_HANDLE)
|
if (_earth_patch_index_buffer.buffer != VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
FrameResources *frame = _context->currentFrame;
|
FrameResources *frame = _context->currentFrame;
|
||||||
|
TextureCache *textures = _context->textures;
|
||||||
|
|
||||||
// Destroy per-patch vertex buffers.
|
// Destroy per-patch vertex buffers.
|
||||||
for (const auto &kv : _earth_patch_lookup)
|
for (const auto &kv : _earth_patch_lookup)
|
||||||
@@ -210,8 +286,28 @@ void PlanetSystem::ensure_earth_patch_index_buffer()
|
|||||||
|
|
||||||
_earth_patch_lookup.clear();
|
_earth_patch_lookup.clear();
|
||||||
_earth_patch_lru.clear();
|
_earth_patch_lru.clear();
|
||||||
|
|
||||||
|
if (textures)
|
||||||
|
{
|
||||||
|
for (EarthPatch &p : _earth_patches)
|
||||||
|
{
|
||||||
|
if (p.material_instance.materialSet != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
textures->unwatchSet(p.material_instance.materialSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_earth_patch_free.clear();
|
_earth_patch_free.clear();
|
||||||
_earth_patches.clear();
|
_earth_patch_free.reserve(_earth_patches.size());
|
||||||
|
for (uint32_t idx = 0; idx < static_cast<uint32_t>(_earth_patches.size()); ++idx)
|
||||||
|
{
|
||||||
|
EarthPatch &p = _earth_patches[idx];
|
||||||
|
const VkDescriptorSet keep_set = p.material_instance.materialSet;
|
||||||
|
p = EarthPatch{};
|
||||||
|
p.material_instance.materialSet = keep_set;
|
||||||
|
_earth_patch_free.push_back(idx);
|
||||||
|
}
|
||||||
|
|
||||||
const AllocatedBuffer ib = _earth_patch_index_buffer;
|
const AllocatedBuffer ib = _earth_patch_index_buffer;
|
||||||
if (frame)
|
if (frame)
|
||||||
@@ -239,6 +335,227 @@ void PlanetSystem::ensure_earth_patch_index_buffer()
|
|||||||
_earth_patch_index_resolution = _earth_patch_resolution;
|
_earth_patch_index_resolution = _earth_patch_resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlanetSystem::ensure_earth_patch_material_layout()
|
||||||
|
{
|
||||||
|
if (_earth_patch_material_layout != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_context)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceManager *device = _context->getDevice();
|
||||||
|
if (!device)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorLayoutBuilder layoutBuilder;
|
||||||
|
layoutBuilder.add_binding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||||
|
layoutBuilder.add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
layoutBuilder.add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
layoutBuilder.add_binding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
layoutBuilder.add_binding(4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
layoutBuilder.add_binding(5, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
|
||||||
|
_earth_patch_material_layout = layoutBuilder.build(device->device(),
|
||||||
|
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
|
nullptr,
|
||||||
|
VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlanetSystem::ensure_earth_patch_material_constants_buffer()
|
||||||
|
{
|
||||||
|
if (_earth_patch_material_constants_buffer.buffer != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_context)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceManager *rm = _context->getResources();
|
||||||
|
DeviceManager *device = _context->getDevice();
|
||||||
|
if (!rm || !device)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GLTFMetallic_Roughness::MaterialConstants constants = make_planet_constants();
|
||||||
|
|
||||||
|
_earth_patch_material_constants_buffer =
|
||||||
|
rm->create_buffer(sizeof(GLTFMetallic_Roughness::MaterialConstants),
|
||||||
|
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||||
|
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||||
|
|
||||||
|
if (_earth_patch_material_constants_buffer.buffer == VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VmaAllocationInfo allocInfo{};
|
||||||
|
vmaGetAllocationInfo(device->allocator(), _earth_patch_material_constants_buffer.allocation, &allocInfo);
|
||||||
|
auto *mapped = static_cast<GLTFMetallic_Roughness::MaterialConstants *>(allocInfo.pMappedData);
|
||||||
|
if (mapped)
|
||||||
|
{
|
||||||
|
*mapped = constants;
|
||||||
|
vmaFlushAllocation(device->allocator(), _earth_patch_material_constants_buffer.allocation, 0, sizeof(constants));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlanetSystem::ensure_earth_patch_material_instance(EarthPatch &patch, const PlanetBody &earth)
|
||||||
|
{
|
||||||
|
if (!_context || !earth.material)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceManager *device = _context->getDevice();
|
||||||
|
SamplerManager *samplers = _context->getSamplers();
|
||||||
|
AssetManager *assets = _context->assets;
|
||||||
|
if (!device || !assets)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_earth_patch_material_layout();
|
||||||
|
ensure_earth_patch_material_constants_buffer();
|
||||||
|
|
||||||
|
if (_earth_patch_material_layout == VK_NULL_HANDLE ||
|
||||||
|
_earth_patch_material_constants_buffer.buffer == VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_earth_patch_material_allocator_initialized)
|
||||||
|
{
|
||||||
|
std::vector<DescriptorAllocatorGrowable::PoolSizeRatio> sizes = {
|
||||||
|
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1},
|
||||||
|
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6},
|
||||||
|
};
|
||||||
|
_earth_patch_material_allocator.init(device->device(), 128, sizes);
|
||||||
|
_earth_patch_material_allocator_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patch.material_instance.materialSet == VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
patch.material_instance.materialSet =
|
||||||
|
_earth_patch_material_allocator.allocate(device->device(), _earth_patch_material_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
patch.material_instance.pipeline = earth.material->data.pipeline;
|
||||||
|
patch.material_instance.passType = earth.material->data.passType;
|
||||||
|
|
||||||
|
VkSampler tileSampler = samplers ? samplers->linearClampEdge() : VK_NULL_HANDLE;
|
||||||
|
if (tileSampler == VK_NULL_HANDLE && samplers)
|
||||||
|
{
|
||||||
|
tileSampler = samplers->defaultLinear();
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageView checker = assets->fallback_checkerboard_view();
|
||||||
|
VkImageView white = assets->fallback_white_view();
|
||||||
|
VkImageView flatNormal = assets->fallback_flat_normal_view();
|
||||||
|
VkImageView black = assets->fallback_black_view();
|
||||||
|
|
||||||
|
if (checker == VK_NULL_HANDLE) checker = white;
|
||||||
|
if (white == VK_NULL_HANDLE) white = checker;
|
||||||
|
if (flatNormal == VK_NULL_HANDLE) flatNormal = white;
|
||||||
|
if (black == VK_NULL_HANDLE) black = white;
|
||||||
|
|
||||||
|
if (patch.material_instance.materialSet != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
DescriptorWriter writer;
|
||||||
|
writer.write_buffer(0,
|
||||||
|
_earth_patch_material_constants_buffer.buffer,
|
||||||
|
sizeof(GLTFMetallic_Roughness::MaterialConstants),
|
||||||
|
0,
|
||||||
|
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||||
|
writer.write_image(1,
|
||||||
|
checker,
|
||||||
|
tileSampler,
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
|
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
writer.write_image(2,
|
||||||
|
white,
|
||||||
|
tileSampler,
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
|
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
writer.write_image(3,
|
||||||
|
flatNormal,
|
||||||
|
tileSampler,
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
|
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
writer.write_image(4,
|
||||||
|
white,
|
||||||
|
tileSampler,
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
|
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
writer.write_image(5,
|
||||||
|
black,
|
||||||
|
tileSampler,
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
|
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
writer.update_set(device->device(), patch.material_instance.materialSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-patch tiled textures via TextureCache (albedo only for now).
|
||||||
|
if (_context->textures && tileSampler != VK_NULL_HANDLE && patch.material_instance.materialSet != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
auto face_legacy = [](planet::CubeFace f) -> const char * {
|
||||||
|
switch (f)
|
||||||
|
{
|
||||||
|
case planet::CubeFace::PosX: return "px";
|
||||||
|
case planet::CubeFace::NegX: return "nx";
|
||||||
|
case planet::CubeFace::PosY: return "py";
|
||||||
|
case planet::CubeFace::NegY: return "ny";
|
||||||
|
case planet::CubeFace::PosZ: return "pz";
|
||||||
|
case planet::CubeFace::NegZ: return "nz";
|
||||||
|
}
|
||||||
|
return "px";
|
||||||
|
};
|
||||||
|
|
||||||
|
const planet::PatchKey &k = patch.key;
|
||||||
|
const uint32_t face_index = static_cast<uint32_t>(k.face);
|
||||||
|
|
||||||
|
std::vector<std::string> candidates;
|
||||||
|
candidates.reserve(2);
|
||||||
|
candidates.push_back(fmt::format("planets/earth/albedo/face{}/L{}/X{}_Y{}.ktx2", face_index, k.level, k.x, k.y));
|
||||||
|
if (k.level == 0u && k.x == 0u && k.y == 0u)
|
||||||
|
{
|
||||||
|
candidates.push_back(fmt::format("planets/earth/albedo/L0/{}.ktx2", face_legacy(k.face)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string resolved_path;
|
||||||
|
for (const std::string &rel : candidates)
|
||||||
|
{
|
||||||
|
std::string abs = assets->assetPath(rel);
|
||||||
|
std::error_code ec;
|
||||||
|
if (!abs.empty() && std::filesystem::exists(abs, ec) && !ec)
|
||||||
|
{
|
||||||
|
resolved_path = std::move(abs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resolved_path.empty())
|
||||||
|
{
|
||||||
|
TextureCache::TextureKey tk{};
|
||||||
|
tk.kind = TextureCache::TextureKey::SourceKind::FilePath;
|
||||||
|
tk.path = resolved_path;
|
||||||
|
tk.srgb = true;
|
||||||
|
tk.mipmapped = true;
|
||||||
|
|
||||||
|
TextureCache::TextureHandle h = _context->textures->request(tk, tileSampler);
|
||||||
|
_context->textures->watchBinding(h, patch.material_instance.materialSet, 1u, tileSampler, checker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PlanetSystem::EarthPatch *PlanetSystem::get_or_create_earth_patch(const PlanetBody &earth,
|
PlanetSystem::EarthPatch *PlanetSystem::get_or_create_earth_patch(const PlanetBody &earth,
|
||||||
const planet::PatchKey &key,
|
const planet::PatchKey &key,
|
||||||
uint32_t frame_index)
|
uint32_t frame_index)
|
||||||
@@ -330,6 +647,8 @@ PlanetSystem::EarthPatch *PlanetSystem::get_or_create_earth_patch(const PlanetBo
|
|||||||
_earth_patch_lru.push_front(idx);
|
_earth_patch_lru.push_front(idx);
|
||||||
p.lru_it = _earth_patch_lru.begin();
|
p.lru_it = _earth_patch_lru.begin();
|
||||||
|
|
||||||
|
ensure_earth_patch_material_instance(p, earth);
|
||||||
|
|
||||||
_earth_patch_lookup.emplace(key, idx);
|
_earth_patch_lookup.emplace(key, idx);
|
||||||
return &p;
|
return &p;
|
||||||
}
|
}
|
||||||
@@ -358,6 +677,7 @@ void PlanetSystem::trim_earth_patch_cache()
|
|||||||
}
|
}
|
||||||
|
|
||||||
FrameResources *frame = _context->currentFrame;
|
FrameResources *frame = _context->currentFrame;
|
||||||
|
TextureCache *textures = _context->textures;
|
||||||
const uint32_t now = _earth_patch_frame_stamp;
|
const uint32_t now = _earth_patch_frame_stamp;
|
||||||
|
|
||||||
size_t guard = 0;
|
size_t guard = 0;
|
||||||
@@ -392,6 +712,11 @@ void PlanetSystem::trim_earth_patch_cache()
|
|||||||
_earth_patch_lru.erase(p.lru_it);
|
_earth_patch_lru.erase(p.lru_it);
|
||||||
_earth_patch_lookup.erase(p.key);
|
_earth_patch_lookup.erase(p.key);
|
||||||
|
|
||||||
|
if (textures && p.material_instance.materialSet != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
textures->unwatchSet(p.material_instance.materialSet);
|
||||||
|
}
|
||||||
|
|
||||||
if (p.vertex_buffer.buffer != VK_NULL_HANDLE)
|
if (p.vertex_buffer.buffer != VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
const AllocatedBuffer vb = p.vertex_buffer;
|
const AllocatedBuffer vb = p.vertex_buffer;
|
||||||
@@ -405,7 +730,9 @@ void PlanetSystem::trim_earth_patch_cache()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VkDescriptorSet keep_set = p.material_instance.materialSet;
|
||||||
p = EarthPatch{};
|
p = EarthPatch{};
|
||||||
|
p.material_instance.materialSet = keep_set;
|
||||||
_earth_patch_free.push_back(idx);
|
_earth_patch_free.push_back(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -446,6 +773,23 @@ void PlanetSystem::update_and_emit(const SceneManager &scene, DrawContext &draw_
|
|||||||
|
|
||||||
ensure_earth_patch_index_buffer();
|
ensure_earth_patch_index_buffer();
|
||||||
|
|
||||||
|
size_t desired_capacity =
|
||||||
|
static_cast<size_t>(_earth_patches.size()) +
|
||||||
|
static_cast<size_t>(_earth_patch_create_budget_per_frame) +
|
||||||
|
32u;
|
||||||
|
if (_earth_patch_cache_max != 0)
|
||||||
|
{
|
||||||
|
desired_capacity = std::max(
|
||||||
|
desired_capacity,
|
||||||
|
static_cast<size_t>(_earth_patch_cache_max) +
|
||||||
|
static_cast<size_t>(_earth_patch_create_budget_per_frame) +
|
||||||
|
32u);
|
||||||
|
}
|
||||||
|
if (_earth_patches.capacity() < desired_capacity)
|
||||||
|
{
|
||||||
|
_earth_patches.reserve(desired_capacity);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t created_patches = 0;
|
uint32_t created_patches = 0;
|
||||||
double ms_patch_create = 0.0;
|
double ms_patch_create = 0.0;
|
||||||
const uint32_t max_create = _earth_patch_create_budget_per_frame;
|
const uint32_t max_create = _earth_patch_create_budget_per_frame;
|
||||||
@@ -454,6 +798,9 @@ void PlanetSystem::update_and_emit(const SceneManager &scene, DrawContext &draw_
|
|||||||
const uint32_t frame_index = ++_earth_patch_frame_stamp;
|
const uint32_t frame_index = ++_earth_patch_frame_stamp;
|
||||||
|
|
||||||
const Clock::time_point t_emit0 = Clock::now();
|
const Clock::time_point t_emit0 = Clock::now();
|
||||||
|
std::vector<uint32_t> ready_patch_indices;
|
||||||
|
ready_patch_indices.reserve(_earth_quadtree.visible_leaves().size());
|
||||||
|
|
||||||
for (const planet::PatchKey &k : _earth_quadtree.visible_leaves())
|
for (const planet::PatchKey &k : _earth_quadtree.visible_leaves())
|
||||||
{
|
{
|
||||||
EarthPatch *patch = find_earth_patch(k);
|
EarthPatch *patch = find_earth_patch(k);
|
||||||
@@ -462,6 +809,10 @@ void PlanetSystem::update_and_emit(const SceneManager &scene, DrawContext &draw_
|
|||||||
{
|
{
|
||||||
patch->last_used_frame = frame_index;
|
patch->last_used_frame = frame_index;
|
||||||
_earth_patch_lru.splice(_earth_patch_lru.begin(), _earth_patch_lru, patch->lru_it);
|
_earth_patch_lru.splice(_earth_patch_lru.begin(), _earth_patch_lru, patch->lru_it);
|
||||||
|
if (patch->material_instance.materialSet == VK_NULL_HANDLE || patch->material_instance.pipeline == nullptr)
|
||||||
|
{
|
||||||
|
ensure_earth_patch_material_instance(*patch, *earth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -481,10 +832,26 @@ void PlanetSystem::update_and_emit(const SceneManager &scene, DrawContext &draw_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!patch ||
|
if (patch)
|
||||||
patch->state != EarthPatchState::Ready ||
|
{
|
||||||
patch->vertex_buffer.buffer == VK_NULL_HANDLE ||
|
const uint32_t idx = static_cast<uint32_t>(patch - _earth_patches.data());
|
||||||
patch->vertex_buffer_address == 0 ||
|
ready_patch_indices.push_back(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t idx : ready_patch_indices)
|
||||||
|
{
|
||||||
|
if (idx >= _earth_patches.size())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
EarthPatch &patch = _earth_patches[idx];
|
||||||
|
if (patch.state != EarthPatchState::Ready ||
|
||||||
|
patch.vertex_buffer.buffer == VK_NULL_HANDLE ||
|
||||||
|
patch.vertex_buffer_address == 0 ||
|
||||||
|
patch.material_instance.materialSet == VK_NULL_HANDLE ||
|
||||||
|
patch.material_instance.pipeline == nullptr ||
|
||||||
_earth_patch_index_buffer.buffer == VK_NULL_HANDLE ||
|
_earth_patch_index_buffer.buffer == VK_NULL_HANDLE ||
|
||||||
_earth_patch_index_count == 0)
|
_earth_patch_index_count == 0)
|
||||||
{
|
{
|
||||||
@@ -492,23 +859,23 @@ void PlanetSystem::update_and_emit(const SceneManager &scene, DrawContext &draw_
|
|||||||
}
|
}
|
||||||
|
|
||||||
const WorldVec3 patch_center_world =
|
const WorldVec3 patch_center_world =
|
||||||
earth->center_world + patch->patch_center_dir * earth->radius_m;
|
earth->center_world + patch.patch_center_dir * earth->radius_m;
|
||||||
const glm::vec3 patch_center_local = world_to_local(patch_center_world, origin_world);
|
const glm::vec3 patch_center_local = world_to_local(patch_center_world, origin_world);
|
||||||
const glm::mat4 transform = glm::translate(glm::mat4(1.0f), patch_center_local);
|
const glm::mat4 transform = glm::translate(glm::mat4(1.0f), patch_center_local);
|
||||||
|
|
||||||
Bounds b{};
|
Bounds b{};
|
||||||
b.origin = patch->bounds_origin;
|
b.origin = patch.bounds_origin;
|
||||||
b.extents = patch->bounds_extents;
|
b.extents = patch.bounds_extents;
|
||||||
b.sphereRadius = patch->bounds_sphere_radius;
|
b.sphereRadius = patch.bounds_sphere_radius;
|
||||||
b.type = BoundsType::Box;
|
b.type = BoundsType::Box;
|
||||||
|
|
||||||
RenderObject obj{};
|
RenderObject obj{};
|
||||||
obj.indexCount = _earth_patch_index_count;
|
obj.indexCount = _earth_patch_index_count;
|
||||||
obj.firstIndex = 0;
|
obj.firstIndex = 0;
|
||||||
obj.indexBuffer = _earth_patch_index_buffer.buffer;
|
obj.indexBuffer = _earth_patch_index_buffer.buffer;
|
||||||
obj.vertexBuffer = patch->vertex_buffer.buffer;
|
obj.vertexBuffer = patch.vertex_buffer.buffer;
|
||||||
obj.vertexBufferAddress = patch->vertex_buffer_address;
|
obj.vertexBufferAddress = patch.vertex_buffer_address;
|
||||||
obj.material = earth->material ? &earth->material->data : nullptr;
|
obj.material = &patch.material_instance;
|
||||||
obj.bounds = b;
|
obj.bounds = b;
|
||||||
obj.transform = transform;
|
obj.transform = transform;
|
||||||
// Planet terrain patches are not meaningful RT occluders; skip BLAS/TLAS builds.
|
// Planet terrain patches are not meaningful RT occluders; skip BLAS/TLAS builds.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <core/world.h>
|
#include <core/world.h>
|
||||||
|
#include <core/descriptor/descriptors.h>
|
||||||
#include <scene/planet/planet_quadtree.h>
|
#include <scene/planet/planet_quadtree.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -50,6 +51,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void init(EngineContext *context);
|
void init(EngineContext *context);
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
void update_and_emit(const SceneManager &scene, DrawContext &draw_context);
|
void update_and_emit(const SceneManager &scene, DrawContext &draw_context);
|
||||||
|
|
||||||
@@ -88,6 +90,8 @@ private:
|
|||||||
AllocatedBuffer vertex_buffer{};
|
AllocatedBuffer vertex_buffer{};
|
||||||
VkDeviceAddress vertex_buffer_address = 0;
|
VkDeviceAddress vertex_buffer_address = 0;
|
||||||
|
|
||||||
|
MaterialInstance material_instance{};
|
||||||
|
|
||||||
glm::vec3 bounds_origin{0.0f};
|
glm::vec3 bounds_origin{0.0f};
|
||||||
glm::vec3 bounds_extents{0.5f};
|
glm::vec3 bounds_extents{0.5f};
|
||||||
float bounds_sphere_radius = 0.5f;
|
float bounds_sphere_radius = 0.5f;
|
||||||
@@ -103,13 +107,16 @@ private:
|
|||||||
const planet::PatchKey &key,
|
const planet::PatchKey &key,
|
||||||
uint32_t frame_index);
|
uint32_t frame_index);
|
||||||
void ensure_earth_patch_index_buffer();
|
void ensure_earth_patch_index_buffer();
|
||||||
|
void ensure_earth_patch_material_layout();
|
||||||
|
void ensure_earth_patch_material_constants_buffer();
|
||||||
|
void ensure_earth_patch_material_instance(EarthPatch &patch, const PlanetBody &earth);
|
||||||
void trim_earth_patch_cache();
|
void trim_earth_patch_cache();
|
||||||
|
|
||||||
EngineContext *_context = nullptr;
|
EngineContext *_context = nullptr;
|
||||||
bool _enabled = true;
|
bool _enabled = true;
|
||||||
std::vector<PlanetBody> _bodies;
|
std::vector<PlanetBody> _bodies;
|
||||||
|
|
||||||
// Earth cube-sphere quadtree (Milestone B4).
|
// Earth cube-sphere quadtree
|
||||||
planet::PlanetQuadtree _earth_quadtree{};
|
planet::PlanetQuadtree _earth_quadtree{};
|
||||||
planet::PlanetQuadtree::Settings _earth_quadtree_settings{};
|
planet::PlanetQuadtree::Settings _earth_quadtree_settings{};
|
||||||
EarthDebugStats _earth_debug_stats{};
|
EarthDebugStats _earth_debug_stats{};
|
||||||
@@ -120,6 +127,12 @@ private:
|
|||||||
AllocatedBuffer _earth_patch_index_buffer{};
|
AllocatedBuffer _earth_patch_index_buffer{};
|
||||||
uint32_t _earth_patch_index_count = 0;
|
uint32_t _earth_patch_index_count = 0;
|
||||||
uint32_t _earth_patch_index_resolution = 0;
|
uint32_t _earth_patch_index_resolution = 0;
|
||||||
|
|
||||||
|
VkDescriptorSetLayout _earth_patch_material_layout = VK_NULL_HANDLE;
|
||||||
|
DescriptorAllocatorGrowable _earth_patch_material_allocator{};
|
||||||
|
bool _earth_patch_material_allocator_initialized = false;
|
||||||
|
AllocatedBuffer _earth_patch_material_constants_buffer{};
|
||||||
|
|
||||||
uint32_t _earth_patch_frame_stamp = 0;
|
uint32_t _earth_patch_frame_stamp = 0;
|
||||||
uint32_t _earth_patch_resolution = 33;
|
uint32_t _earth_patch_resolution = 33;
|
||||||
uint32_t _earth_patch_create_budget_per_frame = 16;
|
uint32_t _earth_patch_create_budget_per_frame = 16;
|
||||||
|
|||||||
@@ -527,6 +527,12 @@ std::shared_ptr<LoadedGLTF> SceneManager::getScene(const std::string &name)
|
|||||||
|
|
||||||
void SceneManager::cleanup()
|
void SceneManager::cleanup()
|
||||||
{
|
{
|
||||||
|
if (_planetSystem)
|
||||||
|
{
|
||||||
|
_planetSystem->cleanup();
|
||||||
|
_planetSystem.reset();
|
||||||
|
}
|
||||||
|
|
||||||
// Explicitly clear dynamic instances first to drop any extra shared_ptrs
|
// Explicitly clear dynamic instances first to drop any extra shared_ptrs
|
||||||
// that could keep GPU resources alive.
|
// that could keep GPU resources alive.
|
||||||
clearMeshInstances();
|
clearMeshInstances();
|
||||||
|
|||||||
Reference in New Issue
Block a user