Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gamedev/fggl
  • onuralpsezer/fggl
2 results
Show changes
Showing
with 1689 additions and 1175 deletions
find_package(assimp CONFIG)
if (MSVC)
target_link_libraries(${PROJECT_NAME} PUBLIC assimp::assimp)
else ()
target_link_libraries(${PROJECT_NAME} PUBLIC assimp)
endif ()
target_sources(fggl
PRIVATE
module.cpp
image.cpp
)
\ No newline at end of file
...@@ -12,19 +12,9 @@ ...@@ -12,19 +12,9 @@
* If not, see <https://www.gnu.org/licenses/>. * If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef FGGL_DATA_PROCEDURE_HPP //
#define FGGL_DATA_PROCEDURE_HPP // Created by webpigeon on 22/10/22.
//
namespace fggl::data { #define STB_IMAGE_IMPLEMENTATION
#include "../../stb/stb_image.h"
class DataRegistry {
public:
DataRegistry();
~DataRegistry();
};
}
#endif
/*
* 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 18/10/22.
//
#include "fggl/data/assimp/module.hpp"
#include "fggl/debug/logging.hpp"
#include "fggl/assets/manager.hpp"
#include "fggl/mesh/mesh.hpp"
#include "../../stb/stb_image.h"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <filesystem>
namespace fggl::data::models {
constexpr auto convert(aiVector3D& vec) -> math::vec3 {
return {vec.x, vec.y, vec.z};
}
constexpr auto convert2(aiVector2D& vec) -> math::vec2 {
return {vec.x, vec.y};
}
constexpr auto convert2(aiVector3D& vec) -> math::vec2 {
return {vec.x, vec.y};
}
constexpr auto convert(aiColor3D& col) -> math::vec3 {
return {col.r, col.g, col.b};
}
static void process_mesh(mesh::Mesh3D& mesh, aiMesh* assimpMesh, const aiScene* scene, const std::vector<assets::AssetID>& assets) {
assert( assimpMesh != nullptr );
assert( scene != nullptr );
// process vertex data
for ( auto idx = 0U; idx < assimpMesh->mNumVertices; ++idx ) {
mesh::Vertex3D vertex{
.position = convert(assimpMesh->mVertices[idx]),
.normal = convert(assimpMesh->mNormals[idx])
};
if ( assimpMesh->mTextureCoords[0] != nullptr ) {
vertex.texPos = convert2( assimpMesh->mTextureCoords[0][idx] );
}
mesh.data.push_back(vertex);
}
// process faces (indexes)
for ( auto idx = 0U; idx < assimpMesh->mNumFaces; ++idx ) {
auto face = assimpMesh->mFaces[idx];
assert( face.mNumIndices == 3);
for ( auto vid = 0U; vid < face.mNumIndices; ++vid) {
mesh.indices.push_back( face.mIndices[vid] );
}
}
// process material
if ( assimpMesh->mMaterialIndex >= 0) {
mesh.material = assets[assimpMesh->mMaterialIndex];
}
}
static void process_node(mesh::MultiMesh3D& mesh, aiNode* node, const aiScene* scene, const std::vector<assets::AssetID>& assets) {
for ( auto idx = 0U; idx < node->mNumMeshes; ++idx ) {
auto *assimpMesh = scene->mMeshes[ node->mMeshes[idx] ];
// process assimp submesh
mesh::Mesh3D meshData;
process_mesh(meshData, assimpMesh, scene, assets);
mesh.meshes.push_back(meshData);
}
for ( auto idx = 0U; idx < node->mNumChildren; ++idx) {
process_node(mesh, node->mChildren[idx], scene, assets);
}
}
struct AssetStuff {
assets::Loader* loader;
assets::AssetManager* manager;
inline void checkLoaded(assets::AssetID asset) {
if (!manager->has(asset)) {
debug::info("triggered JIT upload for {}, use the chain loader", asset);
loader->load(asset);
}
}
inline auto isLoaded(assets::AssetID asset) const -> bool {
return manager->has(asset);
}
inline void set(assets::AssetID asset, auto* assetPtr) {
assert(assetPtr != nullptr);
manager->set(asset, assetPtr);
}
};
static auto process_texture( const aiTextureType type, const aiMaterial* assimpMat, AssetStuff& stuff, const assets::LoaderContext& config) -> std::vector<assets::AssetID> {
std::vector<assets::AssetID> matRefs;
matRefs.reserve( assimpMat->GetTextureCount(type) );
// iterate through things
for ( auto i = 0U ; i < assimpMat->GetTextureCount(type); ++i ) {
aiString texName;
assimpMat->GetTexture( type, i, &texName );
const auto texID = config.makeRef(texName.C_Str());
// trigger asset loading
stuff.checkLoaded( texID );
// assets
matRefs.push_back( texID );
}
return matRefs;
}
static auto process_mat_colour(const aiMaterial* mat, const char* name, int a1, int a2) -> math::vec3 {
aiColor3D col{0.0F, 0.0F, 0.0F};
mat->Get( name, a1, a2, col );
debug::info("read colour: {}, {}, {}, {}", name, col.r, col.g, col.b);
return convert(col);
}
static void process_material(const assets::AssetID& guid, aiMaterial* assimpMat, AssetStuff stuff, const assets::LoaderContext& config) {
auto* material = new mesh::Material();
debug::info("processing: {}", guid);
// for each material, calculate what it's name would be then request it
material->diffuseTextures = process_texture( aiTextureType_DIFFUSE, assimpMat, stuff, config );
material->normalTextures = process_texture( aiTextureType_NORMALS, assimpMat, stuff, config );
material->specularTextures = process_texture( aiTextureType_SPECULAR, assimpMat, stuff, config );
material->ambient = process_mat_colour( assimpMat, AI_MATKEY_COLOR_AMBIENT );
material->diffuse = process_mat_colour( assimpMat, AI_MATKEY_COLOR_DIFFUSE );
material->specular = process_mat_colour( assimpMat, AI_MATKEY_COLOR_SPECULAR );
stuff.set( guid, material );
}
auto load_assimp_model(assets::Loader* loader, const assets::AssetID& guid, const assets::LoaderContext& data, void* userPtr) -> assets::AssetRefRaw {
// auto *filePath = std::get<assets::AssetPath *>(data);
auto filePath = data.assetPath;
AssetStuff stuff {
.loader = loader,
.manager = (assets::AssetManager*)userPtr
};
if ( stuff.isLoaded(guid) ) {
// asset in DB, what do you want me to do?
return nullptr;
}
// assimp stuff
Assimp::Importer importer;
// import the scene from disk
const aiScene *scene = importer.ReadFile(filePath.c_str(), aiProcessPreset_TargetRealtime_Fast | aiProcess_FlipUVs);
if ( scene == nullptr || (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) != 0 || scene->mRootNode == nullptr ) {
debug::warning("unable to load required model asset");
return nullptr;
}
debug::debug("Processing assimp mesh, {} meshes, {} materials, {} textures", scene->mNumMeshes, scene->mNumMaterials, scene->mNumTextures);
// calculate the mapping from material => asset mappings
auto parentRel = std::filesystem::relative( filePath.parent_path(), filePath.parent_path().parent_path() );
std::vector<assets::AssetID> matAssets;
for ( auto idx = 0U; idx < scene->mNumMaterials; ++idx ) {
aiString matName = scene->mMaterials[idx]->GetName();
auto matRel = parentRel / matName.C_Str();
matAssets.push_back( assets::make_asset_id_rt( data.pack, matRel ) );
}
// now we can try importing the mesh data
auto* packedMesh = new mesh::MultiMesh3D();
process_node( *packedMesh, scene->mRootNode, scene, matAssets);
// now we try importing the materials
for (auto idx = 0U; idx < scene->mNumMaterials; ++idx ) {
process_material( matAssets[idx], scene->mMaterials[idx], stuff, data );
}
// FIXME asset loading system needs rework, this is bonkers.
stuff.set(guid, packedMesh);
return nullptr;
}
auto load_assimp_texture(assets::Loader* /*loader*/, const assets::AssetID& guid, const assets::LoaderContext& data, void* userPtr) -> assets::AssetRefRaw {
auto* manager = (assets::AssetManager*)userPtr;
if ( manager->has(guid) ) {
// already loaded.
return nullptr;
}
auto filePath = data.assetPath;
stbi_set_flip_vertically_on_load(1);
//load the texture data into memory
auto* image = new data::Texture2D();
image->data = stbi_load(filePath.c_str(), &image->size.x, &image->size.y, &image->channels, 3);
if ( image->data == nullptr ) {
debug::warning("error reading texture: {}", stbi_failure_reason());
return nullptr;
} else {
debug::info("image reports it loaded correctly, adding {} to database", guid);
manager->set(guid, image );
}
return nullptr;
}
auto load_tex_stb(const std::filesystem::path& filePath, assets::MemoryBlock& block) -> bool {
stbi_set_flip_vertically_on_load(1);
//load the texture data into memory
auto* image = new data::Texture2D();
image->data = stbi_load(filePath.c_str(), &image->size.x, &image->size.y, &image->channels, 3);
if ( image->data == nullptr ) {
debug::warning("error reading texture: {}", stbi_failure_reason());
delete image;
return false;
} else {
// TODO pass metadata to loader in a sensible way
block.size = image->channels * image->size.x * image->size.y;
block.data = (std::byte*)image->data;
delete image;
return true;
}
}
auto is_tex_stb(const std::filesystem::path& filePath) -> assets::AssetTypeID {
// detect jpgs
if ( filePath.extension() == ".jpg" || filePath.extension() == ".jpeg" ) {
return TEXTURE_RGBA;
}
// detect png
if ( filePath.extension() == ".png" ) {
return TEXTURE_RGBA;
}
return assets::INVALID_ASSET_TYPE;
}
auto is_model_assimp(const std::filesystem::path& filePath) -> assets::AssetTypeID {
auto ext = filePath.extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
if ( ext == ".obj" || ext == ".fbx" ) {
return MODEL_MULTI3D;
}
return assets::INVALID_ASSET_TYPE;
}
auto extract_requirements(const std::string& packName, const std::filesystem::path& packRoot, assets::ResourceRecord& rr) -> bool {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile( rr.m_path, aiProcess_Triangulate | aiProcess_FlipUVs);
if ( !scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode ) {
debug::warning("unable to load required model asset");
return false;
}
// we want to find out about dependencies
auto baseDir = std::filesystem::relative( rr.m_path.parent_path(), packRoot );
for ( auto idx = 0U; idx < scene->mNumMaterials; ++idx) {
const auto *mat = scene->mMaterials[idx];
std::array matTypes { aiTextureType_DIFFUSE, aiTextureType_NORMALS, aiTextureType_SPECULAR };
for ( auto& matType : matTypes ) {
const auto diffCount = mat->GetTextureCount(matType);
for (auto idx2 = 0U; idx2 < diffCount; ++idx2) {
aiString texName;
mat->GetTexture(matType, idx2, &texName);
auto assetPath = baseDir / texName.C_Str();
auto assetRequired = assets::make_asset_id_rt(packName, assetPath);
rr.m_requires.push_back(assetRequired);
}
}
}
return true;
}
auto AssimpModule::factory(modules::ServiceName service, modules::Services &serviceManager) -> bool {
if ( service == MODEL_PROVIDER ) {
auto* assetLoader = serviceManager.get<assets::Loader>();
assetLoader->setFactory( MODEL_MULTI3D, load_assimp_model, assets::LoadType::PATH );
assetLoader->setFactory( TEXTURE_RGBA, load_assimp_texture, assets::LoadType::PATH );
// new loading system
auto* checkin = serviceManager.get<assets::CheckinAdapted>();
checkin->setLoader( MIME_JPG, load_tex_stb, is_tex_stb );
checkin->setLoader( MIME_PNG, load_tex_stb, is_tex_stb );
checkin->setLoader( MIME_OBJ, assets::NEEDS_CHECKIN, is_model_assimp );
checkin->setLoader( MIME_FBX, assets::NEEDS_CHECKIN, is_model_assimp );
checkin->setProcessor( MIME_OBJ, extract_requirements);
checkin->setProcessor( MIME_FBX, extract_requirements );
return false;
}
return false;
}
} // namespace fggl::data
...@@ -23,103 +23,104 @@ ...@@ -23,103 +23,104 @@
namespace fggl::data { namespace fggl::data {
void gridVertexNormals(data::Vertex *locations) { void gridVertexNormals(data::Vertex *locations) {
const int sizeX = data::heightMaxX; const int sizeX = data::heightMaxX;
const int sizeY = data::heightMaxZ; const int sizeY = data::heightMaxZ;
const int gridOffset = sizeX * sizeY; const int gridOffset = sizeX * sizeY;
// calculate normals for each triangle // calculate normals for each triangle
math::vec3* triNormals = new math::vec3[sizeX * sizeY * 2]; auto *triNormals = new math::vec3[sizeX * sizeY * 2];
for (int i = 0; i < sizeX - 1; i++) { for (int i = 0; i < sizeX - 1; i++) {
for (int j = 0; j < sizeY - 1; j++) { for (int j = 0; j < sizeY - 1; j++) {
// calculate vertex // calculate vertex
const auto &a = locations[i * sizeY + j].posititon; const auto &a = locations[i * sizeY + j].posititon;
const auto &b = locations[(i + 1) * sizeY + j].posititon; const auto &b = locations[(i + 1) * sizeY + j].posititon;
const auto &c = locations[i * sizeY + (j + 1)].posititon; const auto &c = locations[i * sizeY + (j + 1)].posititon;
const auto &d = locations[(i + 1) * sizeY + (j + 1)].posititon; const auto &d = locations[(i + 1) * sizeY + (j + 1)].posititon;
const auto normalA = glm::cross(b - a, a - d); const auto normalA = glm::cross(b - a, a - d);
const auto normalB = glm::cross(d - c, c - b); const auto normalB = glm::cross(d - c, c - b);
// store the normals // store the normals
int idx1 = idx(i, j, sizeY); int idx1 = idx(i, j, sizeY);
int idx2 = idx1 + gridOffset; int idx2 = idx1 + gridOffset;
triNormals[idx1] = glm::normalize(normalA); triNormals[idx1] = glm::normalize(normalA);
triNormals[idx2] = glm::normalize(normalB); triNormals[idx2] = glm::normalize(normalB);
} }
} }
// calculate normals for each vertex // calculate normals for each vertex
for (int i = 0; i < sizeX; i++) { for (int i = 0; i < sizeX; i++) {
for (int j = 0; j < sizeY; j++) { for (int j = 0; j < sizeY; j++) {
const auto firstRow = (i == 0); const auto firstRow = (i == 0);
const auto firstCol = (j == 0); const auto firstCol = (j == 0);
const auto lastRow = (i == sizeX - 1); const auto lastRow = (i == sizeX - 1);
const auto lastCol = (i == sizeY - 1); const auto lastCol = (i == sizeY - 1);
auto finalNormal = glm::vec3(0.0f, 0.0f, 0.0f); auto finalNormal = glm::vec3(0.0F, 0.0F, 0.0F);
if (!firstRow && !firstCol) { if (!firstRow && !firstCol) {
finalNormal += triNormals[idx(i - 1, j - 1, sizeY)]; finalNormal += triNormals[idx(i - 1, j - 1, sizeY)];
} }
if (!lastRow && !lastCol) { if (!lastRow && !lastCol) {
finalNormal += triNormals[idx(i, j, sizeY)]; finalNormal += triNormals[idx(i, j, sizeY)];
} }
if (!firstRow && lastCol) { if (!firstRow && lastCol) {
finalNormal += triNormals[idx(i - 1, j, sizeY)]; finalNormal += triNormals[idx(i - 1, j, sizeY)];
finalNormal += triNormals[idx(i - 1, j, sizeY) + gridOffset]; finalNormal += triNormals[idx(i - 1, j, sizeY) + gridOffset];
} }
if (!lastRow && !firstCol) { if (!lastRow && !firstCol) {
finalNormal += triNormals[idx(i, j - 1, sizeY)]; finalNormal += triNormals[idx(i, j - 1, sizeY)];
finalNormal += triNormals[idx(i, j - 1, sizeY) + gridOffset]; finalNormal += triNormals[idx(i, j - 1, sizeY) + gridOffset];
} }
locations[idx(i, j, sizeY)].normal = glm::normalize(finalNormal) * -1.0f; //FIXME the normals seem wrong. locations[idx(i, j, sizeY)].normal =
} glm::normalize(finalNormal) * -1.0F; //FIXME the normals seem wrong.
} }
delete[] triNormals; }
} delete[] triNormals;
}
void generateHeightMesh(const data::HeightMap& heights, data::Mesh &mesh) {
void generateHeightMesh(const data::HeightMap &heights, data::Mesh &mesh) {
// step 1: convert height data into vertex locations
const int numElms = data::heightMaxX * data::heightMaxZ; // step 1: convert height data into vertex locations
const int numElms = data::heightMaxX * data::heightMaxZ;
std::array<data::Vertex, numElms> locations{}; std::array<data::Vertex, numElms> locations{};
// iterate the // iterate the
for (std::size_t x = 0; x < data::heightMaxX; x++) { for (std::size_t x = 0; x < data::heightMaxX; x++) {
for (std::size_t z = 0; z < data::heightMaxZ; z++) { for (std::size_t z = 0; z < data::heightMaxZ; z++) {
float level = heights.getValue(x, z); float level = heights.getValue(x, z);
auto xPos = float(x); auto xPos = float(x);
auto zPos = float(z); auto zPos = float(z);
std::size_t idx1 = idx(x, z, data::heightMaxZ); std::size_t idx1 = idx(x, z, data::heightMaxZ);
locations[idx1].colour = fggl::math::vec3(1.0f, 1.0f, 1.0f); locations[idx1].colour = fggl::math::vec3(1.0F, 1.0F, 1.0F);
locations[idx1].posititon = math::vec3(-0.5f + xPos, level, -0.5f - zPos); locations[idx1].posititon = math::vec3(-0.5F + xPos, level, -0.5F - zPos);
} }
} }
gridVertexNormals(locations.data()); gridVertexNormals(locations.data());
mesh.restartVertex = data::heightMaxZ * data::heightMaxX; mesh.restartVertex = data::heightMaxZ * data::heightMaxX;
// populate mesh // populate mesh
for (auto i = 0; i < numElms; i++) { for (auto i = 0; i < numElms; i++) {
mesh.pushVertex(locations[i]); mesh.pushVertex(locations[i]);
} }
for (std::size_t x = 0; x < data::heightMaxX - 1; x++) { for (std::size_t x = 0; x < data::heightMaxX - 1; x++) {
for (std::size_t z = 0; z < data::heightMaxZ; z++) { for (std::size_t z = 0; z < data::heightMaxZ; z++) {
for (int k=0; k < 2; k++) { for (int k = 0; k < 2; k++) {
auto idx = (x+k) * data::heightMaxZ + z; auto idx = (x + k) * data::heightMaxZ + z;
mesh.pushIndex(idx); mesh.pushIndex(idx);
} }
} }
mesh.pushIndex(mesh.restartVertex); mesh.pushIndex(mesh.restartVertex);
} }
} }
} }
...@@ -24,19 +24,19 @@ Mesh::Mesh() : restartVertex(Mesh::INVALID_IDX), m_verts(0), m_index(0) { ...@@ -24,19 +24,19 @@ Mesh::Mesh() : restartVertex(Mesh::INVALID_IDX), m_verts(0), m_index(0) {
} }
void Mesh::pushIndex(unsigned int idx) { void Mesh::pushIndex(unsigned int idx) {
assert( idx < m_verts.size() || idx == this->restartVertex ); assert(idx < m_verts.size() || idx == this->restartVertex);
m_index.push_back(idx); m_index.push_back(idx);
} }
Mesh::IndexType Mesh::pushVertex(Vertex vert) { auto Mesh::pushVertex(Vertex vert) -> Mesh::IndexType {
auto idx = m_verts.size(); auto idx = m_verts.size();
m_verts.push_back( vert ); m_verts.push_back(vert);
return idx; return idx;
} }
Mesh::IndexType Mesh::indexOf(Vertex term) { auto Mesh::indexOf(Vertex term) -> Mesh::IndexType {
auto itr = std::find(m_verts.begin(), m_verts.end(), term); auto itr = std::find(m_verts.begin(), m_verts.end(), term);
if ( itr == m_verts.end() ){ if (itr == m_verts.end()) {
return INVALID_IDX; return INVALID_IDX;
} }
return itr - m_verts.begin(); return itr - m_verts.begin();
...@@ -47,24 +47,24 @@ void Mesh::removeDups() { ...@@ -47,24 +47,24 @@ void Mesh::removeDups() {
std::map<Vertex, unsigned int> mapping; std::map<Vertex, unsigned int> mapping;
// new data // new data
std::vector< Vertex > newVerts; std::vector<Vertex> newVerts;
std::vector< unsigned int > newIndexes; std::vector<unsigned int> newIndexes;
newIndexes.reserve( m_index.size() ); // newIndex will be same size as oldIndex newIndexes.reserve(m_index.size()); // newIndex will be same size as oldIndex
unsigned int nextID = 0; unsigned int nextID = 0;
for ( auto& idx : m_index ) { for (auto &idx : m_index) {
auto& vertex = m_verts[idx]; auto &vertex = m_verts[idx];
auto newID = nextID; auto newID = nextID;
// if the vertex is known, use the existing ID, else allocate a new ID // if the vertex is known, use the existing ID, else allocate a new ID
try { try {
newID = mapping.at( vertex ); newID = mapping.at(vertex);
} catch ( std::out_of_range& e ){ } catch (std::out_of_range &e) {
mapping[ vertex ] = newID; mapping[vertex] = newID;
newVerts.push_back( vertex ); newVerts.push_back(vertex);
++nextID; ++nextID;
} }
newIndexes.push_back( newID ); newIndexes.push_back(newID);
} }
// assign the replacement lists // assign the replacement lists
......
/*
* 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 20/08/22.
//
#include "fggl/data/module.hpp"
namespace fggl::data {
auto LocalStorage::factory(modules::ServiceName service, modules::Services &data) -> bool {
if (service == SERVICE_STORAGE) {
// FIXME: no easy way to set the application name
auto pathConfig = fggl::platform::calc_engine_paths("fggl-demo");
data.create<Storage>(pathConfig);
return true;
}
return false;
}
} // namespace fggl::data
\ No newline at end of file
...@@ -17,64 +17,63 @@ ...@@ -17,64 +17,63 @@
#include <fggl/data/model.hpp> #include <fggl/data/model.hpp>
#include <glm/ext/matrix_transform.hpp> #include <glm/ext/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <iostream>
#include <array> #include <array>
#include <glm/geometric.hpp> #include <glm/geometric.hpp>
#include "fggl/mesh/mesh.hpp"
using namespace fggl::data; using namespace fggl::data;
// from https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal // from https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal
static glm::vec3 calcSurfaceNormal(glm::vec3 vert1, glm::vec3 vert2, glm::vec3 vert3) { static auto calcSurfaceNormal(glm::vec3 vert1, glm::vec3 vert2, glm::vec3 vert3) -> glm::vec3 {
const glm::vec3 edge1 = vert2 - vert1; const glm::vec3 edge1 = vert2 - vert1;
const glm::vec3 edge2 = vert3 - vert1; const glm::vec3 edge2 = vert3 - vert1;
return glm::normalize( glm::cross( edge1, edge2 ) ); return glm::normalize(glm::cross(edge1, edge2));
} }
static void computeNormalsDirect( fggl::data::Mesh& mesh, const fggl::data::Mesh::IndexType * colIdx, int nPoints) { static void computeNormalsDirect(fggl::mesh::Mesh3D &mesh, const fggl::data::Mesh::IndexType *colIdx, int nPoints) {
// we're assuming all the normals are zero... // we're assuming all the normals are zero...
for ( int i=0; i<nPoints; i++ ) { for (int i = 0; i < nPoints; i++) {
auto& vertex = mesh.vertex( colIdx[i] ); auto &vertex = mesh.data[colIdx[i]];
vertex.normal = glm::vec3(0.0F); vertex.normal = glm::vec3(0.0F);
} }
// We're assuming each vertex appears only once (because we're not indexed) // We're assuming each vertex appears only once (because we're not indexed)
for (int i=0; i<nPoints; i += 3) { for (int i = 0; i < nPoints; i += 3) {
auto& v1 = mesh.vertex( colIdx[ i ] ); auto &v1 = mesh.data[colIdx[i]];
auto& v2 = mesh.vertex( colIdx[ i + 1 ] ); auto &v2 = mesh.data[colIdx[i + 1]];
auto& v3 = mesh.vertex( colIdx[ i + 2 ] ); auto &v3 = mesh.data[colIdx[i + 2]];
const glm::vec3 normal = glm::normalize( calcSurfaceNormal( v1.posititon, v2.posititon, v3.posititon ) ); const glm::vec3 normal = glm::normalize(calcSurfaceNormal(v1.position, v2.position, v3.position));
v1.normal = normal; v1.normal = normal;
v2.normal = normal; v2.normal = normal;
v3.normal = normal; v3.normal = normal;
} }
} }
static void compute_normals(fggl::data::Mesh& mesh, static void compute_normals(fggl::mesh::Mesh3D &mesh,
const std::vector<Mesh::IndexType>& idxList, // source index const std::vector<Mesh::IndexType> &idxList, // source index
const std::vector<Mesh::IndexType>& idxMapping // source-to-mesh lookup const std::vector<Mesh::IndexType> &idxMapping // source-to-mesh lookup
) { ) {
// clear the normals, so the summation below works correctly // clear the normals, so the summation below works correctly
for (auto vertexIndex : idxMapping) { for (auto vertexIndex : idxMapping) {
auto& vertex = mesh.vertex( vertexIndex ); auto &vertex = mesh.data[vertexIndex];
vertex.normal = ILLEGAL_NORMAL; vertex.normal = ILLEGAL_NORMAL;
} }
// we need to calculate the contribution for each vertex // we need to calculate the contribution for each vertex
// this assumes IDXList describes a raw triangle list (ie, not quads and not a strip) // this assumes IDXList describes a raw triangle list (ie, not quads and not a strip)
for ( std::size_t i=0; i < idxList.size(); i += 3) { for (std::size_t i = 0; i < idxList.size(); i += 3) {
auto& v1 = mesh.vertex( idxMapping[idxList[i]] ); auto &v1 = mesh.data[ idxMapping[idxList[i]] ];
auto& v2 = mesh.vertex( idxMapping[idxList[i + 1]] ); auto &v2 = mesh.data[ idxMapping[idxList[i + 1]] ];
auto& v3 = mesh.vertex( idxMapping[idxList[i + 2]] ); auto &v3 = mesh.data[ idxMapping[idxList[i + 2]] ];
// calculate the normal and area (formula for area the math textbook) // calculate the normal and area (formula for area the math textbook)
float area = glm::length(glm::cross(v3.posititon - v2.posititon, v1.posititon - v3.posititon)) / 2; float area = glm::length(glm::cross(v3.position - v2.position, v1.position - v3.position)) / 2;
auto faceNormal = calcSurfaceNormal(v1.posititon, v2.posititon, v3.posititon); auto faceNormal = calcSurfaceNormal(v1.position, v2.position, v3.position);
// weight the normal according to the area of the surface (bigger area = more impact) // weight the normal according to the area of the surface (bigger area = more impact)
v1.normal += area * faceNormal; v1.normal += area * faceNormal;
...@@ -84,32 +83,32 @@ static void compute_normals(fggl::data::Mesh& mesh, ...@@ -84,32 +83,32 @@ static void compute_normals(fggl::data::Mesh& mesh,
// re-normalise the normals // re-normalise the normals
for (unsigned int vertexIndex : idxMapping) { for (unsigned int vertexIndex : idxMapping) {
auto& vertex = mesh.vertex( vertexIndex ); auto &vertex = mesh.data[vertexIndex];
vertex.normal = glm::normalize(vertex.normal); vertex.normal = glm::normalize(vertex.normal);
} }
} }
static void populateMesh(fggl::data::Mesh& mesh, const fggl::math::mat4 transform, static void populateMesh(fggl::mesh::Mesh3D &mesh, const fggl::math::mat4 transform,
const int nIdx, const fggl::math::vec3* pos, const Mesh::IndexType* idx ) { const int nIdx, const fggl::math::vec3 *pos, const Mesh::IndexType *idx) {
auto* colIdx = new fggl::data::Mesh::IndexType[nIdx]; auto *colIdx = new fggl::data::Mesh::IndexType[nIdx];
// generate mesh // generate mesh
for (int i=0; i<nIdx; i++) { for (int i = 0; i < nIdx; i++) {
glm::vec3 rawPos = transform * glm::vec4( pos[ idx[i] ], 1.0 ); glm::vec3 rawPos = transform * glm::vec4(pos[idx[i]], 1.0);
colIdx[ i ] = mesh.pushVertex( Vertex::from_pos(rawPos) ); colIdx[i] = mesh.append(fggl::mesh::Vertex3D::from_pos(rawPos));
mesh.pushIndex( colIdx[i] ); mesh.indices.push_back(colIdx[i]);
} }
computeNormalsDirect( mesh, colIdx, nIdx ); computeNormalsDirect(mesh, colIdx, nIdx);
delete[] colIdx; delete[] colIdx;
} }
static void populateMesh(fggl::data::Mesh& mesh, static void populateMesh(fggl::mesh::Mesh3D &mesh,
const fggl::math::mat4 transform, const fggl::math::mat4 transform,
const std::vector<fggl::math::vec3>& posList, const std::vector<fggl::math::vec3> &posList,
const std::vector<fggl::data::Mesh::IndexType>& idxList) { const std::vector<fggl::data::Mesh::IndexType> &idxList) {
// tmp store the resulting mesh indexes (incase the mesh has multiple primatives) // tmp store the resulting mesh indexes (incase the mesh has multiple primatives)
std::vector<unsigned int> colIdx; std::vector<unsigned int> colIdx;
...@@ -118,12 +117,13 @@ static void populateMesh(fggl::data::Mesh& mesh, ...@@ -118,12 +117,13 @@ static void populateMesh(fggl::data::Mesh& mesh,
// clion this thinks this loop is infinite, my assumption is it's gone bananas // clion this thinks this loop is infinite, my assumption is it's gone bananas
for (std::size_t i = 0; i < posList.size(); ++i) { for (std::size_t i = 0; i < posList.size(); ++i) {
glm::vec3 position = transform * fggl::math::vec4(posList[i], 1.0F); glm::vec3 position = transform * fggl::math::vec4(posList[i], 1.0F);
colIdx[i] = mesh.pushVertex( Vertex::from_pos(position) ); auto vert = fggl::mesh::Vertex3D::from_pos(position);
colIdx[i] = mesh.append(vert);
} }
// use the remapped indexes for the mesh // use the remapped indexes for the mesh
for (auto idx : idxList) { for (auto idx : idxList) {
mesh.pushIndex( colIdx[idx] ); mesh.indices.push_back(colIdx[idx]);
} }
compute_normals(mesh, idxList, colIdx); compute_normals(mesh, idxList, colIdx);
...@@ -131,7 +131,7 @@ static void populateMesh(fggl::data::Mesh& mesh, ...@@ -131,7 +131,7 @@ static void populateMesh(fggl::data::Mesh& mesh,
namespace fggl::data { namespace fggl::data {
static void quads2Tris(std::vector<Mesh::IndexType>& indexList, uint32_t stacks, uint32_t slices) { static void quads2Tris(std::vector<Mesh::IndexType> &indexList, uint32_t stacks, uint32_t slices) {
const auto HORZ_SIZE = slices + 1; const auto HORZ_SIZE = slices + 1;
for (uint32_t vertical = 0; vertical < stacks; vertical++) { for (uint32_t vertical = 0; vertical < stacks; vertical++) {
...@@ -152,19 +152,19 @@ namespace fggl::data { ...@@ -152,19 +152,19 @@ namespace fggl::data {
} }
} }
void make_sphere(Mesh &mesh, const math::mat4& offset, uint32_t slices, uint32_t stacks) { void make_sphere(fggl::mesh::Mesh3D &mesh, const math::mat4 &offset, uint32_t slices, uint32_t stacks) {
std::vector<math::vec3> positions; std::vector<math::vec3> positions;
float verticalAngularStride = math::PI / (float)stacks; float verticalAngularStride = math::PI / (float) stacks;
float horizontalAngularStride = math::TAU / (float)slices; float horizontalAngularStride = math::TAU / (float) slices;
// calculate vertex positions // calculate vertex positions
for (uint32_t vertical = 0; vertical < (stacks + 1); vertical++) { for (uint32_t vertical = 0; vertical < (stacks + 1); vertical++) {
float theta = (math::HALF_PI) - verticalAngularStride * (float)vertical; float theta = (math::HALF_PI) - verticalAngularStride * (float) vertical;
for ( uint32_t horz = 0; horz < (slices + 1); horz++ ) { for (uint32_t horz = 0; horz < (slices + 1); horz++) {
float phi = horizontalAngularStride * (float)horz; float phi = horizontalAngularStride * (float) horz;
math::vec3 position { math::vec3 position{
cosf(theta) * cosf(phi), cosf(theta) * cosf(phi),
cosf(theta) * sinf(phi), cosf(theta) * sinf(phi),
sinf(theta) sinf(theta)
...@@ -180,131 +180,127 @@ namespace fggl::data { ...@@ -180,131 +180,127 @@ namespace fggl::data {
populateMesh(mesh, offset, positions, indexList); populateMesh(mesh, offset, positions, indexList);
} }
} // namespace fggl::data } // namespace fggl::data
fggl::data::Mesh fggl::data::make_triangle() { auto fggl::data::make_triangle() -> fggl::data::Mesh {
constexpr fggl::math::vec3 pos[]{ constexpr fggl::math::vec3 pos[]{
{-0.5F, -0.5F, 0.0F}, {-0.5F, -0.5F, 0.0F},
{0.5F, -0.5F, 0.0F}, {0.5F, -0.5F, 0.0F},
{0.0F, 0.5F, 0.0F} {0.0F, 0.5F, 0.0F}
}; };
// add points // add points
fggl::data::Mesh mesh; fggl::data::Mesh mesh;
for (auto po : pos) { for (auto po : pos) {
mesh.push( Vertex::from_pos(po) ); mesh.push(Vertex::from_pos(po));
} }
// mesh // mesh
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
mesh.pushIndex(i); mesh.pushIndex(i);
} }
return mesh; return mesh;
} }
fggl::data::Mesh fggl::data::make_quad_xy() { auto fggl::data::make_quad_xy() -> fggl::data::Mesh {
constexpr fggl::math::vec3 pos[] { constexpr fggl::math::vec3 pos[]{
{-0.5F, -0.5F, 0.0F}, {-0.5F, -0.5F, 0.0F},
{ 0.5F, -0.5F, 0.0F}, {0.5F, -0.5F, 0.0F},
{ 0.5F, 0.5F, 0.0F}, {0.5F, 0.5F, 0.0F},
{-0.5F, 0.5F, 0.0F} {-0.5F, 0.5F, 0.0F}
}; };
constexpr int idx[] { constexpr int idx[]{
0, 1, 3, 0, 1, 3,
3, 1, 2 3, 1, 2
}; };
fggl::data::Mesh mesh; fggl::data::Mesh mesh;
std::array<int,4> colIdx{}; std::array<int, 4> colIdx{};
for (int i = 0; i < 4; ++i){ for (int i = 0; i < 4; ++i) {
colIdx[ i ] = mesh.pushVertex( Vertex::from_pos(pos[i])); colIdx[i] = mesh.pushVertex(Vertex::from_pos(pos[i]));
} }
for( auto i : idx ) { for (auto i : idx) {
mesh.pushIndex( colIdx[ i ] ); mesh.pushIndex(colIdx[i]);
} }
return mesh; return mesh;
} }
fggl::data::Mesh fggl::data::make_quad_xz() { auto fggl::data::make_quad_xz() -> fggl::data::Mesh {
constexpr std::array<fggl::math::vec3, 4> pos {{ constexpr std::array<fggl::math::vec3, 4> pos{{
{-0.5F, 0.0F, -0.5F}, {-0.5F, 0.0F, -0.5F},
{0.5F, 0.0F, -0.5F}, {0.5F, 0.0F, -0.5F},
{0.5F, 0.0F, 0.5F}, {0.5F, 0.0F, 0.5F},
{-0.5F, 0.0F, 0.5F} {-0.5F, 0.0F, 0.5F}
}}; }};
constexpr std::array<int, 6> idx {{ constexpr std::array<int, 6> idx{{
0, 1, 3, 0, 1, 3,
3, 1, 2 3, 1, 2
}}; }};
fggl::data::Mesh mesh; fggl::data::Mesh mesh;
std::array<int, 4> colIdx{}; std::array<int, 4> colIdx{};
for (int i = 0; i < 4; ++i){ for (int i = 0; i < 4; ++i) {
colIdx[ i ] = mesh.pushVertex( Vertex::from_pos(pos[i]) ); colIdx[i] = mesh.pushVertex(Vertex::from_pos(pos[i]));
} }
for( auto i : idx ) { for (auto i : idx) {
mesh.pushIndex(colIdx.at( i )); mesh.pushIndex(colIdx.at(i));
} }
return mesh; return mesh;
} }
auto fggl::data::make_cube(fggl::mesh::Mesh3D &mesh, const fggl::math::mat4 &transform) -> fggl::mesh::Mesh3D {
fggl::data::Mesh fggl::data::make_cube(fggl::data::Mesh& mesh, const fggl::math::mat4& transform) {
// done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7 // done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7
constexpr std::array<fggl::math::vec3, 8> pos {{ constexpr std::array<fggl::math::vec3, 8> pos{{
{-0.5, 0.5, -0.5}, // 0 TOP LOOP {-0.5, 0.5, -0.5}, // 0 TOP LOOP
{0.5, 0.5, -0.5}, // 1 {0.5, 0.5, -0.5}, // 1
{0.5, 0.5, 0.5}, // 2 {0.5, 0.5, 0.5}, // 2
{-0.5, 0.5, 0.5}, // 3 {-0.5, 0.5, 0.5}, // 3
{-0.5, -0.5, -0.5}, // 4 BOTTOM LOOP {-0.5, -0.5, -0.5}, // 4 BOTTOM LOOP
{0.5, -0.5, -0.5}, // 5 {0.5, -0.5, -0.5}, // 5
{0.5, -0.5, 0.5}, // 6 {0.5, -0.5, 0.5}, // 6
{-0.5, -0.5, 0.5} // 7 {-0.5, -0.5, 0.5} // 7
}}; }};
constexpr std::array<Mesh::IndexType, 36> idx {{ constexpr std::array<Mesh::IndexType, 36> idx{{
0, 3, 1, // top 0, 3, 1, // top
3, 2, 1, 3, 2, 1,
0, 1, 4, // side 0 - 1 0, 1, 4, // side 0 - 1
5, 4, 1, 5, 4, 1,
1, 2, 5, // side 1 - 2 1, 2, 5, // side 1 - 2
2, 6, 5, 2, 6, 5,
3, 7, 2, // side 2 - 3 3, 7, 2, // side 2 - 3
2, 7, 6, 2, 7, 6,
0, 4, 3, // side 3 - 0 0, 4, 3, // side 3 - 0
4, 7, 3, 4, 7, 3,
4, 5, 7, // bottom 4, 5, 7, // bottom
7, 5, 6, 7, 5, 6,
}}; }};
populateMesh(mesh, transform, idx.size(), pos.data(), idx.data()); populateMesh(mesh, transform, idx.size(), pos.data(), idx.data());
return mesh; return mesh;
} }
fggl::data::Mesh fggl::data::make_slope(fggl::data::Mesh& mesh, const fggl::math::mat4& transform) { auto fggl::data::make_slope(fggl::mesh::Mesh3D &mesh, const fggl::math::mat4 &transform) -> fggl::mesh::Mesh3D {
// done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7 // done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7
// FIXME remove 2 and 3 and renumber the index list accordingly // FIXME remove 2 and 3 and renumber the index list accordingly
constexpr fggl::math::vec3 pos[] { constexpr fggl::math::vec3 pos[]{
{-0.5, 0.5, -0.5}, // 0 TOP LOOP {-0.5, 0.5, -0.5}, // 0 TOP LOOP
{ 0.5, 0.5, -0.5}, // 1 {0.5, 0.5, -0.5}, // 1
{ 0.5, 0.5, 0.5}, // 2 {0.5, 0.5, 0.5}, // 2
{-0.5, 0.5, 0.5}, // 3 {-0.5, 0.5, 0.5}, // 3
{-0.5, -0.5, -0.5}, // 4 BOTTOM LOOP {-0.5, -0.5, -0.5}, // 4 BOTTOM LOOP
{ 0.5, -0.5, -0.5}, // 5 {0.5, -0.5, -0.5}, // 5
{ 0.5, -0.5, 0.5}, // 6 {0.5, -0.5, 0.5}, // 6
{-0.5, -0.5, 0.5} // 7 {-0.5, -0.5, 0.5} // 7
}; };
constexpr Mesh::IndexType idx[] { constexpr Mesh::IndexType idx[]{
0, 7, 1, // ramp 0, 7, 1, // ramp
7, 6, 1, 7, 6, 1,
0, 1, 4, // side 0 - 1 0, 1, 4, // side 0 - 1
...@@ -320,23 +316,22 @@ fggl::data::Mesh fggl::data::make_slope(fggl::data::Mesh& mesh, const fggl::math ...@@ -320,23 +316,22 @@ fggl::data::Mesh fggl::data::make_slope(fggl::data::Mesh& mesh, const fggl::math
return mesh; return mesh;
} }
auto fggl::data::make_ditch(fggl::mesh::Mesh3D &mesh, const fggl::math::mat4 &transform) -> fggl::mesh::Mesh3D {
fggl::data::Mesh fggl::data::make_ditch(fggl::data::Mesh& mesh, const fggl::math::mat4& transform) {
// done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7 // done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7
// FIXME remove 2 and renumber the index list accordingly // FIXME remove 2 and renumber the index list accordingly
constexpr fggl::math::vec3 pos[] { constexpr fggl::math::vec3 pos[]{
{-0.5, 0.5, -0.5}, // 0 TOP LOOP {-0.5, 0.5, -0.5}, // 0 TOP LOOP
{ 0.5, 0.5, -0.5}, // 1 {0.5, 0.5, -0.5}, // 1
{ 0.5, 0.5, 0.5}, // 2 {0.5, 0.5, 0.5}, // 2
{-0.5, 0.5, 0.5}, // 3 {-0.5, 0.5, 0.5}, // 3
{-0.5, -0.5, -0.5}, // 4 BOTTOM LOOP {-0.5, -0.5, -0.5}, // 4 BOTTOM LOOP
{ 0.5, -0.5, -0.5}, // 5 {0.5, -0.5, -0.5}, // 5
{ 0.5, -0.5, 0.5}, // 6 {0.5, -0.5, 0.5}, // 6
{-0.5, -0.5, 0.5} // 7 {-0.5, -0.5, 0.5} // 7
}; };
constexpr Mesh::IndexType idx[] { constexpr Mesh::IndexType idx[]{
0, 3, 1, // top 0, 3, 1, // top
3, 6, 1, 3, 6, 1,
0, 1, 4, // side 0 - 1 0, 1, 4, // side 0 - 1
...@@ -354,21 +349,21 @@ fggl::data::Mesh fggl::data::make_ditch(fggl::data::Mesh& mesh, const fggl::math ...@@ -354,21 +349,21 @@ fggl::data::Mesh fggl::data::make_ditch(fggl::data::Mesh& mesh, const fggl::math
return mesh; return mesh;
} }
fggl::data::Mesh fggl::data::make_point(fggl::data::Mesh& mesh, const fggl::math::mat4& transform) { auto fggl::data::make_point(fggl::mesh::Mesh3D &mesh, const fggl::math::mat4 &transform) -> fggl::mesh::Mesh3D {
// done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7 // done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7
constexpr fggl::math::vec3 pos[] { constexpr fggl::math::vec3 pos[]{
{-0.5, 0.5, -0.5}, // 0 TOP LOOP {-0.5, 0.5, -0.5}, // 0 TOP LOOP
{ 0.5, 0.5, -0.5}, // 1 {0.5, 0.5, -0.5}, // 1
{ 0.5, 0.5, 0.5}, // 2 {0.5, 0.5, 0.5}, // 2
{-0.5, 0.5, 0.5}, // 3 {-0.5, 0.5, 0.5}, // 3
{-0.5, -0.5, -0.5}, // 4 BOTTOM LOOP {-0.5, -0.5, -0.5}, // 4 BOTTOM LOOP
{ 0.5, -0.5, -0.5}, // 5 {0.5, -0.5, -0.5}, // 5
{ 0.5, -0.5, 0.5}, // 6 {0.5, -0.5, 0.5}, // 6
{-0.5, -0.5, 0.5} // 7 {-0.5, -0.5, 0.5} // 7
}; };
constexpr Mesh::IndexType idx[] { constexpr Mesh::IndexType idx[]{
0, 7, 5, // top 0, 7, 5, // top
7, 6, 5, 7, 6, 5,
0, 5, 4, // side 0 - 1 0, 5, 4, // side 0 - 1
......
target_sources(fggl target_sources(fggl
PRIVATE PRIVATE
debug.cpp debug.cpp
debug_draw.cpp debug_draw.cpp
) logging.cpp
)
# spdlog for cleaner logging # spdlog for cleaner logging
find_package(spdlog) find_package(spdlog)
......
...@@ -20,11 +20,11 @@ ...@@ -20,11 +20,11 @@
using fggl::debug::DebugUI; using fggl::debug::DebugUI;
DebugUI::DebugUI(std::shared_ptr<fggl::display::glfw::Window>& win) : m_visible(false) { DebugUI::DebugUI(std::shared_ptr<fggl::display::glfw::Window> &win) : m_visible(false) {
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
ImGui::CreateContext(); ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
io.IniFilename = nullptr; io.IniFilename = nullptr;
ImGui_ImplGlfw_InitForOpenGL(win->handle(), true); ImGui_ImplGlfw_InitForOpenGL(win->handle(), true);
...@@ -45,14 +45,14 @@ void DebugUI::frameStart() { ...@@ -45,14 +45,14 @@ void DebugUI::frameStart() {
} }
void DebugUI::draw() { void DebugUI::draw() {
for ( auto& [name, data] : m_windows) { for (auto &[name, data] : m_windows) {
if ( data.m_visible ) { if (data.m_visible) {
data.m_callback( &data.m_visible ); data.m_callback(&data.m_visible);
} }
} }
ImGui::Render(); ImGui::Render();
if ( m_visible ) { if (m_visible) {
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
} }
} }
target_link_libraries(fggl PRIVATE imgui) target_link_libraries(fggl PRIVATE imgui)
target_sources( fggl target_sources(fggl
PRIVATE PRIVATE
imgui_impl_glfw.cpp imgui_impl_glfw.cpp
imgui_impl_opengl3.cpp imgui_impl_opengl3.cpp
) )
target_include_directories( fggl target_include_directories(fggl
PRIVATE PRIVATE
include/ include/
) )
\ No newline at end of file \ No newline at end of file
This diff is collapsed.
This diff is collapsed.
...@@ -21,16 +21,16 @@ ...@@ -21,16 +21,16 @@
struct GLFWwindow; struct GLFWwindow;
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API auto ImGui_ImplGlfw_InitForOpenGL(GLFWwindow *window, bool installCallbacks) -> bool;
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API auto ImGui_ImplGlfw_InitForVulkan(GLFWwindow *window, bool installCallbacks) -> bool;
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API auto ImGui_ImplGlfw_InitForOther(GLFWwindow *window, bool installCallbacks) -> bool;
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
// GLFW callbacks // GLFW callbacks
// - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will call user's previously installed callbacks, if any. // - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will call user's previously installed callbacks, if any.
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call those function yourself from your own GLFW callbacks. // - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call those function yourself from your own GLFW callbacks.
IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow *window, int button, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow *window, double xoffset, double yoffset);
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow *window, unsigned int c);
...@@ -25,16 +25,16 @@ ...@@ -25,16 +25,16 @@
#include "imgui.h" // IMGUI_IMPL_API #include "imgui.h" // IMGUI_IMPL_API
// Backend API // Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); IMGUI_IMPL_API auto ImGui_ImplOpenGL3_Init(const char *glsl_version = nullptr) -> bool;
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData *draw_data);
// (Optional) Called by Init/NewFrame/Shutdown // (Optional) Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); IMGUI_IMPL_API auto ImGui_ImplOpenGL3_CreateFontsTexture() -> bool;
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); IMGUI_IMPL_API auto ImGui_ImplOpenGL3_CreateDeviceObjects() -> bool;
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
// Specific OpenGL ES versions // Specific OpenGL ES versions
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten //#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
...@@ -66,22 +66,22 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); ...@@ -66,22 +66,22 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
// Otherwise try to detect supported Desktop OpenGL loaders.. // Otherwise try to detect supported Desktop OpenGL loaders..
#elif defined(__has_include) #elif defined(__has_include)
#if __has_include(<GL/glew.h>) #if __has_include(<GL/glew.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLEW #define IMGUI_IMPL_OPENGL_LOADER_GLEW
#elif __has_include(<glad/glad.h>) #elif __has_include(<glad/glad.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD #define IMGUI_IMPL_OPENGL_LOADER_GLAD
#elif __has_include(<glad/gl.h>) #elif __has_include(<glad/gl.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD2 #define IMGUI_IMPL_OPENGL_LOADER_GLAD2
#elif __has_include(<GL/gl3w.h>) #elif __has_include(<GL/gl3w.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GL3W #define IMGUI_IMPL_OPENGL_LOADER_GL3W
#elif __has_include(<glbinding/glbinding.h>) #elif __has_include(<glbinding/glbinding.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3 #define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
#elif __has_include(<glbinding/Binding.h>) #elif __has_include(<glbinding/Binding.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2 #define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
#else #else
#error "Cannot detect OpenGL loader!" #error "Cannot detect OpenGL loader!"
#endif #endif
#else #else
#define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository #define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository
#endif #endif
#endif #endif
...@@ -12,39 +12,32 @@ ...@@ -12,39 +12,32 @@
* If not, see <https://www.gnu.org/licenses/>. * If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef FGGL_ECS3_UTILS_HPP //
#define FGGL_ECS3_UTILS_HPP // Created by webpigeon on 11/09/22.
//
#include <cstddef>
namespace fggl::utils { #ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
template<typename T> namespace fggl::debug {
std::size_t search(const T *data, const std::size_t size, const T &v) { auto demangle(const char *name) -> std::string {
// empty list == not found int status = -4;
if (size == 0) {
return size;
}
std::size_t left = 0; std::unique_ptr<char, decltype(&std::free)> res{
std::size_t right = size - 1; abi::__cxa_demangle(name, nullptr, nullptr, &status),
while (left <= right) { std::free
std::size_t m = (left + right) / 2; };
if (data[m] == v) {
return m;
} else if (v < data[m]) {
if (m == 0) {
return size;
}
right = m - 1;
} else {
left = m + 1;
}
}
return size; return (status == 0) ? res.get() : name;
}
}
#else
namespace fggl::debug {
std::string demangle(const char* name) {
return name;
}
} }
}
#endif #endif
target_sources(fggl target_sources(fggl
PRIVATE PRIVATE
loader/loader.cpp loader/loader.cpp
module.cpp module.cpp
) )
...@@ -18,29 +18,82 @@ ...@@ -18,29 +18,82 @@
#include "fggl/entity/loader/loader.hpp" #include "fggl/entity/loader/loader.hpp"
#include "fggl/debug/logging.hpp" #include "fggl/debug/logging.hpp"
#include "fggl/scenes/game.hpp"
namespace fggl::entity { namespace fggl::entity {
assets::AssetRefRaw load_prototype(EntityFactory* factory, const assets::AssetGUID& guid, assets::AssetData data) { auto load_scene(assets::Loader* /*loader*/, const assets::AssetID& /*asset*/, const assets::LoaderContext& data, void* ptr) -> assets::AssetRefRaw {
auto* filePath = std::get<assets::AssetPath*>(data); auto* gamePtr = (scenes::Game*)ptr;
auto filePath = data.assetPath;
// We need to process the prototypes, and load them into the asset system. // load assets
auto nodes = YAML::LoadAllFromFile( filePath->c_str() ); auto* entityFactory = gamePtr->owner().service<EntityFactory>();
auto nodes = YAML::LoadAllFromFile(filePath.c_str());
for (const auto& node : nodes) { for (const auto& node : nodes) {
auto scene = node["scene"];
if ( !scene ) {
debug::warning("no scene node in YAML file...");
return nullptr;
}
// create and personalize
for (const auto& item : scene) {
// only safe if personalize is called BEFORE end of loop itr
auto personalize = [&entityFactory, &item, gamePtr](EntityManager& manager, const EntityID eid) {
for ( const auto& compConfig : item["components"]) {
auto compName = compConfig.first.as<fggl::util::GUID>();
auto& compInfo = entityFactory->getInfo(compName);
// setup config data
ComponentSpec spec;
spec.config = compConfig.second;
// re-run the factory with the modified arguments (might need to support a 'patch' function...)
compInfo.factory( spec, manager, eid, gamePtr->owner().services());
}
};
// finally, load the entity and trigger personalize
auto prefab = item["prefab"].as<fggl::util::GUID>();
auto entity = entityFactory->create(prefab, gamePtr->world(), personalize);
// metadata
if ( item["name"].IsDefined() ) {
gamePtr->world().setName(item["name"].as<std::string>(), entity);
}
if ( item["tags"].IsDefined() ) {
for ( auto tag : item["tags"] ) {
auto tagGuid = tag.as<fggl::util::GUID>();
gamePtr->world().addTag(entity, tagGuid);
}
}
}
}
return nullptr;
}
auto load_prototype(assets::Loader* /*loader*/, const assets::AssetID &/*guid*/, const assets::LoaderContext& data, EntityFactory* factory) -> assets::AssetRefRaw {
auto filePath = data.assetPath;
// We need to process the prototypes, and load them into the asset system.
auto nodes = YAML::LoadAllFromFile(filePath.c_str());
for (const auto &node : nodes) {
auto prefabs = node["prefabs"]; auto prefabs = node["prefabs"];
for ( const auto& prefab : prefabs ) { for (const auto &prefab : prefabs) {
auto name = prefab["name"].as<fggl::util::GUID>(); auto name = prefab["name"].as<fggl::util::GUID>();
debug::info("found prefab: {}", name);
#ifndef NDEBUG
debug::info("found prefab: {}", fggl::util::guidToString(name) );
#endif
// set up the components // set up the components
EntitySpec entity{}; EntitySpec entity{};
entity.parent = prefab["parent"].as<fggl::util::GUID>(NO_PARENT); entity.parent = prefab["parent"].as<fggl::util::GUID>(NO_PARENT);
for (const auto& compEntry : prefab["components"]) { for (const auto &compEntry : prefab["components"]) {
auto compId = compEntry.first.as<fggl::util::GUID>(); auto compId = compEntry.first.as<fggl::util::GUID>();
ComponentSpec compSpec{}; ComponentSpec compSpec{};
...@@ -48,12 +101,16 @@ namespace fggl::entity { ...@@ -48,12 +101,16 @@ namespace fggl::entity {
entity.components[compId] = compSpec; entity.components[compId] = compSpec;
entity.ordering.push_back(compId); entity.ordering.push_back(compId);
#ifndef NDEBUG debug::trace("prefab {} has component {}", name, compId);
debug::trace("{} has component {}", fggl::util::guidToString(name), fggl::util::guidToString(compId)); }
#endif
if ( prefab["tags"].IsDefined() ) {
for ( const auto& tagNode : prefab["tags"] ) {
entity.tags.push_back( tagNode.as< util::GUID >() );
}
} }
factory->define( name, entity ); factory->define(name, entity);
} }
} }
......
...@@ -18,31 +18,73 @@ ...@@ -18,31 +18,73 @@
#include "fggl/entity/module.hpp" #include "fggl/entity/module.hpp"
#include "fggl/math/types.hpp" #include "fggl/math/types.hpp"
#include "fggl/scenes/game.hpp"
namespace fggl::entity { namespace fggl::entity {
void make_transform(const entity::ComponentSpec& spec, EntityManager& manager, const entity::EntityID entity){ void make_transform(const entity::ComponentSpec &spec, EntityManager &manager, const entity::EntityID entity, modules::Services &/*svc*/) {
auto& transform = manager.add<math::Transform>(entity); auto &transform = manager.add<math::Transform>(entity);
transform.origin( spec.get<math::vec3>( "origin", math::VEC3_ZERO ) );
transform.euler( spec.get<math::vec3>( "rotation", math::VEC3_ZERO ) );
transform.scale( spec.get<math::vec3>( "scale", math::VEC3_ONES ) );
debug::trace("created transform for entity {}", (uint64_t)entity); //FIXME won't work for patching!
transform.origin(spec.get<math::vec3>("origin", math::VEC3_ZERO));
transform.euler(spec.get<math::vec3>("rotation", math::VEC3_ZERO));
transform.scale(spec.get<math::vec3>("scale", math::VEC3_ONES));
debug::trace("created transform for entity {}", (uint64_t) entity);
} }
void install_component_factories(entity::EntityFactory* factory) { void install_component_factories(entity::EntityFactory *factory) {
factory->bind(math::Transform::guid, make_transform); factory->bind(math::Transform::guid, make_transform);
} }
bool ECS::factory(modules::ModuleService service, modules::Services& services) { static auto is_scene(std::filesystem::path path) -> assets::AssetTypeID {
if ( service == EntityFactory::service) { if ( path.extension() == ".yml" ) {
auto* factory = services.create<EntityFactory>(); return ENTITY_SCENE;
}
return assets::INVALID_ASSET_TYPE;
}
auto get_scene_deps(const std::string& /*packName*/, std::filesystem::path /*packRoot*/, assets::ResourceRecord& rr) -> bool {
auto nodes = YAML::LoadAllFromFile( rr.m_path );
for ( auto& node : nodes ) {
auto scripts = node["scripts"];
if ( !scripts ) {
continue;
}
for (auto script : scripts) {
auto scriptName = script.as<std::string>();
auto scriptRef = assets::asset_from_user(scriptName, rr.m_pack);
rr.m_requires.push_back(scriptRef);
}
}
return true;
}
auto ECS::factory(modules::ServiceName service, modules::Services &services) -> bool {
if (service == EntityFactory::service) {
auto *factory = services.create<EntityFactory>(services);
install_component_factories(factory); install_component_factories(factory);
// we are responsible for prefabs... // we are responsible for prefabs...
auto* assetLoader = services.get<assets::Loader>(); auto *assetLoader = services.get<assets::Loader>();
assetLoader->setFactory(PROTOTYPE_ASSET, [factory](const assets::AssetGUID& a, assets::AssetData b) { assetLoader->setFactory(ENTITY_PROTOTYPE, [factory](assets::Loader* loader, const assets::AssetID& a, assets::LoaderContext b, void* ptr) {
return load_prototype(factory, a, b); }, assets::LoadType::PATH ); EntityFactory* facPtr = factory;
if ( ptr != nullptr ) {
facPtr = (EntityFactory*)ptr;
}
return load_prototype(loader, a, b, facPtr);
}, assets::LoadType::PATH);
assetLoader->setFactory(ENTITY_SCENE, load_scene, assets::LoadType::PATH);
// allow auto-detection
auto *checkin = services.get<assets::CheckinAdapted>();
checkin->setLoader(MIME_SCENE, assets::NEEDS_CHECKIN, is_scene);
checkin->setProcessor(MIME_SCENE, get_scene_deps);
return true; return true;
} }
......
# Sources # Sources
find_package( glfw3 REQUIRED ) find_package(glfw3 REQUIRED)
include(CMakePrintHelpers) include(CMakePrintHelpers)
cmake_print_variables(GLFW_TARGETS) cmake_print_variables(GLFW_TARGETS)
...@@ -8,11 +8,11 @@ cmake_print_variables(GLFW_TARGETS) ...@@ -8,11 +8,11 @@ cmake_print_variables(GLFW_TARGETS)
target_link_libraries(fggl PUBLIC glfw fggl-glad) target_link_libraries(fggl PUBLIC glfw fggl-glad)
target_sources(fggl target_sources(fggl
PRIVATE PRIVATE
window.cpp window.cpp
input.cpp input.cpp
atlas.cpp atlas.cpp
) )
# OpenGL backend # OpenGL backend
add_subdirectory(ogl) add_subdirectory(ogl)
......
...@@ -14,10 +14,8 @@ ...@@ -14,10 +14,8 @@
#include <fggl/gfx/atlas.hpp> #include <fggl/gfx/atlas.hpp>
#include <fggl/math/types.hpp>
#include <array> #include <array>
#include <vector> #include <vector>
#include <string>
#define STBRP_STATIC #define STBRP_STATIC
#define STB_RECT_PACK_IMPLEMENTATION #define STB_RECT_PACK_IMPLEMENTATION
...@@ -25,57 +23,56 @@ ...@@ -25,57 +23,56 @@
using Query = std::vector<fggl::gfx::Bounds2D>; using Query = std::vector<fggl::gfx::Bounds2D>;
static void populate_stbrp_query(Query& query, std::vector<stbrp_rect>& data) { static void populate_stbrp_query(Query &query, std::vector<stbrp_rect> &data) {
for ( std::size_t i = 0; i < query.size(); i++ ) { for (std::size_t i = 0; i < query.size(); i++) {
data.push_back({ data.push_back({
(int)i, (int) i,
query[i].size.x, query[i].size.x,
query[i].size.y, query[i].size.y,
0, 0,
0, 0,
0 0
}); });
} }
} }
static void unpack_stbrp_query(Query& query, std::vector<stbrp_rect>& data) { static void unpack_stbrp_query(Query &query, std::vector<stbrp_rect> &data) {
for( const auto& rect : data ) { for (const auto &rect : data) {
query[ rect.id ].pos = { rect.x, rect.y }; query[rect.id].pos = {rect.x, rect.y};
} }
} }
bool pack_iter(int width, int height, std::vector<stbrp_rect>& query) { auto pack_iter(int width, int height, std::vector<stbrp_rect> &query) -> bool {
stbrp_node* tmp = new stbrp_node[width]; auto *tmp = new stbrp_node[width];
// setup context // setup context
stbrp_context context; stbrp_context context;
stbrp_init_target( &context, width, height, tmp, width ); stbrp_init_target(&context, width, height, tmp, width);
// see if it worked // see if it worked
auto result = stbrp_pack_rects( &context, query.data(), query.size() ); auto result = stbrp_pack_rects(&context, query.data(), query.size());
return result == 1; return result == 1;
} }
namespace fggl::gfx { namespace fggl::gfx {
auto pack(std::vector<Bounds2D> &pack) -> bool {
bool pack(std::vector<Bounds2D>& pack){ // setup query structure
// setup query structure std::vector<stbrp_rect> query;
std::vector<stbrp_rect> query; query.reserve(pack.size());
query.reserve( pack.size() ); populate_stbrp_query(pack, query);
populate_stbrp_query(pack, query);
// try packing into powers of 2, starting with 32, up to 4096
// try packing into powers of 2, starting with 32, up to 4096 for (int i = 5; i <= 12; i++) {
for ( int i=5; i <= 12; i++ ) { int dim = i * i;
int dim = i * i; if (pack_iter(dim, dim, query)) {
if ( pack_iter(dim, dim, query) ) { unpack_stbrp_query(pack, query);
unpack_stbrp_query(pack, query); return true;
return true; }
} }
}
return false;
return false; }
}
} // namespace fggl::gfx } // namespace fggl::gfx