Compare commits

...

3 Commits

  1. 22
      CMakeLists.txt
  2. 29
      assets/shaders/write/material_properties.gl
  3. 29
      assets/shaders/write/ubo_info.gl
  4. 15
      source/camera.hpp
  5. 13
      source/expected.hpp
  6. 8
      source/main.cpp
  7. 199
      source/mesh.cpp
  8. 50
      source/mesh.h
  9. 1
      source/sph.hpp
  10. 48
      source/tests.cpp
  11. 11
      source/vfs.cpp
  12. 129
      source/vfs.hpp
  13. 2
      source/window.cpp
  14. 7
      source/window.hpp

22
CMakeLists.txt

@ -20,6 +20,7 @@ endif() @@ -20,6 +20,7 @@ endif()
#set(CMAKE_C_COMPILER "zig" cc ${compile-target})
#set(CMAKE_CXX_COMPILER "zig" c++ ${compile-target})
set(BUILD_SHARED_LIBS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(OpenGL_GL_PREFERENCE "GLVND")
@ -150,6 +151,7 @@ target_link_libraries(imgui PUBLIC SDL2::SDL2-static) @@ -150,6 +151,7 @@ target_link_libraries(imgui PUBLIC SDL2::SDL2-static)
add_executable(test_exe
source/tests.cpp
source/vfs.cpp
)
@ -168,11 +170,31 @@ add_executable(enttge_exe @@ -168,11 +170,31 @@ add_executable(enttge_exe
source/shaders.h
source/stb_image.cpp
source/vfs.hpp
source/vfs.cpp
)
add_executable(enttge::exe ALIAS enttge_exe)
if(CMAKE_BUILD_TYPE MATCHES "Debug")
set(
CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -Werror -fsanitize=undefined -fsanitize=address -Og"
)
set(
CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Werror -fsanitize=undefined -fsanitize=address -Og"
)
target_link_options(test_exe
BEFORE PUBLIC -fsanitize=undefined PUBLIC -fsanitize=address
)
endif()
set_property(TARGET enttge_exe PROPERTY OUTPUT_NAME enttge)
set_property(TARGET test_exe PROPERTY OUTPUT_NAME test)
target_include_directories(enttge_exe PUBLIC ${stb_SOURCE_DIR})

29
assets/shaders/write/material_properties.gl

@ -1,15 +1,16 @@ @@ -1,15 +1,16 @@
layout (std140) uniform ubo_info
{
// alignment offset
vec4 view_pos; // 16 0
mat4 model; // 16 16
mat4 view; // 16 80
mat4 projection; // 16 144
vec4 offset; // 16 208
vec4 colour; // 16 224
int debug_shader; // 4 240
bool instancing_enabled; // 4 244
float gamma; // 4 248
float exposure; // 4 252
bool use_hdr; // 4 256
layout (std140) uniform ubo_info
{
// alignment offset
vec4 view_pos; // 16 0
mat4 model; // 16 16
mat4 view; // 16 80
mat4 projection; // 16 144
vec4 offset; // 16 208
vec4 colour; // 16 224
int debug_shader; // 4 240
bool instancing_enabled; // 4 244
float gamma; // 4 248
float exposure; // 4 252
bool use_hdr; // 4 256
};4 256
};

29
assets/shaders/write/ubo_info.gl

@ -1,15 +1,16 @@ @@ -1,15 +1,16 @@
layout (std140) uniform ubo_info
{
// alignment offset
vec4 view_pos; // 16 0
mat4 model; // 16 16
mat4 view; // 16 80
mat4 projection; // 16 144
vec4 offset; // 16 208
vec4 colour; // 16 224
int debug_shader; // 4 240
bool instancing_enabled; // 4 244
float gamma; // 4 248
float exposure; // 4 252
bool use_hdr; // 4 256
layout (std140) uniform ubo_info
{
// alignment offset
vec4 view_pos; // 16 0
mat4 model; // 16 16
mat4 view; // 16 80
mat4 projection; // 16 144
vec4 offset; // 16 208
vec4 colour; // 16 224
int debug_shader; // 4 240
bool instancing_enabled; // 4 244
float gamma; // 4 248
float exposure; // 4 252
bool use_hdr; // 4 256
};4 256
};

15
source/camera.hpp

@ -22,6 +22,21 @@ public: @@ -22,6 +22,21 @@ public:
glm::mat4 get_transform();
};
template <typename T, class I> class interpolater {
T &item;
T start;
T end;
I interpolating_func;
float end_time;
interpolater(T &item, T end_point, float now, float end_time)
: item(item), start(item), end(end_point), end_time(end_time) {}
bool update(float now, float delta) {
return interpolating_func(item, start, end, end_time);
}
};
struct camera_third_person {
// glm::vec3 front;
// glm::vec3 right;

13
source/expected.hpp

@ -1,6 +1,19 @@ @@ -1,6 +1,19 @@
#pragma once
#include <boost/stacktrace.hpp>
#include <spdlog/spdlog.h>
#include <string>
#include <tl/expected.hpp>
using namespace tl;
struct error {
std::string message;
error(std::string message) : message(message) {
spdlog::error(
"{} : {}", message,
boost::stacktrace::to_string(boost::stacktrace::stacktrace()));
}
};

8
source/main.cpp

@ -424,6 +424,7 @@ int main_loop(game_state& g) @@ -424,6 +424,7 @@ int main_loop(game_state& g)
Model xenia {g.asset_dir / "models"/ "xenia_the_linux_mascot" / "scene.gltf"};
auto m = g.mesh_loader.load_model(*g.filesystem, vfs::zpath_view("models") / "xenia_the_linux_mascot" / "scene.gltf");
Model dry_grass {g.asset_dir / "models"/ "dry_grass" / "scene.gltf"};
dry_grass.enable_instancing();
dry_grass.buffer_instances(init_model_transforms(10000, extent, p,0,0, 0, 9));
@ -723,6 +724,12 @@ void init (game_state &g) { @@ -723,6 +724,12 @@ void init (game_state &g) {
g.registry.emplace<camera_third_person>(cam_entity, (float)g.windowx/g.windowy);
g.registry.emplace<static_model_transform>(cam_entity, glm::vec3(20,11,0));
g.registry.emplace<parent>(cam_entity, pl);
//auto ret = g.model_cache.load("lptree"_hs, vfs::zpath_view(g.asset_dir / "models"/ "trees_low_poly" / "scene.gltf"));
//Model ntrees {g.asset_dir / "models"/ "trees_low_poly" / "scene.gltf"};
}
@ -737,7 +744,6 @@ int main(int argc, char *argv[]) @@ -737,7 +744,6 @@ int main(int argc, char *argv[])
init_imgui(game.window, game.window_glcontext);
game.init_platonic_shapes();
game.init_renderbuffer();
init(game);

199
source/mesh.cpp

@ -12,6 +12,7 @@ @@ -12,6 +12,7 @@
#include <shaders.h>
#include <spdlog/spdlog.h>
#include <stb_image.h>
#include <vfs.hpp>
Mesh::Mesh(std::vector<struct vertex> vertices,
std::vector<unsigned int> indices,
@ -225,36 +226,58 @@ void Model::draw(std::shared_ptr<ubo_proxy<material_properties>> ubo_mat, @@ -225,36 +226,58 @@ void Model::draw(std::shared_ptr<ubo_proxy<material_properties>> ubo_mat,
}
void Model::load_model(std::string path) {
spdlog::info("Loading model {}", path);
const aiScene *scene = importer.ReadFile(
path, aiProcess_Triangulate | aiProcess_FlipUVs |
aiProcess_CalcTangentSpace | aiProcess_GenNormals |
aiProcess_OptimizeMeshes);
spdlog::error("UNSUPPORTED MODEL LOAD NOOP");
}
expected<model, error> model_loader::load_model(vfs::fsmount &fs,
vfs::zpath_view path) {
spdlog::info("Loading model {}", path.str());
auto data =
fs.open_read(path).and_then([](auto a) -> auto { return a->read(); });
if (!data.has_value()) {
unexpected(data.error());
}
Assimp::Importer importer;
const aiScene *scene = importer.ReadFileFromMemory(
data->data(), data->size(),
aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace |
aiProcess_GenNormals | aiProcess_OptimizeMeshes,
path.ext().c_str());
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE ||
!scene->mRootNode) {
std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl;
throw std::exception();
return;
unexpected(error(fmt::format("ASSIMP::{}", importer.GetErrorString())));
}
directory = path.substr(0, path.find_last_of('/'));
// set the root transform
// https://assimp-docs.readthedocs.io/en/v5.1.0/usage/use_the_lib.html#the-node-hierarchy
// AssImp has a transformation for each mesh; which we should use
// But hopefully just getting the root is helpful enough
// We are basically not supporting collada etc scenese with a complex heirachy
// of meshes
// of meshes which i think store the transforms in animation bones
float scale;
root_transform = mat4from_aimat(scene->mRootNode->mTransformation);
auto root_transform = mat4from_aimat(scene->mRootNode->mTransformation);
if (scene->mMetaData->Get("UnitScaleFactor", scale)) {
spdlog::info("Scale factor {}", scale);
root_transform = glm::scale(root_transform, glm::vec3(1 / scale));
}
process_node(scene->mRootNode, scene, root_transform);
std::vector<Mesh> meshes;
texture_cache textures;
glCheckError();
process_node(meshes, scene->mRootNode, scene, root_transform, textures, fs,
path.directory());
std::vector<texture> texture_v;
for (auto [k, v] : textures) {
texture_v.push_back(v);
}
model m{meshes, std::string(path.directory().str()), root_transform};
return m;
}
auto dotrans(auto matrix) {
@ -289,6 +312,93 @@ void print_mat(glm::mat4 x) { @@ -289,6 +312,93 @@ void print_mat(glm::mat4 x) {
spdlog::info("");
}
Mesh model_loader::process_mesh(texture_cache textures_loaded, aiMesh *mesh,
const aiScene *scene, glm::mat4 transform_accum,
vfs::fsmount &fs,
vfs::zpath_view mat_directory) {
std::vector<vertex> vertices;
std::vector<unsigned int> indices;
std::vector<texture> textures;
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
glm::vec3 pos = glm::vec3(mesh->mVertices[i].x, mesh->mVertices[i].y,
mesh->mVertices[i].z);
glm::vec3 norm = glm::vec3(mesh->mNormals[i].x, mesh->mNormals[i].y,
mesh->mNormals[i].z);
glm::vec3 tangent = glm::vec3(mesh->mTangents[i].x, mesh->mTangents[i].y,
mesh->mTangents[i].z);
vertex v;
v.position = pos;
v.normal = norm;
v.tangent = tangent;
if (mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
{
glm::vec2 vec;
vec.x = mesh->mTextureCoords[0][i].x;
vec.y = mesh->mTextureCoords[0][i].y;
v.texture_coords = vec;
} else {
v.texture_coords = glm::vec2(0.0f);
}
vertices.push_back(v);
}
for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for (unsigned int j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
/* https://assimp-docs.readthedocs.io/en/latest/usage/use_the_lib.html#ai-mat-tex
* Each mesh is associated with a material.
* The material stores properties as well as textures.
*/
if (mesh->mMaterialIndex >= 0) {
aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
std::vector<texture> diffuseMaps = load_material_textures(
textures_loaded, scene, material, aiTextureType_DIFFUSE,
"texture_diffuse", fs, mat_directory);
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
std::vector<texture> specularMaps = load_material_textures(
textures_loaded, scene, material, aiTextureType_SPECULAR,
"texture_specular", fs, mat_directory);
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
std::vector<texture> normalMaps = load_material_textures(
textures_loaded, scene, material, aiTextureType_NORMALS,
"texture_normal", fs, mat_directory);
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
}
glCheckError();
return Mesh(vertices, indices, textures, transform_accum);
}
void model_loader::process_node(std::vector<Mesh> &meshes, aiNode *node,
const aiScene *scene, glm::mat4 transform_accum,
texture_cache textures_loaded, vfs::fsmount &fs,
vfs::zpath_view mat_directory) {
auto next_transform = transform_accum * mat4from_aimat(node->mTransformation);
spdlog::info("Got child node {}", node->mName.C_Str());
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(process_mesh(textures_loaded, mesh, scene, next_transform,
fs, mat_directory));
}
// recurse
for (unsigned int i = 0; i < node->mNumChildren; i++) {
process_node(meshes, node->mChildren[i], scene, next_transform,
textures_loaded, fs, mat_directory);
}
glCheckError();
}
void Model::process_node(aiNode *node, const aiScene *scene,
glm::mat4 transform_accum) {
auto next_transform = transform_accum * mat4from_aimat(node->mTransformation);
@ -442,6 +552,69 @@ unsigned int Model::texture_from_file(const char *fname, @@ -442,6 +552,69 @@ unsigned int Model::texture_from_file(const char *fname,
return bind_texture_from_stb(data, width, height, nrComponents);
}
unsigned int model_loader::texture_from_file(vfs::fsmount &fs,
vfs::zpath_view path) {
int width, height, nrComponents;
auto file_data =
fs.open_read(path)
.and_then([](auto a) -> auto { return a->read(); })
.or_else([](error a) -> auto { throw std::runtime_error(a.message); })
.value();
stbi_uc *data = stbi_load_from_memory(
reinterpret_cast<stbi_uc *>(file_data.data()), file_data.size(), &width,
&height, &nrComponents, 4);
return bind_texture_from_stb(data, width, height, nrComponents);
}
std::vector<texture> model_loader::load_material_textures(
std::unordered_map<std::string, texture> textures_loaded,
const aiScene *scene, aiMaterial *mat, aiTextureType type,
std::string typeName, vfs::fsmount &fs, vfs::zpath_view mat_directory) {
std::vector<texture> textures;
for (unsigned int i = 0; i < mat->GetTextureCount(type); i++) {
aiString tex_filename;
mat->GetTexture(type, i, &tex_filename);
bool skip = false;
// checking if already loaded
if (textures_loaded.contains(std::string(tex_filename.C_Str()))) {
textures.push_back(textures_loaded.at(std::string(tex_filename.C_Str())));
spdlog::info("Already loaded texture {}", tex_filename.C_Str());
}
if (!skip) {
texture texture;
if (auto embedded_texture =
scene->GetEmbeddedTexture(tex_filename.C_Str())) {
// embedded texture
spdlog::info("Loading embedded texture");
texture.id = load_embedded_texture(embedded_texture);
texture.type = type;
texture.type_str = typeName;
textures.push_back(texture);
textures_loaded.insert({std::string(tex_filename.C_Str()), texture});
} else {
spdlog::info("Loading texture from file {}", tex_filename.C_Str());
texture.id = model_loader::texture_from_file(
fs, vfs::zpath_view(tex_filename.C_Str()));
texture.type = type;
texture.type_str = typeName;
texture.path = tex_filename.C_Str();
textures.push_back(texture);
textures_loaded.insert({std::string(tex_filename.C_Str()), texture});
}
}
}
glCheckError();
return textures;
}
std::vector<texture> Model::load_material_textures(const aiScene *scene,
aiMaterial *mat,
aiTextureType type,

50
source/mesh.h

@ -2,7 +2,9 @@ @@ -2,7 +2,9 @@
#ifndef MESH_H
#define MESH_H
#include "expected.hpp"
#include "uniform-buffer.hpp"
#include "vfs.hpp"
#include <filesystem>
#include <glm/glm.hpp>
#include <memory>
@ -63,6 +65,14 @@ private: @@ -63,6 +65,14 @@ private:
std::shared_ptr<shader_handle> shader);
};
struct model {
std::vector<Mesh> meshes;
std::string directory;
glm::mat4 root_transform{};
public:
};
class Model {
public:
Model(std::string path) { load_model(path); }
@ -70,6 +80,8 @@ public: @@ -70,6 +80,8 @@ public:
void draw(std::shared_ptr<ubo_proxy<material_properties>> ubo_mat,
std::shared_ptr<ubo_proxy<ubo_info>> ubo_model,
std::shared_ptr<shader_handle> shader, const glm::mat4 transform);
Model(std::vector<Mesh> meshes, std::vector<texture> textures)
: meshes(meshes), textures_loaded(textures) {}
~Model() = default;
Assimp::Importer importer;
std::vector<Mesh> meshes;
@ -90,15 +102,49 @@ public: @@ -90,15 +102,49 @@ public:
private:
bool instancing_enabled = false;
unsigned int instance_bo;
unsigned int texture_from_file(const char *fname, std::string directory);
static unsigned int texture_from_file(const char *fname,
std::string directory);
void load_model(std::string path);
void process_node(aiNode *node, const aiScene *scene, glm::mat4 transform);
Mesh process_mesh(aiMesh *mesh, const aiScene *scene, glm::mat4 transform);
// std::vector<texture> load_material_textures(const aiScene *scene,
// aiMaterial *mat,
// aiTextureType type,
// std::string type_name);
std::vector<texture> load_material_textures(const aiScene *scene,
aiMaterial *mat,
aiTextureType type,
std::string type_name);
std::string typeName);
};
struct model_loader {
typedef std::unordered_map<std::string, texture> texture_cache;
using result_type = std::shared_ptr<Model>;
expected<model, error> load_model(vfs::fsmount &fs, vfs::zpath_view path);
static unsigned int texture_from_file(vfs::fsmount &fs, vfs::zpath_view path);
static Mesh process_mesh(texture_cache textures_loaded, aiMesh *mesh,
const aiScene *scene, glm::mat4 transform_accum,
vfs::fsmount &fs, vfs::zpath_view mat_directory);
static void process_node(std::vector<Mesh> &meshes, aiNode *node,
const aiScene *scene, glm::mat4 transform_accum,
texture_cache textures_loaded, vfs::fsmount &fs,
vfs::zpath_view mat_directory);
static std::vector<texture>
load_material_textures(texture_cache textures_loaded, const aiScene *scene,
aiMaterial *mat, aiTextureType type,
std::string typeName, vfs::fsmount &fs,
vfs::zpath_view mat_directory);
template <typename... Args> result_type operator()(Args &&...args) {
return std::make_shared<Model>(std::forward<Args>(args)...);
}
};
#endif

1
source/sph.hpp

@ -0,0 +1 @@ @@ -0,0 +1 @@

48
source/tests.cpp

@ -74,13 +74,20 @@ TEST_CASE("zpath join string") { @@ -74,13 +74,20 @@ TEST_CASE("zpath join string") {
SUBCASE("basic") {
vfs::zpath_view p{"hello"};
auto n = p / "world";
CHECK(strcmp(p.c_str(), "hello/world") == 0);
CHECK(strcmp(p.c_str(), "hello") == 0);
INFO(n.c_str());
CHECK(strcmp(n.c_str(), "hello/world") == 0);
CHECK(!buffers_differ(n.get_span(),
vfs::zpath_view("hello/world").get_span()));
CHECK_EQ(n.str(), std::string("hello/world"));
CHECK_EQ(p.str(), std::string("hello/world"));
// CHECK(!buffers_differ(n.get_span(),
// vfs::zpath_view("hello/world").get_span()));
// CHECK_EQ(n.str(), std::string("hello/world"));
CHECK_EQ(p.str(), std::string("hello"));
}
SUBCASE("span copy") {
vfs::zpath_view p{"hello"};
vfs::zpath_view q{p.get_span()};
vfs::zpath_view r{std::span(p.get_span().begin(), p.get_span().end())};
CHECK_EQ(p.str(), q.str());
}
SUBCASE("const zpath") {
const vfs::zpath_view p{"hello"};
@ -88,10 +95,10 @@ TEST_CASE("zpath join string") { @@ -88,10 +95,10 @@ TEST_CASE("zpath join string") {
CHECK(strcmp(p.c_str(), "hello") == 0);
INFO(n.c_str());
CHECK(strcmp(n.c_str(), "hello/world") == 0);
CHECK(!buffers_differ(n.get_span(),
vfs::zpath_view("hello/world").get_span()));
// CHECK(!buffers_differ(n.get_span(),
// vfs::zpath_view("hello/world").get_span()));
CHECK(!buffers_differ(p.get_span(), vfs::zpath_view("hello").get_span()));
CHECK_EQ(n.str(), std::string("hello/world"));
// CHECK_EQ(n.str(), std::string("hello/world"));
CHECK_EQ(p.str(), std::string("hello"));
}
SUBCASE("divop") {
@ -100,9 +107,30 @@ TEST_CASE("zpath join string") { @@ -100,9 +107,30 @@ TEST_CASE("zpath join string") {
CHECK_EQ(p.str(), std::string("root/home/user/beans"));
vfs::zpath_view q = p / "Documents";
CHECK_EQ(p.str(), std::string("root/home/user/beans"));
CHECK_EQ(q.str(), std::string("root/home/user/beans/Documents"));
}
SUBCASE("divop nonconst") {
vfs::zpath_view p = vfs::zpath_view("root") / "home" / "user" / "beans";
CHECK_EQ(p.str(), std::string("root/home/user/beans"));
vfs::zpath_view q = p / "Documents";
CHECK_EQ(p.str(), std::string("root/home/user/beans"));
CHECK_EQ(q.str(), std::string("root/home/user/beans/Documents"));
}
SUBCASE("substr") {
const vfs::zpath_view p =
vfs::zpath_view("root") / "home" / "user" / "beans.exe";
CHECK_EQ(p.ext().str(), std::string("exe"));
CHECK_EQ(p.filename().str(), std::string("beans.exe"));
const vfs::zpath_view q;
CHECK_EQ(q.filename().str(), std::string(""));
const vfs::zpath_view r{"beans"};
CHECK_EQ(r.filename().str(), std::string("beans"));
CHECK_EQ(r.ext().str(), std::string(""));
}
}
TEST_CASE("merge method") {
@ -124,7 +152,7 @@ TEST_CASE("merge method") { @@ -124,7 +152,7 @@ TEST_CASE("merge method") {
}
TEST_CASE("vfs") {
vfs::vfmount_sdlfile<char> test{"test/vfs"};
vfs::mount_sdlfile<char> test{"test/vfs"};
auto fl = test.open_read("testfile");
@ -141,7 +169,7 @@ TEST_CASE("vfs") { @@ -141,7 +169,7 @@ TEST_CASE("vfs") {
REQUIRE(res->size() != 0);
REQUIRE((*fl)->close() == false);
auto flw = test.open_readwrite("testfilew");
auto flw = test.open_write("testfilew");
REQUIRE(flw.has_value());
auto testw = std::string("alskdahldha");

11
source/vfs.cpp

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
#include "vfs.hpp"
vfs::zpath vfs::zpath_view::operator/(const zpath_view &other) && {
auto z = zpath{*this} / other;
this->view = std::span<char>(z.data(), z.size() + 1);
return z;
}
vfs::zpath vfs::zpath_view::operator/(const zpath_view &other) const & {
return zpath{*this} + "/" + other.c_str();
}

129
source/vfs.hpp

@ -19,11 +19,9 @@ @@ -19,11 +19,9 @@
namespace vfs {
struct error {
std::string message;
error(std::string message) : message(message) { spdlog::error(message); }
};
typedef unsigned char ftype;
class zpath;
/**
* A null-terminated path object.
* This mainly exists to allow a zero-copy view of a null-terminated string.
@ -34,29 +32,29 @@ struct error { @@ -34,29 +32,29 @@ struct error {
class zpath_view {
// in case we need to own some data for join
std::vector<char> storage{}; // must be first
std::span<const char> view;
zpath_view(std::vector<char> data) : storage(data), view(storage) {
Expects(data.back() == '\0');
}
public:
zpath_view(std::span<const char> data) : view(data) {
Expects(data.back() == '\0');
}
zpath_view(zpath_view &&o) : storage(std::move(o.storage)), view(o.view) {}
zpath_view() : view() {}
zpath_view(
const zpath_view &other) // copy constructor
// Make a copy of storage only if it is set, then
// make our view a view into that instance
: storage(other.storage),
view(storage.size() > 0 ? storage : other.view) {}
zpath_view(zpath_view &&o) : view(o.view) {}
bool operator==(const zpath_view &o) const {
if (size() != o.size())
return false;
for (int i = 0; i < size(); i++) {
if (view[i] != o.view[i]) {
return false;
}
}
return true;
}
zpath_view &operator=(zpath_view &&o) {
storage = std::move(o.storage);
view = o.view;
return *this;
}
@ -65,16 +63,15 @@ public: @@ -65,16 +63,15 @@ public:
if (this == &other) {
return *this;
}
storage = other.storage;
if (storage.size() > 0) {
view = std::span(storage);
} else {
view = other.view;
}
view = other.view;
return *this;
}
zpath_view(const char *data) : view(data, strlen(data) + 1) {}
zpath operator/(const zpath_view &other) &&;
zpath operator/(const zpath_view &other) const &;
zpath_view(const zpath_view &v) : view(v.view) {}
zpath_view(const char *const data) : view(data, strlen(data) + 1) {}
zpath_view(const std::string &path)
: view(std::span(path.c_str(), path.size() + 1)) {}
@ -102,11 +99,6 @@ public: @@ -102,11 +99,6 @@ public:
auto data() { return view.data(); }
auto empty() { return view.empty(); }
zpath_view operator/(zpath_view other) { return join_into(other); }
zpath_view operator/(auto other) { return join_into(zpath_view(other)); }
zpath_view operator/(zpath_view other) const { return join(other); }
zpath_view operator/(auto other) const { return join(zpath_view(other)); }
static void merge_paths(std::vector<char> &first_inout,
const std::span<const char> other) {
Expects(other.back() == '\0');
@ -123,22 +115,54 @@ public: @@ -123,22 +115,54 @@ public:
first_inout.insert(std::end(first_inout), other.begin(), other.end());
}
zpath_view &join_into(const zpath_view other) {
if (storage.size() != view.size()) {
storage.clear();
storage.insert(storage.begin(), view.begin(), view.end());
zpath_view directory() const {
auto last_slash = std::string_view(view.begin(), view.end() - 1).rfind('/');
return zpath_view(std::span(view.begin(), last_slash));
}
const zpath_view filename() const {
auto dot = str().rfind('/');
if (dot != std::string::npos) {
return zpath_view(std::span(view.begin() + dot + 1, view.end()));
}
merge_paths(storage, other.get_span());
view = std::span(storage);
return *this;
}
};
zpath_view join(const zpath_view other) const {
std::vector<char> inner{};
inner.insert(inner.begin(), view.begin(), view.end());
merge_paths(inner, other.get_span());
return zpath_view(inner);
const zpath_view ext() const {
auto dot = str().rfind('.');
if (dot != std::string::npos) {
return zpath_view(std::span(view.begin() + dot + 1, view.end()));
}
return zpath_view(std::span(view.end() - 1, view.end()));
};
};
class zpath_view;
class zpath : public std::string {
public:
zpath operator/(const zpath_view &other) const {
zpath copy{*this};
copy.append("/");
copy.append(other.c_str());
return copy;
}
zpath operator/(const zpath_view &other) {
this->append("/");
this->append(other.c_str());
return *this;
}
bool operator==(const zpath &o) const = default;
zpath &operator=(zpath &&o) = default;
zpath &operator=(const zpath &o) = default;
zpath(const std::string &o) : std::string(o){};
template <class... Type>
zpath(Type... args) : std::string(std::forward<Type>(args)...){};
zpath(const zpath_view &o) : std::string(o.str()){};
zpath(const zpath &o) = default;
zpath(zpath &&o) = default;
~zpath() = default;
};
template <typename T> class file_node {
@ -301,18 +325,29 @@ public: @@ -301,18 +325,29 @@ public:
}
};
template <typename T> struct vfmount_sdlfile {
template <typename T> class mount {
public:
virtual expected<std::unique_ptr<file_node<T>>, error>
open_read(zpath_view path) = 0;
virtual expected<std::unique_ptr<file_node<T>>, error>
open_write(zpath_view path) = 0;
};
template <typename T> struct mount_sdlfile : mount<T> {
private:
typedef std::unique_ptr<file_node<T>> file_ptr;
public:
const zpath_view location;
const zpath location;
vfmount_sdlfile(zpath_view path) : location(path) {}
mount_sdlfile(zpath_view path) : location(path) {}
expected<file_ptr, error> open_with_mode(zpath_view path, const char *mode) {
auto npath = location / path;
spdlog::info("Path str {} cstr {}", npath.str(), npath.c_str());
spdlog::info("path '{}' location '{}'", path.str(), location);
spdlog::info("path '{}' location '{}'", path.c_str(), location.c_str());
spdlog::info("Path str '{}' cstr '{}'", npath, npath.c_str());
auto node = std::make_unique<sdl_file_node<T>>(npath.c_str(), mode);
if (node->valid()) {
@ -327,9 +362,11 @@ public: @@ -327,9 +362,11 @@ public:
return open_with_mode(path, "r");
};
expected<file_ptr, error> open_readwrite(zpath_view path) {
expected<file_ptr, error> open_write(zpath_view path) {
return open_with_mode(path, "w");
};
};
typedef mount<ftype> fsmount;
typedef mount_sdlfile<ftype> fsmount_sdl;
}; // namespace vfs

2
source/window.cpp

@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
#include "window.hpp"
game_state::game_state(const std::string& asset_dir)
: asset_dir(asset_dir)
: asset_dir(asset_dir), filesystem(std::make_unique<vfs::fsmount_sdl>(vfs::zpath_view{asset_dir}))
{
}

7
source/window.hpp

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
#include <entt/entt.hpp>
#include "glheader.hpp"
#include "mesh.h"
#include "shaders.h"
#include "uniform-buffer.hpp"
@ -75,6 +76,10 @@ struct game_state { @@ -75,6 +76,10 @@ struct game_state {
SDL_Window *window;
int windowx, windowy;
const std::filesystem::path asset_dir;
std::unique_ptr<vfs::fsmount> filesystem;
model_loader mesh_loader;
std::string window_name;
entt::registry registry;
std::shared_ptr<ubo_proxy<material_properties>> material_ubo =
@ -82,6 +87,8 @@ struct game_state { @@ -82,6 +87,8 @@ struct game_state {
std::shared_ptr<ubo_proxy<ubo_info>> model_ubo =
std::make_shared<ubo_proxy<ubo_info>>();
// entt::resource_cache<model, model_loader> model_cache{};
std::unordered_map<std::string, std::string> shader_templates{
{"##" + material_ubo->block_name + "##",
material_ubo->fmt_uniform_block()},

Loading…
Cancel
Save