diff --git a/.gitignore b/.gitignore index 637196074c032c32bda22ecab4ca7d27c6efd7db..8eebd05a1fa7a95850b5cbdf309a1cbf3ab78f2a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ imgui.ini .flatpak-builder/ compile_commands.json +# Engine data +demo/data/*.bin + # dotfiles (IDE) .idea/ .cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt index b5a980e3a0569c693e50f6e37c7cf4531788e621..6e7c22b6e640ffc12851571f53138e143eb77e73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,4 +129,4 @@ export(EXPORT "${PROJECT_NAME}Targets" # 3rd party integrations add_subdirectory( integrations/bullet ) - +add_subdirectory( tools/pack ) diff --git a/demo/data/rollball.yml b/demo/data/rollball.yml index 015c22605f03d8895b67d13da92d582e9ffa0416..966d1f863d6e3c0a9fdfbcb06ae3a13efc4e1201 100644 --- a/demo/data/rollball.yml +++ b/demo/data/rollball.yml @@ -1,19 +1,19 @@ --- prefabs: - - name: "environment" + - name: "rb_environment" components: gfx::material: ambient: [0.0215, 0.1754, 0.0215] diffuse: [1, 1, 1] specular: [0.0633, 0.727811, 0.633] shininess: 16 - - name: "wallX" - parent: "environment" + - name: "rb_wallX" + parent: "rb_environment" components: Transform: StaticMesh: pipeline: redbook/debug - shape_id: "rb_wall_x" + shape_id: "mesh_rb_wall_x" shape: type: box scale: [1.0, 5.0, 41] @@ -23,13 +23,13 @@ prefabs: type: box extents: [0.5, 2.5, 20.5] # Wall Z shorter to avoid z-fighting - - name: "wallZ" - parent: "environment" + - name: "rb_wallZ" + parent: "rb_environment" components: Transform: StaticMesh: pipeline: redbook/debug - shape_id: "rb_wall_z" + shape_id: "mesh_rb_wall_z" shape: type: box scale: [39, 5, 1] @@ -38,13 +38,13 @@ prefabs: shape: type: box extents: [ 19.5, 2.5, 0.5 ] - - name: "floor" - parent: "environment" + - name: "rb_floor" + parent: "rb_environment" components: Transform: StaticMesh: pipeline: redbook/debug - shape_id: "rb_floor" + shape_id: "mesh_rb_floor" shape: type: box # we don't (currently) support planes... scale: [39, 0.5, 39] @@ -53,12 +53,12 @@ prefabs: shape: type: box # we don't (currently) support planes... extents: [19.5, 0.25, 19.5] - - name: player + - name: rb_player components: Transform: StaticMesh: pipeline: redbook/debug - shape_id: "rb_player" + shape_id: "mesh_rb_player" shape: type: sphere gfx::material: @@ -70,12 +70,12 @@ prefabs: shape: type: sphere radius: 1 - - name: collectable + - name: rb_collectable components: Transform: StaticMesh: pipeline: redbook/debug - shape_id: "rb_collect" + shape_id: "mesh_rb_collect" shape: type: box gfx::material: @@ -86,4 +86,37 @@ prefabs: phys::Body: type: kinematic shape: - type: box \ No newline at end of file + type: box +scene: + - prefab: rb_wallX + components: + Transform: + origin: [20, 0, 0] + - prefab: rb_wallX + components: + Transform: + origin: [-20, 0, 0] + - prefab: rb_wallZ + components: + Transform: + origin: [0, 0, -20] + - prefab: rb_wallZ + components: + Transform: + origin: [0, 0, 20] + - prefab: rb_floor + components: + Transform: + origin: [0, -2.5, 0] + - prefab: rb_collectable + components: + Transform: + origin: [-5, -0.5, 12] + - prefab: rb_collectable + components: + Transform: + origin: [15, -0.5, 0.5] + - prefab: rb_collectable + components: + Transform: + origin: [6, -0.5, 15] diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp index 75439bb41c6e8ad8b20f2ff76703b08873c2cc90..cccdac0f7932bca3d4f0aae0b28559b3267f2104 100644 --- a/demo/demo/rollball.cpp +++ b/demo/demo/rollball.cpp @@ -31,11 +31,11 @@ #include <array> -static const fggl::util::GUID playerPrefab = "player"_fid; -static const fggl::util::GUID collectablePrefab = "collectable"_fid; -static const fggl::util::GUID WallNPrefab = "wallX"_fid; -static const fggl::util::GUID WallEPrefab = "wallZ"_fid; -static const fggl::util::GUID floorPrefab = "floor"_fid; +static const fggl::util::GUID playerPrefab = "rb_player"_fid; +static const fggl::util::GUID collectablePrefab = "rb_collectable"_fid; +static const fggl::util::GUID WallNPrefab = "rb_wallX"_fid; +static const fggl::util::GUID WallEPrefab = "rb_wallZ"_fid; +static const fggl::util::GUID floorPrefab = "rb_floor"_fid; static void setup_camera(fggl::entity::EntityManager& world) { auto prototype = world.create(); diff --git a/include/fggl/data/model.hpp b/include/fggl/data/model.hpp index 61cc4b582df4d58b1c79836435c9956da2d4c7a6..b042590ef43be92c07c3a75198934735b872a94a 100644 --- a/include/fggl/data/model.hpp +++ b/include/fggl/data/model.hpp @@ -167,8 +167,7 @@ namespace fggl::data { data::Mesh mesh; std::string pipeline; - inline StaticMesh() : mesh(), pipeline() {} - + inline StaticMesh() = default; inline StaticMesh(const data::Mesh &aMesh, std::string aPipeline) : mesh(aMesh), pipeline(std::move(aPipeline)) {} }; diff --git a/include/fggl/entity/loader/loader.hpp b/include/fggl/entity/loader/loader.hpp index 2ba077519b4175f8c63d20abe57e69547b460a50..a23493619b97beba9bfe05a2db3a48d7b5169d77 100644 --- a/include/fggl/entity/loader/loader.hpp +++ b/include/fggl/entity/loader/loader.hpp @@ -47,34 +47,26 @@ namespace fggl::entity { inline EntityFactory(modules::Services &services) : m_services(services) {} EntityID create(const EntityType &spec, EntityManager &manager, const CustomiseFunc &customise = nullptr) { - debug::warning("creating {}", spec); - assert( m_prototypes.contains(spec) && "asked to create undefined prototype!" ); + std::vector<CustomiseFunc> finishers; - try { - std::vector<CustomiseFunc> finishers; - - // set up the components for the entity - auto entity = setupComponents(spec, manager, finishers); - if (entity == entity::INVALID) { - debug::error("EntityFactory: failed to build from prototype {}", std::to_string(spec.get())); - return entity::INVALID; - } - - // allow the caller to perform any setup needed - if (customise != nullptr) { - customise(manager, entity); - } + // build the setup + auto entity = setupComponents(spec, manager, finishers); + if ( entity == entity::INVALID ) { + debug::error("EntityFactory: failed to build from prototype {}", std::to_string(spec.get())); + return entity::INVALID; + } - // finally, we run any cleanup/init setups required by the component factories - for (auto &finisher : finishers) { - finisher(manager, entity); - } + // if requested, allow the user to customize the creation + if ( customise != nullptr ) { + customise(manager, entity); + } - return entity; - } catch (std::out_of_range &ex) { - debug::error("EntityFactory: Unknown entity type '{}'", spec); - return fggl::entity::INVALID; + // run finishers for components + for ( auto& finisher : finishers ) { + finisher( manager, entity ); } + + return entity; } void log_known_types() const { @@ -113,7 +105,13 @@ namespace fggl::entity { auto currentType = entityType; while (currentType != NO_PARENT) { - auto &entitySpec = m_prototypes.at(currentType); + const auto& specEntry = m_prototypes.find( currentType ); + if ( specEntry == m_prototypes.end() ) { + debug::warning("Asked to setup {}, for {} but was not a known prototype", specEntry->first, entityType); + return entity::INVALID; + } + + auto entitySpec = specEntry->second; debug::debug("constructing {} for {} ({} comps)", currentType, entityType, entitySpec.components.size()); assert( entitySpec.ordering.size() == entitySpec.components.size() && "ordering incorrect size, bad things happend!" ); diff --git a/include/fggl/platform/fallback/file.hpp b/include/fggl/platform/fallback/file.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8d2b855a1ea73c67a4a14af8f21fb36230a2f8ce --- /dev/null +++ b/include/fggl/platform/fallback/file.hpp @@ -0,0 +1,63 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 19/09/22. +// + +#ifndef FGGL_PLATFORM_FALLBACK_FILE_HPP +#define FGGL_PLATFORM_FALLBACK_FILE_HPP + +#include <cstdio> +#include <cassert> + +namespace fggl::platform { + + class File { + public: + inline File(FILE* filePtr) : m_handle(filePtr) { + assert(filePtr != nullptr); + } + + inline ~File() { + release(); + } + + template<typename T> + inline void write(const T* dataPtr) { + assert( m_handle != nullptr ); + int status = fwrite(dataPtr, sizeof(T), 1, m_handle ); + assert( status == 1); + } + + template<typename T> + inline void writeArr(const T* dataPtr, std::size_t numElms) { + assert( m_handle != nullptr); + int status = fwrite(dataPtr, sizeof(T), numElms, m_handle ); + assert( status == 1); + } + + private: + std::FILE* m_handle; + + inline void release() { + if ( m_handle != NULL) { + fclose(m_handle); + } + } + }; + +} // namespace fggl::platform + +#endif //FGGL_PLATFORM_FALLBACK_FILE_HPP diff --git a/tools/pack/CMakeLists.txt b/tools/pack/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..88c5dc565b7558dba0bcf3f5416b29eb784b374e --- /dev/null +++ b/tools/pack/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(fgpak) + +target_link_libraries(fgpak fggl) + +target_sources(fgpak + PRIVATE + src/main.cpp +) \ No newline at end of file diff --git a/tools/pack/src/binary.hpp b/tools/pack/src/binary.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8c52fdcf8ad0ec80b33c123bc784526b19b8e2f5 --- /dev/null +++ b/tools/pack/src/binary.hpp @@ -0,0 +1,135 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 11/09/22. +// + +#ifndef FGGL_TOOLS_PACK_INCLUDES_BINARY_HPP +#define FGGL_TOOLS_PACK_INCLUDES_BINARY_HPP + +#include <cstdint> +#include <iostream> +#include <cstdio> + +#include "fggl/data/model.hpp" + +namespace fggl::data { + + enum class ModelType { + OPENGL + }; + + struct ModelHeader { + unsigned long guid; + std::size_t size; + ModelType type; + }; + + void write_mesh(FILE* file, const Mesh& mesh) { + static_assert( std::is_standard_layout_v<Vertex> ); + static_assert( std::is_standard_layout_v<Mesh::IndexType> ); + + // write vertex data + std::size_t vertexCount = mesh.vertexCount(); + fwrite( &vertexCount, sizeof(vertexCount), 1, file ); + fwrite( mesh.vertexList().data(), sizeof(Vertex), vertexCount, file ); + + std::size_t indexCount = mesh.indexCount(); + fwrite( &indexCount, sizeof(indexCount), 1, file); + fwrite( mesh.indexList().data(), sizeof(Mesh::IndexType), indexCount, file); + } + + void write_model(FILE* file, const ModelHeader& header, const Mesh& mesh) { + assert( header.type == ModelType::OPENGL ); + fwrite( &header , sizeof(ModelHeader), 1, file); + write_mesh(file, mesh); + } + + void read_mesh(FILE* fin, data::Mesh& mesh) { + static_assert( std::is_standard_layout_v<Vertex> ); + static_assert( std::is_standard_layout_v<Mesh::IndexType> ); + std::size_t readCount; + + // vertex data + std::size_t vertexCount = 0; + readCount = fread( &vertexCount, sizeof(vertexCount), 1, fin ); + assert(ferror(fin) == 0); + if (readCount != 1) { + std::cerr << "failed to read vertex count" << std::endl; + return; + } + + // push vertex data into mesh + auto* vertexData = new data::Vertex[vertexCount]; + readCount = fread( vertexData, sizeof(Vertex), vertexCount, fin ); + assert(ferror(fin) == 0); + if ( readCount != vertexCount ) { + std::cerr << "failed to read vertex data" << std::endl; + return; + } + for ( std::size_t i = 0; i < vertexCount; ++i) { + mesh.pushVertex(vertexData[i]); + } + delete[] vertexData; + + // read index size + std::size_t indexCount = 0; + readCount = fread( &indexCount, sizeof(indexCount), 1, fin); + assert(ferror(fin) == 0); + if (readCount != 1 ) { + std::cerr << "failed to read index count" << std::endl; + return; + } + + // read index data + auto* idxData = new Mesh::IndexType[indexCount]; + readCount = fread( idxData, sizeof(Mesh::IndexType), indexCount, fin); + assert(ferror(fin) == 0); + + if (readCount != indexCount) { + std::cerr << "failed to read index data, expected: " << indexCount << ", got: " << readCount << std::endl; + return; + } + + for (int i=0; i < indexCount; ++i) { + mesh.pushIndex( idxData[i] ); + } + delete[] idxData; + } + + void read_model(FILE* file, ModelHeader& header, Mesh& mesh) { + fread( &header, sizeof(ModelHeader), 1, file); + if ( header.type == ModelType::OPENGL ) { + read_mesh(file, mesh); + } + } + +} + +#endif //FGGL_TOOLS_PACK_INCLUDES_BINARY_HPP diff --git a/tools/pack/src/main.cpp b/tools/pack/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..afc26fdb4ab6ccba13b8b3af07e27cb5fca38104 --- /dev/null +++ b/tools/pack/src/main.cpp @@ -0,0 +1,158 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 11/09/22. +// + +#include <cstdlib> +#include <iostream> +#include <fstream> +#include <filesystem> +#include <map> + +#include <yaml-cpp/yaml.h> + +#include "fggl/data/model.hpp" +#include "fggl/data/procedural.hpp" +#include "fggl/entity/loader/loader.hpp" + +#include "binary.hpp" + +constexpr const char* YAML_PREFAB = "prefabs"; +constexpr const char* YAML_COMPONENT = "components"; + +constexpr uint32_t DEFAULT_STACKS = 16; +constexpr uint32_t DEFAULT_SLICES = 16; +constexpr const char *SHAPE_SPHERE{"sphere"}; +constexpr const char *SHAPE_BOX{"box"}; + +namespace fggl { + static void process_shape(const YAML::Node &node, data::Mesh &mesh) { + auto transform = data::OFFSET_NONE; + + auto offset = node["offset"].as<math::vec3>(math::VEC3_ZERO); + transform = glm::translate(transform, offset); + + auto scale = node["scale"].as<math::vec3>(math::VEC3_ONES); + transform = glm::scale(transform, scale); + debug::debug("scale: {}, {}, {}", scale.x, scale.y, scale.z); + + // now the shape itself + auto type = node["type"].as<std::string>(); + if (type == SHAPE_BOX) { + data::make_cube(mesh, transform); + } else if (type == SHAPE_SPHERE) { + auto stacks = node["stacks"].as<uint32_t>(DEFAULT_STACKS); + auto slices = node["slices"].as<uint32_t>(DEFAULT_SLICES); + data::make_sphere(mesh, transform, stacks, slices); + } else { + debug::log(debug::Level::warning, "unknown shape type requested: {}", type); + } + } + + static void process_mesh(const YAML::Node& spec, fggl::data::Mesh& mesh) { + // process shape structure + if (spec["shape"].IsSequence()) { + for (const auto &node : spec["shape"]) { + process_shape(node, mesh); + } + } else { + process_shape(spec["shape"], mesh); + } + mesh.removeDups(); + } +} + +void write_comp_static_model(FILE* fout, const YAML::Node& config) { + // calculate mesh + fggl::data::Mesh mesh; + fggl::process_mesh( config, mesh); + + // calculate correct name + auto meshName = config["shape_id"]; + auto meshNameStr = meshName.as<std::string>(); + auto meshGuid = fggl::util::make_guid_rt(meshNameStr); + + // header data + fggl::data::ModelHeader header{ + .guid = meshGuid.get(), + .type = fggl::data::ModelType::OPENGL, + }; + + // write the mesh to disk + fggl::data::write_model( fout, header, mesh ); +} + +int main(int argc, char* argv[]) { + auto* packName = argv[1]; + std::cout << "generating " << packName << std::endl; + + std::map< fggl::util::GUID, std::string > guids; + + // rollball + auto rollPath = std::filesystem::current_path() / "rollball.yml"; + std::cout << rollPath << std::endl; + + YAML::Node root = YAML::LoadFile(rollPath); + if ( !root ){ + return EXIT_FAILURE; + } + + //std::cout << root[ YAML_PREFAB ] << std::endl; + + std::map<std::string, std::function<void(FILE* fout, const YAML::Node&)>> converters; + converters["StaticMesh"] = write_comp_static_model; + + std::string meshFile = "rollball_models.bin"; + FILE* fout = fopen(meshFile.c_str(), "w"); + + // pack prefabs + for (const auto& prefab : root[YAML_PREFAB]) { + // name + auto name = prefab["name"].as<std::string>(); + auto nameRef = fggl::util::make_guid(name.c_str()); + guids[nameRef] = name; + std::cout << name << std::endl; + + // parent + //auto parentName = prefab["parent"].as<std::string>(); + //auto parentGuid = fggl::util::make_guid(parentName.c_str()); + + // components + for( const auto& key : prefab[YAML_COMPONENT] ) { + auto compStr = key.first.as<std::string>(); + auto compGuid = fggl::util::make_guid_rt(compStr); + guids[compGuid] = compStr; + + // figure out the type + auto compWrite = converters.find(compStr); + if ( compWrite != converters.end()) { + compWrite->second(fout, key.second); + } + } + + } + + fclose(fout); + + // guid table + std::cerr << "GUID Table" << std::endl; + std::cerr << "guid,str" << std::endl; + for (auto& [guid,str] : guids) { + std::cerr << guid.get() << "," << str << std::endl; + } + + return EXIT_SUCCESS; +} \ No newline at end of file