diff --git a/Makefile b/Makefile index 69dc369..7f0f49a 100644 --- a/Makefile +++ b/Makefile @@ -43,13 +43,13 @@ else CFLAGS += -O3 endif -CXXFLAGS += -std=gnu++11 -Wall -pedantic $(fpic) -DHAVE_ZIP_DEFLATE +CXXFLAGS += -std=gnu++11 -Wall -pedantic $(fpic) -DHAVE_ZIP_DEFLATE $(shell pkg-config assimp --cflags) CFLAGS += -std=gnu99 -Wall -pedantic $(fpic) -DHAVE_ZIP_DEFLATE SOURCES := $(wildcard *.cpp) $(wildcard */*.cpp) CSOURCES := $(wildcard *.c) $(wildcard */*.c) OBJECTS := $(SOURCES:.cpp=.o) $(CSOURCES:.c=.o) -LIBS += $(GL_LIB) +LIBS += $(GL_LIB) $(shell pkg-config assimp --libs) all: $(TARGET) diff --git a/app/heightmap.cpp b/app/heightmap.cpp index d865caa..481aa72 100644 --- a/app/heightmap.cpp +++ b/app/heightmap.cpp @@ -3,83 +3,61 @@ #include #include #include +#include #include #include -#include -#include -#include - using namespace std; using namespace glm; using namespace GL; -class HeightGrid +class Scene { public: - void init(unsigned size) + void init(const std::string& path) { - init_vertices(size); - init_indices(size); - vector arrays = { - { Shader::VertexLocation, 2, GL_SHORT, GL_FALSE, 0, 0 }, - }; - array.setup(arrays, &vertex, &elems); + auto meshes = load_scene(path); + + for (auto& mesh : meshes) + { + auto drawable = Util::make_unique(); + drawable->vertex.init(GL_ARRAY_BUFFER, mesh.vbo, Buffer::None); + drawable->elems.init(GL_ELEMENT_ARRAY_BUFFER, mesh.ibo, Buffer::None); + drawable->array.setup(mesh.arrays, &drawable->vertex, + &drawable->elems); + drawable->indices = mesh.ibo.size(); + drawables.push_back(move(drawable)); + + } + + mat4 model = translate(mat4(1.0), vec3(0, -5, -10)); + uniform_offset.init(GL_UNIFORM_BUFFER, sizeof(model), + Buffer::None, value_ptr(model), Shader::VertexSlot1); } void render() { - array.bind(); - glDrawElements(GL_TRIANGLE_STRIP, elements, GL_UNSIGNED_SHORT, nullptr); - array.unbind(); + uniform_offset.bind(); + for (auto& drawable : drawables) + { + drawable->array.bind(); + glDrawElements(GL_TRIANGLES, drawable->indices, + GL_UNSIGNED_INT, nullptr); + drawable->array.unbind(); + } + uniform_offset.unbind(); } private: - VertexArray array; - Buffer vertex; - Buffer elems; - unsigned elements = 0; - - void init_vertices(unsigned size) + struct Drawable { - vector vertices; - vertices.reserve(2 * size * size); - for (unsigned y = 0; y < size; y++) - { - for (unsigned x = 0; x < size; x++) - { - vertices.push_back(x); - vertices.push_back(int(size) - 1 - y); - } - } - - vertex.init(GL_ARRAY_BUFFER, 2 * size * size * sizeof(GLshort), Buffer::None, vertices.data()); - } - - void init_indices(unsigned size) - { - elements = (size - 1) * (2 * size + 1); - - vector indices; - indices.reserve(elements); - - int pos = 0; - for (unsigned y = 0; y < size - 1; y++) - { - int dir_odd = -int(size) + ((y & 1) ? -1 : 1); - int dir_even = size; - - for (unsigned x = 0; x < 2 * size - 1; x++) - { - indices.push_back(pos); - pos += (x & 1) ? dir_odd : dir_even; - } - indices.push_back(pos); - indices.push_back(pos); - } - - elems.init(GL_ELEMENT_ARRAY_BUFFER, elements * sizeof(GLushort), Buffer::None, indices.data()); - } + VertexArray array; + Buffer vertex; + Buffer elems; + size_t indices; + }; + Buffer uniform_offset; + vector> drawables; }; class HeightmapApp : public LibretroGLApplication @@ -139,12 +117,24 @@ class HeightmapApp : public LibretroGLApplication global.camera_pos = vec4(player_pos.x, player_pos.y, player_pos.z, 0.0); + global_fragment.camera_pos = global.camera_pos; + global_fragment.light_pos = vec4(100.0, 100.0, 100.0, 1.0); + global_fragment.light_color = vec4(1.0); + global_fragment.light_ambient = vec4(0.25); + GlobalTransforms *buf; if (global_buffer.map(buf)) { *buf = global; global_buffer.unmap(); } + + GlobalFragmentData *frag_buf; + if (global_fragment_buffer.map(frag_buf)) + { + *frag_buf = global_fragment; + global_fragment_buffer.unmap(); + } } void viewport_changed(const Resolution& res) override @@ -169,7 +159,7 @@ class HeightmapApp : public LibretroGLApplication vec3 right_walk_dir = vec3(rotate_y_right * vec4(0, 0, -1, 1)); - vec3 mod_speed = buttons.r ? vec3(120.0f) : vec3(60.0f); + vec3 mod_speed = buttons.r ? vec3(240.0f) : vec3(120.0f); vec3 velocity = player_look_dir * vec3(analog.y * -0.25f) + right_walk_dir * vec3(analog.x * 0.25f); @@ -177,30 +167,6 @@ class HeightmapApp : public LibretroGLApplication update_global_data(); } - void bind_all() - { - global_buffer.bind(); - global_frag_buffer.bind(); - shader.use(); - tex.bind(0); - sampler.bind(0); - - skybox.tex.bind(1); - skybox.sampler.bind(1); - } - - void unbind_all() - { - global_buffer.unbind(); - global_frag_buffer.unbind(); - shader.unbind(); - tex.unbind(0); - sampler.unbind(0); - - skybox.tex.unbind(1); - skybox.sampler.unbind(1); - } - void run(float delta, const InputState& input, GLuint) override { auto analog = input.analog; @@ -214,12 +180,6 @@ class HeightmapApp : public LibretroGLApplication analog.ry = 0.0f; update_input(delta, analog, input.pressed); - if (input.triggered.a) - { - foo_define ^= 1; - shader.set_global_define("FOO", foo_define); - } - glViewport(0, 0, width, height); glClearColor(0.3f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); @@ -228,13 +188,26 @@ class HeightmapApp : public LibretroGLApplication glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); - bind_all(); - grid.render(); + global_buffer.bind(); + global_fragment_buffer.bind(); + + skybox.tex.bind(0); + skybox.sampler.bind(0); + + shader.use(); + scene.render(); + shader.unbind(); + skybox.shader.use(); skybox.arrays.bind(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); skybox.arrays.unbind(); - unbind_all(); + skybox.shader.unbind(); + + global_buffer.unbind(); + global_fragment_buffer.unbind(); + skybox.tex.unbind(0); + skybox.sampler.unbind(0); } void get_context_version(unsigned& major, unsigned& minor) const override @@ -245,10 +218,9 @@ class HeightmapApp : public LibretroGLApplication void load() override { - global_buffer.init(GL_UNIFORM_BUFFER, sizeof(global), Buffer::WriteOnly); - - vec4 global_color(0.8f, 0.6f, 0.2f, 1.0f); - global_frag_buffer.init(GL_UNIFORM_BUFFER, sizeof(global_color), Buffer::None, value_ptr(global_color), 1); + global_buffer.init(GL_UNIFORM_BUFFER, sizeof(global), Buffer::WriteOnly, nullptr, Shader::GlobalVertexData); + global_fragment_buffer.init(GL_UNIFORM_BUFFER, + sizeof(global_fragment), Buffer::WriteOnly, nullptr, Shader::GlobalFragmentData); player_pos = vec3(0.0f); player_look_dir = vec3(0, 0, -1); @@ -256,19 +228,14 @@ class HeightmapApp : public LibretroGLApplication player_view_deg_y = 0.0f; shader.init(path("test.vs"), path("test.fs")); - Shader::reserve_global_define("FOO", 1); - grid.init(128); - - tex.load_texture_2d({Texture::Texture2D, { path("app/test.png") }, true }); - sampler.init(Sampler::TrilinearClamp); - + scene.init(path("test.obj")); skybox.tex.load_texture_2d({Texture::TextureCube, { - path("app/skybox_autum_forest_right.png"), - path("app/skybox_pine_forest_left.png"), - path("app/skybox_autum_forest_top.png"), - path("app/skybox_autum_forest_bottom.png"), - path("app/skybox_pine_forest_front.png"), - path("app/skybox_autum_forest_back.png"), + path("app/xpos.png"), + path("app/xneg.png"), + path("app/ypos.png"), + path("app/yneg.png"), + path("app/zpos.png"), + path("app/zneg.png"), }, true}); skybox.sampler.init(Sampler::TrilinearClamp); skybox.shader.init(path("skybox.vs"), path("skybox.fs")); @@ -299,16 +266,21 @@ class HeightmapApp : public LibretroGLApplication vec4 camera_pos; }; + struct GlobalFragmentData + { + vec4 camera_pos; + vec4 light_pos; + vec4 light_color; + vec4 light_ambient; + }; + GlobalTransforms global; + GlobalFragmentData global_fragment; Buffer global_buffer; - Buffer global_frag_buffer; + Buffer global_fragment_buffer; + Shader shader; - unsigned foo_define = 0; - - HeightGrid grid; - - Texture tex; - Sampler sampler; + Scene scene; struct { @@ -322,6 +294,6 @@ class HeightmapApp : public LibretroGLApplication unique_ptr libretro_gl_application_create() { - return unique_ptr(new HeightmapApp); + return Util::make_unique(); } diff --git a/app/skybox_autum_forest_back.png b/app/skybox_autum_forest_back.png deleted file mode 100644 index eec6267..0000000 Binary files a/app/skybox_autum_forest_back.png and /dev/null differ diff --git a/app/skybox_autum_forest_bottom.png b/app/skybox_autum_forest_bottom.png deleted file mode 100644 index f9b3204..0000000 Binary files a/app/skybox_autum_forest_bottom.png and /dev/null differ diff --git a/app/skybox_autum_forest_right.png b/app/skybox_autum_forest_right.png deleted file mode 100644 index 0bc34ba..0000000 Binary files a/app/skybox_autum_forest_right.png and /dev/null differ diff --git a/app/skybox_autum_forest_top.png b/app/skybox_autum_forest_top.png deleted file mode 100644 index 765126a..0000000 Binary files a/app/skybox_autum_forest_top.png and /dev/null differ diff --git a/app/skybox_pine_forest_front.png b/app/skybox_pine_forest_front.png deleted file mode 100644 index 7aa92e5..0000000 Binary files a/app/skybox_pine_forest_front.png and /dev/null differ diff --git a/app/skybox_pine_forest_left.png b/app/skybox_pine_forest_left.png deleted file mode 100644 index bf9e06a..0000000 Binary files a/app/skybox_pine_forest_left.png and /dev/null differ diff --git a/app/xneg.png b/app/xneg.png new file mode 100644 index 0000000..cb3d048 Binary files /dev/null and b/app/xneg.png differ diff --git a/app/xpos.png b/app/xpos.png new file mode 100644 index 0000000..fdf03f2 Binary files /dev/null and b/app/xpos.png differ diff --git a/app/yneg.png b/app/yneg.png new file mode 100644 index 0000000..b360c21 Binary files /dev/null and b/app/yneg.png differ diff --git a/app/ypos.png b/app/ypos.png new file mode 100644 index 0000000..a9f355a Binary files /dev/null and b/app/ypos.png differ diff --git a/app/zneg.png b/app/zneg.png new file mode 100644 index 0000000..3610621 Binary files /dev/null and b/app/zneg.png differ diff --git a/app/zpos.png b/app/zpos.png new file mode 100644 index 0000000..c7e23b0 Binary files /dev/null and b/app/zpos.png differ diff --git a/gl/buffer.hpp b/gl/buffer.hpp index 2d6b36a..51596d9 100644 --- a/gl/buffer.hpp +++ b/gl/buffer.hpp @@ -4,6 +4,7 @@ #include "global.hpp" #include #include +#include namespace GL { @@ -18,6 +19,13 @@ namespace GL }; void init(GLenum target, GLsizei size, GLuint flags, const void *initial_data = nullptr, GLuint index = 0); + + template + typename std::enable_if::type init(GLenum target, const T& t, GLuint flags, GLuint index = 0) + { + init(target, t.size() * sizeof(typename T::value_type), flags, t.data(), index); + } + void reset() override; void destroyed() override; diff --git a/gl/global.cpp b/gl/global.cpp index 6cc220e..428dfe9 100644 --- a/gl/global.cpp +++ b/gl/global.cpp @@ -48,12 +48,17 @@ namespace GL void ContextManager::register_listener(ContextListener *listener) { - ListenerState state{}; - state.listener = listener; - state.id = context_id++; +#ifdef GL_DEBUG + cerr << "Registering listener: " << + static_cast(listener) << endl; +#endif + + auto state = make_shared(); + state->listener = listener; + state->id = context_id++; if (alive) - state.reset_chain(); - listeners.push_back(std::move(state)); + state->reset_chain(); + listeners.push_back(move(state)); } void ContextManager::register_dependency(ContextListener *master, ContextListener *slave) @@ -61,15 +66,28 @@ namespace GL if (!master || !slave) return; - auto& itr_master = find_or_throw(listeners, *master); - auto& itr_slave = find_or_throw(listeners, *slave); - itr_master.dependencies.push_back(&itr_slave); - itr_slave.dependers.push_back(&itr_master); +#ifdef GL_DEBUG + cerr << "Registering dependency: " << + static_cast(master) << " < " << static_cast(slave) << endl; +#endif + + auto pred_master = [master](const shared_ptr& state) { + return state->listener == master; + }; + + auto pred_slave = [slave](const shared_ptr& state) { + return state->listener == slave; + }; + + auto& itr_master = find_if_or_throw(listeners, pred_master); + auto& itr_slave = find_if_or_throw(listeners, pred_slave); + itr_master->dependencies.push_back(itr_slave); + itr_slave->dependers.push_back(itr_master); if (alive) { - itr_master.reset_chain(); - itr_slave.reset_chain(); + itr_master->reset_chain(); + itr_slave->reset_chain(); } } @@ -78,24 +96,59 @@ namespace GL if (!master_ptr || !slave_ptr) return; - auto& itr_master = find_or_throw(listeners, *master_ptr); - auto& itr_slave = find_or_throw(listeners, *slave_ptr); - erase_all(itr_master.dependencies, &itr_slave); - erase_all(itr_slave.dependers, &itr_master); +#ifdef GL_DEBUG + cerr << "Unregistering dependency: " << + static_cast(master_ptr) << " < " << static_cast(slave_ptr) << endl; +#endif + + auto pred_master = [master_ptr](const shared_ptr& state) { + return state->listener == master_ptr; + }; + + auto pred_slave = [slave_ptr](const shared_ptr& state) { + return state->listener == slave_ptr; + }; + + auto& itr_master = find_if_or_throw(listeners, pred_master); + auto& itr_slave = find_if_or_throw(listeners, pred_slave); + + auto pred_master_erase = [&itr_master](const weak_ptr& state) { + return state.lock() == itr_master; + }; + + auto pred_slave_erase = [&itr_slave](const weak_ptr& state) { + return state.lock() == itr_slave; + }; + + erase_if_all(itr_master->dependencies, pred_slave_erase); + erase_if_all(itr_slave->dependers, pred_master_erase); } void ContextManager::unregister_listener(const ContextListener *listener) { - auto& itr = find_or_throw(listeners, *listener); - for (auto& parent_listener : itr.dependers) - erase_all(parent_listener->dependencies, &itr); - for (auto& child_listener : itr.dependencies) - erase_all(child_listener->dependers, &itr); +#ifdef GL_DEBUG + cerr << "Unregistering listener: " << + static_cast(listener) << endl; +#endif - if (itr.signaled) + auto pred = [listener](const weak_ptr& state) { + return shared_ptr(state)->listener == listener; + }; + + auto& itr = find_if_or_throw(listeners, pred); + auto itr_pred = [&itr](const weak_ptr& state) { + return state.lock() == itr; + }; + + for (auto& parent_listener : itr->dependers) + erase_if_all(parent_listener.lock()->dependencies, itr_pred); + for (auto& child_listener : itr->dependencies) + erase_if_all(child_listener.lock()->dependers, itr_pred); + + if (itr->signaled) { - itr.signaled = false; - itr.listener->destroyed(); + itr->signaled = false; + itr->listener->destroyed(); } erase_all(listeners, itr); @@ -105,13 +158,13 @@ namespace GL { alive = true; for (auto& state : listeners) - state.reset_chain(); + state->reset_chain(); } void ContextManager::notify_destroyed() { for (auto& state : listeners) - state.destroy_chain(); + state->destroy_chain(); alive = false; } @@ -121,7 +174,12 @@ namespace GL { signaled = true; for (auto state : dependencies) - state->reset_chain(); + state.lock()->reset_chain(); + +#ifdef GL_DEBUG + cerr << "Resetting: " << + static_cast(listener) << endl; +#endif listener->reset(); } } @@ -132,7 +190,12 @@ namespace GL { signaled = false; for (auto state : dependers) - state->destroy_chain(); + state.lock()->destroy_chain(); + +#ifdef GL_DEBUG + cerr << "Destroying: " << + static_cast(listener) << endl; +#endif listener->destroyed(); } } diff --git a/gl/global.hpp b/gl/global.hpp index 192cfbe..f3c7d95 100644 --- a/gl/global.hpp +++ b/gl/global.hpp @@ -10,6 +10,10 @@ #include "util.hpp" +#include +#include +#include + namespace GL { class LibretroGLApplication @@ -101,10 +105,10 @@ namespace GL struct ListenerState { - ContextListener *listener; + ContextListener *listener = nullptr; bool signaled = false; - std::vector dependencies; - std::vector dependers; + std::vector> dependencies; + std::vector> dependers; uint64_t id = 0; void reset_chain(); @@ -116,7 +120,7 @@ namespace GL bool operator!=(const ContextListener& other) const { return listener != &other; } }; - std::vector listeners; + std::vector> listeners; bool alive = false; uint64_t context_id = 0; diff --git a/skybox.fs b/skybox.fs index a521a0d..2a14690 100644 --- a/skybox.fs +++ b/skybox.fs @@ -1,10 +1,26 @@ +uniform GlobalFragmentData +{ + vec4 camera_pos; + vec4 light_pos; + vec4 light_color; + vec4 light_ambient; +}; -uniform samplerCube uSampler1; +uniform samplerCube uSampler0; in vec3 vDirection; out vec4 FragColor; +float saturate(float a) +{ + return clamp(a, 0.0, 1.0); +} + void main() { - FragColor = texture(uSampler1, normalize(vDirection)); + vec3 dir = normalize(vDirection); + vec4 col = texture(uSampler0, dir); + vec3 to_light = light_pos.xyz - camera_pos.xyz; + col += pow(saturate(dot(dir, normalize(to_light))), 200.0 * length(to_light)); + FragColor = col; } diff --git a/test.fs b/test.fs index 1b6cea7..556ad6b 100644 --- a/test.fs +++ b/test.fs @@ -1,40 +1,46 @@ -uniform GlobalVertexData +uniform GlobalFragmentData { - mat4 vp; - mat4 view; - mat4 view_nt; - mat4 proj; - mat4 inv_vp; - mat4 inv_view; - mat4 inv_view_nt; - mat4 inv_proj; vec4 camera_pos; + vec4 light_pos; + vec4 light_color; + vec4 light_ambient; }; -in vec2 vTex; in vec3 vWorldPos; +in vec3 vNormal; -uniform sampler2D uSampler0; -uniform samplerCube uSampler1; +uniform samplerCube uSampler0; out vec4 FragColor; +float saturate(float a) +{ + return clamp(a, 0.0, 1.0); +} + void main() { - vec3 to_plane = normalize(vWorldPos - camera_pos.xyz); - vec3 normal = vec3(0.02 * sin(vWorldPos.x * 0.15), 0.01 * cos(vWorldPos.y * 0.29), 1.0); - normal = normalize(normal); - vec3 reflect_dir = reflect(to_plane, normal); - vec4 col_reflect = texture(uSampler1, reflect_dir); + vec3 vEye = normalize(camera_pos.xyz - vWorldPos); + vec3 vLight = normalize(light_pos.xyz - vWorldPos); + vec3 normal = normalize(vNormal); - float fres = 1.0; - vec3 refract_dir = refract(to_plane, normal, 1.4); - vec4 col_refract = vec4(0.0); - if (refract_dir != vec3(0.0)) + float ndotl = saturate(dot(vLight, normal)); + float spec = 0.0; + if (ndotl > 0.0) { - col_refract = texture(uSampler1, refract_dir); - fres = pow(1.0 - dot(refract_dir, -normal), 5.0); + vec3 half_vec = normalize(vEye + vLight); + spec = pow(saturate(dot(half_vec, normal)), 50.0); } - FragColor = mix(col_refract, col_reflect, fres); + vec3 refract_normal = refract(-vEye, normal, 1.4); + vec3 reflect_normal = reflect(-vEye, normal); + + float incidence = 0.0; + incidence = dot(refract_normal, -normal); + + vec4 refracted = texture(uSampler0, refract_normal); + vec4 reflected = texture(uSampler0, reflect_normal); + + vec4 col = light_ambient + light_color * spec; + FragColor = mix(reflected, refracted, incidence) + col * vec4(1.1, 0.8, 0.7, 1.0); } diff --git a/test.vs b/test.vs index 705f9ae..90e4367 100644 --- a/test.vs +++ b/test.vs @@ -11,18 +11,22 @@ uniform GlobalVertexData vec4 camera_pos; }; -in vec2 aVertex; -in vec2 aTexCoord; +uniform VertexSlot1 +{ + mat4 model; +}; + +in vec3 aVertex; in vec3 aNormal; -out vec2 vTex; out vec3 vWorldPos; +out vec3 vNormal; void main() { - vec4 world = vec4(aVertex.x - 64.0, aVertex.y - 64.0, -10.0, 1.0); + vec4 world = model * vec4(aVertex, 1.0); gl_Position = vp * world; - vTex = vec2(1.0, -1.0) * aVertex / 128.0; vWorldPos = world.xyz; + vNormal = mat3(model) * aNormal; } diff --git a/util.hpp b/util.hpp index 56ffbe3..2e742f9 100644 --- a/util.hpp +++ b/util.hpp @@ -12,6 +12,16 @@ #include #include #include +#include + +namespace Util +{ + template + inline std::unique_ptr make_unique(P&&... p) + { + return std::unique_ptr(new T(std::forward

(p)...)); + } +} namespace Template { @@ -24,10 +34,25 @@ namespace Template return *itr; } + template + inline auto find_if_or_throw(T& t, const P& pred) -> decltype(*std::begin(t)) + { + auto itr = std::find_if(std::begin(t), std::end(t), pred); + if (itr == std::end(t)) + throw std::runtime_error("Couldn't find element in iterator."); + return *itr; + } + + template + inline void erase_if_all(T& t, const P& p) + { + t.erase(std::remove_if(std::begin(t), std::end(t), p), std::end(t)); + } + template inline void erase_all(T& t, const U& u) { - t.erase(std::remove_if(std::begin(t), std::end(t), [&u](const U& other) { return other == u; }), std::end(t)); + erase_if_all(t, [&u](const U& other) { return other == u; }); } }