Загрузчик двоичных ресурсов на сервере (Alpha)
This commit is contained in:
119
Src/Client/FrustumCull.h
Normal file
119
Src/Client/FrustumCull.h
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
// https://gist.github.com/podgorskiy/e698d18879588ada9014768e3e82a644
|
||||||
|
|
||||||
|
#include <glm/matrix.hpp>
|
||||||
|
|
||||||
|
class Frustum
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Frustum() {}
|
||||||
|
|
||||||
|
// m = ProjectionMatrix * ViewMatrix
|
||||||
|
Frustum(glm::mat4 m);
|
||||||
|
|
||||||
|
// http://iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm
|
||||||
|
bool IsBoxVisible(const glm::vec3& minp, const glm::vec3& maxp) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum Planes
|
||||||
|
{
|
||||||
|
Left = 0,
|
||||||
|
Right,
|
||||||
|
Bottom,
|
||||||
|
Top,
|
||||||
|
Near,
|
||||||
|
Far,
|
||||||
|
Count,
|
||||||
|
Combinations = Count * (Count - 1) / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
template<Planes i, Planes j>
|
||||||
|
struct ij2k
|
||||||
|
{
|
||||||
|
enum { k = i * (9 - i) / 2 + j - 1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
template<Planes a, Planes b, Planes c>
|
||||||
|
glm::vec3 intersection(const glm::vec3* crosses) const;
|
||||||
|
|
||||||
|
glm::vec4 m_planes[Count];
|
||||||
|
glm::vec3 m_points[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Frustum::Frustum(glm::mat4 m)
|
||||||
|
{
|
||||||
|
m = glm::transpose(m);
|
||||||
|
m_planes[Left] = m[3] + m[0];
|
||||||
|
m_planes[Right] = m[3] - m[0];
|
||||||
|
m_planes[Bottom] = m[3] + m[1];
|
||||||
|
m_planes[Top] = m[3] - m[1];
|
||||||
|
m_planes[Near] = m[3] + m[2];
|
||||||
|
m_planes[Far] = m[3] - m[2];
|
||||||
|
|
||||||
|
glm::vec3 crosses[Combinations] = {
|
||||||
|
glm::cross(glm::vec3(m_planes[Left]), glm::vec3(m_planes[Right])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Left]), glm::vec3(m_planes[Bottom])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Left]), glm::vec3(m_planes[Top])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Left]), glm::vec3(m_planes[Near])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Left]), glm::vec3(m_planes[Far])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Right]), glm::vec3(m_planes[Bottom])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Right]), glm::vec3(m_planes[Top])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Right]), glm::vec3(m_planes[Near])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Right]), glm::vec3(m_planes[Far])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Bottom]), glm::vec3(m_planes[Top])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Bottom]), glm::vec3(m_planes[Near])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Bottom]), glm::vec3(m_planes[Far])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Top]), glm::vec3(m_planes[Near])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Top]), glm::vec3(m_planes[Far])),
|
||||||
|
glm::cross(glm::vec3(m_planes[Near]), glm::vec3(m_planes[Far]))
|
||||||
|
};
|
||||||
|
|
||||||
|
m_points[0] = intersection<Left, Bottom, Near>(crosses);
|
||||||
|
m_points[1] = intersection<Left, Top, Near>(crosses);
|
||||||
|
m_points[2] = intersection<Right, Bottom, Near>(crosses);
|
||||||
|
m_points[3] = intersection<Right, Top, Near>(crosses);
|
||||||
|
m_points[4] = intersection<Left, Bottom, Far>(crosses);
|
||||||
|
m_points[5] = intersection<Left, Top, Far>(crosses);
|
||||||
|
m_points[6] = intersection<Right, Bottom, Far>(crosses);
|
||||||
|
m_points[7] = intersection<Right, Top, Far>(crosses);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm
|
||||||
|
inline bool Frustum::IsBoxVisible(const glm::vec3& minp, const glm::vec3& maxp) const
|
||||||
|
{
|
||||||
|
// check box outside/inside of frustum
|
||||||
|
for (int i = 0; i < Count; i++)
|
||||||
|
{
|
||||||
|
if ((glm::dot(m_planes[i], glm::vec4(minp.x, minp.y, minp.z, 1.0f)) < 0.0) &&
|
||||||
|
(glm::dot(m_planes[i], glm::vec4(maxp.x, minp.y, minp.z, 1.0f)) < 0.0) &&
|
||||||
|
(glm::dot(m_planes[i], glm::vec4(minp.x, maxp.y, minp.z, 1.0f)) < 0.0) &&
|
||||||
|
(glm::dot(m_planes[i], glm::vec4(maxp.x, maxp.y, minp.z, 1.0f)) < 0.0) &&
|
||||||
|
(glm::dot(m_planes[i], glm::vec4(minp.x, minp.y, maxp.z, 1.0f)) < 0.0) &&
|
||||||
|
(glm::dot(m_planes[i], glm::vec4(maxp.x, minp.y, maxp.z, 1.0f)) < 0.0) &&
|
||||||
|
(glm::dot(m_planes[i], glm::vec4(minp.x, maxp.y, maxp.z, 1.0f)) < 0.0) &&
|
||||||
|
(glm::dot(m_planes[i], glm::vec4(maxp.x, maxp.y, maxp.z, 1.0f)) < 0.0))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check frustum outside/inside box
|
||||||
|
int out;
|
||||||
|
out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].x > maxp.x) ? 1 : 0); if (out == 8) return false;
|
||||||
|
out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].x < minp.x) ? 1 : 0); if (out == 8) return false;
|
||||||
|
out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].y > maxp.y) ? 1 : 0); if (out == 8) return false;
|
||||||
|
out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].y < minp.y) ? 1 : 0); if (out == 8) return false;
|
||||||
|
out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].z > maxp.z) ? 1 : 0); if (out == 8) return false;
|
||||||
|
out = 0; for (int i = 0; i<8; i++) out += ((m_points[i].z < minp.z) ? 1 : 0); if (out == 8) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<Frustum::Planes a, Frustum::Planes b, Frustum::Planes c>
|
||||||
|
inline glm::vec3 Frustum::intersection(const glm::vec3* crosses) const
|
||||||
|
{
|
||||||
|
float D = glm::dot(glm::vec3(m_planes[a]), crosses[ij2k<b, c>::k]);
|
||||||
|
glm::vec3 res = glm::mat3(crosses[ij2k<b, c>::k], -crosses[ij2k<a, c>::k], crosses[ij2k<a, b>::k]) *
|
||||||
|
glm::vec3(m_planes[a].w, m_planes[b].w, m_planes[c].w);
|
||||||
|
return res * (-1.0f / D);
|
||||||
|
}
|
||||||
@@ -1190,7 +1190,11 @@ void VulkanRenderSession::drawWorld(GlobalTime gTime, float dTime, VkCommandBuff
|
|||||||
MainAtlas_LightMap_PipelineLayout, 0, 2,
|
MainAtlas_LightMap_PipelineLayout, 0, 2,
|
||||||
(const VkDescriptorSet[]) {MainAtlasDescriptor, VoxelLightMapDescriptor}, 0, nullptr);
|
(const VkDescriptorSet[]) {MainAtlasDescriptor, VoxelLightMapDescriptor}, 0, nullptr);
|
||||||
|
|
||||||
PCO.Model = glm::mat4(1);
|
{
|
||||||
|
// glm::vec4 offset = glm::inverse(Quat)*glm::vec4(0, 0, -64, 1);
|
||||||
|
// PCO.Model = glm::translate(glm::mat4(1), glm::vec3(offset));
|
||||||
|
PCO.Model = glm::mat4(1);
|
||||||
|
}
|
||||||
VkBuffer vkBuffer = VKCTX->TestQuad;
|
VkBuffer vkBuffer = VKCTX->TestQuad;
|
||||||
VkDeviceSize vkOffsets = 0;
|
VkDeviceSize vkOffsets = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
#include "Abstract.hpp"
|
#include "Abstract.hpp"
|
||||||
#include "TOSLib.hpp"
|
#include "TOSLib.hpp"
|
||||||
#include "VertexPool.hpp"
|
#include "VertexPool.hpp"
|
||||||
|
#include "glm/fwd.hpp"
|
||||||
|
#include "../FrustumCull.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
У движка есть один текстурный атлас VK_IMAGE_VIEW_TYPE_2D_ARRAY(RGBA_UINT) и к нему Storage с инфой о положении текстур
|
У движка есть один текстурный атлас VK_IMAGE_VIEW_TYPE_2D_ARRAY(RGBA_UINT) и к нему Storage с инфой о положении текстур
|
||||||
@@ -192,28 +194,16 @@ class VulkanRenderSession : public IRenderSession, public IVulkanDependent {
|
|||||||
if(iterWorld == ChunkMesh.end())
|
if(iterWorld == ChunkMesh.end())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
Frustum fr(projView);
|
||||||
|
|
||||||
for(int z = -distance; z <= distance; z++) {
|
for(int z = -distance; z <= distance; z++) {
|
||||||
for(int y = -distance; y <= distance; y++) {
|
for(int y = -distance; y <= distance; y++) {
|
||||||
for(int x = -distance; x <= distance; x++) {
|
for(int x = -distance; x <= distance; x++) {
|
||||||
Pos::GlobalRegion region = center + Pos::GlobalRegion(x, y, z);
|
Pos::GlobalRegion region = center + Pos::GlobalRegion(x, y, z);
|
||||||
glm::vec3 begin = glm::vec3(region - x64offset);
|
glm::vec3 begin = glm::vec3(region - x64offset) * 64.f;
|
||||||
|
glm::vec3 end = begin + glm::vec3(64.f);
|
||||||
|
|
||||||
bool isVisible = false;
|
if(!fr.IsBoxVisible(begin, end))
|
||||||
for(int index = 0; index < 8; index++) {
|
|
||||||
glm::vec4 vec((begin+glm::vec3(index&1, (index>>1)&1, (index>>2)&1))*64.f, 1);
|
|
||||||
glm::vec4 temp = projView * vec;
|
|
||||||
temp /= temp.w;
|
|
||||||
|
|
||||||
if(temp.x >= -1 && temp.x <= 1
|
|
||||||
&& temp.y >= -1 && temp.y <= 1
|
|
||||||
&& temp.z >= 0 && temp.z <= 1
|
|
||||||
) {
|
|
||||||
isVisible = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isVisible)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto iterRegion = iterWorld->second.find(region);
|
auto iterRegion = iterWorld->second.find(region);
|
||||||
@@ -223,12 +213,19 @@ class VulkanRenderSession : public IRenderSession, public IVulkanDependent {
|
|||||||
Pos::GlobalChunk local = Pos::GlobalChunk(region) << 2;
|
Pos::GlobalChunk local = Pos::GlobalChunk(region) << 2;
|
||||||
|
|
||||||
for(size_t index = 0; index < iterRegion->second.size(); index++) {
|
for(size_t index = 0; index < iterRegion->second.size(); index++) {
|
||||||
|
Pos::bvec4u localPos;
|
||||||
|
localPos.unpack(index);
|
||||||
|
|
||||||
|
glm::vec3 chunkPos = begin+glm::vec3(localPos)*16.f;
|
||||||
|
if(!fr.IsBoxVisible(chunkPos, chunkPos+glm::vec3(16)))
|
||||||
|
continue;
|
||||||
|
|
||||||
auto &chunk = iterRegion->second[index];
|
auto &chunk = iterRegion->second[index];
|
||||||
|
|
||||||
if(chunk.VoxelPointer)
|
if(chunk.VoxelPointer)
|
||||||
vertexVoxels.emplace_back(local+Pos::GlobalChunk(Pos::bvec4u().unpack(index)), VertexPool_Voxels.map(chunk.VoxelPointer), chunk.VoxelPointer.VertexCount);
|
vertexVoxels.emplace_back(local+Pos::GlobalChunk(localPos), VertexPool_Voxels.map(chunk.VoxelPointer), chunk.VoxelPointer.VertexCount);
|
||||||
if(chunk.NodePointer)
|
if(chunk.NodePointer)
|
||||||
vertexNodes.emplace_back(local+Pos::GlobalChunk(Pos::bvec4u().unpack(index)), VertexPool_Nodes.map(chunk.NodePointer), chunk.NodePointer.VertexCount);
|
vertexNodes.emplace_back(local+Pos::GlobalChunk(localPos), VertexPool_Nodes.map(chunk.NodePointer), chunk.NodePointer.VertexCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -549,4 +549,16 @@ struct hash<LV::Pos::BitVec3<T, BitsPerComponent>> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<LV::Hash_t> {
|
||||||
|
std::size_t operator()(const LV::Hash_t& hash) const noexcept {
|
||||||
|
std::size_t v = 14695981039346656037ULL;
|
||||||
|
for (const auto& byte : hash) {
|
||||||
|
v ^= static_cast<std::size_t>(byte);
|
||||||
|
v *= 1099511628211ULL;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
#include "Common/Abstract.hpp"
|
||||||
|
#include "Server/Abstract.hpp"
|
||||||
|
#include <filesystem>
|
||||||
|
#include <unordered_map>
|
||||||
#define BOOST_ASIO_HAS_IO_URING 1
|
#define BOOST_ASIO_HAS_IO_URING 1
|
||||||
#include "BinaryResourceManager.hpp"
|
#include "BinaryResourceManager.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -5,19 +9,316 @@
|
|||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <boost/asio/stream_file.hpp>
|
#include <boost/asio/stream_file.hpp>
|
||||||
#include <TOSLib.hpp>
|
#include <TOSLib.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
namespace LV::Server {
|
namespace LV::Server {
|
||||||
|
|
||||||
|
struct UriParse {
|
||||||
|
std::string Protocol, Path;
|
||||||
|
|
||||||
|
std::string getFull() const {
|
||||||
|
return Protocol + "://" + Path;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
UriParse parseUri(const std::string& uri) {
|
||||||
|
size_t pos = uri.find("://");
|
||||||
|
|
||||||
|
if(pos == std::string::npos)
|
||||||
|
return {"assets", uri};
|
||||||
|
else
|
||||||
|
return {uri.substr(0, pos), uri.substr(pos+3)};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Resource {
|
||||||
|
// Файл загруженный с диска
|
||||||
|
std::shared_ptr<ResourceFile> Loaded;
|
||||||
|
// Источник
|
||||||
|
Hash_t Hash;
|
||||||
|
size_t LastUsedTime = 0;
|
||||||
|
EnumBinResource Type;
|
||||||
|
ResourceId_t ResId;
|
||||||
|
UriParse Uri;
|
||||||
|
|
||||||
|
std::string LastError;
|
||||||
|
};
|
||||||
|
|
||||||
|
void BinaryResourceManager::run() {
|
||||||
|
TOS::Logger LOG = "BinaryResourceManager::run";
|
||||||
|
LOG.debug() << "Поток чтения двоичных ресурсов запущен";
|
||||||
|
|
||||||
|
// Ресурсы - кешированные в оперативную память или в процессе загрузки
|
||||||
|
std::unordered_map<ResourceId_t, std::shared_ptr<Resource>> knownResource[(int) EnumBinResource::MAX_ENUM];
|
||||||
|
// Трансляция идентификаторов в Uri (противоположность KnownResource)
|
||||||
|
std::vector<std::string> resIdToUri[(int) EnumBinResource::MAX_ENUM];
|
||||||
|
// Новые полученные идентификаторы и те, чьи ресурсы нужно снова загрузить
|
||||||
|
std::vector<ResourceId_t> newRes[(int) EnumBinResource::MAX_ENUM];
|
||||||
|
// Пути поиска ресурсов
|
||||||
|
std::vector<fs::path> assets;
|
||||||
|
// Запросы хешей
|
||||||
|
std::vector<ResourceId_t> binToHash[(int) EnumBinResource::MAX_ENUM];
|
||||||
|
//
|
||||||
|
std::unordered_map<Hash_t, std::shared_ptr<Resource>> hashToResource;
|
||||||
|
std::vector<Hash_t> hashToLoad;
|
||||||
|
|
||||||
|
auto lambdaLoadResource = [&](UriParse uri, int type) -> std::variant<std::shared_ptr<ResourceFile>, std::string>
|
||||||
|
{
|
||||||
|
// std::shared_ptr<Resource> resObj = std::make_shared<Resource>();
|
||||||
|
// knownResource[type][resId] = resObj;
|
||||||
|
|
||||||
|
if(uri.Protocol != "assets")
|
||||||
|
return "Протокол не поддерживается";
|
||||||
|
else {
|
||||||
|
auto var = loadFile(assets, uri.Path, (EnumBinResource) type);
|
||||||
|
|
||||||
|
if(var.index() == 0) {
|
||||||
|
std::shared_ptr<ResourceFile> resource = std::get<0>(var);
|
||||||
|
resource->calcHash();
|
||||||
|
return resource;
|
||||||
|
} else {
|
||||||
|
return std::get<1>(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
while(!NeedShutdown) {
|
||||||
|
bool hasWork = false;
|
||||||
|
auto lock = Local.lock();
|
||||||
|
|
||||||
|
for(int type = 0; type < (int) EnumBinResource::MAX_ENUM; type++) {
|
||||||
|
for(ResourceId_t iter = 0; iter < lock->ResIdToUri[type].size(); iter++) {
|
||||||
|
newRes[type].push_back(resIdToUri[type].size()+iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
resIdToUri[type].insert(resIdToUri[type].end(), lock->ResIdToUri[type].begin(), lock->ResIdToUri[type].end());
|
||||||
|
resIdToUri[type].clear();
|
||||||
|
|
||||||
|
binToHash[type].insert(binToHash[type].end(), lock->BinToHash[type].begin(), lock->BinToHash[type].end());
|
||||||
|
lock->BinToHash[type].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool assetsUpdate = false;
|
||||||
|
if(!lock->Assets.empty()) {
|
||||||
|
// Требуется пересмотр всех ресурсов
|
||||||
|
assets = std::move(lock->Assets);
|
||||||
|
assetsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Hash_t> hashRequest = std::move(lock->Hashes);
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
if(!hashRequest.empty()) {
|
||||||
|
std::vector<std::shared_ptr<ResourceFile>> hashToResourceOut;
|
||||||
|
|
||||||
|
for(Hash_t hash : hashRequest) {
|
||||||
|
auto iter = hashToResource.find(hash);
|
||||||
|
|
||||||
|
if(iter == hashToResource.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!iter->second->Loaded) {
|
||||||
|
hashToLoad.push_back(hash);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter->second->LastUsedTime = TOS::Time::getSeconds();
|
||||||
|
hashToResourceOut.push_back(iter->second->Loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto outLock = Out.lock();
|
||||||
|
outLock->HashToResource.insert(outLock->HashToResource.end(), hashToResourceOut.begin(), hashToResourceOut.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unordered_map<ResourceId_t, ResourceFile::Hash_t> binToHashOut[(int) EnumBinResource::MAX_ENUM];
|
||||||
|
|
||||||
|
for(int type = 0; type < (int) EnumBinResource::MAX_ENUM; type++) {
|
||||||
|
for(ResourceId_t resId : binToHash[type]) {
|
||||||
|
std::shared_ptr<Resource> resource = knownResource[type][resId];
|
||||||
|
if(!resource)
|
||||||
|
continue; // Идентификатор не известен
|
||||||
|
|
||||||
|
binToHashOut[type][resId] = resource->Hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка ресурсов по новым идентификаторам
|
||||||
|
for(int type = 0; type < (int) EnumBinResource::MAX_ENUM; type++) {
|
||||||
|
if(newRes[type].empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hasWork = true;
|
||||||
|
|
||||||
|
while(!newRes[type].empty()) {
|
||||||
|
ResourceId_t resId = newRes[type].back();
|
||||||
|
newRes[type].pop_back();
|
||||||
|
|
||||||
|
assert(resId < resIdToUri[type].size());
|
||||||
|
UriParse uri = parseUri(resIdToUri[type][resId]);
|
||||||
|
std::shared_ptr<Resource> resObj = std::make_shared<Resource>();
|
||||||
|
resObj->LastUsedTime = TOS::Time::getSeconds();
|
||||||
|
resObj->Type = (EnumBinResource) type;
|
||||||
|
resObj->ResId = resId;
|
||||||
|
resObj->Uri = uri;
|
||||||
|
|
||||||
|
auto var = lambdaLoadResource(uri, type);
|
||||||
|
|
||||||
|
if(var.index() == 0) {
|
||||||
|
resObj->Loaded = std::get<0>(var);
|
||||||
|
resObj->Hash = resObj->Loaded->Hash;
|
||||||
|
hashToResource[resObj->Hash] = resObj;
|
||||||
|
} else {
|
||||||
|
std::fill(resObj->Hash.begin(), resObj->Hash.end(), 0);
|
||||||
|
resObj->LastError = std::get<1>(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
knownResource[type][resId] = resObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(!hashToLoad.empty()) {
|
||||||
|
Hash_t hash = hashToLoad.back();
|
||||||
|
hashToLoad.pop_back();
|
||||||
|
|
||||||
|
auto iter = hashToResource.find(hash);
|
||||||
|
if(iter == hashToResource.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::shared_ptr<Resource> &res = iter->second;
|
||||||
|
|
||||||
|
if(res->Loaded) {
|
||||||
|
Out.lock()->HashToResource.push_back(res->Loaded);
|
||||||
|
} else {
|
||||||
|
if(!res->LastError.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto var = lambdaLoadResource(res->Uri, (int) res->Type);
|
||||||
|
if(var.index() == 0) {
|
||||||
|
hasWork = true;
|
||||||
|
res->Loaded = std::get<0>(var);
|
||||||
|
res->LastUsedTime = TOS::Time::getSeconds();
|
||||||
|
res->LastError.clear();
|
||||||
|
|
||||||
|
if(res->Hash != res->Loaded->Hash) {
|
||||||
|
// Хеш изменился
|
||||||
|
Out.lock()->BinToHash[(int) res->Type][res->ResId] = res->Loaded->Hash;
|
||||||
|
res->Hash = res->Loaded->Hash;
|
||||||
|
std::shared_ptr<Resource> resObj = res;
|
||||||
|
hashToResource.erase(iter);
|
||||||
|
hashToResource[hash] = resObj;
|
||||||
|
} else {
|
||||||
|
Out.lock()->HashToResource.push_back(res->Loaded);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res->LastError = std::get<1>(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем долго не используемые ресурсы
|
||||||
|
{
|
||||||
|
size_t now = TOS::Time::getSeconds();
|
||||||
|
for(int type = 0; type < (int) EnumBinResource::MAX_ENUM; type++) {
|
||||||
|
for(auto& resObj : knownResource[type]) {
|
||||||
|
if(now - resObj.second->LastUsedTime > 30)
|
||||||
|
resObj.second->Loaded = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(assetsUpdate) {
|
||||||
|
hashToLoad.clear();
|
||||||
|
hashToResource.clear();
|
||||||
|
|
||||||
|
for(int type = 0; type < (int) EnumBinResource::MAX_ENUM; type++) {
|
||||||
|
for(auto& [resId, resObj] : knownResource[type]) {
|
||||||
|
auto var = lambdaLoadResource(resObj->Uri, type);
|
||||||
|
|
||||||
|
if(var.index() == 0) {
|
||||||
|
hasWork = true;
|
||||||
|
resObj->Loaded = std::get<0>(var);
|
||||||
|
resObj->LastUsedTime = TOS::Time::getSeconds();
|
||||||
|
resObj->LastError.clear();
|
||||||
|
|
||||||
|
if(resObj->Hash != resObj->Loaded->Hash) {
|
||||||
|
// Хеш изменился
|
||||||
|
Out.lock()->BinToHash[type][resId] = resObj->Loaded->Hash;
|
||||||
|
resObj->Hash = resObj->Loaded->Hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
hashToResource[resObj->Hash] = resObj;
|
||||||
|
} else {
|
||||||
|
resObj->LastError = std::get<1>(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!hasWork)
|
||||||
|
TOS::Time::sleep3(10);
|
||||||
|
}
|
||||||
|
} catch(const std::exception& exc) {
|
||||||
|
LOG.error() << exc.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
NeedShutdown = true;
|
||||||
|
LOG.debug() << "Поток чтения двоичных ресурсов остановлен";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::variant<std::shared_ptr<ResourceFile>, std::string>
|
||||||
|
BinaryResourceManager::loadFile(const std::vector<fs::path>& assets, const std::string& path, EnumBinResource type)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
std::shared_ptr<ResourceFile> file = std::make_shared<ResourceFile>();
|
||||||
|
|
||||||
|
std::string firstPath;
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case EnumBinResource::Texture: firstPath = "texture"; break;
|
||||||
|
case EnumBinResource::Animation: firstPath = "animation"; break;
|
||||||
|
case EnumBinResource::Model: firstPath = "model"; break;
|
||||||
|
case EnumBinResource::Sound: firstPath = "sound"; break;
|
||||||
|
case EnumBinResource::Font: firstPath = "font"; break;
|
||||||
|
default: assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(fs::path assetsPath : assets) {
|
||||||
|
fs::path p = assetsPath / firstPath / path;
|
||||||
|
if(!fs::exists(p))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::ifstream fd(p);
|
||||||
|
|
||||||
|
if(!fd)
|
||||||
|
MAKE_ERROR("Не удалось открыть файл: " << p.string());
|
||||||
|
|
||||||
|
fd.seekg(0, std::ios::end);
|
||||||
|
std::streamsize size = fd.tellg();
|
||||||
|
fd.seekg(0, std::ios::beg);
|
||||||
|
file->Data.resize(size);
|
||||||
|
fd.read((char*) file->Data.data(), size);
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
MAKE_ERROR("Файл не найден");
|
||||||
|
} catch(const std::exception& exc) {
|
||||||
|
return exc.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BinaryResourceManager::BinaryResourceManager(asio::io_context &ioc)
|
BinaryResourceManager::BinaryResourceManager(asio::io_context &ioc)
|
||||||
: AsyncObject(ioc)
|
: AsyncObject(ioc), Thread(&BinaryResourceManager::run, this)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
BinaryResourceManager::~BinaryResourceManager() {
|
BinaryResourceManager::~BinaryResourceManager() {
|
||||||
|
NeedShutdown = true;
|
||||||
|
Thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinaryResourceManager::recheckResources(std::vector<fs::path> assets /* Пути до активных папок assets */) {
|
void BinaryResourceManager::recheckResources(std::vector<fs::path> assets /* Пути до активных папок assets */) {
|
||||||
@@ -50,15 +351,6 @@ void BinaryResourceManager::update(float dtime) {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
BinaryResourceManager::UriParse BinaryResourceManager::parseUri(const std::string &uri) {
|
|
||||||
size_t pos = uri.find("://");
|
|
||||||
|
|
||||||
if(pos == std::string::npos)
|
|
||||||
return {"assets", uri};
|
|
||||||
else
|
|
||||||
return {uri.substr(0, pos), uri.substr(pos+3)};
|
|
||||||
}
|
|
||||||
|
|
||||||
// coro<> BinaryResourceManager::checkResource_Assets(ResourceId_t id, fs::path path, std::shared_ptr<Resource> res) {
|
// coro<> BinaryResourceManager::checkResource_Assets(ResourceId_t id, fs::path path, std::shared_ptr<Resource> res) {
|
||||||
// try {
|
// try {
|
||||||
// asio::stream_file fd(IOC, path, asio::stream_file::flags::read_only);
|
// asio::stream_file fd(IOC, path, asio::stream_file::flags::read_only);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <Common/Async.hpp>
|
#include <Common/Async.hpp>
|
||||||
#include "Abstract.hpp"
|
#include "Abstract.hpp"
|
||||||
@@ -23,62 +24,58 @@ namespace fs = std::filesystem;
|
|||||||
|
|
||||||
Чтение происходит отдельным потоком, переконвертацию пока предлагаю в realtime.
|
Чтение происходит отдельным потоком, переконвертацию пока предлагаю в realtime.
|
||||||
Хэш вычисляется после чтения и может быть иным чем при прошлом чтении (ресурс изменили наживую)
|
Хэш вычисляется после чтения и может быть иным чем при прошлом чтении (ресурс изменили наживую)
|
||||||
тогда обычным оповещениям клиентам дойдёт новая версия
|
тогда обычным оповещением клиентам дойдёт новая версия
|
||||||
|
|
||||||
Подержать какое-то время ресурс в памяти
|
Подержать какое-то время ресурс в памяти
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Ключи сопоставляются с идентификаторами и с хешеми. При появлении нового ключа,
|
||||||
|
ему выдаётся идентификатор и делается запрос на загрузку ресурса для вычисления хеша.
|
||||||
|
recheckResources делает повторную загрузку всех ресурсов для проверки изменения хешей.
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class BinaryResourceManager : public AsyncObject {
|
class BinaryResourceManager : public AsyncObject {
|
||||||
public:
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Resource {
|
|
||||||
// Файл загруженный с диска
|
|
||||||
std::shared_ptr<ResourceFile> Loaded;
|
|
||||||
// Источник
|
|
||||||
std::string Uri;
|
|
||||||
bool IsLoading = false;
|
|
||||||
size_t LastUsedTime = 0;
|
|
||||||
|
|
||||||
std::string LastError;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UriParse {
|
|
||||||
std::string Protocol, Path;
|
|
||||||
|
|
||||||
std::string getFull() const {
|
|
||||||
return Protocol + "://" + Path;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Поток сервера
|
// Поток сервера
|
||||||
// Последовательная регистрация ресурсов
|
// Последовательная регистрация ресурсов
|
||||||
ResourceId_t NextId[(int) EnumBinResource::MAX_ENUM] = {0};
|
ResourceId_t NextId[(int) EnumBinResource::MAX_ENUM] = {0};
|
||||||
// Известные ресурсы, им присвоен идентификатор
|
// Известные ресурсы, им присвоен идентификатор
|
||||||
|
// Нужно для потока загрузки
|
||||||
std::unordered_map<std::string, ResourceId_t> KnownResource[(int) EnumBinResource::MAX_ENUM];
|
std::unordered_map<std::string, ResourceId_t> KnownResource[(int) EnumBinResource::MAX_ENUM];
|
||||||
std::unordered_map<ResourceId_t, std::shared_ptr<Resource>> ResourcesInfo[(int) EnumBinResource::MAX_ENUM];
|
|
||||||
|
|
||||||
// Местные потоки
|
// Местные потоки
|
||||||
struct LocalObj_t {
|
struct LocalObj_t {
|
||||||
// Ресурсы - кешированные в оперативную память или в процессе загрузки
|
// Трансляция идентификаторов в Uri (противоположность KnownResource)
|
||||||
std::map<BinTextureId_t, std::shared_ptr<Resource>> InMemory[(int) EnumBinResource::MAX_ENUM];
|
// Передаётся в отдельный поток
|
||||||
// Кому-то нужно сопоставить идентификаторы с хэшами
|
std::vector<std::string> ResIdToUri[(int) EnumBinResource::MAX_ENUM];
|
||||||
|
// Кому-то нужно сопоставить идентификаторы с хешами
|
||||||
std::vector<ResourceId_t> BinToHash[(int) EnumBinResource::MAX_ENUM];
|
std::vector<ResourceId_t> BinToHash[(int) EnumBinResource::MAX_ENUM];
|
||||||
// Запрос ресурсов, по которым потоки загружают ресурсы с диска
|
// Запрос ресурсов, по которым потоки загружают ресурсы с диска
|
||||||
std::vector<Hash_t> Hashes;
|
std::vector<Hash_t> Hashes;
|
||||||
|
// Передача новых путей поиска ресурсов в другой поток
|
||||||
|
std::vector<fs::path> Assets;
|
||||||
};
|
};
|
||||||
|
|
||||||
TOS::SpinlockObject<LocalObj_t> Local;
|
TOS::SpinlockObject<LocalObj_t> Local;
|
||||||
public:
|
public:
|
||||||
// Подготовленные оповещения о ресурсах
|
// Подготовленные оповещения о ресурсах
|
||||||
struct OutObj_t {
|
struct OutObj_t {
|
||||||
std::unordered_map<ResourceId_t, ResourceFile::Hash_t> BinToHash[(int) EnumBinResource::MAX_ENUM];
|
std::unordered_map<ResourceId_t, ResourceFile::Hash_t> BinToHash[(int) EnumBinResource::MAX_ENUM];
|
||||||
std::vector<std::shared_ptr<ResourceFile>> HashToResource;
|
std::vector<std::shared_ptr<ResourceFile>> HashToResource;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TOS::SpinlockObject<OutObj_t> Out;
|
TOS::SpinlockObject<OutObj_t> Out;
|
||||||
|
volatile bool NeedShutdown = false;
|
||||||
|
std::thread Thread;
|
||||||
|
|
||||||
|
void run();
|
||||||
|
std::variant<std::shared_ptr<ResourceFile>, std::string>
|
||||||
|
loadFile(const std::vector<fs::path>& assets, const std::string& path, EnumBinResource type);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Если ресурс будет обновлён или загружен будет вызвано onResourceUpdate
|
// Если ресурс будет обновлён или загружен будет вызвано onResourceUpdate
|
||||||
@@ -89,7 +86,17 @@ public:
|
|||||||
void recheckResources(std::vector<fs::path> assets /* Пути до активных папок assets */);
|
void recheckResources(std::vector<fs::path> assets /* Пути до активных папок assets */);
|
||||||
// Выдаёт или назначает идентификатор для ресурса
|
// Выдаёт или назначает идентификатор для ресурса
|
||||||
ResourceId_t getResource(const std::string& uri, EnumBinResource bin) {
|
ResourceId_t getResource(const std::string& uri, EnumBinResource bin) {
|
||||||
std::string fullUri = parseUri(uri).getFull();
|
std::string fullUri;
|
||||||
|
|
||||||
|
{
|
||||||
|
size_t pos = uri.find("://");
|
||||||
|
|
||||||
|
if(pos == std::string::npos)
|
||||||
|
fullUri = "assets://" + uri;
|
||||||
|
else
|
||||||
|
fullUri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
int index = (int) bin;
|
int index = (int) bin;
|
||||||
|
|
||||||
auto &container = KnownResource[index];
|
auto &container = KnownResource[index];
|
||||||
@@ -98,6 +105,10 @@ public:
|
|||||||
assert(NextId[index] != ResourceId_t(-1));
|
assert(NextId[index] != ResourceId_t(-1));
|
||||||
ResourceId_t nextId = NextId[index]++;
|
ResourceId_t nextId = NextId[index]++;
|
||||||
container.insert(iter, {fullUri, nextId});
|
container.insert(iter, {fullUri, nextId});
|
||||||
|
|
||||||
|
auto lock = Local.lock();
|
||||||
|
lock->ResIdToUri[index].push_back(uri);
|
||||||
|
|
||||||
return nextId;
|
return nextId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,9 +146,6 @@ public:
|
|||||||
// Серверный такт
|
// Серверный такт
|
||||||
void update(float dtime);
|
void update(float dtime);
|
||||||
|
|
||||||
protected:
|
|
||||||
UriParse parseUri(const std::string &uri);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,16 +11,20 @@ layout(location = 0) in GeometryObj {
|
|||||||
|
|
||||||
layout(location = 0) out FragmentObj {
|
layout(location = 0) out FragmentObj {
|
||||||
vec3 GeoPos; // Реальная позиция в мире
|
vec3 GeoPos; // Реальная позиция в мире
|
||||||
|
vec3 Normal;
|
||||||
flat uint Texture; // Текстура
|
flat uint Texture; // Текстура
|
||||||
vec2 UV;
|
vec2 UV;
|
||||||
} Fragment;
|
} Fragment;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
vec3 normal = normalize(cross(Geometry[1].GeoPos-Geometry[0].GeoPos, Geometry[2].GeoPos-Geometry[0].GeoPos));
|
||||||
|
|
||||||
for(int iter = 0; iter < 3; iter++) {
|
for(int iter = 0; iter < 3; iter++) {
|
||||||
gl_Position = gl_in[iter].gl_Position;
|
gl_Position = gl_in[iter].gl_Position;
|
||||||
Fragment.GeoPos = Geometry[iter].GeoPos;
|
Fragment.GeoPos = Geometry[iter].GeoPos;
|
||||||
Fragment.Texture = Geometry[iter].Texture;
|
Fragment.Texture = Geometry[iter].Texture;
|
||||||
Fragment.UV = Geometry[iter].UV;
|
Fragment.UV = Geometry[iter].UV;
|
||||||
|
Fragment.Normal = normal;
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
layout(location = 0) in FragmentObj {
|
layout(location = 0) in FragmentObj {
|
||||||
vec3 GeoPos; // Реальная позиция в мире
|
vec3 GeoPos; // Реальная позиция в мире
|
||||||
|
vec3 Normal;
|
||||||
flat uint Texture; // Текстура
|
flat uint Texture; // Текстура
|
||||||
vec2 UV;
|
vec2 UV;
|
||||||
} Fragment;
|
} Fragment;
|
||||||
@@ -69,8 +70,21 @@ vec4 atlasColor(uint texId, vec2 uv)
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec3 blendOverlay(vec3 base, vec3 blend) {
|
||||||
|
vec3 result;
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
if (base[i] <= 0.5)
|
||||||
|
result[i] = 2.0 * base[i] * blend[i];
|
||||||
|
else
|
||||||
|
result[i] = 1.0 - 2.0 * (1.0 - base[i]) * (1.0 - blend[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
Frame = atlasColor(Fragment.Texture, Fragment.UV);
|
Frame = atlasColor(Fragment.Texture, Fragment.UV);
|
||||||
|
Frame.xyz *= max(0.2f, dot(Fragment.Normal, normalize(vec3(0.5, 1, 0.8))));
|
||||||
|
// Frame = vec4(blendOverlay(vec3(Frame), vec3(Fragment.GeoPos/64.f)), Frame.w);
|
||||||
if(Frame.w == 0)
|
if(Frame.w == 0)
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
Binary file not shown.
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
layout(location = 0) in FragmentObj {
|
layout(location = 0) in FragmentObj {
|
||||||
vec3 GeoPos; // Реальная позиция в мире
|
vec3 GeoPos; // Реальная позиция в мире
|
||||||
|
vec3 Normal;
|
||||||
flat uint Texture; // Текстура
|
flat uint Texture; // Текстура
|
||||||
vec2 UV;
|
vec2 UV;
|
||||||
} Fragment;
|
} Fragment;
|
||||||
|
|||||||
Binary file not shown.
BIN
assets/textures/acacia_planks.png
Normal file
BIN
assets/textures/acacia_planks.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB |
BIN
assets/textures/jungle_planks.png
Normal file
BIN
assets/textures/jungle_planks.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/textures/oak_planks.png
Normal file
BIN
assets/textures/oak_planks.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Reference in New Issue
Block a user