diff --git a/demo/data/redbook/debug_frag.glsl b/demo/data/redbook/debug_frag.glsl index 299da0c8d66d548bd13f576e9741bdc0b840c862..050be21eef92edb2ff0f7d897fc359ca36d04dee 100644 --- a/demo/data/redbook/debug_frag.glsl +++ b/demo/data/redbook/debug_frag.glsl @@ -1,19 +1,80 @@ /** * OpenGL RedBook Shader. * Examples 7.8, 7.9 and 7.10. + * + * Reflections are happening in camera space */ #version 330 core -in vec4 Position; -in vec3 Normal; -in vec4 Colour; -in vec2 TexPos; +in Vertex { + vec3 Position; + vec3 Normal; + vec3 Colour; + vec2 TexPos; +}; out vec4 FragColour; +const float constant = 1.0; +const float linear = 0.022; +const float quadratic = 0.0019; + +float specPower = 0.5; +const float shininess = 8; + uniform sampler2D diffuseTexture; +uniform sampler2D specularTexture; + +uniform mat4 MVMatrix; +uniform vec3 viewerPos_ws; + +struct DirectionalLight { + vec3 direction; + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +uniform DirectionalLight light; + +const int hasPos = 0; + +vec4 calcDirLight(DirectionalLight light, vec3 Normal, vec3 viewDir, vec4 specPx, vec4 diffPx) { + vec3 lightDir = normalize( ( vec4(light.direction, 1) * MVMatrix).xyz - Position.xyz ); + vec4 ambient = 0.1 * vec4( light.ambient, 1); + + vec3 reflectDir = reflect( -lightDir, Normal ); + float spec = pow( max( dot(viewDir, reflectDir), 0.0), shininess); + vec4 specular = 1.0 * vec4( light.specular, 1) * specPower * (spec * specPx); + + float diff = max( dot(Normal, lightDir), 0.0 ); + vec4 diffuse = vec4(light.diffuse,1) * (diff * diffPx); + + if ( hasPos == 1 ) { + vec3 lightPos = ( vec4(light.direction, 1) * MVMatrix).xyz; + float distance = length( lightPos - Position.xyz ); + float att = 1.0 / (constant + + (linear * distance) + + (quadratic * (distance * distance))); + + ambient *= att; + diffuse *= att; + specular *= att; + } + + return (ambient + diffuse + specular); +} void main() { - vec3 normalScale = 0.5 + (Normal / 2); - FragColour = Colour * vec4(normalScale, 1) * texture(diffuseTexture, TexPos); + vec3 viewDir = normalize(-Position); + + vec4 diffPx = vec4(1, 1, 1, 1); + vec4 specPx = vec4(1, 1, 1, 1); + if ( hasPos != 1) { + diffPx = texture(diffuseTexture, TexPos); + specPx = texture(specularTexture, TexPos); + } + + FragColour = vec4(Colour, 1); + FragColour *= calcDirLight(light, Normal, viewDir, specPx, diffPx); } \ No newline at end of file diff --git a/demo/data/redbook/debug_vert.glsl b/demo/data/redbook/debug_vert.glsl index cf0e34136c219bbf5cc3fc992fe648bb807c4042..20266622cdbf072b92371cbdec8d9daea2ffd1c6 100644 --- a/demo/data/redbook/debug_vert.glsl +++ b/demo/data/redbook/debug_vert.glsl @@ -4,6 +4,7 @@ */ #version 330 core + layout (location = 0) in vec3 VertexPosition; layout (location = 1) in vec3 VertexNormal; layout (location = 2) in vec3 VertexColour; @@ -13,15 +14,18 @@ uniform mat4 MVPMatrix; uniform mat4 MVMatrix; uniform mat3 NormalMatrix; -out vec4 Position; -out vec3 Normal; -out vec4 Colour; -out vec2 TexPos; +out Vertex { + vec3 Position; + vec3 Normal; + vec3 Colour; + vec2 TexPos; +}; void main() { - Colour = vec4(VertexColour, 1); - Normal = NormalMatrix * VertexNormal; + Colour = VertexColour; + Normal = mat3(transpose(inverse(MVMatrix))) * VertexNormal; TexPos = VertexTex; - Position = MVMatrix * vec4(VertexPosition, 1); + Position = vec3(MVMatrix * vec4(VertexPosition, 1)); + gl_Position = MVPMatrix * vec4(VertexPosition, 1); } \ No newline at end of file diff --git a/demo/data/rollball.yml b/demo/data/rollball.yml index 5c562bc6250db16a173357b163cdc5d221d21bd0..8eec78b5fe86df0d2f91800def85d3532dab0ce5 100644 --- a/demo/data/rollball.yml +++ b/demo/data/rollball.yml @@ -89,6 +89,11 @@ prefabs: type: kinematic shape: type: box + - name: rb_light + components: + Transform: + gfx::phong::directional: + direction: [10, 5, 0] scene: - prefab: rb_wallX components: @@ -124,5 +129,6 @@ scene: origin: [6, -0.5, -15] - prefab: rb_player name: "player" + - prefab: rb_light scripts: - "rollball.lua" \ No newline at end of file diff --git a/demo/demo/GameScene.cpp b/demo/demo/GameScene.cpp index 0ce3b09be5e333100302f67340b7f199cd516c07..4923d15654a75b0adf888f2a47e9fd1a21ff657f 100644 --- a/demo/demo/GameScene.cpp +++ b/demo/demo/GameScene.cpp @@ -24,6 +24,7 @@ #include "GameScene.h" #include "fggl/entity/loader/loader.hpp" +#include "fggl/mesh/components.hpp" camera_type cam_mode = cam_free; @@ -128,7 +129,8 @@ static void setupBunkerPrototype(fggl::entity::EntityFactory* factory) { // mesh int nSections = 2; - fggl::data::Mesh mesh; + fggl::mesh::MultiMesh3D mesh; + for (int j=-(nSections/2); j<=nSections/2; j++) { const auto shapeOffset = glm::vec3( 0.0f, 0.5f, (float)j * 1.0f ); @@ -140,37 +142,41 @@ static void setupBunkerPrototype(fggl::entity::EntityFactory* factory) { glm::vec3( 1.0f, 0.0f, 0.0f) + shapeOffset, glm::vec3( 0.0f, fggl::math::HALF_PI, 0.0f) ); - fggl::data::make_cube( mesh, cubeMat ); - fggl::data::make_slope( mesh, leftSlope ); - fggl::data::make_slope( mesh, rightSlope ); + fggl::data::make_cube( mesh.generate(), cubeMat); + fggl::data::make_slope( mesh.generate(), leftSlope ); + fggl::data::make_slope( mesh.generate(), rightSlope ); } - mesh.removeDups(); + //mesh.removeDups(); // generate mesh component data // FIXME: find a better way to do this, avoid re-uploading the whole mesh. fggl::entity::ComponentSpec procMesh{}; procMesh.set<std::string>("pipeline", "redbook/debug"); - YAML::Node vertexData; - for (auto& vertex : mesh.vertexList()) { - YAML::Node vertexNode; - vertexNode["position"] = vertex.posititon; - vertexNode["normal"] = vertex.normal; - vertexNode["colour"] = vertex.colour; - vertexNode["texPos"] = vertex.texPos; - vertexData.push_back(vertexNode); - } + YAML::Node modelNode; + for (auto& submesh : mesh.meshes) { + YAML::Node vertexData; + for (auto& vertex : submesh.data) { + YAML::Node vertexNode; + vertexNode["position"] = vertex.position; + vertexNode["normal"] = vertex.normal; + vertexNode["colour"] = vertex.colour; + vertexNode["texPos"] = vertex.texPos; + vertexData.push_back(vertexNode); + } - YAML::Node indexData; - for (auto& index : mesh.indexList()) { - indexData.push_back(index); - } + YAML::Node indexData; + for (auto& index : submesh.indices) { + indexData.push_back(index); + } - YAML::Node meshData; - meshData["vertex"] = vertexData; - meshData["index"] = indexData; - procMesh.set("mesh", meshData); - bunkerSpec.addComp(fggl::data::StaticMesh::guid, procMesh); + YAML::Node meshNode; + meshNode["vertex"] = vertexData; + meshNode["index"] = indexData; + modelNode.push_back( meshNode ); + } + procMesh.set("model", modelNode); + bunkerSpec.addComp(fggl::mesh::StaticMultiMesh3D::guid, procMesh); factory->define(BUNKER_PROTOTYPE, bunkerSpec); } diff --git a/demo/demo/models/viewer.cpp b/demo/demo/models/viewer.cpp index c1860d29abb326a26538452c4b71d84b1c5a369e..bf7b3ebababdb36e212537ee2a47e5c0ce56e754 100644 --- a/demo/demo/models/viewer.cpp +++ b/demo/demo/models/viewer.cpp @@ -28,6 +28,7 @@ #include "fggl/gfx/phong.hpp" #include "fggl/gfx/camera.hpp" +#include "fggl/gfx/paint.hpp" namespace demo { @@ -90,6 +91,17 @@ namespace demo { cameraKeys.rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E); } + static void setup_lighting(fggl::entity::EntityManager& ecs) { + auto light = ecs.create(); + auto& transform = ecs.add<fggl::math::Transform>(light); + + auto& lightComp = ecs.add<fggl::gfx::DirectionalLight>(light); + lightComp.position = fggl::math::vec3( 10.0F, 5.0F, 0.0F ); + lightComp.diffuse = fggl::gfx::colours::CORNSILK; + lightComp.ambient = fggl::gfx::colours::MIDNIGHT_BLUE; + lightComp.specular = fggl::gfx::colours::MIDNIGHT_BLUE; + } + Viewer::Viewer(fggl::App &app) : fggl::scenes::Game(app), m_model(fggl::entity::INVALID) { } @@ -105,6 +117,7 @@ namespace demo { // create camera setup_camera(world()); + setup_lighting(world()); // setup model m_model = build_model(world(), manager); diff --git a/demo/demo/topdown.cpp b/demo/demo/topdown.cpp index c94ca175535ea312ff90e99919eaa77d6990a8cc..0ad73c816c9b7e47f50f3df1127defe23051e16e 100644 --- a/demo/demo/topdown.cpp +++ b/demo/demo/topdown.cpp @@ -20,6 +20,8 @@ #include "fggl/data/storage.hpp" #include "fggl/gfx/camera.hpp" +#include "fggl/gfx/phong.hpp" + #include "fggl/input/camera_input.hpp" #include "fggl/entity/loader/loader.hpp" @@ -50,6 +52,17 @@ namespace demo { cameraKeys.rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E); } + static void setup_lighting(fggl::entity::EntityManager& ecs) { + auto light = ecs.create(); + auto& transform = ecs.add<fggl::math::Transform>(light); + + auto& lightComp = ecs.add<fggl::gfx::DirectionalLight>(light); + lightComp.position = fggl::math::vec3( 10.0F, 5.0F, 0.0F ); + lightComp.diffuse = fggl::gfx::colours::CORNSILK; + lightComp.ambient = fggl::gfx::colours::MIDNIGHT_BLUE; + lightComp.specular = fggl::gfx::colours::MIDNIGHT_BLUE; + } + static void place_cover_boxes(fggl::entity::EntityFactory* factory, fggl::entity::EntityManager& world) { std::array<fggl::math::vec3,8> boxPos = {{ {-10.0F, 0.0F, -10.0F}, @@ -91,6 +104,7 @@ namespace demo { } } + setup_lighting(world); place_cover_boxes(factory, world); } diff --git a/fggl/data/assimp/module.cpp b/fggl/data/assimp/module.cpp index bc587be6bfb7e5f409d0f14e656522274c580d71..8ba764885f0c0cf1e698ebfe78565723a9d23153 100644 --- a/fggl/data/assimp/module.cpp +++ b/fggl/data/assimp/module.cpp @@ -118,6 +118,16 @@ namespace fggl::data::models { material->normalTextures.push_back(textureGuid); } + for ( unsigned int i = 0U ; i < assimpMat->GetTextureCount(aiTextureType_SPECULAR); ++i ) { + aiString texName; + assimpMat->GetTexture( aiTextureType_SPECULAR, i, &texName ); + + auto textureGuid = prefix + "/" + texName.C_Str(); + loader->load(textureGuid, DATA_TEXTURE2D, manager); + + material->specularTextures.push_back(textureGuid); + } + manager->set( guid, material ); } diff --git a/fggl/data/procedural.cpp b/fggl/data/procedural.cpp index 34d9a2e68094bc89d6cddd349e835427983fbe7c..dfacbe9285fed17406a481be7ce96363b5827ab3 100644 --- a/fggl/data/procedural.cpp +++ b/fggl/data/procedural.cpp @@ -23,6 +23,7 @@ #include <array> #include <glm/geometric.hpp> +#include "fggl/mesh/mesh.hpp" using namespace fggl::data; @@ -33,48 +34,48 @@ static glm::vec3 calcSurfaceNormal(glm::vec3 vert1, glm::vec3 vert2, glm::vec3 v 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... 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); } // We're assuming each vertex appears only once (because we're not indexed) for (int i = 0; i < nPoints; i += 3) { - auto &v1 = mesh.vertex(colIdx[i]); - auto &v2 = mesh.vertex(colIdx[i + 1]); - auto &v3 = mesh.vertex(colIdx[i + 2]); + auto &v1 = mesh.data[colIdx[i]]; + auto &v2 = mesh.data[colIdx[i + 1]]; + 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; v2.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> &idxMapping // source-to-mesh lookup ) { // clear the normals, so the summation below works correctly for (auto vertexIndex : idxMapping) { - auto &vertex = mesh.vertex(vertexIndex); + auto &vertex = mesh.data[vertexIndex]; vertex.normal = ILLEGAL_NORMAL; } // we need to calculate the contribution for each vertex // 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) { - auto &v1 = mesh.vertex(idxMapping[idxList[i]]); - auto &v2 = mesh.vertex(idxMapping[idxList[i + 1]]); - auto &v3 = mesh.vertex(idxMapping[idxList[i + 2]]); + auto &v1 = mesh.data[ idxMapping[idxList[i]] ]; + auto &v2 = mesh.data[ idxMapping[idxList[i + 1]] ]; + auto &v3 = mesh.data[ idxMapping[idxList[i + 2]] ]; // 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; - auto faceNormal = calcSurfaceNormal(v1.posititon, v2.posititon, v3.posititon); + float area = glm::length(glm::cross(v3.position - v2.position, v1.position - v3.position)) / 2; + auto faceNormal = calcSurfaceNormal(v1.position, v2.position, v3.position); // weight the normal according to the area of the surface (bigger area = more impact) v1.normal += area * faceNormal; @@ -84,12 +85,12 @@ static void compute_normals(fggl::data::Mesh &mesh, // re-normalise the normals for (unsigned int vertexIndex : idxMapping) { - auto &vertex = mesh.vertex(vertexIndex); + auto &vertex = mesh.data[vertexIndex]; 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) { auto *colIdx = new fggl::data::Mesh::IndexType[nIdx]; @@ -97,8 +98,8 @@ static void populateMesh(fggl::data::Mesh &mesh, const fggl::math::mat4 transfor // generate mesh for (int i = 0; i < nIdx; i++) { glm::vec3 rawPos = transform * glm::vec4(pos[idx[i]], 1.0); - colIdx[i] = mesh.pushVertex(Vertex::from_pos(rawPos)); - mesh.pushIndex(colIdx[i]); + colIdx[i] = mesh.append(fggl::mesh::Vertex3D::from_pos(rawPos)); + mesh.indices.push_back(colIdx[i]); } computeNormalsDirect(mesh, colIdx, nIdx); @@ -106,7 +107,7 @@ static void populateMesh(fggl::data::Mesh &mesh, const fggl::math::mat4 transfor delete[] colIdx; } -static void populateMesh(fggl::data::Mesh &mesh, +static void populateMesh(fggl::mesh::Mesh3D &mesh, const fggl::math::mat4 transform, const std::vector<fggl::math::vec3> &posList, const std::vector<fggl::data::Mesh::IndexType> &idxList) { @@ -118,12 +119,12 @@ static void populateMesh(fggl::data::Mesh &mesh, // clion this thinks this loop is infinite, my assumption is it's gone bananas for (std::size_t i = 0; i < posList.size(); ++i) { glm::vec3 position = transform * fggl::math::vec4(posList[i], 1.0F); - colIdx[i] = mesh.pushVertex(Vertex::from_pos(position)); + colIdx[i] = mesh.append(fggl::mesh::Vertex3D::from_pos(position)); } // use the remapped indexes for the mesh for (auto idx : idxList) { - mesh.pushIndex(colIdx[idx]); + mesh.indices.push_back(colIdx[idx]); } compute_normals(mesh, idxList, colIdx); @@ -152,7 +153,7 @@ 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; @@ -254,7 +255,7 @@ fggl::data::Mesh fggl::data::make_quad_xz() { return mesh; } -fggl::data::Mesh fggl::data::make_cube(fggl::data::Mesh &mesh, const fggl::math::mat4 &transform) { +fggl::mesh::Mesh3D fggl::data::make_cube(fggl::mesh::Mesh3D &mesh, const fggl::math::mat4 &transform) { // 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{{ {-0.5, 0.5, -0.5}, // 0 TOP LOOP @@ -285,7 +286,7 @@ fggl::data::Mesh fggl::data::make_cube(fggl::data::Mesh &mesh, const fggl::math: return mesh; } -fggl::data::Mesh fggl::data::make_slope(fggl::data::Mesh &mesh, const fggl::math::mat4 &transform) { +fggl::mesh::Mesh3D fggl::data::make_slope(fggl::mesh::Mesh3D &mesh, const fggl::math::mat4 &transform) { // 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 @@ -316,7 +317,7 @@ fggl::data::Mesh fggl::data::make_slope(fggl::data::Mesh &mesh, const fggl::math return mesh; } -fggl::data::Mesh fggl::data::make_ditch(fggl::data::Mesh &mesh, const fggl::math::mat4 &transform) { +fggl::mesh::Mesh3D fggl::data::make_ditch(fggl::mesh::Mesh3D &mesh, const fggl::math::mat4 &transform) { // 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 @@ -349,7 +350,7 @@ fggl::data::Mesh fggl::data::make_ditch(fggl::data::Mesh &mesh, const fggl::math return mesh; } -fggl::data::Mesh fggl::data::make_point(fggl::data::Mesh &mesh, const fggl::math::mat4 &transform) { +fggl::mesh::Mesh3D fggl::data::make_point(fggl::mesh::Mesh3D &mesh, const fggl::math::mat4 &transform) { // done as two loops, top loop is 0,1,2,3, bottom loop is 4,5,6,7 constexpr fggl::math::vec3 pos[]{ diff --git a/fggl/gfx/ogl/renderer.cpp b/fggl/gfx/ogl/renderer.cpp index f0a4bb0db22e21c28ce044e3fbabecd965cd0882..a2caa21e55a5640355541f91ffa6d1fd34259f8f 100644 --- a/fggl/gfx/ogl/renderer.cpp +++ b/fggl/gfx/ogl/renderer.cpp @@ -166,7 +166,7 @@ namespace fggl::gfx { //Set pixel to red colours[ 0 ] = 0xFF; colours[ 1 ] = 0x00; - colours[ 2 ] = 0x00; + colours[ 2 ] = 0xFF; colours[ 3 ] = 0xFF; } } diff --git a/fggl/gfx/ogl4/meshes.cpp b/fggl/gfx/ogl4/meshes.cpp index 0a9dfb50db0a8df78cfa16be75521423fb9384d4..961c4f6c441fc14df8afffeb0ffc8eb395306857 100644 --- a/fggl/gfx/ogl4/meshes.cpp +++ b/fggl/gfx/ogl4/meshes.cpp @@ -13,6 +13,8 @@ */ #include "fggl/gfx/ogl4/meshes.hpp" +#include "fggl/gfx/phong.hpp" + #include "fggl/data/texture.hpp" // @@ -52,11 +54,22 @@ namespace fggl::gfx::ogl4 { vao->bind(); vertexData->bind(); - if ( material->m_diffuse != nullptr ) { - material->m_diffuse->bind(0); - if ( shader->hasUniform("diffuseTexture") ) { - shader->setUniformI(shader->uniform("diffuseTexture"), 0); + if ( material != nullptr ) { + if (material->m_diffuse != nullptr) { + material->m_diffuse->bind(0); + if (shader->hasUniform("diffuseTexture")) { + shader->setUniformI(shader->uniform("diffuseTexture"), 0); + } + } + + if (material->m_specular != nullptr) { + material->m_specular->bind(1); + if (shader->hasUniform("specularTexture")) { + shader->setUniformI(shader->uniform("specularTexture"), 1); + } } + } else { + debug::info("no material is active, cannot bind textures!"); } if ( drawInfo.restartIndex != ogl::NO_RESTART_IDX) { @@ -90,6 +103,19 @@ namespace fggl::gfx::ogl4 { shader->setUniformF(shader->uniform("materials[0].shininess"), material->shininess); } + void setup_lighting(const std::shared_ptr<ogl::Shader>& shader, const DirectionalLight* light) { + assert( light != nullptr ); + if ( !shader->hasUniform("light.direction") ) { + fggl::debug::warning("asked for directional lighting, but shader does not support!"); + return; + } + + shader->setUniformF( shader->uniform("light.direction"), light->position); + shader->setUniformF( shader->uniform("light.ambient"), light->ambient); + shader->setUniformF( shader->uniform("light.diffuse"), light->diffuse); + shader->setUniformF( shader->uniform("light.specular"), light->specular); + } + void setup_lighting(const std::shared_ptr<ogl::Shader>& shader, const math::mat4& viewMatrix, const math::Transform& camTransform, const math::Transform& transform, math::vec3 lightPos) { if (shader->hasUniform("lightPos")) { shader->setUniformF(shader->uniform("lightPos"), lightPos); @@ -132,72 +158,6 @@ namespace fggl::gfx::ogl4 { shader->setUniformF(shader->uniform("lights[0].colour"), {0.5f, 0.5f, 0.5f}); } - - void forward_pass_multi_mesh(const entity::EntityID& camera, const fggl::entity::EntityManager& world) { - - // enable required OpenGL state - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - // enable depth testing - glEnable(GL_DEPTH_TEST); - - // set-up camera matrices - const auto &camTransform = world.get<fggl::math::Transform>(camera); - const auto &camComp = world.get<fggl::gfx::Camera>(camera); - - const math::mat4 projectionMatrix = - glm::perspective(camComp.fov, camComp.aspectRatio, camComp.nearPlane, camComp.farPlane); - const math::mat4 viewMatrix = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up()); - - // TODO lighting needs to not be this... - math::vec3 lightPos{0.0f, 10.0f, 0.0f}; - - std::shared_ptr<ogl::Shader> shader = nullptr; - ogl::Location mvpMatrixUniform = 0; - ogl::Location mvMatrixUniform = 0; - - auto entityView = world.find<StaticMultiMesh>(); - for (const auto &entity : entityView) { - debug::trace("Multi-mesh happened!"); - - // ensure that the model pipeline actually exists... - const auto &model = world.get<StaticMultiMesh>(entity); - if (model.pipeline == nullptr) { - debug::warning("shader was null, aborting render"); - continue; - } - - // check if we switched shaders - if (shader == nullptr || shader->shaderID() != model.pipeline->shaderID()) { - // new shader - need to re-send the view and projection matrices - shader = model.pipeline; - shader->use(); - if (shader->hasUniform("projection")) { - shader->setUniformMtx(shader->uniform("view"), viewMatrix); - shader->setUniformMtx(shader->uniform("projection"), projectionMatrix); - } - mvpMatrixUniform = shader->uniform("MVPMatrix"); - mvMatrixUniform = shader->uniform("MVMatrix"); - } - - // set model transform - const auto &transform = world.get<math::Transform>(entity); - shader->setUniformMtx(mvpMatrixUniform, projectionMatrix * viewMatrix * transform.model()); - shader->setUniformMtx(mvMatrixUniform, viewMatrix * transform.model()); - - auto normalMatrix = glm::mat3(glm::transpose(inverse(transform.model()))); - shader->setUniformMtx(shader->uniform("NormalMatrix"), normalMatrix); - - // setup lighting mode - setup_lighting(shader, viewMatrix, camTransform, transform, lightPos); - setup_material(shader, world.tryGet<PhongMaterial>(entity, &DEFAULT_MATERIAL)); - - model.draw(); - - } - } - static ogl::Texture* upload_texture( std::string name, assets::AssetManager* manager ) { debug::info("loading texture: {}", name); @@ -220,11 +180,24 @@ namespace fggl::gfx::ogl4 { return manager->set("ogl_"+name, texture); } + static Material* get_fallback_material(assets::AssetManager* manager) { + auto* fallback = manager->get<Material>(FALLBACK_MAT); + if ( fallback != nullptr ) { + return fallback; + } + + Material* mat = new Material(); + mat->m_diffuse = manager->get<ogl::Texture>(FALLBACK_TEX); + mat->m_specular = manager->get<ogl::Texture>(FALLBACK_TEX); + mat->m_normals = manager->get<ogl::Texture>(FALLBACK_TEX); + return manager->set(FALLBACK_MAT, mat); + } + static Material* upload_material( std::string name, assets::AssetManager* manager ) { auto* meshMaterial = manager->get<mesh::Material>(name); if ( meshMaterial == nullptr ){ debug::error("attempted to load material {}, but did not exist!", name); - return {}; + return get_fallback_material(manager); } auto* material = manager->get<Material>("ogl_"+name); @@ -239,6 +212,9 @@ namespace fggl::gfx::ogl4 { if ( !meshMaterial->normalTextures.empty() ) { material->m_normals = upload_texture(meshMaterial->getPrimaryNormals(), manager); } + if ( !meshMaterial->specularTextures.empty() ) { + material->m_specular = upload_texture(meshMaterial->getPrimarySpecular(), manager); + } return manager->set("ogl_"+name, material); } diff --git a/fggl/gfx/ogl4/models.cpp b/fggl/gfx/ogl4/models.cpp index 8bfdce4f743289a7eae9289e67a97c8f084bf15c..d2c61b7e0971ba6abdae7f879759968a9c1d4166 100644 --- a/fggl/gfx/ogl4/models.cpp +++ b/fggl/gfx/ogl4/models.cpp @@ -428,7 +428,7 @@ namespace fggl::gfx::ogl4 { // perform a rendering pass for each camera (will usually only be one...) for (const auto &cameraEnt : cameras) { //TODO should be clipping this to only visible objects - forward_camera_pass(cameraEnt, world); + //forward_camera_pass(cameraEnt, world); forward_pass<ogl4::StaticMesh>(cameraEnt, world, m_assets); forward_pass<ogl4::StaticMultiMesh>(cameraEnt, world, m_assets); diff --git a/fggl/gfx/ogl4/module.cpp b/fggl/gfx/ogl4/module.cpp index e4bb673cd5b007cfffcd7030e8cf74d075cc5062..b20b384c7fe2f448baecbc8bb95c5e136451c0f8 100644 --- a/fggl/gfx/ogl4/module.cpp +++ b/fggl/gfx/ogl4/module.cpp @@ -31,7 +31,7 @@ namespace fggl::gfx { constexpr const char *SHAPE_SPHERE{"sphere"}; constexpr const char *SHAPE_BOX{"box"}; - static void process_shape(const YAML::Node &node, data::Mesh &mesh) { + static void process_shape(const YAML::Node &node, mesh::Mesh3D &mesh) { auto transform = data::OFFSET_NONE; auto offset = node["offset"].as<math::vec3>(math::VEC3_ZERO); @@ -66,46 +66,57 @@ namespace fggl::gfx { // asset is a procedural mesh description if (spec.has("shape")) { - data::Mesh* meshAsset = nullptr; auto pipeline = spec.get<std::string>("pipeline", ""); // check if we had previously loaded this asset - const auto shapeName = spec.get<std::string>("shape_id", ""); + /*const auto shapeName = spec.get<std::string>("shape_id", ""); if ( !shapeName.empty() ) { meshAsset = assetService->get<data::Mesh>(shapeName); - } + }*/ // we've not loaded this before - generate mesh - if ( meshAsset == nullptr ) { - // procedural meshes are build as static meshes first - auto* meshTmp = new data::Mesh(); + if ( true ) { + // procedural meshes are build as static meshes first if (spec.config["shape"].IsSequence()) { + mesh::MultiMesh3D* multiMesh; + for (const auto &node : spec.config["shape"]) { - process_shape(node, *meshTmp); + mesh::Mesh3D* meshAsset = new mesh::Mesh3D(); + process_shape(node, *meshAsset); + multiMesh->meshes.push_back(*meshAsset); + delete meshAsset; } - } else { - process_shape(spec.config["shape"], *meshTmp); - } - meshTmp->removeDups(); - if (!shapeName.empty()) { - meshAsset = assetService->set(shapeName, meshTmp); + #ifdef FGGL_ALLOW_DEFERRED_UPLOAD + // the graphics stack can detect static meshes without a rendering proxy at runtime and fix it but this + // requires loading the whole model into the ECS (and should be removed in the future). instead we should + // be triggering the upload at this point (if needed). + auto &entityMesh = manager.add<mesh::StaticMultiMesh3D>(id); + entityMesh.mesh = *multiMesh; + entityMesh.pipeline = pipeline; + debug::warning("HACKY: Triggered proc mesh - using deferred upload"); + #endif + } else { - meshAsset = meshTmp; + mesh::Mesh3D* meshAsset = new mesh::Mesh3D(); + process_shape(spec.config["shape"], *meshAsset); + + #ifdef FGGL_ALLOW_DEFERRED_UPLOAD + // the graphics stack can detect static meshes without a rendering proxy at runtime and fix it but this + // requires loading the whole model into the ECS (and should be removed in the future). instead we should + // be triggering the upload at this point (if needed). + auto &entityMesh = manager.add<mesh::StaticMesh3D>(id); + entityMesh.mesh = *meshAsset; + entityMesh.pipeline = pipeline; + debug::warning("HACKY: Triggered proc mesh - using deferred upload"); + #endif } + + //assetService->set(shapeName, meshTmp); } // TODO we need to trigger an upload to the GPU (somehow) - #ifdef FGGL_ALLOW_DEFERRED_UPLOAD - // the graphics stack can detect static meshes without a rendering proxy at runtime and fix it but this - // requires loading the whole model into the ECS (and should be removed in the future). instead we should - // be triggering the upload at this point (if needed). - auto &entityMesh = manager.add<data::StaticMesh>(id); - entityMesh.pipeline = pipeline; - entityMesh.mesh = *meshAsset; - debug::warning("HACKY: Triggered proc mesh - using deferred upload"); - #endif return; } @@ -139,8 +150,21 @@ namespace fggl::gfx { mat.shininess = spec.get<float>("ambient", gfx::DEFAULT_SHININESS); } - void attach_light(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services& services) { - auto &light = manager.add<gfx::Light>(id); + void attach_light_directional(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services& services) { + auto &light = manager.add<gfx::DirectionalLight>(id); + light.position = spec.get<math::vec3>("direction", -math::UP); + light.ambient = spec.get<math::vec3>("ambient", gfx::colours::WHITE); + light.specular = spec.get<math::vec3>("specular", gfx::colours::WHITE); + light.diffuse = spec.get<math::vec3>("diffuse", gfx::colours::WHITE); + } + + void attach_light_point(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services& services) { + auto &light = manager.add<gfx::PointLight>(id); + light.position = spec.get<math::vec3>("position", math::VEC3_ZERO); + + light.constant = spec.get<float>("constant", 1.0F); + light.linear = spec.get<float>("linear", 0.0014F); + light.quadratic = spec.get<float>("quadratic", 0.000007F); } bool OpenGL4::factory(modules::ModuleService service, modules::Services &services) { @@ -158,7 +182,9 @@ namespace fggl::gfx { entityFactory->bind(mesh::StaticMesh3D::guid, attach_mesh); entityFactory->bind(mesh::StaticMultiMesh3D::guid, attach_mesh); entityFactory->bind(gfx::PhongMaterial::guid, attach_material); - entityFactory->bind(gfx::Light::guid, attach_light); + + entityFactory->bind(gfx::DirectionalLight::guid, attach_light_directional); + entityFactory->bind(gfx::PointLight::guid, attach_light_point); return true; } diff --git a/fggl/scenes/game.cpp b/fggl/scenes/game.cpp index 14a2139b475e8e14d474e99ea4493eeb45e10be9..bf104d23ec77572d2aa0a4ce7a51d5c8b5f34c88 100644 --- a/fggl/scenes/game.cpp +++ b/fggl/scenes/game.cpp @@ -67,18 +67,23 @@ namespace fggl::scenes { if (escapePressed) { m_owner.change_state(m_previous); } + + if ( m_input->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_F2)) ) { + m_debug = !m_debug; + } } if (m_phys != nullptr) { m_phys->step(); } + // debug render toggle //m_world->reapEntities(); } void Game::render(fggl::gfx::Graphics &gfx) { if (m_world != nullptr) { - gfx.drawScene(*m_world); + gfx.drawScene(*m_world, m_debug); } } diff --git a/include/fggl/data/procedural.hpp b/include/fggl/data/procedural.hpp index 53ec3e6ececa559c8f86c7d62511e109634419c9..b4471b387f26dffe6fa3d5143a2dc76b3b0a0790 100644 --- a/include/fggl/data/procedural.hpp +++ b/include/fggl/data/procedural.hpp @@ -16,6 +16,7 @@ #define FGGL_DATA_PROCEDURAL_HPP #include "model.hpp" +#include "fggl/mesh/mesh.hpp" namespace fggl::data { @@ -28,7 +29,7 @@ namespace fggl::data { // platonic solids void make_tetrahedron(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE); - Mesh make_cube(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE); + mesh::Mesh3D make_cube(mesh::Mesh3D &mesh, const math::mat4 &offset = OFFSET_NONE); void make_octahedron(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE); void make_icosahedron(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE); void make_dodecahedron(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE); @@ -38,13 +39,13 @@ namespace fggl::data { void make_sphere_iso(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE); // level block-out shapes - Mesh make_slope(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE); - Mesh make_ditch(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE); - Mesh make_point(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE); + mesh::Mesh3D make_slope(mesh::Mesh3D &mesh, const math::mat4 &offset = OFFSET_NONE); + mesh::Mesh3D make_ditch(mesh::Mesh3D &mesh, const math::mat4 &offset = OFFSET_NONE); + mesh::Mesh3D make_point(mesh::Mesh3D &mesh, const math::mat4 &offset = OFFSET_NONE); // other useful types people expect void make_capsule(Mesh &mesh); - void make_sphere(Mesh &mesh, const math::mat4 &offset = OFFSET_NONE, uint32_t stacks = 16U, uint32_t slices = 16U); + void make_sphere(mesh::Mesh3D &mesh, const math::mat4 &offset = OFFSET_NONE, uint32_t stacks = 16U, uint32_t slices = 16U); } #endif \ No newline at end of file diff --git a/include/fggl/gfx/ogl4/fallback.hpp b/include/fggl/gfx/ogl4/fallback.hpp index 3d725c52713ec1484934d7978cb5f29b2dc04cd9..27479b922a8c7f9311474277a48b43fc60636ee0 100644 --- a/include/fggl/gfx/ogl4/fallback.hpp +++ b/include/fggl/gfx/ogl4/fallback.hpp @@ -61,6 +61,7 @@ namespace fggl::gfx::ogl4 { constexpr const std::array<uint32_t, 4> TEX_WHITE{ 0xFF, 0xFF, 0xFF, 0xFF }; constexpr const std::array<uint32_t, 4> TEX_CHECKER{ 0xFF, 0x00, 0x00, 0xFF }; constexpr const char* FALLBACK_TEX = "FALLBACK_TEX"; + constexpr const char* FALLBACK_MAT = "FALLBACK_MAT"; } // namespace fggl::gfx::ogl4 diff --git a/include/fggl/gfx/ogl4/meshes.hpp b/include/fggl/gfx/ogl4/meshes.hpp index 196ed7889d9f45e94b6dfdef18b19cdfddb8887d..7cae65b9e24a69ff27938d604704bb168cd70c3b 100644 --- a/include/fggl/gfx/ogl4/meshes.hpp +++ b/include/fggl/gfx/ogl4/meshes.hpp @@ -39,6 +39,7 @@ namespace fggl::gfx::ogl4 { struct Material { ogl::Texture* m_diffuse; ogl::Texture* m_normals; + ogl::Texture* m_specular; }; struct MeshData { @@ -76,6 +77,8 @@ namespace fggl::gfx::ogl4 { void setup_material(const std::shared_ptr<ogl::Shader>& shader, const PhongMaterial* material); void setup_lighting(const std::shared_ptr<ogl::Shader>& shader, const math::mat4& viewMatrix, const math::Transform& camTransform, const math::Transform& transform, math::vec3 lightPos); + void setup_lighting(const std::shared_ptr<ogl::Shader>& shader, const DirectionalLight* light); + template<typename T> void forward_pass(const entity::EntityID& camera, const fggl::entity::EntityManager& world, const assets::AssetManager* assets) { @@ -89,6 +92,7 @@ namespace fggl::gfx::ogl4 { // prep the fallback textures auto *fallbackTex = assets->template get<ogl::Texture>(FALLBACK_TEX); fallbackTex->bind(0); + fallbackTex->bind(1); // set-up camera matrices const auto &camTransform = world.get<fggl::math::Transform>(camera); @@ -97,9 +101,6 @@ namespace fggl::gfx::ogl4 { const math::mat4 projectionMatrix = camComp.perspective(); const math::mat4 viewMatrix = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up()); - // TODO lighting needs to not be this... - math::vec3 lightPos{0.0F, 10.0F, 0.0F}; - std::shared_ptr<ogl::Shader> shader = nullptr; ogl::Location mvpMatrixUniform = 0; ogl::Location mvMatrixUniform = 0; @@ -107,6 +108,16 @@ namespace fggl::gfx::ogl4 { auto entityView = world.find<T>(); debug::info("Triggering rendering pass for {} entities", entityView.size()); + // find directional light in scene + auto lightEnts = world.find<DirectionalLight>(); + const DirectionalLight* light; + if ( !lightEnts.empty() ) { + light = world.tryGet<DirectionalLight>(lightEnts[0]); + } else { + debug::warning("no light component in scene, it's gunna be dark..."); + light = nullptr; + } + for (const auto &entity : entityView) { // ensure that the model pipeline actually exists... const auto &model = world.get<T>(entity); @@ -130,6 +141,10 @@ namespace fggl::gfx::ogl4 { if ( shader->hasUniform("diffuseTexture") ) { shader->setUniformI(shader->uniform("diffuseTexture"), 0); } + + if ( shader->hasUniform("specularTexture") ) { + shader->setUniformI(shader->uniform("specularTexture"), 1); + } } // set model transform @@ -137,11 +152,14 @@ namespace fggl::gfx::ogl4 { shader->setUniformMtx(mvpMatrixUniform, projectionMatrix * viewMatrix * transform.model()); shader->setUniformMtx(mvMatrixUniform, viewMatrix * transform.model()); - auto normalMatrix = glm::mat3(glm::transpose(inverse(transform.model()))); - shader->setUniformMtx(shader->uniform("NormalMatrix"), normalMatrix); + /*auto normalMatrix = glm::mat3(glm::transpose(inverse(transform.model()))); + shader->setUniformMtx(shader->uniform("NormalMatrix"), normalMatrix);*/ // setup lighting mode - setup_lighting(shader, viewMatrix, camTransform, transform, lightPos); + //setup_lighting(shader, viewMatrix, camTransform, transform, lightPos); + if ( light != nullptr ) { + setup_lighting(shader, light); + } setup_material(shader, world.tryGet<PhongMaterial>(entity, &DEFAULT_MATERIAL)); model.draw(); diff --git a/include/fggl/gfx/ogl4/models.hpp b/include/fggl/gfx/ogl4/models.hpp index 66ffbe2c9cabe7cbe56ad2a27ea18a62022e4bad..5b8fba68e781d7d01ce5413167514feef349fa8c 100644 --- a/include/fggl/gfx/ogl4/models.hpp +++ b/include/fggl/gfx/ogl4/models.hpp @@ -71,6 +71,7 @@ namespace fggl::gfx::ogl4 { public: inline StaticModelRenderer(gfx::ShaderCache *cache, assets::AssetManager *assets) : m_assets(assets), m_shaders(cache), m_phong(nullptr), m_vao(), m_vertexList(), m_indexList() { + m_phong = cache->get("redbook/debug"); } diff --git a/include/fggl/gfx/phong.hpp b/include/fggl/gfx/phong.hpp index e0852eec4e21727b6e472dc82592ad542d38af58..5bd3a31478a7e95fc9dc7f81a4e483bcf982217e 100644 --- a/include/fggl/gfx/phong.hpp +++ b/include/fggl/gfx/phong.hpp @@ -50,6 +50,27 @@ namespace fggl::gfx { }; struct Light { + math::vec3 position; + math::vec3 ambient; + math::vec3 specular; + math::vec3 diffuse; + }; + + struct DirectionalLight : public Light { + constexpr static const char *name = "gfx::phong::directional"; + constexpr static const util::GUID guid = util::make_guid(name); + }; + + struct PointLight : public Light { + constexpr static const char *name = "gfx::phong::point"; + constexpr static const util::GUID guid = util::make_guid(name); + + float constant = 1.0F; + float linear; + float quadratic; + }; + + struct Light2 { constexpr static const char *name = "gfx::light"; constexpr static const util::GUID guid = util::make_guid("gfx::light"); bool enabled; diff --git a/include/fggl/mesh/mesh.hpp b/include/fggl/mesh/mesh.hpp index 005ce8a20717de18c49b2e696d8fa23148eb5b51..1da889666f323504c1afa709e63108afeb8bff96 100644 --- a/include/fggl/mesh/mesh.hpp +++ b/include/fggl/mesh/mesh.hpp @@ -29,8 +29,17 @@ namespace fggl::mesh { struct Vertex3D { math::vec3 position; math::vec3 normal; - math::vec3 colour{ 1.0F, 1.0F, 1.0f }; + math::vec3 colour{ 1.0F, 1.0F, 1.0F }; math::vec2 texPos{ NAN, NAN }; + + static Vertex3D from_pos(math::vec3 pos) { + return { + .position = pos, + .normal {NAN, NAN, NAN}, + .colour {1.0F, 1.0F, 1.0F}, + .texPos { pos.x, pos.z } + }; + } }; struct Vertex2D { @@ -50,6 +59,7 @@ namespace fggl::mesh { math::vec3 specular; std::vector<std::string> diffuseTextures{}; std::vector<std::string> normalTextures{}; + std::vector<std::string> specularTextures{}; inline std::string getPrimaryDiffuse() { assert( !diffuseTextures.empty() ); @@ -60,6 +70,11 @@ namespace fggl::mesh { assert( !normalTextures.empty() ); return normalTextures.empty() ? "" : normalTextures[0]; } + + inline std::string getPrimarySpecular() { + assert( !specularTextures.empty() ); + return specularTextures.empty() ? "" : specularTextures[0]; + } }; template<typename VertexFormat> @@ -67,12 +82,22 @@ namespace fggl::mesh { std::vector<VertexFormat> data; std::vector<uint32_t> indices; std::string material; + + inline uint32_t append(const VertexFormat& vert) { + auto nextIdx = data.size(); + data.push_back(vert); + return nextIdx; + } }; template<typename MeshFormat> struct MultiMesh { std::vector<MeshFormat> meshes; std::vector<std::string> materials; + + MeshFormat& generate() { + return meshes.template emplace_back(); + } }; using Mesh2D = Mesh<Vertex2D>; diff --git a/include/fggl/scenes/game.hpp b/include/fggl/scenes/game.hpp index 21f35cfddab44e6b75e03a4aae94321d6c2f20c3..d844f7f1829bd6e1a258da2b2ef2d5833595942f 100644 --- a/include/fggl/scenes/game.hpp +++ b/include/fggl/scenes/game.hpp @@ -77,6 +77,8 @@ namespace fggl::scenes { return *m_input; } + bool m_debug; + private: input::Input *m_input; std::unique_ptr<entity::EntityManager> m_world; diff --git a/vendor/imgui/CMakeLists.txt b/vendor/imgui/CMakeLists.txt index 665c4727492ceb40011ffc8eaf85a3d1b82f0f51..63532343122eff447d856a7935c690c75a36d506 100644 --- a/vendor/imgui/CMakeLists.txt +++ b/vendor/imgui/CMakeLists.txt @@ -23,5 +23,5 @@ include(GNUInstallDirs) install(TARGETS imgui EXPORT fgglTargets PUBLIC_HEADER - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/fggl/imgui + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/fggl/imgui ) \ No newline at end of file