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 432 additions and 521 deletions
......@@ -20,7 +20,7 @@
namespace fggl::data {
bool LocalStorage::factory(modules::ModuleService service, modules::Services &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");
......
......@@ -17,64 +17,63 @@
#include <fggl/data/model.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <iostream>
#include <array>
#include <glm/geometric.hpp>
#include "fggl/mesh/mesh.hpp"
using namespace fggl::data;
// 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 edge2 = vert3 - vert1;
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 +83,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 +96,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 +105,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 +117,13 @@ 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));
auto vert = fggl::mesh::Vertex3D::from_pos(position);
colIdx[i] = mesh.append(vert);
}
// 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 +152,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;
......@@ -183,7 +183,7 @@ 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[]{
{-0.5F, -0.5F, 0.0F},
{0.5F, -0.5F, 0.0F},
......@@ -204,7 +204,7 @@ fggl::data::Mesh fggl::data::make_triangle() {
return mesh;
}
fggl::data::Mesh fggl::data::make_quad_xy() {
auto fggl::data::make_quad_xy() -> fggl::data::Mesh {
constexpr fggl::math::vec3 pos[]{
{-0.5F, -0.5F, 0.0F},
{0.5F, -0.5F, 0.0F},
......@@ -229,7 +229,7 @@ fggl::data::Mesh fggl::data::make_quad_xy() {
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{{
{-0.5F, 0.0F, -0.5F},
{0.5F, 0.0F, -0.5F},
......@@ -254,7 +254,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) {
auto fggl::data::make_cube(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
constexpr std::array<fggl::math::vec3, 8> pos{{
{-0.5, 0.5, -0.5}, // 0 TOP LOOP
......@@ -285,7 +285,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) {
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
// FIXME remove 2 and 3 and renumber the index list accordingly
......@@ -316,7 +316,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) {
auto fggl::data::make_ditch(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
// FIXME remove 2 and renumber the index list accordingly
......@@ -349,7 +349,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) {
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
constexpr fggl::math::vec3 pos[]{
......
......@@ -21,9 +21,9 @@
struct GLFWwindow;
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow *window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow *window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow *window, bool install_callbacks);
IMGUI_IMPL_API auto ImGui_ImplGlfw_InitForOpenGL(GLFWwindow *window, bool installCallbacks) -> bool;
IMGUI_IMPL_API auto ImGui_ImplGlfw_InitForVulkan(GLFWwindow *window, bool installCallbacks) -> bool;
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_NewFrame();
......
......@@ -25,15 +25,15 @@
#include "imgui.h" // IMGUI_IMPL_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_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData *draw_data);
// (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 bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API auto ImGui_ImplOpenGL3_CreateDeviceObjects() -> bool;
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
// Specific OpenGL ES versions
......
......@@ -23,7 +23,7 @@
#include <cxxabi.h>
namespace fggl::debug {
std::string demangle(const char *name) {
auto demangle(const char *name) -> std::string {
int status = -4;
std::unique_ptr<char, decltype(&std::free)> res{
......
......@@ -22,14 +22,14 @@
namespace fggl::entity {
assets::AssetRefRaw load_scene(assets::Loader* loader, const assets::AssetGUID& asset, assets::AssetData data, void* ptr) {
auto *filePath = std::get<assets::AssetPath *>(data);
scenes::Game* gamePtr = (scenes::Game*)ptr;
auto load_scene(assets::Loader* /*loader*/, const assets::AssetID& /*asset*/, const assets::LoaderContext& data, void* ptr) -> assets::AssetRefRaw {
auto* gamePtr = (scenes::Game*)ptr;
auto filePath = data.assetPath;
// load assets
auto* entityFactory = gamePtr->owner().service<EntityFactory>();
auto nodes = YAML::LoadAllFromFile(filePath->c_str());
auto nodes = YAML::LoadAllFromFile(filePath.c_str());
for (const auto& node : nodes) {
auto scene = node["scene"];
if ( !scene ) {
......@@ -77,11 +77,11 @@ namespace fggl::entity {
return nullptr;
}
assets::AssetRefRaw load_prototype(assets::Loader* loader, const assets::AssetGUID &guid, assets::AssetData data, EntityFactory* factory) {
auto *filePath = std::get<assets::AssetPath *>(data);
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());
auto nodes = YAML::LoadAllFromFile(filePath.c_str());
for (const auto &node : nodes) {
auto prefabs = node["prefabs"];
......@@ -105,7 +105,7 @@ namespace fggl::entity {
}
if ( prefab["tags"].IsDefined() ) {
for ( auto& tagNode : prefab["tags"] ) {
for ( const auto& tagNode : prefab["tags"] ) {
entity.tags.push_back( tagNode.as< util::GUID >() );
}
}
......
......@@ -37,21 +37,54 @@ namespace fggl::entity {
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 ( path.extension() == ".yml" ) {
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);
// we are responsible for prefabs...
auto *assetLoader = services.get<assets::Loader>();
assetLoader->setFactory(PROTOTYPE_ASSET, [factory](assets::Loader* loader, const assets::AssetGUID &a, assets::AssetData b, void* ptr) {
assetLoader->setFactory(ENTITY_PROTOTYPE, [factory](assets::Loader* loader, const assets::AssetID& a, assets::LoaderContext b, void* ptr) {
EntityFactory* facPtr = factory;
if ( ptr != nullptr ) {
facPtr = (EntityFactory*)ptr;
}
return load_prototype(loader, a, b, facPtr);
}, assets::LoadType::PATH);
assetLoader->setFactory(SCENE, load_scene, 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;
}
......
......@@ -14,10 +14,8 @@
#include <fggl/gfx/atlas.hpp>
#include <fggl/math/types.hpp>
#include <array>
#include <vector>
#include <string>
#define STBRP_STATIC
#define STB_RECT_PACK_IMPLEMENTATION
......@@ -44,8 +42,8 @@ static void unpack_stbrp_query(Query &query, std::vector<stbrp_rect> &data) {
}
}
bool pack_iter(int width, int height, std::vector<stbrp_rect> &query) {
stbrp_node *tmp = new stbrp_node[width];
auto pack_iter(int width, int height, std::vector<stbrp_rect> &query) -> bool {
auto *tmp = new stbrp_node[width];
// setup context
stbrp_context context;
......@@ -58,7 +56,7 @@ bool pack_iter(int width, int height, std::vector<stbrp_rect> &query) {
namespace fggl::gfx {
bool pack(std::vector<Bounds2D> &pack) {
auto pack(std::vector<Bounds2D> &pack) -> bool {
// setup query structure
std::vector<stbrp_rect> query;
query.reserve(pack.size());
......
......@@ -13,10 +13,6 @@
*/
#include <fggl/gfx/input.hpp>
#include <cassert>
#include <bitset>
#include <iostream>
using fggl::gfx::Input;
......@@ -57,15 +53,15 @@ void Input::mousePos(double x, double y) {
m_mouse_curr.cursor[1] = y;
}
double Input::cursorDeltaX() const {
auto Input::cursorDeltaX() const -> double {
return m_mouse_last.cursor[0] - m_mouse_curr.cursor[0];
}
double Input::cursorDeltaY() const {
auto Input::cursorDeltaY() const -> double {
return m_mouse_last.cursor[1] - m_mouse_curr.cursor[1];
}
const double *Input::mousePos() const {
auto Input::mousePos() const -> const double * {
return m_mouse_curr.scroll.data();
}
......@@ -74,15 +70,15 @@ void Input::mouseScroll(double deltaX, double deltaY) {
m_mouse_curr.scroll[1] = deltaY;
}
const double *Input::mouseScroll() const {
auto Input::mouseScroll() const -> const double * {
return m_mouse_curr.scroll.data();
}
double Input::scrollDeltaX() const {
auto Input::scrollDeltaX() const -> double {
return m_mouse_curr.scroll[0];
}
double Input::scrollDeltaY() const {
auto Input::scrollDeltaY() const -> double {
return m_mouse_curr.scroll[1];
}
......@@ -94,15 +90,15 @@ void Input::mouseBtn(const MouseButton btn, bool state) {
}
}
bool Input::mouseDown(const MouseButton btn) const {
auto Input::mouseDown(const MouseButton btn) const -> bool {
return m_mouse_curr.buttons & btn;
}
bool Input::mousePressed(const MouseButton btn) const {
auto Input::mousePressed(const MouseButton btn) const -> bool {
return (m_mouse_curr.buttons & btn) && !(m_mouse_last.buttons & btn);
}
bool Input::mouseReleased(const MouseButton btn) const {
auto Input::mouseReleased(const MouseButton btn) const -> bool {
return !(m_mouse_curr.buttons & btn) && (m_mouse_last.buttons & btn);
}
......@@ -118,11 +114,11 @@ void Input::joystickDisconnect(int id) {
m_joydata[id] = Joystick();
}
bool Input::hasJoystick(int id) const {
auto Input::hasJoystick(int id) const -> bool {
return m_joysticks[id];
}
const fggl::gfx::Joystick &Input::joystick(int id) const {
auto Input::joystick(int id) const -> const fggl::gfx::Joystick & {
return m_joydata[id];
}
......@@ -130,22 +126,22 @@ void Input::padState(int id, const PadState &state) {
m_pad_curr[id] = state;
}
bool Input::padDown(int id, PadButton btn) {
auto Input::padDown(int id, PadButton btn) -> bool {
return m_pad_curr[id].buttons[(int) btn];
}
bool Input::padPressed(int id, PadButton btn) {
auto Input::padPressed(int id, PadButton btn) -> bool {
return m_pad_curr[id].buttons[(int) btn] && !m_pad_last[id].buttons[(int) btn];
}
bool Input::padReleased(int id, PadButton btn) {
auto Input::padReleased(int id, PadButton btn) -> bool {
return !m_pad_curr[id].buttons[(int) btn] && m_pad_last[id].buttons[(int) btn];
}
float Input::padAxis(int id, PadAxis axis) {
auto Input::padAxis(int id, PadAxis axis) -> float {
return m_pad_curr[id].axes[(int) axis];
}
float Input::padAxisDelta(int id, PadAxis axis) {
auto Input::padAxisDelta(int id, PadAxis axis) -> float {
return m_pad_last[id].axes[(int) axis] - m_pad_curr[id].axes[(int) axis];
}
......@@ -27,7 +27,6 @@
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <memory>
......@@ -64,24 +63,15 @@ constexpr auto fggl_ogl_source(GLenum source) -> const char * {
constexpr auto static fggl_ogl_type(GLenum type) -> const char * {
switch (type) {
case GL_DEBUG_TYPE_ERROR: return "Error";
break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "Deprecated Behaviour";
break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "Undefined Behaviour";
break;
case GL_DEBUG_TYPE_PERFORMANCE: return "Performance";
break;
case GL_DEBUG_TYPE_PORTABILITY: return "Portability";
break;
case GL_DEBUG_TYPE_MARKER: return "Marker";
break;
case GL_DEBUG_TYPE_PUSH_GROUP: return "Push Group";
break;
case GL_DEBUG_TYPE_POP_GROUP: return "Pop Group";
break;
default:
case GL_DEBUG_TYPE_OTHER: return "Other";
break;
}
assert(false);
return "unknown";
......@@ -150,42 +140,52 @@ namespace fggl::gfx {
cache->load(ShaderConfig::named("debug"));
}
static void splat_checkerboard(GLuint* memory) {
for (int i = 0; i < 128 * 128; ++i) {
GLubyte* colours = (GLubyte*)&memory[i];
if( i / 128 & 16 ^ i % 128 & 16 )
{
//Set pixel to white
colours[ 0 ] = 0xFF;
colours[ 1 ] = 0xFF;
colours[ 2 ] = 0xFF;
colours[ 3 ] = 0xFF;
}
else
{
//Set pixel to red
colours[ 0 ] = 0xFF;
colours[ 1 ] = 0x00;
colours[ 2 ] = 0x00;
colours[ 3 ] = 0xFF;
}
static void splat_checkerboard(GLuint* memory, unsigned int width = 128, unsigned int height = 128) {
int counter = 0;
auto colour = ogl4::TEX_CHECKER;
for ( auto i = 0u; i < width * height; ++i) {
memory[i] = ogl4::TEX_CHECKER;
counter++;
if (counter == 5) {
counter = 0;
colour = colour == ogl4::TEX_CHECKER ? ogl4::TEX_WHITE : ogl4::TEX_CHECKER;
}
}
}
static auto make_solid(uint8_t width, uint8_t height, GLuint colour) -> ogl::Image {
ogl::PixelDataArray data(ogl::PixelFormat::UNSIGNED_INT, width * height);
for ( auto i = 0u; i < width * height; ++i) {
data.ui[i] = colour;
}
return ogl::Image(
ogl::ImageFormat::RGBA,
{width, height},
std::move(data)
);
}
static void setup_fallback_texture(assets::AssetManager* assets) {
auto* fallback2D = new ogl::Texture(ogl::TextureType::Tex2D);
GLuint myData[128 * 128];
splat_checkerboard(myData);
ogl::Image image{
.type = ogl::PixelFormat::UNSIGNED_BYTE,
.format = ogl::ImageFormat::RGBA,
.size = {128, 128},
.data = myData
};
fallback2D->setData(ogl::InternalImageFormat::RedGreenBlueAlpha, image);
assets->set(ogl4::FALLBACK_TEX, fallback2D);
{
// generate the image
ogl::Image image(ogl::ImageFormat::RGBA, ogl::PixelFormat::UNSIGNED_INT, {128,128});
splat_checkerboard( image.data.ui );
// setup the texture
auto *fallback2D = new ogl::Texture(ogl::TextureType::Tex2D);
fallback2D->setData(ogl::InternalImageFormat::RedGreenBlueAlpha, image, ogl::PixelFormat::UNSIGNED_BYTE);
assets->set(ogl4::FALLBACK_TEX, fallback2D);
}
{
ogl::Image image = make_solid(128, 128, ogl4::TEX_WHITE);
auto *solid2D = new ogl::Texture(ogl::TextureType::Tex2D);
solid2D->setData(ogl::InternalImageFormat::RedGreenBlueAlpha, image);
assets->set(ogl4::SOLID_TEX, solid2D);
}
}
OpenGL4Backend::OpenGL4Backend(data::Storage *storage, gui::FontLibrary *fonts, assets::AssetManager *assets, GlFunctionLoader loader)
......
......@@ -17,12 +17,13 @@
#include "fggl/gfx/ogl4/fallback.hpp"
#include <iostream>
#include <fstream>
#include <vector>
#include <spdlog/spdlog.h>
namespace fggl::gfx {
bool ShaderCache::compileShaderFromSource(const std::string &source, GLuint sid) {
auto ShaderCache::compileShaderFromSource(const std::string &source, GLuint sid) -> bool {
// upload and compile shader
const char *src = source.c_str();
glShaderSource(sid, 1, &src, nullptr);
......@@ -46,7 +47,7 @@ namespace fggl::gfx {
return true;
}
bool ShaderCache::readAndCompileShader(const std::string &filename, GLuint shader) {
auto ShaderCache::readAndCompileShader(const std::string &filename, GLuint shader) -> bool {
std::string source;
bool result = m_storage->load(fggl::data::Data, filename, &source);
if (!result) {
......@@ -88,7 +89,7 @@ namespace fggl::gfx {
}
bool ShaderCache::loadFromDisk(GLuint pid, const std::string &pipelineName) {
auto ShaderCache::loadFromDisk(GLuint pid, const std::string &pipelineName) -> bool {
BinaryCache cache;
auto fname = "shader_" + pipelineName + ".bin";
......@@ -112,7 +113,7 @@ namespace fggl::gfx {
m_storage->save(fggl::data::Cache, fname, &cache);
}
ShaderCache::ShaderPtr ShaderCache::getOrLoad(const ShaderConfig &config) {
auto ShaderCache::getOrLoad(const ShaderConfig &config) -> ShaderCache::ShaderPtr {
try {
return m_shaders.at(config.name);
} catch (std::out_of_range &e) {
......@@ -120,11 +121,15 @@ namespace fggl::gfx {
}
}
ShaderCache::ShaderPtr ShaderCache::get(const std::string &name) {
return m_shaders.at(name);
auto ShaderCache::get(const std::string &name) -> ShaderCache::ShaderPtr {
auto itr = m_shaders.find(name);
if ( itr != m_shaders.end() ){
return itr->second;
}
return nullptr;
}
ShaderCache::ShaderPtr ShaderCache::load(const ShaderConfig &config) {
auto ShaderCache::load(const ShaderConfig &config) -> ShaderCache::ShaderPtr {
spdlog::debug("starting shader program generation for {}", config.name);
GLuint pid = glCreateProgram();
......@@ -195,7 +200,7 @@ namespace fggl::gfx {
return shaderPtr;
}
ShaderCache::ShaderPtr ShaderCache::load(const ShaderSources &sources, bool allowBinaryCache) {
auto ShaderCache::load(const ShaderSources &sources, bool allowBinaryCache) -> ShaderCache::ShaderPtr {
spdlog::debug("starting shader program generation for {}", sources.name);
......@@ -276,7 +281,7 @@ namespace fggl::gfx {
glGetProgramBinary(pid, length, &cache->size, &cache->format, cache->data);
}
bool ShaderCache::cacheLoad(GLuint pid, const BinaryCache *cache) {
auto ShaderCache::cacheLoad(GLuint pid, const BinaryCache *cache) -> bool {
if (!m_binary) {
return false;
}
......@@ -301,8 +306,8 @@ namespace fggl::gfx {
}
template<>
bool fggl::data::fggl_deserialize(std::filesystem::path &data, fggl::gfx::BinaryCache *out) {
auto f =
auto fggl::data::fggl_deserialize(std::filesystem::path &data, fggl::gfx::BinaryCache *out) -> bool {
auto* f =
#ifdef _MSC_VER
_wfopen(data.c_str(), L"r");
#else
......@@ -343,11 +348,9 @@ namespace fggl::gfx {
return result;
}
#include <iostream>
#include <fstream>
template<>
bool fggl::data::fggl_deserialize(std::filesystem::path &data, std::string *out) {
auto fggl::data::fggl_deserialize(std::filesystem::path &data, std::string *out) -> bool {
std::ifstream ifs(data);
out->assign((std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>()));
......@@ -355,10 +358,10 @@ bool fggl::data::fggl_deserialize(std::filesystem::path &data, std::string *out)
}
template<>
bool fggl::data::fggl_serialize(std::filesystem::path &data, const fggl::gfx::BinaryCache *out) {
auto fggl::data::fggl_serialize(std::filesystem::path &data, const fggl::gfx::BinaryCache *out) -> bool {
// try and write
auto f =
auto *f =
#ifdef _MSC_VER
_wfopen( data.c_str(), L"w");
#else
......
......@@ -44,7 +44,7 @@ namespace fggl::gfx::ogl {
other.m_obj = 0;
}
VertexArray &VertexArray::operator=(VertexArray &&other) {
auto VertexArray::operator=(VertexArray &&other) -> VertexArray & {
if (this != &other) {
release();
std::swap(m_obj, other.m_obj);
......
......@@ -153,21 +153,21 @@ namespace fggl::gfx::ogl4 {
// get the expected font
std::shared_ptr<gui::FontFace> face = m_fonts->getFont("LiberationSans-Regular.ttf");
std::shared_ptr<gui::FontFace> face = m_fonts->getDefaultFont();
if (face == nullptr) {
// we don't know about that font...
return;
}
// setup the shader
// set up the shader
shader.use();
auto projMat = glm::ortho(0.0f, 1920.0f, 1080.0f, 0.f);
auto projMat = glm::ortho(0.0F, 1920.0F, 1080.0F, 0.F);
shader.setUniformMtx(shader.uniform("projection"), projMat);
// bind the vbo we'll use for writing
m_vao.bind();
// setup the openGL state we expect for rendering
// set up the openGL state we expect for rendering
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
......@@ -179,14 +179,15 @@ namespace fggl::gfx::ogl4 {
const auto label = textCmd.text;
math::vec2 penPos(textCmd.pos);
// set up a non-owning holder for the characters
data::Texture2D tex;
tex.channels = 1;
for (auto letter : label) {
ogl::Image img{};
img.format = ogl::ImageFormat::R;
img.type = ogl::PixelFormat::UNSIGNED_BYTE;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
face->texture(letter, img.size.x, img.size.y, &img.data);
m_fontTex.setData(ogl::InternalImageFormat::Red, img);
face->texture(letter, tex.size.x, tex.size.y, &tex.data);
m_fontTex.setData(ogl::InternalImageFormat::Red, &tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
......@@ -218,6 +219,9 @@ namespace fggl::gfx::ogl4 {
penPos.x += (metrics.advance >> 6);
}
// textures assume they own their contained data, we need to make sure we clear it
tex.data = nullptr;
}
glDisable(GL_BLEND);
......
......@@ -13,6 +13,8 @@
*/
#include "fggl/gfx/ogl4/meshes.hpp"
#include "fggl/gfx/phong.hpp"
#include "fggl/data/texture.hpp"
//
......@@ -21,8 +23,8 @@
namespace fggl::gfx::ogl4 {
static std::shared_ptr<ogl::ArrayBuffer> setup_array_buffer(std::shared_ptr<ogl::VertexArray> &vao,
const std::vector<mesh::Vertex3D> &data) {
static auto setup_array_buffer(std::shared_ptr<ogl::VertexArray> &vao,
const std::vector<mesh::Vertex3D> &data) -> std::shared_ptr<ogl::ArrayBuffer> {
// upload the data to the GPU
auto buff = std::make_shared<ogl::ArrayBuffer>();
buff->write(data.size() * sizeof(mesh::Vertex3D), data.data(), ogl::BufUsage::STATIC_DRAW);
......@@ -41,24 +43,65 @@ namespace fggl::gfx::ogl4 {
return buff;
}
static std::shared_ptr<ogl::ElementBuffer> setup_index_buffer(const std::vector<uint32_t>& data) {
static auto setup_index_buffer(const std::vector<uint32_t>& data) -> std::shared_ptr<ogl::ElementBuffer> {
auto elementBuffer = std::make_shared<ogl::ElementBuffer>();
elementBuffer->write(data.size() * sizeof(uint32_t),
data.data(), ogl::BufUsage::STATIC_DRAW);
return elementBuffer;
}
void MeshData::draw(std::shared_ptr<ogl::Shader> shader) const {
vao->bind();
vertexData->bind();
void setup_material(const std::shared_ptr<ogl::Shader>& shader, const PhongMaterial* material) {
if ( !shader->hasUniform("material.ambient") ) {
return;
}
if ( material->m_diffuse != nullptr ) {
// setup material block
shader->setUniformF(shader->uniform("material.emission"), material->emission);
shader->setUniformF(shader->uniform("material.ambient"), material->ambient);
shader->setUniformF(shader->uniform("material.diffuse"), material->diffuse);
shader->setUniformF(shader->uniform("material.specular"), material->specular);
//shader->setUniformF(shader->uniform("material.shininess"), material->shininess);
}
void setup_material(const std::shared_ptr<ogl::Shader>& shader, const Material* material) {
if ( shader->hasUniform("material.diffuse") ) {
// setup material block
// shader->setUniformF(shader->uniform("material.emission"), material->emission);
// shader->setUniformF(shader->uniform("material.ambient"), material->ambient);
shader->setUniformF(shader->uniform("material.diffuse"), material->m_diffCol);
shader->setUniformF(shader->uniform("material.specular"), material->m_specCol);
}
// setup diffuse texture
if (material->m_diffuse != nullptr) {
material->m_diffuse->bind(0);
if ( shader->hasUniform("diffuseTexture") ) {
if (shader->hasUniform("diffuseTexture")) {
shader->setUniformI(shader->uniform("diffuseTexture"), 0);
}
}
// setup specular texture
if (material->m_specular != nullptr) {
material->m_specular->bind(1);
if (shader->hasUniform("specularTexture")) {
shader->setUniformI(shader->uniform("specularTexture"), 1);
}
}
//shader->setUniformF(shader->uniform("material.shininess"), material->shininess);
}
void MeshData::draw(std::shared_ptr<ogl::Shader> shader) const {
vao->bind();
vertexData->bind();
if ( material != nullptr ) {
setup_material(shader, material);
} else {
debug::info("no material is active, cannot bind textures!");
}
if ( drawInfo.restartIndex != ogl::NO_RESTART_IDX) {
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex( drawInfo.restartIndex );
......@@ -77,17 +120,17 @@ namespace fggl::gfx::ogl4 {
}
}
void setup_material(const std::shared_ptr<ogl::Shader>& shader, const PhongMaterial* material) {
if ( !shader->hasUniform("materials[0].ambient") ) {
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;
}
// setup material block
shader->setUniformF(shader->uniform("materials[0].emission"), material->emission);
shader->setUniformF(shader->uniform("materials[0].ambient"), material->ambient);
shader->setUniformF(shader->uniform("materials[0].diffuse"), material->diffuse);
shader->setUniformF(shader->uniform("materials[0].specular"), material->specular);
shader->setUniformF(shader->uniform("materials[0].shininess"), material->shininess);
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) {
......@@ -105,9 +148,9 @@ namespace fggl::gfx::ogl4 {
shader->setUniformI(shader->uniform("lights[0].isLocal"), local);
shader->setUniformI(shader->uniform("lights[0].isSpot"), 0);
shader->setUniformF(shader->uniform("lights[0].constantAttenuation"), 5.0f);
shader->setUniformF(shader->uniform("lights[0].linearAttenuation"), 0.0f);
shader->setUniformF(shader->uniform("lights[0].quadraticAttenuation"), 0.0f);
shader->setUniformF(shader->uniform("lights[0].constantAttenuation"), 5.0F);
shader->setUniformF(shader->uniform("lights[0].linearAttenuation"), 0.0F);
shader->setUniformF(shader->uniform("lights[0].quadraticAttenuation"), 0.0F);
shader->setUniformF(shader->uniform("Strength"), 0.6F);
......@@ -119,131 +162,96 @@ namespace fggl::gfx::ogl4 {
shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
shader->setUniformF(shader->uniform("lights[0].position"), lightPos);
} else {
auto camModelView = (viewMatrix * camTransform.model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
auto modelModelView = (viewMatrix * transform.model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
auto camModelView = (viewMatrix * camTransform.model() * math::vec4(0.0F, 0.0F, 0.0F, 1.0F));
auto modelModelView = (viewMatrix * transform.model() * math::vec4(0.0F, 0.0F, 0.0F, 1.0F));
math::vec3 viewDir = glm::normalize(camModelView - modelModelView);
shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
shader->setUniformF(shader->uniform("lights[0].position"),
math::vec3(viewMatrix * math::vec4(lightPos, 1.0f)));
math::vec3(viewMatrix * math::vec4(lightPos, 1.0F)));
}
shader->setUniformF(shader->uniform("lights[0].ambient"), {0.0f, 0.5f, 0.0f});
shader->setUniformF(shader->uniform("lights[0].colour"), {0.5f, 0.5f, 0.5f});
shader->setUniformF(shader->uniform("lights[0].ambient"), {0.0F, 0.5F, 0.0F});
shader->setUniformF(shader->uniform("lights[0].colour"), {0.5F, 0.5F, 0.5F});
}
static auto upload_texture( assets::AssetID name, assets::AssetManager* manager ) -> ogl::Texture* {
debug::info("loading texture: {}", name.get());
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);
auto uploadedTex = assets::make_asset_id_rt("ogl", std::to_string(name.get()) );
auto* texture = manager->get<ogl::Texture>( uploadedTex );
if ( texture != nullptr ) {
return texture;
}
// setup lighting mode
setup_lighting(shader, viewMatrix, camTransform, transform, lightPos);
setup_material(shader, world.tryGet<PhongMaterial>(entity, &DEFAULT_MATERIAL));
// get the texture data we plan to load
auto *textureData = manager->get<data::Texture2D>(name);
model.draw();
// create a texture
texture = new ogl::Texture(ogl::TextureType::Tex2D);
texture->setData( ogl::InternalImageFormat::RedGreenBlue, textureData);
}
return manager->set(uploadedTex, texture);
}
static ogl::Texture* upload_texture( std::string name, assets::AssetManager* manager ) {
debug::info("loading texture: {}", name);
auto* texture = manager->get<ogl::Texture>("ogl_"+name);
if ( texture != nullptr ) {
return texture;
static auto get_fallback_material(assets::AssetManager* manager) -> Material* {
auto* fallback = manager->get<Material>(FALLBACK_MAT);
if ( fallback != nullptr ) {
return fallback;
}
auto textureData = manager->get<data::Texture2D>(name);
ogl::Image image{
.type = ogl::PixelFormat::UNSIGNED_BYTE,
.format = ogl::ImageFormat::RGB,
.size = textureData->size,
.data = textureData->data,
};
texture = new ogl::Texture(ogl::TextureType::Tex2D);
texture->setData( ogl::InternalImageFormat::RedGreenBlue, image);
auto* mat = new Material();
mat->m_diffCol = FALLBACK_COLOUR;
mat->m_specCol= FALLBACK_COLOUR;
return manager->set("ogl_"+name, texture);
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>(SOLID_TEX);
return manager->set(FALLBACK_MAT, mat);
}
static Material* upload_material( std::string name, assets::AssetManager* manager ) {
static auto upload_material( assets::AssetID name, assets::AssetManager* manager ) -> Material* {
auto* meshMaterial = manager->get<mesh::Material>(name);
if ( meshMaterial == nullptr ){
debug::error("attempted to load material {}, but did not exist!", name);
return {};
debug::error("attempted to load material {}, but did not exist!", name.get());
return get_fallback_material(manager);
}
auto* material = manager->get<Material>("ogl_"+name);
auto uploadedMat = assets::make_asset_id_rt("ogl", std::to_string(name.get()) );
auto* material = manager->get<Material>( uploadedMat );
if ( material != nullptr ) {
return material;
}
material = new Material();
material->m_diffCol = meshMaterial->diffuse;
material->m_specCol = meshMaterial->specular;
// do we have a diffuse texture?
if ( !meshMaterial->diffuseTextures.empty() ) {
material->m_diffuse = upload_texture(meshMaterial->getPrimaryDiffuse(), manager);
} else {
material->m_diffuse = manager->get<ogl::Texture>( ogl4::SOLID_TEX );
}
// do we have a normal Texture?
if ( !meshMaterial->normalTextures.empty() ) {
material->m_normals = upload_texture(meshMaterial->getPrimaryNormals(), manager);
} else {
material->m_normals = manager->get<ogl::Texture>( ogl4::SOLID_TEX );
}
// do we have a specular texture?
if ( !meshMaterial->specularTextures.empty() ) {
material->m_specular = upload_texture(meshMaterial->getPrimarySpecular(), manager);
} else {
material->m_specular = manager->get<ogl::Texture>( ogl4::SOLID_TEX );
}
return manager->set("ogl_"+name, material);
return manager->set( uploadedMat, material);
}
MeshData upload_mesh(const mesh::Mesh3D& rawMesh, assets::AssetManager* manager) {
auto upload_mesh(const mesh::Mesh3D& rawMesh, assets::AssetManager* manager) -> MeshData {
auto vao = std::make_shared<ogl::VertexArray>();
return {
.vao = vao,
......@@ -255,7 +263,7 @@ namespace fggl::gfx::ogl4 {
};
}
std::vector<MeshData> upload_multi_mesh(const mesh::MultiMesh3D& rawMesh, assets::AssetManager* manager) {
auto upload_multi_mesh(const mesh::MultiMesh3D& rawMesh, assets::AssetManager* manager) -> std::vector<MeshData> {
std::vector<MeshData> gpuMeshes;
for (const auto& mesh : rawMesh.meshes) {
gpuMeshes.push_back( upload_mesh(mesh, manager) );
......
......@@ -28,8 +28,8 @@
namespace fggl::gfx::ogl4 {
static std::shared_ptr<ogl::ArrayBuffer> setupArrayBuffer(std::shared_ptr<ogl::VertexArray> &vao,
const std::vector<data::Vertex> &data) {
static auto setup_array_buffer(std::shared_ptr<ogl::VertexArray> &vao,
const std::vector<data::Vertex> &data) -> std::shared_ptr<ogl::ArrayBuffer> {
auto buff = std::make_shared<ogl::ArrayBuffer>();
buff->write(data.size() * sizeof(data::Vertex), data.data(), ogl::BufUsage::STATIC_DRAW);
......@@ -45,18 +45,18 @@ namespace fggl::gfx::ogl4 {
}
static std::shared_ptr<ogl::ElementBuffer> setupIndexBuffer(std::shared_ptr<ogl::VertexArray> &vao,
const std::vector<uint32_t> &data) {
static auto setup_index_buffer(std::shared_ptr<ogl::VertexArray> &vao,
const std::vector<uint32_t> &data) -> std::shared_ptr<ogl::ElementBuffer> {
auto elementBuffer = std::make_shared<ogl::ElementBuffer>();
elementBuffer->write(data.size() * sizeof(uint32_t),
data.data(), ogl::BufUsage::STATIC_DRAW);
return elementBuffer;
}
static void setupComponent(StaticModel &modelComp, std::shared_ptr<ogl::Shader> &shader, data::Mesh &mesh) {
static void setup_component(StaticModel &modelComp, std::shared_ptr<ogl::Shader> &shader, data::Mesh &mesh) {
auto vao = std::make_shared<ogl::VertexArray>();
auto meshBuffer = setupArrayBuffer(vao, mesh.vertexList());
auto elementBuffer = setupIndexBuffer(vao, mesh.indexList());
auto meshBuffer = setup_array_buffer(vao, mesh.vertexList());
auto elementBuffer = setup_index_buffer(vao, mesh.indexList());
// set up the element attributes
modelComp.vao = vao;
......@@ -67,7 +67,7 @@ namespace fggl::gfx::ogl4 {
modelComp.drawType = ogl::Primitive::TRIANGLE;
}
StaticModel* StaticModelRenderer::uploadMesh(assets::AssetGUID guid, const data::Mesh &mesh, bool allowCache) {
auto StaticModelRenderer::uploadMesh(assets::AssetID guid, const data::Mesh &mesh, bool allowCache) -> StaticModel* {
assert( m_assets != nullptr );
// if the asset has already been uploaded, we don't need to do anything
......@@ -79,8 +79,8 @@ namespace fggl::gfx::ogl4 {
// the asset does not exist, we need to upload it
auto* modelAsset = new StaticModel();
modelAsset->vao = std::make_shared<ogl::VertexArray>();
modelAsset->vertexData = setupArrayBuffer(modelAsset->vao, mesh.vertexList());
modelAsset->elements = setupIndexBuffer(modelAsset->vao, mesh.indexList());
modelAsset->vertexData = setup_array_buffer(modelAsset->vao, mesh.vertexList());
modelAsset->elements = setup_index_buffer(modelAsset->vao, mesh.indexList());
modelAsset->elementCount = mesh.indexCount();
modelAsset->drawType = ogl::Primitive::TRIANGLE;
......@@ -117,7 +117,7 @@ namespace fggl::gfx::ogl4 {
return modelAsset;
}*/
StaticModelGPU* StaticModelRenderer::uploadMesh2(const assets::AssetGUID& meshName, const data::Mesh &mesh) {
auto StaticModelRenderer::uploadMesh2(const assets::AssetID& meshName, const data::Mesh &mesh) -> StaticModelGPU* {
assert( m_assets != nullptr );
if ( m_assets->has(meshName) ) {
......@@ -127,8 +127,8 @@ namespace fggl::gfx::ogl4 {
auto* modelAsset = new StaticModelGPU();
modelAsset->vao = std::make_shared<ogl::VertexArray>();
modelAsset->vertices = setupArrayBuffer(modelAsset->vao, mesh.vertexList());
modelAsset->elements = setupIndexBuffer(modelAsset->vao, mesh.indexList());
modelAsset->vertices = setup_array_buffer(modelAsset->vao, mesh.vertexList());
modelAsset->elements = setup_index_buffer(modelAsset->vao, mesh.indexList());
modelAsset->elementCount = mesh.indexCount();
modelAsset->drawType = ogl::Primitive::TRIANGLE;
......@@ -136,7 +136,6 @@ namespace fggl::gfx::ogl4 {
}
#ifdef FGGL_ALLOW_DEFERRED_UPLOAD
static void setup_meshes(entity::EntityManager& world, ShaderCache* shaders, assets::AssetManager* manager) {
auto entsWithModels = world.find<mesh::StaticMesh3D>();
for ( const auto& mesher : entsWithModels ) {
......@@ -186,37 +185,14 @@ namespace fggl::gfx::ogl4 {
}
void StaticModelRenderer::resolveModels(entity::EntityManager &world) {
// FIXME: this needs something reactive or performance will suck.
auto renderableEnts = world.find<data::StaticMesh>();
for (const auto &renderable : renderableEnts) {
auto *currModel = world.tryGet<StaticModel>(renderable);
if (currModel != nullptr) {
continue;
}
auto &meshComp = world.get<data::StaticMesh>(renderable);
// model loading (should be via asset system)
auto loadedModel = uploadMesh("DEFER_ENT_"+ std::to_string((uint64_t)renderable), meshComp.mesh, false);
auto loadedShader = m_shaders->get(meshComp.pipeline);
// splice the loaded asset into the ecs
auto& entityModel = world.add<StaticModel>(renderable);
entityModel = *loadedModel;
entityModel.pipeline = loadedShader;
// let the user know we just did this...
debug::log(debug::Level::info, "Added static mesh to {}, pipeline was: {}", (uint64_t) renderable, meshComp.pipeline);
}
// multi-meshes
// new mesh formats
setup_meshes(world, m_shaders, m_assets);
setup_multi_meshes(world, m_shaders, m_assets);
// terrain
auto terrain = world.find<data::HeightMap>();
for (auto &renderable : terrain) {
auto currModel = world.tryGet<StaticModel>(renderable);
for (const auto &renderable : terrain) {
auto *currModel = world.tryGet<StaticModel>(renderable);
if (currModel != nullptr) {
continue;
}
......@@ -226,7 +202,7 @@ namespace fggl::gfx::ogl4 {
data::generateHeightMesh(heightmap, heightMapMesh);
auto &modelComp = world.add<StaticModel>(renderable);
setupComponent(modelComp, m_phong, heightMapMesh);
setup_component(modelComp, m_phong, heightMapMesh);
// we know this is a triangle strip with a restart vertex...
// FIXME the model should be telling us this...
......@@ -239,180 +215,6 @@ namespace fggl::gfx::ogl4 {
}
#endif
static void forward_camera_pass(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 renderables = world.find<StaticModel>();
for (const auto &entity : renderables) {
// ensure that the model pipeline actually exists...
const auto &model = world.get<StaticModel>(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
if (shader->hasUniform("lights[0].isEnabled")) {
bool local = true;
shader->setUniformI(shader->uniform("lights[0].isEnabled"), 1);
shader->setUniformI(shader->uniform("lights[0].isLocal"), local);
shader->setUniformI(shader->uniform("lights[0].isSpot"), 0);
shader->setUniformF(shader->uniform("lights[0].constantAttenuation"), 5.0f);
shader->setUniformF(shader->uniform("lights[0].linearAttenuation"), 0.0f);
shader->setUniformF(shader->uniform("lights[0].quadraticAttenuation"), 0.0f);
shader->setUniformF(shader->uniform("Strength"), 0.6F);
if (!local) {
lightPos = glm::normalize(lightPos);
auto viewDir = glm::normalize(camTransform.origin() - transform.origin());
auto halfVector = glm::normalize(lightPos + viewDir);
shader->setUniformF(shader->uniform("lights[0].halfVector"), halfVector);
shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
shader->setUniformF(shader->uniform("lights[0].position"), lightPos);
} else {
auto camModelView = (viewMatrix * camTransform.model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
auto modelModelView = (viewMatrix * transform.model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
math::vec3 viewDir = glm::normalize(camModelView - modelModelView);
shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
shader->setUniformF(shader->uniform("lights[0].position"),
math::vec3(viewMatrix * math::vec4(lightPos, 1.0f)));
}
shader->setUniformF(shader->uniform("lights[0].ambient"), {0.0f, 0.5f, 0.0f});
shader->setUniformF(shader->uniform("lights[0].colour"), {0.5f, 0.5f, 0.5f});
}
// material detection with fallback
auto* material = world.tryGet<PhongMaterial>(entity);
if ( material == nullptr ) {
material = &DEFAULT_MATERIAL;
}
if ( shader->hasUniform("materials[0].ambient") ) {
shader->setUniformF(shader->uniform("materials[0].emission"), material->emission);
shader->setUniformF(shader->uniform("materials[0].ambient"), material->ambient);
shader->setUniformF(shader->uniform("materials[0].diffuse"), material->diffuse);
shader->setUniformF(shader->uniform("materials[0].specular"), material->specular);
shader->setUniformF(shader->uniform("materials[0].shininess"), material->shininess);
}
if (shader->hasUniform("lightPos")) {
shader->setUniformF(shader->uniform("lightPos"), lightPos);
}
auto vao = model.vao;
vao->bind();
model.vertexData->bind();
if (model.restartIndex != ogl::NO_RESTART_IDX) {
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(model.restartIndex);
}
auto *elements = model.elements.get();
vao->drawElements(*elements, model.drawType, model.elementCount);
if (model.restartIndex != ogl::NO_RESTART_IDX) {
glDisable(GL_PRIMITIVE_RESTART);
}
}
}
static void forward_normal_pass(const entity::EntityID& camera, const fggl::entity::EntityManager& world, std::shared_ptr<ogl::Shader> shader) {
// 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 = camComp.perspective();
const math::mat4 viewMatrix = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up());
ogl::Location modelUniform = shader->uniform("model");
ogl::Location viewUniform = shader->uniform("view");
ogl::Location projUniform = shader->uniform("projection");
shader->use();
shader->setUniformMtx(projUniform, projectionMatrix);
shader->setUniformMtx(viewUniform, viewMatrix);
auto renderables = world.find<StaticModel>();
for (const auto &entity : renderables) {
// ensure that the model pipeline actually exists...
const auto &model = world.get<StaticModel>(entity);
// set model transform
const auto &transform = world.get<math::Transform>(entity);
shader->setUniformMtx(modelUniform, transform.model());
// render model
auto vao = model.vao;
vao->bind();
model.vertexData->bind();
if (model.restartIndex != ogl::NO_RESTART_IDX) {
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(model.restartIndex);
}
auto *elements = model.elements.get();
vao->drawElements(*elements, model.drawType, model.elementCount);
if (model.restartIndex != ogl::NO_RESTART_IDX) {
glDisable(GL_PRIMITIVE_RESTART);
}
}
}
void StaticModelRenderer::renderModelsForward(const entity::EntityManager &world, bool debugMode) {
......@@ -428,7 +230,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);
......@@ -438,7 +240,7 @@ namespace fggl::gfx::ogl4 {
auto normalShader = m_shaders->get("normals");
forward_pass_normals<StaticMesh>(cameraEnt, world, normalShader);
forward_pass_normals<StaticMultiMesh>(cameraEnt, world, normalShader);
forward_normal_pass(cameraEnt, world, normalShader);
//forward_normal_pass(cameraEnt, world, normalShader);
}
}
}
......
......@@ -31,29 +31,32 @@ 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) {
auto transform = data::OFFSET_NONE;
auto offset = node["offset"].as<math::vec3>(math::VEC3_ZERO);
transform = glm::translate(transform, offset);
auto scale = node["scale"].as<math::vec3>(math::VEC3_ONES);
transform = glm::scale(transform, scale);
debug::debug("scale: {}, {}, {}", scale.x, scale.y, scale.z);
// now the shape itself
auto type = node["type"].as<std::string>();
if (type == SHAPE_BOX) {
data::make_cube(mesh, transform);
} else if (type == SHAPE_SPHERE) {
auto stacks = node["stacks"].as<uint32_t>(DEFAULT_STACKS);
auto slices = node["slices"].as<uint32_t>(DEFAULT_SLICES);
data::make_sphere(mesh, transform, stacks, slices);
} else {
debug::log(debug::Level::warning, "unknown shape type requested: {}", type);
namespace {
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);
transform = glm::translate(transform, offset);
auto scale = node["scale"].as<math::vec3>(math::VEC3_ONES);
transform = glm::scale(transform, scale);
debug::debug("scale: {}, {}, {}", scale.x, scale.y, scale.z);
// now the shape itself
auto type = node["type"].as<std::string>();
if (type == SHAPE_BOX) {
data::make_cube(mesh, transform);
} else if (type == SHAPE_SPHERE) {
auto stacks = node["stacks"].as<uint32_t>(DEFAULT_STACKS);
auto slices = node["slices"].as<uint32_t>(DEFAULT_SLICES);
data::make_sphere(mesh, transform, stacks, slices);
} else {
debug::log(debug::Level::warning, "unknown shape type requested: {}", type);
}
}
}
void attach_mesh(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services &services) {
// check for the asset service
......@@ -66,46 +69,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);
auto* 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;
auto* 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;
}
......@@ -114,7 +128,9 @@ namespace fggl::gfx {
if ( spec.has("model") ) {
// figure out what model we want
auto assetStr = spec.get<std::string>("model", "");
auto* asset = assetService->get<ogl4::StaticModel>(assetStr);
auto assetId = assets::asset_from_user( assetStr );
auto* asset = assetService->get<ogl4::StaticModel>(assetId);
if ( asset == nullptr ) {
// the asset is not loaded/does not exist
......@@ -131,7 +147,7 @@ namespace fggl::gfx {
void attach_material(const entity::ComponentSpec &spec,
entity::EntityManager &manager,
const entity::EntityID &id,
modules::Services& services ) {
modules::Services& /*services*/ ) {
auto &mat = manager.add<gfx::PhongMaterial>(id);
mat.ambient = spec.get<math::vec3>("ambient", gfx::DEFAULT_AMBIENT);
mat.diffuse = spec.get<math::vec3>("diffuse", gfx::DEFAULT_DIFFUSE);
......@@ -139,17 +155,29 @@ 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) {
auto OpenGL4::factory(modules::ServiceName service, modules::Services &services) -> bool {
if (service == WindowGraphics::service) {
// setup the thing responsible for graphics
auto *storage = services.get<data::Storage>();
auto *fontLibrary = services.get<gui::FontLibrary>();
auto *assets = services.get<assets::AssetManager>();
services.bind<WindowGraphics, ogl4::WindowGraphics>(storage, fontLibrary, assets);
// register as responsible for creating rendering components
......@@ -158,7 +186,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;
}
......
......@@ -20,7 +20,7 @@
namespace fggl::gfx::ogl4 {
Graphics *WindowGraphics::create(display::Window &window) {
auto WindowGraphics::create(display::Window &window) -> Graphics * {
return new OpenGL4Backend(m_storage, m_fonts, m_assets, (GlFunctionLoader)glfwGetProcAddress);
}
......
......@@ -190,6 +190,7 @@ namespace fggl::display::glfw {
Window::~Window() {
if ( m_graphics != nullptr ) {
delete m_graphics;
m_graphics = nullptr;
}
......@@ -218,14 +219,14 @@ namespace fggl::display::glfw {
glfwMakeContextCurrent(m_window);
}
fggl::math::vec2i Window::frameSize() const {
auto Window::frameSize() const -> fggl::math::vec2i {
assert(m_window != nullptr);
math::vec2i size;
glfwGetFramebufferSize(m_window, &size.x, &size.y);
return size;
}
bool Window::wantClose() const {
auto Window::wantClose() const -> bool {
assert(m_window != nullptr);
return glfwWindowShouldClose(m_window) == GLFW_TRUE;
}
......
target_sources( fggl
PRIVATE
hexagon.cpp
)
\ No newline at end of file
/*
* 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 10/12/22.
//
#include "fggl/grid/hexagon.hpp"
namespace fggl::grid {
std::vector<IntHex> lineTo(const IntHex& start, const IntHex& end) {
const int distance = start.distance(end);
std::vector<IntHex> line;
for (auto i=0; i < distance; ++i) {
line.push_back( round2(hexLerp(start, end, 1.0F/distance * i)) );
}
return line;
}
} // namespace fggl:grid