diff --git a/CMakeLists.txt b/CMakeLists.txt index 112b2b6..3446b1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,9 +35,10 @@ set(SOURCES # Libraries include(FetchContent) find_package(raylib REQUIRED) +find_package(GLEW REQUIRED) find_package(libmorton REQUIRED) find_package(Boost COMPONENTS program_options REQUIRED) -set(LIBS raylib Boost::headers Boost::program_options) +set(LIBS raylib GLEW::GLEW Boost::headers Boost::program_options) set(FLAGS "") if(WIN32) diff --git a/flake.nix b/flake.nix index 01968a1..2c161a3 100644 --- a/flake.nix +++ b/flake.nix @@ -250,6 +250,7 @@ rec { buildInputs = with pkgs; [ # C/C++: raylib + glew raygui thread-pool libmorton @@ -424,4 +425,4 @@ rec { }; } ); -} +} \ No newline at end of file diff --git a/include/config.hpp b/include/config.hpp index fdb76c3..1dc4d53 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -75,8 +75,9 @@ constexpr float SOFTENING = 0.05; // Barnes-Hut [0.01, 1.0] // Graph Drawing static const Color EDGE_COLOR = Fade(BLUE, 0.3); +constexpr int DRAW_EDGES_LIMIT = 5'000'000; constexpr float VERTEX_SIZE = 0.75; -constexpr int DRAW_VERTICES_LIMIT = 1000000; +constexpr int DRAW_VERTICES_LIMIT = 1'000'000; static const Color VERTEX_COLOR = Fade(BLUE, 0.8); constexpr Color VERTEX_VISITED_COLOR = ORANGE; constexpr Color VERTEX_START_COLOR = ORANGE; diff --git a/include/renderer.hpp b/include/renderer.hpp index 884e616..3d8d741 100644 --- a/include/renderer.hpp +++ b/include/renderer.hpp @@ -24,10 +24,15 @@ private: RenderTexture klotski_target = LoadRenderTexture(GetScreenWidth() / 2, GetScreenHeight() - MENU_HEIGHT); RenderTexture menu_target = LoadRenderTexture(GetScreenWidth(), MENU_HEIGHT); - // Batching + // Edges + unsigned int edge_vao_id = 0; + unsigned int edge_vbo_id = 0; + std::vector edge_vertices; + Shader edge_shader = LoadShader("shader/edge_vertex.glsl", "shader/edge_fragment.glsl"); + int edge_color_loc = -1; std::vector> connections; - // Instancing + // Vertex instancing static constexpr int INSTANCE_COLOR_ATTR = 5; std::vector transforms; std::vector colors; @@ -37,30 +42,45 @@ private: unsigned int color_vbo_id = 0; public: - renderer(const orbit_camera& _camera, - const state_manager& _state, - input_handler& _input, - user_interface& _gui) + // TODO: I am allocating HUGE vertex buffers instead of resizing dynamically... + // Edges: 5'000'000 * 2 * 12 Byte ~= 115 MB + // Verts: 1'000'000 * 16 Byte ~= 15 MB + // This is also allocated on the CPU by the vectors + renderer(const orbit_camera& _camera, const state_manager& _state, input_handler& _input, user_interface& _gui) : state(_state), input(_input), gui(_gui), camera(_camera) { + // Edges + edge_shader.locs[SHADER_LOC_VERTEX_POSITION] = GetShaderLocationAttrib(edge_shader, "vertexPosition"); + edge_shader.locs[SHADER_LOC_MATRIX_MVP] = GetShaderLocation(edge_shader, "mvp"); + edge_shader.locs[SHADER_LOC_COLOR_DIFFUSE] = GetShaderLocation(edge_shader, "colDiffuse"); + edge_color_loc = GetShaderLocation(edge_shader, "colDiffuse"); + + edge_vertices.reserve(DRAW_EDGES_LIMIT * 2); + + edge_vao_id = rlLoadVertexArray(); + edge_vbo_id = rlLoadVertexBuffer(nullptr, DRAW_EDGES_LIMIT * 2 * sizeof(Vector3), true); + + rlEnableVertexArray(edge_vao_id); + rlEnableVertexBuffer(edge_vbo_id); + + rlSetVertexAttribute(0, 3, RL_FLOAT, false, sizeof(Vector3), 0); + rlEnableVertexAttribute(0); + + rlDisableVertexBuffer(); + rlDisableVertexArray(); + + // Vertex instancing instancing_shader.locs[SHADER_LOC_MATRIX_MVP] = GetShaderLocation(instancing_shader, "mvp"); instancing_shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocationAttrib( instancing_shader, "instanceTransform"); instancing_shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(instancing_shader, "viewPos"); - // infoln("LOC vertexPosition: {}", - // rlGetLocationAttrib(instancing_shader.id, "vertexPosition")); - // infoln("LOC instanceTransform: {}", - // rlGetLocationAttrib(instancing_shader.id, "instanceTransform")); - // infoln("LOC instanceColor: {}", rlGetLocationAttrib(instancing_shader.id, "instanceColor")); - - // vertex_mat.maps[MATERIAL_MAP_DIFFUSE].color = VERTEX_COLOR; vertex_mat.shader = instancing_shader; transforms.reserve(DRAW_VERTICES_LIMIT); colors.reserve(DRAW_VERTICES_LIMIT); - color_vbo_id = rlLoadVertexBuffer(colors.data(), DRAW_VERTICES_LIMIT * sizeof(Color), true); + color_vbo_id = rlLoadVertexBuffer(nullptr, DRAW_VERTICES_LIMIT * sizeof(Color), true); rlEnableVertexArray(cube_instance.vaoId); rlEnableVertexBuffer(color_vbo_id); @@ -83,12 +103,19 @@ public: UnloadRenderTexture(klotski_target); UnloadRenderTexture(menu_target); + // Edges + rlUnloadVertexArray(edge_vao_id); + rlUnloadVertexBuffer(edge_vbo_id); + UnloadShader(edge_shader); + // Instancing UnloadMaterial(vertex_mat); UnloadMesh(cube_instance); // I think the shader already gets unloaded with the material? // UnloadShader(instancing_shader); + + rlUnloadVertexBuffer(color_vbo_id); } private: diff --git a/shader/edge_fragment.glsl b/shader/edge_fragment.glsl new file mode 100644 index 0000000..c4e009c --- /dev/null +++ b/shader/edge_fragment.glsl @@ -0,0 +1,9 @@ +#version 330 + +uniform vec4 colDiffuse; +out vec4 finalColor; + +void main() +{ + finalColor = colDiffuse; +} \ No newline at end of file diff --git a/shader/edge_vertex.glsl b/shader/edge_vertex.glsl new file mode 100644 index 0000000..fedba82 --- /dev/null +++ b/shader/edge_vertex.glsl @@ -0,0 +1,9 @@ +#version 330 + +in vec3 vertexPosition; +uniform mat4 mvp; + +void main() +{ + gl_Position = mvp * vec4(vertexPosition, 1.0); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 4a69c90..933a1f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,3 @@ -#include -#include -#include - #include "config.hpp" #include "input_handler.hpp" #include "cpu_layout_engine.hpp" @@ -9,6 +5,10 @@ #include "state_manager.hpp" #include "user_interface.hpp" +#include +#include +#include +#include #include #if not defined(_WIN32) @@ -65,6 +65,13 @@ auto ui_mode() -> int SetConfigFlags(FLAG_WINDOW_ALWAYS_RUN); InitWindow(INITIAL_WIDTH * 2, INITIAL_HEIGHT + MENU_HEIGHT, "MassSprings"); + // GLEW setup + glewExperimental = GL_TRUE; + const GLenum glew_err = glewInit(); + if (glew_err != GLEW_OK) { + TraceLog(LOG_FATAL, "Failed to initialize GLEW: %s", glewGetErrorString(glew_err)); + } + // Game setup cpu_layout_engine physics(thread_pool); state_manager state(physics, preset_file); diff --git a/src/renderer.cpp b/src/renderer.cpp index a66f32f..f24f95f 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -4,6 +4,7 @@ #include #include #include +#include auto renderer::update_texture_sizes() -> void { @@ -34,10 +35,24 @@ auto renderer::draw_mass_springs(const std::vector& masses) -> void return; } - // Prepare connection batching + // Prepare edge buffer { #ifdef TRACY - ZoneNamedN(prepare_connections, "PrepareConnectionsBatching", true); + ZoneNamedN(prepare_edge_buffers, "PrepareEdgeBuffers", true); + #endif + + edge_vertices.clear(); + for (const auto& [from, to] : state.get_links()) { + edge_vertices.push_back(masses[from]); + edge_vertices.push_back(masses[to]); + } + rlUpdateVertexBuffer(edge_vbo_id, edge_vertices.data(), edge_vertices.size() * sizeof(Vector3), 0); + } + + // Prepare connection drawing + { + #ifdef TRACY + ZoneNamedN(prepare_connections, "PrepareConnectionsDrawing", true); #endif connections.clear(); @@ -47,7 +62,6 @@ auto renderer::draw_mass_springs(const std::vector& masses) -> void const Vector3& current_mass = masses[state.get_current_index()]; const Vector3& winning_mass = masses[_state]; connections.emplace_back(current_mass, winning_mass); - DrawLine3D(current_mass, winning_mass, Fade(TARGET_BLOCK_COLOR, 0.5)); } } } @@ -73,24 +87,26 @@ auto renderer::draw_mass_springs(const std::vector& masses) -> void #endif const Ray ray = GetScreenToWorldRayEx( GetMousePosition() - Vector2(GetScreenWidth() / 2.0f, MENU_HEIGHT), - camera.camera, graph_target.texture.width, graph_target.texture.height); - RayCollision collision; // Ray collision hit info + camera.camera, + graph_target.texture.width, + graph_target.texture.height); + // Ray collision hit info size_t mass = 0; for (const auto& [x, y, z] : masses) { - collision = GetRayCollisionBox(ray, - BoundingBox{ - { - x - VERTEX_SIZE / 2.0f, - y - VERTEX_SIZE / 2.0f, - z - VERTEX_SIZE / 2.0f - }, - { - x + VERTEX_SIZE / 2.0f, - y + VERTEX_SIZE / 2.0f, - z + VERTEX_SIZE / 2.0f - } - }); + const RayCollision collision = GetRayCollisionBox(ray, + BoundingBox{ + { + x - VERTEX_SIZE / 2.0f, + y - VERTEX_SIZE / 2.0f, + z - VERTEX_SIZE / 2.0f + }, + { + x + VERTEX_SIZE / 2.0f, + y + VERTEX_SIZE / 2.0f, + z + VERTEX_SIZE / 2.0f + } + }); if (collision.hit) { input.collision_mass = mass; break; @@ -160,23 +176,53 @@ auto renderer::draw_mass_springs(const std::vector& masses) -> void ClearBackground(RAYWHITE); BeginMode3D(camera.camera); - // Draw springs (batched) + rlDrawRenderBatchActive(); + + // Draw edges { #ifdef TRACY ZoneNamedN(draw_springs, "DrawSprings", true); #endif - rlBegin(RL_LINES); - for (const auto& [from, to] : state.get_links()) { - if (masses.size() > from && masses.size() > to) { - const auto& [ax, ay, az] = masses[from]; - const auto& [bx, by, bz] = masses[to]; - rlColor4ub(EDGE_COLOR.r, EDGE_COLOR.g, EDGE_COLOR.b, EDGE_COLOR.a); - rlVertex3f(ax, ay, az); - rlVertex3f(bx, by, bz); - } - } - rlEnd(); + rlEnableShader(edge_shader.id); + + Matrix modelview = rlGetMatrixModelview(); + Matrix projection = rlGetMatrixProjection(); + Matrix mvp = MatrixMultiply(modelview, projection); + rlSetUniformMatrix(edge_shader.locs[SHADER_LOC_MATRIX_MVP], mvp); + + const std::array edge_color = { + EDGE_COLOR.r / 255.0f, + EDGE_COLOR.g / 255.0f, + EDGE_COLOR.b / 255.0f, + EDGE_COLOR.a / 255.0f + }; + rlSetUniform(edge_color_loc, edge_color.data(), SHADER_UNIFORM_VEC4, 1); + + glBindVertexArray(edge_vao_id); + glDrawArrays(GL_LINES, 0, edge_vertices.size()); + glBindVertexArray(0); + + rlDisableShader(); + + // This draws triangles: + // rlEnableVertexArray(edge_vao_id); + // rlColor4ub(EDGE_COLOR.r, EDGE_COLOR.g, EDGE_COLOR.b, EDGE_COLOR.a); + // rlDrawVertexArray(0, edge_vertices.size()); + // rlDisableVertexArray(); + + // This is fucking slow: + // rlBegin(RL_LINES); + // for (const auto& [from, to] : state.get_links()) { + // if (masses.size() > from && masses.size() > to) { + // const auto& [ax, ay, az] = masses[from]; + // const auto& [bx, by, bz] = masses[to]; + // rlColor4ub(EDGE_COLOR.r, EDGE_COLOR.g, EDGE_COLOR.b, EDGE_COLOR.a); + // rlVertex3f(ax, ay, az); + // rlVertex3f(bx, by, bz); + // } + // } + // rlEnd(); } // Draw masses (instanced)