This commit is contained in:
commit
a8d8b9b9ab
99
.clang-format
Normal file
99
.clang-format
Normal file
@ -0,0 +1,99 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Microsoft
|
||||
# Includes
|
||||
SortIncludes: CaseInsensitive
|
||||
IncludeCategories:
|
||||
# Our OpenGL loader must come before GLFW
|
||||
- Regex: 'glad\/gl\.h'
|
||||
Priority: 1
|
||||
CaseSensitive: false
|
||||
- Regex: 'GLFW\/glfw3\.h'
|
||||
Priority: 2
|
||||
CaseSensitive: false
|
||||
# Windows.h comes first to avoid some wonky redefinitions
|
||||
- Regex: "Windows.h"
|
||||
Priority: 10000
|
||||
CaseSensitive: false
|
||||
# Alignment
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: Right
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: true
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: true
|
||||
AlignCompound: true
|
||||
AlignFunctionPointers: true
|
||||
PadOperators: true
|
||||
AlignConsecutiveDeclarations: true
|
||||
AlignConsecutiveMacros: true
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: AlignAfterOperator
|
||||
AlignTrailingComments: Always
|
||||
# Single-line blocks
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
#AllowShortEnumsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLambdasOnASingleLine: Empty
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
# ???
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
# ???
|
||||
BitFieldColonSpacing: Both
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: true
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: true
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: true
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
# Line breaks
|
||||
BreakTemplateDeclarations: Yes
|
||||
BreakAfterAttributes: Always
|
||||
BreakBeforeConceptDeclarations: Allowed
|
||||
BreakBeforeInlineASMColon: OnlyMultiline
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakStringLiterals: true
|
||||
PackConstructorInitializers: NextLineOnly # Could change to CurrentLine or NextLine
|
||||
# idk
|
||||
ColumnLimit: 120
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
InsertNewlineAtEOF: true
|
||||
# KeepEmptyLines:
|
||||
# AtEndOfFile: false
|
||||
# AtStartOfBlock: false
|
||||
# AtStartOfFile: false
|
||||
LineEnding: DeriveLF
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: Inner
|
||||
PointerAlignment: Left
|
||||
QualifierAlignment: Left
|
||||
# ReflowComments: true
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
2
.clang-tidy
Normal file
2
.clang-tidy
Normal file
@ -0,0 +1,2 @@
|
||||
Checks: '-*,cppcoreguidelines-*,performance-*,bugprone-*,clang-diagnostic-*,clang-analyzer-*'
|
||||
WarningsAsErrors: ''
|
25
.gitea/workflows/build-linux.yml
Normal file
25
.gitea/workflows/build-linux.yml
Normal file
@ -0,0 +1,25 @@
|
||||
name: Build (Arch Linux)
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
CFLAGS: -static -static-libgcc -static-libstdc++
|
||||
CXXFLAGS: -static -static-libgcc -static-libstdc++
|
||||
CC: /usr/bin/gcc
|
||||
CXX: /usr/bin/g++
|
||||
AS: /usr/bin/as
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: archlinux-latest
|
||||
steps:
|
||||
- name: Install prerequisite packages
|
||||
run: pacman -Syu --noconfirm && pacman -S --noconfirm git base-devel cmake ninja rustup nodejs mesa wayland libx11 libxkbcommon libxrandr libxinerama libxcursor libxi
|
||||
- name: Install bin2hpp
|
||||
run: rustup install stable && cargo install --locked --profile release --root /usr/local/ --git https://github.com/twokilohertz/bin2hpp.git --branch master
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{ gitea.workspace }}/build -G Ninja -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_C_COMPILER=${{ env.CC }} -DCMAKE_CXX_COMPILER=${{ env.CXX }} -DBUILD_SHARED_LIBS=OFF
|
||||
- name: Build
|
||||
run: cd ${{ gitea.workspace }}/build && ninja
|
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
|
||||
# IntelliJ IDEs
|
||||
.idea/
|
||||
|
||||
# CMake
|
||||
build/
|
||||
target/
|
||||
out/
|
||||
|
||||
# clangd
|
||||
.cache/
|
||||
|
||||
# Build artefacts
|
||||
*.exe
|
||||
*.dll
|
||||
*.lib
|
||||
*.pdb
|
||||
*.dylib
|
||||
*.so
|
||||
*.o
|
||||
*.a
|
||||
|
||||
# Binaries
|
||||
*.bin
|
||||
|
||||
# Miscellaneous stuff
|
||||
.directory
|
||||
.DS_Store
|
||||
desktop.ini
|
||||
Thumbs.db
|
205
CMakeLists.txt
Normal file
205
CMakeLists.txt
Normal file
@ -0,0 +1,205 @@
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
|
||||
project(
|
||||
kuiper-engine
|
||||
VERSION 0.1
|
||||
DESCRIPTION "Kuiper Engine"
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
add_library(
|
||||
kuiper
|
||||
STATIC
|
||||
src/cli/cli.cpp
|
||||
src/core/engine.cpp
|
||||
src/core/io.cpp
|
||||
src/core/entrypoint.cpp
|
||||
src/graphics/opengl/buffer.cpp
|
||||
src/graphics/opengl/framebuffer.cpp
|
||||
src/graphics/opengl/texture.cpp
|
||||
src/graphics/opengl/texture_array.cpp
|
||||
src/graphics/opengl/shader.cpp
|
||||
src/graphics/opengl/program.cpp
|
||||
src/graphics/opengl/render_context.cpp
|
||||
src/graphics/opengl/vertex_array.cpp
|
||||
src/resources/image.cpp
|
||||
src/resources/model.cpp
|
||||
src/resources/resource_index.cpp
|
||||
src/resources/resource_manager.cpp
|
||||
src/platform/glfw/glfw_window.cpp
|
||||
src/renderer/gl_renderer.cpp
|
||||
src/window/input_system.cpp
|
||||
)
|
||||
target_compile_features(kuiper PRIVATE cxx_std_23)
|
||||
target_include_directories(kuiper PUBLIC "include/")
|
||||
|
||||
# Compiler options
|
||||
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
||||
# GCC & Clang
|
||||
message(STATUS "Adding GCC & Clang-specific compiler options")
|
||||
if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
||||
target_compile_options(kuiper PRIVATE -Wall)
|
||||
elseif (${CMAKE_BUILD_TYPE} STREQUAL "Release")
|
||||
target_compile_options(kuiper PRIVATE -O3)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Binary resource inclusion because std::embed still isn't here
|
||||
# TODO: do this in a loop: https://cmake.org/cmake/help/book/mastering-cmake/chapter/Custom%20Commands.html#adding-a-custom-target
|
||||
|
||||
find_program(
|
||||
BIN2HPP_FOUND
|
||||
bin2hpp
|
||||
REQUIRED
|
||||
)
|
||||
|
||||
set(EMBEDDED_SHADERS_DIR ${CMAKE_CURRENT_BINARY_DIR}/bin2hpp)
|
||||
file(MAKE_DIRECTORY ${EMBEDDED_SHADERS_DIR})
|
||||
|
||||
# Vertex shader
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bin2hpp/default_vs.hpp
|
||||
COMMAND bin2hpp -i "${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/geometry.vert" -o "${EMBEDDED_SHADERS_DIR}/default_vs.hpp" -n bin -s default_vs
|
||||
)
|
||||
|
||||
# Fragment shader
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bin2hpp/default_fs.hpp
|
||||
COMMAND bin2hpp -i "${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/pbr.frag" -o "${EMBEDDED_SHADERS_DIR}/default_fs.hpp" -n bin -s default_fs
|
||||
)
|
||||
|
||||
# Clustering computer shader
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bin2hpp/clustering_cs.hpp
|
||||
COMMAND bin2hpp -i "${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/build_clusters.comp" -o "${EMBEDDED_SHADERS_DIR}/clustering_cs.hpp" -n bin -s clustering_cs
|
||||
)
|
||||
|
||||
# Light-culling computer shader
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bin2hpp/light_culling_cs.hpp
|
||||
COMMAND bin2hpp -i "${CMAKE_CURRENT_SOURCE_DIR}/assets/shaders/cull_lights.comp" -o "${EMBEDDED_SHADERS_DIR}/light_culling_cs.hpp" -n bin -s light_culling_cs
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
embedded-shaders
|
||||
ALL
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/bin2hpp/default_vs.hpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/bin2hpp/default_fs.hpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/bin2hpp/clustering_cs.hpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/bin2hpp/light_culling_cs.hpp
|
||||
)
|
||||
add_dependencies(kuiper embedded-shaders)
|
||||
|
||||
target_include_directories(kuiper PUBLIC ${EMBEDDED_SHADERS_DIR})
|
||||
|
||||
# Dependencies
|
||||
|
||||
include(FetchContent)
|
||||
target_link_libraries(kuiper PUBLIC glad glfw glm::glm spdlog imgui stb freetype fastgltf flecs::flecs_static)
|
||||
target_link_libraries(kuiper PRIVATE absl::flat_hash_map)
|
||||
|
||||
# GLAD
|
||||
add_subdirectory("thirdparty/glad/")
|
||||
|
||||
# GLFW
|
||||
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
|
||||
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
|
||||
FetchContent_Declare(
|
||||
glfw
|
||||
GIT_REPOSITORY https://github.com/glfw/glfw.git
|
||||
GIT_TAG 3.4
|
||||
OVERRIDE_FIND_PACKAGE # For use with find_package()
|
||||
)
|
||||
FetchContent_MakeAvailable(glfw)
|
||||
# find_package(glfw3 3.4 REQUIRED)
|
||||
|
||||
# OpenGL Mathematics (GLM)
|
||||
FetchContent_Declare(
|
||||
glm
|
||||
GIT_REPOSITORY https://github.com/g-truc/glm.git
|
||||
GIT_TAG 1.0.1
|
||||
)
|
||||
FetchContent_MakeAvailable(glm)
|
||||
|
||||
# spdlog
|
||||
set(SPDLOG_USE_STD_FORMAT ON CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_BUILD_EXAMPLE OFF CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_BUILD_EXAMPLE_HO OFF CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_BUILD_TESTS_HO OFF CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_BUILD_BENCH OFF CACHE BOOL "" FORCE)
|
||||
set(SPDLOG_DISABLE_DEFAULT_LOGGER ON CACHE BOOL "" FORCE)
|
||||
|
||||
FetchContent_Declare(
|
||||
spdlog
|
||||
GIT_REPOSITORY https://github.com/gabime/spdlog.git
|
||||
GIT_TAG v1.14.1
|
||||
)
|
||||
FetchContent_MakeAvailable(spdlog)
|
||||
|
||||
# UTF8-CPP
|
||||
target_include_directories(kuiper PRIVATE "thirdparty/utfcpp/include/")
|
||||
|
||||
# FreeType
|
||||
set(FT_DISABLE_BROTLI ON CACHE BOOL "" FORCE)
|
||||
set(FT_DISABLE_BZIP2 ON CACHE BOOL "" FORCE)
|
||||
set(FT_DISABLE_HARFBUZZ ON CACHE BOOL "" FORCE)
|
||||
set(FT_DISABLE_PNG ON CACHE BOOL "" FORCE)
|
||||
set(FT_DISABLE_ZLIB ON CACHE BOOL "" FORCE)
|
||||
set(FT_ENABLE_ERROR_STRINGS OFF CACHE BOOL "" FORCE)
|
||||
|
||||
FetchContent_Declare(
|
||||
freetype
|
||||
URL https://download.savannah.gnu.org/releases/freetype/freetype-2.13.3.tar.gz
|
||||
URL_HASH SHA256=5c3a8e78f7b24c20b25b54ee575d6daa40007a5f4eea2845861c3409b3021747
|
||||
OVERRIDE_FIND_PACKAGE
|
||||
)
|
||||
FetchContent_MakeAvailable(freetype)
|
||||
|
||||
# simdjson (note: also a dependency of fastgltf)
|
||||
set(SIMDJSON_DEVELOPER_MODE OFF CACHE BOOL "" FORCE)
|
||||
set(SIMDJSON_DISABLE_DEPRECATED_API ON CACHE BOOL "" FORCE)
|
||||
|
||||
FetchContent_Declare(
|
||||
simdjson
|
||||
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
|
||||
GIT_TAG v3.11.3
|
||||
OVERRIDE_FIND_PACKAGE # So fastgltf can find it
|
||||
)
|
||||
FetchContent_MakeAvailable(simdjson)
|
||||
|
||||
# fastgltf
|
||||
set(FASTGLTF_COMPILE_AS_CPP20 ON CACHE BOOL "" FORCE)
|
||||
|
||||
FetchContent_Declare(
|
||||
fastgltf
|
||||
GIT_REPOSITORY https://github.com/spnda/fastgltf.git
|
||||
GIT_TAG v0.8.0
|
||||
)
|
||||
FetchContent_MakeAvailable(fastgltf)
|
||||
|
||||
# flecs
|
||||
set(FLECS_SHARED OFF CACHE BOOL "" FORCE)
|
||||
|
||||
FetchContent_Declare(
|
||||
flecs
|
||||
GIT_REPOSITORY https://github.com/SanderMertens/flecs.git
|
||||
GIT_TAG v4.0.4
|
||||
)
|
||||
FetchContent_MakeAvailable(flecs)
|
||||
|
||||
# Abseil C++
|
||||
FetchContent_Declare(
|
||||
abseil
|
||||
GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git
|
||||
GIT_TAG 20250127.1
|
||||
)
|
||||
FetchContent_MakeAvailable(abseil)
|
||||
|
||||
# Dear ImGui
|
||||
add_subdirectory("thirdparty/imgui/")
|
||||
|
||||
# STB
|
||||
add_subdirectory("thirdparty/stb/")
|
13
README.md
Normal file
13
README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Kuiper Engine
|
||||
|
||||
Experimental rendering engine written in C++ using OpenGL
|
||||
|
||||
## Minimum System Requirements
|
||||
|
||||
- A C++23 compiler (& corresponding standard library)
|
||||
- OpenGL hardware & driver supporting OpenGL 4.6
|
||||
|
||||
### Windows-specific
|
||||
|
||||
- _NOTE: Not yet supported! I **hate** wchar_t strings!_
|
||||
- **Windows 10 1903** or later, for native APIs returning UTF-8
|
26
TODO.md
Normal file
26
TODO.md
Normal file
@ -0,0 +1,26 @@
|
||||
# The Big To-do List
|
||||
|
||||
- OpenGL backend
|
||||
- DSA for all abstractions
|
||||
- Dedicated OpenGL (& render?) thread with message queue
|
||||
- Renderer
|
||||
- Skybox
|
||||
- Shadow maps
|
||||
- Directional lights
|
||||
- Light radius derived from its power / distance^2
|
||||
- Compute shader frustum culling
|
||||
- Notion of layers
|
||||
- Viewmodel
|
||||
- UI
|
||||
- Text
|
||||
- Audio
|
||||
- Loading audio
|
||||
- Playing audio
|
||||
- Audio mixer/queue
|
||||
- Networking
|
||||
- Game simulation thread runs as part of the "server"
|
||||
- Local, LAN & WAN
|
||||
- Game
|
||||
- Collision
|
||||
- Editor
|
||||
- Model previews
|
80
assets/shaders/build_clusters.comp
Normal file
80
assets/shaders/build_clusters.comp
Normal file
@ -0,0 +1,80 @@
|
||||
#version 460 core
|
||||
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
const uint MAX_NUM_LIGHTS = 100u;
|
||||
|
||||
struct cluster {
|
||||
vec4 min_point;
|
||||
vec4 max_point;
|
||||
uint count;
|
||||
uint light_indices[MAX_NUM_LIGHTS];
|
||||
};
|
||||
|
||||
layout(std430, binding = 1) restrict buffer cluster_buf {
|
||||
cluster clusters[];
|
||||
};
|
||||
|
||||
uniform float z_near;
|
||||
uniform float z_far;
|
||||
uniform mat4 inverse_proj_mat;
|
||||
uniform uvec3 grid_size;
|
||||
uniform uvec2 screen_dimensions;
|
||||
|
||||
vec3 screen_to_view(vec2 screen_coord);
|
||||
vec3 line_intersection_with_z_plane(vec3 start_point, vec3 end_point, float z_distance);
|
||||
|
||||
/*
|
||||
context: glViewport is referred to as the "screen"
|
||||
clusters are built based on a 2d screen-space grid and depth slices.
|
||||
Later when shading, it is easy to figure what cluster a fragment is in based on
|
||||
gl_FragCoord.xy and the fragment's z depth from camera
|
||||
*/
|
||||
void main() {
|
||||
uint tile_idx =
|
||||
gl_WorkGroupID.x + (gl_WorkGroupID.y * grid_size.x) + (gl_WorkGroupID.z * grid_size.x * grid_size.y);
|
||||
vec2 tile_size = screen_dimensions / grid_size.xy;
|
||||
|
||||
// tile in screen-space
|
||||
vec2 min_tile_screen = gl_WorkGroupID.xy * tile_size;
|
||||
vec2 max_tile_screen = (gl_WorkGroupID.xy + 1) * tile_size;
|
||||
|
||||
// convert tile to view space sitting on the near plane
|
||||
vec3 min_tile = screen_to_view(min_tile_screen);
|
||||
vec3 max_tile = screen_to_view(max_tile_screen);
|
||||
|
||||
float plane_near = z_near * pow(z_far / z_near, gl_WorkGroupID.z / float(grid_size.z));
|
||||
float plane_far = z_near * pow(z_far / z_near, (gl_WorkGroupID.z + 1) / float(grid_size.z));
|
||||
|
||||
// the line goes from the eye position in view space (0, 0, 0)
|
||||
// through the min/max points of a tile to intersect with a given cluster's near-far planes
|
||||
vec3 min_point_near = line_intersection_with_z_plane(vec3(0, 0, 0), min_tile, plane_near);
|
||||
vec3 min_point_far = line_intersection_with_z_plane(vec3(0, 0, 0), min_tile, plane_far);
|
||||
vec3 max_point_near = line_intersection_with_z_plane(vec3(0, 0, 0), max_tile, plane_near);
|
||||
vec3 max_point_far = line_intersection_with_z_plane(vec3(0, 0, 0), max_tile, plane_far);
|
||||
|
||||
clusters[tile_idx].min_point = vec4(min(min_point_near, min_point_far), 0.0);
|
||||
clusters[tile_idx].max_point = vec4(max(max_point_near, max_point_far), 0.0);
|
||||
}
|
||||
|
||||
// Returns the intersection point of an infinite line and a
|
||||
// plane perpendicular to the Z-axis
|
||||
vec3 line_intersection_with_z_plane(vec3 start_point, vec3 end_point, float z_distance) {
|
||||
vec3 direction = end_point - start_point;
|
||||
vec3 normal = vec3(0.0, 0.0, -1.0); // plane normal
|
||||
|
||||
// skip check if the line is parallel to the plane.
|
||||
|
||||
float t = (z_distance - dot(normal, start_point)) / dot(normal, direction);
|
||||
return start_point + t * direction; // the parametric form of the line equation
|
||||
}
|
||||
|
||||
vec3 screen_to_view(vec2 screen_coord) {
|
||||
// normalize screen_coord to [-1, 1] and
|
||||
// set the NDC depth of the coordinate to be on the near plane. This is -1 by
|
||||
// default in OpenGL
|
||||
vec4 ndc = vec4(screen_coord / screen_dimensions * 2.0 - 1.0, -1.0, 1.0);
|
||||
|
||||
vec4 view_coord = inverse_proj_mat * ndc;
|
||||
view_coord /= view_coord.w;
|
||||
return view_coord.xyz;
|
||||
}
|
73
assets/shaders/cull_lights.comp
Normal file
73
assets/shaders/cull_lights.comp
Normal file
@ -0,0 +1,73 @@
|
||||
#version 460 core
|
||||
|
||||
const uint LOCAL_SIZE = 128u;
|
||||
|
||||
layout(local_size_x = LOCAL_SIZE, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
struct point_light {
|
||||
vec4 position;
|
||||
vec4 colour;
|
||||
float intensity;
|
||||
float radius;
|
||||
};
|
||||
|
||||
const uint MAX_NUM_LIGHTS = 100u;
|
||||
|
||||
struct cluster {
|
||||
vec4 min_point;
|
||||
vec4 max_point;
|
||||
uint count;
|
||||
uint light_indices[MAX_NUM_LIGHTS];
|
||||
};
|
||||
|
||||
layout(std430, binding = 1) restrict buffer cluster_buf {
|
||||
cluster clusters[];
|
||||
};
|
||||
|
||||
layout(std430, binding = 2) restrict buffer lights_buf {
|
||||
point_light point_lights[];
|
||||
};
|
||||
|
||||
uniform mat4 view_mat;
|
||||
|
||||
bool test_sphere_aabb(uint i, cluster c);
|
||||
|
||||
// each invocation of main() is a thread processing a cluster
|
||||
void main() {
|
||||
uint n_lights = point_lights.length();
|
||||
uint cluster_idx = gl_WorkGroupID.x * LOCAL_SIZE + gl_LocalInvocationID.x;
|
||||
cluster c = clusters[cluster_idx];
|
||||
|
||||
// we need to reset count because culling runs every frame.
|
||||
// otherwise it would accumulate.
|
||||
c.count = 0;
|
||||
|
||||
for (uint i = 0; i < n_lights; ++i) {
|
||||
if (test_sphere_aabb(i, c) && c.count < 100) {
|
||||
c.light_indices[c.count] = i;
|
||||
c.count++;
|
||||
}
|
||||
}
|
||||
|
||||
clusters[cluster_idx] = c;
|
||||
}
|
||||
|
||||
bool sphere_aabb_intersection(vec3 center, float radius, vec3 aabb_min, vec3 aabb_max) {
|
||||
// closest point on the AABB to the sphere center
|
||||
vec3 closest_point = clamp(center, aabb_min, aabb_max);
|
||||
|
||||
// squared distance between the sphere center and closest point
|
||||
float distance_squared = dot(closest_point - center, closest_point - center);
|
||||
return distance_squared <= radius * radius;
|
||||
}
|
||||
|
||||
// this just unpacks data for sphere_aabb_intersection
|
||||
bool test_sphere_aabb(uint i, cluster c) {
|
||||
vec3 center = vec3(view_mat * point_lights[i].position);
|
||||
float radius = point_lights[i].radius;
|
||||
|
||||
vec3 aabb_min = c.min_point.xyz;
|
||||
vec3 aabb_max = c.max_point.xyz;
|
||||
|
||||
return sphere_aabb_intersection(center, radius, aabb_min, aabb_max);
|
||||
}
|
40
assets/shaders/geometry.vert
Normal file
40
assets/shaders/geometry.vert
Normal file
@ -0,0 +1,40 @@
|
||||
#version 460 core
|
||||
|
||||
layout(location = 0) in vec3 pos;
|
||||
layout(location = 1) in vec3 norm;
|
||||
layout(location = 2) in vec3 tangent;
|
||||
layout(location = 3) in vec2 uv;
|
||||
layout(location = 4) in uint draw_id;
|
||||
layout(location = 5) in mat4 model_mat; // also occupies location 5, 6, 7 & 8
|
||||
layout(location = 9) in mat3 normal_mat;
|
||||
|
||||
out VS_OUT {
|
||||
vec3 pos;
|
||||
vec3 tan_pos;
|
||||
vec2 uv;
|
||||
flat uint draw_id;
|
||||
mat3 TBN;
|
||||
}
|
||||
vs_out;
|
||||
|
||||
// Uniforms
|
||||
uniform mat4 view_proj_mat; // View-projection matrix
|
||||
|
||||
void main() {
|
||||
// Tangent-space matrix
|
||||
vec3 T = normalize(normal_mat * tangent);
|
||||
vec3 N = normalize(normal_mat * norm);
|
||||
T = normalize(T - dot(T, N) * N); // Re-orthogonalisation
|
||||
vec3 B = cross(N, T);
|
||||
mat3 TBN = transpose(mat3(T, B, N));
|
||||
|
||||
vec4 world_pos = model_mat * vec4(pos, 1.0);
|
||||
|
||||
vs_out.pos = vec3(world_pos) / world_pos.w; // World-space position
|
||||
vs_out.tan_pos = TBN * vs_out.pos; // Tangent-space position
|
||||
vs_out.uv = uv; // Texture coordinates
|
||||
vs_out.draw_id = draw_id; // MDI draw ID
|
||||
vs_out.TBN = TBN; // World-space -> tangent-space matrix
|
||||
|
||||
gl_Position = view_proj_mat * world_pos;
|
||||
}
|
179
assets/shaders/pbr.frag
Normal file
179
assets/shaders/pbr.frag
Normal file
@ -0,0 +1,179 @@
|
||||
#version 460 core
|
||||
|
||||
const uint MAX_FRAG_TEX_UNITS = 32u;
|
||||
const uint MAX_NUM_LIGHTS = 100u;
|
||||
const float PI = 3.14159265358979;
|
||||
|
||||
in VS_OUT {
|
||||
vec3 pos;
|
||||
vec3 tan_pos;
|
||||
vec2 uv;
|
||||
flat uint draw_id;
|
||||
mat3 TBN;
|
||||
}
|
||||
fs_in;
|
||||
|
||||
out vec4 out_colour;
|
||||
|
||||
struct point_light {
|
||||
vec4 position;
|
||||
vec4 colour;
|
||||
float intensity;
|
||||
float radius;
|
||||
};
|
||||
|
||||
struct cluster {
|
||||
vec4 min_point;
|
||||
vec4 max_point;
|
||||
uint count;
|
||||
uint light_indices[MAX_NUM_LIGHTS];
|
||||
};
|
||||
|
||||
layout(std430, binding = 1) readonly buffer cluster_buf {
|
||||
cluster clusters[];
|
||||
};
|
||||
|
||||
layout(std430, binding = 2) readonly buffer lights_buf {
|
||||
point_light point_lights[];
|
||||
};
|
||||
|
||||
uniform mat4 view_mat;
|
||||
uniform float z_near;
|
||||
uniform float z_far;
|
||||
uniform uvec3 grid_size;
|
||||
uniform uvec2 screen_dimensions;
|
||||
uniform vec3 view_pos; // Camera world position
|
||||
uniform sampler2DArray packed_tex[MAX_FRAG_TEX_UNITS]; // z index = 0: Albedo, 1: Normal, 2: Metallic-Roughness
|
||||
|
||||
vec3 get_normal();
|
||||
float trowbridge_reitz(vec3 normal, vec3 halfway, float roughness);
|
||||
float schlick(float n_dot_v, float roughness);
|
||||
float smith(vec3 normal, vec3 view, vec3 light, float roughness);
|
||||
vec3 fresnel_schlick(float cos_theta, vec3 F0);
|
||||
|
||||
void main() {
|
||||
vec3 albedo = texture(packed_tex[fs_in.draw_id % MAX_FRAG_TEX_UNITS], vec3(fs_in.uv, 0.0)).rgb;
|
||||
vec3 normal = get_normal();
|
||||
vec3 met_rough = texture(packed_tex[fs_in.draw_id % MAX_FRAG_TEX_UNITS], vec3(fs_in.uv, 2.0)).rgb;
|
||||
float metallic = met_rough.b;
|
||||
float roughness = met_rough.g;
|
||||
|
||||
vec3 tangent_frag_pos = fs_in.tan_pos;
|
||||
vec3 tangent_view_pos = fs_in.TBN * view_pos;
|
||||
vec3 view_dir = normalize(tangent_view_pos - tangent_frag_pos);
|
||||
|
||||
// Normal incidence reflectance
|
||||
vec3 F0 = mix(vec3(0.04), albedo, metallic);
|
||||
|
||||
// Find light cluster index
|
||||
|
||||
// Position of this fragment in view space
|
||||
vec3 view_space_pos = vec3(view_mat * vec4(fs_in.pos, 1.0));
|
||||
|
||||
// Locating which cluster this fragment is part of
|
||||
uint z_tile = uint((log(abs(view_space_pos.z) / z_near) * grid_size.z) / log(z_far / z_near));
|
||||
vec2 tile_size = screen_dimensions / grid_size.xy;
|
||||
uvec3 tile = uvec3(gl_FragCoord.xy / tile_size, z_tile);
|
||||
uint tile_idx = tile.x + (tile.y * grid_size.x) + (tile.z * grid_size.x * grid_size.y);
|
||||
|
||||
// Shade fragment for each light
|
||||
|
||||
uint n_lights = clusters[tile_idx].count;
|
||||
|
||||
vec3 light_reflected = vec3(0.0, 0.0, 0.0);
|
||||
|
||||
for (uint i = 0u; i < n_lights; ++i) {
|
||||
uint light_idx = clusters[tile_idx].light_indices[i];
|
||||
point_light light = point_lights[light_idx];
|
||||
|
||||
vec3 tangent_light_pos = fs_in.TBN * vec3(light.position);
|
||||
|
||||
// Per-light radiance
|
||||
vec3 light_dir = normalize(tangent_light_pos - tangent_frag_pos);
|
||||
vec3 halfway = normalize(view_dir + light_dir);
|
||||
float dist = length(tangent_light_pos - tangent_frag_pos);
|
||||
float attenuation = light.intensity / (dist * dist);
|
||||
vec3 radiance = vec3(light.colour) * attenuation;
|
||||
|
||||
// Cook-Torrance BRDF
|
||||
|
||||
float NDF = trowbridge_reitz(normal, halfway, roughness);
|
||||
float G = smith(normal, view_dir, light_dir, roughness);
|
||||
vec3 F = fresnel_schlick(clamp(dot(halfway, view_dir), 0.0, 1.0), F0);
|
||||
|
||||
vec3 numerator = NDF * G * F;
|
||||
float denominator = 4.0 * max(dot(normal, view_dir), 0.0) * max(dot(normal, light_dir), 0.0) + 0.0001; // + 0.0001 to prevent divide by zero
|
||||
vec3 specular = numerator / denominator;
|
||||
|
||||
// kS is equal to Fresnel
|
||||
vec3 kS = F;
|
||||
// for energy conservation, the diffuse and specular light can't
|
||||
// be above 1.0 (unless the surface emits light); to preserve this
|
||||
// relationship the diffuse component (kD) should equal 1.0 - kS.
|
||||
vec3 kD = vec3(1.0) - kS;
|
||||
// multiply kD by the inverse metalness such that only non-metals
|
||||
// have diffuse lighting, or a linear blend if partly metal (pure metals
|
||||
// have no diffuse light).
|
||||
kD *= 1.0 - metallic;
|
||||
|
||||
// scale light by NdotL
|
||||
float n_dot_l = max(dot(normal, light_dir), 0.0);
|
||||
|
||||
// add to outgoing radiance Lo
|
||||
light_reflected += (kD * albedo / PI + specular) * radiance * n_dot_l; // note that we already multiplied the BRDF by the Fresnel (kS) so we won't multiply by kS again
|
||||
}
|
||||
|
||||
// Don't need to gamma-correct as PNG textures are already gamma corrected (?)
|
||||
// const float screen_gamma = 2.2;
|
||||
// light_collect = pow(light_collect, vec3(1.0 / screen_gamma));
|
||||
|
||||
vec3 ambient = vec3(0.03) * albedo;
|
||||
|
||||
out_colour = vec4(ambient, 1.0) + vec4(light_reflected, 1.0);
|
||||
}
|
||||
|
||||
vec3 get_normal() {
|
||||
vec3 normal_map_sample = texture(packed_tex[fs_in.draw_id % MAX_FRAG_TEX_UNITS], vec3(fs_in.uv, 1.0)).rgb;
|
||||
// Transform normal vector to range [-1, 1]
|
||||
return normalize(normal_map_sample * 2.0 - 1.0); // Tangent-space
|
||||
}
|
||||
|
||||
vec3 cook_torrance(vec3 albedo) {
|
||||
return albedo;
|
||||
}
|
||||
|
||||
// Normal distribution function (a.k.a. GGX)
|
||||
float trowbridge_reitz(vec3 normal, vec3 halfway, float roughness) {
|
||||
float a_squared = roughness * roughness;
|
||||
float n_dot_h = max(dot(normal, halfway), 0.0); // how closely the normal & halfway align
|
||||
float n_dot_h_squared = n_dot_h * n_dot_h;
|
||||
|
||||
float nom = a_squared;
|
||||
float denom = (n_dot_h_squared * (a_squared - 1.0) + 1.0);
|
||||
denom = PI * denom * denom;
|
||||
|
||||
return nom / denom;
|
||||
}
|
||||
|
||||
// Geometry function (self-shadowing)
|
||||
float schlick(float n_dot_v, float roughness) {
|
||||
float nom = n_dot_v;
|
||||
float denom = n_dot_v * (1.0 - roughness) + roughness;
|
||||
|
||||
return nom / denom;
|
||||
}
|
||||
|
||||
// Geometry approximation using Schlick-GGX
|
||||
float smith(vec3 normal, vec3 view, vec3 light, float roughness) {
|
||||
float n_dot_v = max(dot(normal, view), 0.0);
|
||||
float n_dot_l = max(dot(normal, light), 0.0);
|
||||
float ggx_1 = schlick(n_dot_v, roughness);
|
||||
float ggx_2 = schlick(n_dot_l, roughness);
|
||||
|
||||
return ggx_1 * ggx_2;
|
||||
}
|
||||
|
||||
// Fresnel effect
|
||||
vec3 fresnel_schlick(float cos_theta, vec3 F0) {
|
||||
return F0 + (1.0 - F0) * pow(clamp(1.0 - cos_theta, 0.0, 1.0), 5.0);
|
||||
}
|
23
include/cli/cli.hpp
Normal file
23
include/cli/cli.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
|
||||
enum class cli_args_parse_err : std::uint8_t {
|
||||
not_enough_args,
|
||||
invalid_argument,
|
||||
};
|
||||
|
||||
class cli {
|
||||
public:
|
||||
static std::expected<cli, cli_args_parse_err> try_construct(int argc, char** argv);
|
||||
|
||||
static void print_usage();
|
||||
static std::string_view cli_args_parse_err_str(cli_args_parse_err err);
|
||||
|
||||
public:
|
||||
std::filesystem::path root_path {};
|
||||
bool prefer_x11 = false;
|
||||
};
|
20
include/components/camera.hpp
Normal file
20
include/components/camera.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/ext/scalar_constants.hpp> // glm::pi
|
||||
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace component
|
||||
{
|
||||
|
||||
struct camera_props {
|
||||
glm::vec3 euler {0.0f, -(glm::pi<float>() / 2.0f), (glm::pi<float>() / 2.0f)}; // looking down the -Z axis
|
||||
float fov = (glm::pi<float>() / 2.0f); // 90 degrees
|
||||
};
|
||||
|
||||
} // namespace component
|
||||
|
||||
} // namespace kuiper
|
22
include/components/lights.hpp
Normal file
22
include/components/lights.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace component
|
||||
{
|
||||
|
||||
struct directional_light {
|
||||
glm::vec3 direction {0.0f, -1.0f, 0.0f}; // Initial points straight down
|
||||
};
|
||||
|
||||
struct point_light {
|
||||
glm::vec3 colour {1.0f, 1.0f, 1.0f};
|
||||
float intensity {1.0f};
|
||||
};
|
||||
|
||||
} // namespace component
|
||||
|
||||
} // namespace kuiper
|
16
include/components/physics.hpp
Normal file
16
include/components/physics.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace component
|
||||
{
|
||||
|
||||
struct physics_props {
|
||||
float mass {0.0f};
|
||||
float velocity {0.0f};
|
||||
};
|
||||
|
||||
} // namespace component
|
||||
|
||||
} // namespace kuiper
|
17
include/components/resources.hpp
Normal file
17
include/components/resources.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "resources/resource_manager.hpp"
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace component
|
||||
{
|
||||
|
||||
struct model_resource {
|
||||
resource_id_t id {0};
|
||||
};
|
||||
|
||||
} // namespace component
|
||||
|
||||
} // namespace kuiper
|
152
include/containers/sparse_map.hpp
Normal file
152
include/containers/sparse_map.hpp
Normal file
@ -0,0 +1,152 @@
|
||||
#pragma once
|
||||
|
||||
#include "containers/sparse_set.hpp"
|
||||
|
||||
#include <cstddef> // std::size_t
|
||||
#include <format> // std::format
|
||||
#include <stdexcept> // std::out_of_range
|
||||
#include <utility> // std::get
|
||||
#include <vector> // values container
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace containers
|
||||
{
|
||||
|
||||
template<typename K, typename V>
|
||||
class sparse_map {
|
||||
public:
|
||||
using sparse_set_t = containers::sparse_set<K>;
|
||||
using values_container_t = std::vector<V>;
|
||||
|
||||
public:
|
||||
// Capacity
|
||||
|
||||
/// Reserve a number of slots in the underlying containers
|
||||
void reserve(std::size_t size) {
|
||||
if (size > m_capacity) {
|
||||
m_values.resize(size);
|
||||
m_capacity = size;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Access
|
||||
|
||||
/// Access value specified by key (non-const)
|
||||
V& at(const K& key) {
|
||||
if (!m_sparse_set.contains(key)) {
|
||||
throw std::out_of_range(std::format("key \"{}\" does not exist in the sparse map", key));
|
||||
}
|
||||
|
||||
const auto dense_idx = m_sparse_set.sparse()[key];
|
||||
return m_values[dense_idx];
|
||||
}
|
||||
|
||||
/// Access value specified by key (const, read-only)
|
||||
const V& at(const K& key) const {
|
||||
if (!m_sparse_set.contains(key)) {
|
||||
throw std::out_of_range(std::format("key \"{}\" does not exist in the sparse map", key));
|
||||
}
|
||||
|
||||
const auto dense_idx = m_sparse_set.sparse()[key];
|
||||
return m_values[dense_idx];
|
||||
}
|
||||
|
||||
// Modifiers
|
||||
|
||||
void insert(const K& key, const V& value) {
|
||||
// 1. Insert key into the underlying sparse set
|
||||
// 2. Insert value into the corresponding index in the sparse map
|
||||
|
||||
if (!m_sparse_set.contains(key)) {
|
||||
// Key does not yet exist in set
|
||||
|
||||
const auto insertion = m_sparse_set.insert(key);
|
||||
|
||||
if (!std::get<1>(insertion))
|
||||
return;
|
||||
|
||||
const auto dense_idx = std::get<0>(insertion);
|
||||
reserve(dense_idx + 1); // no-op if there's already enough space
|
||||
m_values[dense_idx] = value;
|
||||
++m_size;
|
||||
|
||||
return;
|
||||
} else {
|
||||
// Key already exists in set
|
||||
|
||||
const auto dense_idx = m_sparse_set.sparse()[key];
|
||||
m_values[dense_idx] = value;
|
||||
++m_size;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an element from the map
|
||||
void erase(const K& key) {
|
||||
// 1. Retrieve dense array index from set
|
||||
// 2. Swap to back
|
||||
// 3. Erase from set
|
||||
// 4. Decrement number of elements
|
||||
|
||||
if (contains(key)) {
|
||||
const auto dense_idx = m_sparse_set.sparse()[key];
|
||||
m_values[dense_idx] = m_values[m_size - 1];
|
||||
m_sparse_set.erase(key);
|
||||
--m_size;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Lookup
|
||||
|
||||
/// Check if the key exists in the map
|
||||
bool contains(const K& key) const {
|
||||
return m_sparse_set.contains(key);
|
||||
}
|
||||
|
||||
// Observers
|
||||
|
||||
/// Access to the underlying sparse_set container
|
||||
const sparse_set_t& sparse_set() const noexcept {
|
||||
return m_sparse_set;
|
||||
}
|
||||
|
||||
/// Access to the underlying values container
|
||||
const values_container_t& values() const noexcept {
|
||||
return m_values;
|
||||
}
|
||||
|
||||
// Operator overloads
|
||||
|
||||
/// Access/insert element specified by key
|
||||
V& operator[](const K& key) {
|
||||
const auto k = key;
|
||||
|
||||
if (!contains(k)) {
|
||||
insert(k, {});
|
||||
}
|
||||
|
||||
return at(k);
|
||||
}
|
||||
|
||||
/// Access element specified by key (const, read-only)
|
||||
const V& operator[](const K& key) const {
|
||||
return at(key);
|
||||
}
|
||||
|
||||
private:
|
||||
containers::sparse_set<K> m_sparse_set {};
|
||||
values_container_t m_values {};
|
||||
std::size_t m_size = 0;
|
||||
std::size_t m_capacity = 0;
|
||||
};
|
||||
|
||||
} // namespace containers
|
||||
|
||||
} // namespace kuiper
|
109
include/containers/sparse_set.hpp
Normal file
109
include/containers/sparse_set.hpp
Normal file
@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts> // std::unsigned_integral
|
||||
#include <cstddef> // std::size_t
|
||||
#include <iterator> // std::distance
|
||||
#include <utility> // std::pair
|
||||
#include <vector> // Underlying arrays
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace containers
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
requires std::unsigned_integral<T>
|
||||
class sparse_set {
|
||||
public:
|
||||
using sparse_container_t = std::vector<T>;
|
||||
using dense_container_t = std::vector<T>;
|
||||
|
||||
public:
|
||||
// Capacity
|
||||
|
||||
/// Reserve a number of slots in the underlying containers
|
||||
void reserve(std::size_t size) {
|
||||
if (size > m_capacity) {
|
||||
m_sparse.resize(size);
|
||||
m_dense.resize(size);
|
||||
m_capacity = size;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Modifiers
|
||||
|
||||
/// Add an element to the set, returns `(dense index, inserted)` pair
|
||||
std::pair<std::size_t, bool> insert(const T& value) {
|
||||
// 1. Append to next slot in the dense array
|
||||
// 2. Record dense array index (current m_size) in sparse array
|
||||
// 3. Increment number of elements
|
||||
|
||||
if (!contains(value)) {
|
||||
reserve(value + 1); // no-op if there's already enough space
|
||||
|
||||
m_dense[m_size] = value;
|
||||
m_sparse[value] = m_size;
|
||||
++m_size;
|
||||
|
||||
return std::make_pair(std::distance(m_dense.cbegin(), m_dense.cbegin() + m_sparse[value]), true);
|
||||
} else {
|
||||
// Value already exists in set, return the index, but not inserted flag
|
||||
return std::make_pair(std::distance(m_dense.cbegin(), m_dense.cbegin() + m_sparse[value]), false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an element from the set
|
||||
void erase(const T& value) {
|
||||
// 1. Swap element to be removed to the back of the dense array
|
||||
// 2. Update the dense array index for the value which was swapped
|
||||
// 3. Decrement number of elements
|
||||
|
||||
if (contains(value)) {
|
||||
m_dense[m_sparse[value]] = m_dense[m_size - 1];
|
||||
// m_dense[m_size - 1] = m_dense[m_sparse[value]];
|
||||
m_sparse[m_dense[m_size - 1]] = m_sparse[value];
|
||||
--m_size;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// Clear all elements from the set
|
||||
void clear() {
|
||||
m_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Lookup
|
||||
|
||||
/// Check if a value is in the set
|
||||
bool contains(const T& value) const {
|
||||
// First condition will short-circuit if `value` is out-of-range
|
||||
return !(value >= m_capacity) && (m_sparse[value] < m_size) && (m_dense[m_sparse[value]] == value);
|
||||
}
|
||||
|
||||
// Observers
|
||||
|
||||
/// Access to the underlying sparse container
|
||||
const sparse_container_t& sparse() const noexcept {
|
||||
return m_sparse;
|
||||
}
|
||||
|
||||
/// Access to the underlying dense container
|
||||
const dense_container_t& dense() const noexcept {
|
||||
return m_dense;
|
||||
}
|
||||
|
||||
private:
|
||||
sparse_container_t m_sparse {};
|
||||
dense_container_t m_dense {};
|
||||
std::size_t m_size = 0; // Number of elements, also the next index into m_dense
|
||||
std::size_t m_capacity = 0; // Number of elements + 1
|
||||
};
|
||||
|
||||
} // namespace containers
|
||||
|
||||
} // namespace kuiper
|
48
include/core/application.hpp
Normal file
48
include/core/application.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
class engine;
|
||||
|
||||
struct application_spec {
|
||||
std::u8string name = u8"Kuiper Engine Application";
|
||||
std::uint32_t width = 640;
|
||||
std::uint32_t height = 480;
|
||||
bool fullscreen = false;
|
||||
};
|
||||
|
||||
class application {
|
||||
public:
|
||||
application(const application_spec& app_spec = application_spec())
|
||||
: m_app_spec(app_spec) {};
|
||||
virtual ~application() = default;
|
||||
|
||||
public:
|
||||
virtual void initialise(kuiper::engine* engine) = 0;
|
||||
virtual void shutdown() = 0;
|
||||
virtual void pre_render(float dt) = 0;
|
||||
virtual void render(float dt) = 0;
|
||||
virtual void post_render(float dt) = 0;
|
||||
|
||||
inline const application_spec& get_app_spec() {
|
||||
return m_app_spec;
|
||||
}
|
||||
|
||||
private:
|
||||
application_spec m_app_spec = application_spec();
|
||||
};
|
||||
|
||||
/// This function must be implement by the user,
|
||||
/// defining & creating the application which will be run by the Kuiper engine.
|
||||
/// Consider this function your application's entrypoint, you can perform initialisation that must
|
||||
/// occur before your application starts here.
|
||||
/// NOTE: This function is expected to return a *unique* pointer to the application, to be owned by the engine.
|
||||
/// Don't store a copy of this pointer to use later as there are no guarantees to whether the pointer is valid.
|
||||
std::unique_ptr<application> create_application(int argc, char* argv[]);
|
||||
|
||||
} // namespace kuiper
|
82
include/core/engine.hpp
Normal file
82
include/core/engine.hpp
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include "cli/cli.hpp"
|
||||
#include "components/camera.hpp"
|
||||
#include "core/application.hpp"
|
||||
#include "graphics/opengl/framebuffer.hpp"
|
||||
#include "platform/glfw/glfw_window.hpp"
|
||||
#include "renderer/gl_renderer.hpp"
|
||||
#include "resources/resource_manager.hpp"
|
||||
|
||||
#include <flecs.h>
|
||||
|
||||
#include <expected>
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
struct engine_metrics {
|
||||
float rendering_time = 0.0f;
|
||||
gl::renderer_metrics renderer_metrics {};
|
||||
};
|
||||
|
||||
class engine {
|
||||
public:
|
||||
engine(std::unique_ptr<application>&& app);
|
||||
~engine();
|
||||
engine(const engine& other) = delete;
|
||||
engine& operator=(const engine& other) = delete;
|
||||
engine(engine&& other) noexcept = delete;
|
||||
engine& operator=(engine&& other) noexcept = delete;
|
||||
|
||||
/// Initialise core engine subsystems
|
||||
static std::expected<void, std::error_code> initialise(const cli& cli_args) noexcept;
|
||||
|
||||
void start();
|
||||
|
||||
public:
|
||||
std::shared_ptr<glfw_window> window() noexcept {
|
||||
return m_window;
|
||||
}
|
||||
|
||||
engine_metrics metrics() const noexcept {
|
||||
return m_metrics;
|
||||
}
|
||||
|
||||
flecs::world& world() noexcept {
|
||||
return m_world;
|
||||
}
|
||||
|
||||
resource_manager& resources() noexcept {
|
||||
return m_resources;
|
||||
}
|
||||
|
||||
void add_render_target(const gl::framebuffer::s_ptr& target) noexcept;
|
||||
void remove_render_target(const gl::framebuffer::s_ptr& target) noexcept;
|
||||
/// Only draw to render targets added with `add_render_target()`
|
||||
void disable_default_framebuffer() noexcept;
|
||||
/// Draw to default framebuffer as well as any additional render targets
|
||||
void enable_default_framebuffer() noexcept;
|
||||
void set_render_camera(const glm::vec3& pos, const component::camera_props& cam_props) noexcept;
|
||||
|
||||
private:
|
||||
std::unique_ptr<application> m_application {nullptr};
|
||||
std::shared_ptr<glfw_window> m_window {nullptr};
|
||||
engine_metrics m_metrics {};
|
||||
|
||||
std::vector<gl::framebuffer::s_ptr> m_render_targets {};
|
||||
bool m_render_to_default {true};
|
||||
glm::vec3 m_cam_pos {};
|
||||
component::camera_props m_cam_props {};
|
||||
|
||||
resource_manager m_resources {};
|
||||
flecs::world m_world {};
|
||||
};
|
||||
|
||||
// Some assumptions that I make
|
||||
static_assert(sizeof(char8_t) == sizeof(char));
|
||||
|
||||
} // namespace kuiper
|
15
include/core/entrypoint.hpp
Normal file
15
include/core/entrypoint.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(_WIN32)
|
||||
// TODO: Windows entrypoint
|
||||
#else
|
||||
/// Standard C entrypoint
|
||||
int main(int argc, char** argv);
|
||||
#endif
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
/// Actual entrypoint, as far as the engine is concerned.
|
||||
/// Once we reach the real entrypoint, `WinMain` should've converted any UTF-16 wchar strings into UTF-8 C-style strings
|
||||
int entrypoint(int argc, char** argv);
|
||||
} // namespace kuiper
|
21
include/core/io.hpp
Normal file
21
include/core/io.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
class io {
|
||||
public:
|
||||
static std::string read_text_file(const std::filesystem::path& path) noexcept;
|
||||
|
||||
static std::filesystem::path get_home_dir() noexcept;
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
16
include/errors/errors.hpp
Normal file
16
include/errors/errors.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
enum class error : std::uint8_t {
|
||||
unknown,
|
||||
failed_to_load,
|
||||
resource_missing,
|
||||
resource_invalid,
|
||||
opengl_error
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
30
include/errors/exceptions.hpp
Normal file
30
include/errors/exceptions.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
namespace exceptions
|
||||
{
|
||||
class file_not_found : public std::exception {
|
||||
public:
|
||||
file_not_found(const std::filesystem::path& path)
|
||||
: m_path(path) {};
|
||||
|
||||
public:
|
||||
inline std::filesystem::path path() const noexcept {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
// Implementing std::exception
|
||||
inline const char* what() const noexcept override {
|
||||
return "file not found";
|
||||
}
|
||||
|
||||
private:
|
||||
std::filesystem::path m_path;
|
||||
};
|
||||
} // namespace exceptions
|
||||
} // namespace kuiper
|
60
include/graphics/opengl/buffer.hpp
Normal file
60
include/graphics/opengl/buffer.hpp
Normal file
@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
class buffer {
|
||||
public:
|
||||
using s_ptr = std::shared_ptr<buffer>;
|
||||
|
||||
explicit buffer(GLuint id, GLenum target)
|
||||
: m_id(id), m_target(target) {}
|
||||
~buffer() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
buffer(const buffer& other) = delete;
|
||||
buffer& operator=(const buffer& other) = delete;
|
||||
buffer(buffer&& other) noexcept = default;
|
||||
buffer& operator=(buffer&& other) noexcept = default;
|
||||
|
||||
static s_ptr make(GLenum target) noexcept;
|
||||
|
||||
public:
|
||||
GLuint id() const noexcept {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
std::size_t size() const noexcept {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
void bind() const noexcept;
|
||||
void bind_to_location(std::uint32_t location) const noexcept;
|
||||
void upload(GLenum usage, std::size_t size, const void* data) noexcept;
|
||||
void update(std::size_t offset, std::size_t size, const void* data) noexcept;
|
||||
void set_vertex_attrib(std::uint32_t index,
|
||||
std::size_t size,
|
||||
GLenum type,
|
||||
std::size_t stride,
|
||||
std::size_t offset,
|
||||
bool normalised = false) noexcept;
|
||||
|
||||
private:
|
||||
GLuint m_id {0};
|
||||
GLenum m_target {0};
|
||||
std::size_t m_size {0};
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
||||
} // namespace kuiper
|
79
include/graphics/opengl/framebuffer.hpp
Normal file
79
include/graphics/opengl/framebuffer.hpp
Normal file
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "graphics/opengl/texture.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
class framebuffer {
|
||||
public:
|
||||
using s_ptr = std::shared_ptr<framebuffer>;
|
||||
|
||||
// OpenGL mandates at least 8 colour buffers
|
||||
static constexpr std::uint32_t max_colour_attachments = 8;
|
||||
using col_tex_storage = std::array<texture::s_ptr, max_colour_attachments>;
|
||||
|
||||
explicit framebuffer(GLuint id)
|
||||
: m_id(id) {}
|
||||
~framebuffer() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
framebuffer(const framebuffer& other) = delete;
|
||||
framebuffer& operator=(const framebuffer& other) = delete;
|
||||
framebuffer(framebuffer&& other) noexcept = default;
|
||||
framebuffer& operator=(framebuffer&& other) noexcept = default;
|
||||
|
||||
static s_ptr make() noexcept;
|
||||
static s_ptr make_default(std::uint32_t width, std::uint32_t height) noexcept;
|
||||
|
||||
public:
|
||||
void destroy() noexcept;
|
||||
|
||||
void bind() const noexcept;
|
||||
|
||||
void resize(std::uint32_t width, std::uint32_t height) noexcept;
|
||||
|
||||
GLuint id() const noexcept {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
const col_tex_storage& colour_tex() const noexcept {
|
||||
return m_colour_texs;
|
||||
}
|
||||
|
||||
const texture::s_ptr& depth_tex() const noexcept {
|
||||
return m_depth_tex;
|
||||
}
|
||||
|
||||
glm::u32vec2 dimensions() const noexcept {
|
||||
return {m_width, m_height};
|
||||
}
|
||||
|
||||
// Attach a colour texture to the framebuffer, optionally at a given attachment point. Takes ownership of `tex`.
|
||||
void attach_colour(texture::s_ptr&& tex, std::uint32_t attachment_point = 0) noexcept;
|
||||
// Attach a depth AND stencil texture to the framebuffer. Takes ownership of `tex`.
|
||||
void attach_depth(texture::s_ptr&& tex) noexcept;
|
||||
|
||||
private:
|
||||
GLuint m_id {0};
|
||||
col_tex_storage m_colour_texs {};
|
||||
texture::s_ptr m_depth_tex {nullptr};
|
||||
std::uint32_t m_width {0};
|
||||
std::uint32_t m_height {0};
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
||||
} // namespace kuiper
|
70
include/graphics/opengl/program.hpp
Normal file
70
include/graphics/opengl/program.hpp
Normal file
@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
#include "graphics/opengl/shader.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <glm/mat3x3.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
class program {
|
||||
public:
|
||||
using s_ptr = std::shared_ptr<program>;
|
||||
|
||||
explicit program(GLuint id)
|
||||
: m_id(id) {}
|
||||
~program() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
program(const program& other) = delete;
|
||||
program& operator=(const program& other) = delete;
|
||||
program(program&& other) noexcept = default;
|
||||
program& operator=(program&& other) noexcept = default;
|
||||
|
||||
static s_ptr make() noexcept;
|
||||
static s_ptr from_shaders(std::initializer_list<shader::s_ptr> shaders);
|
||||
|
||||
public:
|
||||
GLuint id() const noexcept {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
void use() const noexcept;
|
||||
void attach_shader(const shader::s_ptr& shader) noexcept;
|
||||
bool link() noexcept;
|
||||
|
||||
// Uniforms
|
||||
|
||||
GLint uniform_location(const char* name) const noexcept;
|
||||
void set_uint(const char* uniform, std::uint32_t value) noexcept;
|
||||
void set_int(const char* uniform, std::int32_t value) noexcept;
|
||||
void set_int_array(const char* uniform, const std::int32_t* value_arr, std::size_t len) noexcept;
|
||||
void set_float(const char* uniform, float value) noexcept;
|
||||
void set_vec3(const char* uniform, const glm::vec3& value) noexcept;
|
||||
void set_uvec3(const char* uniform, const glm::uvec3& value) noexcept;
|
||||
void set_uvec2(const char* uniform, const glm::uvec2& value) noexcept;
|
||||
void set_mat3(const char* uniform, const glm::mat3& value) noexcept;
|
||||
void set_mat4(const char* uniform, const glm::mat4& value) noexcept;
|
||||
|
||||
private:
|
||||
GLuint m_id {0};
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
||||
} // namespace kuiper
|
74
include/graphics/opengl/render_context.hpp
Normal file
74
include/graphics/opengl/render_context.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "graphics/opengl/framebuffer.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
void initialise();
|
||||
|
||||
void dump_hardware_info();
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
void debug_callback(GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
GLenum severity,
|
||||
GLsizei length,
|
||||
const GLchar* message,
|
||||
const void* userParam) noexcept;
|
||||
#endif
|
||||
|
||||
class render_context {
|
||||
public:
|
||||
/// Binds a framebuffer to the renderer pipeline
|
||||
static void bind_framebuffer(const framebuffer::s_ptr& fb) noexcept;
|
||||
|
||||
/// Binds the default framebuffer
|
||||
static void bind_default_framebuffer() noexcept;
|
||||
|
||||
/// Set the viewport size over the currently bound framebuffer
|
||||
static void set_viewport_size(std::uint32_t width, std::uint32_t height) noexcept;
|
||||
|
||||
/// Enables an OpenGL feature
|
||||
static void enable_feature(GLenum feature) noexcept;
|
||||
|
||||
/// Disables an OpenGL feature
|
||||
static void disable_feature(GLenum feature) noexcept;
|
||||
|
||||
template<typename T>
|
||||
static T get(GLenum param) noexcept {
|
||||
T ret {};
|
||||
|
||||
if constexpr (std::is_same<T, GLint>::value) {
|
||||
glGetIntegerv(param, &ret);
|
||||
} else if constexpr (std::is_same<T, GLint64>::value) {
|
||||
glGetInteger64v(param, &ret);
|
||||
} else if constexpr (std::is_same<T, GLfloat>::value) {
|
||||
glGetFloatv(param, &ret);
|
||||
} else if constexpr (std::is_same<T, GLdouble>::value) {
|
||||
glGetDoublev(param, &ret);
|
||||
} else if constexpr (std::is_same<T, GLboolean>::value) {
|
||||
glGetBooleanv(param, &ret);
|
||||
} else {
|
||||
static_assert(!"non-exhaustive if-constexpr!");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static constexpr const char* GLSL_VERSION = "#version 460 core";
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
||||
} // namespace kuiper
|
57
include/graphics/opengl/shader.hpp
Normal file
57
include/graphics/opengl/shader.hpp
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
enum class shader_type : std::uint8_t {
|
||||
none = 0,
|
||||
vertex,
|
||||
fragment,
|
||||
compute
|
||||
};
|
||||
|
||||
class shader {
|
||||
public:
|
||||
using s_ptr = std::shared_ptr<shader>;
|
||||
|
||||
explicit shader(GLuint id, GLenum type)
|
||||
: m_id(id), m_type(type) {}
|
||||
~shader() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
shader(const shader& other) = delete;
|
||||
shader& operator=(const shader& other) = delete;
|
||||
shader(shader&& other) noexcept = default;
|
||||
shader& operator=(shader&& other) noexcept = default;
|
||||
|
||||
static s_ptr make(shader_type type) noexcept;
|
||||
static s_ptr from_path(const std::filesystem::path& shader_path, shader_type type = shader_type::none) noexcept;
|
||||
static s_ptr from_source(std::string_view source, shader_type type) noexcept;
|
||||
|
||||
public:
|
||||
void destroy() noexcept;
|
||||
void attach_source(std::string_view source) noexcept;
|
||||
void compile() noexcept;
|
||||
GLuint id() const noexcept {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
private:
|
||||
GLuint m_id {0};
|
||||
GLenum m_type {0};
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
||||
} // namespace kuiper
|
97
include/graphics/opengl/texture.hpp
Normal file
97
include/graphics/opengl/texture.hpp
Normal file
@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
enum class texture_type : std::uint8_t {
|
||||
none = 0,
|
||||
tex_2d
|
||||
};
|
||||
|
||||
class texture {
|
||||
public:
|
||||
using s_ptr = std::shared_ptr<texture>;
|
||||
|
||||
explicit texture(GLuint id, GLenum target)
|
||||
: m_id(id), m_target(target) {}
|
||||
~texture() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
texture(const texture& other) = delete;
|
||||
texture& operator=(const texture& other) = delete;
|
||||
texture(texture&& other) noexcept = default;
|
||||
texture& operator=(texture&& other) noexcept = default;
|
||||
|
||||
/// Makes a new, uninitialised texture
|
||||
static s_ptr make(texture_type type = texture_type::tex_2d) noexcept;
|
||||
/// Makes a placeholder 4x4 texture
|
||||
static s_ptr make_placeholder() noexcept;
|
||||
/// Makes a texture from a path to an encoded image
|
||||
static s_ptr from_image_path(const std::filesystem::path& image_path, bool flip_y = true) noexcept;
|
||||
/// Makes a texture from a pointer to encoded image data
|
||||
static s_ptr from_image_data(const std::uint8_t* buffer, std::size_t length, bool flip_y = true) noexcept;
|
||||
|
||||
public:
|
||||
/// Explicitly delete the OpenGL texture object
|
||||
void destroy() noexcept;
|
||||
/// Bind the texture
|
||||
void bind() noexcept;
|
||||
/// Upload image data to the texture object on the GPU
|
||||
void upload(
|
||||
GLint internal_fmt, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* data) noexcept;
|
||||
/// Resize underlying texture buffer
|
||||
void resize(std::uint32_t width, std::uint32_t height) noexcept;
|
||||
/// Set a texture parameter
|
||||
void set_param(GLenum name, GLint param) noexcept;
|
||||
/// Generate texture mipmaps
|
||||
void gen_mipmaps() noexcept;
|
||||
|
||||
/// Get the dimensions of the texture
|
||||
glm::uvec2 dimensions() const noexcept {
|
||||
return {m_width, m_height};
|
||||
}
|
||||
|
||||
GLint internal_format() const noexcept {
|
||||
return m_internal_fmt;
|
||||
}
|
||||
|
||||
GLenum pixel_format() const noexcept {
|
||||
return m_pixel_fmt;
|
||||
}
|
||||
|
||||
GLenum pixel_type() const noexcept {
|
||||
return m_pixel_type;
|
||||
}
|
||||
|
||||
/// Get the internal OpenGL object name
|
||||
GLuint id() const noexcept {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
private:
|
||||
GLuint m_id {0};
|
||||
GLenum m_target {0};
|
||||
std::uint32_t m_width {0};
|
||||
std::uint32_t m_height {0};
|
||||
GLint m_internal_fmt {0};
|
||||
GLenum m_pixel_fmt {0};
|
||||
GLenum m_pixel_type {0};
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
||||
} // namespace kuiper
|
74
include/graphics/opengl/texture_array.hpp
Normal file
74
include/graphics/opengl/texture_array.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
class texture_array {
|
||||
public:
|
||||
using s_ptr = std::shared_ptr<texture_array>;
|
||||
|
||||
explicit texture_array(GLuint id, GLenum target)
|
||||
: m_id(id), m_target(target) {}
|
||||
~texture_array() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
texture_array(const texture_array& other) = delete;
|
||||
texture_array& operator=(const texture_array& other) = delete;
|
||||
texture_array(texture_array&& other) noexcept = default;
|
||||
texture_array& operator=(texture_array&& other) noexcept = default;
|
||||
|
||||
static s_ptr make(GLenum target) noexcept;
|
||||
|
||||
public:
|
||||
void destroy() noexcept;
|
||||
void bind(std::uint32_t unit = 0) const noexcept;
|
||||
void set_storage(GLenum internal_fmt, std::uint32_t width, std::uint32_t height, std::uint32_t depth) noexcept;
|
||||
void upload(std::uint32_t x_offset,
|
||||
std::uint32_t y_offset,
|
||||
std::uint32_t z_offset,
|
||||
std::uint32_t width,
|
||||
std::uint32_t height,
|
||||
std::uint32_t depth,
|
||||
GLenum format,
|
||||
GLenum type,
|
||||
const void* data,
|
||||
std::uint32_t mip_level = 0) noexcept;
|
||||
void set_param(GLenum name, GLint param) noexcept;
|
||||
void gen_mipmaps() noexcept;
|
||||
|
||||
glm::uvec3 dimensions() const noexcept {
|
||||
return {m_width, m_height, m_depth};
|
||||
}
|
||||
|
||||
GLuint id() const noexcept {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
GLenum internal_format() const noexcept {
|
||||
return m_internal_fmt;
|
||||
}
|
||||
|
||||
private:
|
||||
GLuint m_id {0};
|
||||
GLenum m_target {0};
|
||||
GLenum m_internal_fmt {0};
|
||||
std::uint32_t m_width {0};
|
||||
std::uint32_t m_height {0};
|
||||
std::uint32_t m_depth {0};
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
||||
} // namespace kuiper
|
48
include/graphics/opengl/vertex_array.hpp
Normal file
48
include/graphics/opengl/vertex_array.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
class vertex_array {
|
||||
public:
|
||||
using s_ptr = std::shared_ptr<vertex_array>;
|
||||
|
||||
explicit vertex_array(GLuint id)
|
||||
: m_id(id) {}
|
||||
~vertex_array() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
vertex_array(const vertex_array& other) = delete;
|
||||
vertex_array& operator=(const vertex_array& other) = delete;
|
||||
vertex_array(vertex_array&& other) noexcept = default;
|
||||
vertex_array& operator=(vertex_array&& other) noexcept = default;
|
||||
|
||||
static s_ptr make() noexcept;
|
||||
|
||||
public:
|
||||
GLuint id() const noexcept {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
void bind() const noexcept;
|
||||
void enable_attrib(std::uint32_t index) noexcept;
|
||||
void disable_attrib(std::uint32_t index) noexcept;
|
||||
void set_attrib_divisor(std::uint32_t attrib, std::uint32_t divisor) noexcept;
|
||||
void set_binding_divisor(std::uint32_t binding, std::uint32_t divisor) noexcept;
|
||||
|
||||
private:
|
||||
GLuint m_id {0};
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
||||
} // namespace kuiper
|
56
include/logging/engine_logger.hpp
Normal file
56
include/logging/engine_logger.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "logging/logger.hpp"
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
/// @brief Singleton class providing engine-wide logging facilities
|
||||
class engine_logger {
|
||||
private:
|
||||
engine_logger() = default;
|
||||
|
||||
public:
|
||||
~engine_logger() = default;
|
||||
|
||||
public:
|
||||
static inline engine_logger& get(void) {
|
||||
static engine_logger s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void trace(const char* fmt, Args... args) {
|
||||
m_log.trace(fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void debug(const char* fmt, Args... args) {
|
||||
m_log.debug(fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void info(const char* fmt, Args... args) {
|
||||
m_log.info(fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void warn(const char* fmt, Args... args) {
|
||||
m_log.warn(fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void error(const char* fmt, Args... args) {
|
||||
m_log.error(fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void critical(const char* fmt, Args... args) {
|
||||
m_log.critical(fmt, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
kuiper::logger m_log {"engine"};
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
67
include/logging/logger.hpp
Normal file
67
include/logging/logger.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view> // TODO: Use std::format_string when available
|
||||
#include <utility>
|
||||
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
class logger {
|
||||
public:
|
||||
logger()
|
||||
: m_log(spdlog::stdout_color_st("log")) {
|
||||
#if !defined(NDEBUG)
|
||||
m_log->set_level(spdlog::level::debug);
|
||||
#else
|
||||
m_log->set_level(spdlog::level::info);
|
||||
#endif
|
||||
}
|
||||
logger(const char* name)
|
||||
: m_log(spdlog::stdout_color_st(name)) {
|
||||
#if !defined(NDEBUG)
|
||||
m_log->set_level(spdlog::level::debug);
|
||||
#else
|
||||
m_log->set_level(spdlog::level::info);
|
||||
#endif
|
||||
}
|
||||
~logger() = default;
|
||||
|
||||
template<typename... Args>
|
||||
inline void trace(std::string_view fmt, Args&&... args) {
|
||||
m_log->trace(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void debug(std::string_view fmt, Args&&... args) {
|
||||
m_log->debug(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void info(std::string_view fmt, Args&&... args) {
|
||||
m_log->info(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void warn(std::string_view fmt, Args&&... args) {
|
||||
m_log->warn(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void error(std::string_view fmt, Args&&... args) {
|
||||
m_log->error(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void critical(std::string_view fmt, Args&&... args) {
|
||||
m_log->critical(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<spdlog::logger> m_log {nullptr};
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
18
include/maths/aspect_ratio.hpp
Normal file
18
include/maths/aspect_ratio.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace maths
|
||||
{
|
||||
template<typename T, typename V>
|
||||
requires(std::floating_point<V> || std::integral<V>) && std::floating_point<T>
|
||||
constexpr inline T aspect_ratio(V width, V height) {
|
||||
return static_cast<T>(width) / static_cast<T>(height);
|
||||
}
|
||||
|
||||
} // namespace maths
|
||||
|
||||
} // namespace kuiper
|
37
include/maths/pow2.hpp
Normal file
37
include/maths/pow2.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
|
||||
inline std::uint64_t next_pow2(std::uint64_t value) {
|
||||
value--;
|
||||
value |= value >> 1;
|
||||
value |= value >> 2;
|
||||
value |= value >> 4;
|
||||
value |= value >> 8;
|
||||
value |= value >> 16;
|
||||
value |= value >> 32;
|
||||
value++;
|
||||
value += (value == 0); // Case for if value == 0 (returns 0 as the next power of 2, which 0 is not)
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline std::uint32_t next_pow2(std::uint32_t value) {
|
||||
value--;
|
||||
value |= value >> 1;
|
||||
value |= value >> 2;
|
||||
value |= value >> 4;
|
||||
value |= value >> 8;
|
||||
value |= value >> 16;
|
||||
value++;
|
||||
value += (value == 0);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace kuiper
|
52
include/maths/transform.hpp
Normal file
52
include/maths/transform.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "glm/gtc/quaternion.hpp"
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/ext/quaternion_float.hpp>
|
||||
#include <glm/ext/quaternion_trigonometric.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/trigonometric.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace maths
|
||||
{
|
||||
|
||||
struct transform_quat {
|
||||
glm::vec3 position = {0.0f, 0.0f, 0.0f};
|
||||
glm::quat rotation = {1.0f, 0.0f, 0.0f, 0.0f};
|
||||
glm::vec3 scale = {1.0f, 1.0f, 1.0f};
|
||||
|
||||
glm::mat4 to_mat4() const noexcept {
|
||||
static constexpr glm::mat4 identity_mat = glm::mat4(1.0f);
|
||||
|
||||
const glm::mat4 tm = glm::translate(identity_mat, position);
|
||||
const glm::mat4 rm = glm::mat4_cast(rotation);
|
||||
const glm::mat4 sm = glm::scale(identity_mat, scale);
|
||||
|
||||
return tm * rm * sm;
|
||||
}
|
||||
};
|
||||
|
||||
struct transform_euler {
|
||||
glm::vec3 position = {0.0f, 0.0f, 0.0f};
|
||||
glm::vec3 rotation = {0.0f, 0.0f, 0.0f};
|
||||
glm::vec3 scale = {1.0f, 1.0f, 1.0f};
|
||||
|
||||
glm::mat4 to_mat4() const noexcept {
|
||||
static constexpr glm::mat4 identity_mat = glm::mat4(1.0f);
|
||||
|
||||
const glm::mat4 tm = glm::translate(identity_mat, position);
|
||||
const glm::mat4 rm = glm::mat4_cast(glm::quat(rotation));
|
||||
const glm::mat4 sm = glm::scale(identity_mat, scale);
|
||||
|
||||
return tm * rm * sm;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace maths
|
||||
|
||||
} // namespace kuiper
|
89
include/platform/glfw/glfw_window.hpp
Normal file
89
include/platform/glfw/glfw_window.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include "graphics/opengl/framebuffer.hpp"
|
||||
#include "window/input_system.hpp"
|
||||
#include "window/window.hpp"
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
class glfw_window : public window {
|
||||
public:
|
||||
glfw_window(
|
||||
std::uint32_t width, std::uint32_t height, std::u8string_view title, GLFWmonitor* monitor, GLFWwindow* share);
|
||||
~glfw_window() override;
|
||||
|
||||
// Only allow moving
|
||||
glfw_window(const glfw_window& other) = delete;
|
||||
glfw_window& operator=(const glfw_window& other) = delete;
|
||||
glfw_window(glfw_window&& other) noexcept = default;
|
||||
glfw_window& operator=(glfw_window&& other) noexcept = default;
|
||||
|
||||
public:
|
||||
// Implement window
|
||||
|
||||
virtual void* get_native_handle() const noexcept override;
|
||||
virtual void set_title(std::u8string_view title) noexcept override;
|
||||
virtual void poll_events() noexcept override;
|
||||
virtual void swap_buffers() noexcept override;
|
||||
virtual void set_swap_interval(std::uint32_t interval) noexcept override;
|
||||
virtual void bind_context() noexcept override;
|
||||
virtual gl::framebuffer::s_ptr back_buffer() const noexcept override;
|
||||
|
||||
virtual bool should_close() const noexcept override;
|
||||
virtual bool is_minimised() const noexcept override;
|
||||
virtual bool is_cursor_locked() const noexcept override;
|
||||
|
||||
virtual void lock_cursor() noexcept override;
|
||||
virtual void unlock_cursor() noexcept override;
|
||||
virtual void enable_raw_input() noexcept override;
|
||||
virtual void disable_raw_input() noexcept override;
|
||||
|
||||
virtual glm::uvec2 get_size() const noexcept override;
|
||||
virtual glm::uvec2 get_framebuffer_size() const noexcept override; // should this go in render context?
|
||||
virtual glm::vec2 get_content_scale() const noexcept override;
|
||||
|
||||
input_system& input() noexcept {
|
||||
return m_input_state;
|
||||
}
|
||||
|
||||
private:
|
||||
void resize_back_buffer() noexcept;
|
||||
|
||||
public:
|
||||
static void initialise_glfw(bool prefer_x11);
|
||||
|
||||
private:
|
||||
static GLFWwindow* create_glfw_window(std::uint32_t width,
|
||||
std::uint32_t height,
|
||||
std::u8string_view title,
|
||||
GLFWmonitor* monitor,
|
||||
GLFWwindow* share) noexcept;
|
||||
static void push_window_hints() noexcept;
|
||||
|
||||
// GLFW Callbacks
|
||||
|
||||
static void glfw_error_callback(int error_code, const char* description);
|
||||
static void glfw_framebuffer_size_callback(GLFWwindow* window, int width, int height);
|
||||
static void glfw_content_scale_callback(GLFWwindow* window, float xscale, float yscale);
|
||||
static void glfw_key_event_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
|
||||
static void glfw_cursor_pos_callback(GLFWwindow* window, double xpos, double ypos);
|
||||
static void glfw_scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
|
||||
static void glfw_dragdrop_callback(GLFWwindow* window, int path_count, const char* paths[]);
|
||||
|
||||
private:
|
||||
GLFWwindow* m_window_handle = nullptr;
|
||||
input_system m_input_state {};
|
||||
bool m_cursor_locked = false;
|
||||
gl::framebuffer::s_ptr m_default_framebuffer = nullptr;
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
163
include/renderer/gl_renderer.hpp
Normal file
163
include/renderer/gl_renderer.hpp
Normal file
@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
#include "components/camera.hpp"
|
||||
#include "components/lights.hpp"
|
||||
#include "graphics/opengl/buffer.hpp"
|
||||
#include "graphics/opengl/framebuffer.hpp"
|
||||
#include "graphics/opengl/program.hpp"
|
||||
#include "graphics/opengl/vertex_array.hpp"
|
||||
#include "maths/transform.hpp"
|
||||
#include "resources/material.hpp"
|
||||
#include "resources/model.hpp"
|
||||
#include "resources/primitive.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <array>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace gl
|
||||
{
|
||||
static constexpr std::size_t MAX_NUM_LIGHTS = 100;
|
||||
static constexpr std::size_t LIGHT_SSBO_INIT_SIZE = 1000;
|
||||
static constexpr std::uint32_t NUM_CLUSTERS_X = 12;
|
||||
static constexpr std::uint32_t NUM_CLUSTERS_Y = 12;
|
||||
static constexpr std::uint32_t NUM_CLUSTERS_Z = 24;
|
||||
static constexpr std::uint32_t NUM_CLUSTERS = NUM_CLUSTERS_X * NUM_CLUSTERS_Y * NUM_CLUSTERS_Z;
|
||||
static constexpr std::uint32_t LOCAL_SIZE = 128;
|
||||
static constexpr float NEAR_Z_DEPTH = 0.1f;
|
||||
static constexpr float FAR_Z_DEPTH = std::numeric_limits<std::uint16_t>::max();
|
||||
|
||||
struct gl_state {
|
||||
// Vertex & element buffers
|
||||
vertex_array::s_ptr vao {nullptr};
|
||||
buffer::s_ptr vertex_buffer {nullptr};
|
||||
buffer::s_ptr index_buffer {nullptr};
|
||||
|
||||
// Indirect & model matrix buffer
|
||||
|
||||
buffer::s_ptr indirect_buffer {nullptr};
|
||||
buffer::s_ptr matrix_buffer {nullptr};
|
||||
|
||||
// Shaders
|
||||
program::s_ptr pbr_shader {nullptr};
|
||||
program::s_ptr clustering_shader {nullptr};
|
||||
program::s_ptr light_culling_shader {nullptr};
|
||||
|
||||
// Lighting buffers
|
||||
buffer::s_ptr cluster_buffer {nullptr};
|
||||
buffer::s_ptr lights_buffer {nullptr};
|
||||
|
||||
// Configuration
|
||||
std::uint32_t max_texture_units {32};
|
||||
std::vector<std::int32_t> texture_unit_slots {};
|
||||
};
|
||||
|
||||
struct renderer_metrics {
|
||||
std::size_t n_vertices {};
|
||||
std::size_t n_indices {};
|
||||
std::size_t n_draw_cmds {};
|
||||
std::size_t n_matrices {};
|
||||
std::size_t n_materials {};
|
||||
std::size_t n_lights {};
|
||||
};
|
||||
|
||||
class renderer {
|
||||
public:
|
||||
using vertex_t = vertex;
|
||||
using index_t = std::uint32_t;
|
||||
|
||||
struct draw_elements_indirect_cmd {
|
||||
std::uint32_t count {0};
|
||||
std::uint32_t instance_count {0};
|
||||
std::uint32_t first_index {0};
|
||||
std::uint32_t base_vertex {0};
|
||||
std::uint32_t base_instance {0};
|
||||
};
|
||||
|
||||
struct alignas(4) render_matrices {
|
||||
glm::mat4 model_matrix;
|
||||
glm::mat3 normal_matrix;
|
||||
};
|
||||
|
||||
struct alignas(16) cluster {
|
||||
glm::vec4 min_point;
|
||||
glm::vec4 max_point;
|
||||
std::uint32_t count;
|
||||
std::uint32_t light_indices[MAX_NUM_LIGHTS];
|
||||
};
|
||||
|
||||
struct alignas(16) point_light {
|
||||
glm::vec4 position;
|
||||
glm::vec4 colour;
|
||||
float intensity;
|
||||
float radius;
|
||||
};
|
||||
|
||||
private:
|
||||
using vertex_storage_t = std::vector<vertex_t>;
|
||||
using index_storage_t = std::vector<index_t>;
|
||||
using draw_cmd_storage_t = std::vector<draw_elements_indirect_cmd>;
|
||||
using matrix_storage_t = std::vector<render_matrices>;
|
||||
using material_storage_t = std::vector<resource::material>;
|
||||
using light_storage_t = std::vector<point_light>;
|
||||
|
||||
public:
|
||||
renderer(gl_state&& state)
|
||||
: m_state(std::move(state)) {}
|
||||
~renderer() = default;
|
||||
|
||||
renderer(const renderer&) = delete;
|
||||
renderer& operator=(const renderer&) = delete;
|
||||
renderer(renderer&&) noexcept = delete;
|
||||
renderer& operator=(renderer&&) noexcept = delete;
|
||||
|
||||
static renderer init();
|
||||
|
||||
void render(const framebuffer::s_ptr& framebuffer,
|
||||
const glm::vec3& cam_pos,
|
||||
const component::camera_props& cam_props) noexcept;
|
||||
void flush() noexcept;
|
||||
renderer_metrics metrics() const noexcept;
|
||||
|
||||
void draw_model(const resource::model& model, const maths::transform_euler& transform);
|
||||
void draw_mesh(const resource::mesh& mesh,
|
||||
const std::vector<resource::material>& materials,
|
||||
const glm::mat4& transform);
|
||||
void draw_primitive(const resource::primitive& primitive,
|
||||
const resource::material& material,
|
||||
const maths::transform_euler& transform);
|
||||
void draw_primitive(const resource::primitive& primitive,
|
||||
const resource::material& material,
|
||||
const glm::mat4& transform);
|
||||
void add_light(const glm::vec3& pos, const component::point_light& light) noexcept;
|
||||
|
||||
private:
|
||||
void update_and_upload() noexcept;
|
||||
|
||||
private:
|
||||
gl_state m_state {};
|
||||
|
||||
vertex_storage_t m_vertices {};
|
||||
index_storage_t m_indices {};
|
||||
draw_cmd_storage_t m_draw_cmds {};
|
||||
matrix_storage_t m_matrices {};
|
||||
material_storage_t m_materials {};
|
||||
light_storage_t m_lights {};
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
||||
} // namespace kuiper
|
79
include/resources/image.hpp
Normal file
79
include/resources/image.hpp
Normal file
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace resource
|
||||
{
|
||||
|
||||
struct image_error {
|
||||
std::string_view message;
|
||||
};
|
||||
|
||||
class image {
|
||||
public:
|
||||
image(std::uint32_t width,
|
||||
std::uint32_t height,
|
||||
std::uint32_t n_channels,
|
||||
const std::uint8_t* data,
|
||||
std::size_t len)
|
||||
: m_width(width), m_height(height), m_channels(n_channels), m_data(data, data + len) {}
|
||||
image() = default;
|
||||
~image() = default;
|
||||
image(const image& other) = default;
|
||||
image& operator=(const image& other) = default;
|
||||
image(image&& other) noexcept = default;
|
||||
image& operator=(image&& other) noexcept = default;
|
||||
|
||||
static image make_blank(std::uint32_t width, std::uint32_t height, std::uint32_t n_channels) noexcept;
|
||||
static std::expected<image, image_error> from_path(const std::filesystem::path& path,
|
||||
std::uint8_t num_channels = 4) noexcept;
|
||||
static std::expected<image, image_error> from_memory(const std::uint8_t* data,
|
||||
std::size_t len,
|
||||
std::uint8_t num_channels = 4) noexcept;
|
||||
|
||||
std::uint32_t width() const noexcept {
|
||||
return m_width;
|
||||
}
|
||||
|
||||
std::uint32_t height() const noexcept {
|
||||
return m_height;
|
||||
}
|
||||
|
||||
glm::uvec2 dimensions() const noexcept {
|
||||
return {m_width, m_height};
|
||||
}
|
||||
|
||||
std::uint32_t channels() const noexcept {
|
||||
return m_channels;
|
||||
}
|
||||
|
||||
const std::vector<std::uint8_t>& pixel_data() const noexcept {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t>& pixel_data_mut() noexcept {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
void resize(std::uint32_t width, std::uint32_t height) noexcept;
|
||||
|
||||
private:
|
||||
std::uint32_t m_width {0};
|
||||
std::uint32_t m_height {0};
|
||||
std::uint32_t m_channels {0};
|
||||
std::vector<std::uint8_t> m_data {};
|
||||
};
|
||||
|
||||
} // namespace resource
|
||||
|
||||
} // namespace kuiper
|
20
include/resources/material.hpp
Normal file
20
include/resources/material.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "graphics/opengl/texture.hpp"
|
||||
#include "graphics/opengl/texture_array.hpp"
|
||||
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
namespace kuiper::resource
|
||||
{
|
||||
|
||||
struct material {
|
||||
glm::vec4 base_colour_factor {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
float metallic_factor {1.0f};
|
||||
float roughness_factor {1.0f};
|
||||
|
||||
// 0: Albedo, 1: Normal, 2: Metallic-Roughness
|
||||
gl::texture_array::s_ptr textures {nullptr};
|
||||
};
|
||||
|
||||
} // namespace kuiper::resource
|
55
include/resources/mesh.hpp
Normal file
55
include/resources/mesh.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "maths/transform.hpp"
|
||||
#include "resources/primitive.hpp"
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <vector>
|
||||
|
||||
namespace kuiper::resource
|
||||
{
|
||||
|
||||
class mesh {
|
||||
public:
|
||||
using primitive_storage = std::vector<primitive>;
|
||||
using submesh_storage = std::vector<mesh>;
|
||||
|
||||
mesh(const glm::mat4& t)
|
||||
: m_transform(t) {}
|
||||
|
||||
mesh(const glm::mat4& t, std::initializer_list<primitive> primitives)
|
||||
: m_transform(t) {
|
||||
for (const auto& p : primitives) {
|
||||
m_primitives.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
void add_primitive(const primitive& prim) {
|
||||
return m_primitives.push_back(prim);
|
||||
}
|
||||
|
||||
void add_submesh(const class mesh& mesh) {
|
||||
return m_submeshes.push_back(mesh);
|
||||
}
|
||||
|
||||
const glm::mat4& transform() const noexcept {
|
||||
return m_transform;
|
||||
}
|
||||
|
||||
const primitive_storage& primitives() const noexcept {
|
||||
return m_primitives;
|
||||
}
|
||||
|
||||
const submesh_storage& submeshes() const noexcept {
|
||||
return m_submeshes;
|
||||
}
|
||||
|
||||
private:
|
||||
primitive_storage m_primitives {};
|
||||
submesh_storage m_submeshes {};
|
||||
glm::mat4 m_transform {};
|
||||
};
|
||||
|
||||
} // namespace kuiper::resource
|
56
include/resources/model.hpp
Normal file
56
include/resources/model.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "errors/errors.hpp"
|
||||
#include "resources/material.hpp"
|
||||
#include "resources/mesh.hpp"
|
||||
|
||||
#include <fastgltf/core.hpp>
|
||||
|
||||
#include <expected>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
namespace kuiper::resource
|
||||
{
|
||||
|
||||
class model {
|
||||
public:
|
||||
using mesh_storage = std::vector<mesh>;
|
||||
using material_storage = std::vector<material>;
|
||||
|
||||
model() = default;
|
||||
model(std::initializer_list<mesh> meshes) {
|
||||
for (const auto& m : meshes) {
|
||||
m_meshes.push_back(m);
|
||||
}
|
||||
}
|
||||
|
||||
void push_mesh(const mesh& m) {
|
||||
return m_meshes.push_back(m);
|
||||
}
|
||||
|
||||
void push_material(const material& m) {
|
||||
return m_materials.push_back(m);
|
||||
}
|
||||
|
||||
const mesh_storage& meshes() const noexcept {
|
||||
return m_meshes;
|
||||
}
|
||||
|
||||
const material_storage& materials() const noexcept {
|
||||
return m_materials;
|
||||
}
|
||||
|
||||
/// Construct a `model` from a path to a glTF file
|
||||
static std::expected<model, kuiper::error> from_gltf_path(const std::filesystem::path& gltf_path);
|
||||
/// Construct a `model` from a path to a glTF file, prefer this function to the above as fastgltf's `Parser` should
|
||||
/// ideally be re-used (only on one thread, though)
|
||||
static std::expected<model, kuiper::error> from_gltf_path(const std::filesystem::path& gltf_path,
|
||||
fastgltf::Parser& gltf_parser);
|
||||
|
||||
private:
|
||||
mesh_storage m_meshes;
|
||||
material_storage m_materials;
|
||||
};
|
||||
|
||||
} // namespace kuiper::resource
|
38
include/resources/primitive.hpp
Normal file
38
include/resources/primitive.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
struct vertex {
|
||||
glm::vec3 position; // (x, y, z), float
|
||||
glm::vec3 normal; // (x, y, z), float
|
||||
glm::vec4 tangent; // (x, y, z, w), float
|
||||
glm::vec2 uv; // (x, y) float
|
||||
};
|
||||
|
||||
enum class primitive_type : std::uint8_t {
|
||||
none = 0,
|
||||
triangles
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
||||
|
||||
namespace kuiper::resource
|
||||
{
|
||||
|
||||
struct primitive {
|
||||
primitive_type type {primitive_type::none};
|
||||
std::vector<vertex> vertices {};
|
||||
std::vector<std::uint32_t> indices {};
|
||||
std::size_t material_idx {};
|
||||
};
|
||||
|
||||
} // namespace kuiper::resource
|
13
include/resources/resource_index.hpp
Normal file
13
include/resources/resource_index.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace kuiper::resource
|
||||
{
|
||||
|
||||
class index {
|
||||
public:
|
||||
static index from_file(const std::filesystem::path& index_path) noexcept;
|
||||
};
|
||||
|
||||
} // namespace kuiper::resource
|
96
include/resources/resource_manager.hpp
Normal file
96
include/resources/resource_manager.hpp
Normal file
@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include "graphics/opengl/shader.hpp"
|
||||
#include "graphics/opengl/texture.hpp"
|
||||
#include "resources/material.hpp"
|
||||
#include "resources/model.hpp"
|
||||
#include "utils/xoshiro256plusplus.hpp"
|
||||
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
using resource_id_t = std::uint32_t;
|
||||
|
||||
enum class resource_type : std::uint8_t {
|
||||
none = 0,
|
||||
texture,
|
||||
shader,
|
||||
model
|
||||
};
|
||||
|
||||
struct resource_metadata {
|
||||
resource_id_t id {0};
|
||||
resource_type type {resource_type::none};
|
||||
std::string name {};
|
||||
};
|
||||
|
||||
class resource_manager {
|
||||
public:
|
||||
using metadata_map_t = absl::flat_hash_map<resource_id_t, resource_metadata>;
|
||||
using texture_map_t = absl::flat_hash_map<resource_id_t, gl::texture::s_ptr>;
|
||||
using shader_map_t = absl::flat_hash_map<resource_id_t, gl::shader::s_ptr>;
|
||||
using model_map_t = absl::flat_hash_map<resource_id_t, resource::model>;
|
||||
using material_map_t = absl::flat_hash_map<resource_id_t, resource::material>;
|
||||
|
||||
template<typename T>
|
||||
resource_metadata add(T&& resource, std::string_view name = {}) {
|
||||
const auto new_id = static_cast<resource_id_t>(m_prng.next());
|
||||
resource_type type = resource_type::none;
|
||||
|
||||
if constexpr (std::same_as<T, resource::model>) {
|
||||
type = resource_type::model;
|
||||
m_models.insert({new_id, std::forward<T>(resource)});
|
||||
} else {
|
||||
static_assert(!"non-exhaustive if constexpr");
|
||||
}
|
||||
|
||||
resource_metadata metadata {new_id, type, std::string(name)};
|
||||
m_metadata.insert({new_id, metadata});
|
||||
return metadata;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::optional<T> get(resource_id_t id) {
|
||||
if constexpr (std::same_as<T, resource::model>) {
|
||||
if (m_models.contains(id)) {
|
||||
return m_models.at(id);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
static_assert(!"non-exhaustive if constexpr");
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<resource_metadata> import_from_path(const std::filesystem::path& path);
|
||||
|
||||
const metadata_map_t& metadata() const noexcept {
|
||||
return m_metadata;
|
||||
}
|
||||
|
||||
const model_map_t& models() const noexcept {
|
||||
return m_models;
|
||||
}
|
||||
|
||||
private:
|
||||
random::xoshiro256pp m_prng {random::xoshiro256pp::from_random_device()};
|
||||
|
||||
metadata_map_t m_metadata {};
|
||||
texture_map_t m_textures {};
|
||||
shader_map_t m_shaders {};
|
||||
model_map_t m_models {};
|
||||
material_map_t m_materials {};
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
74
include/utils/arc.hpp
Normal file
74
include/utils/arc.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
/// Atomically reference counted type
|
||||
template<typename T>
|
||||
class arc {
|
||||
public:
|
||||
/// Argument-forwarding constructor
|
||||
template<typename... Args>
|
||||
explicit arc(Args&&... args)
|
||||
: m_inner(std::forward<Args>(args)...) {}
|
||||
|
||||
/// Default constructor (no args.)
|
||||
arc()
|
||||
: m_inner() {}
|
||||
|
||||
/// Destructor
|
||||
~arc() {
|
||||
if (m_ref_count.load() > 0)
|
||||
return;
|
||||
}
|
||||
|
||||
/// Copy constructor
|
||||
arc(const arc& other)
|
||||
: m_inner(other.m_inner), m_ref_count(other.m_ref_count.load() + 1) {}
|
||||
|
||||
/// Copy assignment constructor
|
||||
arc& operator=(const arc& other) {
|
||||
m_inner = other.m_inner;
|
||||
m_ref_count = other.m_ref_count.load() + 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Move constructor
|
||||
arc(arc&& other) noexcept
|
||||
: m_inner(std::move(other.m_inner)), m_ref_count(other.m_ref_count.load()) {};
|
||||
|
||||
/// Move assignment constructor
|
||||
arc& operator=(arc&& other) noexcept {
|
||||
*this = std::move(other);
|
||||
return *this;
|
||||
};
|
||||
|
||||
// Inner type access
|
||||
|
||||
/// Reference to the type contained in the `arc`
|
||||
T& inner() noexcept {
|
||||
return m_inner;
|
||||
}
|
||||
|
||||
/// Read-only reference to the type contained in the `arc`
|
||||
const T& const_inner() const noexcept {
|
||||
return m_inner;
|
||||
}
|
||||
|
||||
// Reference count
|
||||
|
||||
std::size_t ref_count() const noexcept {
|
||||
return m_ref_count.load();
|
||||
}
|
||||
|
||||
private:
|
||||
T m_inner;
|
||||
|
||||
std::atomic_size_t m_ref_count = 0;
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
25
include/utils/assert.hpp
Normal file
25
include/utils/assert.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <print>
|
||||
#include <stacktrace>
|
||||
#include <string>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
#define KUIPER_ASSERT(cond) \
|
||||
if (!(cond)) { \
|
||||
std::println(stderr, "Assertion failed at: {}", __PRETTY_FUNCTION__); \
|
||||
std::abort(); \
|
||||
}
|
||||
|
||||
#define KUIPER_ASSERT_STACKTRACE(cond) \
|
||||
if (!(cond)) { \
|
||||
const auto trace = std::stacktrace::current(); \
|
||||
std::println(stderr, "Assertion failed at: {}", __PRETTY_FUNCTION__); \
|
||||
std::println(stderr, "{}", std::to_string(trace)); \
|
||||
std::abort(); \
|
||||
}
|
||||
|
||||
} // namespace kuiper
|
35
include/utils/bytes.hpp
Normal file
35
include/utils/bytes.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
namespace utils
|
||||
{
|
||||
namespace bits
|
||||
{
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
inline T set_bit_n(T value, std::uint8_t bit_n) {
|
||||
return value | (static_cast<T>(0b00000001) << (bit_n - 1));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
inline T unset_bit_n(T value, std::uint8_t bit_n) {
|
||||
return value & ~(static_cast<T>(0b00000001) << (bit_n - 1));
|
||||
}
|
||||
} // namespace bits
|
||||
|
||||
namespace bytes
|
||||
{
|
||||
template<typename T>
|
||||
inline constexpr std::array<std::uint8_t, sizeof(T)> as_byte_array(T value) {
|
||||
return std::bit_cast<std::array<std::uint8_t, sizeof(T)>, T>(value);
|
||||
}
|
||||
} // namespace bytes
|
||||
} // namespace utils
|
||||
} // namespace kuiper
|
17
include/utils/colours.hpp
Normal file
17
include/utils/colours.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace colours
|
||||
{
|
||||
constexpr const glm::vec4 middle_grey(0.737254901961f, 0.737254901961f, 0.737254901961f, 1.0f);
|
||||
constexpr const glm::vec4 dark_grey(0.1337f, 0.1337f, 0.1337f, 1.0f);
|
||||
constexpr const glm::vec4 very_dark_grey(0.05f, 0.05f, 0.05f, 1.0f);
|
||||
constexpr const glm::vec4 black(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
constexpr const glm::vec4 nothing(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
} // namespace colours
|
||||
|
||||
} // namespace kuiper
|
15
include/utils/constants.hpp
Normal file
15
include/utils/constants.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
namespace constants
|
||||
{
|
||||
constexpr const char* kuiper_engine_str = "Kuiper Engine";
|
||||
constexpr const char8_t* kuiper_engine_u8str = u8"Kuiper Engine";
|
||||
constexpr const char* author_str = "Adam Macdonald";
|
||||
constexpr const char8_t* author_u8str = u8"Adam Macdonald";
|
||||
|
||||
} // namespace constants
|
||||
|
||||
} // namespace kuiper
|
73
include/utils/rc.hpp
Normal file
73
include/utils/rc.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
/// Reference counted type
|
||||
template<typename T>
|
||||
class rc {
|
||||
public:
|
||||
/// Argument-forwarding constructor
|
||||
template<typename... Args>
|
||||
explicit rc(Args&&... args)
|
||||
: m_inner(std::forward<Args>(args)...) {}
|
||||
|
||||
/// Default constructor (no args.)
|
||||
rc()
|
||||
: m_inner() {}
|
||||
|
||||
/// Destructor
|
||||
~rc() {
|
||||
if (m_ref_count > 0)
|
||||
return;
|
||||
}
|
||||
|
||||
/// Copy constructor
|
||||
rc(const rc& other)
|
||||
: m_inner(other.m_inner), m_ref_count(other.m_ref_count + 1) {}
|
||||
|
||||
/// Copy assignment constructor
|
||||
rc& operator=(const rc& other) {
|
||||
m_inner = other.m_inner;
|
||||
m_ref_count = other.m_ref_count + 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Move constructor
|
||||
rc(rc&& other) noexcept
|
||||
: m_inner(std::move(other.m_inner)), m_ref_count(other.m_ref_count) {};
|
||||
|
||||
/// Move assignment constructor
|
||||
rc& operator=(rc&& other) noexcept {
|
||||
*this = std::move(other);
|
||||
return *this;
|
||||
};
|
||||
|
||||
// Inner type access
|
||||
|
||||
/// Reference to the type contained in the `rc`
|
||||
T& inner() noexcept {
|
||||
return m_inner;
|
||||
}
|
||||
|
||||
/// Read-only reference to the type contained in the `rc`
|
||||
const T& const_inner() const noexcept {
|
||||
return m_inner;
|
||||
}
|
||||
|
||||
// Reference count
|
||||
|
||||
std::size_t ref_count() const noexcept {
|
||||
return m_ref_count;
|
||||
}
|
||||
|
||||
private:
|
||||
T m_inner;
|
||||
|
||||
std::size_t m_ref_count = 0;
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
39
include/utils/uuid.hpp
Normal file
39
include/utils/uuid.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "random/random.hpp"
|
||||
|
||||
#include <cstddef> // For size_t
|
||||
#include <cstdint> // For uint64_t
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
class UUID {
|
||||
public:
|
||||
UUID()
|
||||
: m_uuid(Random::Instance().next_u64()) {}
|
||||
UUID(const std::uint64_t uuid)
|
||||
: m_uuid(uuid) {}
|
||||
UUID(const UUID&) = default;
|
||||
|
||||
operator std::uint64_t() const {
|
||||
return m_uuid;
|
||||
}
|
||||
|
||||
private:
|
||||
std::uint64_t m_uuid = 0;
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<typename T> struct hash;
|
||||
|
||||
template<> struct hash<kuiper::UUID> {
|
||||
std::size_t operator()(const kuiper::UUID& uuid) const {
|
||||
// SURELY there won't be too many collisions with 2^64 possible combinations :^)
|
||||
return static_cast<uint64_t>(uuid);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
148
include/utils/xoshiro256plusplus.hpp
Normal file
148
include/utils/xoshiro256plusplus.hpp
Normal file
@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
|
||||
namespace kuiper::random
|
||||
{
|
||||
|
||||
class xoshiro256pp {
|
||||
public:
|
||||
xoshiro256pp(const std::array<std::uint64_t, 4>& state)
|
||||
: s(state) {}
|
||||
|
||||
static xoshiro256pp from_random_device() {
|
||||
std::random_device rd {};
|
||||
|
||||
static_assert(sizeof(std::random_device::result_type) == 4);
|
||||
|
||||
const std::uint64_t a = static_cast<std::uint64_t>(rd()) << 32 | static_cast<std::uint64_t>(rd());
|
||||
const std::uint64_t b = static_cast<std::uint64_t>(rd()) << 32 | static_cast<std::uint64_t>(rd());
|
||||
const std::uint64_t c = static_cast<std::uint64_t>(rd()) << 32 | static_cast<std::uint64_t>(rd());
|
||||
const std::uint64_t d = static_cast<std::uint64_t>(rd()) << 32 | static_cast<std::uint64_t>(rd());
|
||||
|
||||
return {{{a, b, c, d}}};
|
||||
}
|
||||
|
||||
public:
|
||||
std::uint64_t next() noexcept {
|
||||
const std::uint64_t result = rotl(s[0] + s[3], 23) + s[0];
|
||||
|
||||
const std::uint64_t t = s[1] << 17;
|
||||
|
||||
s[2] ^= s[0];
|
||||
s[3] ^= s[1];
|
||||
s[1] ^= s[2];
|
||||
s[0] ^= s[3];
|
||||
|
||||
s[2] ^= t;
|
||||
|
||||
s[3] = rotl(s[3], 45);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::uint64_t operator()(void) noexcept {
|
||||
return next();
|
||||
}
|
||||
|
||||
private:
|
||||
/* This is the jump function for the generator. It is equivalent
|
||||
to 2^128 calls to next(); it can be used to generate 2^128
|
||||
non-overlapping subsequences for parallel computations. */
|
||||
|
||||
void jump() noexcept {
|
||||
static constexpr std::array<std::uint64_t, 4> JUMP = {
|
||||
0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c};
|
||||
|
||||
std::uint64_t s0 = 0;
|
||||
std::uint64_t s1 = 0;
|
||||
std::uint64_t s2 = 0;
|
||||
std::uint64_t s3 = 0;
|
||||
for (int i = 0; i < JUMP.size() / sizeof(std::uint64_t); i++)
|
||||
for (int b = 0; b < 64; b++) {
|
||||
if (JUMP[i] & UINT64_C(1) << b) {
|
||||
s0 ^= s[0];
|
||||
s1 ^= s[1];
|
||||
s2 ^= s[2];
|
||||
s3 ^= s[3];
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
s[0] = s0;
|
||||
s[1] = s1;
|
||||
s[2] = s2;
|
||||
s[3] = s3;
|
||||
}
|
||||
|
||||
/* This is the long-jump function for the generator. It is equivalent to
|
||||
2^192 calls to next(); it can be used to generate 2^64 starting points,
|
||||
from each of which jump() will generate 2^64 non-overlapping
|
||||
subsequences for parallel distributed computations. */
|
||||
|
||||
void long_jump() {
|
||||
static constexpr std::array<std::uint64_t, 4> LONG_JUMP = {
|
||||
0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635};
|
||||
|
||||
std::uint64_t s0 = 0;
|
||||
std::uint64_t s1 = 0;
|
||||
std::uint64_t s2 = 0;
|
||||
std::uint64_t s3 = 0;
|
||||
for (int i = 0; i < LONG_JUMP.size() / sizeof(std::uint64_t); i++)
|
||||
for (int b = 0; b < 64; b++) {
|
||||
if (LONG_JUMP[i] & UINT64_C(1) << b) {
|
||||
s0 ^= s[0];
|
||||
s1 ^= s[1];
|
||||
s2 ^= s[2];
|
||||
s3 ^= s[3];
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
s[0] = s0;
|
||||
s[1] = s1;
|
||||
s[2] = s2;
|
||||
s[3] = s3;
|
||||
}
|
||||
|
||||
inline std::uint64_t rotl(const std::uint64_t x, int k) {
|
||||
return (x << k) | (x >> (64 - k));
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<std::uint64_t, 4> s {};
|
||||
};
|
||||
|
||||
// Original license & preface:
|
||||
|
||||
/* Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org)
|
||||
|
||||
To the extent possible under law, the author has dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
|
||||
/* This is xoshiro256++ 1.0, one of our all-purpose, rock-solid generators.
|
||||
It has excellent (sub-ns) speed, a state (256 bits) that is large
|
||||
enough for any parallel application, and it passes all tests we are
|
||||
aware of.
|
||||
|
||||
For generating just floating-point numbers, xoshiro256+ is even faster.
|
||||
|
||||
The state must be seeded so that it is not everywhere zero. If you have
|
||||
a 64-bit seed, we suggest to seed a splitmix64 generator and use its
|
||||
output to fill s. */
|
||||
|
||||
} // namespace kuiper::random
|
76
include/window/input_system.hpp
Normal file
76
include/window/input_system.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
using action_fn_t = std::function<void()>;
|
||||
|
||||
enum action_type : std::uint8_t {
|
||||
none = 0,
|
||||
escape,
|
||||
};
|
||||
|
||||
constexpr std::size_t max_actions = 1 << (sizeof(action_type) * 8);
|
||||
|
||||
struct key_state {
|
||||
int state = GLFW_RELEASE;
|
||||
};
|
||||
|
||||
struct mouse_state {
|
||||
glm::vec2 pos;
|
||||
glm::vec2 delta_pos;
|
||||
};
|
||||
|
||||
class input_system {
|
||||
public:
|
||||
input_system() {
|
||||
// Default engine action keymappings
|
||||
m_action_keymap[GLFW_KEY_ESCAPE] = action_type::escape;
|
||||
return;
|
||||
}
|
||||
~input_system() = default;
|
||||
|
||||
// Keyboard
|
||||
|
||||
void set_key_state(int key, int state);
|
||||
int get_key_state(int key) const;
|
||||
|
||||
bool is_key_pressed(int key) const;
|
||||
bool is_key_released(int key) const;
|
||||
bool is_key_held(int key) const;
|
||||
bool is_key_down(int key) const;
|
||||
|
||||
// Mouse
|
||||
|
||||
void set_mouse_pos(const glm::vec2& pos);
|
||||
void set_mouse_delta(const glm::vec2& pos);
|
||||
|
||||
/// Get mouse position (top-left corner is the origin)
|
||||
glm::vec2 get_mouse_pos() const;
|
||||
/// Get change in mouse position since last frame (top-left corner is the origin)
|
||||
glm::vec2 get_mouse_delta() const;
|
||||
|
||||
// Actions
|
||||
// TODO: user can add a new custom action of their own
|
||||
|
||||
void subscribe_to_action(action_type type, const action_fn_t& callback);
|
||||
|
||||
private:
|
||||
std::array<key_state, GLFW_KEY_LAST> m_key_states {};
|
||||
std::array<action_type, GLFW_KEY_LAST> m_action_keymap {};
|
||||
std::array<std::vector<action_fn_t>, max_actions> m_action_listeners {};
|
||||
mouse_state m_mouse_state {};
|
||||
glm::vec2 m_old_mouse_pos {};
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
40
include/window/window.hpp
Normal file
40
include/window/window.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "graphics/opengl/framebuffer.hpp"
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
namespace kuiper
|
||||
{
|
||||
|
||||
class window {
|
||||
public:
|
||||
virtual ~window() = default;
|
||||
|
||||
virtual void* get_native_handle() const noexcept = 0;
|
||||
virtual void set_title(std::u8string_view title) noexcept = 0;
|
||||
virtual void poll_events() noexcept = 0;
|
||||
virtual void swap_buffers() noexcept = 0;
|
||||
virtual void set_swap_interval(std::uint32_t interval) noexcept = 0;
|
||||
virtual void bind_context() noexcept = 0;
|
||||
virtual gl::framebuffer::s_ptr back_buffer() const noexcept = 0;
|
||||
|
||||
virtual bool should_close() const noexcept = 0;
|
||||
virtual bool is_minimised() const noexcept = 0;
|
||||
virtual bool is_cursor_locked() const noexcept = 0;
|
||||
|
||||
virtual void lock_cursor() noexcept = 0;
|
||||
virtual void unlock_cursor() noexcept = 0;
|
||||
virtual void enable_raw_input() noexcept = 0;
|
||||
virtual void disable_raw_input() noexcept = 0;
|
||||
|
||||
virtual glm::uvec2 get_size() const noexcept = 0;
|
||||
virtual glm::uvec2 get_framebuffer_size() const noexcept = 0; // should this go in render context?
|
||||
virtual glm::vec2 get_content_scale() const noexcept = 0;
|
||||
};
|
||||
|
||||
} // namespace kuiper
|
66
src/cli/cli.cpp
Normal file
66
src/cli/cli.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include "cli/cli.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <expected>
|
||||
#include <format>
|
||||
#include <print>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
void cli::print_usage() {
|
||||
std::println("Usage: doniv-cli [-P process_name | -p process_id] -L library_path");
|
||||
return;
|
||||
}
|
||||
std::string_view cli::cli_args_parse_err_str(cli_args_parse_err err) {
|
||||
switch (err) {
|
||||
case cli_args_parse_err::invalid_argument:
|
||||
return "Invalid command line arguments"sv;
|
||||
case cli_args_parse_err::not_enough_args:
|
||||
return "Not enough command line parameters"sv;
|
||||
default:
|
||||
return ""sv;
|
||||
}
|
||||
}
|
||||
|
||||
std::expected<cli, cli_args_parse_err> cli::try_construct(int argc, char** argv) {
|
||||
if (argc < 1) {
|
||||
return std::unexpected(cli_args_parse_err::not_enough_args);
|
||||
}
|
||||
|
||||
const std::span<char*> cli_args(argv, argc);
|
||||
int root_path_arg_idx = 0;
|
||||
int prefer_x11_arg_idx = 0;
|
||||
|
||||
for (int i = 0; i < cli_args.size(); ++i) {
|
||||
const auto& arg = cli_args[i];
|
||||
const int next_idx = (i + 1);
|
||||
|
||||
if (std::strcmp(arg, "-root") == 0) {
|
||||
if (next_idx < cli_args.size()) {
|
||||
root_path_arg_idx = next_idx;
|
||||
}
|
||||
} else if (std::strcmp(arg, "-x11") == 0) {
|
||||
if (next_idx <= cli_args.size()) {
|
||||
prefer_x11_arg_idx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path root_path;
|
||||
bool prefer_x11 = false;
|
||||
|
||||
if (root_path_arg_idx) {
|
||||
const auto root_path_cstr = cli_args[root_path_arg_idx];
|
||||
root_path = root_path_cstr;
|
||||
}
|
||||
|
||||
prefer_x11 = prefer_x11_arg_idx;
|
||||
|
||||
return cli {root_path, prefer_x11};
|
||||
}
|
184
src/core/engine.cpp
Normal file
184
src/core/engine.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
#include "core/engine.hpp"
|
||||
|
||||
#include "components/camera.hpp"
|
||||
#include "components/lights.hpp"
|
||||
#include "components/resources.hpp"
|
||||
#include "core/application.hpp"
|
||||
#include "flecs/addons/cpp/c_types.hpp"
|
||||
#include "graphics/opengl/program.hpp"
|
||||
#include "graphics/opengl/shader.hpp"
|
||||
#include "logging/engine_logger.hpp"
|
||||
#include "maths/transform.hpp"
|
||||
#include "renderer/gl_renderer.hpp"
|
||||
#include "resources/model.hpp"
|
||||
#include "resources/resource_manager.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
#include <flecs.h>
|
||||
#include <stb_image.h>
|
||||
|
||||
#include <algorithm> // std::clamp
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cmath> // std::fmod
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
using namespace kuiper;
|
||||
|
||||
engine::engine(std::unique_ptr<kuiper::application>&& app)
|
||||
: m_application(std::move(app)),
|
||||
m_window(std::make_unique<glfw_window>(m_application->get_app_spec().width,
|
||||
m_application->get_app_spec().height,
|
||||
m_application->get_app_spec().name,
|
||||
nullptr,
|
||||
nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
engine::~engine() {
|
||||
// Should be in reverse order of subsystems initialised in Initialise()
|
||||
|
||||
// NOTE: Should free window ptr here?
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::expected<void, std::error_code> engine::initialise(const cli& cli_args) noexcept {
|
||||
// OpenGL expects { 0.0, 0.0 } to be the bottom-left corner of the image
|
||||
stbi_set_flip_vertically_on_load(true);
|
||||
|
||||
// Engine working directory
|
||||
if (!cli_args.root_path.empty()) {
|
||||
std::filesystem::current_path(cli_args.root_path);
|
||||
}
|
||||
|
||||
// GLFW
|
||||
glfw_window::initialise_glfw(cli_args.prefer_x11);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void engine::start() {
|
||||
auto logger = engine_logger::get();
|
||||
logger.info("Kuiper Engine starting");
|
||||
|
||||
m_window->lock_cursor();
|
||||
m_window->enable_raw_input();
|
||||
m_application->initialise(this);
|
||||
auto renderer = gl::renderer::init();
|
||||
|
||||
// Local player & camera
|
||||
|
||||
auto local_player = m_world.entity("Local player");
|
||||
local_player.set<maths::transform_euler>({});
|
||||
|
||||
auto local_camera = m_world.entity("Local camera");
|
||||
local_camera.set<maths::transform_euler>({});
|
||||
local_camera.set<component::camera_props>({});
|
||||
local_camera.add(flecs::ChildOf, local_player);
|
||||
|
||||
// Per-frame flecs queries
|
||||
|
||||
const auto renderables =
|
||||
world().query_builder<maths::transform_euler, component::model_resource>().cached().build();
|
||||
const auto lights = world().query_builder<maths::transform_euler, component::point_light>().cached().build();
|
||||
|
||||
auto last_time_point = std::chrono::high_resolution_clock::now(); // Initialised with first time point
|
||||
while (!m_window->should_close()) {
|
||||
const auto time_now = std::chrono::high_resolution_clock::now();
|
||||
const std::chrono::duration<float> delta_time = time_now - last_time_point;
|
||||
last_time_point = time_now;
|
||||
|
||||
// Input
|
||||
|
||||
m_window->poll_events();
|
||||
|
||||
// State
|
||||
// TODO: [update world state here]
|
||||
|
||||
m_application->pre_render(delta_time.count());
|
||||
|
||||
// Rendering
|
||||
|
||||
{
|
||||
auto rendering_start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
lights.each([&renderer](flecs::entity e,
|
||||
const maths::transform_euler& transform,
|
||||
const component::point_light& light) {
|
||||
renderer.add_light(transform.position, light);
|
||||
});
|
||||
|
||||
// Render every entity with a transform & model attached
|
||||
renderables.each([this, &renderer](flecs::entity e,
|
||||
const maths::transform_euler& transform,
|
||||
const component::model_resource& resource) {
|
||||
const auto& model = m_resources.get<resource::model>(resource.id);
|
||||
if (!model.has_value())
|
||||
return;
|
||||
|
||||
renderer.draw_model(model.value(), transform);
|
||||
});
|
||||
|
||||
// Draw to framebuffers
|
||||
for (const auto& target : m_render_targets) {
|
||||
renderer.render(target, m_cam_pos, m_cam_props);
|
||||
}
|
||||
|
||||
// Draw to default framebuffer
|
||||
if (m_render_to_default) {
|
||||
renderer.render(m_window->back_buffer(), m_cam_pos, m_cam_props);
|
||||
}
|
||||
|
||||
// Debug metrics
|
||||
|
||||
m_metrics.renderer_metrics = renderer.metrics();
|
||||
|
||||
// Flush the renderer state, hand over the default frame buffer to the application
|
||||
|
||||
renderer.flush();
|
||||
|
||||
std::chrono::duration<float, std::milli> render_dur =
|
||||
std::chrono::high_resolution_clock::now() - rendering_start;
|
||||
m_metrics.rendering_time = render_dur.count();
|
||||
}
|
||||
|
||||
m_window->back_buffer()->bind(); // so the user application renders to the window
|
||||
|
||||
m_application->post_render(delta_time.count());
|
||||
m_window->swap_buffers();
|
||||
|
||||
// End
|
||||
}
|
||||
|
||||
m_application->shutdown();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void engine::add_render_target(const gl::framebuffer::s_ptr& target) noexcept {
|
||||
return m_render_targets.push_back(target);
|
||||
}
|
||||
|
||||
void engine::remove_render_target(const gl::framebuffer::s_ptr& target) noexcept {
|
||||
std::ignore = std::remove(m_render_targets.begin(), m_render_targets.end(), target);
|
||||
return;
|
||||
}
|
||||
|
||||
void engine::disable_default_framebuffer() noexcept {
|
||||
m_render_to_default = false;
|
||||
return;
|
||||
}
|
||||
|
||||
void engine::enable_default_framebuffer() noexcept {
|
||||
m_render_to_default = true;
|
||||
return;
|
||||
}
|
||||
|
||||
void engine::set_render_camera(const glm::vec3& pos, const component::camera_props& cam_props) noexcept {
|
||||
m_cam_pos = pos;
|
||||
m_cam_props = cam_props;
|
||||
return;
|
||||
}
|
64
src/core/entrypoint.cpp
Normal file
64
src/core/entrypoint.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "core/entrypoint.hpp"
|
||||
|
||||
#include "cli/cli.hpp"
|
||||
#include "core/application.hpp"
|
||||
#include "core/engine.hpp"
|
||||
#include "logging/engine_logger.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
// Implemented by the user
|
||||
extern std::unique_ptr<kuiper::application> kuiper::create_application(int argc, char* argv[]);
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return kuiper::entrypoint(argc, argv);
|
||||
}
|
||||
|
||||
int kuiper::entrypoint(int argc, char* argv[]) {
|
||||
// Initialise logging
|
||||
|
||||
auto logger = engine_logger::get();
|
||||
|
||||
// Parse command line arguments
|
||||
|
||||
// NOTE: We assume that command line arguments are UTF-8 encoded at this point
|
||||
const auto maybe_args = cli::try_construct(argc, argv);
|
||||
if (!maybe_args) {
|
||||
logger.error("Invalid command like argument: {}", cli::cli_args_parse_err_str(maybe_args.error()));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
const auto& cli_args = maybe_args.value();
|
||||
|
||||
// Create instance of client application
|
||||
|
||||
std::unique_ptr<kuiper::application> app = kuiper::create_application(argc, argv);
|
||||
|
||||
if (app == nullptr) {
|
||||
logger.critical("Application was nullptr");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Initialise engine & begin execution
|
||||
|
||||
try {
|
||||
const auto initialised = engine::initialise(cli_args);
|
||||
if (!initialised) {
|
||||
logger.critical("Failed to initialise engine");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
engine engine(std::move(app));
|
||||
engine.start();
|
||||
} catch (const std::exception& ex) {
|
||||
logger.critical("Unhandled std::exception: {}", ex.what());
|
||||
return EXIT_FAILURE;
|
||||
} catch (...) {
|
||||
logger.critical("Unknown unhandled exception!");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS; // TODO: Return error exit code if the app encounters an error during execution
|
||||
}
|
52
src/core/io.cpp
Normal file
52
src/core/io.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "core/io.hpp"
|
||||
|
||||
#include "errors/exceptions.hpp"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#if defined(__linux__)
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
}
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
using namespace kuiper;
|
||||
|
||||
std::string io::read_text_file(const std::filesystem::path& path) noexcept {
|
||||
if (path.empty() || !std::filesystem::is_regular_file(path) || !std::filesystem::exists(path)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
auto input_file = std::ifstream(path, std::ios::ate);
|
||||
if (!input_file.is_open()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto file_size = input_file.tellg();
|
||||
input_file.seekg(0);
|
||||
|
||||
// Use std::stringstream here (?)
|
||||
std::string buf(static_cast<std::size_t>(file_size) + 1, '\0');
|
||||
input_file.read(&buf[0], file_size);
|
||||
input_file.close();
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::filesystem::path io::get_home_dir() noexcept {
|
||||
#if defined(__linux__)
|
||||
const auto uid = getuid();
|
||||
const auto pw = getpwuid(uid);
|
||||
return {pw->pw_dir};
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
59
src/graphics/opengl/buffer.cpp
Normal file
59
src/graphics/opengl/buffer.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#include "graphics/opengl/buffer.hpp"
|
||||
#include <memory>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
buffer::s_ptr buffer::make(GLenum target) noexcept {
|
||||
GLuint name = 0;
|
||||
glGenBuffers(1, &name);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<buffer>(name, target);
|
||||
}
|
||||
|
||||
void buffer::destroy() noexcept {
|
||||
if (m_id)
|
||||
glDeleteBuffers(1, &m_id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void buffer::bind() const noexcept {
|
||||
return glBindBuffer(m_target, m_id);
|
||||
}
|
||||
|
||||
void buffer::bind_to_location(std::uint32_t location) const noexcept {
|
||||
return glBindBufferBase(m_target, location, m_id);
|
||||
}
|
||||
|
||||
void buffer::upload(GLenum usage, std::size_t size, const void* data) noexcept {
|
||||
m_size = size;
|
||||
|
||||
bind();
|
||||
|
||||
return glBufferData(m_target, static_cast<GLsizeiptr>(size), data, usage);
|
||||
}
|
||||
|
||||
void buffer::update(std::size_t offset, std::size_t size, const void* data) noexcept {
|
||||
bind();
|
||||
|
||||
return glBufferSubData(m_target, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), data);
|
||||
}
|
||||
|
||||
void buffer::set_vertex_attrib(std::uint32_t index,
|
||||
std::size_t size,
|
||||
GLenum type,
|
||||
std::size_t stride,
|
||||
std::size_t offset,
|
||||
bool normalised) noexcept {
|
||||
bind();
|
||||
|
||||
return glVertexAttribPointer(index,
|
||||
static_cast<GLint>(size),
|
||||
type,
|
||||
normalised,
|
||||
static_cast<GLsizei>(stride),
|
||||
reinterpret_cast<const void*>(offset));
|
||||
}
|
118
src/graphics/opengl/framebuffer.cpp
Normal file
118
src/graphics/opengl/framebuffer.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "graphics/opengl/framebuffer.hpp"
|
||||
|
||||
#include "graphics/opengl/texture.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
framebuffer::s_ptr framebuffer::make() noexcept {
|
||||
GLuint name = 0;
|
||||
glGenFramebuffers(1, &name);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<framebuffer>(name);
|
||||
}
|
||||
|
||||
framebuffer::s_ptr framebuffer::make_default(std::uint32_t width, std::uint32_t height) noexcept {
|
||||
if (!width || !height)
|
||||
return nullptr;
|
||||
|
||||
auto fb = framebuffer::make();
|
||||
if (!fb)
|
||||
return nullptr;
|
||||
|
||||
// Colour
|
||||
|
||||
auto col_tex = texture::make();
|
||||
col_tex->upload(
|
||||
GL_RGBA8, static_cast<GLsizei>(width), static_cast<GLsizei>(height), GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
col_tex->set_param(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
col_tex->set_param(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// Depth & stencil
|
||||
|
||||
auto depth_tex = texture::make();
|
||||
depth_tex->upload(GL_DEPTH24_STENCIL8,
|
||||
static_cast<GLsizei>(width),
|
||||
static_cast<GLsizei>(height),
|
||||
GL_DEPTH_STENCIL,
|
||||
GL_UNSIGNED_INT_24_8,
|
||||
nullptr);
|
||||
depth_tex->set_param(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
depth_tex->set_param(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
fb->attach_colour(std::move(col_tex));
|
||||
fb->attach_depth(std::move(depth_tex));
|
||||
|
||||
fb->bind();
|
||||
const auto fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return fb;
|
||||
}
|
||||
|
||||
void framebuffer::destroy() noexcept {
|
||||
if (m_id) {
|
||||
glDeleteFramebuffers(1, &m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void framebuffer::bind() const noexcept {
|
||||
return glBindFramebuffer(GL_FRAMEBUFFER, m_id);
|
||||
}
|
||||
|
||||
void framebuffer::resize(std::uint32_t width, std::uint32_t height) noexcept {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
for (const auto& col_tex : m_colour_texs) {
|
||||
if (col_tex == nullptr)
|
||||
continue;
|
||||
|
||||
col_tex->resize(width, height);
|
||||
}
|
||||
|
||||
if (m_depth_tex) {
|
||||
m_depth_tex->resize(width, height);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void framebuffer::attach_colour(texture::s_ptr&& tex, std::uint32_t attachment_point) noexcept {
|
||||
KUIPER_ASSERT(attachment_point < framebuffer::max_colour_attachments);
|
||||
|
||||
m_colour_texs[attachment_point] = std::move(tex);
|
||||
const auto& size = m_colour_texs[attachment_point]->dimensions();
|
||||
m_width = size.x;
|
||||
m_height = size.y;
|
||||
|
||||
bind();
|
||||
return glFramebufferTexture2D(GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0 + attachment_point,
|
||||
GL_TEXTURE_2D,
|
||||
m_colour_texs[attachment_point]->id(),
|
||||
0);
|
||||
}
|
||||
|
||||
void framebuffer::attach_depth(texture::s_ptr&& tex) noexcept {
|
||||
m_depth_tex = std::move(tex);
|
||||
const auto& size = m_depth_tex->dimensions();
|
||||
m_width = size.x;
|
||||
m_height = size.y;
|
||||
|
||||
bind();
|
||||
return glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_depth_tex->id(), 0);
|
||||
}
|
149
src/graphics/opengl/program.cpp
Normal file
149
src/graphics/opengl/program.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
#include "graphics/opengl/program.hpp"
|
||||
|
||||
#include "glm/ext/vector_uint3.hpp"
|
||||
#include "graphics/opengl/shader.hpp"
|
||||
#include "logging/engine_logger.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
program::s_ptr program::make() noexcept {
|
||||
GLuint name = glCreateProgram();
|
||||
|
||||
return std::make_shared<program>(name);
|
||||
}
|
||||
|
||||
program::s_ptr program::from_shaders(std::initializer_list<shader::s_ptr> shaders) {
|
||||
auto prog = program::make();
|
||||
|
||||
for (const auto& s : shaders) {
|
||||
prog->attach_shader(s);
|
||||
}
|
||||
|
||||
if (!prog->link()) {
|
||||
std::array<char, 0x80> link_err_buf {};
|
||||
GLsizei n_chars = 0;
|
||||
glGetProgramInfoLog(prog->id(), link_err_buf.size() - 1 /* null terminator */, &n_chars, link_err_buf.data());
|
||||
engine_logger::get().error("Failed to link program {}: {}", prog->id(), link_err_buf.data());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return prog;
|
||||
}
|
||||
|
||||
void program::destroy() noexcept {
|
||||
if (m_id) {
|
||||
glDeleteProgram(m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::use() const noexcept {
|
||||
return glUseProgram(m_id);
|
||||
}
|
||||
|
||||
void program::attach_shader(const shader::s_ptr& shader) noexcept {
|
||||
return glAttachShader(m_id, shader->id());
|
||||
}
|
||||
|
||||
bool program::link() noexcept {
|
||||
glLinkProgram(m_id);
|
||||
|
||||
GLint link_status = GL_FALSE;
|
||||
glGetProgramiv(m_id, GL_LINK_STATUS, &link_status);
|
||||
|
||||
return link_status == GL_TRUE;
|
||||
}
|
||||
|
||||
// Uniforms
|
||||
|
||||
GLint program::uniform_location(const char* name) const noexcept {
|
||||
const auto loc = glGetUniformLocation(m_id, name);
|
||||
|
||||
if (loc < 0)
|
||||
engine_logger::get().error("glGetUniformLocation failed for uniform with name: \"{}\"", name);
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
void program::set_uint(const char* uniform, std::uint32_t value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform1ui(loc, value);
|
||||
}
|
||||
|
||||
void program::set_int(const char* uniform, std::int32_t value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform1i(loc, value);
|
||||
}
|
||||
|
||||
void program::set_int_array(const char* uniform, const std::int32_t* value_arr, std::size_t len) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform1iv(loc, static_cast<GLsizei>(len), value_arr);
|
||||
}
|
||||
|
||||
void program::set_float(const char* uniform, float value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform1f(loc, value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::set_vec3(const char* uniform, const glm::vec3& value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform3f(loc, value.x, value.y, value.z);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::set_uvec3(const char* uniform, const glm::uvec3& value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform3ui(loc, value.x, value.y, value.z);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::set_uvec2(const char* uniform, const glm::uvec2& value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniform2ui(loc, value.x, value.y);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::set_mat3(const char* uniform, const glm::mat3& value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniformMatrix3fv(loc, 1, GL_FALSE, glm::value_ptr(value));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void program::set_mat4(const char* uniform, const glm::mat4& value) noexcept {
|
||||
const auto loc = uniform_location(uniform);
|
||||
|
||||
use();
|
||||
glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(value));
|
||||
|
||||
return;
|
||||
}
|
94
src/graphics/opengl/render_context.cpp
Normal file
94
src/graphics/opengl/render_context.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
#include "graphics/opengl/render_context.hpp"
|
||||
|
||||
#include "logging/engine_logger.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
using namespace kuiper;
|
||||
using namespace kuiper::gl;
|
||||
|
||||
void gl::initialise() {
|
||||
int version = gladLoadGL(glfwGetProcAddress);
|
||||
if (!version) {
|
||||
throw std::runtime_error("Failed to load & resolve OpenGL symbols with GLAD");
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
#if !defined(NDEBUG)
|
||||
GLint flags = 0;
|
||||
glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
|
||||
|
||||
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) {
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(gl::debug_callback, nullptr);
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto logger = engine_logger::get();
|
||||
logger.debug("OpenGL version information:");
|
||||
logger.debug("\tVendor: {}", (const char*) glGetString(GL_VENDOR));
|
||||
logger.debug("\tRenderer: {}", (const char*) glGetString(GL_RENDERER));
|
||||
logger.debug("\tVersion: {}", (const char*) glGetString(GL_VERSION));
|
||||
logger.debug("\tGLSL version: {}", (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
dump_hardware_info();
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void gl::dump_hardware_info() {
|
||||
auto& logger = engine_logger::get();
|
||||
|
||||
logger.debug("OpenGL driver information:");
|
||||
logger.debug("\tGL_MAX_FRAGMENT_UNIFORM_BLOCKS: {}", render_context::get<GLint>(GL_MAX_FRAGMENT_UNIFORM_BLOCKS));
|
||||
logger.debug("\tGL_MAX_UNIFORM_BLOCK_SIZE: {}", render_context::get<GLint>(GL_MAX_UNIFORM_BLOCK_SIZE));
|
||||
logger.debug("\tGL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: {}",
|
||||
render_context::get<GLint>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT));
|
||||
logger.debug("\tGL_MAX_COLOR_ATTACHMENTS: {}", render_context::get<GLint>(GL_MAX_COLOR_ATTACHMENTS));
|
||||
logger.debug("\tGL_MAX_DRAW_BUFFERS: {}", render_context::get<GLint>(GL_MAX_DRAW_BUFFERS));
|
||||
logger.debug("\tGL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: {}",
|
||||
render_context::get<GLint>(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS));
|
||||
logger.debug("\tGL_MAX_TEXTURE_IMAGE_UNITS: {}", render_context::get<GLint>(GL_MAX_TEXTURE_IMAGE_UNITS));
|
||||
logger.debug("\tGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: {}",
|
||||
render_context::get<GLint>(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
void gl::debug_callback(GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
GLenum severity,
|
||||
GLsizei length,
|
||||
const GLchar* message,
|
||||
const void* userParam) noexcept {
|
||||
auto logger = engine_logger::get();
|
||||
logger.error("OpenGL error: {}", message);
|
||||
}
|
||||
#endif
|
||||
|
||||
void render_context::bind_framebuffer(const framebuffer::s_ptr& fb) noexcept {
|
||||
return fb->bind();
|
||||
}
|
||||
|
||||
void render_context::bind_default_framebuffer() noexcept {
|
||||
return glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void render_context::set_viewport_size(std::uint32_t width, std::uint32_t height) noexcept {
|
||||
return glViewport(0, 0, static_cast<GLint>(width), static_cast<GLint>(height));
|
||||
}
|
||||
|
||||
void render_context::enable_feature(GLenum feature) noexcept {
|
||||
return glEnable(feature);
|
||||
}
|
||||
|
||||
void render_context::disable_feature(GLenum feature) noexcept {
|
||||
return glDisable(feature);
|
||||
}
|
91
src/graphics/opengl/shader.cpp
Normal file
91
src/graphics/opengl/shader.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include "graphics/opengl/shader.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
shader::s_ptr shader::make(shader_type type) noexcept {
|
||||
|
||||
GLenum gl_type = 0;
|
||||
switch (type) {
|
||||
case shader_type::vertex:
|
||||
gl_type = GL_VERTEX_SHADER;
|
||||
break;
|
||||
case shader_type::fragment:
|
||||
gl_type = GL_FRAGMENT_SHADER;
|
||||
break;
|
||||
case shader_type::compute:
|
||||
gl_type = GL_COMPUTE_SHADER;
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GLuint name = glCreateShader(gl_type);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<shader>(name, gl_type);
|
||||
}
|
||||
|
||||
shader::s_ptr shader::from_path(const std::filesystem::path& shader_path, shader_type type) noexcept {
|
||||
if (shader_path.empty() || !std::filesystem::is_regular_file(shader_path) || !std::filesystem::exists(shader_path))
|
||||
return nullptr;
|
||||
|
||||
const auto file_size = std::filesystem::file_size(shader_path);
|
||||
std::vector<char> read_buf(file_size + 1); // +1 for NUL terminator
|
||||
std::ifstream in_file(shader_path); // TODO: std::ios::ate & tellg() trick
|
||||
|
||||
if (!in_file.is_open())
|
||||
return nullptr;
|
||||
|
||||
in_file.read(read_buf.data(), file_size);
|
||||
|
||||
std::string_view file_ext(shader_path.extension().c_str());
|
||||
|
||||
if (type == shader_type::none) {
|
||||
if (file_ext.compare(".vs") == 0 || file_ext.compare(".vert") == 0) {
|
||||
type = shader_type::vertex;
|
||||
} else if (file_ext.compare(".fs") == 0 || file_ext.compare(".frag") == 0) {
|
||||
type = shader_type::fragment;
|
||||
} else if (file_ext.compare(".cs") == 0 || file_ext.compare(".comp") == 0) {
|
||||
type = shader_type::compute;
|
||||
}
|
||||
}
|
||||
|
||||
return shader::from_source(read_buf.data(), type);
|
||||
}
|
||||
|
||||
shader::s_ptr shader::from_source(std::string_view source, shader_type type) noexcept {
|
||||
auto shader = shader::make(type);
|
||||
shader->attach_source(source);
|
||||
shader->compile();
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
void shader::destroy() noexcept {
|
||||
if (m_id) {
|
||||
glDeleteShader(m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void shader::attach_source(std::string_view source) noexcept {
|
||||
const GLint n_shader_sources = 1;
|
||||
const char* const s = source.data();
|
||||
const GLint s_len = static_cast<GLint>(source.size());
|
||||
|
||||
return glShaderSource(m_id, n_shader_sources, &s, &s_len);
|
||||
}
|
||||
|
||||
void shader::compile() noexcept {
|
||||
|
||||
return glCompileShader(m_id);
|
||||
}
|
136
src/graphics/opengl/texture.cpp
Normal file
136
src/graphics/opengl/texture.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#include "graphics/opengl/texture.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <stb_image.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
texture::s_ptr texture::make(texture_type type) noexcept {
|
||||
GLuint name = 0;
|
||||
|
||||
glGenTextures(1, &name);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
GLenum gl_target = 0;
|
||||
switch (type) {
|
||||
case texture_type::tex_2d:
|
||||
gl_target = GL_TEXTURE_2D;
|
||||
break;
|
||||
case texture_type::none:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_shared<texture>(name, gl_target);
|
||||
}
|
||||
|
||||
texture::s_ptr texture::make_placeholder() noexcept {
|
||||
// FIXME: this might be cooked because it's endian-dependent
|
||||
static constexpr std::array<std::uint32_t, 16> data {0xff00ffff,
|
||||
0xff00ffff,
|
||||
0x000000ff,
|
||||
0x000000ff,
|
||||
0xff00ffff,
|
||||
0xff00ffff,
|
||||
0x000000ff,
|
||||
0x000000ff,
|
||||
0x000000ff,
|
||||
0x000000ff,
|
||||
0xff00ffff,
|
||||
0xff00ffff,
|
||||
0x000000ff,
|
||||
0x000000ff,
|
||||
0xff00ffff,
|
||||
0xff00ffff};
|
||||
|
||||
auto tex = texture::make();
|
||||
tex->upload(GL_RGBA, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
|
||||
tex->gen_mipmaps();
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
texture::s_ptr texture::from_image_path(const std::filesystem::path& image_path, bool flip_y) noexcept {
|
||||
if (image_path.empty() || !std::filesystem::is_regular_file(image_path) || !std::filesystem::exists(image_path))
|
||||
return nullptr;
|
||||
|
||||
const auto file_size = std::filesystem::file_size(image_path);
|
||||
std::vector<std::uint8_t> read_buf(file_size);
|
||||
std::ifstream in_file(image_path, std::ios::binary); // TODO: std::ios::ate & tellg() trick
|
||||
|
||||
if (!in_file.is_open())
|
||||
return nullptr;
|
||||
|
||||
in_file.read((char*) read_buf.data(), file_size);
|
||||
|
||||
return texture::from_image_data(read_buf.data(), read_buf.size(), flip_y);
|
||||
}
|
||||
|
||||
texture::s_ptr texture::from_image_data(const std::uint8_t* buffer, std::size_t length, bool flip_y) noexcept {
|
||||
if (buffer == nullptr || length == 0)
|
||||
return nullptr;
|
||||
|
||||
stbi_set_flip_vertically_on_load(flip_y);
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int channels_in_file = 0;
|
||||
std::uint8_t* image_data =
|
||||
stbi_load_from_memory(buffer, static_cast<int>(length), &width, &height, &channels_in_file, 4);
|
||||
|
||||
if (!image_data)
|
||||
return nullptr;
|
||||
|
||||
auto tex = texture::make();
|
||||
tex->upload(GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
|
||||
|
||||
stbi_image_free(image_data);
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
void texture::destroy() noexcept {
|
||||
if (m_id) {
|
||||
glDeleteTextures(1, &m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void texture::bind() noexcept {
|
||||
return glBindTexture(m_target, m_id);
|
||||
}
|
||||
|
||||
void texture::upload(
|
||||
GLint internal_fmt, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* data) noexcept {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_internal_fmt = internal_fmt;
|
||||
m_pixel_fmt = format;
|
||||
m_pixel_type = type;
|
||||
|
||||
bind();
|
||||
return glTexImage2D(m_target, 0, internal_fmt, width, height, 0, format, type, data);
|
||||
}
|
||||
|
||||
void texture::resize(std::uint32_t width, std::uint32_t height) noexcept {
|
||||
return upload(
|
||||
m_internal_fmt, static_cast<GLsizei>(width), static_cast<GLsizei>(height), m_pixel_fmt, m_pixel_type, nullptr);
|
||||
}
|
||||
|
||||
void texture::set_param(GLenum name, GLint param) noexcept {
|
||||
bind();
|
||||
|
||||
return glTexParameteri(m_target, name, param);
|
||||
}
|
||||
|
||||
void texture::gen_mipmaps() noexcept {
|
||||
bind();
|
||||
|
||||
return glGenerateMipmap(m_target);
|
||||
}
|
80
src/graphics/opengl/texture_array.cpp
Normal file
80
src/graphics/opengl/texture_array.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include "graphics/opengl/texture_array.hpp"
|
||||
#include "graphics/opengl/texture.hpp"
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
texture_array::s_ptr texture_array::make(GLenum target) noexcept {
|
||||
GLuint name = 0;
|
||||
|
||||
glCreateTextures(target, 1, &name);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<texture_array>(name, target);
|
||||
}
|
||||
|
||||
void texture_array::destroy() noexcept {
|
||||
if (m_id) {
|
||||
glDeleteTextures(1, &m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void texture_array::bind(std::uint32_t unit) const noexcept {
|
||||
return glBindTextureUnit(unit, m_id);
|
||||
}
|
||||
|
||||
void texture_array::set_storage(GLenum internal_fmt,
|
||||
std::uint32_t width,
|
||||
std::uint32_t height,
|
||||
std::uint32_t depth) noexcept {
|
||||
constexpr GLsizei mip_levels = 1; // only allocating storage for level 0, call gen_mipmaps()
|
||||
|
||||
glTextureStorage3D(m_id,
|
||||
mip_levels,
|
||||
internal_fmt,
|
||||
static_cast<GLsizei>(width),
|
||||
static_cast<GLsizei>(height),
|
||||
static_cast<GLsizei>(depth));
|
||||
|
||||
m_internal_fmt = internal_fmt;
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_depth = depth;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void texture_array::upload(std::uint32_t x_offset,
|
||||
std::uint32_t y_offset,
|
||||
std::uint32_t z_offset,
|
||||
std::uint32_t width,
|
||||
std::uint32_t height,
|
||||
std::uint32_t depth,
|
||||
GLenum format,
|
||||
GLenum type,
|
||||
const void* data,
|
||||
std::uint32_t mip_level) noexcept {
|
||||
return glTextureSubImage3D(m_id,
|
||||
static_cast<GLint>(mip_level),
|
||||
static_cast<GLint>(x_offset),
|
||||
static_cast<GLint>(y_offset),
|
||||
static_cast<GLint>(z_offset),
|
||||
static_cast<GLsizei>(width),
|
||||
static_cast<GLsizei>(height),
|
||||
static_cast<GLsizei>(depth),
|
||||
format,
|
||||
type,
|
||||
data);
|
||||
}
|
||||
|
||||
void texture_array::gen_mipmaps() noexcept {
|
||||
return glGenerateTextureMipmap(m_id);
|
||||
}
|
||||
|
||||
void texture_array::set_param(GLenum name, GLint param) noexcept {
|
||||
return glTextureParameteri(m_id, name, param);
|
||||
}
|
47
src/graphics/opengl/vertex_array.cpp
Normal file
47
src/graphics/opengl/vertex_array.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "graphics/opengl/vertex_array.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
vertex_array::s_ptr vertex_array::make() noexcept {
|
||||
GLuint name = 0;
|
||||
glGenVertexArrays(1, &name);
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
return std::make_shared<vertex_array>(name);
|
||||
}
|
||||
|
||||
void vertex_array::destroy() noexcept {
|
||||
if (m_id)
|
||||
glDeleteVertexArrays(1, &m_id);
|
||||
|
||||
return;
|
||||
}
|
||||
void vertex_array::bind() const noexcept {
|
||||
return glBindVertexArray(m_id);
|
||||
}
|
||||
|
||||
void vertex_array::enable_attrib(std::uint32_t index) noexcept {
|
||||
bind();
|
||||
return glEnableVertexAttribArray(index);
|
||||
}
|
||||
|
||||
void vertex_array::disable_attrib(std::uint32_t index) noexcept {
|
||||
bind();
|
||||
return glDisableVertexAttribArray(index);
|
||||
}
|
||||
|
||||
void vertex_array::set_binding_divisor(std::uint32_t binding, std::uint32_t divisor) noexcept {
|
||||
bind();
|
||||
return glVertexBindingDivisor(binding, divisor);
|
||||
}
|
||||
|
||||
void vertex_array::set_attrib_divisor(std::uint32_t attrib, std::uint32_t divisor) noexcept {
|
||||
bind();
|
||||
return glVertexAttribDivisor(attrib, divisor);
|
||||
}
|
304
src/platform/glfw/glfw_window.cpp
Normal file
304
src/platform/glfw/glfw_window.cpp
Normal file
@ -0,0 +1,304 @@
|
||||
#include "platform/glfw/glfw_window.hpp"
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include "graphics/opengl/framebuffer.hpp"
|
||||
#include "graphics/opengl/render_context.hpp"
|
||||
|
||||
#include "logging/engine_logger.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
using namespace kuiper;
|
||||
|
||||
glfw_window::glfw_window(
|
||||
std::uint32_t width, std::uint32_t height, std::u8string_view title, GLFWmonitor* monitor, GLFWwindow* share)
|
||||
: m_window_handle(create_glfw_window(width, height, title, monitor, share)),
|
||||
m_default_framebuffer(std::make_shared<gl::framebuffer>(gl::framebuffer(0))) {
|
||||
if (!m_window_handle) {
|
||||
throw std::runtime_error("glfwCreateWindow failed, returned nullptr");
|
||||
}
|
||||
|
||||
// Window resize callback
|
||||
|
||||
glfwSetFramebufferSizeCallback(m_window_handle, glfw_window::glfw_framebuffer_size_callback);
|
||||
glfwSetWindowContentScaleCallback(m_window_handle, glfw_window::glfw_content_scale_callback);
|
||||
|
||||
// Input callbacks
|
||||
|
||||
glfwSetKeyCallback(m_window_handle, glfw_window::glfw_key_event_callback);
|
||||
glfwSetCursorPosCallback(m_window_handle, glfw_window::glfw_cursor_pos_callback);
|
||||
glfwSetScrollCallback(m_window_handle, glfw_window::glfw_scroll_callback);
|
||||
glfwSetDropCallback(m_window_handle, glfw_window::glfw_dragdrop_callback);
|
||||
|
||||
// Make instance of this GLFWWindow available to static callbacks
|
||||
|
||||
glfwSetWindowUserPointer(m_window_handle, this);
|
||||
|
||||
// Initialise OpenGL context
|
||||
|
||||
glfwMakeContextCurrent(m_window_handle);
|
||||
gl::initialise();
|
||||
|
||||
// Fixes framebuffer/viewport not being sized correctly on scalings other than 1.0 x 1.0
|
||||
resize_back_buffer();
|
||||
|
||||
// Enable required OpenGL features
|
||||
|
||||
gl::render_context::enable_feature(GL_DEPTH_TEST); // depth buffer
|
||||
// gl::render_context::disable_feature(GL_STENCIL_TEST); // stencil buffer
|
||||
gl::render_context::enable_feature(GL_CULL_FACE); // cull backfacing triangles
|
||||
gl::render_context::enable_feature(GL_BLEND); // blending
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
glfw_window::~glfw_window() {
|
||||
if (m_window_handle != nullptr) {
|
||||
glfwDestroyWindow(m_window_handle);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void* glfw_window::get_native_handle() const noexcept {
|
||||
return m_window_handle;
|
||||
}
|
||||
|
||||
void glfw_window::resize_back_buffer() noexcept {
|
||||
const auto framebuffer_size = get_framebuffer_size();
|
||||
const auto width = framebuffer_size.x;
|
||||
const auto height = framebuffer_size.y;
|
||||
m_default_framebuffer->resize(width, height);
|
||||
gl::render_context::set_viewport_size(width, height);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void glfw_window::initialise_glfw(bool prefer_x11) {
|
||||
auto logger = engine_logger::get();
|
||||
|
||||
// Check for potential GLFW version mismatches & incompatibilities
|
||||
|
||||
logger.debug("Compiled against GLFW {}.{}.{}", GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION);
|
||||
logger.debug("GLFW version string: {}", glfwGetVersionString());
|
||||
|
||||
int glfw_ver_major = 0, glfw_ver_minor = 0, glfw_ver_patch = 0;
|
||||
glfwGetVersion(&glfw_ver_major, &glfw_ver_minor, &glfw_ver_patch);
|
||||
|
||||
if (glfw_ver_major > GLFW_VERSION_MAJOR || glfw_ver_minor < GLFW_VERSION_MINOR) {
|
||||
logger.warn("Potentially incompatible GLFW loaded: {}.{}.{} (loaded) vs. {}.{}.{} (compiled-for)",
|
||||
glfw_ver_major,
|
||||
glfw_ver_minor,
|
||||
glfw_ver_patch,
|
||||
GLFW_VERSION_MAJOR,
|
||||
GLFW_VERSION_MINOR,
|
||||
GLFW_VERSION_REVISION);
|
||||
}
|
||||
|
||||
// GLFW initialisation
|
||||
|
||||
// Set a callback for logging GLFW errors
|
||||
|
||||
glfwSetErrorCallback(glfw_window::glfw_error_callback);
|
||||
|
||||
// Initialisation hints
|
||||
|
||||
if (prefer_x11) {
|
||||
logger.info("Hinting to GLFW that X11 should be used");
|
||||
glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11);
|
||||
}
|
||||
|
||||
// Disable libdecor because I don't care about it, GTK, GNOME or anything else.
|
||||
// Also it prints out an annoying error in the console when it fails to load it.
|
||||
glfwInitHint(GLFW_WAYLAND_LIBDECOR, GLFW_WAYLAND_DISABLE_LIBDECOR);
|
||||
|
||||
if (!glfwInit()) {
|
||||
throw std::runtime_error("Failed to initialise GLFW");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
GLFWwindow* glfw_window::create_glfw_window(std::uint32_t width,
|
||||
std::uint32_t height,
|
||||
std::u8string_view title,
|
||||
GLFWmonitor* monitor,
|
||||
GLFWwindow* share) noexcept {
|
||||
KUIPER_ASSERT((width < std::numeric_limits<int>::max()) && (height < std::numeric_limits<int>::max()));
|
||||
push_window_hints();
|
||||
|
||||
return glfwCreateWindow(
|
||||
static_cast<int>(width), static_cast<int>(height), reinterpret_cast<const char*>(title.data()), monitor, share);
|
||||
}
|
||||
|
||||
void glfw_window::push_window_hints() noexcept {
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
|
||||
glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool glfw_window::should_close() const noexcept {
|
||||
return glfwWindowShouldClose(m_window_handle);
|
||||
}
|
||||
|
||||
void glfw_window::poll_events() noexcept {
|
||||
const auto old_mouse_pos = m_input_state.get_mouse_pos();
|
||||
|
||||
glfwPollEvents();
|
||||
|
||||
const auto delta = m_input_state.get_mouse_pos() - old_mouse_pos;
|
||||
m_input_state.set_mouse_delta(delta);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void glfw_window::swap_buffers() noexcept {
|
||||
return glfwSwapBuffers(m_window_handle);
|
||||
}
|
||||
|
||||
void glfw_window::set_swap_interval(std::uint32_t interval) noexcept {
|
||||
return glfwSwapInterval(static_cast<int>(interval));
|
||||
}
|
||||
|
||||
void glfw_window::bind_context() noexcept {
|
||||
*(volatile unsigned long long*) nullptr = 0x1337beefcafebabe;
|
||||
}
|
||||
|
||||
gl::framebuffer::s_ptr glfw_window::back_buffer() const noexcept {
|
||||
return m_default_framebuffer;
|
||||
}
|
||||
|
||||
bool glfw_window::is_cursor_locked() const noexcept {
|
||||
return m_cursor_locked;
|
||||
}
|
||||
|
||||
void glfw_window::lock_cursor() noexcept {
|
||||
glfwSetInputMode(m_window_handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
m_cursor_locked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
void glfw_window::unlock_cursor() noexcept {
|
||||
glfwSetInputMode(m_window_handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
m_cursor_locked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
void glfw_window::enable_raw_input() noexcept {
|
||||
if (glfwRawMouseMotionSupported())
|
||||
glfwSetInputMode(m_window_handle, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void glfw_window::disable_raw_input() noexcept {
|
||||
if (glfwRawMouseMotionSupported())
|
||||
glfwSetInputMode(m_window_handle, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
glm::uvec2 glfw_window::get_size() const noexcept {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
glfwGetWindowSize(m_window_handle, &width, &height);
|
||||
|
||||
// I don't understand why the width and height is a signed integer
|
||||
// I'd like to see a window with negative width & height
|
||||
KUIPER_ASSERT((width > 0) && (height > 0));
|
||||
|
||||
return {static_cast<std::uint32_t>(width), static_cast<std::uint32_t>(height)};
|
||||
}
|
||||
|
||||
glm::uvec2 glfw_window::get_framebuffer_size() const noexcept {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
glfwGetFramebufferSize(m_window_handle, &width, &height);
|
||||
|
||||
// Once again: why would the framebuffer ever have negative dimensions?
|
||||
KUIPER_ASSERT((width > 0) && (height > 0));
|
||||
|
||||
return {static_cast<std::uint32_t>(width), static_cast<std::uint32_t>(height)};
|
||||
}
|
||||
|
||||
glm::vec2 glfw_window::get_content_scale() const noexcept {
|
||||
float xscale = 1.0f;
|
||||
float yscale = 1.0f;
|
||||
glfwGetWindowContentScale(m_window_handle, &xscale, &yscale);
|
||||
|
||||
return glm::vec2(xscale, yscale);
|
||||
}
|
||||
|
||||
void glfw_window::set_title(std::u8string_view title) noexcept {
|
||||
return glfwSetWindowTitle(m_window_handle, reinterpret_cast<const char*>(title.data()));
|
||||
}
|
||||
|
||||
bool glfw_window::is_minimised() const noexcept {
|
||||
int iconified = glfwGetWindowAttrib(m_window_handle, GLFW_ICONIFIED);
|
||||
|
||||
return (iconified == GLFW_TRUE);
|
||||
}
|
||||
|
||||
// Callbacks
|
||||
|
||||
void glfw_window::glfw_error_callback(int error_code, const char* description) {
|
||||
engine_logger::get().error("GLFW error {}: {}", error_code, description);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void glfw_window::glfw_framebuffer_size_callback(GLFWwindow* window, int width, int height) {
|
||||
KUIPER_ASSERT((width > 0) && (height > 0));
|
||||
engine_logger::get().debug("Framebuffer size changed: {} x {}", width, height);
|
||||
|
||||
glfw_window* window_instance = static_cast<glfw_window*>(glfwGetWindowUserPointer(window));
|
||||
window_instance->m_default_framebuffer->resize(width, height);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void glfw_window::glfw_content_scale_callback(GLFWwindow* window, float xscale, float yscale) {
|
||||
KUIPER_ASSERT(xscale > 0 && yscale > 0);
|
||||
engine_logger::get().debug("Window content scale changed: x: {}, y: {}", xscale, yscale);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void glfw_window::glfw_key_event_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
|
||||
glfw_window* window_instance = static_cast<glfw_window*>(glfwGetWindowUserPointer(window));
|
||||
window_instance->m_input_state.set_key_state(key, action);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void glfw_window::glfw_cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) {
|
||||
glfw_window* window_instance = static_cast<glfw_window*>(glfwGetWindowUserPointer(window));
|
||||
window_instance->m_input_state.set_mouse_pos({xpos, ypos});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void glfw_window::glfw_scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
|
||||
return;
|
||||
}
|
||||
|
||||
void glfw_window::glfw_dragdrop_callback(GLFWwindow* window, int path_count, const char* paths[]) {
|
||||
return;
|
||||
}
|
423
src/renderer/gl_renderer.cpp
Normal file
423
src/renderer/gl_renderer.cpp
Normal file
@ -0,0 +1,423 @@
|
||||
#include "renderer/gl_renderer.hpp"
|
||||
|
||||
#include "components/camera.hpp"
|
||||
#include "core/io.hpp"
|
||||
#include "graphics/opengl/buffer.hpp"
|
||||
#include "graphics/opengl/render_context.hpp"
|
||||
#include "graphics/opengl/shader.hpp"
|
||||
#include "graphics/opengl/vertex_array.hpp"
|
||||
#include "maths/aspect_ratio.hpp"
|
||||
#include "resources/primitive.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/colours.hpp"
|
||||
|
||||
// These headers are generated using bin2hpp during build
|
||||
#include "clustering_cs.hpp"
|
||||
#include "default_fs.hpp"
|
||||
#include "default_vs.hpp"
|
||||
#include "light_culling_cs.hpp"
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <glm/ext/matrix_clip_space.hpp>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/geometric.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/mat3x3.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/matrix.hpp>
|
||||
#include <glm/trigonometric.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <numeric>
|
||||
#include <string_view>
|
||||
|
||||
using namespace kuiper::gl;
|
||||
|
||||
glm::mat4 camera_to_view_matrix(const glm::vec3& pos, const kuiper::component::camera_props& props) noexcept;
|
||||
|
||||
renderer renderer::init() {
|
||||
gl_state state {};
|
||||
|
||||
// Configuration
|
||||
|
||||
state.max_texture_units = render_context::get<GLint>(GL_MAX_TEXTURE_IMAGE_UNITS);
|
||||
state.texture_unit_slots.resize(state.max_texture_units);
|
||||
std::iota(state.texture_unit_slots.begin(), state.texture_unit_slots.end(), 0);
|
||||
|
||||
// Buffers
|
||||
|
||||
state.vertex_buffer = buffer::make(GL_ARRAY_BUFFER);
|
||||
state.index_buffer = buffer::make(GL_ELEMENT_ARRAY_BUFFER);
|
||||
|
||||
state.vao = vertex_array::make();
|
||||
state.vao->bind();
|
||||
|
||||
constexpr std::uint32_t pos_idx = 0;
|
||||
constexpr std::uint32_t normal_idx = 1;
|
||||
constexpr std::uint32_t tangent_idx = 2;
|
||||
constexpr std::uint32_t uv_idx = 3;
|
||||
constexpr std::uint32_t draw_id_idx = 4;
|
||||
constexpr std::uint32_t model_mat_idx = 5; // mat4 occupies slot 5, 6, 7 & 8
|
||||
constexpr std::uint32_t normal_mat_idx = 9;
|
||||
|
||||
state.vao->enable_attrib(pos_idx);
|
||||
state.vao->enable_attrib(normal_idx);
|
||||
state.vao->enable_attrib(tangent_idx);
|
||||
state.vao->enable_attrib(uv_idx);
|
||||
state.vao->enable_attrib(draw_id_idx);
|
||||
state.vao->enable_attrib(model_mat_idx + 0);
|
||||
state.vao->enable_attrib(model_mat_idx + 1);
|
||||
state.vao->enable_attrib(model_mat_idx + 2);
|
||||
state.vao->enable_attrib(model_mat_idx + 3);
|
||||
state.vao->enable_attrib(normal_mat_idx + 0);
|
||||
state.vao->enable_attrib(normal_mat_idx + 1);
|
||||
state.vao->enable_attrib(normal_mat_idx + 2);
|
||||
|
||||
state.index_buffer->bind();
|
||||
|
||||
state.vertex_buffer->bind();
|
||||
state.vertex_buffer->set_vertex_attrib(
|
||||
pos_idx, glm::vec3::length(), GL_FLOAT, sizeof(renderer::vertex_t), offsetof(renderer::vertex_t, position));
|
||||
state.vertex_buffer->set_vertex_attrib(normal_idx,
|
||||
glm::vec3::length(),
|
||||
GL_FLOAT,
|
||||
sizeof(renderer::vertex_t),
|
||||
offsetof(renderer::vertex_t, normal),
|
||||
true);
|
||||
state.vertex_buffer->set_vertex_attrib(
|
||||
tangent_idx, glm::vec3::length(), GL_FLOAT, sizeof(renderer::vertex_t), offsetof(renderer::vertex_t, tangent));
|
||||
state.vertex_buffer->set_vertex_attrib(
|
||||
uv_idx, glm::vec2::length(), GL_FLOAT, sizeof(renderer::vertex_t), offsetof(renderer::vertex_t, uv), true);
|
||||
|
||||
state.matrix_buffer = buffer::make(GL_ARRAY_BUFFER);
|
||||
state.matrix_buffer->bind();
|
||||
// mat4 is essentially four vec4s
|
||||
state.matrix_buffer->set_vertex_attrib(model_mat_idx + 0,
|
||||
glm::vec4::length(),
|
||||
GL_FLOAT,
|
||||
sizeof(render_matrices),
|
||||
offsetof(render_matrices, model_matrix) + (0 * sizeof(glm::vec4)));
|
||||
state.matrix_buffer->set_vertex_attrib(model_mat_idx + 1,
|
||||
glm::vec4::length(),
|
||||
GL_FLOAT,
|
||||
sizeof(render_matrices),
|
||||
offsetof(render_matrices, model_matrix) + (1 * sizeof(glm::vec4)));
|
||||
state.matrix_buffer->set_vertex_attrib(model_mat_idx + 2,
|
||||
glm::vec4::length(),
|
||||
GL_FLOAT,
|
||||
sizeof(render_matrices),
|
||||
offsetof(render_matrices, model_matrix) + (2 * sizeof(glm::vec4)));
|
||||
state.matrix_buffer->set_vertex_attrib(model_mat_idx + 3,
|
||||
glm::vec4::length(),
|
||||
GL_FLOAT,
|
||||
sizeof(render_matrices),
|
||||
offsetof(render_matrices, model_matrix) + (3 * sizeof(glm::vec4)));
|
||||
state.matrix_buffer->set_vertex_attrib(normal_mat_idx + 0,
|
||||
glm::vec3::length(),
|
||||
GL_FLOAT,
|
||||
sizeof(render_matrices),
|
||||
offsetof(render_matrices, normal_matrix) + (0 * sizeof(glm::vec3)));
|
||||
state.matrix_buffer->set_vertex_attrib(normal_mat_idx + 1,
|
||||
glm::vec3::length(),
|
||||
GL_FLOAT,
|
||||
sizeof(render_matrices),
|
||||
offsetof(render_matrices, normal_matrix) + (1 * sizeof(glm::vec3)));
|
||||
state.matrix_buffer->set_vertex_attrib(normal_mat_idx + 2,
|
||||
glm::vec3::length(),
|
||||
GL_FLOAT,
|
||||
sizeof(render_matrices),
|
||||
offsetof(render_matrices, normal_matrix) + (2 * sizeof(glm::vec3)));
|
||||
|
||||
state.indirect_buffer = buffer::make(GL_DRAW_INDIRECT_BUFFER);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, state.indirect_buffer->id());
|
||||
glVertexAttribIPointer(draw_id_idx,
|
||||
1,
|
||||
GL_UNSIGNED_INT,
|
||||
sizeof(draw_elements_indirect_cmd),
|
||||
reinterpret_cast<const void*>(offsetof(draw_elements_indirect_cmd, base_instance)));
|
||||
|
||||
state.vao->set_attrib_divisor(pos_idx, 0); // every fragment
|
||||
state.vao->set_attrib_divisor(normal_idx, 0);
|
||||
state.vao->set_attrib_divisor(tangent_idx, 0);
|
||||
state.vao->set_attrib_divisor(uv_idx, 0);
|
||||
state.vao->set_attrib_divisor(draw_id_idx, 1); // every instance
|
||||
state.vao->set_attrib_divisor(model_mat_idx + 0, 1);
|
||||
state.vao->set_attrib_divisor(model_mat_idx + 1, 1);
|
||||
state.vao->set_attrib_divisor(model_mat_idx + 2, 1);
|
||||
state.vao->set_attrib_divisor(model_mat_idx + 3, 1);
|
||||
state.vao->set_attrib_divisor(normal_mat_idx + 0, 1);
|
||||
state.vao->set_attrib_divisor(normal_mat_idx + 1, 1);
|
||||
state.vao->set_attrib_divisor(normal_mat_idx + 2, 1);
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
state.cluster_buffer = buffer::make(GL_SHADER_STORAGE_BUFFER);
|
||||
state.cluster_buffer->upload(GL_STATIC_COPY, NUM_CLUSTERS * sizeof(cluster), nullptr);
|
||||
|
||||
state.lights_buffer = buffer::make(GL_SHADER_STORAGE_BUFFER);
|
||||
|
||||
// Shaders
|
||||
|
||||
auto vertex = shader::from_source(bin::default_vs, shader_type::vertex);
|
||||
|
||||
// We pre-process the fragment shader to set the correct number of texture unit slots on the system
|
||||
std::string fragment_src(bin::default_fs);
|
||||
constexpr std::string_view fs_search_and_replace {"const uint MAX_FRAG_TEX_UNITS = 32u;"};
|
||||
fragment_src.replace(fragment_src.find(fs_search_and_replace),
|
||||
fs_search_and_replace.length(),
|
||||
std::format("const uint MAX_FRAG_TEX_UNITS = {}u;", state.max_texture_units));
|
||||
auto fragment = shader::from_source(fragment_src, shader_type::fragment);
|
||||
|
||||
state.pbr_shader = program::from_shaders({vertex, fragment});
|
||||
vertex->destroy();
|
||||
fragment->destroy();
|
||||
|
||||
auto clustering_shader = shader::from_source(bin::clustering_cs, shader_type::compute);
|
||||
state.clustering_shader = program::from_shaders({clustering_shader});
|
||||
clustering_shader->destroy();
|
||||
|
||||
auto light_culling_shader = shader::from_source(bin::light_culling_cs, shader_type::compute);
|
||||
state.light_culling_shader = program::from_shaders({light_culling_shader});
|
||||
light_culling_shader->destroy();
|
||||
|
||||
return {std::move(state)};
|
||||
}
|
||||
|
||||
void renderer::render(const framebuffer::s_ptr& framebuffer,
|
||||
const glm::vec3& cam_pos,
|
||||
const component::camera_props& cam_props) noexcept {
|
||||
update_and_upload();
|
||||
|
||||
// Framebuffer
|
||||
|
||||
const auto& framebuffer_size = framebuffer->dimensions();
|
||||
float fb_x = static_cast<float>(framebuffer_size.x);
|
||||
float fb_y = static_cast<float>(framebuffer_size.y);
|
||||
|
||||
framebuffer->bind();
|
||||
render_context::set_viewport_size(framebuffer_size.x, framebuffer_size.y);
|
||||
|
||||
glClearColor(colours::black.r, colours::black.g, colours::black.b, colours::black.a);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// View & projection matrices
|
||||
|
||||
const auto view_matrix = camera_to_view_matrix(cam_pos, cam_props);
|
||||
const auto projection_matrix =
|
||||
glm::perspective(cam_props.fov, maths::aspect_ratio<float>(fb_x, fb_y), NEAR_Z_DEPTH, FAR_Z_DEPTH);
|
||||
|
||||
// Compute shaders: clustering & light culling
|
||||
|
||||
constexpr glm::uvec3 grid_size_vec {NUM_CLUSTERS_X, NUM_CLUSTERS_Y, NUM_CLUSTERS_Z};
|
||||
|
||||
m_state.clustering_shader->use();
|
||||
m_state.clustering_shader->set_float("z_near", NEAR_Z_DEPTH);
|
||||
m_state.clustering_shader->set_float("z_far", FAR_Z_DEPTH);
|
||||
m_state.clustering_shader->set_mat4("inverse_proj_mat", glm::inverse(projection_matrix));
|
||||
m_state.clustering_shader->set_uvec3("grid_size", grid_size_vec);
|
||||
m_state.clustering_shader->set_uvec2("screen_dimensions", framebuffer_size);
|
||||
|
||||
m_state.cluster_buffer->bind_to_location(1);
|
||||
glDispatchCompute(NUM_CLUSTERS_X, NUM_CLUSTERS_Y, NUM_CLUSTERS_Z);
|
||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
|
||||
m_state.light_culling_shader->use();
|
||||
m_state.light_culling_shader->set_mat4("view_mat", view_matrix);
|
||||
|
||||
m_state.cluster_buffer->bind_to_location(1);
|
||||
m_state.lights_buffer->bind_to_location(2);
|
||||
|
||||
glDispatchCompute(NUM_CLUSTERS / LOCAL_SIZE, 1, 1);
|
||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
|
||||
// Geometry & shading
|
||||
|
||||
m_state.vao->bind();
|
||||
|
||||
m_state.pbr_shader->use();
|
||||
m_state.pbr_shader->set_mat4("view_mat", view_matrix);
|
||||
m_state.pbr_shader->set_mat4("view_proj_mat", projection_matrix * view_matrix);
|
||||
|
||||
m_state.pbr_shader->set_float("z_near", NEAR_Z_DEPTH);
|
||||
m_state.pbr_shader->set_float("z_far", FAR_Z_DEPTH);
|
||||
m_state.pbr_shader->set_uvec3("grid_size", grid_size_vec);
|
||||
m_state.pbr_shader->set_uvec2("screen_dimensions", framebuffer_size);
|
||||
m_state.pbr_shader->set_vec3("view_pos", cam_pos);
|
||||
|
||||
m_state.pbr_shader->set_int_array(
|
||||
"packed_tex", m_state.texture_unit_slots.data(), m_state.texture_unit_slots.size());
|
||||
|
||||
for (std::uint32_t draw_count = 0; draw_count < m_draw_cmds.size(); draw_count += m_state.max_texture_units) {
|
||||
for (std::uint32_t i = 0; i < m_state.max_texture_units && i + draw_count < m_materials.size(); ++i) {
|
||||
m_materials[i + draw_count].textures->bind(i);
|
||||
}
|
||||
|
||||
glMultiDrawElementsIndirect(
|
||||
GL_TRIANGLES,
|
||||
GL_UNSIGNED_INT,
|
||||
reinterpret_cast<const void*>(draw_count * sizeof(draw_elements_indirect_cmd)),
|
||||
static_cast<GLsizei>(
|
||||
std::min(static_cast<std::uint32_t>(m_draw_cmds.size() - draw_count), m_state.max_texture_units)),
|
||||
sizeof(draw_elements_indirect_cmd));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void renderer::flush() noexcept {
|
||||
m_draw_cmds.clear();
|
||||
m_matrices.clear();
|
||||
m_vertices.clear();
|
||||
m_indices.clear();
|
||||
m_materials.clear();
|
||||
m_lights.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
renderer_metrics renderer::metrics() const noexcept {
|
||||
renderer_metrics metrics {};
|
||||
|
||||
metrics.n_vertices = m_vertices.size();
|
||||
metrics.n_indices = m_indices.size();
|
||||
metrics.n_draw_cmds = m_draw_cmds.size();
|
||||
metrics.n_matrices = m_matrices.size();
|
||||
metrics.n_materials = m_materials.size();
|
||||
metrics.n_lights = m_lights.size();
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
void renderer::update_and_upload() noexcept {
|
||||
const auto vertices_size_bytes = m_vertices.size() * sizeof(vertex);
|
||||
const auto indices_size_bytes = m_indices.size() * sizeof(renderer::index_t);
|
||||
const auto lights_size_bytes = m_lights.size() * sizeof(renderer::point_light);
|
||||
const auto indirect_cmds_size_bytes = m_draw_cmds.size() * sizeof(draw_elements_indirect_cmd);
|
||||
const auto matrices_size_bytes = m_matrices.size() * sizeof(render_matrices);
|
||||
|
||||
// NOTE: assuming the use of mutable buffers here
|
||||
|
||||
if (m_vertices.size() > m_state.vertex_buffer->size() / sizeof(vertex)) {
|
||||
m_state.vertex_buffer->upload(GL_STREAM_DRAW, vertices_size_bytes, m_vertices.data());
|
||||
} else {
|
||||
m_state.vertex_buffer->update(0, vertices_size_bytes, m_vertices.data());
|
||||
}
|
||||
|
||||
if (m_indices.size() > m_state.index_buffer->size() / sizeof(renderer::index_t)) {
|
||||
m_state.index_buffer->upload(GL_STREAM_DRAW, indices_size_bytes, m_indices.data());
|
||||
} else {
|
||||
m_state.index_buffer->update(0, indices_size_bytes, m_indices.data());
|
||||
}
|
||||
|
||||
if (m_lights.size() > m_state.lights_buffer->size() / sizeof(renderer::point_light)) {
|
||||
m_state.lights_buffer->upload(GL_STREAM_DRAW, lights_size_bytes, m_lights.data());
|
||||
} else {
|
||||
m_state.lights_buffer->update(0, lights_size_bytes, m_lights.data());
|
||||
}
|
||||
|
||||
if (m_draw_cmds.size() > m_state.indirect_buffer->size() / sizeof(draw_elements_indirect_cmd)) {
|
||||
m_state.indirect_buffer->upload(GL_STREAM_DRAW, indirect_cmds_size_bytes, m_draw_cmds.data());
|
||||
} else {
|
||||
m_state.indirect_buffer->update(0, indirect_cmds_size_bytes, m_draw_cmds.data());
|
||||
}
|
||||
|
||||
if (m_matrices.size() > m_state.matrix_buffer->size() / sizeof(render_matrices)) {
|
||||
m_state.matrix_buffer->upload(GL_STREAM_DRAW, matrices_size_bytes, m_matrices.data());
|
||||
} else {
|
||||
m_state.matrix_buffer->update(0, matrices_size_bytes, m_matrices.data());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void renderer::draw_model(const resource::model& model, const maths::transform_euler& transform) {
|
||||
const auto transform_matrix = transform.to_mat4();
|
||||
|
||||
for (const auto& mesh : model.meshes()) {
|
||||
draw_mesh(mesh, model.materials(), transform_matrix);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void renderer::draw_mesh(const resource::mesh& mesh,
|
||||
const std::vector<resource::material>& materials,
|
||||
const glm::mat4& transform) {
|
||||
// From the glTF 2.0 spec:
|
||||
// "When the scale is zero on all three axes [...] implementations are free
|
||||
// to optimize away rendering of the node's mesh and all of the node's
|
||||
// children's meshes"
|
||||
// TODO: optimise for this ^
|
||||
|
||||
// The `transform` parameter is the parent mesh node's local transformation matrix
|
||||
|
||||
const auto rel_mat = transform * mesh.transform();
|
||||
|
||||
for (const auto& prim : mesh.primitives()) {
|
||||
draw_primitive(prim, materials[prim.material_idx], rel_mat);
|
||||
}
|
||||
|
||||
for (const auto& submesh : mesh.submeshes()) {
|
||||
draw_mesh(submesh, materials, rel_mat);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void renderer::draw_primitive(const resource::primitive& primitive,
|
||||
const resource::material& material,
|
||||
const maths::transform_euler& transform) {
|
||||
return draw_primitive(primitive, material, transform.to_mat4());
|
||||
}
|
||||
|
||||
void renderer::draw_primitive(const resource::primitive& primitive,
|
||||
const resource::material& material,
|
||||
const glm::mat4& transform) {
|
||||
const std::uint32_t n_vertices = primitive.vertices.size();
|
||||
const std::uint32_t n_indices = primitive.indices.size();
|
||||
|
||||
const std::uint32_t vtx_start =
|
||||
static_cast<std::uint32_t>(std::distance(std::begin(m_vertices), std::end(m_vertices)));
|
||||
const std::uint32_t idx_start =
|
||||
static_cast<std::uint32_t>(std::distance(std::begin(m_indices), std::end(m_indices)));
|
||||
|
||||
KUIPER_ASSERT(vtx_start <= (std::numeric_limits<std::uint32_t>::max() - n_vertices));
|
||||
KUIPER_ASSERT(idx_start <= (std::numeric_limits<std::uint32_t>::max() - n_indices));
|
||||
|
||||
draw_elements_indirect_cmd cmd {};
|
||||
cmd.count = n_indices;
|
||||
cmd.instance_count = 1;
|
||||
cmd.first_index = idx_start;
|
||||
cmd.base_vertex = vtx_start;
|
||||
cmd.base_instance = m_draw_cmds.size();
|
||||
|
||||
m_vertices.insert(std::end(m_vertices), std::begin(primitive.vertices), std::end(primitive.vertices));
|
||||
m_indices.insert(std::end(m_indices), std::begin(primitive.indices), std::end(primitive.indices));
|
||||
|
||||
m_matrices.push_back({transform, glm::transpose(glm::inverse(glm::mat3(transform)))});
|
||||
m_draw_cmds.push_back(cmd);
|
||||
m_materials.push_back(material);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void renderer::add_light(const glm::vec3& pos, const component::point_light& light) noexcept {
|
||||
m_lights.emplace_back(glm::make_vec4(pos), glm::make_vec4(light.colour), light.intensity, 50.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
glm::mat4 camera_to_view_matrix(const glm::vec3& pos, const kuiper::component::camera_props& props) noexcept {
|
||||
static constexpr glm::vec3 up_unit_vec {0.0f, 1.0f, 0.0f};
|
||||
|
||||
const glm::vec3 direction = {glm::cos(props.euler.y) * glm::cos(props.euler.x),
|
||||
glm::sin(props.euler.x),
|
||||
glm::sin(props.euler.y) * glm::cos(props.euler.x)};
|
||||
const glm::vec3 cam_front = glm::normalize(direction);
|
||||
|
||||
return glm::lookAt(pos, pos + cam_front, up_unit_vec);
|
||||
}
|
102
src/resources/image.cpp
Normal file
102
src/resources/image.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
#include "resources/image.hpp"
|
||||
|
||||
#include "utils/assert.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize2.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
using namespace kuiper::resource;
|
||||
|
||||
image image::make_blank(std::uint32_t width, std::uint32_t height, std::uint32_t n_channels) noexcept {
|
||||
const std::size_t size = width * height * n_channels;
|
||||
const std::vector<std::uint8_t> pixel_data(size, 0u);
|
||||
|
||||
return image {width, height, n_channels, pixel_data.data(), pixel_data.size()};
|
||||
}
|
||||
|
||||
std::expected<image, image_error> image::from_path(const std::filesystem::path& path,
|
||||
std::uint8_t num_channels) noexcept {
|
||||
if (path.empty() || !std::filesystem::is_regular_file(path) || !std::filesystem::exists(path))
|
||||
return std::unexpected(image_error {"invalid path"});
|
||||
|
||||
auto input_file = std::ifstream(path, std::ios::binary | std::ios::ate);
|
||||
const auto file_size = input_file.tellg();
|
||||
|
||||
std::vector<std::uint8_t> buf(file_size);
|
||||
input_file.read((char*) buf.data(), file_size);
|
||||
input_file.close();
|
||||
|
||||
return image::from_memory(buf.data(), buf.size(), num_channels);
|
||||
}
|
||||
|
||||
std::expected<image, image_error> image::from_memory(const std::uint8_t* data,
|
||||
std::size_t len,
|
||||
std::uint8_t num_channels) noexcept {
|
||||
stbi_set_flip_vertically_on_load(false);
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int n_channels = 0;
|
||||
|
||||
auto decoded_image_data =
|
||||
stbi_load_from_memory(data, static_cast<int>(len), &width, &height, &n_channels, num_channels);
|
||||
|
||||
if (!decoded_image_data)
|
||||
return std::unexpected(image_error {stbi_failure_reason()});
|
||||
|
||||
image ret {static_cast<std::uint32_t>(width),
|
||||
static_cast<std::uint32_t>(height),
|
||||
static_cast<std::uint32_t>(n_channels),
|
||||
decoded_image_data,
|
||||
static_cast<std::uint32_t>(width * height * num_channels)};
|
||||
|
||||
stbi_image_free(decoded_image_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void image::resize(std::uint32_t width, std::uint32_t height) noexcept {
|
||||
std::vector<std::uint8_t> out_buf(width * height * m_channels);
|
||||
|
||||
stbir_pixel_layout pixel_layout {STBIR_RGBA};
|
||||
|
||||
switch (m_channels) {
|
||||
case 4:
|
||||
pixel_layout = stbir_pixel_layout::STBIR_RGBA;
|
||||
break;
|
||||
case 3:
|
||||
pixel_layout = stbir_pixel_layout::STBIR_RGB;
|
||||
break;
|
||||
case 2:
|
||||
pixel_layout = stbir_pixel_layout::STBIR_2CHANNEL;
|
||||
break;
|
||||
case 1:
|
||||
pixel_layout = stbir_pixel_layout::STBIR_1CHANNEL;
|
||||
break;
|
||||
default:
|
||||
pixel_layout = stbir_pixel_layout::STBIR_RGBA;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!stbir_resize_uint8_srgb(m_data.data(),
|
||||
static_cast<int>(m_width),
|
||||
static_cast<int>(m_height),
|
||||
0, // packed in memory
|
||||
out_buf.data(),
|
||||
static_cast<int>(width),
|
||||
static_cast<int>(height),
|
||||
0, // packed in memory
|
||||
pixel_layout))
|
||||
KUIPER_ASSERT(false);
|
||||
|
||||
m_data = out_buf;
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
return;
|
||||
}
|
390
src/resources/model.cpp
Normal file
390
src/resources/model.cpp
Normal file
@ -0,0 +1,390 @@
|
||||
#include "resources/model.hpp"
|
||||
|
||||
#include "errors/errors.hpp"
|
||||
#include "graphics/opengl/texture.hpp"
|
||||
#include "graphics/opengl/texture_array.hpp"
|
||||
#include "logging/engine_logger.hpp"
|
||||
#include "maths/transform.hpp"
|
||||
#include "resources/image.hpp"
|
||||
#include "resources/material.hpp"
|
||||
#include "resources/mesh.hpp"
|
||||
#include "resources/primitive.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
#include "fastgltf/core.hpp"
|
||||
#include "fastgltf/glm_element_traits.hpp"
|
||||
#include "fastgltf/tools.hpp"
|
||||
#include "fastgltf/types.hpp"
|
||||
|
||||
#include <glm/geometric.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <format>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
using namespace kuiper;
|
||||
|
||||
resource::mesh parse_mesh_from_root_node(const fastgltf::Asset& asset, const fastgltf::Node& node);
|
||||
std::vector<resource::material> parse_materials(const fastgltf::Asset& asset);
|
||||
|
||||
std::expected<resource::model, kuiper::error> resource::model::from_gltf_path(const std::filesystem::path& gltf_path) {
|
||||
fastgltf::Parser parser {};
|
||||
return model::from_gltf_path(gltf_path, parser);
|
||||
}
|
||||
|
||||
std::expected<resource::model, kuiper::error> resource::model::from_gltf_path(const std::filesystem::path& gltf_path,
|
||||
fastgltf::Parser& gltf_parser) {
|
||||
if (gltf_path.empty() || !std::filesystem::is_regular_file(gltf_path) || !std::filesystem::exists(gltf_path))
|
||||
return {};
|
||||
|
||||
auto logger = engine_logger::get();
|
||||
|
||||
auto data = fastgltf::GltfDataBuffer::FromPath(gltf_path);
|
||||
if (data.error() != fastgltf::Error::None) {
|
||||
logger.error("Failed to load glTF asset from: {}", gltf_path.filename().c_str());
|
||||
return std::unexpected(error::failed_to_load);
|
||||
}
|
||||
|
||||
const fastgltf::Options fastgltf_opts = fastgltf::Options::None | fastgltf::Options::LoadExternalBuffers |
|
||||
fastgltf::Options::LoadExternalImages |
|
||||
fastgltf::Options::GenerateMeshIndices;
|
||||
|
||||
auto asset = gltf_parser.loadGltf(data.get(), gltf_path.parent_path(), fastgltf_opts);
|
||||
if (auto error = asset.error(); error != fastgltf::Error::None) {
|
||||
logger.error("Failed to load glTF asset from: {}", gltf_path.filename().c_str());
|
||||
return std::unexpected(error::failed_to_load);
|
||||
}
|
||||
|
||||
// Parse the glTF scene description
|
||||
|
||||
if (asset->scenes.size() == 0) {
|
||||
logger.error("glTF asset \"{}\" has no scenes", gltf_path.filename().c_str());
|
||||
logger.error("DEBUG: asset created with \"{}\"",
|
||||
asset->assetInfo.has_value() ? asset->assetInfo->generator : "[unknown]");
|
||||
|
||||
return std::unexpected(error::resource_invalid);
|
||||
} else if (asset->scenes.size() > 1) {
|
||||
logger.warn("glTF asset \"{}\" has multiple scenes. Defaulting to scene 0 ...", gltf_path.filename().c_str());
|
||||
logger.warn("DEBUG: asset created with \"{}\"",
|
||||
asset->assetInfo.has_value() ? asset->assetInfo->generator : "[unknown]");
|
||||
}
|
||||
|
||||
class model out_model {};
|
||||
|
||||
// Collect all materials present in the model
|
||||
|
||||
const auto materials = parse_materials(asset.get());
|
||||
|
||||
// Iterate through the default scene node heirarchy
|
||||
|
||||
const std::size_t scene_idx = asset->defaultScene.has_value() ? asset->defaultScene.value() : 0;
|
||||
const auto& scene = asset->scenes[scene_idx];
|
||||
|
||||
for (const std::size_t node_idx : scene.nodeIndices) {
|
||||
out_model.push_mesh(parse_mesh_from_root_node(asset.get(), asset->nodes[node_idx]));
|
||||
}
|
||||
|
||||
for (const auto& mat : materials) {
|
||||
out_model.push_material(mat);
|
||||
}
|
||||
|
||||
return out_model;
|
||||
}
|
||||
|
||||
resource::mesh parse_mesh_from_root_node(const fastgltf::Asset& asset, const fastgltf::Node& node) {
|
||||
// Node (mesh) transform
|
||||
// The glTF specification notes that node transforms are relative to their parent node's transform
|
||||
|
||||
glm::mat4 out_matrix {};
|
||||
|
||||
if (std::holds_alternative<fastgltf::math::fmat4x4>(node.transform)) {
|
||||
// Node contains a transformation matrix
|
||||
|
||||
const auto& node_trs_matrix = std::get<fastgltf::math::fmat4x4>(node.transform);
|
||||
out_matrix = glm::make_mat4x4(node_trs_matrix.data());
|
||||
|
||||
} else if (std::holds_alternative<fastgltf::TRS>(node.transform)) {
|
||||
// Node contains a TRS transform, convert to matrix
|
||||
|
||||
const auto& node_trs = std::get<fastgltf::TRS>(node.transform);
|
||||
|
||||
glm::vec3 pos {node_trs.translation.x(), node_trs.translation.y(), node_trs.translation.z()};
|
||||
glm::quat rot {node_trs.rotation.w(), node_trs.rotation.x(), node_trs.rotation.y(), node_trs.rotation.z()};
|
||||
glm::vec3 scale {node_trs.scale.x(), node_trs.scale.y(), node_trs.scale.z()};
|
||||
|
||||
static constexpr glm::mat4 identity_mat = glm::mat4(1.0f);
|
||||
const glm::mat4 translation = glm::translate(identity_mat, pos);
|
||||
const glm::mat4 rotation = glm::mat4_cast(rot);
|
||||
const glm::mat4 scaling = glm::scale(identity_mat, scale);
|
||||
|
||||
out_matrix = translation * rotation * scaling;
|
||||
}
|
||||
|
||||
// Construct the output node (mesh)
|
||||
|
||||
resource::mesh ret_val {out_matrix};
|
||||
|
||||
// Populate mesh with primitives
|
||||
|
||||
if (node.meshIndex.has_value()) {
|
||||
const auto mesh_idx = node.meshIndex.value();
|
||||
const fastgltf::Mesh& m = asset.meshes[mesh_idx];
|
||||
|
||||
// TODO: Split primitive parsing into separate function
|
||||
|
||||
for (const fastgltf::Primitive& p : m.primitives) {
|
||||
KUIPER_ASSERT(p.type == fastgltf::PrimitiveType::Triangles);
|
||||
|
||||
resource::primitive out_prim {};
|
||||
out_prim.type = primitive_type::triangles; // asserted above
|
||||
|
||||
// Vertex position
|
||||
|
||||
auto pos_attr = p.findAttribute("POSITION");
|
||||
const auto& pos_accessor = asset.accessors[pos_attr->accessorIndex];
|
||||
|
||||
out_prim.vertices.resize(pos_accessor.count);
|
||||
|
||||
fastgltf::iterateAccessorWithIndex<glm::vec3>(
|
||||
asset, pos_accessor, [&out_prim](const glm::vec3& pos, std::size_t idx) {
|
||||
out_prim.vertices[idx].position = pos;
|
||||
});
|
||||
|
||||
// Vertex normals
|
||||
|
||||
auto norm_attr = p.findAttribute("NORMAL");
|
||||
const auto& norm_accessor = asset.accessors[norm_attr->accessorIndex];
|
||||
|
||||
if (norm_accessor.normalized) {
|
||||
fastgltf::iterateAccessorWithIndex<glm::vec3>(
|
||||
asset, norm_accessor, [&out_prim](const glm::vec3& norm, std::size_t idx) {
|
||||
out_prim.vertices[idx].normal = norm;
|
||||
});
|
||||
} else {
|
||||
fastgltf::iterateAccessorWithIndex<glm::vec3>(
|
||||
asset, norm_accessor, [&out_prim](const glm::vec3& norm, std::size_t idx) {
|
||||
out_prim.vertices[idx].normal = glm::normalize(norm);
|
||||
});
|
||||
}
|
||||
|
||||
// Vertex tangents
|
||||
|
||||
auto tan_attr = p.findAttribute("TANGENT");
|
||||
const auto& tan_accessor = asset.accessors[tan_attr->accessorIndex];
|
||||
|
||||
fastgltf::iterateAccessorWithIndex<glm::vec4>(
|
||||
asset, tan_accessor, [&out_prim](const glm::vec4& tan, std::size_t idx) {
|
||||
out_prim.vertices[idx].tangent = tan;
|
||||
});
|
||||
|
||||
// Indices
|
||||
|
||||
const auto& idx_accessor = asset.accessors[p.indicesAccessor.value()];
|
||||
out_prim.indices.resize(idx_accessor.count);
|
||||
fastgltf::copyFromAccessor<std::uint32_t>(asset, idx_accessor, out_prim.indices.data());
|
||||
|
||||
// Materials
|
||||
|
||||
out_prim.material_idx = p.materialIndex.value();
|
||||
|
||||
// Texture coordinates
|
||||
|
||||
const std::size_t tex_uv_idx =
|
||||
asset.materials[p.materialIndex.value()].pbrData.baseColorTexture->texCoordIndex;
|
||||
auto tex_uv_attr = p.findAttribute(std::format("TEXCOORD_{}", tex_uv_idx));
|
||||
const auto& tex_uv_accessor = asset.accessors[tex_uv_attr->accessorIndex];
|
||||
|
||||
fastgltf::iterateAccessorWithIndex<glm::vec2>(
|
||||
asset, tex_uv_accessor, [&out_prim](const glm::vec2& uv, std::size_t idx) {
|
||||
out_prim.vertices[idx].uv = uv;
|
||||
});
|
||||
|
||||
// Add parsed primitive to mesh
|
||||
|
||||
ret_val.add_primitive(out_prim);
|
||||
}
|
||||
}
|
||||
|
||||
if (!node.children.empty()) {
|
||||
for (const auto child_idx : node.children) {
|
||||
const auto submesh = parse_mesh_from_root_node(asset, asset.nodes[child_idx]);
|
||||
ret_val.add_submesh(submesh);
|
||||
}
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
std::expected<resource::image, resource::image_error> load_material_image(const fastgltf::Asset& asset,
|
||||
const fastgltf::TextureInfo& tex_info) {
|
||||
const std::size_t tex_idx = tex_info.textureIndex;
|
||||
const std::size_t img_idx = asset.textures[tex_idx].imageIndex.value();
|
||||
const fastgltf::Image& img = asset.images[img_idx];
|
||||
|
||||
if (std::holds_alternative<fastgltf::sources::URI>(img.data)) {
|
||||
const auto& path = std::get<fastgltf::sources::URI>(img.data);
|
||||
|
||||
return resource::image::from_path(path.uri.path());
|
||||
|
||||
} else if (std::holds_alternative<fastgltf::sources::Array>(img.data)) {
|
||||
const auto& arr = std::get<fastgltf::sources::Array>(img.data);
|
||||
|
||||
return resource::image::from_memory((const std::uint8_t*) arr.bytes.data(), arr.bytes.size_bytes());
|
||||
|
||||
} else if (std::holds_alternative<fastgltf::sources::BufferView>(img.data)) {
|
||||
const auto& view = std::get<fastgltf::sources::BufferView>(img.data);
|
||||
|
||||
const auto& buffer_view = asset.bufferViews[view.bufferViewIndex];
|
||||
const auto& buffer = asset.buffers[buffer_view.bufferIndex];
|
||||
|
||||
if (std::holds_alternative<fastgltf::sources::Array>(buffer.data)) {
|
||||
const auto& arr = std::get<fastgltf::sources::Array>(buffer.data);
|
||||
|
||||
return resource::image::from_memory((const std::uint8_t*) arr.bytes.data() + buffer_view.byteOffset,
|
||||
arr.bytes.size_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
return std::unexpected(resource::image_error {"unknown error"});
|
||||
}
|
||||
|
||||
std::vector<resource::material> parse_materials(const fastgltf::Asset& asset) {
|
||||
auto logger = engine_logger::get();
|
||||
|
||||
std::vector<resource::material> ret_vec(asset.materials.size());
|
||||
|
||||
for (std::uint32_t i = 0; i < asset.materials.size(); ++i) {
|
||||
const auto& gltf_mat = asset.materials[i];
|
||||
auto& ret_mat = ret_vec[i];
|
||||
|
||||
ret_mat.base_colour_factor = glm::make_vec4(gltf_mat.pbrData.baseColorFactor.data());
|
||||
ret_mat.metallic_factor = gltf_mat.pbrData.metallicFactor;
|
||||
ret_mat.roughness_factor = gltf_mat.pbrData.roughnessFactor;
|
||||
|
||||
// Texture parameters
|
||||
|
||||
GLint min_filter = GL_LINEAR_MIPMAP_NEAREST;
|
||||
GLint mag_filter = GL_LINEAR;
|
||||
GLint wrap_s = GL_REPEAT;
|
||||
GLint wrap_t = GL_REPEAT;
|
||||
|
||||
// Load each of the texture images we're interested in
|
||||
|
||||
// 0: Albedo, 1: Normal, 2: Metallic-Roughness
|
||||
std::array<resource::image, 3> imgs {};
|
||||
|
||||
// Base colour
|
||||
|
||||
if (gltf_mat.pbrData.baseColorTexture.has_value()) {
|
||||
{
|
||||
const auto& tex = asset.textures[gltf_mat.pbrData.baseColorTexture->textureIndex];
|
||||
|
||||
if (tex.samplerIndex.has_value()) {
|
||||
const fastgltf::Sampler& sampler = asset.samplers[tex.samplerIndex.value()];
|
||||
|
||||
if (sampler.minFilter.has_value())
|
||||
min_filter = std::to_underlying(sampler.minFilter.value());
|
||||
if (sampler.magFilter.has_value())
|
||||
mag_filter = std::to_underlying(sampler.magFilter.value());
|
||||
|
||||
wrap_s = std::to_underlying(sampler.wrapS);
|
||||
wrap_t = std::to_underlying(sampler.wrapT);
|
||||
}
|
||||
}
|
||||
|
||||
auto base_colour_img = load_material_image(asset, gltf_mat.pbrData.baseColorTexture.value());
|
||||
|
||||
if (base_colour_img.has_value()) {
|
||||
imgs[0] = std::move(base_colour_img.value());
|
||||
} else {
|
||||
logger.warn("Failed to load material's base colour texture image: {}", base_colour_img.error().message);
|
||||
imgs[0] = resource::image::make_blank(64, 64, 4);
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.warn("Material does not have a base colour texture");
|
||||
imgs[0] = resource::image::make_blank(64, 64, 4);
|
||||
}
|
||||
|
||||
// Normal map
|
||||
|
||||
if (gltf_mat.normalTexture.has_value()) {
|
||||
auto normal_img = load_material_image(asset, gltf_mat.normalTexture.value());
|
||||
|
||||
if (normal_img.has_value()) {
|
||||
imgs[1] = std::move(normal_img.value());
|
||||
} else {
|
||||
logger.warn("Failed to load material's normal texture image: {}", normal_img.error().message);
|
||||
imgs[1] = resource::image::make_blank(64, 64, 4);
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.warn("Material does not have a normal map texture");
|
||||
imgs[1] = resource::image::make_blank(64, 64, 4);
|
||||
}
|
||||
|
||||
if (gltf_mat.pbrData.metallicRoughnessTexture.has_value()) {
|
||||
auto met_rough_img = load_material_image(asset, gltf_mat.pbrData.metallicRoughnessTexture.value());
|
||||
|
||||
if (met_rough_img.has_value()) {
|
||||
imgs[2] = std::move(met_rough_img.value());
|
||||
} else {
|
||||
logger.warn("Failed to load material's metallic-roughness texture image: {}",
|
||||
met_rough_img.error().message);
|
||||
imgs[2] = resource::image::make_blank(64, 64, 4);
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.warn("Material does not have a metallic-roughness texture");
|
||||
imgs[2] = resource::image::make_blank(64, 64, 4);
|
||||
}
|
||||
|
||||
// Texture 2D arrays require that each layer has the same width & height
|
||||
// This is used to scale all textures in this material to the same size later on
|
||||
|
||||
std::uint32_t max_width = 0;
|
||||
std::uint32_t max_height = 0;
|
||||
|
||||
for (const auto& img : imgs) {
|
||||
KUIPER_ASSERT(img.width() == img.height());
|
||||
|
||||
if (img.width() > max_width)
|
||||
max_width = img.width();
|
||||
|
||||
if (img.height() > max_height)
|
||||
max_height = img.height();
|
||||
}
|
||||
|
||||
for (auto& img : imgs) {
|
||||
if (img.width() < max_width || img.height() < img.height())
|
||||
img.resize(max_width, max_height);
|
||||
}
|
||||
|
||||
ret_mat.textures = gl::texture_array::make(GL_TEXTURE_2D_ARRAY);
|
||||
ret_mat.textures->set_storage(GL_RGBA8, max_width, max_height, 3);
|
||||
ret_mat.textures->upload(
|
||||
0, 0, 0, max_width, max_height, 1, GL_RGBA, GL_UNSIGNED_BYTE, imgs[0].pixel_data().data());
|
||||
ret_mat.textures->upload(
|
||||
0, 0, 1, max_width, max_height, 1, GL_RGBA, GL_UNSIGNED_BYTE, imgs[1].pixel_data().data());
|
||||
ret_mat.textures->upload(
|
||||
0, 0, 2, max_width, max_height, 1, GL_RGBA, GL_UNSIGNED_BYTE, imgs[2].pixel_data().data());
|
||||
|
||||
ret_mat.textures->set_param(GL_TEXTURE_MIN_FILTER, min_filter);
|
||||
ret_mat.textures->set_param(GL_TEXTURE_MAG_FILTER, mag_filter);
|
||||
ret_mat.textures->set_param(GL_TEXTURE_WRAP_S, wrap_s);
|
||||
ret_mat.textures->set_param(GL_TEXTURE_WRAP_T, wrap_t);
|
||||
|
||||
ret_mat.textures->gen_mipmaps();
|
||||
}
|
||||
|
||||
return ret_vec;
|
||||
}
|
7
src/resources/resource_index.cpp
Normal file
7
src/resources/resource_index.cpp
Normal file
@ -0,0 +1,7 @@
|
||||
#include "resources/resource_index.hpp"
|
||||
|
||||
using namespace kuiper;
|
||||
|
||||
resource::index resource::index::from_file(const std::filesystem::path& index_path) noexcept {
|
||||
return {};
|
||||
}
|
39
src/resources/resource_manager.cpp
Normal file
39
src/resources/resource_manager.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "resources/resource_manager.hpp"
|
||||
|
||||
#include "resources/model.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
using namespace kuiper;
|
||||
|
||||
static constexpr std::array<std::string_view, 2> GLTF_FILE_EXTENSIONS {".gltf", ".glb"};
|
||||
|
||||
std::optional<resource_metadata> resource_manager::import_from_path(const std::filesystem::path& path) {
|
||||
if (!std::filesystem::exists(path) || !std::filesystem::is_regular_file(path)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string file_ext = path.extension().string();
|
||||
std::transform(file_ext.begin(), file_ext.end(), file_ext.begin(), [](const auto c) {
|
||||
return std::tolower(c);
|
||||
});
|
||||
|
||||
const auto model_ext_it =
|
||||
std::find_if(GLTF_FILE_EXTENSIONS.begin(), GLTF_FILE_EXTENSIONS.end(), [&file_ext](const auto s) {
|
||||
return file_ext == s;
|
||||
});
|
||||
|
||||
if (model_ext_it != GLTF_FILE_EXTENSIONS.end()) {
|
||||
auto maybe_model = resource::model::from_gltf_path(path);
|
||||
if (maybe_model)
|
||||
add<resource::model>(std::move(maybe_model.value()), path.relative_path().string());
|
||||
}
|
||||
|
||||
return std::optional<resource_metadata>();
|
||||
}
|
64
src/window/input_system.cpp
Normal file
64
src/window/input_system.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
#include "window/input_system.hpp"
|
||||
|
||||
using namespace kuiper;
|
||||
|
||||
void input_system::set_key_state(int key, int state) {
|
||||
m_key_states[key].state = state;
|
||||
|
||||
// Call any action handlers
|
||||
|
||||
if (state == GLFW_PRESS) {
|
||||
for (const auto& f : m_action_listeners[m_action_keymap[key]]) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int input_system::get_key_state(int key) const {
|
||||
return m_key_states[key].state;
|
||||
}
|
||||
|
||||
bool input_system::is_key_pressed(int key) const {
|
||||
return m_key_states[key].state == GLFW_PRESS;
|
||||
}
|
||||
|
||||
bool input_system::is_key_released(int key) const {
|
||||
return m_key_states[key].state == GLFW_RELEASE;
|
||||
}
|
||||
|
||||
bool input_system::is_key_held(int key) const {
|
||||
return m_key_states[key].state == GLFW_REPEAT;
|
||||
}
|
||||
|
||||
bool input_system::is_key_down(int key) const {
|
||||
return m_key_states[key].state == GLFW_PRESS || m_key_states[key].state == GLFW_REPEAT;
|
||||
}
|
||||
|
||||
// Mouse
|
||||
|
||||
void input_system::set_mouse_pos(const glm::vec2& pos) {
|
||||
m_mouse_state.delta_pos = m_old_mouse_pos - pos;
|
||||
m_mouse_state.pos = pos;
|
||||
return;
|
||||
}
|
||||
|
||||
void input_system::set_mouse_delta(const glm::vec2& pos) {
|
||||
m_mouse_state.delta_pos = pos;
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec2 input_system::get_mouse_pos() const {
|
||||
return m_mouse_state.pos;
|
||||
}
|
||||
|
||||
glm::vec2 input_system::get_mouse_delta() const {
|
||||
return m_mouse_state.delta_pos;
|
||||
}
|
||||
|
||||
void input_system::subscribe_to_action(action_type type, const action_fn_t& callback) {
|
||||
m_action_listeners[type].push_back(callback);
|
||||
}
|
9
thirdparty/glad/CMakeLists.txt
vendored
Normal file
9
thirdparty/glad/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(
|
||||
glad
|
||||
LANGUAGES C
|
||||
)
|
||||
|
||||
add_library(glad src/gl.c)
|
||||
target_include_directories(glad PUBLIC "include/")
|
63
thirdparty/glad/LICENSE
vendored
Normal file
63
thirdparty/glad/LICENSE
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
The glad source code:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2022 David Herberth
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
The Khronos Specifications:
|
||||
|
||||
Copyright (c) 2013-2020 The Khronos Group Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
The EGL Specification and various headers:
|
||||
|
||||
Copyright (c) 2007-2016 The Khronos Group Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and/or associated documentation files (the
|
||||
"Materials"), to deal in the Materials without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
permit persons to whom the Materials are furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Materials.
|
||||
|
||||
THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
311
thirdparty/glad/include/KHR/khrplatform.h
vendored
Normal file
311
thirdparty/glad/include/KHR/khrplatform.h
vendored
Normal file
@ -0,0 +1,311 @@
|
||||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
3637
thirdparty/glad/include/glad/gl.h
vendored
Normal file
3637
thirdparty/glad/include/glad/gl.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1620
thirdparty/glad/src/gl.c
vendored
Normal file
1620
thirdparty/glad/src/gl.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
37
thirdparty/imgui/CMakeLists.txt
vendored
Normal file
37
thirdparty/imgui/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
project(
|
||||
imgui
|
||||
VERSION 1.91.8
|
||||
DESCRIPTION "imgui v1.91.8-docking"
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
message(STATUS "Build Dear ImGui: ${PROJECT_VERSION}")
|
||||
|
||||
add_library(
|
||||
imgui
|
||||
# Core
|
||||
src/imgui.cpp
|
||||
src/imgui_stdlib.cpp
|
||||
src/imgui_tables.cpp
|
||||
src/imgui_demo.cpp
|
||||
src/imgui_draw.cpp
|
||||
src/imgui_widgets.cpp
|
||||
# Backends
|
||||
src/imgui_impl_glfw.cpp
|
||||
src/imgui_impl_opengl3.cpp
|
||||
# Miscellaneous
|
||||
src/misc/freetype/imgui_freetype.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(imgui PUBLIC IMGUI_ENABLE_FREETYPE)
|
||||
target_include_directories(imgui PUBLIC "include/")
|
||||
|
||||
# GLFW (this feels kinda hacky, OVERRIDE_FIND_PACKAGE'd in the parent CMakeLists.txt)
|
||||
find_package(glfw 3.4 REQUIRED)
|
||||
# ditto FreeType
|
||||
find_package(freetype REQUIRED)
|
||||
|
||||
target_link_libraries(imgui PRIVATE glfw freetype)
|
||||
target_include_directories(imgui PRIVATE glfw)
|
21
thirdparty/imgui/LICENSE
vendored
Normal file
21
thirdparty/imgui/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2024 Omar Cornut
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
142
thirdparty/imgui/include/imconfig.h
vendored
Normal file
142
thirdparty/imgui/include/imconfig.h
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// DEAR IMGUI COMPILE-TIME OPTIONS
|
||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
|
||||
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
|
||||
//-----------------------------------------------------------------------------
|
||||
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
|
||||
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
|
||||
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
|
||||
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
|
||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
|
||||
|
||||
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||
//#define IMGUI_USE_STB_SPRINTF
|
||||
|
||||
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg.
|
||||
// - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions.
|
||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||
|
||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||
|
||||
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||
/*
|
||||
#define IM_VEC2_CLASS_EXTRA \
|
||||
constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
|
||||
operator MyVec2() const { return MyVec2(x,y); }
|
||||
|
||||
#define IM_VEC4_CLASS_EXTRA \
|
||||
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
|
||||
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||
*/
|
||||
//---- ...Or use Dear ImGui's own very basic math operators.
|
||||
//#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
||||
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||
//#define ImDrawIdx unsigned int
|
||||
|
||||
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
|
||||
//struct ImDrawList;
|
||||
//struct ImDrawCmd;
|
||||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#define ImDrawCallback MyImDrawCallback
|
||||
|
||||
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
|
||||
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
|
||||
/*
|
||||
namespace ImGui
|
||||
{
|
||||
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||
}
|
||||
*/
|
4001
thirdparty/imgui/include/imgui.h
vendored
Normal file
4001
thirdparty/imgui/include/imgui.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
67
thirdparty/imgui/include/imgui_impl_glfw.h
vendored
Normal file
67
thirdparty/imgui/include/imgui_impl_glfw.h
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
// dear imgui: Platform Backend for GLFW
|
||||
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)
|
||||
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
|
||||
// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ for full feature support.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support.
|
||||
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only).
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
|
||||
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||
// Missing features or Issues:
|
||||
// [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
struct GLFWwindow;
|
||||
struct GLFWmonitor;
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
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 void ImGui_ImplGlfw_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
|
||||
|
||||
// Emscripten related initialization phase methods (call after ImGui_ImplGlfw_InitForOpenGL)
|
||||
#ifdef __EMSCRIPTEN__
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector);
|
||||
//static inline void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) { ImGui_ImplGlfw_InstallEmscriptenCallbacks(nullptr, canvas_selector); } } // Renamed in 1.91.0
|
||||
#endif
|
||||
|
||||
// GLFW callbacks install
|
||||
// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any.
|
||||
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks.
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window);
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window);
|
||||
|
||||
// GFLW callbacks options:
|
||||
// - Set 'chain_for_all_windows=true' to enable chaining callbacks for all windows (including secondary viewports created by backends or by user)
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows);
|
||||
|
||||
// GLFW callbacks (individual callbacks to call yourself if you didn't install callbacks)
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event);
|
||||
|
||||
// GLFW helpers
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_Sleep(int milliseconds);
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
67
thirdparty/imgui/include/imgui_impl_opengl3.h
vendored
Normal file
67
thirdparty/imgui/include/imgui_impl_opengl3.h
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
|
||||
// - Desktop GL: 2.x 3.x 4.x
|
||||
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
||||
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!]
|
||||
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
|
||||
|
||||
// About WebGL/ES:
|
||||
// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES.
|
||||
// - This is done automatically on iOS, Android and Emscripten targets.
|
||||
// - For other targets, the define needs to be visible from the imgui_impl_opengl3.cpp compilation unit. If unsure, define globally or in imconfig.h.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
// About GLSL version:
|
||||
// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string.
|
||||
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
|
||||
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);
|
||||
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 void ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||
|
||||
// Configuration flags to add in your imconfig file:
|
||||
//#define IMGUI_IMPL_OPENGL_ES2 // Enable ES 2 (Auto-detected on Emscripten)
|
||||
//#define IMGUI_IMPL_OPENGL_ES3 // Enable ES 3 (Auto-detected on iOS/Android)
|
||||
|
||||
// You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
|
||||
// Try to detect GLES on matching platforms
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
|
||||
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
|
||||
#elif defined(__EMSCRIPTEN__) || defined(__amigaos4__)
|
||||
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
|
||||
#else
|
||||
// Otherwise imgui_impl_opengl3_loader.h will be used.
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
916
thirdparty/imgui/include/imgui_impl_opengl3_loader.h
vendored
Normal file
916
thirdparty/imgui/include/imgui_impl_opengl3_loader.h
vendored
Normal file
@ -0,0 +1,916 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// About imgui_impl_opengl3_loader.h:
|
||||
//
|
||||
// We embed our own OpenGL loader to not require user to provide their own or to have to use ours,
|
||||
// which proved to be endless problems for users.
|
||||
// Our loader is custom-generated, based on gl3w but automatically filtered to only include
|
||||
// enums/functions that we use in our imgui_impl_opengl3.cpp source file in order to be small.
|
||||
//
|
||||
// YOU SHOULD NOT NEED TO INCLUDE/USE THIS DIRECTLY. THIS IS USED BY imgui_impl_opengl3.cpp ONLY.
|
||||
// THE REST OF YOUR APP SHOULD USE A DIFFERENT GL LOADER: ANY GL LOADER OF YOUR CHOICE.
|
||||
//
|
||||
// IF YOU GET BUILD ERRORS IN THIS FILE (commonly macro redefinitions or function redefinitions):
|
||||
// IT LIKELY MEANS THAT YOU ARE BUILDING 'imgui_impl_opengl3.cpp' OR INCLUDING 'imgui_impl_opengl3_loader.h'
|
||||
// IN THE SAME COMPILATION UNIT AS ONE OF YOUR FILE WHICH IS USING A THIRD-PARTY OPENGL LOADER.
|
||||
// (e.g. COULD HAPPEN IF YOU ARE DOING A UNITY/JUMBO BUILD, OR INCLUDING .CPP FILES FROM OTHERS)
|
||||
// YOU SHOULD NOT BUILD BOTH IN THE SAME COMPILATION UNIT.
|
||||
// BUT IF YOU REALLY WANT TO, you can '#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM' and imgui_impl_opengl3.cpp
|
||||
// WILL NOT BE USING OUR LOADER, AND INSTEAD EXPECT ANOTHER/YOUR LOADER TO BE AVAILABLE IN THE COMPILATION UNIT.
|
||||
//
|
||||
// Regenerate with:
|
||||
// python3 gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt
|
||||
//
|
||||
// More info:
|
||||
// https://github.com/dearimgui/gl3w_stripped
|
||||
// https://github.com/ocornut/imgui/issues/4445
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* This file was generated with gl3w_gen.py, part of imgl3w
|
||||
* (hosted at https://github.com/dearimgui/gl3w_stripped)
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*
|
||||
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
* distribute this software, either in source code form or as a compiled
|
||||
* binary, for any purpose, commercial or non-commercial, and by any
|
||||
* means.
|
||||
*
|
||||
* In jurisdictions that recognize copyright laws, the author or authors
|
||||
* of this software dedicate any and all copyright interest in the
|
||||
* software to the public domain. We make this dedication for the benefit
|
||||
* of the public at large and to the detriment of our heirs and
|
||||
* successors. We intend this dedication to be an overt act of
|
||||
* relinquishment in perpetuity of all present and future rights to this
|
||||
* software under copyright law.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __gl3w_h_
|
||||
#define __gl3w_h_
|
||||
|
||||
// Adapted from KHR/khrplatform.h to avoid including entire file.
|
||||
#ifndef __khrplatform_h_
|
||||
typedef float khronos_float_t;
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
#ifdef _WIN64
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef signed long int khronos_ssize_t;
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
typedef signed __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100)
|
||||
#include <stdint.h>
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#else
|
||||
typedef signed long long khronos_int64_t;
|
||||
typedef unsigned long long khronos_uint64_t;
|
||||
#endif
|
||||
#endif // __khrplatform_h_
|
||||
|
||||
#ifndef __gl_glcorearb_h_
|
||||
#define __gl_glcorearb_h_ 1
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
** Copyright 2013-2020 The Khronos Group Inc.
|
||||
** SPDX-License-Identifier: MIT
|
||||
**
|
||||
** This header is generated from the Khronos OpenGL / OpenGL ES XML
|
||||
** API Registry. The current version of the Registry, generator scripts
|
||||
** used to make the header, and the header can be found at
|
||||
** https://github.com/KhronosGroup/OpenGL-Registry
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#ifndef APIENTRY
|
||||
#define APIENTRY
|
||||
#endif
|
||||
#ifndef APIENTRYP
|
||||
#define APIENTRYP APIENTRY *
|
||||
#endif
|
||||
#ifndef GLAPI
|
||||
#define GLAPI extern
|
||||
#endif
|
||||
/* glcorearb.h is for use with OpenGL core profile implementations.
|
||||
** It should should be placed in the same directory as gl.h and
|
||||
** included as <GL/glcorearb.h>.
|
||||
**
|
||||
** glcorearb.h includes only APIs in the latest OpenGL core profile
|
||||
** implementation together with APIs in newer ARB extensions which
|
||||
** can be supported by the core profile. It does not, and never will
|
||||
** include functionality removed from the core profile, such as
|
||||
** fixed-function vertex and fragment processing.
|
||||
**
|
||||
** Do not #include both <GL/glcorearb.h> and either of <GL/gl.h> or
|
||||
** <GL/glext.h> in the same source file.
|
||||
*/
|
||||
/* Generated C header for:
|
||||
* API: gl
|
||||
* Profile: core
|
||||
* Versions considered: .*
|
||||
* Versions emitted: .*
|
||||
* Default extensions included: glcore
|
||||
* Additional extensions included: _nomatch_^
|
||||
* Extensions removed: _nomatch_^
|
||||
*/
|
||||
#ifndef GL_VERSION_1_0
|
||||
typedef void GLvoid;
|
||||
typedef unsigned int GLenum;
|
||||
|
||||
typedef khronos_float_t GLfloat;
|
||||
typedef int GLint;
|
||||
typedef int GLsizei;
|
||||
typedef unsigned int GLbitfield;
|
||||
typedef double GLdouble;
|
||||
typedef unsigned int GLuint;
|
||||
typedef unsigned char GLboolean;
|
||||
typedef khronos_uint8_t GLubyte;
|
||||
#define GL_COLOR_BUFFER_BIT 0x00004000
|
||||
#define GL_FALSE 0
|
||||
#define GL_TRUE 1
|
||||
#define GL_TRIANGLES 0x0004
|
||||
#define GL_ONE 1
|
||||
#define GL_SRC_ALPHA 0x0302
|
||||
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
|
||||
#define GL_FRONT 0x0404
|
||||
#define GL_BACK 0x0405
|
||||
#define GL_FRONT_AND_BACK 0x0408
|
||||
#define GL_POLYGON_MODE 0x0B40
|
||||
#define GL_CULL_FACE 0x0B44
|
||||
#define GL_DEPTH_TEST 0x0B71
|
||||
#define GL_STENCIL_TEST 0x0B90
|
||||
#define GL_VIEWPORT 0x0BA2
|
||||
#define GL_BLEND 0x0BE2
|
||||
#define GL_SCISSOR_BOX 0x0C10
|
||||
#define GL_SCISSOR_TEST 0x0C11
|
||||
#define GL_UNPACK_ROW_LENGTH 0x0CF2
|
||||
#define GL_PACK_ALIGNMENT 0x0D05
|
||||
#define GL_TEXTURE_2D 0x0DE1
|
||||
#define GL_UNSIGNED_BYTE 0x1401
|
||||
#define GL_UNSIGNED_SHORT 0x1403
|
||||
#define GL_UNSIGNED_INT 0x1405
|
||||
#define GL_FLOAT 0x1406
|
||||
#define GL_RGBA 0x1908
|
||||
#define GL_FILL 0x1B02
|
||||
#define GL_VENDOR 0x1F00
|
||||
#define GL_RENDERER 0x1F01
|
||||
#define GL_VERSION 0x1F02
|
||||
#define GL_EXTENSIONS 0x1F03
|
||||
#define GL_LINEAR 0x2601
|
||||
#define GL_TEXTURE_MAG_FILTER 0x2800
|
||||
#define GL_TEXTURE_MIN_FILTER 0x2801
|
||||
#define GL_TEXTURE_WRAP_S 0x2802
|
||||
#define GL_TEXTURE_WRAP_T 0x2803
|
||||
#define GL_REPEAT 0x2901
|
||||
typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode);
|
||||
typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);
|
||||
typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
|
||||
typedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask);
|
||||
typedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
|
||||
typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap);
|
||||
typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap);
|
||||
typedef void (APIENTRYP PFNGLFLUSHPROC) (void);
|
||||
typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param);
|
||||
typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
|
||||
typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void);
|
||||
typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data);
|
||||
typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name);
|
||||
typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap);
|
||||
typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode);
|
||||
GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
GLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param);
|
||||
GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
|
||||
GLAPI void APIENTRY glClear (GLbitfield mask);
|
||||
GLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
|
||||
GLAPI void APIENTRY glDisable (GLenum cap);
|
||||
GLAPI void APIENTRY glEnable (GLenum cap);
|
||||
GLAPI void APIENTRY glFlush (void);
|
||||
GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param);
|
||||
GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
|
||||
GLAPI GLenum APIENTRY glGetError (void);
|
||||
GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data);
|
||||
GLAPI const GLubyte *APIENTRY glGetString (GLenum name);
|
||||
GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap);
|
||||
GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
#endif
|
||||
#endif /* GL_VERSION_1_0 */
|
||||
#ifndef GL_VERSION_1_1
|
||||
typedef khronos_float_t GLclampf;
|
||||
typedef double GLclampd;
|
||||
#define GL_TEXTURE_BINDING_2D 0x8069
|
||||
typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices);
|
||||
typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture);
|
||||
typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures);
|
||||
typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);
|
||||
GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture);
|
||||
GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures);
|
||||
GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures);
|
||||
#endif
|
||||
#endif /* GL_VERSION_1_1 */
|
||||
#ifndef GL_VERSION_1_2
|
||||
#define GL_CLAMP_TO_EDGE 0x812F
|
||||
#endif /* GL_VERSION_1_2 */
|
||||
#ifndef GL_VERSION_1_3
|
||||
#define GL_TEXTURE0 0x84C0
|
||||
#define GL_ACTIVE_TEXTURE 0x84E0
|
||||
typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glActiveTexture (GLenum texture);
|
||||
#endif
|
||||
#endif /* GL_VERSION_1_3 */
|
||||
#ifndef GL_VERSION_1_4
|
||||
#define GL_BLEND_DST_RGB 0x80C8
|
||||
#define GL_BLEND_SRC_RGB 0x80C9
|
||||
#define GL_BLEND_DST_ALPHA 0x80CA
|
||||
#define GL_BLEND_SRC_ALPHA 0x80CB
|
||||
#define GL_FUNC_ADD 0x8006
|
||||
typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
|
||||
typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
|
||||
GLAPI void APIENTRY glBlendEquation (GLenum mode);
|
||||
#endif
|
||||
#endif /* GL_VERSION_1_4 */
|
||||
#ifndef GL_VERSION_1_5
|
||||
typedef khronos_ssize_t GLsizeiptr;
|
||||
typedef khronos_intptr_t GLintptr;
|
||||
#define GL_ARRAY_BUFFER 0x8892
|
||||
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
|
||||
#define GL_ARRAY_BUFFER_BINDING 0x8894
|
||||
#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
|
||||
#define GL_STREAM_DRAW 0x88E0
|
||||
typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
|
||||
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
|
||||
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
|
||||
typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
|
||||
typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer);
|
||||
GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers);
|
||||
GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers);
|
||||
GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
|
||||
GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
|
||||
#endif
|
||||
#endif /* GL_VERSION_1_5 */
|
||||
#ifndef GL_VERSION_2_0
|
||||
typedef char GLchar;
|
||||
typedef khronos_int16_t GLshort;
|
||||
typedef khronos_int8_t GLbyte;
|
||||
typedef khronos_uint16_t GLushort;
|
||||
#define GL_BLEND_EQUATION_RGB 0x8009
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
|
||||
#define GL_BLEND_EQUATION_ALPHA 0x883D
|
||||
#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
|
||||
#define GL_FRAGMENT_SHADER 0x8B30
|
||||
#define GL_VERTEX_SHADER 0x8B31
|
||||
#define GL_COMPILE_STATUS 0x8B81
|
||||
#define GL_LINK_STATUS 0x8B82
|
||||
#define GL_INFO_LOG_LENGTH 0x8B84
|
||||
#define GL_CURRENT_PROGRAM 0x8B8D
|
||||
#define GL_UPPER_LEFT 0x8CA2
|
||||
typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha);
|
||||
typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
|
||||
typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader);
|
||||
typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void);
|
||||
typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type);
|
||||
typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program);
|
||||
typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader);
|
||||
typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader);
|
||||
typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index);
|
||||
typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
|
||||
typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);
|
||||
typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params);
|
||||
typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
|
||||
typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);
|
||||
typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
|
||||
typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name);
|
||||
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params);
|
||||
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer);
|
||||
typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program);
|
||||
typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program);
|
||||
typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
|
||||
typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program);
|
||||
typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
|
||||
typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha);
|
||||
GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader);
|
||||
GLAPI void APIENTRY glCompileShader (GLuint shader);
|
||||
GLAPI GLuint APIENTRY glCreateProgram (void);
|
||||
GLAPI GLuint APIENTRY glCreateShader (GLenum type);
|
||||
GLAPI void APIENTRY glDeleteProgram (GLuint program);
|
||||
GLAPI void APIENTRY glDeleteShader (GLuint shader);
|
||||
GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader);
|
||||
GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index);
|
||||
GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index);
|
||||
GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name);
|
||||
GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params);
|
||||
GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
|
||||
GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params);
|
||||
GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
|
||||
GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name);
|
||||
GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params);
|
||||
GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer);
|
||||
GLAPI GLboolean APIENTRY glIsProgram (GLuint program);
|
||||
GLAPI void APIENTRY glLinkProgram (GLuint program);
|
||||
GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
|
||||
GLAPI void APIENTRY glUseProgram (GLuint program);
|
||||
GLAPI void APIENTRY glUniform1i (GLint location, GLint v0);
|
||||
GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
|
||||
#endif
|
||||
#endif /* GL_VERSION_2_0 */
|
||||
#ifndef GL_VERSION_2_1
|
||||
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
|
||||
#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
|
||||
#endif /* GL_VERSION_2_1 */
|
||||
#ifndef GL_VERSION_3_0
|
||||
typedef khronos_uint16_t GLhalf;
|
||||
#define GL_MAJOR_VERSION 0x821B
|
||||
#define GL_MINOR_VERSION 0x821C
|
||||
#define GL_NUM_EXTENSIONS 0x821D
|
||||
#define GL_FRAMEBUFFER_SRGB 0x8DB9
|
||||
#define GL_VERTEX_ARRAY_BINDING 0x85B5
|
||||
typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data);
|
||||
typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data);
|
||||
typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index);
|
||||
typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array);
|
||||
typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays);
|
||||
typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index);
|
||||
GLAPI void APIENTRY glBindVertexArray (GLuint array);
|
||||
GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays);
|
||||
GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays);
|
||||
#endif
|
||||
#endif /* GL_VERSION_3_0 */
|
||||
#ifndef GL_VERSION_3_1
|
||||
#define GL_VERSION_3_1 1
|
||||
#define GL_PRIMITIVE_RESTART 0x8F9D
|
||||
#endif /* GL_VERSION_3_1 */
|
||||
#ifndef GL_VERSION_3_2
|
||||
#define GL_VERSION_3_2 1
|
||||
typedef struct __GLsync *GLsync;
|
||||
typedef khronos_uint64_t GLuint64;
|
||||
typedef khronos_int64_t GLint64;
|
||||
#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
|
||||
#define GL_CONTEXT_PROFILE_MASK 0x9126
|
||||
typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
|
||||
typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
|
||||
#endif
|
||||
#endif /* GL_VERSION_3_2 */
|
||||
#ifndef GL_VERSION_3_3
|
||||
#define GL_VERSION_3_3 1
|
||||
#define GL_SAMPLER_BINDING 0x8919
|
||||
typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler);
|
||||
#endif
|
||||
#endif /* GL_VERSION_3_3 */
|
||||
#ifndef GL_VERSION_4_1
|
||||
typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data);
|
||||
typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data);
|
||||
#endif /* GL_VERSION_4_1 */
|
||||
#ifndef GL_VERSION_4_3
|
||||
typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
|
||||
#endif /* GL_VERSION_4_3 */
|
||||
#ifndef GL_VERSION_4_5
|
||||
#define GL_CLIP_ORIGIN 0x935C
|
||||
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param);
|
||||
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param);
|
||||
#endif /* GL_VERSION_4_5 */
|
||||
#ifndef GL_ARB_bindless_texture
|
||||
typedef khronos_uint64_t GLuint64EXT;
|
||||
#endif /* GL_ARB_bindless_texture */
|
||||
#ifndef GL_ARB_cl_event
|
||||
struct _cl_context;
|
||||
struct _cl_event;
|
||||
#endif /* GL_ARB_cl_event */
|
||||
#ifndef GL_ARB_clip_control
|
||||
#define GL_ARB_clip_control 1
|
||||
#endif /* GL_ARB_clip_control */
|
||||
#ifndef GL_ARB_debug_output
|
||||
typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
|
||||
#endif /* GL_ARB_debug_output */
|
||||
#ifndef GL_EXT_EGL_image_storage
|
||||
typedef void *GLeglImageOES;
|
||||
#endif /* GL_EXT_EGL_image_storage */
|
||||
#ifndef GL_EXT_direct_state_access
|
||||
typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params);
|
||||
typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params);
|
||||
typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params);
|
||||
typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param);
|
||||
typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param);
|
||||
#endif /* GL_EXT_direct_state_access */
|
||||
#ifndef GL_NV_draw_vulkan_image
|
||||
typedef void (APIENTRY *GLVULKANPROCNV)(void);
|
||||
#endif /* GL_NV_draw_vulkan_image */
|
||||
#ifndef GL_NV_gpu_shader5
|
||||
typedef khronos_int64_t GLint64EXT;
|
||||
#endif /* GL_NV_gpu_shader5 */
|
||||
#ifndef GL_NV_vertex_buffer_unified_memory
|
||||
typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result);
|
||||
#endif /* GL_NV_vertex_buffer_unified_memory */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GL3W_API
|
||||
#define GL3W_API
|
||||
#endif
|
||||
|
||||
#ifndef __gl_h_
|
||||
#define __gl_h_
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define GL3W_OK 0
|
||||
#define GL3W_ERROR_INIT -1
|
||||
#define GL3W_ERROR_LIBRARY_OPEN -2
|
||||
#define GL3W_ERROR_OPENGL_VERSION -3
|
||||
|
||||
typedef void (*GL3WglProc)(void);
|
||||
typedef GL3WglProc (*GL3WGetProcAddressProc)(const char *proc);
|
||||
|
||||
/* gl3w api */
|
||||
GL3W_API int imgl3wInit(void);
|
||||
GL3W_API int imgl3wInit2(GL3WGetProcAddressProc proc);
|
||||
GL3W_API int imgl3wIsSupported(int major, int minor);
|
||||
GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc);
|
||||
|
||||
/* gl3w internal state */
|
||||
union ImGL3WProcs {
|
||||
GL3WglProc ptr[59];
|
||||
struct {
|
||||
PFNGLACTIVETEXTUREPROC ActiveTexture;
|
||||
PFNGLATTACHSHADERPROC AttachShader;
|
||||
PFNGLBINDBUFFERPROC BindBuffer;
|
||||
PFNGLBINDSAMPLERPROC BindSampler;
|
||||
PFNGLBINDTEXTUREPROC BindTexture;
|
||||
PFNGLBINDVERTEXARRAYPROC BindVertexArray;
|
||||
PFNGLBLENDEQUATIONPROC BlendEquation;
|
||||
PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate;
|
||||
PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate;
|
||||
PFNGLBUFFERDATAPROC BufferData;
|
||||
PFNGLBUFFERSUBDATAPROC BufferSubData;
|
||||
PFNGLCLEARPROC Clear;
|
||||
PFNGLCLEARCOLORPROC ClearColor;
|
||||
PFNGLCOMPILESHADERPROC CompileShader;
|
||||
PFNGLCREATEPROGRAMPROC CreateProgram;
|
||||
PFNGLCREATESHADERPROC CreateShader;
|
||||
PFNGLDELETEBUFFERSPROC DeleteBuffers;
|
||||
PFNGLDELETEPROGRAMPROC DeleteProgram;
|
||||
PFNGLDELETESHADERPROC DeleteShader;
|
||||
PFNGLDELETETEXTURESPROC DeleteTextures;
|
||||
PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays;
|
||||
PFNGLDETACHSHADERPROC DetachShader;
|
||||
PFNGLDISABLEPROC Disable;
|
||||
PFNGLDISABLEVERTEXATTRIBARRAYPROC DisableVertexAttribArray;
|
||||
PFNGLDRAWELEMENTSPROC DrawElements;
|
||||
PFNGLDRAWELEMENTSBASEVERTEXPROC DrawElementsBaseVertex;
|
||||
PFNGLENABLEPROC Enable;
|
||||
PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray;
|
||||
PFNGLFLUSHPROC Flush;
|
||||
PFNGLGENBUFFERSPROC GenBuffers;
|
||||
PFNGLGENTEXTURESPROC GenTextures;
|
||||
PFNGLGENVERTEXARRAYSPROC GenVertexArrays;
|
||||
PFNGLGETATTRIBLOCATIONPROC GetAttribLocation;
|
||||
PFNGLGETERRORPROC GetError;
|
||||
PFNGLGETINTEGERVPROC GetIntegerv;
|
||||
PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog;
|
||||
PFNGLGETPROGRAMIVPROC GetProgramiv;
|
||||
PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog;
|
||||
PFNGLGETSHADERIVPROC GetShaderiv;
|
||||
PFNGLGETSTRINGPROC GetString;
|
||||
PFNGLGETSTRINGIPROC GetStringi;
|
||||
PFNGLGETUNIFORMLOCATIONPROC GetUniformLocation;
|
||||
PFNGLGETVERTEXATTRIBPOINTERVPROC GetVertexAttribPointerv;
|
||||
PFNGLGETVERTEXATTRIBIVPROC GetVertexAttribiv;
|
||||
PFNGLISENABLEDPROC IsEnabled;
|
||||
PFNGLISPROGRAMPROC IsProgram;
|
||||
PFNGLLINKPROGRAMPROC LinkProgram;
|
||||
PFNGLPIXELSTOREIPROC PixelStorei;
|
||||
PFNGLPOLYGONMODEPROC PolygonMode;
|
||||
PFNGLREADPIXELSPROC ReadPixels;
|
||||
PFNGLSCISSORPROC Scissor;
|
||||
PFNGLSHADERSOURCEPROC ShaderSource;
|
||||
PFNGLTEXIMAGE2DPROC TexImage2D;
|
||||
PFNGLTEXPARAMETERIPROC TexParameteri;
|
||||
PFNGLUNIFORM1IPROC Uniform1i;
|
||||
PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv;
|
||||
PFNGLUSEPROGRAMPROC UseProgram;
|
||||
PFNGLVERTEXATTRIBPOINTERPROC VertexAttribPointer;
|
||||
PFNGLVIEWPORTPROC Viewport;
|
||||
} gl;
|
||||
};
|
||||
|
||||
GL3W_API extern union ImGL3WProcs imgl3wProcs;
|
||||
|
||||
/* OpenGL functions */
|
||||
#define glActiveTexture imgl3wProcs.gl.ActiveTexture
|
||||
#define glAttachShader imgl3wProcs.gl.AttachShader
|
||||
#define glBindBuffer imgl3wProcs.gl.BindBuffer
|
||||
#define glBindSampler imgl3wProcs.gl.BindSampler
|
||||
#define glBindTexture imgl3wProcs.gl.BindTexture
|
||||
#define glBindVertexArray imgl3wProcs.gl.BindVertexArray
|
||||
#define glBlendEquation imgl3wProcs.gl.BlendEquation
|
||||
#define glBlendEquationSeparate imgl3wProcs.gl.BlendEquationSeparate
|
||||
#define glBlendFuncSeparate imgl3wProcs.gl.BlendFuncSeparate
|
||||
#define glBufferData imgl3wProcs.gl.BufferData
|
||||
#define glBufferSubData imgl3wProcs.gl.BufferSubData
|
||||
#define glClear imgl3wProcs.gl.Clear
|
||||
#define glClearColor imgl3wProcs.gl.ClearColor
|
||||
#define glCompileShader imgl3wProcs.gl.CompileShader
|
||||
#define glCreateProgram imgl3wProcs.gl.CreateProgram
|
||||
#define glCreateShader imgl3wProcs.gl.CreateShader
|
||||
#define glDeleteBuffers imgl3wProcs.gl.DeleteBuffers
|
||||
#define glDeleteProgram imgl3wProcs.gl.DeleteProgram
|
||||
#define glDeleteShader imgl3wProcs.gl.DeleteShader
|
||||
#define glDeleteTextures imgl3wProcs.gl.DeleteTextures
|
||||
#define glDeleteVertexArrays imgl3wProcs.gl.DeleteVertexArrays
|
||||
#define glDetachShader imgl3wProcs.gl.DetachShader
|
||||
#define glDisable imgl3wProcs.gl.Disable
|
||||
#define glDisableVertexAttribArray imgl3wProcs.gl.DisableVertexAttribArray
|
||||
#define glDrawElements imgl3wProcs.gl.DrawElements
|
||||
#define glDrawElementsBaseVertex imgl3wProcs.gl.DrawElementsBaseVertex
|
||||
#define glEnable imgl3wProcs.gl.Enable
|
||||
#define glEnableVertexAttribArray imgl3wProcs.gl.EnableVertexAttribArray
|
||||
#define glFlush imgl3wProcs.gl.Flush
|
||||
#define glGenBuffers imgl3wProcs.gl.GenBuffers
|
||||
#define glGenTextures imgl3wProcs.gl.GenTextures
|
||||
#define glGenVertexArrays imgl3wProcs.gl.GenVertexArrays
|
||||
#define glGetAttribLocation imgl3wProcs.gl.GetAttribLocation
|
||||
#define glGetError imgl3wProcs.gl.GetError
|
||||
#define glGetIntegerv imgl3wProcs.gl.GetIntegerv
|
||||
#define glGetProgramInfoLog imgl3wProcs.gl.GetProgramInfoLog
|
||||
#define glGetProgramiv imgl3wProcs.gl.GetProgramiv
|
||||
#define glGetShaderInfoLog imgl3wProcs.gl.GetShaderInfoLog
|
||||
#define glGetShaderiv imgl3wProcs.gl.GetShaderiv
|
||||
#define glGetString imgl3wProcs.gl.GetString
|
||||
#define glGetStringi imgl3wProcs.gl.GetStringi
|
||||
#define glGetUniformLocation imgl3wProcs.gl.GetUniformLocation
|
||||
#define glGetVertexAttribPointerv imgl3wProcs.gl.GetVertexAttribPointerv
|
||||
#define glGetVertexAttribiv imgl3wProcs.gl.GetVertexAttribiv
|
||||
#define glIsEnabled imgl3wProcs.gl.IsEnabled
|
||||
#define glIsProgram imgl3wProcs.gl.IsProgram
|
||||
#define glLinkProgram imgl3wProcs.gl.LinkProgram
|
||||
#define glPixelStorei imgl3wProcs.gl.PixelStorei
|
||||
#define glPolygonMode imgl3wProcs.gl.PolygonMode
|
||||
#define glReadPixels imgl3wProcs.gl.ReadPixels
|
||||
#define glScissor imgl3wProcs.gl.Scissor
|
||||
#define glShaderSource imgl3wProcs.gl.ShaderSource
|
||||
#define glTexImage2D imgl3wProcs.gl.TexImage2D
|
||||
#define glTexParameteri imgl3wProcs.gl.TexParameteri
|
||||
#define glUniform1i imgl3wProcs.gl.Uniform1i
|
||||
#define glUniformMatrix4fv imgl3wProcs.gl.UniformMatrix4fv
|
||||
#define glUseProgram imgl3wProcs.gl.UseProgram
|
||||
#define glVertexAttribPointer imgl3wProcs.gl.VertexAttribPointer
|
||||
#define glViewport imgl3wProcs.gl.Viewport
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef IMGL3W_IMPL
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define GL3W_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#if defined(_WIN32)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
static HMODULE libgl;
|
||||
typedef PROC(__stdcall* GL3WglGetProcAddr)(LPCSTR);
|
||||
static GL3WglGetProcAddr wgl_get_proc_address;
|
||||
|
||||
static int open_libgl(void)
|
||||
{
|
||||
libgl = LoadLibraryA("opengl32.dll");
|
||||
if (!libgl)
|
||||
return GL3W_ERROR_LIBRARY_OPEN;
|
||||
wgl_get_proc_address = (GL3WglGetProcAddr)GetProcAddress(libgl, "wglGetProcAddress");
|
||||
return GL3W_OK;
|
||||
}
|
||||
|
||||
static void close_libgl(void) { FreeLibrary(libgl); }
|
||||
static GL3WglProc get_proc(const char *proc)
|
||||
{
|
||||
GL3WglProc res;
|
||||
res = (GL3WglProc)wgl_get_proc_address(proc);
|
||||
if (!res)
|
||||
res = (GL3WglProc)GetProcAddress(libgl, proc);
|
||||
return res;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
#include <dlfcn.h>
|
||||
|
||||
static void *libgl;
|
||||
static int open_libgl(void)
|
||||
{
|
||||
libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!libgl)
|
||||
return GL3W_ERROR_LIBRARY_OPEN;
|
||||
return GL3W_OK;
|
||||
}
|
||||
|
||||
static void close_libgl(void) { dlclose(libgl); }
|
||||
|
||||
static GL3WglProc get_proc(const char *proc)
|
||||
{
|
||||
GL3WglProc res;
|
||||
*(void **)(&res) = dlsym(libgl, proc);
|
||||
return res;
|
||||
}
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
|
||||
static void* libgl; // OpenGL library
|
||||
static void* libglx; // GLX library
|
||||
static void* libegl; // EGL library
|
||||
static GL3WGetProcAddressProc gl_get_proc_address;
|
||||
|
||||
static void close_libgl(void)
|
||||
{
|
||||
if (libgl) {
|
||||
dlclose(libgl);
|
||||
libgl = NULL;
|
||||
}
|
||||
if (libegl) {
|
||||
dlclose(libegl);
|
||||
libegl = NULL;
|
||||
}
|
||||
if (libglx) {
|
||||
dlclose(libglx);
|
||||
libglx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int is_library_loaded(const char* name, void** lib)
|
||||
{
|
||||
*lib = dlopen(name, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
|
||||
return *lib != NULL;
|
||||
}
|
||||
|
||||
static int open_libs(void)
|
||||
{
|
||||
// On Linux we have two APIs to get process addresses: EGL and GLX.
|
||||
// EGL is supported under both X11 and Wayland, whereas GLX is X11-specific.
|
||||
|
||||
libgl = NULL;
|
||||
libegl = NULL;
|
||||
libglx = NULL;
|
||||
|
||||
// First check what's already loaded, the windowing library might have
|
||||
// already loaded either EGL or GLX and we want to use the same one.
|
||||
|
||||
if (is_library_loaded("libEGL.so.1", &libegl) ||
|
||||
is_library_loaded("libGLX.so.0", &libglx)) {
|
||||
libgl = dlopen("libOpenGL.so.0", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (libgl)
|
||||
return GL3W_OK;
|
||||
else
|
||||
close_libgl();
|
||||
}
|
||||
|
||||
if (is_library_loaded("libGL.so", &libgl))
|
||||
return GL3W_OK;
|
||||
if (is_library_loaded("libGL.so.1", &libgl))
|
||||
return GL3W_OK;
|
||||
if (is_library_loaded("libGL.so.3", &libgl))
|
||||
return GL3W_OK;
|
||||
|
||||
// Neither is already loaded, so we have to load one. Try EGL first
|
||||
// because it is supported under both X11 and Wayland.
|
||||
|
||||
// Load OpenGL + EGL
|
||||
libgl = dlopen("libOpenGL.so.0", RTLD_LAZY | RTLD_LOCAL);
|
||||
libegl = dlopen("libEGL.so.1", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (libgl && libegl)
|
||||
return GL3W_OK;
|
||||
else
|
||||
close_libgl();
|
||||
|
||||
// Fall back to legacy libGL, which includes GLX
|
||||
// While most systems use libGL.so.1, NetBSD seems to use that libGL.so.3. See https://github.com/ocornut/imgui/issues/6983
|
||||
libgl = dlopen("libGL.so", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!libgl)
|
||||
libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!libgl)
|
||||
libgl = dlopen("libGL.so.3", RTLD_LAZY | RTLD_LOCAL);
|
||||
|
||||
if (libgl)
|
||||
return GL3W_OK;
|
||||
|
||||
return GL3W_ERROR_LIBRARY_OPEN;
|
||||
}
|
||||
|
||||
static int open_libgl(void)
|
||||
{
|
||||
int res = open_libs();
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (libegl)
|
||||
*(void**)(&gl_get_proc_address) = dlsym(libegl, "eglGetProcAddress");
|
||||
else if (libglx)
|
||||
*(void**)(&gl_get_proc_address) = dlsym(libglx, "glXGetProcAddressARB");
|
||||
else
|
||||
*(void**)(&gl_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB");
|
||||
|
||||
if (!gl_get_proc_address) {
|
||||
close_libgl();
|
||||
return GL3W_ERROR_LIBRARY_OPEN;
|
||||
}
|
||||
|
||||
return GL3W_OK;
|
||||
}
|
||||
|
||||
static GL3WglProc get_proc(const char* proc)
|
||||
{
|
||||
GL3WglProc res = NULL;
|
||||
|
||||
// Before EGL version 1.5, eglGetProcAddress doesn't support querying core
|
||||
// functions and may return a dummy function if we try, so try to load the
|
||||
// function from the GL library directly first.
|
||||
if (libegl)
|
||||
*(void**)(&res) = dlsym(libgl, proc);
|
||||
|
||||
if (!res)
|
||||
res = gl_get_proc_address(proc);
|
||||
|
||||
if (!libegl && !res)
|
||||
*(void**)(&res) = dlsym(libgl, proc);
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct { int major, minor; } version;
|
||||
|
||||
static int parse_version(void)
|
||||
{
|
||||
if (!glGetIntegerv)
|
||||
return GL3W_ERROR_INIT;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &version.major);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &version.minor);
|
||||
if (version.major == 0 && version.minor == 0)
|
||||
{
|
||||
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
|
||||
if (const char* gl_version = (const char*)glGetString(GL_VERSION))
|
||||
sscanf(gl_version, "%d.%d", &version.major, &version.minor);
|
||||
}
|
||||
if (version.major < 2)
|
||||
return GL3W_ERROR_OPENGL_VERSION;
|
||||
return GL3W_OK;
|
||||
}
|
||||
|
||||
static void load_procs(GL3WGetProcAddressProc proc);
|
||||
|
||||
int imgl3wInit(void)
|
||||
{
|
||||
int res = open_libgl();
|
||||
if (res)
|
||||
return res;
|
||||
atexit(close_libgl);
|
||||
return imgl3wInit2(get_proc);
|
||||
}
|
||||
|
||||
int imgl3wInit2(GL3WGetProcAddressProc proc)
|
||||
{
|
||||
load_procs(proc);
|
||||
return parse_version();
|
||||
}
|
||||
|
||||
int imgl3wIsSupported(int major, int minor)
|
||||
{
|
||||
if (major < 2)
|
||||
return 0;
|
||||
if (version.major == major)
|
||||
return version.minor >= minor;
|
||||
return version.major >= major;
|
||||
}
|
||||
|
||||
GL3WglProc imgl3wGetProcAddress(const char *proc) { return get_proc(proc); }
|
||||
|
||||
static const char *proc_names[] = {
|
||||
"glActiveTexture",
|
||||
"glAttachShader",
|
||||
"glBindBuffer",
|
||||
"glBindSampler",
|
||||
"glBindTexture",
|
||||
"glBindVertexArray",
|
||||
"glBlendEquation",
|
||||
"glBlendEquationSeparate",
|
||||
"glBlendFuncSeparate",
|
||||
"glBufferData",
|
||||
"glBufferSubData",
|
||||
"glClear",
|
||||
"glClearColor",
|
||||
"glCompileShader",
|
||||
"glCreateProgram",
|
||||
"glCreateShader",
|
||||
"glDeleteBuffers",
|
||||
"glDeleteProgram",
|
||||
"glDeleteShader",
|
||||
"glDeleteTextures",
|
||||
"glDeleteVertexArrays",
|
||||
"glDetachShader",
|
||||
"glDisable",
|
||||
"glDisableVertexAttribArray",
|
||||
"glDrawElements",
|
||||
"glDrawElementsBaseVertex",
|
||||
"glEnable",
|
||||
"glEnableVertexAttribArray",
|
||||
"glFlush",
|
||||
"glGenBuffers",
|
||||
"glGenTextures",
|
||||
"glGenVertexArrays",
|
||||
"glGetAttribLocation",
|
||||
"glGetError",
|
||||
"glGetIntegerv",
|
||||
"glGetProgramInfoLog",
|
||||
"glGetProgramiv",
|
||||
"glGetShaderInfoLog",
|
||||
"glGetShaderiv",
|
||||
"glGetString",
|
||||
"glGetStringi",
|
||||
"glGetUniformLocation",
|
||||
"glGetVertexAttribPointerv",
|
||||
"glGetVertexAttribiv",
|
||||
"glIsEnabled",
|
||||
"glIsProgram",
|
||||
"glLinkProgram",
|
||||
"glPixelStorei",
|
||||
"glPolygonMode",
|
||||
"glReadPixels",
|
||||
"glScissor",
|
||||
"glShaderSource",
|
||||
"glTexImage2D",
|
||||
"glTexParameteri",
|
||||
"glUniform1i",
|
||||
"glUniformMatrix4fv",
|
||||
"glUseProgram",
|
||||
"glVertexAttribPointer",
|
||||
"glViewport",
|
||||
};
|
||||
|
||||
GL3W_API union ImGL3WProcs imgl3wProcs;
|
||||
|
||||
static void load_procs(GL3WGetProcAddressProc proc)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < GL3W_ARRAY_SIZE(proc_names); i++)
|
||||
imgl3wProcs.ptr[i] = proc(proc_names[i]);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
3918
thirdparty/imgui/include/imgui_internal.h
vendored
Normal file
3918
thirdparty/imgui/include/imgui_internal.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
25
thirdparty/imgui/include/imgui_stdlib.h
vendored
Normal file
25
thirdparty/imgui/include/imgui_stdlib.h
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||
// This is also an example of how you may wrap your own similar types.
|
||||
|
||||
// Changelog:
|
||||
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||
|
||||
// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki:
|
||||
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
// ImGui::InputText() with std::string
|
||||
// Because text input needs dynamic resizing, we need to setup a callback to grow the capacity
|
||||
IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||
}
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
627
thirdparty/imgui/include/imstb_rectpack.h
vendored
Normal file
627
thirdparty/imgui/include/imstb_rectpack.h
vendored
Normal file
@ -0,0 +1,627 @@
|
||||
// [DEAR IMGUI]
|
||||
// This is a slightly modified version of stb_rect_pack.h 1.01.
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
//
|
||||
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Before #including,
|
||||
//
|
||||
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||
//
|
||||
// in the file that you want to have the implementation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
//
|
||||
// Has only had a few tests run, may have issues.
|
||||
//
|
||||
// More docs to come.
|
||||
//
|
||||
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||
//
|
||||
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||
//
|
||||
// Please note: better rectangle packers are welcome! Please
|
||||
// implement them to the same API, but with a different init
|
||||
// function.
|
||||
//
|
||||
// Credits
|
||||
//
|
||||
// Library
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
// Fabian Giesen
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||
// 0.01: initial release
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// INCLUDE SECTION
|
||||
//
|
||||
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
typedef int stbrp_coord;
|
||||
|
||||
#define STBRP__MAXVAL 0x7fffffff
|
||||
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
//
|
||||
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||
// have the 'was_packed' flag set to 0.
|
||||
//
|
||||
// You should not try to access the 'rects' array from another thread
|
||||
// while this function is running, as the function temporarily reorders
|
||||
// the array while it executes.
|
||||
//
|
||||
// To pack into another rectangle, you need to call stbrp_init_target
|
||||
// again. To continue packing into the same rectangle, you can call
|
||||
// this function again. Calling this multiple times with multiple rect
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
// reserved for your use:
|
||||
int id;
|
||||
|
||||
// input:
|
||||
stbrp_coord w, h;
|
||||
|
||||
// output:
|
||||
stbrp_coord x, y;
|
||||
int was_packed; // non-zero if valid packing
|
||||
|
||||
}; // 16 bytes, nominally
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
// Initialize a rectangle packer to:
|
||||
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||
//
|
||||
// You must call this function every time you start packing into a new target.
|
||||
//
|
||||
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||
// the call (or calls) finish.
|
||||
//
|
||||
// Note: to guarantee best results, either:
|
||||
// 1. make sure 'num_nodes' >= 'width'
|
||||
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||
//
|
||||
// If you don't do either of the above things, widths will be quantized to multiples
|
||||
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||
//
|
||||
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||
// may run out of temporary storage and be unable to pack some rectangles.
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
// Optionally call this function after init but before doing any packing to
|
||||
// change the handling of the out-of-temp-memory scenario, described above.
|
||||
// If you call init again, this will be reset to the default (false).
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
// Optionally select which packing heuristic the library should use. Different
|
||||
// heuristics will produce better/worse results for different data sets.
|
||||
// If you call init again, this will be reset to the default.
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// the details of the following structures don't matter to you, but they must
|
||||
// be visible so you can handle the memory allocations for them
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION SECTION
|
||||
//
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#ifndef STBRP_SORT
|
||||
#include <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
// if it's ok to run out of memory, then don't bother aligning them;
|
||||
// this gives better packing, but may fail due to OOM (even though
|
||||
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||
context->align = 1;
|
||||
else {
|
||||
// if it's not ok to run out of memory, then quantize the widths
|
||||
// so that num_nodes is always enough nodes.
|
||||
//
|
||||
// I.e. num_nodes * align >= width
|
||||
// align >= width / num_nodes
|
||||
// align = ceil(width/num_nodes)
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
context->extra[1].y = (1<<30);
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
// find minimum y position if it starts at x1
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
|
||||
STBRP__NOTUSED(c);
|
||||
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
|
||||
#if 0
|
||||
// skip in case we're past the node
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
// add waste area
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
// align to multiple of c->align
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
// if it can't possibly fit, bail immediately
|
||||
if (width > c->width || height > c->height) {
|
||||
fr.prev_link = NULL;
|
||||
fr.x = fr.y = 0;
|
||||
return fr;
|
||||
}
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||
// bottom left
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
// best-fit
|
||||
if (y + height <= c->height) {
|
||||
// can only use it if it first vertically
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||
//
|
||||
// e.g, if fitting
|
||||
//
|
||||
// ____________________
|
||||
// |____________________|
|
||||
//
|
||||
// into
|
||||
//
|
||||
// | |
|
||||
// | ____________|
|
||||
// |____________|
|
||||
//
|
||||
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||
//
|
||||
// This makes BF take about 2x the time
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
// find first node that's admissible
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
// find the left position that matches this
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height <= c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
//STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
// find best position according to heuristic
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
// bail if:
|
||||
// 1. it failed
|
||||
// 2. the best node doesn't fit (we don't always check this)
|
||||
// 3. we're out of memory
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
// on success, create new node
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
// preserve the existing one, so start testing with the next one
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
// from here, traverse cur and free the nodes, until we get to one
|
||||
// that shouldn't be freed
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
// move the current node to the free list
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
// stitch the list back in
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||
} else {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
1469
thirdparty/imgui/include/imstb_textedit.h
vendored
Normal file
1469
thirdparty/imgui/include/imstb_textedit.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5085
thirdparty/imgui/include/imstb_truetype.h
vendored
Normal file
5085
thirdparty/imgui/include/imstb_truetype.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
22836
thirdparty/imgui/src/imgui.cpp
vendored
Normal file
22836
thirdparty/imgui/src/imgui.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10740
thirdparty/imgui/src/imgui_demo.cpp
vendored
Normal file
10740
thirdparty/imgui/src/imgui_demo.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4866
thirdparty/imgui/src/imgui_draw.cpp
vendored
Normal file
4866
thirdparty/imgui/src/imgui_draw.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1453
thirdparty/imgui/src/imgui_impl_glfw.cpp
vendored
Normal file
1453
thirdparty/imgui/src/imgui_impl_glfw.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1006
thirdparty/imgui/src/imgui_impl_opengl3.cpp
vendored
Normal file
1006
thirdparty/imgui/src/imgui_impl_opengl3.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
88
thirdparty/imgui/src/imgui_stdlib.cpp
vendored
Normal file
88
thirdparty/imgui/src/imgui_stdlib.cpp
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||
// This is also an example of how you may wrap your own similar types.
|
||||
|
||||
// Changelog:
|
||||
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||
|
||||
// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki:
|
||||
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_stdlib.h"
|
||||
|
||||
// Clang warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#endif
|
||||
|
||||
struct InputTextCallback_UserData
|
||||
{
|
||||
std::string* Str;
|
||||
ImGuiInputTextCallback ChainCallback;
|
||||
void* ChainCallbackUserData;
|
||||
};
|
||||
|
||||
static int InputTextCallback(ImGuiInputTextCallbackData* data)
|
||||
{
|
||||
InputTextCallback_UserData* user_data = (InputTextCallback_UserData*)data->UserData;
|
||||
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
|
||||
{
|
||||
// Resize string callback
|
||||
// If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want.
|
||||
std::string* str = user_data->Str;
|
||||
IM_ASSERT(data->Buf == str->c_str());
|
||||
str->resize(data->BufTextLen);
|
||||
data->Buf = (char*)str->c_str();
|
||||
}
|
||||
else if (user_data->ChainCallback)
|
||||
{
|
||||
// Forward to user callback, if any
|
||||
data->UserData = user_data->ChainCallbackUserData;
|
||||
return user_data->ChainCallback(data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ImGui::InputText(const char* label, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||
{
|
||||
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||
|
||||
InputTextCallback_UserData cb_user_data;
|
||||
cb_user_data.Str = str;
|
||||
cb_user_data.ChainCallback = callback;
|
||||
cb_user_data.ChainCallbackUserData = user_data;
|
||||
return InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||
}
|
||||
|
||||
bool ImGui::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||
{
|
||||
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||
|
||||
InputTextCallback_UserData cb_user_data;
|
||||
cb_user_data.Str = str;
|
||||
cb_user_data.ChainCallback = callback;
|
||||
cb_user_data.ChainCallbackUserData = user_data;
|
||||
return InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cb_user_data);
|
||||
}
|
||||
|
||||
bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||
{
|
||||
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||
|
||||
InputTextCallback_UserData cb_user_data;
|
||||
cb_user_data.Str = str;
|
||||
cb_user_data.ChainCallback = callback;
|
||||
cb_user_data.ChainCallbackUserData = user_data;
|
||||
return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
4501
thirdparty/imgui/src/imgui_tables.cpp
vendored
Normal file
4501
thirdparty/imgui/src/imgui_tables.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10529
thirdparty/imgui/src/imgui_widgets.cpp
vendored
Normal file
10529
thirdparty/imgui/src/imgui_widgets.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user