color picker start

main
Brett 2025-07-09 21:26:30 -04:00
commit af2295963e
10 changed files with 650 additions and 0 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "lib/blt-with-graphics"]
path = lib/blt-with-graphics
url = https://git.tpgc.me/tri11paragon/BLT-With-Graphics-Template

82
CMakeLists.txt Normal file
View File

@ -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()

56
default.nix Normal file
View File

@ -0,0 +1,56 @@
{ pkgs ? (import <nixpkgs> {
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";
}

2
depends.txt Normal file
View File

@ -0,0 +1,2 @@
sqlite3
opengl

22
include/asset_loader.h Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#ifndef ASSET_LOADER_H
#define ASSET_LOADER_H
#endif //ASSET_LOADER_H

358
include/sql.h Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#ifndef SQL_H
#define SQL_H
#include <sqlite3.h>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <blt/std/utility.h>
namespace detail
{
template <typename T>
std::string_view sql_name()
{
using Decay = std::decay_t<T>
if constexpr (std::is_same_v<Decay, nullptr_t>)
return "NULL";
else if constexpr (std::is_integral_v<Decay>)
return "INTEGER";
else if constexpr (std::is_floating_point_v<Decay>)
return "REAL";
else if constexpr (std::is_same_v<Decay, std::string> || std::is_same_v<Decay, std::string_view> || std::is_same_v<Decay, char*>)
return "TEXT";
else if constexpr (std::is_same_v<Decay, bool>)
return "BOOLEAN";
else
return "BLOB";
}
}
class column_t
{
public:
explicit column_t(sqlite3_stmt* statement): statement{statement}
{}
template <typename T>
auto get(const int col) const -> decltype(auto)
{
using Decay = std::decay_t<T>;
if constexpr (std::is_integral_v<Decay>)
{
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<Decay, double>)
{
return sqlite3_column_double(statement, col);
} else if constexpr (std::is_same_v<Decay, float>)
{
return static_cast<float>(sqlite3_column_double(statement, col));
} else if constexpr (std::is_same_v<Decay, std::string> || std::is_same_v<Decay, std::string_view> || std::is_same_v<Decay, char*>)
{
return T(sqlite3_column_text(statement, col));
} else
{
return static_cast<T>(sqlite3_column_blob(statement, col));
}
}
template <typename... Types>
[[nodiscard]] std::tuple<Types...> get() const
{
return get_internal<Types...>(std::index_sequence_for<Types...>());
}
[[nodiscard]] size_t size(const int col) const
{
return static_cast<size_t>(sqlite3_column_bytes(statement, col));
}
private:
template <typename... Types, size_t... Indices>
[[nodiscard]] std::tuple<Types...> get_internal(std::index_sequence<Indices>) const
{
return std::tuple<Types...>{get<Types>(Indices)...};
}
sqlite3_stmt* statement;
};
class column_binder_t
{
public:
explicit column_binder_t(sqlite3_stmt* statement): statement{statement}
{}
template <typename T>
auto bind(T&& type, int col)
{
static_assert(!std::is_rvalue_reference_v<T>, "Lifetime of object must outlive its usage!");
using Decay = std::decay<T>;
if constexpr (std::is_same_v<Decay, bool> || std::is_integral_v<Decay>)
{
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<Decay>)
{
return sqlite3_bind_double(statement, col, type);
} else if constexpr (std::is_same_v<Decay, nullptr_t>)
{
return sqlite3_bind_null(statement, col);
} else if constexpr (std::is_same_v<Decay, std::string> || std::is_same_v<Decay, std::string_view>)
{
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 <typename T>
auto bind(T&& type, int col)
{
return column_binder_t{statement}.bind(std::forward<T>(type), col);
}
template <typename T>
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<T>(type), index);
}
template <typename... Types>
auto bind(Types&& types)
{
return bind_internal<Types...>(std::index_sequence_for<Types...>(), std::forward<Types>(types)...);
}
private:
template <typename... Types, size_t Indices>
auto bind_internal(std::index_sequence<Indices...>, Types&& types)
{
return ((bind<Types>(Indices, std::forward<Types>(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<std::string>& 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<std::string>& columns;
std::vector<std::string> 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<typename T>
table_column_builder_t& with_type(const std::string& name)
{
return table_column_builder_t{*this, columns, detail::sql_name<T>(), name};
}
private:
std::vector<std::string> 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

1
lib/blt-with-graphics Submodule

@ -0,0 +1 @@
Subproject commit dd3a242b4110552f9a9b9252d9abd28093e66d22

18
src/asset_loader.cpp Normal file
View File

@ -0,0 +1,18 @@
/*
* <Short Description>
* 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 <https://www.gnu.org/licenses/>.
*/
#include <asset_loader.h>

60
src/main.cpp Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#include <blt/gfx/window.h>
#include "blt/gfx/renderer/resource_manager.h"
#include "blt/gfx/renderer/batch_2d_renderer.h"
#include "blt/gfx/renderer/camera.h"
#include <imgui.h>
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));
}

48
src/sql.cpp Normal file
View File

@ -0,0 +1,48 @@
/*
* <Short Description>
* 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 <https://www.gnu.org/licenses/>.
*/
#include <sql.h>
#include <blt/logging/logging.h>
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);
}