From af2295963e11e70191c58acc0ce6287ffbed70f2 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Wed, 9 Jul 2025 21:26:30 -0400 Subject: [PATCH] color picker start --- .gitmodules | 3 + CMakeLists.txt | 82 ++++++++++ default.nix | 56 +++++++ depends.txt | 2 + include/asset_loader.h | 22 +++ include/sql.h | 358 +++++++++++++++++++++++++++++++++++++++++ lib/blt-with-graphics | 1 + src/asset_loader.cpp | 18 +++ src/main.cpp | 60 +++++++ src/sql.cpp | 48 ++++++ 10 files changed, 650 insertions(+) create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 default.nix create mode 100644 depends.txt create mode 100644 include/asset_loader.h create mode 100644 include/sql.h create mode 160000 lib/blt-with-graphics create mode 100644 src/asset_loader.cpp create mode 100644 src/main.cpp create mode 100644 src/sql.cpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..70a51517 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/blt-with-graphics"] + path = lib/blt-with-graphics + url = https://git.tpgc.me/tri11paragon/BLT-With-Graphics-Template diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..6fd61023 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,82 @@ +cmake_minimum_required(VERSION 3.25) + +macro(sanitizers target_name) + if (${ENABLE_ADDRSAN} MATCHES ON) + target_compile_options(${target_name} PRIVATE -fsanitize=address) + target_link_options(${target_name} PRIVATE -fsanitize=address) + endif () + + if (${ENABLE_UBSAN} MATCHES ON) + target_compile_options(${target_name} PRIVATE -fsanitize=undefined) + target_link_options(${target_name} PRIVATE -fsanitize=undefined) + endif () + + if (${ENABLE_TSAN} MATCHES ON) + target_compile_options(${target_name} PRIVATE -fsanitize=thread) + target_link_options(${target_name} PRIVATE -fsanitize=thread) + endif () +endmacro() + +macro(compile_options target_name) + if (NOT ${MOLD} STREQUAL MOLD-NOTFOUND) + target_compile_options(${target_name} PUBLIC -fuse-ld=mold) + endif () + + target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment) + target_link_options(${target_name} PRIVATE -Wall -Wextra -Wpedantic -Wno-comment) + sanitizers(${target_name}) +endmacro() + +macro(blt_add_project name source type) + + project(${name}-${type}) + + add_executable(${name}-${type} ${source}) + + target_link_libraries(${name}-${type} PRIVATE BLT blt-gp Threads::Threads) + + compile_options(${name}-${type}) + target_compile_definitions(${name}-${type} PRIVATE BLT_DEBUG_LEVEL=${DEBUG_LEVEL}) + + if (${TRACK_ALLOCATIONS}) + target_compile_definitions(${name}-${type} PRIVATE BLT_TRACK_ALLOCATIONS=1) + endif () + + add_test(NAME ${name} COMMAND ${name}-${type}) + + set_property(TEST ${name} PROPERTY FAIL_REGULAR_EXPRESSION "FAIL;ERROR;FATAL;exception") + + project(minecraft-color-picker) +endmacro() + +project(minecraft-color-picker VERSION 0.0.1) + +option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) +option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) +option(ENABLE_TSAN "Enable the thread data race sanitizer" OFF) +option(BUILD_MINECRAFT_COLOR_PICKER_EXAMPLES "Build example programs. This will build with CTest" OFF) +option(BUILD_MINECRAFT_COLOR_PICKER_TESTS "Build test programs. This will build with CTest" OFF) + +set(CMAKE_CXX_STANDARD 17) + +add_subdirectory(lib/blt-with-graphics) + +find_package(SQLite3 REQUIRED) + +include_directories(include/) +file(GLOB_RECURSE PROJECT_BUILD_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") + +add_executable(minecraft-color-picker ${PROJECT_BUILD_FILES}) + +compile_options(minecraft-color-picker) + +target_link_libraries(minecraft-color-picker PRIVATE BLT_WITH_GRAPHICS SQLite::SQLite3) + + +if (${BUILD_MINECRAFT_COLOR_PICKER_EXAMPLES}) + +endif() + +if (BUILD_MINECRAFT_COLOR_PICKER_TESTS) + +endif() diff --git a/default.nix b/default.nix new file mode 100644 index 00000000..b1f3f0bc --- /dev/null +++ b/default.nix @@ -0,0 +1,56 @@ +{ pkgs ? (import { + config.allowUnfree = true; + config.segger-jlink.acceptLicense = true; +}), customPkgs ? (import /home/brett/my-nixpkgs { + config.allowUnfree = true; + config.segger-jlink.acceptLicense = true; +}), ... }: +pkgs.mkShell +{ + buildInputs = with pkgs; [ + cmake + gcc + clang + emscripten + ninja + customPkgs.jetbrains.clion + #clion = import ~/my-nixpkgs/pkgs/applications/editors/jetbrains {}; + renderdoc + valgrind + ]; + propagatedBuildInputs = with pkgs; [ + xorg.libX11 + xorg.libX11.dev + xorg.libXcursor + xorg.libXcursor.dev + xorg.libXext + xorg.libXext.dev + xorg.libXinerama + xorg.libXinerama.dev + xorg.libXrandr + xorg.libXrandr.dev + xorg.libXrender + xorg.libXrender.dev + xorg.libxcb + xorg.libxcb.dev + xorg.libXi + xorg.libXi.dev + harfbuzz + harfbuzz.dev + zlib + zlib.dev + bzip2 + bzip2.dev + pngpp + brotli + brotli.dev + pulseaudio.dev + git + libGL + libGL.dev + sqlite.dev + sqlite + glfw + ]; + LD_LIBRARY_PATH="/run/opengl-driver/lib:/run/opengl-driver-32/lib"; +} diff --git a/depends.txt b/depends.txt new file mode 100644 index 00000000..112bf8c2 --- /dev/null +++ b/depends.txt @@ -0,0 +1,2 @@ +sqlite3 +opengl diff --git a/include/asset_loader.h b/include/asset_loader.h new file mode 100644 index 00000000..f6744a62 --- /dev/null +++ b/include/asset_loader.h @@ -0,0 +1,22 @@ +#pragma once +/* + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ASSET_LOADER_H +#define ASSET_LOADER_H + +#endif //ASSET_LOADER_H diff --git a/include/sql.h b/include/sql.h new file mode 100644 index 00000000..7a398c9b --- /dev/null +++ b/include/sql.h @@ -0,0 +1,358 @@ +#pragma once +/* + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SQL_H +#define SQL_H + +#include +#include +#include +#include +#include +#include + +namespace detail +{ + template + std::string_view sql_name() + { + using Decay = std::decay_t + if constexpr (std::is_same_v) + return "NULL"; + else if constexpr (std::is_integral_v) + return "INTEGER"; + else if constexpr (std::is_floating_point_v) + return "REAL"; + else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) + return "TEXT"; + else if constexpr (std::is_same_v) + return "BOOLEAN"; + else + return "BLOB"; + } +} + +class column_t +{ +public: + explicit column_t(sqlite3_stmt* statement): statement{statement} + {} + + template + auto get(const int col) const -> decltype(auto) + { + using Decay = std::decay_t; + if constexpr (std::is_integral_v) + { + if constexpr (sizeof(Decay) == 8) + { + return sqlite3_column_int64(statement, col); + } else + { + return sqlite3_column_int(statement, col); + } + } else if constexpr (std::is_same_v) + { + return sqlite3_column_double(statement, col); + } else if constexpr (std::is_same_v) + { + return static_cast(sqlite3_column_double(statement, col)); + } else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) + { + return T(sqlite3_column_text(statement, col)); + } else + { + return static_cast(sqlite3_column_blob(statement, col)); + } + } + + template + [[nodiscard]] std::tuple get() const + { + return get_internal(std::index_sequence_for()); + } + + [[nodiscard]] size_t size(const int col) const + { + return static_cast(sqlite3_column_bytes(statement, col)); + } + +private: + template + [[nodiscard]] std::tuple get_internal(std::index_sequence) const + { + return std::tuple{get(Indices)...}; + } + + sqlite3_stmt* statement; +}; + +class column_binder_t +{ +public: + explicit column_binder_t(sqlite3_stmt* statement): statement{statement} + {} + + template + auto bind(T&& type, int col) + { + static_assert(!std::is_rvalue_reference_v, "Lifetime of object must outlive its usage!"); + using Decay = std::decay; + if constexpr (std::is_same_v || std::is_integral_v) + { + if constexpr (sizeof(Decay) == 8) + return sqlite3_bind_int64(statement, col, type); + else + return sqlite3_bind_int(statement, col, type); + } else if constexpr (std::is_floating_point_v) + { + return sqlite3_bind_double(statement, col, type); + } else if constexpr (std::is_same_v) + { + return sqlite3_bind_null(statement, col); + } else if constexpr (std::is_same_v || std::is_same_v) + { + return sqlite3_bind_text(statement, col, type.data(), type.size(), nullptr); + } else + { + return sqlite3_bind_blob64(statement, col, type.data(), type.size()); + } + } + +private: + sqlite3_stmt* statement; +}; + +class binder_t +{ +public: + explicit binder_t(sqlite3_stmt* statement): statement(statement) + {} + + /** + * Indexes start at 1 for the left most template parameter + */ + template + auto bind(T&& type, int col) + { + return column_binder_t{statement}.bind(std::forward(type), col); + } + + template + auto bind(T&& type, const std::string& name) + { + auto index = sqlite3_bind_parameter_index(statement, name.c_str()); + return column_binder_t{statement}.bind(std::forward(type), index); + } + + template + auto bind(Types&& types) + { + return bind_internal(std::index_sequence_for(), std::forward(types)...); + } + +private: + template + auto bind_internal(std::index_sequence, Types&& types) + { + return ((bind(Indices, std::forward(types)) != SQLITE_OK) || ...); + } + + sqlite3_stmt* statement; +}; + +class statement_t +{ +public: + explicit statement_t(sqlite3* db, const std::string& stmt); + + statement_t(const statement_t& copy) = delete; + + statement_t(statement_t& move) noexcept: statement{std::exchange(move.statement, nullptr)}, db{move.db} + {} + + statement_t& operator=(const statement_t&) = delete; + + statement_t& operator=(statement_t&& move) noexcept + { + statement = std::exchange(move.statement, statement); + db = std::exchange(move.db, db); + return *this; + } + + binder_t bind() const + { + sqlite3_reset(statement); + return binder_t{statement}; + } + + [[nodiscard]] bool execute() const; + + [[nodiscard]] column_t fetch() const + { + return column_t{statement}; + } + + ~statement_t(); + +private: + sqlite3_stmt* statement = nullptr; + sqlite3* db; +}; + +class table_builder_t; + +class table_column_builder_t +{ +public: + table_column_builder_t(table_builder_t& parent, std::vector& columns, std::string type, std::string name): parent{parent}, + columns{columns}, + type{std::move(type)}, + name{std::move(name)} + {} + + table_column_builder_t& primary_key() + { + attributes.emplace_back("PRIMARY KEY"); + return *this; + } + + table_column_builder_t& unique() + { + attributes.emplace_back("UNIQUE"); + return *this; + } + + table_column_builder_t& with_default(const std::string& value) + { + if (type == "TEXT") + attributes.push_back("DEFAULT \"" + value + '"'); + else + attributes.push_back("DEFAULT " + value); + return *this; + } + + table_column_builder_t& not_null() + { + attributes.emplace_back("NOT NULL"); + return *this; + } + + table_builder_t& finish() + { + if (!created) + { + columns.push_back(name + ' ' + type + ' ' + join()); + created = true; + } + return parent; + } + + ~table_column_builder_t() + { + finish(); + } + +private: + [[nodiscard]] std::string join() const + { + std::string out; + for (const auto& str : attributes) + { + out += str; + out += ' '; + } + return out; + } + + bool created = false; + table_builder_t& parent + std::vector& columns; + std::vector attributes; + std::string type; + std::string name; +}; + +class table_builder_t +{ +public: + explicit table_builder_t(sqlite3* db, std::string name): db{db}, name{std::move(name)} + {} + + template + table_column_builder_t& with_type(const std::string& name) + { + return table_column_builder_t{*this, columns, detail::sql_name(), name}; + } + +private: + std::vector columns{}; + sqlite3* db; + std::string name{}; +}; + +class statement_builder_t +{ +public: + explicit statement_builder_t(sqlite3* db): db{db} + {} + + table_builder_t create_table(const std::string& name) + { + return table_builder_t{db, name}; + } + +private: + sqlite3* db; +}; + +class database_t +{ +public: + explicit database_t(const std::string& file); + + database_t(const database_t& copy) = delete; + + database_t(database_t& move) noexcept: db{std::exchange(move.db, nullptr)} + {} + + database_t& operator=(const database_t&) = delete; + + database_t& operator=(database_t&& move) noexcept + { + db = std::exchange(move.db, db); + return *this; + } + + [[nodiscard]] statement_t prepare(const std::string& stmt) const + { + return statement_t{db, stmt}; + } + + [[nodiscard]] statement_builder_t builder() const + { + return statement_builder_t{db}; + } + + ~database_t(); + +private: + sqlite3* db = nullptr; +}; + +#endif //SQL_H diff --git a/lib/blt-with-graphics b/lib/blt-with-graphics new file mode 160000 index 00000000..dd3a242b --- /dev/null +++ b/lib/blt-with-graphics @@ -0,0 +1 @@ +Subproject commit dd3a242b4110552f9a9b9252d9abd28093e66d22 diff --git a/src/asset_loader.cpp b/src/asset_loader.cpp new file mode 100644 index 00000000..cc6393bb --- /dev/null +++ b/src/asset_loader.cpp @@ -0,0 +1,18 @@ +/* + * + * Copyright (C) 2025 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 00000000..19c4eb49 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,60 @@ +/* +* Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "blt/gfx/renderer/resource_manager.h" +#include "blt/gfx/renderer/batch_2d_renderer.h" +#include "blt/gfx/renderer/camera.h" +#include + +blt::gfx::matrix_state_manager global_matrices; +blt::gfx::resource_manager resources; +blt::gfx::batch_renderer_2d renderer_2d(resources, global_matrices); +blt::gfx::first_person_camera camera; + +void init(const blt::gfx::window_data&) +{ + using namespace blt::gfx; + + + global_matrices.create_internals(); + resources.load_resources(); + renderer_2d.create(); +} + +void update(const blt::gfx::window_data& data) +{ + global_matrices.update_perspectives(data.width, data.height, 90, 0.1, 2000); + + camera.update(); + camera.update_view(global_matrices); + global_matrices.update(); + + renderer_2d.render(data.width, data.height); +} + +void destroy(const blt::gfx::window_data&) +{ + global_matrices.cleanup(); + resources.cleanup(); + renderer_2d.cleanup(); + blt::gfx::cleanup(); +} + +int main() +{ + blt::gfx::init(blt::gfx::window_data{"Minecraft Color Picker", init, update, destroy}.setSyncInterval(1)); +} \ No newline at end of file diff --git a/src/sql.cpp b/src/sql.cpp new file mode 100644 index 00000000..7084299e --- /dev/null +++ b/src/sql.cpp @@ -0,0 +1,48 @@ +/* + * + * Copyright (C) 2025 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include + +statement_t::statement_t(sqlite3* db, const std::string& stmt): db{db} +{ + if (sqlite3_prepare_v2(db, stmt.c_str(), stmt.size(), &statement, nullptr) != SQLITE_OK) + BLT_ERROR("Failed to create statement object '{}' cause '{}'", stmt, sqlite3_errmsg(db)); +} + +bool statement_t::execute() const +{ + return sqlite3_step(statement) == SQLITE_OK; +} + +statement_t::~statement_t() +{ + sqlite3_finalize(statement); +} + +database_t::database_t(const std::string& file) +{ + if (sqlite3_open(file.c_str(), &db) != SQLITE_OK) + BLT_ERROR("Failed to open database '{}' got error message '{}'.", file, sqlite3_errmsg(db)); + else + BLT_DEBUG("Opened database '{}' successfully.", file); +} + +database_t::~database_t() +{ + sqlite3_close(db); +}