|
27 | 27 | #include "graphics/mesh/animation.h" |
28 | 28 | #include "graphics/mesh/attachbinder.h" |
29 | 29 | #include "graphics/material.h" |
| 30 | +#include "graphics/shaders.h" |
| 31 | +#include "utils/workers.h" |
| 32 | +#include "world/worlddata.h" |
| 33 | +#include <future> |
| 34 | +#include <stdexcept> |
30 | 35 | #include "dmusic/directmusic.h" |
31 | 36 | #include "utils/fileext.h" |
32 | 37 | #include "utils/gthfont.h" |
@@ -122,11 +127,15 @@ Resources::Resources(Tempest::Device &device) |
122 | 127 | } |
123 | 128 |
|
124 | 129 | void Resources::mountWork(const std::filesystem::path& path) { |
| 130 | + std::lock_guard<std::mutex> guard(inst->syncWorld); |
| 131 | + inst->worldCache.reset(); |
125 | 132 | inst->gothicAssets.mkdir("/_work"); |
126 | 133 | inst->gothicAssets.mount_host(path, "/_work", zenkit::VfsOverwriteBehavior::NONE); |
127 | 134 | } |
128 | 135 |
|
129 | 136 | void Resources::loadVdfs(const std::vector<std::u16string>& modvdfs, bool modFilter) { |
| 137 | + std::lock_guard<std::mutex> guard(inst->syncWorld); |
| 138 | + inst->worldCache.reset(); |
130 | 139 | std::vector<Archive> archives; |
131 | 140 | inst->detectVdf(archives,Gothic::nestedPath({u"Data"},Dir::FT_Dir)); |
132 | 141 |
|
@@ -310,6 +319,69 @@ const zenkit::Vfs& Resources::vdfsIndex() { |
310 | 319 | return inst->gothicAssets; |
311 | 320 | } |
312 | 321 |
|
| 322 | +std::shared_ptr<const WorldData> Resources::loadWorld(std::string_view name, zenkit::GameVersion version) { |
| 323 | + std::lock_guard<std::mutex> guard(inst->syncWorld); |
| 324 | + const auto* source = vdfsIndex().find(name); |
| 325 | + if(source==nullptr) |
| 326 | + throw std::runtime_error("unable to open Zen-file: " + std::string(name)); |
| 327 | + const auto budget = size_t(std::clamp(Gothic::settingsGetI("ENGINE", "worldCacheMiB"),0,1024))*1024*1024; |
| 328 | + const bool softwareRayTracing = Shaders::options().doSoftwareRT; |
| 329 | + auto& cache = inst->worldCache; |
| 330 | + if(cache && cache->source==source && cache->version==version && |
| 331 | + cache->softwareRayTracing==softwareRayTracing && cache->cacheBytes<=budget) { |
| 332 | + return cache; |
| 333 | + } |
| 334 | + // Only one level is retained. Release it before preparing a different level. |
| 335 | + cache.reset(); |
| 336 | + auto data = std::make_shared<WorldData>(); |
| 337 | + data->source = source; |
| 338 | + data->version = version; |
| 339 | + data->softwareRayTracing = softwareRayTracing; |
| 340 | + auto input = source->open_read(); |
| 341 | + data->world.load(input.get(),version); |
| 342 | + |
| 343 | + auto physics = std::async(std::launch::async, [&] { |
| 344 | + Workers::setThreadName("Loading: BVH thread"); |
| 345 | + return DynamicWorld::buildLandscape(data->world.world_mesh); |
| 346 | + }); |
| 347 | + data->visual = std::make_unique<PackedMesh>(data->world.world_mesh,PackedMesh::PK_VisualLnd); |
| 348 | + data->landscape = physics.get(); |
| 349 | + // Discard the raw mesh after both prepared representations are ready. |
| 350 | + data->world.world_mesh = zenkit::Mesh(); |
| 351 | + auto& mesh = *data->visual; |
| 352 | + auto bytes = [](const auto& values) { return values.capacity()*sizeof(values[0]); }; |
| 353 | + auto& bsp = data->world.world_bsp_tree; |
| 354 | + decltype(bsp.polygon_indices)().swap(bsp.polygon_indices); |
| 355 | + decltype(bsp.leaf_polygons)().swap(bsp.leaf_polygons); |
| 356 | + decltype(bsp.light_points)().swap(bsp.light_points); |
| 357 | + decltype(bsp.portal_polygon_indices)().swap(bsp.portal_polygon_indices); |
| 358 | + std::vector<const zenkit::VirtualObject*> pending; |
| 359 | + for(const auto& vob:data->world.world_vobs) |
| 360 | + pending.push_back(vob.get()); |
| 361 | + size_t vobCount = 0; |
| 362 | + while(!pending.empty()) { |
| 363 | + const auto* vob = pending.back(); |
| 364 | + pending.pop_back(); |
| 365 | + ++vobCount; |
| 366 | + for(const auto& child:vob->children) |
| 367 | + pending.push_back(child.get()); |
| 368 | + } |
| 369 | + // Allow an additional KiB per vob for derived descriptors, strings and child lists. |
| 370 | + // This is a cache estimate, not an allocator-level RAM limit. |
| 371 | + size_t metadataBytes = vobCount*(sizeof(zenkit::VirtualObject)+1024) + bytes(bsp.nodes) + bytes(bsp.leaf_node_indices) + bytes(bsp.sectors); |
| 372 | + for(const auto& sector:bsp.sectors) |
| 373 | + metadataBytes += sector.name.capacity() + bytes(sector.node_indices) + bytes(sector.portal_polygon_indices); |
| 374 | + if(data->world.way_net) |
| 375 | + metadataBytes += data->world.way_net->points.size()*512 + bytes(data->world.way_net->edges); |
| 376 | + const auto visualBytes = bytes(mesh.vertices) + bytes(mesh.verticesA) + bytes(mesh.indices) + bytes(mesh.indices8) + |
| 377 | + bytes(mesh.subMeshes) + bytes(mesh.meshletBounds) + bytes(mesh.verticesId) + |
| 378 | + bytes(mesh.bvhNodes) + bytes(mesh.bvh8Nodes); |
| 379 | + data->cacheBytes = metadataBytes + visualBytes + DynamicWorld::landscapeBytes(*data->landscape); |
| 380 | + if(data->cacheBytes<=budget) |
| 381 | + cache = data; |
| 382 | + return data; |
| 383 | + } |
| 384 | + |
313 | 385 | const Tempest::IndexBuffer<uint16_t>& Resources::cubeIbo() { |
314 | 386 | return inst->cube; |
315 | 387 | } |
|
0 commit comments