diff --git a/archive/Bigfoot71/r3d.txt b/archive/Bigfoot71/r3d.txt new file mode 100644 index 00000000..df8b1e33 --- /dev/null +++ b/archive/Bigfoot71/r3d.txt @@ -0,0 +1,62810 @@ +Project Path: arc_Bigfoot71_r3d_zc6i1tj6 + +Source Tree: + +```txt +arc_Bigfoot71_r3d_zc6i1tj6 +├── CMakeLists.txt +├── CMakePresets.json +├── Doxyfile.in +├── LICENSE +├── README.md +├── ROADMAP.md +├── assets +│ ├── brdf_lut_512_rg16_float.raw +│ ├── smaa_area_160x560_rg8.raw +│ └── smaa_search_64x16_r8.raw +├── cmake +│ ├── EmbedAssets.cmake +│ ├── EmbedShaders.cmake +│ ├── mingw-w32-x86_64.cmake +│ └── mingw-w64-x86_64.cmake +├── docs +│ └── shaders +│ ├── screen_shader.md +│ ├── sky_shader.md +│ └── surface_shader.md +├── examples +│ ├── CMakeLists.txt +│ ├── animation.c +│ ├── animtree.c +│ ├── basic.c +│ ├── basic.cpp +│ ├── billboards.c +│ ├── bloom.c +│ ├── decal.c +│ ├── dof.c +│ ├── instanced.c +│ ├── kinematics.c +│ ├── lights.c +│ ├── multiview.c +│ ├── particles.c +│ ├── pbr.c +│ ├── probe.c +│ ├── resize.c +│ ├── resources +│ │ ├── images +│ │ ├── models +│ │ ├── panorama +│ │ └── shaders +│ │ ├── material.glsl +│ │ ├── screen.glsl +│ │ └── sky.glsl +│ ├── screenshots +│ ├── shader.c +│ ├── skybox.c +│ ├── sponza.c +│ ├── sprite.c +│ ├── stencil.c +│ ├── sun.c +│ ├── to_texture.c +│ └── transparency.c +├── external +│ ├── assimp +│ ├── glad +│ │ └── glad.h +│ ├── raylib +│ ├── tinycthread +│ │ ├── README.txt +│ │ ├── tinycthread.c +│ │ └── tinycthread.h +│ └── uthash +│ ├── LICENSE +│ ├── utarray.h +│ ├── uthash.h +│ ├── utlist.h +│ ├── utringbuffer.h +│ ├── utstack.h +│ └── utstring.h +├── include +│ └── r3d +│ ├── r3d.h +│ ├── r3d_ambient_map.h +│ ├── r3d_animation.h +│ ├── r3d_animation_player.h +│ ├── r3d_animation_tree.h +│ ├── r3d_camera.h +│ ├── r3d_color.h +│ ├── r3d_core.h +│ ├── r3d_cubemap.h +│ ├── r3d_decal.h +│ ├── r3d_draw.h +│ ├── r3d_environment.h +│ ├── r3d_frustum.h +│ ├── r3d_importer.h +│ ├── r3d_instance.h +│ ├── r3d_kinematics.h +│ ├── r3d_lighting.h +│ ├── r3d_material.h +│ ├── r3d_mesh.h +│ ├── r3d_mesh_data.h +│ ├── r3d_model.h +│ ├── r3d_pack.h +│ ├── r3d_platform.h +│ ├── r3d_probe.h +│ ├── r3d_screen_shader.h +│ ├── r3d_shape.h +│ ├── r3d_skeleton.h +│ ├── r3d_sky.h +│ ├── r3d_sky_shader.h +│ ├── r3d_surface_shader.h +│ ├── r3d_texture.h +│ ├── r3d_utils.h +│ └── r3d_vertex.h +├── scripts +│ ├── bin2c.py +│ └── glsl_processor.py +├── shaders +│ ├── blit +│ │ ├── down_pdss.frag +│ │ ├── down_rgss.frag +│ │ ├── up_bicubic.frag +│ │ └── up_lanczos.frag +│ ├── deferred +│ │ ├── ambient.frag +│ │ ├── compose.frag +│ │ ├── fog.frag +│ │ └── lighting.frag +│ ├── external +│ │ ├── Fxaa3_11.h +│ │ └── SMAA.hlsl +│ ├── generic +│ │ ├── color.frag +│ │ ├── cubemap.vert +│ │ └── screen.vert +│ ├── include +│ │ ├── blocks +│ │ │ ├── env.glsl +│ │ │ ├── fog.glsl +│ │ │ ├── frame.glsl +│ │ │ ├── light.glsl +│ │ │ └── view.glsl +│ │ ├── math.glsl +│ │ ├── pbr.glsl +│ │ ├── smaa.glsl +│ │ └── user +│ │ ├── scene.frag +│ │ └── scene.vert +│ ├── post +│ │ ├── auto_exposure.frag +│ │ ├── bloom.frag +│ │ ├── dof.frag +│ │ ├── fxaa.frag +│ │ ├── output.frag +│ │ ├── screen.frag +│ │ ├── smaa.frag +│ │ ├── smaa.vert +│ │ └── visualizer.frag +│ ├── prepare +│ │ ├── bloom_down.frag +│ │ ├── bloom_up.frag +│ │ ├── blur_down.frag +│ │ ├── blur_up.frag +│ │ ├── cubemap_custom_sky.frag +│ │ ├── cubemap_from_equirectangular.frag +│ │ ├── cubemap_irradiance.frag +│ │ ├── cubemap_prefilter.frag +│ │ ├── cubemap_procedural_sky.frag +│ │ ├── denoiser_atrous.frag +│ │ ├── denoiser_sparse.frag +│ │ ├── depth_pyramid.frag +│ │ ├── dof_blur.frag +│ │ ├── dof_coc.frag +│ │ ├── dof_down.frag +│ │ ├── exposure_adapt.frag +│ │ ├── luminance.frag +│ │ ├── smaa_blending_weigths.frag +│ │ ├── smaa_blending_weigths.vert +│ │ ├── smaa_edge_detection.frag +│ │ ├── smaa_edge_detection.vert +│ │ ├── ssao.frag +│ │ ├── ssao_in_down.frag +│ │ ├── ssgi.frag +│ │ ├── ssgi_in_down.frag +│ │ ├── ssil.frag +│ │ ├── ssil_in_down.frag +│ │ ├── ssr.frag +│ │ └── ssr_in_down.frag +│ └── scene +│ ├── decal.frag +│ ├── depth.frag +│ ├── depth_cube.frag +│ ├── forward.frag +│ ├── geometry.frag +│ ├── scene.vert +│ ├── skybox.frag +│ ├── skybox.vert +│ └── unlit.frag +└── src + ├── common + │ ├── r3d_anim.c + │ ├── r3d_anim.h + │ ├── r3d_half.h + │ ├── r3d_hash.h + │ ├── r3d_helper.c + │ ├── r3d_helper.h + │ ├── r3d_image.c + │ ├── r3d_image.h + │ ├── r3d_math.h + │ ├── r3d_pass.c + │ ├── r3d_pass.h + │ └── r3d_rshade.h + ├── importer + │ ├── r3d_importer_animation.c + │ ├── r3d_importer_internal.h + │ ├── r3d_importer_material.c + │ ├── r3d_importer_mesh.c + │ ├── r3d_importer_skeleton.c + │ └── r3d_importer_texture.c + ├── modules + │ ├── r3d_driver.c + │ ├── r3d_driver.h + │ ├── r3d_env.c + │ ├── r3d_env.h + │ ├── r3d_light.c + │ ├── r3d_light.h + │ ├── r3d_render.c + │ ├── r3d_render.h + │ ├── r3d_shader.c + │ ├── r3d_shader.h + │ ├── r3d_target.c + │ ├── r3d_target.h + │ ├── r3d_texture.c + │ └── r3d_texture.h + ├── r3d_ambient_map.c + ├── r3d_animation.c + ├── r3d_animation_player.c + ├── r3d_animation_tree.c + ├── r3d_camera.c + ├── r3d_color.c + ├── r3d_config.h.in + ├── r3d_core.c + ├── r3d_core_state.h + ├── r3d_cubemap.c + ├── r3d_decal.c + ├── r3d_draw.c + ├── r3d_environment.c + ├── r3d_frustum.c + ├── r3d_importer.c + ├── r3d_instance.c + ├── r3d_kinematics.c + ├── r3d_lighting.c + ├── r3d_material.c + ├── r3d_mesh.c + ├── r3d_mesh_data.c + ├── r3d_model.c + ├── r3d_pack.c + ├── r3d_probe.c + ├── r3d_screen_shader.c + ├── r3d_shape.c + ├── r3d_skeleton.c + ├── r3d_sky.c + ├── r3d_sky_shader.c + ├── r3d_surface_shader.c + ├── r3d_texture.c + ├── r3d_utils.c + └── r3d_vertex.c + +``` + +`CMakeLists.txt`: + +```txt +cmake_minimum_required(VERSION 3.8) +project(r3d VERSION 0.10.0 LANGUAGES C CXX) + +# ======================================== +# Standards and Policies +# ======================================== + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) + +if(POLICY CMP0141) + cmake_policy(SET CMP0141 NEW) + # This section configures MSVC (Visual Studio) to enable "Edit and Continue" debugging + set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") +endif() + +# ======================================== +# Project Configuration +# ======================================== + +set(R3D_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(R3D_IS_MAIN ON) +else() + set(R3D_IS_MAIN OFF) +endif() + +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${R3D_ROOT_PATH}/cmake") + +option(R3D_RAYLIB_VENDORED "Use vendored raylib from submodule" OFF) +option(R3D_ASSIMP_VENDORED "Use vendored assimp from submodule" OFF) +option(R3D_SUPPORT_ASSIMP "Build with Assimp importer support" ON) +option(R3D_BUILD_EXAMPLES "Build the examples" ${R3D_IS_MAIN}) +option(R3D_BUILD_DOCS "Build the doxygen documentation" OFF) + +# ======================================== +# Output Directories +# ======================================== + +set(R3D_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +set(R3D_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") +set(R3D_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${R3D_RUNTIME_OUTPUT_DIRECTORY}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${R3D_LIBRARY_OUTPUT_DIRECTORY}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${R3D_ARCHIVE_OUTPUT_DIRECTORY}) + +# ======================================== +# R3D Compile-Time Configuration +# ======================================== + +set(R3D_CONFIG_DEFAULTS + R3D_MAX_LIGHT_FORWARD_PER_MESH 8 + R3D_MAX_PROBE_ON_SCREEN 8 + R3D_PROBE_CAPTURE_SIZE 256 + R3D_SHADOW_MAP_DIRECTIONAL_SIZE 4096 + R3D_SHADOW_MAP_SPOT_SIZE 2048 + R3D_SHADOW_MAP_OMNI_SIZE 2048 + R3D_CUBEMAP_IRRADIANCE_SIZE 32 + R3D_CUBEMAP_PREFILTER_SIZE 128 + R3D_MAX_SHADER_CODE_LENGTH 16384 + R3D_MAX_SHADER_SAMPLERS 4 + R3D_MAX_SHADER_UNIFORMS 16 + R3D_MAX_SCREEN_SHADERS 4 + R3D_ENABLE_TRACELOG 1 +) + +list(LENGTH R3D_CONFIG_DEFAULTS _len) +math(EXPR _count "${_len} / 2") +math(EXPR _last "${_count} - 1") + +foreach(i RANGE 0 ${_last}) + math(EXPR key_index "2 * ${i}") + math(EXPR val_index "2 * ${i} + 1") + list(GET R3D_CONFIG_DEFAULTS ${key_index} key) + list(GET R3D_CONFIG_DEFAULTS ${val_index} val) + set(${key} ${val} CACHE STRING "R3D compile-time configuration") +endforeach() + +set(R3D_GENERATED_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated/include") + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/src/r3d_config.h.in + ${R3D_GENERATED_INCLUDE_DIR}/r3d_config.h + @ONLY +) + +# ======================================== +# Dependencies: Assimp +# ======================================== + +if(R3D_SUPPORT_ASSIMP) + if(R3D_ASSIMP_VENDORED) + set(R3D_ASSIMP_SUBMODULE_PATH "${R3D_ROOT_PATH}/external/assimp") + set(R3D_ASSIMP_CMAKELISTS "${R3D_ASSIMP_SUBMODULE_PATH}/CMakeLists.txt") + + if(NOT EXISTS "${R3D_ASSIMP_CMAKELISTS}") + message(FATAL_ERROR + "Vendored assimp not found!\n" + "Missing: ${R3D_ASSIMP_CMAKELISTS}\n" + "Please initialize the submodule with:\n" + " git submodule update --init --recursive") + endif() + + message(STATUS "Using vendored assimp from: ${R3D_ASSIMP_SUBMODULE_PATH}") + + # Common settings + set(ASSIMP_INJECT_DEBUG_POSTFIX OFF CACHE BOOL "") + set(ASSIMP_WARNINGS_AS_ERRORS OFF CACHE BOOL "") + set(ASSIMP_IGNORE_GIT_HASH ON CACHE BOOL "") + set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "") + set(ASSIMP_NO_EXPORT ON CACHE BOOL "") + set(ASSIMP_INSTALL OFF CACHE BOOL "") + + # Enabled importers + # Note: There may be conflicts between Assimp and raylib if both enable the M3D format + set(_ASSIMP_ENABLED_IMPORTERS + COLLADA GLTF OBJ FBX IQM + ) + + # Disabled importers + set(_ASSIMP_DISABLED_IMPORTERS + AMF BVH OFF COB STL 3DS AC ASE ASSBIN B3D DXF CSM HMP + IRRMESH IRR LWO LWS M3D MD2 MD3 MD5 MDC MDL NFF NDO OGRE + OPENGEX PLY MS3D BLEND IFC XGL Q3D Q3BSP RAW SIB SMD + TERRAGEN 3D X X3D 3MF MMD + ) + + foreach(importer ${_ASSIMP_ENABLED_IMPORTERS}) + set(ASSIMP_BUILD_${importer}_IMPORTER ON CACHE BOOL "") + endforeach() + + foreach(importer ${_ASSIMP_DISABLED_IMPORTERS}) + set(ASSIMP_BUILD_${importer}_IMPORTER OFF CACHE BOOL "") + endforeach() + + add_subdirectory("${R3D_ASSIMP_SUBMODULE_PATH}") + + set(R3D_ASSIMP_INC "${R3D_ASSIMP_SUBMODULE_PATH}/include" CACHE STRING "" FORCE) + set(R3D_ASSIMP_LIB "assimp" CACHE STRING "" FORCE) + + set_target_properties(assimp PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${R3D_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${R3D_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${R3D_ARCHIVE_OUTPUT_DIRECTORY} + ) + else() + find_package(assimp QUIET) + if(assimp_FOUND) + message(STATUS "Using system-installed Assimp") + set(R3D_ASSIMP_INC "${ASSIMP_INCLUDE_DIRS}" CACHE STRING "" FORCE) + set(R3D_ASSIMP_LIB "${ASSIMP_LIBRARIES}" CACHE STRING "" FORCE) + else() + message(FATAL_ERROR + "System Assimp not found.\n" + "Please install Assimp development files for your platform, " + "or use the vendored version included with this project.") + endif() + endif() +endif() + +# ======================================== +# Dependencies: Raylib +# ======================================== + +if(R3D_RAYLIB_VENDORED) + set(R3D_RAYLIB_SUBMODULE_PATH "${R3D_ROOT_PATH}/external/raylib") + set(R3D_RAYLIB_CMAKELISTS "${R3D_RAYLIB_SUBMODULE_PATH}/CMakeLists.txt") + + if(NOT EXISTS "${R3D_RAYLIB_CMAKELISTS}") + message(FATAL_ERROR + "Vendored raylib not found!\n" + "Missing: ${R3D_RAYLIB_CMAKELISTS}\n" + "Please initialize the submodule with:\n" + " git submodule update --init --recursive") + endif() + + message(STATUS "Using vendored raylib from: ${R3D_RAYLIB_SUBMODULE_PATH}") + + set(CUSTOMIZE_BUILD ON CACHE BOOL "" FORCE) + + # Enabled file formats + set(_RAYLIB_ENABLED_FORMATS + HDR JPG + ) + + # Disabled file formats + # r3d provides its own model importers via Assimp + # Note: There may be conflicts between raylib and Assimp if both enable the M3D format + set(_RAYLIB_DISABLED_FORMATS + OBJ MTL IQM GLTF VOX M3D + ) + + foreach(format ${_RAYLIB_ENABLED_FORMATS}) + set(SUPPORT_FILEFORMAT_${format} ON CACHE BOOL "" FORCE) + endforeach() + + foreach(format ${_RAYLIB_DISABLED_FORMATS}) + set(SUPPORT_FILEFORMAT_${format} OFF CACHE BOOL "" FORCE) + endforeach() + + add_subdirectory("${R3D_RAYLIB_SUBMODULE_PATH}") + + set(R3D_RAYLIB_INC "${R3D_RAYLIB_SUBMODULE_PATH}/src" CACHE STRING "" FORCE) + set(R3D_RAYLIB_LIB "raylib" CACHE STRING "" FORCE) + + set_target_properties(raylib PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${R3D_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${R3D_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${R3D_ARCHIVE_OUTPUT_DIRECTORY} + ) +else() + find_package(raylib QUIET) + if(raylib_FOUND) + message(STATUS "Using system-installed raylib") + set(R3D_RAYLIB_INC "${RAYLIB_INCLUDE_DIRS}" CACHE STRING "" FORCE) + set(R3D_RAYLIB_LIB "${RAYLIB_LIBRARIES}" CACHE STRING "" FORCE) + else() + message(FATAL_ERROR + "System raylib not found.\n" + "Please install raylib development files for your platform, " + "or use the vendored version included with this project.") + endif() +endif() + +# ======================================== +# Library Definition +# ======================================== + +include(CheckCSourceCompiles) +include(CMakePushCheckState) +include(CheckLibraryExists) +include(EmbedShaders) +include(EmbedAssets) + +if(BUILD_SHARED_LIBS) + add_definitions(-DR3D_BUILD_SHARED) + add_definitions(-DGLAD_API_CALL_EXPORT) +endif() + +if(R3D_SUPPORT_ASSIMP) + set(R3D_IMPORTER_SOURCES + "${R3D_ROOT_PATH}/src/importer/r3d_importer_animation.c" + "${R3D_ROOT_PATH}/src/importer/r3d_importer_material.c" + "${R3D_ROOT_PATH}/src/importer/r3d_importer_skeleton.c" + "${R3D_ROOT_PATH}/src/importer/r3d_importer_texture.c" + "${R3D_ROOT_PATH}/src/importer/r3d_importer_mesh.c" + ) +endif() + +add_library(${PROJECT_NAME} + # Importer + ${R3D_IMPORTER_SOURCES} + # Common + "${R3D_ROOT_PATH}/src/common/r3d_anim.c" + "${R3D_ROOT_PATH}/src/common/r3d_helper.c" + "${R3D_ROOT_PATH}/src/common/r3d_image.c" + "${R3D_ROOT_PATH}/src/common/r3d_pass.c" + # Modules + "${R3D_ROOT_PATH}/src/modules/r3d_texture.c" + "${R3D_ROOT_PATH}/src/modules/r3d_target.c" + "${R3D_ROOT_PATH}/src/modules/r3d_shader.c" + "${R3D_ROOT_PATH}/src/modules/r3d_driver.c" + "${R3D_ROOT_PATH}/src/modules/r3d_light.c" + "${R3D_ROOT_PATH}/src/modules/r3d_render.c" + "${R3D_ROOT_PATH}/src/modules/r3d_env.c" + # Core + "${R3D_ROOT_PATH}/src/r3d_animation_player.c" + "${R3D_ROOT_PATH}/src/r3d_animation_tree.c" + "${R3D_ROOT_PATH}/src/r3d_animation.c" + "${R3D_ROOT_PATH}/src/r3d_color.c" + "${R3D_ROOT_PATH}/src/r3d_core.c" + "${R3D_ROOT_PATH}/src/r3d_camera.c" + "${R3D_ROOT_PATH}/src/r3d_cubemap.c" + "${R3D_ROOT_PATH}/src/r3d_draw.c" + "${R3D_ROOT_PATH}/src/r3d_decal.c" + "${R3D_ROOT_PATH}/src/r3d_ambient_map.c" + "${R3D_ROOT_PATH}/src/r3d_environment.c" + "${R3D_ROOT_PATH}/src/r3d_frustum.c" + "${R3D_ROOT_PATH}/src/r3d_importer.c" + "${R3D_ROOT_PATH}/src/r3d_instance.c" + "${R3D_ROOT_PATH}/src/r3d_kinematics.c" + "${R3D_ROOT_PATH}/src/r3d_lighting.c" + "${R3D_ROOT_PATH}/src/r3d_surface_shader.c" + "${R3D_ROOT_PATH}/src/r3d_screen_shader.c" + "${R3D_ROOT_PATH}/src/r3d_sky_shader.c" + "${R3D_ROOT_PATH}/src/r3d_material.c" + "${R3D_ROOT_PATH}/src/r3d_texture.c" + "${R3D_ROOT_PATH}/src/r3d_mesh.c" + "${R3D_ROOT_PATH}/src/r3d_mesh_data.c" + "${R3D_ROOT_PATH}/src/r3d_model.c" + "${R3D_ROOT_PATH}/src/r3d_pack.c" + "${R3D_ROOT_PATH}/src/r3d_probe.c" + "${R3D_ROOT_PATH}/src/r3d_skeleton.c" + "${R3D_ROOT_PATH}/src/r3d_shape.c" + "${R3D_ROOT_PATH}/src/r3d_sky.c" + "${R3D_ROOT_PATH}/src/r3d_utils.c" + "${R3D_ROOT_PATH}/src/r3d_vertex.c" +) + +# ======================================== +# Embedded Resources +# ======================================== + +embed_shaders(${PROJECT_NAME} + # Generic + "${R3D_ROOT_PATH}/shaders/generic/color.frag" + "${R3D_ROOT_PATH}/shaders/generic/screen.vert" + "${R3D_ROOT_PATH}/shaders/generic/cubemap.vert" + # Prepare + "${R3D_ROOT_PATH}/shaders/prepare/denoiser_atrous.frag" + "${R3D_ROOT_PATH}/shaders/prepare/denoiser_sparse.frag" + "${R3D_ROOT_PATH}/shaders/prepare/smaa_blending_weigths.vert" + "${R3D_ROOT_PATH}/shaders/prepare/smaa_blending_weigths.frag" + "${R3D_ROOT_PATH}/shaders/prepare/smaa_edge_detection.vert" + "${R3D_ROOT_PATH}/shaders/prepare/smaa_edge_detection.frag" + "${R3D_ROOT_PATH}/shaders/prepare/blur_down.frag" + "${R3D_ROOT_PATH}/shaders/prepare/blur_up.frag" + "${R3D_ROOT_PATH}/shaders/prepare/luminance.frag" + "${R3D_ROOT_PATH}/shaders/prepare/exposure_adapt.frag" + "${R3D_ROOT_PATH}/shaders/prepare/depth_pyramid.frag" + "${R3D_ROOT_PATH}/shaders/prepare/ssao_in_down.frag" + "${R3D_ROOT_PATH}/shaders/prepare/ssao.frag" + "${R3D_ROOT_PATH}/shaders/prepare/ssil_in_down.frag" + "${R3D_ROOT_PATH}/shaders/prepare/ssil.frag" + "${R3D_ROOT_PATH}/shaders/prepare/ssgi_in_down.frag" + "${R3D_ROOT_PATH}/shaders/prepare/ssgi.frag" + "${R3D_ROOT_PATH}/shaders/prepare/ssr_in_down.frag" + "${R3D_ROOT_PATH}/shaders/prepare/ssr.frag" + "${R3D_ROOT_PATH}/shaders/prepare/dof_coc.frag" + "${R3D_ROOT_PATH}/shaders/prepare/dof_down.frag" + "${R3D_ROOT_PATH}/shaders/prepare/dof_blur.frag" + "${R3D_ROOT_PATH}/shaders/prepare/bloom_down.frag" + "${R3D_ROOT_PATH}/shaders/prepare/bloom_up.frag" + "${R3D_ROOT_PATH}/shaders/prepare/cubemap_from_equirectangular.frag" + "${R3D_ROOT_PATH}/shaders/prepare/cubemap_irradiance.frag" + "${R3D_ROOT_PATH}/shaders/prepare/cubemap_prefilter.frag" + "${R3D_ROOT_PATH}/shaders/prepare/cubemap_procedural_sky.frag" + "${R3D_ROOT_PATH}/shaders/prepare/cubemap_custom_sky.frag" + # Scene + "${R3D_ROOT_PATH}/shaders/scene/scene.vert" + "${R3D_ROOT_PATH}/shaders/scene/geometry.frag" + "${R3D_ROOT_PATH}/shaders/scene/forward.frag" + "${R3D_ROOT_PATH}/shaders/scene/unlit.frag" + "${R3D_ROOT_PATH}/shaders/scene/depth.frag" + "${R3D_ROOT_PATH}/shaders/scene/depth_cube.frag" + "${R3D_ROOT_PATH}/shaders/scene/decal.frag" + "${R3D_ROOT_PATH}/shaders/scene/skybox.vert" + "${R3D_ROOT_PATH}/shaders/scene/skybox.frag" + # Deferred + "${R3D_ROOT_PATH}/shaders/deferred/ambient.frag" + "${R3D_ROOT_PATH}/shaders/deferred/lighting.frag" + "${R3D_ROOT_PATH}/shaders/deferred/compose.frag" + "${R3D_ROOT_PATH}/shaders/deferred/fog.frag" + # Post + "${R3D_ROOT_PATH}/shaders/post/dof.frag" + "${R3D_ROOT_PATH}/shaders/post/bloom.frag" + "${R3D_ROOT_PATH}/shaders/post/auto_exposure.frag" + "${R3D_ROOT_PATH}/shaders/post/screen.frag" + "${R3D_ROOT_PATH}/shaders/post/output.frag" + "${R3D_ROOT_PATH}/shaders/post/fxaa.frag" + "${R3D_ROOT_PATH}/shaders/post/smaa.vert" + "${R3D_ROOT_PATH}/shaders/post/smaa.frag" + "${R3D_ROOT_PATH}/shaders/post/visualizer.frag" + # Blit + "${R3D_ROOT_PATH}/shaders/blit/up_bicubic.frag" + "${R3D_ROOT_PATH}/shaders/blit/up_lanczos.frag" + "${R3D_ROOT_PATH}/shaders/blit/down_rgss.frag" + "${R3D_ROOT_PATH}/shaders/blit/down_pdss.frag" +) + +embed_assets(${PROJECT_NAME} + "${R3D_ROOT_PATH}/assets/brdf_lut_512_rg16_float.raw" + "${R3D_ROOT_PATH}/assets/smaa_area_160x560_rg8.raw" + "${R3D_ROOT_PATH}/assets/smaa_search_64x16_r8.raw" +) + +# ======================================== +# Include Directories +# ======================================== + +target_include_directories(${PROJECT_NAME} + PRIVATE + "${R3D_ROOT_PATH}/external/tinycthread" + "${R3D_ROOT_PATH}/external/uthash" + "${R3D_ROOT_PATH}/external/glad" + "${R3D_GENERATED_INCLUDE_DIR}" + "${R3D_ROOT_PATH}/include" + "${R3D_RAYLIB_INC}" +) + +if(R3D_SUPPORT_ASSIMP) + target_include_directories(${PROJECT_NAME} + PRIVATE + "${R3D_ASSIMP_INC}" + ) +endif() + +# ======================================== +# Link Libraries +# ======================================== + +target_link_libraries(${PROJECT_NAME} + PRIVATE + "${R3D_RAYLIB_LIB}" +) + +if(R3D_SUPPORT_ASSIMP) + target_link_libraries(${PROJECT_NAME} + PRIVATE + "${R3D_ASSIMP_LIB}" + ) +endif() + +check_library_exists(m cos "" HAVE_LIB_M) +if(HAVE_LIB_M) + target_link_libraries(${PROJECT_NAME} PUBLIC m) +endif() + +# ======================================== +# Compiler Settings +# ======================================== + +if(CMAKE_VERSION VERSION_GREATER 3.12) + set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 11) +endif() + +if(MSVC) + target_compile_options(${PROJECT_NAME} PRIVATE /experimental:c11atomics) + target_compile_definitions(${PROJECT_NAME} PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() + +# ======================================== +# Threading Support +# ======================================== + +cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_FLAGS "-std=c11") + check_c_source_compiles(" + #include + int dummy_thread(void *arg) { return 0; } + int main(void) { + thrd_t t; + thrd_create(&t, dummy_thread, NULL); + thrd_join(t, NULL); + struct timespec ts = {0, 0}; + thrd_sleep(&ts, NULL); + mtx_t m; + mtx_init(&m, mtx_plain); + mtx_lock(&m); + mtx_unlock(&m); + mtx_destroy(&m); + return 0; + } + " R3D_HAS_C11_THREADS) +cmake_pop_check_state() + +if(NOT R3D_HAS_C11_THREADS) + message(STATUS "Threading: threads.h unavailable or incomplete, using tinycthread") + target_sources(${PROJECT_NAME} PRIVATE "${R3D_ROOT_PATH}/external/tinycthread/tinycthread.c") + target_compile_definitions(${PROJECT_NAME} PRIVATE R3D_NO_C11_THREADS) +else() + message(STATUS "Threading: using native threads.h") +endif() + +# ======================================== +# Documentation +# ======================================== + +if(R3D_BUILD_DOCS) + find_package(Doxygen QUIET) + + if(NOT Doxygen_FOUND) + message(FATAL_ERROR "Doxygen not found. Install Doxygen to build documentation.") + endif() + + set(DOXYGEN_IN ${R3D_ROOT_PATH}/Doxyfile.in) + set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + + add_custom_target(docs + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM + ) +endif() + +# ======================================== +# Examples +# ======================================== + +if(R3D_BUILD_EXAMPLES) + include("${R3D_ROOT_PATH}/examples/CMakeLists.txt") +endif() + +``` + +`CMakePresets.json`: + +```json +{ + "version": 3, + "configurePresets": [ + { + "name": "windows-base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "x64-debug", + "displayName": "x64 Debug", + "inherits": "windows-base", + "architecture": { + "value": "x64", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "R3D_RAYLIB_VENDORED": "ON", + "R3D_ASSIMP_VENDORED": "ON" + } + }, + { + "name": "x64-release", + "displayName": "x64 Release", + "inherits": "x64-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "R3D_RAYLIB_VENDORED": "ON", + "R3D_ASSIMP_VENDORED": "ON" + } + }, + { + "name": "x86-debug", + "displayName": "x86 Debug", + "inherits": "windows-base", + "architecture": { + "value": "x86", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "R3D_RAYLIB_VENDORED": "ON", + "R3D_ASSIMP_VENDORED": "ON" + } + }, + { + "name": "x86-release", + "displayName": "x86 Release", + "inherits": "x86-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "R3D_RAYLIB_VENDORED": "ON", + "R3D_ASSIMP_VENDORED": "ON" + } + }, + { + "name": "linux-debug", + "displayName": "Linux Debug", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "R3D_RAYLIB_VENDORED": "ON", + "R3D_ASSIMP_VENDORED": "ON" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "vendor": { + "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { + "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" + } + } + }, + { + "name": "macos-debug", + "displayName": "macOS Debug", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "R3D_RAYLIB_VENDORED": "ON", + "R3D_ASSIMP_VENDORED": "ON" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "vendor": { + "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { + "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" + } + } + } + ] +} + +``` + +`Doxyfile.in`: + +```in +PROJECT_NAME = "R3D" +PROJECT_NUMBER = "@PROJECT_VERSION@" +OUTPUT_DIRECTORY = "@CMAKE_CURRENT_BINARY_DIR@/docs" +GENERATE_LATEX = NO +GENERATE_HTML = YES +GENERATE_MAN = YES +GENERATE_RTF = NO +GENERATE_XML = NO +INPUT = @R3D_ROOT_PATH@/include +RECURSIVE = YES +QUIET = YES +``` + +`LICENSE`: + +``` +Copyright (c) 2025-2026 Le Juez Victor + +This software is provided "as-is", without any express or implied warranty. In no event +will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial +applications, and to alter it and redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you + wrote the original software. If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be misrepresented + as being the original software. + + 3. This notice may not be removed or altered from any source distribution. +``` + +`README.md`: + +```md +# R3D – 3D Extension Library for raylib + + +
+R3D is an extension library for raylib that expands its 3D capabilities, including rendering, lighting, kinematics, mesh utilities, and related helpers, without turning raylib into a full engine. +
+ +--- + +[![GitHub Stars](https://img.shields.io/github/stars/Bigfoot71/r3d?style=flat&label=stars)](https://github.com/Bigfoot71/r3d/stargazers) +[![License](https://img.shields.io/badge/license-zlib-blue.svg)](LICENSE) +[![CI](https://github.com/Bigfoot71/r3d/actions/workflows/build.yml/badge.svg)](https://github.com/Bigfoot71/r3d/actions/workflows/build.yml) + +> [!WARNING] +> **It is recommended to use the pre-release tags.** +> While you can use the master branch, unexpected API breaking changes may occur until the first official release is published. + +--- + +## Key Features + +- **Hybrid Renderer**: Deferred pipeline with forward rendering for transparency. +- **Advanced Materials**: Complete PBR material system (Burley/SchlickGGX) +- **Custom Shaders**: Support for surface shaders (materials/decals), sky and screen shaders. +- **Dynamic Lighting**: Directional, spot, and omni lights with soft shadows +- **Image-Based Lighting**: Supports environment IBL and reflection probes. +- **Post-Processing**: SSAO, SSGI, SSR, DoF, bloom, fog, tonemapping, and more +- **Kinematics Support**: Basic kinematic system with capsule and mesh-based colliders. +- **Mesh Utilities**: Mesh generation, manipulation, and helper utilities. +- **Model Loading**: Assimp integration with animations and mesh generation +- **Performance**: Built-in frustum culling, instanced rendering, and more + +## Requirements + +To build R3D, you must have: +- **raylib 5.5+** (optionally provided as a submodule) +- **Assimp 6.0.2+** (optionally provided as a submodule) +- **Python 3.6+** (used to process shaders during compilation) + +To use R3D, the required specifications are: +- **OpenGL 3.3+** (with support for `GL_ARB_texture_cube_map_array`) + +## Bindings + +Here is a list with all the ports available. Feel free to send a PR if you know of any binding/wrapper not in this list. + +| Name | R3D Version | Language | License | +| --- | :-: | :-: | :-: | +| [r3d-odin](https://github.com/Bigfoot71/r3d-odin) | **0.10.0** | [Odin](https://odin-lang.org/) | Zlib | +| [ray4laz_r3d](https://github.com/GuvaCode/ray4laz_r3d) | **0.9.1** | [FreePascal](https://en.wikipedia.org/wiki/Free_Pascal) | MIT | +| [r3d-cs](https://github.com/graphnode/r3d-cs) | **0.9.1** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | + +## Installation + +If you don't know how to start and want to try it quickly: +```bash +git clone --depth 1 --recurse-submodules https://github.com/Bigfoot71/r3d +cd r3d +mkdir build && cd build +cmake .. -DR3D_RAYLIB_VENDORED=ON -DR3D_ASSIMP_VENDORED=ON +cmake --build . +``` + +Or you can use [r3dStarter](https://github.com/jensroth-git/r3dStarter) made by [Jens Roth](https://github.com/jensroth-git). + +## Quick Start + +```c +#include +#include + +int main(void) +{ + InitWindow(800, 600, "R3D Example"); + SetTargetFPS(60); + + R3D_Init(800, 600); + + // Create scene objects + R3D_Mesh mesh = R3D_GenMeshSphere(1.0f, 16, 32); + R3D_Material material = R3D_GetDefaultMaterial(); + + // Setup lighting + R3D_Light light = R3D_CreateLight(R3D_LIGHT_DIR); + R3D_SetLightDirection(light, (Vector3){-1, -1, -1}); + R3D_SetLightActive(light, true); + + // Camera setup + Camera3D camera = { + .position = {3, 3, 3}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60.0f, + .projection = CAMERA_PERSPECTIVE + }; + + // Main loop + while (!WindowShouldClose()) { + UpdateCamera(&camera, CAMERA_ORBITAL); + BeginDrawing(); + R3D_Begin(camera); + R3D_DrawMesh(mesh, material, Vector3Zero(), 1.0f); + R3D_End(); + EndDrawing(); + } + + R3D_UnloadMesh(mesh); + R3D_Close(); + CloseWindow(); + return 0; +} +``` + +## License + +Licensed under the **Zlib License** - see [LICENSE](LICENSE) for details. + +## Screenshots + + + + + + + + + + +
+ +``` + +`ROADMAP.md`: + +```md +# R3D Roadmap + +## **v0.11** + +* [ ] **Considering a possible redesign of the lights and probes management API** + This part of the API will likely be revised and may introduce breaking changes in preparation for the final release. + +* [ ] **Adding an optional rendering context system** + See this discussion for more details: https://github.com/Bigfoot71/r3d/discussions/288 + +*Note: This version will mainly focus on preparatory work for the official r3d release.* + +## **Ideas (Not Planned Yet)** + +* [ ] Improve support for shadow/transparency interaction (e.g., colored shadows). +* [ ] Implement Cascaded Shadow Maps (or alternative) for directional lights. +* [ ] Make wiki pages for the repo, consider it for the release. + +``` + +`cmake/EmbedAssets.cmake`: + +```cmake +find_program(PYTHON_EXECUTABLE python3 REQUIRED) + +function(process_asset asset_file output_file_var generated_dir) + get_filename_component(asset_name "${asset_file}" NAME) + set(output_file "${generated_dir}/assets/${asset_name}.h") + set("${output_file_var}" "${output_file}" PARENT_SCOPE) + + add_custom_command( + OUTPUT "${output_file}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${generated_dir}/assets" + COMMAND "${PYTHON_EXECUTABLE}" "${R3D_ROOT_PATH}/scripts/bin2c.py" + --file "${asset_file}" --name "${asset_name}" --mode binary "${output_file}" + DEPENDS "${asset_file}" + COMMENT "Processing asset: ${asset_file}" + VERBATIM + ) +endfunction() + +function(embed_assets target_name) + set(asset_files ${ARGN}) + + if(NOT asset_files) + message(FATAL_ERROR "embed_assets: No asset file specified") + endif() + + if(NOT DEFINED R3D_GENERATED_INCLUDE_DIR) + message(FATAL_ERROR "embed_assets: R3D_GENERATED_INCLUDE_DIR is not defined") + endif() + + set(output_files) + list(LENGTH asset_files num_assets) + message(STATUS "Configuring processing of ${num_assets} asset(s) for target ${target_name}...") + + foreach(asset_file ${asset_files}) + if(NOT EXISTS "${asset_file}") + message(FATAL_ERROR "embed_assets: Asset file not found: ${asset_file}") + endif() + + get_filename_component(asset_name "${asset_file}" NAME) + process_asset("${asset_file}" output_file "${R3D_GENERATED_INCLUDE_DIR}") + list(APPEND output_files "${output_file}") + message(STATUS " - ${asset_file} -> assets/${asset_name}.h") + endforeach() + + set(assets_target "${target_name}_assets") + add_custom_target(${assets_target} + DEPENDS ${output_files} + COMMENT "Generating asset headers for ${target_name}" + ) + + add_dependencies(${target_name} ${assets_target}) + + message(STATUS "Target ${assets_target} created with ${num_assets} asset(s)") + message(STATUS "Assets will be automatically recompiled if modified") +endfunction() + +``` + +`cmake/EmbedShaders.cmake`: + +```cmake +find_program(PYTHON_EXECUTABLE python3 REQUIRED) + +function(process_shader shader_file output_file_var generated_dir) + get_filename_component(shader_name "${shader_file}" NAME) + set(output_file "${generated_dir}/shaders/${shader_name}.h") + set("${output_file_var}" "${output_file}" PARENT_SCOPE) + + add_custom_command( + OUTPUT "${output_file}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${generated_dir}/shaders" + COMMAND "${PYTHON_EXECUTABLE}" "${R3D_ROOT_PATH}/scripts/glsl_processor.py" + "${shader_file}" "${CMAKE_CURRENT_BINARY_DIR}/shader_${shader_name}.tmp" + COMMAND "${PYTHON_EXECUTABLE}" "${R3D_ROOT_PATH}/scripts/bin2c.py" + --file "${CMAKE_CURRENT_BINARY_DIR}/shader_${shader_name}.tmp" + --name "${shader_name}" --mode text "${output_file}" + COMMAND ${CMAKE_COMMAND} -E remove "${CMAKE_CURRENT_BINARY_DIR}/shader_${shader_name}.tmp" + DEPENDS "${shader_file}" + COMMENT "Processing shader: ${shader_file}" + VERBATIM + ) +endfunction() + +function(embed_shaders target_name) + set(shader_files ${ARGN}) + + if(NOT shader_files) + message(FATAL_ERROR "embed_shaders: No shader file specified") + endif() + + if(NOT DEFINED R3D_GENERATED_INCLUDE_DIR) + message(FATAL_ERROR "embed_shaders: R3D_GENERATED_INCLUDE_DIR is not defined") + endif() + + set(output_files) + list(LENGTH shader_files shader_count) + message(STATUS "Configuring processing of ${shader_count} shader(s) for target ${target_name}...") + + foreach(shader_file ${shader_files}) + if(NOT EXISTS "${shader_file}") + message(FATAL_ERROR "embed_shaders: Shader file not found: ${shader_file}") + endif() + + process_shader("${shader_file}" output_file "${R3D_GENERATED_INCLUDE_DIR}") + list(APPEND output_files "${output_file}") + message(STATUS " - ${shader_file} -> ${output_file}") + endforeach() + + set(shader_target "${target_name}_shaders") + add_custom_target(${shader_target} + DEPENDS ${output_files} + COMMENT "Generating shader headers for ${target_name}" + ) + + add_dependencies(${target_name} ${shader_target}) + + message(STATUS "Target ${shader_target} created with ${shader_count} shader(s)") +endfunction() + +``` + +`cmake/mingw-w32-x86_64.cmake`: + +```cmake +# Sample toolchain file for building for Windows from a Linux system. +# +# Typical usage: +# On Ubuntu/Debian: +# *) install cross compiler: `sudo apt-get install mingw-w64` +# On Fedora: +# *) install cross compiler: `sudo dnf install mingw32-gcc mingw32-gcc-c++` +# *) cd build +# *) cmake -DCMAKE_TOOLCHAIN_FILE=cmake/mingw-w32-x86_64.cmake .. +# This is free and unencumbered software released into the public domain. + +set(CMAKE_SYSTEM_NAME Windows) +set(TOOLCHAIN_PREFIX x86_64-w64-mingw32) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +# cross compilers to use for C, C++ +set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) +set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) + +# target environment on the build host system +set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) + +# modify default behavior of FIND_XXX() commands +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +``` + +`cmake/mingw-w64-x86_64.cmake`: + +```cmake +# Sample toolchain file for building for Windows from a Linux system. +# +# Typical usage: +# On Ubuntu/Debian: +# *) install cross compiler: `sudo apt-get install mingw-w64` +# On Fedora: +# *) install cross compiler: `sudo dnf install mingw64-gcc mingw64-gcc-c++` +# *) cd build +# *) cmake -DCMAKE_TOOLCHAIN_FILE=cmake/mingw-w64-x86_64.cmake .. +# This is free and unencumbered software released into the public domain. + +set(CMAKE_SYSTEM_NAME Windows) +set(TOOLCHAIN_PREFIX x86_64-w64-mingw32) + +# cross compilers to use for C, C++ +set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) +set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) + +# target environment on the build host system +set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) + +# modify default behavior of FIND_XXX() commands +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +``` + +`docs/shaders/screen_shader.md`: + +```md +# Screen Shaders + +Screen shaders are post-processing effects applied to the entire rendered frame. +Unlike surface shaders, they operate on the final image rather than individual objects. + +## Table of Contents + +- [Overview](#overview) +- [Entry Point](#entry-point) +- [Built-in Variables](#built-in-variables) +- [Helper Functions](#helper-functions) +- [Uniforms](#uniforms) +- [Shader Chains](#shader-chains) +- [Best Practices](#best-practices) +- [Quick Reference](#quick-reference) + +--- + +## Overview + +Screen shaders process the entire rendered frame as a 2D image. They run after all 3D rendering is complete and can read from depth, color, and geometry buffers to create post-processing effects. + +### Basic Example + +```glsl +void fragment() { + COLOR = SampleColor(TEXCOORD) * vec3(1.0, 0.5, 0.5); // Red tint +} +``` + +### Loading a Shader + +```c +// From file +R3D_ScreenShader* shader = R3D_LoadScreenShader("vignette.glsl"); + +// From memory +const char* code = "void fragment() { COLOR = vec3(1.0 - SampleColor(TEXCOORD)); }"; +R3D_ScreenShader* shader = R3D_LoadScreenShaderFromMemory(code); + +// Don't forget to unload when done +R3D_UnloadScreenShader(shader); +``` + +--- + +## Entry Point + +Screen shaders have only **one required entry point**: `fragment()`. There is no vertex stage, and consequently, no varyings. + +### Fragment Stage + +Runs once per screen pixel to compute the final output color. + +```glsl +void fragment() { + // Read input color + vec3 color = SampleColor(TEXCOORD); + + // Apply effect + color = vec3(dot(color, vec3(0.299, 0.587, 0.114))); // Grayscale conversion + + // Write output + COLOR = color; +} +``` + +--- + +## Built-in Variables + +Screen shaders provide built-in variables for screen-space operations: + +| Variable | Type | Description | +|----------|------|-------------| +| `CAMERA_POSITION` | `vec3` | Camera position in world space (read-only) | +| `MATRIX_VIEW` | `mat4` | View matrix (read-only) | +| `MATRIX_INV_VIEW` | `mat4` | Inverse view matrix (read-only) | +| `MATRIX_PROJECTION` | `mat4` | Projection matrix (read-only) | +| `MATRIX_INV_PROJECTION` | `mat4` | Inverse projection matrix (read-only) | +| `MATRIX_VIEW_PROJECTION` | `mat4` | Combined view-projection matrix (read-only) | +| `PROJECTION_MODE` | `int` | Projection mode (same as raylib's projection type) (read-only) | +| `NEAR_PLANE` | `float` | Near clipping plane distance (read-only) | +| `FAR_PLANE` | `float` | Far clipping plane distance (read-only) | +| `RESOLUTION` | `vec2` | Screen resolution in pixels (read-only) | +| `TEXEL_SIZE` | `vec2` | Size of one texel (1.0 / RESOLUTION) (read-only) | +| `ASPECT` | `float` | Screen aspect ratio (read-only) | +| `TEXCOORD` | `vec2` | Normalized texture coordinates (0.0 to 1.0) | +| `PIXCOORD` | `ivec2` | Integer pixel coordinates (0 to resolution-1) | +| `FRAME_INDEX` | `int` | Index incremented at each frame | +| `TIME` | `float` | Time provided by the raylib's `GetTime()` | +| `COLOR` | `vec3` | Output color (write to this) | + +### Usage Examples + +**Texture coordinates:** +```glsl +void fragment() { + // Sample at current screen position + COLOR = SampleColor(TEXCOORD); +} +``` + +**Pixel coordinates:** +```glsl +void fragment() { + // Checkerboard pattern + int checker = (PIXCOORD.x / 8 + PIXCOORD.y / 8) % 2; + COLOR = SampleColor(TEXCOORD) * (checker == 0 ? 1.0 : 0.5); +} +``` + +**Resolution-aware effects:** +```glsl +void fragment() { + // Blur using texel size for offset + vec3 color = vec3(0.0); + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + vec2 offset = vec2(x, y) * TEXEL_SIZE; + color += SampleColor(TEXCOORD + offset); + } + } + COLOR = color / 9.0; // Average of 3x3 grid +} +``` + +--- + +## Helper Functions + +Screen shaders provide convenience functions for accessing frame data. Each function comes in two variants: `Fetch` (uses integer pixel coordinates) and `Sample` (uses normalized texture coordinates). + +### Color Sampling + +```glsl +vec3 FetchColor(ivec2 pixCoord); // Fast, no filtering +vec3 SampleColor(vec2 texCoord); // Bilinear filtering +``` + +**Example:** +```glsl +void fragment() { + // Fetch exact pixel (faster) + vec3 center = FetchColor(PIXCOORD); + + // Sample with filtering (smoother) + vec3 blurred = SampleColor(TEXCOORD + vec2(0.01, 0.0)); + + COLOR = mix(center, blurred, 0.5); +} +``` + +### Depth Sampling + +Returns linear depth values from the depth buffer. + +```glsl +float FetchDepth(ivec2 pixCoord); // linear depth [near, far] +float SampleDepth(vec2 texCoord); + +float FetchDepth01(ivec2 pixCoord); // linear depth normalized [0, 1] +float SampleDepth01(vec2 texCoord); +``` + +**Example:** +```glsl +void fragment() { + vec3 color = SampleColor(TEXCOORD); + + const float outline_size = 1.5; + vec2 px = TEXEL_SIZE * outline_size; + + // Edge detection using depth + float d = SampleDepth(TEXCOORD); + float dx1 = abs(d - SampleDepth(TEXCOORD + vec2(px.x, 0))); + float dx2 = abs(d - SampleDepth(TEXCOORD - vec2(px.x, 0))); + float dy1 = abs(d - SampleDepth(TEXCOORD + vec2(0, px.y))); + float dy2 = abs(d - SampleDepth(TEXCOORD - vec2(0, px.y))); + + float edge = step(0.5, max(max(dx1, dx2), max(dy1, dy2))); + + COLOR = mix(color, vec3(0.0), edge); +} +``` + +### Position Sampling + +Returns view-space position (camera-relative coordinates). + +```glsl +vec3 FetchPosition(ivec2 pixCoord); +vec3 SamplePosition(vec2 texCoord); +``` + +**Example:** +```glsl +void fragment() { + vec3 position = SamplePosition(TEXCOORD); + + // Distance from camera + float distance = length(position); + + // Depth-based effect + vec3 color = SampleColor(TEXCOORD); + COLOR = color * (1.0 - smoothstep(10.0, 50.0, distance)); +} +``` + +### Normal Sampling + +Returns view-space surface normal. + +```glsl +vec3 FetchNormal(ivec2 pixCoord); +vec3 SampleNormal(vec2 texCoord); +``` + +**Example:** +```glsl +void fragment() { + vec3 normal = SampleNormal(TEXCOORD); + + // Edge detection using normals + vec3 normal_right = SampleNormal(TEXCOORD + vec2(TEXEL_SIZE.x, 0.0)); + float edge = length(normal - normal_right); + + vec3 color = SampleColor(TEXCOORD); + COLOR = mix(color, vec3(0.0), edge * 10.0); +} +``` + +### Fetch vs Sample + +- **`Fetch`**: Uses integer pixel coordinates, no filtering, faster + - Best for: Exact pixel reads, performance-critical code + - Use with: `PIXCOORD` + +- **`Sample`**: Uses normalized coordinates, bilinear filtering, smoother + - Best for: Smooth effects, interpolated values + - Use with: `TEXCOORD` + +--- + +## Uniforms + +Screen shaders support the same uniform system as surface shaders, with the same limits and behavior. + +### Supported Types + +**Values:** +- Scalars: `bool`, `int`, `float` +- Vectors: `vec2`, `vec3`, `vec4` +- Matrices: `mat2`, `mat3`, `mat4` + +**Samplers:** +- `sampler1D`, `sampler2D`, `sampler3D`, `samplerCube` + +### Limits + +- **Maximum uniform values:** 16 by default (configurable via `R3D_MAX_SHADER_UNIFORMS`) +- **Maximum samplers:** 4 by default (configurable via `R3D_MAX_SHADER_SAMPLERS`) + +### Example + +```glsl +uniform float u_intensity; +uniform sampler2D u_lut; + +void fragment() { + vec3 color = SampleColor(TEXCOORD); + + // Apply color grading + color = texture(u_lut, color.rg).rgb; + + // Apply intensity + COLOR = mix(SampleColor(TEXCOORD), color, u_intensity); +} +``` + +```c +float intensity = 0.5f; +Texture2D lut = LoadTexture("color_lut.png"); + +R3D_SetScreenShaderUniform(shader, "u_intensity", &intensity); +R3D_SetScreenShaderSampler(shader, "u_lut", lut); +``` + +--- + +## Shader Chains + +Screen shaders can be chained, R3D executes them using internal ping-pong buffers, avoiding extra buffers like with a `RenderTexture`. + +### Setting Up a Chain + +```c +void R3D_SetScreenShaderChain(R3D_ScreenShader** shaders, int count); +``` + +- **Maximum shaders:** Defined by `R3D_MAX_SCREEN_SHADERS` (default: 8) +- Shaders execute in the order specified +- Each shader receives the output of the previous shader +- `NULL` entries in the array are safely ignored +- Passing `shaders = NULL` or `count = 0` disables all screen shaders + +### Example + +```c +// Create shaders +R3D_ScreenShader* outline = R3D_LoadScreenShader("outline.glsl"); +R3D_ScreenShader* fisheye = R3D_LoadScreenShader("fisheye.glsl"); +R3D_ScreenShader* grain = R3D_LoadScreenShader("grain.glsl"); + +// Set up chain +R3D_ScreenShader* chain[] = {outline, fisheye, grain}; +R3D_SetScreenShaderChain(chain, 3); + +// Render loop +while (!WindowShouldClose()) { + R3D_Begin(); + // ... draw 3D scene ... + R3D_End(); // Screen shaders execute here +} + +// Disable screen shaders (not mandatory at the end of the program) +R3D_SetScreenShaderChain(NULL, 0); + +// Cleanup +R3D_UnloadScreenShader(fisheye); +R3D_UnloadScreenShader(grain); +R3D_UnloadScreenShader(outline); +``` + +--- + +## Best Practices + +### Performance + +1. **Use Fetch when possible:** `FetchColor(PIXCOORD)` is faster than `SampleColor(TEXCOORD)` when filtering isn't needed +2. **Minimize texture samples:** Cache sampled values if used multiple times +3. **Keep chains short:** Each shader in the chain adds overhead +4. **Avoid heavy loops:** Keep iterations bounded and minimal +5. **Leverage built-ins:** Use `TEXEL_SIZE` instead of computing `1.0 / RESOLUTION` + +### Quality + +1. **Respect aspect ratio:** Use `ASPECT` for aspect-aware effects +2. **Test at different resolutions:** Effects should scale properly +3. **Use linear filtering wisely:** Sample (filtered) for smooth effects, Fetch (unfiltered) for sharp details +4. **Consider edge cases:** Check behavior at screen edges (TEXCOORD = 0.0 or 1.0) + +### Organization + +1. **One effect per shader:** Keep shaders focused and reusable +2. **Chain for complexity:** Combine simple shaders instead of creating monolithic ones +3. **Name meaningfully:** Use clear, descriptive names for shaders and uniforms +4. **Document parameters:** Comment uniform purposes and expected ranges + +### Example: Well-Structured Effect + +**vignette.glsl:** +```glsl +// Simple vignette effect +// u_intensity: Controls vignette strength (0.0 = none, 1.0 = full) +// u_radius: Controls vignette size (0.0 = tight, 1.0 = wide) + +uniform float u_intensity; +uniform float u_radius; + +void fragment() { + vec3 color = SampleColor(TEXCOORD); + + // Calculate distance from center + vec2 center = TEXCOORD - vec2(0.5); + center.x *= ASPECT; // Aspect correction + float dist = length(center); + + // Apply vignette + float vignette = smoothstep(u_radius, u_radius * 0.5, dist); + COLOR = color * mix(1.0, vignette, u_intensity); +} +``` + +--- + +## Quick Reference + +### Loading/Unloading +```c +R3D_ScreenShader* R3D_LoadScreenShader(const char* filePath); +R3D_ScreenShader* R3D_LoadScreenShaderFromMemory(const char* code); +void R3D_UnloadScreenShader(R3D_ScreenShader* shader); +``` + +### Setting Uniforms +```c +void R3D_SetScreenShaderUniform(R3D_ScreenShader* shader, const char* name, const void* value); +void R3D_SetScreenShaderSampler(R3D_ScreenShader* shader, const char* name, Texture texture); +``` + +### Shader Chain +```c +void R3D_SetScreenShaderChain(R3D_ScreenShader** shaders, int count); +``` + +### Shader Structure +```glsl +uniform ; // Optional: uniforms + +void fragment() { // Required: fragment stage + // Read: TEXCOORD, PIXCOORD, RESOLUTION, TEXEL_SIZE, ASPECT + // Sample: SampleColor(), SampleDepth(), SamplePosition(), SampleNormal() + // Fetch: FetchColor(), FetchDepth(), FetchPosition(), FetchNormal() + // Write: COLOR +} +``` + +### Built-in Variables +```glsl +// Input (read-only) +vec2 TEXCOORD; // Normalized coordinates [0..1] +ivec2 PIXCOORD; // Integer pixel coordinates +vec2 TEXEL_SIZE; // Size of one pixel (1.0 / RESOLUTION) +vec2 RESOLUTION; // Screen resolution +float ASPECT; // Screen aspect ratio + +// Output (write) +vec3 COLOR; // Final pixel color +``` + +### Helper Functions +```glsl +// Color +vec3 FetchColor(ivec2 pixCoord); +vec3 SampleColor(vec2 texCoord); + +// Depth (linear, near to far) +float FetchDepth(ivec2 pixCoord); +float SampleDepth(vec2 texCoord); + +// Depth (linear normalized, 0 to 1) +float FetchDepth01(ivec2 pixCoord); +float SampleDepth01(vec2 texCoord); + +// Position (view-space) +vec3 FetchPosition(ivec2 pixCoord); +vec3 SamplePosition(vec2 texCoord); + +// Normal (view-space) +vec3 FetchNormal(ivec2 pixCoord); +vec3 SampleNormal(vec2 texCoord); +``` + +``` + +`docs/shaders/sky_shader.md`: + +```md +# Sky Shaders + +Sky shaders are used to procedurally generate skybox cubemaps. Unlike screen shaders, they do not process a rendered frame, they render each face of a cubemap from scratch. + +## Table of Contents + +- [Overview](#overview) +- [Entry Point](#entry-point) +- [Built-in Variables](#built-in-variables) +- [Helper Functions](#helper-functions) +- [Uniforms](#uniforms) +- [Usage](#usage) +- [Best Practices](#best-practices) +- [Quick Reference](#quick-reference) + +--- + +## Overview + +A sky shader runs once per texel of a cubemap to compute the sky color seen from that direction. R3D renders all six faces of the cubemap using an internal unit cube, calling your `fragment()` function for each pixel. + +Sky shaders are not used during the normal frame rendering process, they are invoked explicitly to generate or update a `R3D_Cubemap`. + +### Basic Example + +```glsl +void fragment() { + // Simple gradient sky: blue at horizon, dark at zenith + float t = max(EYEDIR.y, 0.0); + COLOR = mix(vec3(0.5, 0.7, 1.0), vec3(0.1, 0.2, 0.5), t); +} +``` + +### Loading a Shader + +```c +// From file +R3D_SkyShader* shader = R3D_LoadSkyShader("sky.glsl"); + +// From memory +const char* code = "void fragment() { COLOR = vec3(0.2, 0.4, 0.8); }"; +R3D_SkyShader* shader = R3D_LoadSkyShaderFromMemory(code); + +// Don't forget to unload when done +R3D_UnloadSkyShader(shader); +``` + +--- + +## Entry Point + +Sky shaders have only **one required entry point**: `fragment()`. There is no vertex stage, and consequently, no varyings. + +### Fragment Stage + +Runs once per cubemap texel to compute the sky color for that direction. + +```glsl +void fragment() { + // Use EYEDIR to determine sky color based on view direction + vec3 sunDir = normalize(vec3(0.5, 0.8, 0.3)); + float sun = pow(max(dot(EYEDIR, sunDir), 0.0), 64.0); + COLOR = mix(vec3(0.1, 0.3, 0.8), vec3(1.0, 0.9, 0.6), sun); +} +``` + +--- + +## Built-in Variables + +Sky shaders provide built-in variables describing the current cubemap texel: + +| Variable | Type | Description | +|----------|------|-------------| +| `POSITION` | `vec3` | Interpolated local position on the unit cube | +| `TEXCOORD` | `vec2` | 2D texture coordinates on the current cube face (0.0 to 1.0) | +| `EYEDIR` | `vec3` | Normalized direction vector into the cubemap | +| `FRAME_INDEX` | `int` | Index incremented at each frame | +| `TIME` | `float` | Time provided by raylib's `GetTime()` | +| `COLOR` | `vec3` | Output color (write to this) | + +### Key Variable: `EYEDIR` + +`EYEDIR` is the most commonly useful variable. It is the normalized version of `POSITION` and represents the 3D direction from the origin toward the current texel in the cubemap. Use it to: + +- Determine sky color based on elevation (`EYEDIR.y`) +- Compute sun/moon angle via `dot(EYEDIR, lightDir)` +- Sample an equirectangular texture using the provided helper + +```glsl +void fragment() { + // Sky gradient based on elevation + float horizon = smoothstep(-0.05, 0.1, EYEDIR.y); + COLOR = mix(vec3(0.8, 0.6, 0.4), vec3(0.2, 0.4, 0.9), horizon); +} +``` + +### `TEXCOORD` vs `EYEDIR` + +- Use `EYEDIR` for 3D directional effects (gradients, sun, stars, procedural atmosphere). +- Use `TEXCOORD` when you need 2D face-local coordinates, for example when sampling a per-face texture or applying a per-face pattern. + +### `TIME` and `FRAME_INDEX` + +These work identically to their equivalents in surface and screen shaders. `TIME` is useful for animating sky conditions (moving clouds, day/night cycle), and `FRAME_INDEX` can drive frame-dependent noise effects when updating the cubemap each frame. + +--- + +## Helper Functions + +Sky shaders include a built-in helper to convert a direction vector to equirectangular (spherical) UV coordinates: + +```glsl +vec2 GetSphericalCoord(vec3 direction); +``` + +Returns UV coordinates suitable for sampling an equirectangular (panoramic) texture from a direction vector. + +**Example:** + +```glsl +uniform sampler2D u_panorama; + +void fragment() { + vec2 uv = GetSphericalCoord(EYEDIR); + COLOR = texture(u_panorama, uv).rgb; +} +``` + +--- + +## Uniforms + +Sky shaders support the same uniform system as surface and screen shaders, with identical limits and behavior. + +### Supported Types + +**Values:** `bool`, `int`, `float`, `vec2`, `vec3`, `vec4`, `mat2`, `mat3`, `mat4` + +**Samplers:** `sampler1D`, `sampler2D`, `sampler3D`, `samplerCube` + +### Limits + +- **Maximum uniform values:** 16 by default (configurable via `R3D_MAX_SHADER_UNIFORMS`) +- **Maximum samplers:** 4 by default (configurable via `R3D_MAX_SHADER_SAMPLERS`) + +### Setting Uniforms from C + +```c +float time = GetTime(); +R3D_SetSkyShaderUniform(shader, "u_time", &time); + +Texture2D panorama = LoadTexture("sky.hdr"); +R3D_SetSkyShaderSampler(shader, "u_panorama", panorama); +``` + +--- + +## Usage + +Sky shaders are used exclusively through two functions: + +```c +R3D_Cubemap R3D_GenCustomSky(int size, R3D_SkyShader* shader); +void R3D_UpdateCustomSky(R3D_Cubemap* cubemap, R3D_SkyShader* shader); +``` + +- **`R3D_GenCustomSky`** allocates a new cubemap and renders all six faces using the provided shader. +- **`R3D_UpdateCustomSky`** re-renders an existing cubemap in place. + +### Example + +```c +R3D_SkyShader* shader = R3D_LoadSkyShader("sky.glsl"); + +// Generate once at startup and set it to the environment +R3D_Cubemap sky = R3D_GenCustomSky(512, shader); +R3D_GetEnvironment()->background.sky = sky; + +float time = 0.0f; + +while (!WindowShouldClose()) { + // Update sky every frame for animated effects + time += GetFrameTime(); + R3D_SetSkyShaderUniform(shader, "u_time", &time); + R3D_UpdateCustomSky(&sky, shader); + + R3D_Begin(); + // ... draw scene with sky cubemap ... + R3D_End(); +} + +R3D_UnloadSkyShader(shader); +``` + +> **Note:** Updating a cubemap every frame can be expensive for large sizes. Consider updating at a lower frequency, or using a smaller size (e.g. 64–128) when details are not critical. + +--- + +## Best Practices + +### Performance + +1. **Choose an appropriate cubemap size:** 64 is enough for smooth gradients and distant lighting; 256–512 is needed for sharp sun discs or detailed clouds. +2. **Update selectively:** Only call `R3D_UpdateCustomSky` when the sky actually changes (new time of day, weather, etc.). +3. **Avoid heavy loops:** Keep procedural noise and atmospheric scattering computations minimal or pre-baked into textures. +4. **Cache expensive values:** Pre-compute quantities like `dot(EYEDIR, sunDir)` once and reuse them. + +### Quality + +1. **Normalize directions before use:** `EYEDIR` is already normalized, but any derived direction (reflected rays, sun direction) should be explicitly normalized. +2. **Guard against negative `EYEDIR.y`:** When computing atmospheric effects based on elevation, clamp or check `EYEDIR.y` to avoid artifacts below the horizon. +3. **Use `GetSphericalCoord` for panoramas:** Manually computing spherical coordinates is error-prone; prefer the built-in helper. + +### Organization + +1. **One shader per sky type:** Separate procedural sky, starfield, and panoramic sky into distinct shaders. +2. **Document parameter ranges:** Comment expected ranges for uniforms like sun elevation or cloud density. + +--- + +## Quick Reference + +### Loading/Unloading +```c +R3D_SkyShader* R3D_LoadSkyShader(const char* filePath); +R3D_SkyShader* R3D_LoadSkyShaderFromMemory(const char* code); +void R3D_UnloadSkyShader(R3D_SkyShader* shader); +``` + +### Setting Uniforms +```c +void R3D_SetSkyShaderUniform(R3D_SkyShader* shader, const char* name, const void* value); +void R3D_SetSkyShaderSampler(R3D_SkyShader* shader, const char* name, Texture texture); +``` + +### Generating/Updating Cubemaps +```c +R3D_Cubemap R3D_GenCustomSky(int size, R3D_SkyShader* shader); +void R3D_UpdateCustomSky(R3D_Cubemap* cubemap, R3D_SkyShader* shader); +``` + +### Shader Structure +```glsl +uniform ; // Optional: uniforms + +void fragment() { // Required: fragment stage + // Read: POSITION, TEXCOORD, EYEDIR, TIME, FRAME_INDEX + // Helper: GetSphericalCoord(vec3 direction) -> vec2 + // Write: COLOR +} +``` + +### Built-in Variables +```glsl +// Input (read-only) +vec3 POSITION; // Local position on the unit cube +vec2 TEXCOORD; // UV coordinates on the current cube face [0..1] +vec3 EYEDIR; // Normalized direction into the cubemap +int FRAME_INDEX; // Frame counter +float TIME; // Elapsed time + +// Output (write) +vec3 COLOR; // Output sky color for this texel +``` + +### Helper Functions +```glsl +vec2 GetSphericalCoord(vec3 direction); // Direction → equirectangular UV +``` + +``` + +`docs/shaders/surface_shader.md`: + +```md +# Surface Shaders + +Surface shaders are custom shaders that can be applied to materials and decals in R3D. They provide a simplified way to write GLSL code by abstracting away the complexity of multiple render passes. + +## Table of Contents + +- [Overview](#overview) +- [Entry Points](#entry-points) +- [Built-in Variables](#built-in-variables) +- [Varyings](#varyings) +- [Uniforms](#uniforms) +- [Material Sampling](#material-sampling) +- [Usage Hints](#usage-hints) +- [Best Practices](#best-practices) +- [Quick Reference](#quick-reference) + +--- + +## Overview + +A surface shader is a simplified shader interface that allows you to modify vertex and fragment behavior without worrying about the underlying render pipeline. R3D automatically handles multiple render passes (opaque, transparent, shadows, etc.) from a single shader definition. + +### Basic Example + +```glsl +uniform float u_time; + +void fragment() { + ALBEDO *= 0.5 + 0.5 * sin(u_time); +} +``` + +### Loading a Shader + +```c +// From file +R3D_SurfaceShader* shader = R3D_LoadSurfaceShader("my_shader.glsl"); + +// From memory +const char* code = "void fragment() { ALBEDO = vec3(1.0, 0.0, 0.0); }"; +R3D_SurfaceShader* shader = R3D_LoadSurfaceShaderFromMemory(code); + +// Don't forget to unload when done +R3D_UnloadSurfaceShader(shader); +``` + +--- + +## Entry Points + +Surface shaders have two optional entry points: `vertex()` and `fragment()`. At least one must be defined. + +### Vertex Stage + +Runs once per vertex, before rasterization. Use it to modify vertex positions, colors, or pass data to the fragment stage. + +```glsl +void vertex() { + POSITION.y += sin(POSITION.x * 10.0) * 0.1; +} +``` + +### Fragment Stage + +Runs once per pixel. Use it to modify final surface properties like albedo, roughness, or emission. + +```glsl +void fragment() { + ALBEDO = vec3(1.0, 0.0, 0.0); // Red surface + ROUGHNESS = 0.5; +} +``` + +### Both Stages + +You can define both stages to create complex effects: + +```glsl +varying float v_height; + +void vertex() { + v_height = POSITION.y; +} + +void fragment() { + ALBEDO = mix(vec3(0.0, 0.5, 0.0), vec3(1.0, 1.0, 1.0), v_height); +} +``` + +--- + +## Built-in Variables + +Built-in variables are pre-defined values you can read and modify in your shader. They can only be accessed within their corresponding entry point. + +### Vertex Stage + +All vertex-stage variables are initialized with local (pre-transformation) attribute values: + +| Variable | Type | Description | +|----------|------|-------------| +| `MATRIX_MODEL` | `mat4` | Model matrix (read-only) | +| `MATRIX_NORMAL` | `mat3` | Normal matrix (read-only) | +| `MATRIX_INV_VIEW` | `mat4` | Inverse view matrix (read-only) | +| `MATRIX_VIEW_PROJECTION` | `mat4` | View-projection matrix (read-only) | +| `POSITION` | `vec3` | Vertex position (local space) | +| `TEXCOORD` | `vec2` | Texture coordinates | +| `NORMAL` | `vec3` | Vertex normal (local space) | +| `TANGENT` | `vec4` | Vertex tangent (w = handedness) | +| `COLOR` | `vec4` | Vertex color | +| `EMISSION` | `vec3` | Vertex emission | +| `INSTANCE_POSITION` | `vec3` | Instance position (world space) | +| `INSTANCE_ROTATION` | `vec4` | Instance rotation (quaternion: x, y, z, w) | +| `INSTANCE_SCALE` | `vec3` | Instance scale | +| `INSTANCE_COLOR` | `vec4` | Instance color | +| `INSTANCE_CUSTOM` | `vec4` | Custom user-defined instance data | +| `FRAME_INDEX` | `int` | Index incremented at each frame | +| `TIME` | `float` | Time provided by the raylib's `GetTime()` | + +> [!WARNING] +> Mesh attributes (`POSITION`, `NORMAL`, etc.) are provided in **local space** and must remain in local space. +> You should **not manually apply model, view, or projection transformations** to them. + +**Instance Variables:** + +The `INSTANCE_*` variables are always available, even for non-instanced rendering. +When instancing is not used or when an instance buffer doesn't define certain attributes, they default to: + +```glsl +INSTANCE_POSITION = vec3(0.0); +INSTANCE_ROTATION = vec4(0.0, 0.0, 0.0, 1.0); // Identity quaternion +INSTANCE_SCALE = vec3(1.0); +INSTANCE_COLOR = vec4(1.0); +INSTANCE_CUSTOM = vec4(0.0); +``` + +You can modify mesh-local attributes (`POSITION`, `NORMAL`, etc.) and instance attributes (`INSTANCE_*`) independently. +R3D automatically composes them internally, so you don't need to manually combine them. + +**Custom Instance Data:** + +`INSTANCE_CUSTOM` is reserved for user-defined data. Unlike other instance attributes, it has no predefined meaning and can store any data you need. +If you want to use it in the fragment stage, pass it through a varying: + +```glsl +varying vec4 v_custom_data; + +void vertex() { + // Use custom data however you want + v_custom_data = INSTANCE_CUSTOM; + + // Example: use as animation offset + POSITION.y += INSTANCE_CUSTOM.x * sin(INSTANCE_CUSTOM.y); +} + +void fragment() { + // Access custom data passed from vertex stage + ALBEDO *= v_custom_data.rgb; +} +``` + +**Example:** +```glsl +void vertex() { + // Modify local mesh attributes + POSITION *= 1.5; // Scale vertex position + COLOR.rgb *= 0.5; // Darken vertex color + + // Modify instance attributes separately + INSTANCE_SCALE *= 2.0; // Double instance scale + INSTANCE_COLOR.a *= 0.8; // Make instance more transparent + + // R3D will compose these automatically +} +``` + +### Fragment Stage + +Fragment-stage variables are pre-initialized with material values (unless `R3D_NO_AUTO_FETCH` is defined): + +| Variable | Type | Description | +|----------|------|-------------| +| `TEXCOORD` | `vec2` | Interpolated texture coordinates | +| `POSITION` | `vec3` | Interpolated fragments's world position (read-only) | +| `NORMAL` | `vec3` | Surface normal (world space) | +| `TANGENT` | `vec3` | Surface tangent | +| `BITANGENT` | `vec3` | Surface bitangent | +| `ALBEDO` | `vec3` | Base color | +| `ALPHA` | `float` | Transparency | +| `EMISSION` | `vec3` | Emissive color | +| `NORMAL_MAP` | `vec3` | Normal map value | +| `OCCLUSION` | `float` | Ambient occlusion | +| `ROUGHNESS` | `float` | Surface roughness (0 = smooth, 1 = rough) | +| `METALNESS` | `float` | Metallic property (0 = dielectric, 1 = metal) | +| `SPECULAR` | `float` | Base reflectivity of non-metal materials | +| `FRAME_INDEX` | `int` | Index incremented at each frame | +| `TIME` | `float` | Time provided by the raylib's `GetTime()` | + +**Example:** +```glsl +void fragment() { + ALBEDO = vec3(1.0, 0.0, 0.0); // Red surface + ROUGHNESS = 0.2; // Shiny + METALNESS = 1.0; // Metallic +} +``` + +### Important Notes + +- Built-in variables are **scoped to their entry point**. You cannot access `ALBEDO` in `vertex()` or `POSITION` in `fragment()`. +- If you need to pass data between stages, use [varyings](#varyings). +- To use built-in variables in helper functions, pass them as parameters: + +```glsl +vec3 darken(vec3 color, float amount) { + return color * amount; +} + +void fragment() { + ALBEDO = darken(ALBEDO, 0.5); +} +``` + +--- + +## Varyings + +Varyings allow you to pass data from the vertex stage to the fragment stage. They are automatically interpolated across the triangle. + +### Basic Usage + +```glsl +varying float v_height; + +void vertex() { + v_height = POSITION.y; +} + +void fragment() { + ALBEDO = mix(vec3(0.2, 0.8, 0.2), vec3(1.0), v_height); +} +``` + +### Interpolation Qualifiers + +You can control how varyings are interpolated using qualifiers: + +| Qualifier | Description | +|-----------|-------------| +| `flat` | No interpolation (use value from provoking vertex) | +| `smooth` | Perspective-correct interpolation (default) | +| `noperspective` | Linear interpolation in screen space | + +**Example:** +```glsl +flat varying int v_material_id; +noperspective varying vec2 v_screen_uv; + +void vertex() { + v_material_id = 1; + v_screen_uv = TEXCOORD; +} + +void fragment() { + if (v_material_id == 1) { + ALBEDO = texture(u_texture, v_screen_uv).rgb; + } +} +``` + +### Limits + +- **Maximum varyings:** 32 (hardware permitting) +- In practice, you'll rarely need more than a handful + +--- + +## Uniforms + +Uniforms are constant values that can be set from your C code. They remain constant across all vertices/fragments in a draw call. + +### Supported Types + +**Values:** +- Scalars: `bool`, `int`, `float` +- Vectors: `vec2`, `vec3`, `vec4` +- Matrices: `mat2`, `mat3`, `mat4` + +**Samplers:** +- `sampler1D`, `sampler2D`, `sampler3D`, `samplerCube` + +### Limits + +- **Maximum uniform values:** 16 by default (configurable via `R3D_MAX_SHADER_UNIFORMS`) +- **Maximum samplers:** 4 by default (configurable via `R3D_MAX_SHADER_SAMPLERS`) + +### Declaring Uniforms + +```glsl +uniform float u_time; +uniform vec3 u_color; +uniform mat4 u_transform; +uniform sampler2D u_texture; +``` + +### Setting Uniforms from C + +**Values:** +```c +float time = GetTime(); +R3D_SetSurfaceShaderUniform(shader, "u_time", &time); + +Vector3 color = {1.0f, 0.0f, 0.0f}; +R3D_SetSurfaceShaderUniform(shader, "u_color", &color); + +// For booleans, use int (4 bytes) +int flag = 1; // true +R3D_SetSurfaceShaderUniform(shader, "u_flag", &flag); +``` + +**Samplers:** +```c +Texture2D texture = LoadTexture("texture.png"); +R3D_SetSurfaceShaderSampler(shader, "u_texture", texture); +``` + +### Important Notes + +- **Default values:** All uniforms default to zero (samplers have no default texture) +- **Boolean handling:** When setting `bool` uniforms from C, pass an `int` (4 bytes). Non-zero values are `true`, zero is `false`. +- **Persistence:** Uniform values persist across frames until changed +- **Per-shader state:** Each shader maintains its own uniform state +- **Update timing:** Uniforms are uploaded to GPU only when needed (marked dirty) +- **No per-draw updates:** You cannot change uniforms between draw calls within a single frame. Since rendering happens at `R3D_End()`, only the last uniform value set before `R3D_End()` will be used. + +### Example + +```glsl +uniform float u_time; +uniform sampler2D u_noise; +uniform bool u_enable_effect; + +void fragment() { + if (u_enable_effect) { + vec2 uv = TEXCOORD + texture(u_noise, TEXCOORD * 2.0).xy * 0.1; + ALBEDO *= 0.5 + 0.5 * sin(u_time + uv.x * 10.0); + } +} +``` + +```c +float time = 0.0f; +Texture2D noise = LoadTexture("noise.png"); +int enableEffect = 1; + +R3D_SetSurfaceShaderSampler(shader, "u_noise", noise); +R3D_SetSurfaceShaderUniform(shader, "u_enable_effect", &enableEffect); + +while (!WindowShouldClose()) { + time += GetFrameTime(); + R3D_SetSurfaceShaderUniform(shader, "u_time", &time); + + R3D_Begin(); + // ... draw with shader ... + R3D_End(); +} +``` + +--- + +## Material Sampling + +By default, material textures (albedo, normal, ORM) are automatically sampled and available as built-in variables in the fragment stage. + +### Disabling Auto-Fetch + +If you want to start with zero values and sample materials manually, define: + +```glsl +#define R3D_NO_AUTO_FETCH +``` + +This sets `ALBEDO`, `NORMAL_MAP`, and `OCCLUSION`/`ROUGHNESS`/`METALNESS`/`SPECULAR` to zero. + +### Manual Sampling Functions + +```glsl +vec4 SampleAlbedo(vec2 texCoord); +vec3 SampleEmission(vec2 texCoord); +vec3 SampleNormal(vec2 texCoord); +vec4 SampleOrm(vec2 texCoord); // (Occlusion, Roughness, Metalness, Specular) +``` + +### Quick Auto-Fill + +To automatically fill all built-in variables with material values: + +```glsl +void FetchMaterial(vec2 texCoord); +``` + +### Example + +```glsl +#define R3D_NO_AUTO_FETCH + +void fragment() { + // Sample material at distorted UV + vec2 distorted_uv = TEXCOORD + vec2(sin(TEXCOORD.y * 10.0) * 0.1, 0.0); + FetchMaterial(distorted_uv); + + // Or sample individual textures + // ALBEDO = SampleAlbedo(distorted_uv).rgb; + // vec4 orm = SampleOrm(distorted_uv); + // ROUGHNESS = orm.g; +} +``` + +--- + +## Usage Hints + +R3D compiles multiple shader variants for different render passes (opaque, transparent, shadows, etc.). By default, only the opaque variant is pre-compiled; others compile on-demand when needed. + +### The Problem + +On-demand compilation can cause stuttering when a new variant is first used. For example, if your shader is used on a transparent object, the transparent variant compiles when the object first becomes visible. + +### The Solution + +Use `#pragma usage` to specify which variants should be pre-compiled: + +```glsl +#pragma usage transparent shadow +``` + +### Available Usage Hints + +| Hint | Description | +|------|-------------| +| `opaque` | Opaque rendering for **lit objects** (default if no pragma specified) | +| `prepass` | Transparent pre-pass rendering for **lit objects** | +| `transparent` | Transparent rendering (color/alpha blending) for **lit objects** | +| `unlit` | Unlit rendering (handles both opaque and transparent **unlit objects**) | +| `shadow` | Shadow map rendering | +| `decal` | Decal rendering | +| `probe` | Reflection probe rendering | + +### Examples + +**Opaque object with shadows:** +```glsl +#pragma usage opaque shadow + +void fragment() { + ALBEDO = vec3(1.0, 0.0, 0.0); + ALPHA = 0.5; // Alpha cutoff +} +``` + +**Transparent object:** +```glsl +#pragma usage transparent + +void fragment() { + ALBEDO = vec3(0.0, 0.5, 1.0); + ALPHA = 0.5; // Alpha blending +} +``` + +**Unlit object:** +```glsl +#pragma usage unlit + +void fragment() { + ALBEDO = vec3(1.0, 1.0, 0.0); + ALPHA = 0.5; // Alpha cutoff or blending +} +``` + +**Decal shader:** +```glsl +#pragma usage decal + +void fragment() { + ALBEDO = vec3(1.0, 1.0, 0.0); + ALPHA = 0.5; // Alpha fading +} +``` + +### Important Notes + +- Usage hints are **optional**; missing variants will still compile on-demand +- Multiple hints can be specified: `#pragma usage opaque transparent shadow` +- Rendering mode separation: `opaque`, `prepass`, and `transparent` apply to **lit objects** only, while `unlit` applies to **unlit objects** regardless of opacity +- If no pragma is specified, only `opaque` is pre-compiled +- Variants not in the pragma can still be used; they just compile lazily + +--- + +## Best Practices + +### Performance + +1. **Minimize varyings:** Only pass what's needed +2. **Leverage vertex math:** Compute values per-vertex when they can be interpolated +3. **Avoid dynamic loops & heavy branches:** Keep loops bounded and branches predictable/simple +4. **Reuse calculations:** Don't repeat work in shader + +### Debugging + +1. **Test incrementally:** Start simple, add complexity gradually +2. **Use constants first:** Replace textures or calculations with constants to confirm logic +3. **Isolate effects:** Disable parts of the shader to identify issues +4. **Use emissive for debugging:** `EMISSION = vec3(some_value)` to visualize values + +### Organization + +1. **One shader per effect:** Don't create mega-shaders with branches for different effects +2. **Use meaningful names:** You can prefix uniforms with `u_`, varyings with `v_` +3. **Use usage hints:** Pre-compile variants you know you'll need + +--- + +## Quick Reference + +### Loading/Unloading +```c +R3D_SurfaceShader* R3D_LoadSurfaceShader(const char* filePath); +R3D_SurfaceShader* R3D_LoadSurfaceShaderFromMemory(const char* code); +void R3D_UnloadSurfaceShader(R3D_SurfaceShader* shader); +``` + +### Setting Uniforms +```c +void R3D_SetSurfaceShaderUniform(R3D_SurfaceShader* shader, const char* name, const void* value); +void R3D_SetSurfaceShaderSampler(R3D_SurfaceShader* shader, const char* name, Texture texture); +``` + +### Shader Structure +```glsl +#pragma usage // Optional: opaque, transparent, shadow, etc. +#define R3D_NO_AUTO_FETCH // Optional: disable automatic material sampling + +uniform ; // Uniforms +varying ; // Varyings (communication between stages) + +void vertex() { // Optional: vertex stage + // Modify POSITION, NORMAL, etc. +} + +void fragment() { // Optional: fragment stage + // Modify ALBEDO, ROUGHNESS, etc. +} +``` + +``` + +`examples/CMakeLists.txt`: + +```txt +set(RESOURCES_PATH "${R3D_ROOT_PATH}/examples/resources/") +set(INCLUDE_PATH "${R3D_ROOT_PATH}/include") + +function(add_example example_name source_file) + add_executable(${example_name} ${source_file}) + target_link_libraries(${example_name} PRIVATE raylib r3d) + target_compile_definitions(${example_name} PRIVATE RESOURCES_PATH="${RESOURCES_PATH}") + target_include_directories(${example_name} PRIVATE ${RAYLIB_PATH} ${INCLUDE_PATH}) +endfunction() + +add_example(r3d_basic "examples/basic.c") +add_example(r3d_basic_cpp "examples/basic.cpp") +add_example(r3d_probe "examples/probe.c") +add_example(r3d_lights "examples/lights.c") +add_example(r3d_pbr "examples/pbr.c") +add_example(r3d_transparency "examples/transparency.c") +add_example(r3d_stencil "examples/stencil.c") +add_example(r3d_skybox "examples/skybox.c") +add_example(r3d_sponza "examples/sponza.c") +add_example(r3d_sprite "examples/sprite.c") +add_example(r3d_animation "examples/animation.c") +add_example(r3d_animtree "examples/animtree.c") +add_example(r3d_bloom "examples/bloom.c") +add_example(r3d_resize "examples/resize.c") +add_example(r3d_shader "examples/shader.c") +add_example(r3d_kinematics "examples/kinematics.c") +add_example(r3d_particles "examples/particles.c") +add_example(r3d_instanced "examples/instanced.c") +add_example(r3d_billboards "examples/billboards.c") +add_example(r3d_sun "examples/sun.c") +add_example(r3d_dof "examples/dof.c") +add_example(r3d_decal "examples/decal.c") +add_example(r3d_multiview "examples/multiview.c") +add_example(r3d_to_texture "examples/to_texture.c") + +``` + +`examples/animation.c`: + +```c +#include +#include + +#ifndef RESOURCES_PATH +# define RESOURCES_PATH "./" +#endif + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Animation example"); + SetTargetFPS(60); + + // Initialize R3D with FXAA + R3D_Init(GetScreenWidth(), GetScreenHeight()); + R3D_SetAntiAliasingMode(R3D_ANTI_ALIASING_MODE_FXAA); + + // Setup environment sky + R3D_Cubemap cubemap = R3D_LoadCubemap(RESOURCES_PATH "panorama/indoor.hdr", R3D_CUBEMAP_LAYOUT_AUTO_DETECT); + R3D_ENVIRONMENT_SET(background.skyBlur, 0.3f); + R3D_ENVIRONMENT_SET(background.energy, 0.6f); + R3D_ENVIRONMENT_SET(background.sky, cubemap); + + // Setup environment ambient + R3D_AmbientMap ambientMap = R3D_GenAmbientMap(cubemap, R3D_AMBIENT_ILLUMINATION); + R3D_ENVIRONMENT_SET(ambient.map, ambientMap); + R3D_ENVIRONMENT_SET(ambient.energy, 0.25f); + + // Setup tonemapping + R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_FILMIC); + R3D_ENVIRONMENT_SET(tonemap.exposure, 0.75f); + + // Generate a ground plane and load the animated model + R3D_Mesh plane = R3D_GenMeshPlane(10, 10, 1, 1); + R3D_Model model = R3D_LoadModel(RESOURCES_PATH "models/CesiumMan.glb"); + + // Load animations + R3D_AnimationLib modelAnims = R3D_LoadAnimationLib(RESOURCES_PATH "models/CesiumMan.glb"); + R3D_AnimationPlayer modelPlayer = R3D_LoadAnimationPlayer(model.skeleton, modelAnims); + + // Setup animation playing + R3D_SetAnimationLoop(&modelPlayer, 0, true); + R3D_PlayAnimation(&modelPlayer, 0); + + // Create model instances + R3D_InstanceBuffer instances = R3D_LoadInstanceBuffer(4, R3D_INSTANCE_POSITION); + Vector3* positions = R3D_MapInstances(instances, R3D_INSTANCE_POSITION, false); + for (int z = 0; z < 2; z++) { + for (int x = 0; x < 2; x++) { + positions[z*2 + x] = (Vector3) {(float)x - 0.5f, 0, (float)z - 0.5f}; + } + } + R3D_UnmapInstances(instances, R3D_INSTANCE_POSITION); + + // Setup lights with shadows + R3D_Light light = R3D_CreateLight(R3D_LIGHT_DIR); + R3D_SetLightDirection(light, (Vector3){-1.0f, -1.0f, -1.0f}); + R3D_SetLightActive(light, true); + R3D_SetLightRange(light, 10.0f); + R3D_EnableShadow(light); + + // Setup camera + Camera3D camera = { + .position = {0, 1.5f, 3.0f}, + .target = {0, 0.75f, 0.0f}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Main loop + while (!WindowShouldClose()) + { + float delta = GetFrameTime(); + + UpdateCamera(&camera, CAMERA_ORBITAL); + R3D_UpdateAnimationPlayer(&modelPlayer, delta); + + BeginDrawing(); + ClearBackground(RAYWHITE); + R3D_Begin(camera); + R3D_DrawMesh(plane, R3D_MATERIAL_BASE, Vector3Zero(), 1.0f); + R3D_DrawAnimatedModel(model, modelPlayer, Vector3Zero(), 1.25f); + R3D_DrawAnimatedModelInstanced(model, modelPlayer, instances, 4); + R3D_End(); + EndDrawing(); + } + + // Cleanup + R3D_UnloadAnimationPlayer(modelPlayer); + R3D_UnloadAnimationLib(modelAnims); + R3D_UnloadModel(model, true); + R3D_UnloadMesh(plane); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/animtree.c`: + +```c +#include +#include + +#ifndef RESOURCES_PATH +# define RESOURCES_PATH "./" +#endif + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Animation tree example"); + SetTargetFPS(60); + + // Initialize R3D with FXAA + R3D_Init(GetScreenWidth(), GetScreenHeight()); + R3D_SetAntiAliasingMode(R3D_ANTI_ALIASING_MODE_FXAA); + + // Setup environment sky + R3D_Cubemap cubemap = R3D_LoadCubemap(RESOURCES_PATH "panorama/indoor.hdr", R3D_CUBEMAP_LAYOUT_AUTO_DETECT); + R3D_ENVIRONMENT_SET(background.skyBlur, 0.3f); + R3D_ENVIRONMENT_SET(background.energy, 0.6f); + R3D_ENVIRONMENT_SET(background.sky, cubemap); + + // Setup environment ambient + R3D_AmbientMap ambientMap = R3D_GenAmbientMap(cubemap, R3D_AMBIENT_ILLUMINATION); + R3D_ENVIRONMENT_SET(ambient.map, ambientMap); + R3D_ENVIRONMENT_SET(ambient.energy, 0.25f); + + // Setup tonemapping + R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_FILMIC); + R3D_ENVIRONMENT_SET(tonemap.exposure, 0.75f); + + // Generate a ground plane and load the animated model + R3D_Mesh plane = R3D_GenMeshPlane(10, 10, 1, 1); + R3D_Model model = R3D_LoadModel(RESOURCES_PATH "models/YBot.glb"); + + // Load animations + R3D_AnimationLib modelAnims = R3D_LoadAnimationLib(RESOURCES_PATH "models/YBot.glb"); + R3D_AnimationPlayer modelPlayer = R3D_LoadAnimationPlayer(model.skeleton, modelAnims); + + // Create & define animation tree structure + R3D_AnimationTree animTree = R3D_LoadAnimationTreeEx(modelPlayer, 12, 0); + + R3D_AnimationState animState = { + .speed = 0.8f, + .play = true, + .loop = true + }; + R3D_StmEdgeParams edgeParams = { + .mode = R3D_STM_EDGE_ONDONE, + .status = R3D_STM_EDGE_AUTO, + .xFadeTime = 0.0f + }; + R3D_StmEdgeParams fadedEdgeParams = { + .mode = R3D_STM_EDGE_ONDONE, + .status = R3D_STM_EDGE_AUTO, + .xFadeTime = 0.3f + }; + R3D_AnimationNodeParams loopingAnimParams = { + .state = animState, + .looper = true + }; + + R3D_AnimationTreeNode* leftRightStmNode = R3D_CreateStmNode(&animTree, 4, 4); + { + TextCopy(loopingAnimParams.name, "walk left"); + R3D_AnimationTreeNode* animNode0 = R3D_CreateAnimationNode(&animTree, loopingAnimParams); + R3D_AnimationTreeNode* animNode1 = R3D_CreateAnimationNode(&animTree, loopingAnimParams); + + TextCopy(loopingAnimParams.name, "walk right"); + R3D_AnimationTreeNode* animNode2 = R3D_CreateAnimationNode(&animTree, loopingAnimParams); + R3D_AnimationTreeNode* animNode3 = R3D_CreateAnimationNode(&animTree, loopingAnimParams); + + R3D_AnimationStmIndex stateIdx0 = R3D_CreateStmNodeState(leftRightStmNode, animNode0, 1); + R3D_AnimationStmIndex stateIdx1 = R3D_CreateStmNodeState(leftRightStmNode, animNode1, 1); + R3D_AnimationStmIndex stateIdx2 = R3D_CreateStmNodeState(leftRightStmNode, animNode2, 1); + R3D_AnimationStmIndex stateIdx3 = R3D_CreateStmNodeState(leftRightStmNode, animNode3, 1); + R3D_CreateStmNodeEdge(leftRightStmNode, stateIdx0, stateIdx1, edgeParams); + R3D_CreateStmNodeEdge(leftRightStmNode, stateIdx1, stateIdx2, fadedEdgeParams); + R3D_CreateStmNodeEdge(leftRightStmNode, stateIdx2, stateIdx3, edgeParams); + R3D_CreateStmNodeEdge(leftRightStmNode, stateIdx3, stateIdx0, fadedEdgeParams); + } + + R3D_AnimationTreeNode* forwBackStmNode = R3D_CreateStmNode(&animTree, 4, 4); + { + TextCopy(loopingAnimParams.name, "walk forward"); + R3D_AnimationTreeNode* animNode0 = R3D_CreateAnimationNode(&animTree, loopingAnimParams); + R3D_AnimationTreeNode* animNode1 = R3D_CreateAnimationNode(&animTree, loopingAnimParams); + + TextCopy(loopingAnimParams.name, "walk backward"); + R3D_AnimationTreeNode* animNode2 = R3D_CreateAnimationNode(&animTree, loopingAnimParams); + R3D_AnimationTreeNode* animNode3 = R3D_CreateAnimationNode(&animTree, loopingAnimParams); + + R3D_AnimationStmIndex stateIdx0 = R3D_CreateStmNodeState(forwBackStmNode, animNode0, 1); + R3D_AnimationStmIndex stateIdx1 = R3D_CreateStmNodeState(forwBackStmNode, animNode1, 1); + R3D_AnimationStmIndex stateIdx2 = R3D_CreateStmNodeState(forwBackStmNode, animNode2, 1); + R3D_AnimationStmIndex stateIdx3 = R3D_CreateStmNodeState(forwBackStmNode, animNode3, 1); + R3D_CreateStmNodeEdge(forwBackStmNode, stateIdx0, stateIdx1, edgeParams); + R3D_CreateStmNodeEdge(forwBackStmNode, stateIdx1, stateIdx2, fadedEdgeParams); + R3D_CreateStmNodeEdge(forwBackStmNode, stateIdx2, stateIdx3, edgeParams); + R3D_CreateStmNodeEdge(forwBackStmNode, stateIdx3, stateIdx0, fadedEdgeParams); + } + + R3D_SwitchNodeParams switchParams = { + .synced = false, + .activeInput = 0, + .xFadeTime = 0.4f + }; + R3D_AnimationTreeNode* switchNode = R3D_CreateSwitchNode(&animTree, 3, switchParams); + R3D_AnimationTreeNode* idleNode = R3D_CreateAnimationNode(&animTree, (R3D_AnimationNodeParams){ + .name = "idle", + .state = animState + }); + R3D_AddAnimationNode(switchNode, idleNode, 0); + R3D_AddAnimationNode(switchNode, leftRightStmNode, 1); + R3D_AddAnimationNode(switchNode, forwBackStmNode, 2); + R3D_AddRootAnimationNode(&animTree, switchNode); + + // Setup lights with shadows + R3D_Light light = R3D_CreateLight(R3D_LIGHT_DIR); + R3D_SetLightDirection(light, (Vector3){-1.0f, -1.0f, -1.0f}); + R3D_SetLightActive(light, true); + R3D_SetLightRange(light, 10.0f); + R3D_EnableShadow(light); + + // Setup camera + Camera3D camera = { + .position = {0, 1.5f, 3.0f}, + .target = {0, 0.75f, 0.0f}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Main loop + while (!WindowShouldClose()) + { + float delta = GetFrameTime(); + + if (IsKeyDown(KEY_ONE)) switchParams.activeInput = 0; + if (IsKeyDown(KEY_TWO)) switchParams.activeInput = 1; + if (IsKeyDown(KEY_THREE)) switchParams.activeInput = 2; + R3D_SetSwitchNodeParams(switchNode, switchParams); + + UpdateCamera(&camera, CAMERA_ORBITAL); + R3D_UpdateAnimationTree(&animTree, delta); + + BeginDrawing(); + ClearBackground(RAYWHITE); + R3D_Begin(camera); + R3D_DrawMesh(plane, R3D_MATERIAL_BASE, Vector3Zero(), 1.0f); + R3D_DrawAnimatedModel(model, modelPlayer, Vector3Zero(), 1.0f); + R3D_End(); + DrawText("Press '1' to idle", 10, GetScreenHeight()-74, 20, BLACK); + DrawText("Press '2' to walk left and right", 10, GetScreenHeight()-54, 20, BLACK); + DrawText("Press '3' to walk forward and backward", 10, GetScreenHeight()-34, 20, BLACK); + EndDrawing(); + } + + // Cleanup + R3D_UnloadAnimationTree(animTree); + R3D_UnloadAnimationPlayer(modelPlayer); + R3D_UnloadAnimationLib(modelAnims); + R3D_UnloadModel(model, true); + R3D_UnloadMesh(plane); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/basic.c`: + +```c +#include +#include + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Basic example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Create meshes + R3D_Mesh plane = R3D_GenMeshPlane(1000, 1000, 1, 1); + R3D_Mesh sphere = R3D_GenMeshSphere(0.5f, 64, 64); + R3D_Material material = R3D_GetDefaultMaterial(); + + // Setup environment + R3D_ENVIRONMENT_SET(ambient.color, (Color){10, 10, 10, 255}); + + // Create light + R3D_Light light = R3D_CreateLight(R3D_LIGHT_SPOT); + R3D_LightLookAt(light, (Vector3){0, 10, 5}, (Vector3){0}); + R3D_EnableShadow(light); + R3D_SetLightActive(light, true); + + // Setup camera + Camera3D camera = { + .position = {0, 2, 2}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_ORBITAL); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + R3D_Begin(camera); + R3D_DrawMesh(plane, material, (Vector3) {0, -0.5f, 0}, 1.0f); + R3D_DrawMesh(sphere, material, Vector3Zero(), 1.0f); + R3D_End(); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadMesh(sphere); + R3D_UnloadMesh(plane); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/basic.cpp`: + +```cpp +#include +#include + +int main() +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Basic example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Create meshes + R3D_Mesh plane = R3D_GenMeshPlane(1000, 1000, 1, 1); + R3D_Mesh sphere = R3D_GenMeshSphere(0.5f, 64, 64); + R3D_Material material = R3D_GetDefaultMaterial(); + + // Setup environment + R3D_ENVIRONMENT_SET(ambient.color, Color{10, 10, 10, 255}); + + // Create light + R3D_Light light = R3D_CreateLight(R3D_LIGHT_SPOT); + R3D_LightLookAt(light, {0, 10, 5}, {}); + R3D_EnableShadow(light); + R3D_SetLightActive(light, true); + + // Setup camera + Camera3D camera = {}; + camera.position = {0, 2, 2}; + camera.target = {0, 0, 0}; + camera.up = {0, 1, 0}; + camera.fovy = 60; + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_ORBITAL); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + R3D_Begin(camera); + R3D_DrawMesh(plane, material, {0, -0.5f, 0}, 1.0f); + R3D_DrawMesh(sphere, material, {}, 1.0f); + R3D_End(); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadMesh(sphere); + R3D_UnloadMesh(plane); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/billboards.c`: + +```c +#include +#include + +#ifndef RESOURCES_PATH +# define RESOURCES_PATH "./" +#endif + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Billboards example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + R3D_SetTextureFilter(TEXTURE_FILTER_POINT); + + // Set background/ambient color + R3D_ENVIRONMENT_SET(background.color, (Color){102, 191, 255, 255}); + R3D_ENVIRONMENT_SET(ambient.color, (Color){10, 19, 25, 255}); + R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_FILMIC); + + // Create ground mesh and material + R3D_Mesh meshGround = R3D_GenMeshPlane(200, 200, 1, 1); + R3D_Material matGround = R3D_GetDefaultMaterial(); + matGround.albedo.color = GREEN; + + // Create billboard mesh and material + R3D_Mesh meshBillboard = R3D_GenMeshQuad(1.0f, 1.0f, 1, 1, (Vector3){0.0f, 0.0f, 1.0f}); + meshBillboard.shadowCastMode = R3D_SHADOW_CAST_ON_DOUBLE_SIDED; + + R3D_Material matBillboard = R3D_GetDefaultMaterial(); + matBillboard.albedo = R3D_LoadAlbedoMap(RESOURCES_PATH "images/tree.png", WHITE); + matBillboard.billboardMode = R3D_BILLBOARD_Y_AXIS; + + // Create transforms for instanced billboards + R3D_InstanceBuffer instances = R3D_LoadInstanceBuffer(64, R3D_INSTANCE_POSITION | R3D_INSTANCE_SCALE); + Vector3* positions = R3D_MapInstances(instances, R3D_INSTANCE_POSITION, false); + Vector3* scales = R3D_MapInstances(instances, R3D_INSTANCE_SCALE, false); + for (int i = 0; i < 64; i++) { + float scaleFactor = GetRandomValue(25, 50) / 10.0f; + scales[i] = (Vector3) {scaleFactor, scaleFactor, 1.0f}; + positions[i] = (Vector3) { + (float)GetRandomValue(-100, 100), + scaleFactor * 0.5f, + (float)GetRandomValue(-100, 100) + }; + } + R3D_UnmapInstances(instances, R3D_INSTANCE_POSITION | R3D_INSTANCE_SCALE); + + // Setup directional light with shadows + R3D_Light light = R3D_CreateLight(R3D_LIGHT_DIR); + R3D_SetLightDirection(light, (Vector3){-1, -1, -1}); + R3D_EnableShadow(light); + R3D_SetLightActive(light, true); + R3D_SetLightRange(light, 32.0f); + + // Setup camera + Camera3D camera = { + .position = {0, 5, 0}, + .target = {0, 5, -1}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Capture mouse + DisableCursor(); + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_FREE); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + R3D_Begin(camera); + R3D_DrawMesh(meshGround, matGround, Vector3Zero(), 1.0f); + R3D_DrawMeshInstanced(meshBillboard, matBillboard, instances, 64); + R3D_End(); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadMaterial(matBillboard); + R3D_UnloadMesh(meshBillboard); + R3D_UnloadMesh(meshGround); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/bloom.c`: + +```c +#include +#include + +static bool IsKeyDownDelay(int key); +static const char* GetBloomModeName(void); +static void DrawTextRight(const char* text, int y, int fontSize, Color color); +static void AdjustBloomParam(float* param, int direction, float step, float min, float max); + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Bloom example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Setup bloom and tonemapping + R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_ACES); + R3D_ENVIRONMENT_SET(bloom.mode, R3D_BLOOM_MIX); + R3D_ENVIRONMENT_SET(bloom.levels, 1.0f); + + // Set background + R3D_ENVIRONMENT_SET(background.color, BLACK); + + // Create cube mesh and material + R3D_Mesh cube = R3D_GenMeshCube(1.0f, 1.0f, 1.0f); + R3D_Material material = R3D_GetDefaultMaterial(); + float hueCube = 0.0f; + material.emission.color = ColorFromHSV(hueCube, 1.0f, 1.0f); + material.emission.energy = 1.0f; + material.albedo.color = BLACK; + + // Setup camera + Camera3D camera = { + .position = {0, 3.5f, 5}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Main loop + while (!WindowShouldClose()) + { + float delta = GetFrameTime(); + UpdateCamera(&camera, CAMERA_ORBITAL); + + // Change cube color + if (IsKeyDown(KEY_C)) { + hueCube = Wrap(hueCube + 45.0f * delta, 0, 360); + material.emission.color = ColorFromHSV(hueCube, 1.0f, 1.0f); + } + + // Adjust bloom parameters + float intensity = R3D_ENVIRONMENT_GET(bloom.intensity); + int intensityDir = IsKeyDownDelay(KEY_RIGHT) - IsKeyDownDelay(KEY_LEFT); + AdjustBloomParam(&intensity, intensityDir, 0.01f, 0.0f, INFINITY); + R3D_ENVIRONMENT_SET(bloom.intensity, intensity); + + float radius = R3D_ENVIRONMENT_GET(bloom.filterRadius); + int radiusDir = IsKeyDownDelay(KEY_UP) - IsKeyDownDelay(KEY_DOWN); + AdjustBloomParam(&radius, radiusDir, 0.1f, 0.0f, INFINITY); + R3D_ENVIRONMENT_SET(bloom.filterRadius, radius); + + int levelDir = IsMouseButtonDown(MOUSE_BUTTON_RIGHT) - IsMouseButtonDown(MOUSE_BUTTON_LEFT); + float levels = R3D_ENVIRONMENT_GET(bloom.levels); + AdjustBloomParam(&levels, levelDir, 0.01f, 0.0f, 1.0f); + R3D_ENVIRONMENT_SET(bloom.levels, levels); + + // Draw scene + if (IsKeyPressed(KEY_SPACE)) { + R3D_ENVIRONMENT_SET(bloom.mode, (R3D_ENVIRONMENT_GET(bloom.mode) + 1) % (R3D_BLOOM_SCREEN + 1)); + } + + BeginDrawing(); + ClearBackground(RAYWHITE); + + R3D_Begin(camera); + R3D_DrawMesh(cube, material, Vector3Zero(), 1.0f); + R3D_End(); + + // Draw bloom info + DrawTextRight(TextFormat("Mode: %s", GetBloomModeName()), 10, 20, LIME); + DrawTextRight(TextFormat("Intensity: %.2f", R3D_ENVIRONMENT_GET(bloom.intensity)), 40, 20, LIME); + DrawTextRight(TextFormat("Filter Radius: %.2f", R3D_ENVIRONMENT_GET(bloom.filterRadius)), 70, 20, LIME); + DrawTextRight(TextFormat("Levels: %.2f", R3D_ENVIRONMENT_GET(bloom.levels)), 100, 20, LIME); + + EndDrawing(); + } + + R3D_UnloadMesh(cube); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +bool IsKeyDownDelay(int key) +{ + return IsKeyPressedRepeat(key) || IsKeyPressed(key); +} + +const char* GetBloomModeName(void) +{ + const char* modes[] = {"Disabled", "Mix", "Additive", "Screen"}; + int mode = R3D_ENVIRONMENT_GET(bloom.mode); + return (mode >= 0 && mode <= R3D_BLOOM_SCREEN) ? modes[mode] : "Unknown"; +} + +void DrawTextRight(const char* text, int y, int fontSize, Color color) +{ + int width = MeasureText(text, fontSize); + DrawText(text, GetScreenWidth() - width - 10, y, fontSize, color); +} + +void AdjustBloomParam(float* param, int direction, float step, float min, float max) +{ + if (direction != 0) + *param = Clamp(*param + direction * step, min, max); +} + +``` + +`examples/decal.c`: + +```c +#include +#include + +#ifndef RESOURCES_PATH +# define RESOURCES_PATH "./" +#endif + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Decal example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Create meshes + R3D_Mesh plane = R3D_GenMeshPlane(5.0f, 5.0f, 1, 1); + R3D_Mesh sphere = R3D_GenMeshSphere(0.5f, 64, 64); + R3D_Mesh cylinder = R3D_GenMeshCylinder(0.5f, 1, 64); + R3D_Material material = R3D_GetDefaultMaterial(); + material.albedo.color = GRAY; + + // Create decal + R3D_Decal decal = R3D_DECAL_BASE; + R3D_SetTextureFilter(TEXTURE_FILTER_BILINEAR); + decal.albedo = R3D_LoadAlbedoMap(RESOURCES_PATH "images/decal.png", WHITE); + decal.normal = R3D_LoadNormalMap(RESOURCES_PATH "images/decal_normal.png", 1.0f); + decal.normalThreshold = 45.0f; + decal.fadeWidth = 20.0f; + + // Create data for instanced drawing + R3D_InstanceBuffer instances = R3D_LoadInstanceBuffer(3, R3D_INSTANCE_POSITION); + Vector3* positions = R3D_MapInstances(instances, R3D_INSTANCE_POSITION, false); + positions[0] = (Vector3){ -1.25f, 0, 1 }; + positions[1] = (Vector3){ 0, 0, 1 }; + positions[2] = (Vector3){ 1.25f, 0, 1 }; + R3D_UnmapInstances(instances, R3D_INSTANCE_POSITION); + + // Setup environment + R3D_ENVIRONMENT_SET(ambient.color, (Color){ 10, 10, 10, 255 }); + + // Create light + R3D_Light light = R3D_CreateLight(R3D_LIGHT_DIR); + R3D_SetLightDirection(light, (Vector3){ 0.5f, -1, -0.5f }); + R3D_EnableShadow(light); + R3D_SetLightActive(light, true); + + // Setup camera + Camera3D camera = (Camera3D){ + .position = (Vector3){0, 3, 3}, + .target = (Vector3){0, 0, 0}, + .up = (Vector3){0, 1, 0}, + .fovy = 60, + }; + + // Capture mouse + DisableCursor(); + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_FREE); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + R3D_Begin(camera); + R3D_DrawMesh(plane, material, (Vector3){ 0, 0, 0 }, 1.0f); + R3D_DrawMesh(sphere, material, (Vector3){ -1, 0.5f, -1 }, 1.0f); + R3D_DrawMeshEx(cylinder, material, (Vector3){ 1, 0.5f, -1 }, QuaternionFromEuler(0, 0, PI/2), Vector3One()); + + R3D_DrawDecal(decal, (Vector3){ -1, 1, -1 }, 1.0f); + R3D_DrawDecalEx(decal, (Vector3){ 1, 0.5f, -0.5f }, QuaternionFromEuler(PI/2, 0, 0), (Vector3){1.25f, 1.25f, 1.25f}); + R3D_DrawDecalInstanced(decal, instances, 3); + R3D_End(); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadMesh(plane); + R3D_UnloadMesh(sphere); + R3D_UnloadMesh(cylinder); + R3D_UnloadMaterial(material); + R3D_UnloadDecalMaps(decal); + R3D_Close(); + + CloseWindow(); + + return 0; +} +``` + +`examples/dof.c`: + +```c +#include +#include +#include +#include + +#define X_INSTANCES 10 +#define Y_INSTANCES 10 +#define INSTANCE_COUNT (X_INSTANCES * Y_INSTANCES) + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - DoF example"); + SetTargetFPS(60); + + // Initialize R3D with FXAA + R3D_Init(GetScreenWidth(), GetScreenHeight()); + R3D_SetAntiAliasingMode(R3D_ANTI_ALIASING_MODE_FXAA); + + // Configure depth of field and background + R3D_ENVIRONMENT_SET(background.color, BLACK); + R3D_ENVIRONMENT_SET(dof.mode, R3D_DOF_ENABLED); + R3D_ENVIRONMENT_SET(dof.focusPoint, 2.0f); + R3D_ENVIRONMENT_SET(dof.focusScale, 3.0f); + R3D_ENVIRONMENT_SET(dof.maxBlurSize, 20.0f); + + // Create directional light + R3D_Light light = R3D_CreateLight(R3D_LIGHT_DIR); + R3D_SetLightDirection(light, (Vector3){0, -1, 0}); + R3D_SetLightActive(light, true); + + // Create sphere mesh and default material + R3D_Mesh meshSphere = R3D_GenMeshSphere(0.2f, 64, 64); + R3D_Material matDefault = R3D_GetDefaultMaterial(); + + // Generate instance matrices and colors + float spacing = 0.5f; + float offsetX = (X_INSTANCES * spacing) / 2.0f; + float offsetZ = (Y_INSTANCES * spacing) / 2.0f; + int idx = 0; + R3D_InstanceBuffer instances = R3D_LoadInstanceBuffer(INSTANCE_COUNT, R3D_INSTANCE_POSITION | R3D_INSTANCE_COLOR); + Vector3* positions = R3D_MapInstances(instances, R3D_INSTANCE_POSITION, false); + Color* colors = R3D_MapInstances(instances, R3D_INSTANCE_COLOR, false); + for (int x = 0; x < X_INSTANCES; x++) { + for (int y = 0; y < Y_INSTANCES; y++) { + positions[idx] = (Vector3) {x * spacing - offsetX, 0, y * spacing - offsetZ}; + colors[idx] = (Color){rand()%256, rand()%256, rand()%256, 255}; + idx++; + } + } + R3D_UnmapInstances(instances, R3D_INSTANCE_POSITION | R3D_INSTANCE_COLOR); + + // Setup camera + Camera3D camDefault = { + .position = {0, 2, 2}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Main loop + while (!WindowShouldClose()) + { + float delta = GetFrameTime(); + + // Rotate camera + Matrix rotation = MatrixRotate(camDefault.up, 0.1f * delta); + Vector3 view = Vector3Subtract(camDefault.position, camDefault.target); + view = Vector3Transform(view, rotation); + camDefault.position = Vector3Add(camDefault.target, view); + + // Adjust DoF based on mouse + Vector2 mousePos = GetMousePosition(); + float focusPoint = 0.5f + (5.0f - (mousePos.y / GetScreenHeight()) * 5.0f); + float focusScale = 0.5f + (5.0f - (mousePos.x / GetScreenWidth()) * 5.0f); + R3D_ENVIRONMENT_SET(dof.focusPoint, focusPoint); + R3D_ENVIRONMENT_SET(dof.focusScale, focusScale); + + float mouseWheel = GetMouseWheelMove(); + if (mouseWheel != 0.0f) { + float maxBlur = R3D_ENVIRONMENT_GET(dof.maxBlurSize); + R3D_ENVIRONMENT_SET(dof.maxBlurSize, maxBlur + mouseWheel * 0.1f); + } + + if (IsKeyPressed(KEY_F1)) { + R3D_SetOutputMode(R3D_GetOutputMode() == R3D_OUTPUT_SCENE ? R3D_OUTPUT_DOF : R3D_OUTPUT_SCENE); + } + + BeginDrawing(); + ClearBackground(BLACK); + + // Render scene + R3D_Begin(camDefault); + R3D_DrawMeshInstanced(meshSphere, matDefault, instances, INSTANCE_COUNT); + R3D_End(); + + // Display DoF values + char dofText[128]; + snprintf(dofText, sizeof(dofText), "Focus Point: %.2f\nFocus Scale: %.2f\nMax Blur Size: %.2f\nDebug Mode: %d", + R3D_ENVIRONMENT_GET(dof.focusPoint), R3D_ENVIRONMENT_GET(dof.focusScale), + R3D_ENVIRONMENT_GET(dof.maxBlurSize), (R3D_GetOutputMode() == R3D_OUTPUT_SCENE)); + + DrawText(dofText, 10, 30, 20, (Color) {255, 255, 255, 127}); + + // Display instructions + DrawText("F1: Toggle Debug Mode\nScroll: Adjust Max Blur Size\nMouse Left/Right: Shallow/Deep DoF\nMouse Up/Down: Adjust Focus Point Depth", 300, 10, 20, (Color) {255, 255, 255, 127}); + + // Display FPS + char fpsText[32]; + snprintf(fpsText, sizeof(fpsText), "FPS: %d", GetFPS()); + DrawText(fpsText, 10, 10, 20, (Color) {255, 255, 255, 127}); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadInstanceBuffer(instances); + R3D_UnloadMesh(meshSphere); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/instanced.c`: + +```c +#include +#include +#include + +#define INSTANCE_COUNT 1000 + +typedef struct PackedRotation { + int16_t x, y, z, w; +} PackedRotation; + +typedef struct PackedScale { + uint16_t x, y, z; +} PackedScale; + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Instanced rendering example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Set ambient light + R3D_ENVIRONMENT_SET(ambient.color, DARKGRAY); + + // Create cube mesh and default material + R3D_Mesh mesh = R3D_GenMeshCube(1, 1, 1); + R3D_Material material = R3D_GetDefaultMaterial(); + + R3D_InstanceLayout layout = { + .formats = { + R3D_INSTANCE_FORMAT_FLOAT32, // position + R3D_INSTANCE_FORMAT_SNORM16, // rotation quaternion + R3D_INSTANCE_FORMAT_FLOAT16, // scale + R3D_INSTANCE_FORMAT_UNORM8, // color + }, + .flags = R3D_INSTANCE_POSITION | + R3D_INSTANCE_ROTATION | + R3D_INSTANCE_SCALE | + R3D_INSTANCE_COLOR, + }; + + R3D_InstanceBuffer instances = R3D_LoadInstanceBufferEx(INSTANCE_COUNT, layout); + + Vector3* positions = R3D_MapInstances(instances, R3D_INSTANCE_POSITION, false); + PackedRotation* rotations = R3D_MapInstances(instances, R3D_INSTANCE_ROTATION, false); + PackedScale* scales = R3D_MapInstances(instances, R3D_INSTANCE_SCALE, false); + Color* colors = R3D_MapInstances(instances, R3D_INSTANCE_COLOR, false); + + for (int i = 0; i < INSTANCE_COUNT; i++) + { + positions[i] = (Vector3) { + (float)GetRandomValue(-50000, 50000) / 1000.0f, + (float)GetRandomValue(-50000, 50000) / 1000.0f, + (float)GetRandomValue(-50000, 50000) / 1000.0f + }; + + Quaternion rotation = QuaternionFromEuler( + (float)GetRandomValue(-314000, 314000) / 100000.0f, + (float)GetRandomValue(-314000, 314000) / 100000.0f, + (float)GetRandomValue(-314000, 314000) / 100000.0f + ); + + rotations[i] = (PackedRotation) { + R3D_PackSnorm16(rotation.x), + R3D_PackSnorm16(rotation.y), + R3D_PackSnorm16(rotation.z), + R3D_PackSnorm16(rotation.w) + }; + + Vector3 scale = { + (float)GetRandomValue(100, 2000) / 1000.0f, + (float)GetRandomValue(100, 2000) / 1000.0f, + (float)GetRandomValue(100, 2000) / 1000.0f + }; + + scales[i] = (PackedScale) { + R3D_PackFloat16(scale.x), + R3D_PackFloat16(scale.y), + R3D_PackFloat16(scale.z) + }; + + colors[i] = ColorFromHSV( + (float)GetRandomValue(0, 360000) / 1000.0f, + 1.0f, + 1.0f + ); + } + + R3D_UnmapInstances( + instances, + R3D_INSTANCE_POSITION | + R3D_INSTANCE_ROTATION | + R3D_INSTANCE_SCALE | + R3D_INSTANCE_COLOR + ); + + R3D_Light light = R3D_CreateLight(R3D_LIGHT_DIR); + R3D_SetLightDirection(light, (Vector3){0, -1, 0}); + R3D_SetLightActive(light, true); + + // Setup camera + Camera3D camera = { + .position = {0, 2, 2}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Capture mouse + DisableCursor(); + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_FREE); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + R3D_Begin(camera); + R3D_DrawMeshInstanced(mesh, material, instances, INSTANCE_COUNT); + R3D_End(); + + DrawFPS(10, 10); + EndDrawing(); + } + + // Cleanup + R3D_UnloadInstanceBuffer(instances); + R3D_UnloadMaterial(material); + R3D_UnloadMesh(mesh); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/kinematics.c`: + +```c +#include +#include +#include + +#ifndef RESOURCES_PATH +#define RESOURCES_PATH "./" +#endif + +#define GRAVITY -15.0f +#define MOVE_SPEED 5.0f +#define JUMP_FORCE 8.0f + +#define CAPSULE_CENTER(caps) Vector3Scale(Vector3Add((caps).start, (caps).end), 0.5f) +#define BOX_CENTER(box) Vector3Scale(Vector3Add((box).min, (box).max), 0.5f) + +int main(void) +{ + InitWindow(800, 450, "[r3d] - Kinematics Example"); + SetTargetFPS(60); + + R3D_Init(GetScreenWidth(), GetScreenHeight()); + R3D_SetTextureFilter(TEXTURE_FILTER_ANISOTROPIC_8X); + R3D_SetTextureWrap(TEXTURE_WRAP_REPEAT); + + R3D_Cubemap sky = R3D_GenProceduralSky(1024, R3D_PROCEDURAL_SKY_BASE); + R3D_AmbientMap ambient = R3D_GenAmbientMap(sky, R3D_AMBIENT_ILLUMINATION | R3D_AMBIENT_REFLECTION); + R3D_ENVIRONMENT_SET(background.sky, sky); + R3D_ENVIRONMENT_SET(ambient.map, ambient); + + R3D_Light light = R3D_CreateLight(R3D_LIGHT_DIR); + R3D_SetLightDirection(light, (Vector3) {-1, -1, -1}); + R3D_SetLightRange(light, 16.0f); + R3D_SetLightActive(light, true); + R3D_EnableShadow(light); + + // Load materials + R3D_AlbedoMap baseAlbedo = R3D_LoadAlbedoMap(RESOURCES_PATH "images/placeholder.png", WHITE); + + R3D_Material groundMat = R3D_GetDefaultMaterial(); + groundMat.uvScale = (Vector2) {250.0f, 250.0f}; + groundMat.albedo = baseAlbedo; + + R3D_Material slopeMat = R3D_GetDefaultMaterial(); + slopeMat.albedo.color = (Color) {255,255,0,255}; + slopeMat.albedo.texture = baseAlbedo.texture; + + // Ground + R3D_Mesh groundMesh = R3D_GenMeshPlane(1000, 1000, 1, 1); + BoundingBox groundBox = {.min = {-500, -1, -500}, .max = {500, 0, 500}}; + + // Slope obstacle + R3D_MeshData slopeMeshData = R3D_GenMeshDataSlope(2, 2, 2, (Vector3) {0, 1, -1}); + R3D_Mesh slopeMesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, slopeMeshData, NULL); + Matrix slopeTransform = MatrixTranslate(0, 1, 5); + + // Player capsule + R3D_Capsule capsule = {.start = {0, 0.5f, 0}, .end = {0, 1.5f, 0}, .radius = 0.5f}; + R3D_Mesh capsMesh = R3D_GenMeshCapsule(0.5f, 1.0f, 64, 32); + Vector3 velocity = {0, 0, 0}; + + // Camera + float cameraAngle = 0.0f; + float cameraPitch = 30.0f; + Camera3D camera = { + .position = {0, 5, 5}, + .target = CAPSULE_CENTER(capsule), + .up = {0, 1, 0}, + .fovy = 60 + }; + + DisableCursor(); + + while (!WindowShouldClose()) + { + float dt = GetFrameTime(); + + // Camera rotation + Vector2 mouseDelta = GetMouseDelta(); + cameraAngle -= mouseDelta.x * 0.15f; + cameraPitch = Clamp(cameraPitch + mouseDelta.y * 0.15f, -7.5f, 80.0f); + + // Movement input relative to camera + int dx = IsKeyDown(KEY_A) - IsKeyDown(KEY_D); + int dz = IsKeyDown(KEY_W) - IsKeyDown(KEY_S); + + Vector3 moveInput = {0}; + if (dx != 0 || dz != 0) { + float angleRad = cameraAngle * DEG2RAD; + Vector3 right = {cosf(angleRad), 0, -sinf(angleRad)}; + Vector3 forward = {sinf(angleRad), 0, cosf(angleRad)}; + moveInput = Vector3Normalize(Vector3Add(Vector3Scale(right, (float)dx), Vector3Scale(forward, (float)dz))); + } + + // Check grounded + bool isGrounded = R3D_CheckCapsuleSupportBoundingBox(capsule, (Vector3) {0,-1,0}, 0.01f, groundBox, NULL) || + R3D_CheckCapsuleSupportMesh(capsule, (Vector3) {0,-1,0}, 0.3f, slopeMeshData, slopeTransform, NULL); + + // Jump and apply gravity + if (isGrounded && IsKeyPressed(KEY_SPACE)) velocity.y = JUMP_FORCE; + if (!isGrounded) velocity.y += GRAVITY * dt; + else if (velocity.y < 0) velocity.y = 0; + + // Calculate total movement + Vector3 movement = Vector3Scale(moveInput, MOVE_SPEED * dt); + movement.y = velocity.y * dt; + + // Apply movement with collision + movement = R3D_SlideCapsuleMesh(capsule, movement, slopeMeshData, slopeTransform, NULL); + capsule.start = Vector3Add(capsule.start, movement); + capsule.end = Vector3Add(capsule.end, movement); + + // Ground clamp + if (capsule.start.y < 0.5f) { + float correction = 0.5f - capsule.start.y; + capsule.start.y += correction; + capsule.end.y += correction; + velocity.y = 0; + } + + // Update camera position + Vector3 target = CAPSULE_CENTER(capsule); + float pitchRad = cameraPitch * DEG2RAD; + float angleRad = cameraAngle * DEG2RAD; + camera.position = (Vector3) { + target.x - sinf(angleRad) * cosf(pitchRad) * 5.0f, + target.y + sinf(pitchRad) * 5.0f, + target.z - cosf(angleRad) * cosf(pitchRad) * 5.0f + }; + camera.target = target; + + BeginDrawing(); + ClearBackground(BLACK); + R3D_Begin(camera); + R3D_DrawMeshPro(slopeMesh, slopeMat, slopeTransform); + R3D_DrawMesh(groundMesh, groundMat, Vector3Zero(), 1.0f); + R3D_DrawMesh(capsMesh, R3D_MATERIAL_BASE, CAPSULE_CENTER(capsule), 1.0f); + R3D_End(); + DrawFPS(10, 10); + DrawText(isGrounded ? "GROUNDED" : "AIRBORNE", 10, GetScreenHeight() - 30, 20, isGrounded ? LIME : YELLOW); + EndDrawing(); + } + + R3D_UnloadMeshData(slopeMeshData); + R3D_UnloadMesh(groundMesh); + R3D_UnloadMesh(slopeMesh); + R3D_UnloadMesh(capsMesh); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/lights.c`: + +```c +#include +#include +#include + +#define NUM_LIGHTS 128 +#define GRID_SIZE 100 + +static inline float randf(float min, float max) { + return min + (max - min) * ((float)rand() / (float)RAND_MAX); +} + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Many lights example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Set ambient light + R3D_ENVIRONMENT_SET(background.color, BLACK); + R3D_ENVIRONMENT_SET(ambient.color, (Color){10, 10, 10, 255}); + + // Create plane and cube meshes + R3D_Mesh plane = R3D_GenMeshPlane(100, 100, 1, 1); + R3D_Mesh cube = R3D_GenMeshCube(0.5f, 0.5f, 0.5f); + R3D_Material material = R3D_GetDefaultMaterial(); + + // Allocate transforms for all spheres + R3D_InstanceBuffer instances = R3D_LoadInstanceBuffer(GRID_SIZE * GRID_SIZE, R3D_INSTANCE_POSITION); + Vector3* positions = R3D_MapInstances(instances, R3D_INSTANCE_POSITION, false); + for (int x = -50; x < 50; x++) { + for (int z = -50; z < 50; z++) { + positions[(z+50)*GRID_SIZE + (x+50)] = (Vector3) {x + 0.5f, 0, z + 0.5f}; + } + } + R3D_UnmapInstances(instances, R3D_INSTANCE_POSITION); + + // Create lights + R3D_Light lights[NUM_LIGHTS]; + for (int i = 0; i < NUM_LIGHTS; i++) { + lights[i] = R3D_CreateLight(R3D_LIGHT_OMNI); + R3D_SetLightPosition(lights[i], (Vector3){randf(-50.0f, 50.0f), randf(1.0f, 5.0f), randf(-50.0f, 50.0f)}); + R3D_SetLightColor(lights[i], ColorFromHSV(randf(0.0f, 360.0f), 1.0f, 1.0f)); + R3D_SetLightRange(lights[i], randf(8.0f, 16.0f)); + R3D_SetLightActive(lights[i], true); + } + + // Setup camera + Camera3D camera = { + .position = {0, 10, 10}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_ORBITAL); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + // Draw scene + R3D_Begin(camera); + R3D_DrawMesh(plane, material, (Vector3) {0, -0.25f, 0}, 1.0f); + R3D_DrawMeshInstanced(cube, material, instances, GRID_SIZE*GRID_SIZE); + R3D_End(); + + // Optionally show lights shapes + if (IsKeyDown(KEY_F)) { + BeginMode3D(camera); + for (int i = 0; i < NUM_LIGHTS; i++) { + R3D_DrawLightShape(lights[i]); + } + EndMode3D(); + } + + DrawFPS(10, 10); + DrawText("Press 'F' to show the lights", 10, GetScreenHeight()-34, 24, BLACK); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadInstanceBuffer(instances); + R3D_UnloadMesh(cube); + R3D_UnloadMesh(plane); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/multiview.c`: + +```c +#include +#include + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Multiview"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Create meshes + R3D_Mesh plane = R3D_GenMeshPlane(1000, 1000, 1, 1); + R3D_Mesh sphere = R3D_GenMeshSphere(0.5f, 64, 64); + R3D_Material material = R3D_GetDefaultMaterial(); + + // Setup environment + R3D_ENVIRONMENT_SET(ambient.color, (Color){10, 10, 10, 255}); + + // Create light + R3D_Light light = R3D_CreateLight(R3D_LIGHT_SPOT); + R3D_LightLookAt(light, (Vector3){0, 10, 5}, (Vector3){0}); + R3D_EnableShadow(light); + R3D_SetLightActive(light, true); + + // Setup cameras + R3D_Camera cam0 = R3D_CAMERA_BASE; + R3D_Camera cam1 = R3D_CAMERA_BASE; + + // Main loop + while (!WindowShouldClose()) + { + float time = (float)GetTime(); + + cam0.position.x = 4.0f * cosf(time); + cam0.position.y = 4.0f; + cam0.position.z = 4.0f * sinf(time); + + cam1.position.x = 4.0f * cosf(-time); + cam1.position.y = 4.0f; + cam1.position.z = 4.0f * sinf(-time); + + R3D_CameraLookAt(&cam0, (Vector3){0}, (Vector3){0, 1, 0}); + R3D_CameraLookAt(&cam1, (Vector3){0}, (Vector3){0, 1, 0}); + + float hw = (float)GetScreenWidth()/2; + float h = (float)GetScreenHeight(); + + R3D_View view0 = {.camera = cam0, .viewport = {0, 0, hw, h}}; + R3D_View view1 = {.camera = cam1, .viewport = {hw, 0, hw, h}}; + + BeginDrawing(); + ClearBackground(RAYWHITE); + + R3D_BeginPro(view0); + R3D_DrawMesh(plane, material, (Vector3) {0, -0.5f, 0}, 1.0f); + R3D_DrawMesh(sphere, material, Vector3Zero(), 1.0f); + R3D_End(); + + R3D_BeginPro(view1); + R3D_DrawMesh(plane, material, (Vector3) {0, -0.5f, 0}, 1.0f); + R3D_DrawMesh(sphere, material, Vector3Zero(), 1.0f); + R3D_End(); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadMesh(sphere); + R3D_UnloadMesh(plane); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/particles.c`: + +```c +#include +#include + +#define MAX_PARTICLES 4096 + +typedef struct { + Vector3 pos; + Vector3 vel; + float life; +} Particle; + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Particles example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Set environment + R3D_ENVIRONMENT_SET(background.color, (Color){4, 4, 4}); + R3D_ENVIRONMENT_SET(bloom.mode, R3D_BLOOM_ADDITIVE); + + // Generate a gradient as emission texture for our particles + Image image = GenImageGradientRadial(64, 64, 0.0f, WHITE, BLACK); + Texture texture = LoadTextureFromImage(image); + UnloadImage(image); + + // Generate a quad mesh for our particles + R3D_Mesh mesh = R3D_GenMeshQuad(0.25f, 0.25f, 1, 1, (Vector3){0, 0, 1}); + + // Setup particle material + R3D_Material material = R3D_GetDefaultMaterial(); + material.billboardMode = R3D_BILLBOARD_FRONT; + material.blendMode = R3D_BLEND_ADDITIVE; + material.albedo.texture = R3D_GetBlackTexture(); + material.emission.color = (Color){255, 0, 0, 255}; + material.emission.texture = texture; + material.emission.energy = 1.0f; + + // Create particle instance buffer + R3D_InstanceBuffer instances = R3D_LoadInstanceBuffer(MAX_PARTICLES, R3D_INSTANCE_POSITION); + + // Setup camera + Camera3D camera = { + .position = {-7, 7, -7}, + .target = {0, 1, 0}, + .up = {0, 1, 0}, + .fovy = 60.0f, + .projection = CAMERA_PERSPECTIVE + }; + + // CPU buffer for storing particles + Particle particles[MAX_PARTICLES] = {0}; + Vector3 positions[MAX_PARTICLES]; + int particleCount = 0; + + while (!WindowShouldClose()) + { + float dt = GetFrameTime(); + UpdateCamera(&camera, CAMERA_ORBITAL); + + // Spawn particles + for (int i = 0; i < 10; i++) { + if (particleCount < MAX_PARTICLES) { + float angle = GetRandomValue(0, 360) * DEG2RAD; + particles[particleCount].pos = (Vector3){0, 0, 0}; + particles[particleCount].vel = (Vector3){ + cosf(angle) * GetRandomValue(20, 40) / 10.0f, + GetRandomValue(60, 80) / 10.0f, + sinf(angle) * GetRandomValue(20, 40) / 10.0f + }; + particles[particleCount].life = 1.0f; + particleCount++; + } + } + + // Update particles + int alive = 0; + for (int i = 0; i < particleCount; i++) { + particles[i].vel.y -= 9.81f * dt; + particles[i].pos.x += particles[i].vel.x * dt; + particles[i].pos.y += particles[i].vel.y * dt; + particles[i].pos.z += particles[i].vel.z * dt; + particles[i].life -= dt * 0.5f; + if (particles[i].life > 0) { + positions[alive] = particles[i].pos; + particles[alive] = particles[i]; + alive++; + } + } + particleCount = alive; + + R3D_UploadInstances(instances, R3D_INSTANCE_POSITION, 0, particleCount, positions, true); + + BeginDrawing(); + R3D_Begin(camera); + R3D_DrawMeshInstanced(mesh, material, instances, particleCount); + R3D_End(); + DrawFPS(10, 10); + EndDrawing(); + } + + R3D_UnloadInstanceBuffer(instances); + R3D_UnloadMaterial(material); + R3D_UnloadMesh(mesh); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/pbr.c`: + +```c +#include +#include + +#ifndef RESOURCES_PATH +# define RESOURCES_PATH "./" +#endif + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - PBR example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + R3D_SetAntiAliasingMode(R3D_ANTI_ALIASING_MODE_FXAA); + + // Setup environment sky + R3D_Cubemap cubemap = R3D_LoadCubemap(RESOURCES_PATH "panorama/indoor.hdr", R3D_CUBEMAP_LAYOUT_AUTO_DETECT); + R3D_ENVIRONMENT_SET(background.skyBlur, 0.775f); + R3D_ENVIRONMENT_SET(background.sky, cubemap); + + // Setup environment ambient + R3D_AmbientMap ambientMap = R3D_GenAmbientMap(cubemap, R3D_AMBIENT_ILLUMINATION | R3D_AMBIENT_REFLECTION); + R3D_ENVIRONMENT_SET(ambient.map, ambientMap); + + // Setup bloom + R3D_ENVIRONMENT_SET(bloom.mode, R3D_BLOOM_MIX); + R3D_ENVIRONMENT_SET(bloom.intensity, 0.02f); + + // Setup tonemapping + R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_FILMIC); + R3D_ENVIRONMENT_SET(tonemap.exposure, 0.5f); + R3D_ENVIRONMENT_SET(tonemap.white, 4.0f); + + // Load model + R3D_SetTextureFilter(TEXTURE_FILTER_ANISOTROPIC_4X); + R3D_Model model = R3D_LoadModel(RESOURCES_PATH "models/DamagedHelmet.glb"); + Matrix modelMatrix = MatrixIdentity(); + float modelScale = 1.0f; + + // Setup camera + Camera3D camera = { + .position = {0, 0, 2.5f}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Main loop + while (!WindowShouldClose()) + { + // Update model scale with mouse wheel + modelScale = Clamp(modelScale + GetMouseWheelMove() * 0.1f, 0.25f, 2.5f); + + // Rotate model with left mouse button + if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { + float pitch = (GetMouseDelta().y * 0.005f) / modelScale; + float yaw = (GetMouseDelta().x * 0.005f) / modelScale; + Matrix rotate = MatrixRotateXYZ((Vector3){pitch, yaw, 0.0f}); + modelMatrix = MatrixMultiply(modelMatrix, rotate); + } + + BeginDrawing(); + ClearBackground(RAYWHITE); + R3D_Begin(camera); + Matrix scale = MatrixScale(modelScale, modelScale, modelScale); + Matrix transform = MatrixMultiply(modelMatrix, scale); + R3D_DrawModelPro(model, transform); + R3D_End(); + EndDrawing(); + } + + // Cleanup + R3D_UnloadModel(model, true); + R3D_UnloadAmbientMap(ambientMap); + R3D_UnloadCubemap(cubemap); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/probe.c`: + +```c +#include +#include + +#ifndef RESOURCES_PATH +# define RESOURCES_PATH "./" +#endif + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Probe example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Setup environment sky + R3D_Cubemap cubemap = R3D_LoadCubemap(RESOURCES_PATH "panorama/indoor.hdr", R3D_CUBEMAP_LAYOUT_AUTO_DETECT); + R3D_ENVIRONMENT_SET(background.skyBlur, 0.3f); + R3D_ENVIRONMENT_SET(background.sky, cubemap); + + // Setup environment ambient + R3D_AmbientMap ambientMap = R3D_GenAmbientMap(cubemap, R3D_AMBIENT_ILLUMINATION | R3D_AMBIENT_REFLECTION); + R3D_ENVIRONMENT_SET(ambient.map, ambientMap); + R3D_ENVIRONMENT_SET(ambient.energy, 0.2f); + + // Setup tonemapping + R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_FILMIC); + + // Create meshes + R3D_Mesh plane = R3D_GenMeshPlane(30, 30, 1, 1); + R3D_Mesh sphere = R3D_GenMeshSphere(0.5f, 64, 64); + R3D_Material material = R3D_GetDefaultMaterial(); + + // Create light + R3D_Light light = R3D_CreateLight(R3D_LIGHT_SPOT); + R3D_LightLookAt(light, (Vector3){0, 10, 5}, (Vector3){0}); + R3D_SetLightActive(light, true); + R3D_EnableShadow(light); + + // Create probe + R3D_Probe probe = R3D_CreateProbe(R3D_PROBE_ILLUMINATION | R3D_PROBE_REFLECTION); + R3D_SetProbePosition(probe, (Vector3) {0, 1, 0}); + R3D_SetProbeShadows(probe, true); + R3D_SetProbeFalloff(probe, 0.5f); + R3D_SetProbeActive(probe, true); + + // Setup camera + Camera3D camera = { + .position = {0, 3.0f, 6.0f}, + .target = {0, 0.5f, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_ORBITAL); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + R3D_Begin(camera); + + material.orm.roughness = 0.5f; + material.orm.metalness = 0.0f; + R3D_DrawMesh(plane, material, Vector3Zero(), 1.0f); + + for (int i = -1; i <= 1; i++) { + material.orm.roughness = fabsf((float)i) * 0.4f; + material.orm.metalness = 1.0f - fabsf((float)i); + R3D_DrawMesh(sphere, material, (Vector3) {(float)i * 3.0f, 1.0f, 0}, 2.0f); + } + + R3D_End(); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadAmbientMap(ambientMap); + R3D_UnloadCubemap(cubemap); + R3D_UnloadMesh(sphere); + R3D_UnloadMesh(plane); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/resize.c`: + +```c +#include +#include + +static const char* GetAspectModeName(R3D_AspectMode mode); +static const char* GetUpscaleModeName(R3D_UpscaleMode mode); + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Resize example"); + SetWindowState(FLAG_WINDOW_RESIZABLE); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Create sphere mesh and materials + R3D_Mesh sphere = R3D_GenMeshSphere(0.5f, 64, 64); + R3D_Material materials[5]; + for (int i = 0; i < 5; i++) + { + materials[i] = R3D_GetDefaultMaterial(); + materials[i].albedo.color = ColorFromHSV((float)i / 5 * 330, 1.0f, 1.0f); + } + + // Setup directional light + R3D_Light light = R3D_CreateLight(R3D_LIGHT_DIR); + R3D_SetLightDirection(light, (Vector3){0, 0, -1}); + R3D_SetLightActive(light, true); + + // Setup camera + Camera3D camera = { + .position = {0, 2, 2}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Current blit state + R3D_AspectMode aspect = R3D_ASPECT_EXPAND; + R3D_UpscaleMode upscale = R3D_UPSCALE_NEAREST; + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_ORBITAL); + + // Toggle aspect keep + if (IsKeyPressed(KEY_R)) { + aspect = (aspect + 1) % 2; + R3D_SetAspectMode(aspect); + } + + // Toggle linear filtering + if (IsKeyPressed(KEY_F)) { + upscale = (upscale + 1) % 4; + R3D_SetUpscaleMode(upscale); + } + + BeginDrawing(); + ClearBackground(BLACK); + + // Draw spheres + R3D_Begin(camera); + for (int i = 0; i < 5; i++) { + R3D_DrawMesh(sphere, materials[i], (Vector3) {(float)i - 2, 0, 0}, 1.0f); + } + R3D_End(); + + // Draw info + DrawText(TextFormat("Resize mode: %s", GetAspectModeName(aspect)), 10, 10, 20, RAYWHITE); + DrawText(TextFormat("Filter mode: %s", GetUpscaleModeName(upscale)), 10, 40, 20, RAYWHITE); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadMesh(sphere); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +const char* GetAspectModeName(R3D_AspectMode mode) +{ + switch (mode) { + case R3D_ASPECT_EXPAND: return "EXPAND"; + case R3D_ASPECT_KEEP: return "KEEP"; + } + return "UNKNOWN"; +} + +const char* GetUpscaleModeName(R3D_UpscaleMode mode) +{ + switch (mode) { + case R3D_UPSCALE_NEAREST: return "NEAREST"; + case R3D_UPSCALE_LINEAR: return "LINEAR"; + case R3D_UPSCALE_BICUBIC: return "BICUBIC"; + case R3D_UPSCALE_LANCZOS: return "LANCZOS"; + } + return "UNKNOWN"; +} + +``` + +`examples/resources/shaders/material.glsl`: + +```glsl +#pragma usage opaque shadow + +uniform sampler2D u_texture; +uniform float u_time_scale = 2.0; + +flat varying float v_time; + +void vertex() { + v_time = 0.5 * sin(TIME * u_time_scale) + 0.5; + POSITION *= 1.0 + v_time; +} + +void fragment() { + vec2 uv = TEXCOORD; + uv.y += v_time * 0.5; + + const vec3 base_color = vec3(0.5, 0.1, 0.0); + const vec3 active_color = vec3(1.0, 0.2, 0.0); + vec3 color = mix(base_color, active_color, v_time); + + ALBEDO = color * texture(u_texture, uv).rgb; + EMISSION = ALBEDO * v_time * 0.5; + + ROUGHNESS = mix(1.0, 0.25, v_time); + METALNESS = mix(0.5, 1.0, v_time); +} + +``` + +`examples/resources/shaders/screen.glsl`: + +```glsl +uniform float u_time_scale; + +void fragment() { + vec2 uv = TEXCOORD * 2.0 - 1.0; + float time = TIME * u_time_scale; + uv.x += sin(uv.y * 10.0 + time * 3.0) * 0.01; + uv.y += sin(uv.x * 10.0 + time * 2.0) * 0.01; + COLOR = SampleColor(uv * 0.5 + 0.5); +} + +``` + +`examples/resources/shaders/sky.glsl`: + +```glsl +uniform vec3 u_color; +uniform ivec2 u_cells; +uniform float u_line_px; + +void fragment() +{ + vec2 uv = TEXCOORD; + + vec2 cells = max(vec2(u_cells), vec2(1.0)); + vec2 g = uv * cells; + vec2 cell_uv = fract(g); + vec2 dist_to_edge = min(cell_uv, 1.0 - cell_uv); + + vec2 px = fwidth(cell_uv); + vec2 half_thick = 0.5 * u_line_px * px; + + float line_x = 1.0 - smoothstep(half_thick.x, half_thick.x + px.x, dist_to_edge.x); + float line_y = 1.0 - smoothstep(half_thick.y, half_thick.y + px.y, dist_to_edge.y); + + float grid = max(line_x, line_y); + + COLOR = u_color * grid; +} + +``` + +`examples/shader.c`: + +```c +#include +#include + +#ifndef RESOURCES_PATH +# define RESOURCES_PATH "./" +#endif + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Shader example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Setup environment + R3D_ENVIRONMENT_SET(ambient.color, (Color){10, 10, 10, 255}); + R3D_ENVIRONMENT_SET(bloom.mode, R3D_BLOOM_ADDITIVE); + + // Create meshes + R3D_Mesh plane = R3D_GenMeshPlane(1000, 1000, 1, 1); + R3D_Mesh torus = R3D_GenMeshTorus(0.5f, 0.1f, 32, 16); + + // Create material + R3D_Material material = R3D_GetDefaultMaterial(); + material.shader = R3D_LoadSurfaceShader(RESOURCES_PATH "shaders/material.glsl"); + + // Generate a texture for custom sampler + Image image = GenImageChecked(512, 512, 16, 32, WHITE, BLACK); + Texture texture = LoadTextureFromImage(image); + UnloadImage(image); + + // Set material custom uniform/sampler + R3D_SetSurfaceShaderSampler(material.shader, "u_texture", texture); + + // Load a screen shader + R3D_ScreenShader* shader = R3D_LoadScreenShader(RESOURCES_PATH "shaders/screen.glsl"); + R3D_SetScreenShaderChain(R3D_SCREEN_SHADER_STAGE_OUTPUT, &shader, 1); + + // Set screen custom unforms + R3D_SetScreenShaderUniform(shader, "u_time_scale", (float[]){2.5f}); + + // Create light + R3D_Light light = R3D_CreateLight(R3D_LIGHT_SPOT); + R3D_LightLookAt(light, (Vector3){0, 10, 5}, (Vector3){0}); + R3D_EnableShadow(light); + R3D_SetLightActive(light, true); + + // Setup camera + Camera3D camera = { + .position = {0, 2, 2}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_ORBITAL); + + BeginDrawing(); + ClearBackground(RAYWHITE); + R3D_Begin(camera); + R3D_DrawMesh(plane, R3D_MATERIAL_BASE, (Vector3) {0, -0.5f, 0}, 1.0f); + R3D_DrawMesh(torus, material, Vector3Zero(), 1.0f); + R3D_End(); + EndDrawing(); + } + + // Cleanup + R3D_UnloadSurfaceShader(material.shader); + R3D_UnloadScreenShader(shader); + R3D_UnloadMesh(torus); + R3D_UnloadMesh(plane); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/skybox.c`: + +```c +#include +#include + +#ifndef RESOURCES_PATH +# define RESOURCES_PATH "./" +#endif + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Skybox example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Create sphere mesh + R3D_Mesh sphere = R3D_GenMeshSphere(0.5f, 32, 64); + + // Define procedural skybox parameters + R3D_ProceduralSky skyParams = R3D_PROCEDURAL_SKY_BASE; + skyParams.groundEnergy = 2.0f; + skyParams.skyEnergy = 2.0f; + skyParams.sunEnergy = 2.0f; + + // Load a custom sky shader + R3D_SkyShader* shader = R3D_LoadSkyShader(RESOURCES_PATH "shaders/sky.glsl"); + R3D_SetSkyShaderUniform(shader, "u_color", &(Vector3){0.0f, 0.5f, 0.0f}); + R3D_SetSkyShaderUniform(shader, "u_cells", (int[2]){10, 10}); + R3D_SetSkyShaderUniform(shader, "u_line_px", (float[1]){1.0f}); + + // Load and generate skyboxes + R3D_Cubemap skyPanorama = R3D_LoadCubemap(RESOURCES_PATH "panorama/sky.hdr", R3D_CUBEMAP_LAYOUT_AUTO_DETECT); + R3D_Cubemap skyProcedural = R3D_GenProceduralSky(1024, skyParams); + R3D_Cubemap skyCustom = R3D_GenCustomSky(512, shader); + + // Generate ambient maps + R3D_AmbientMap ambientPanorama = R3D_GenAmbientMap(skyPanorama, R3D_AMBIENT_ILLUMINATION | R3D_AMBIENT_REFLECTION); + R3D_AmbientMap ambientProcedural = R3D_GenAmbientMap(skyProcedural, R3D_AMBIENT_ILLUMINATION | R3D_AMBIENT_REFLECTION); + R3D_AmbientMap ambientCustom = R3D_GenAmbientMap(skyCustom, R3D_AMBIENT_ILLUMINATION | R3D_AMBIENT_REFLECTION); + + // Store skies/ambients + R3D_EnvBackground backgrounds[3] = {0}; + R3D_EnvAmbient ambients[3] = {0}; + int currentSky = 0; + + for (int i = 0; i < 3; i++) { + backgrounds[i].energy = 1.0f; + ambients[i].energy = 1.0f; + } + + backgrounds[0].sky = skyPanorama; + backgrounds[1].sky = skyProcedural; + backgrounds[2].sky = skyCustom; + + ambients[0].map = ambientPanorama; + ambients[1].map = ambientProcedural; + ambients[2].map = ambientCustom; + + // Set default sky/ambient maps + R3D_ENVIRONMENT_SET(background.sky, skyPanorama); + R3D_ENVIRONMENT_SET(ambient.map, ambientPanorama); + + // Set tonemapping + R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_AGX); + + // Setup camera + Camera3D camera = { + .position = {0, 0, 10}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Capture mouse + DisableCursor(); + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_FREE); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + currentSky += IsMouseButtonPressed(MOUSE_RIGHT_BUTTON) - IsMouseButtonPressed(MOUSE_LEFT_BUTTON); + currentSky = (currentSky + 3) % 3; + + R3D_ENVIRONMENT_SET(background, backgrounds[currentSky]); + R3D_ENVIRONMENT_SET(ambient, ambients[currentSky]); + + // Draw sphere grid + R3D_Begin(camera); + for (int x = 0; x <= 8; x++) { + for (int y = 0; y <= 8; y++) { + R3D_Material material = R3D_MATERIAL_BASE; + material.orm.roughness = Remap((float)y, 0.0f, 8.0f, 0.0f, 1.0f); + material.orm.metalness = Remap((float)x, 0.0f, 8.0f, 0.0f, 1.0f); + R3D_DrawMesh(sphere, material, (Vector3) {(float)(x - 4) * 1.25f, ((float)y - 4) * 1.25f, 0.0f}, 1.0f); + } + } + R3D_End(); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadAmbientMap(ambientProcedural); + R3D_UnloadAmbientMap(ambientPanorama); + R3D_UnloadCubemap(skyProcedural); + R3D_UnloadCubemap(skyPanorama); + R3D_UnloadMesh(sphere); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/sponza.c`: + +```c +#include +#include + +#ifndef RESOURCES_PATH +# define RESOURCES_PATH "./" +#endif + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Sponza example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Post-processing setup + R3D_ENVIRONMENT_SET(bloom.mode, R3D_BLOOM_MIX); + R3D_ENVIRONMENT_SET(ssgi.intensity, 8.0f); + R3D_ENVIRONMENT_SET(ssao.enabled, true); + + // Background and ambient + R3D_ENVIRONMENT_SET(background.color, SKYBLUE); + R3D_ENVIRONMENT_SET(ambient.color, DARKGRAY); + + // Load Sponza model + R3D_SetTextureFilter(TEXTURE_FILTER_ANISOTROPIC_8X); + R3D_Model sponza = R3D_LoadModel(RESOURCES_PATH "models/Sponza.glb"); + + // Setup lights + R3D_Light lights[2]; + for (int i = 0; i < 2; i++) { + lights[i] = R3D_CreateLight(R3D_LIGHT_OMNI); + R3D_SetLightPosition(lights[i], (Vector3) {i ? -10.0f : 10.0f, 20.0f, 0.0f}); + R3D_SetLightActive(lights[i], true); + R3D_SetLightEnergy(lights[i], 8.0f); + R3D_SetShadowUpdateMode(lights[i], R3D_SHADOW_UPDATE_MANUAL); + R3D_EnableShadow(lights[i]); + } + + // Setup camera + Camera3D camera = { + .position = {8.0f, 1.0f, 0.5f}, + .target = {0.0f, 2.0f, -2.0f}, + .up = {0.0f, 1.0f, 0.0f}, + .fovy = 60.0f + }; + + // Capture mouse + DisableCursor(); + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_FREE); + + // Toggle SSAO + if (IsKeyPressed(KEY_ONE)) { + R3D_ENVIRONMENT_SET(ssao.enabled, !R3D_ENVIRONMENT_GET(ssao.enabled)); + } + + // Toggle SSIL + if (IsKeyPressed(KEY_TWO)) { + R3D_ENVIRONMENT_SET(ssil.enabled, !R3D_ENVIRONMENT_GET(ssil.enabled)); + } + + // Toggle SSGI + if (IsKeyPressed(KEY_THREE)) { + R3D_ENVIRONMENT_SET(ssgi.enabled, !R3D_ENVIRONMENT_GET(ssgi.enabled)); + } + + // Toggle SSR + if (IsKeyPressed(KEY_FOUR)) { + R3D_ENVIRONMENT_SET(ssr.enabled, !R3D_ENVIRONMENT_GET(ssr.enabled)); + } + + // Toggle fog + if (IsKeyPressed(KEY_FIVE)) { + R3D_ENVIRONMENT_SET(fog.mode, R3D_ENVIRONMENT_GET(fog.mode) == R3D_FOG_DISABLED ? R3D_FOG_EXP : R3D_FOG_DISABLED); + } + + // Swtich anti aliasing mode + if (IsKeyPressed(KEY_F)) { + R3D_AntiAliasingMode aaMode = R3D_GetAntiAliasingMode(); + R3D_SetAntiAliasingMode((aaMode + 1) % 3); + } + + // Swtich anti aliasing preset + if (IsKeyPressed(KEY_G)) { + R3D_AntiAliasingPreset aaPreset = R3D_GetAntiAliasingPreset(); + R3D_SetAntiAliasingPreset((aaPreset + 1) % R3D_ANTI_ALIASING_PRESET_COUNT); + } + + // Swtich tonemapping + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { + R3D_Tonemap mode = R3D_ENVIRONMENT_GET(tonemap.mode); + R3D_ENVIRONMENT_SET(tonemap.mode, (mode + R3D_TONEMAP_COUNT - 1) % R3D_TONEMAP_COUNT); + } + if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) { + R3D_Tonemap mode = R3D_ENVIRONMENT_GET(tonemap.mode); + R3D_ENVIRONMENT_SET(tonemap.mode, (mode + 1) % R3D_TONEMAP_COUNT); + } + + BeginDrawing(); + ClearBackground(RAYWHITE); + + // Draw Sponza model + R3D_Begin(camera); + R3D_DrawModel(sponza, Vector3Zero(), 1.0f); + R3D_End(); + + // Draw lights + BeginMode3D(camera); + DrawSphere(R3D_GetLightPosition(lights[0]), 0.5f, WHITE); + DrawSphere(R3D_GetLightPosition(lights[1]), 0.5f, WHITE); + EndMode3D(); + + // Display tonemapping + R3D_Tonemap tonemap = R3D_ENVIRONMENT_GET(tonemap.mode); + const char* tonemapText = ""; + switch (tonemap) + { + case R3D_TONEMAP_LINEAR: tonemapText = "< TONEMAP LINEAR >"; break; + case R3D_TONEMAP_REINHARD: tonemapText = "< TONEMAP REINHARD >"; break; + case R3D_TONEMAP_FILMIC: tonemapText = "< TONEMAP FILMIC >"; break; + case R3D_TONEMAP_ACES: tonemapText = "< TONEMAP ACES >"; break; + case R3D_TONEMAP_AGX: tonemapText = "< TONEMAP AGX >"; break; + default: break; + } + DrawText(tonemapText, GetScreenWidth() - MeasureText(tonemapText, 20) - 10, 10, 20, LIME); + + // Display anti aliasing mode + R3D_AntiAliasingMode aaMode = R3D_GetAntiAliasingMode(); + const char* aaModeText = ""; + switch (aaMode) + { + case R3D_ANTI_ALIASING_MODE_NONE: aaModeText = "AA: NONE"; break; + case R3D_ANTI_ALIASING_MODE_FXAA: aaModeText = "AA: FXAA"; break; + case R3D_ANTI_ALIASING_MODE_SMAA: aaModeText = "AA: SMAA"; break; + default: break; + } + DrawText(aaModeText, 10, GetScreenHeight() - 30, 20, LIME); + + // Display anti aliasing preset + R3D_AntiAliasingPreset aaPreset = R3D_GetAntiAliasingPreset(); + const char* aaPresetText = ""; + switch (aaPreset) + { + case R3D_ANTI_ALIASING_PRESET_LOW: aaPresetText = "- Low"; break; + case R3D_ANTI_ALIASING_PRESET_MEDIUM: aaPresetText = "- Medium"; break; + case R3D_ANTI_ALIASING_PRESET_HIGH: aaPresetText = "- High"; break; + case R3D_ANTI_ALIASING_PRESET_ULTRA: aaPresetText = "- Ultra"; break; + default: break; + } + DrawText(aaPresetText, MeasureText(aaModeText, 20) + 20, GetScreenHeight() - 30, 20, LIME); + + DrawFPS(10, 10); + EndDrawing(); + } + + // Cleanup + R3D_UnloadModel(sponza, true); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/sprite.c`: + +```c +#include "r3d/r3d_core.h" +#include "raylib.h" +#include +#include + +#ifndef RESOURCES_PATH +# define RESOURCES_PATH "./" +#endif + +static void GetTexCoordScaleOffset(Vector2* uvScale, Vector2* uvOffset, int xFrameCount, int yFrameCount, float currentFrame); + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Sprite example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + R3D_SetTextureFilter(TEXTURE_FILTER_POINT); + R3D_SetTextureWrap(TEXTURE_WRAP_REPEAT); + + // Set background/ambient color + R3D_ENVIRONMENT_SET(background.color, (Color){102, 191, 255, 255}); + R3D_ENVIRONMENT_SET(ambient.color, (Color){10, 19, 25, 255}); + R3D_ENVIRONMENT_SET(tonemap.mode, R3D_TONEMAP_FILMIC); + + // Create ground mesh and material + R3D_Mesh meshGround = R3D_GenMeshPlane(200, 200, 1, 1); + R3D_Material matGround = R3D_GetDefaultMaterial(); + matGround.albedo.color = GREEN; + + // Create sprite mesh and material + R3D_Mesh meshSprite = R3D_GenMeshQuad(1.0f, 1.0f, 1, 1, (Vector3){0,0,1}); + meshSprite.shadowCastMode = R3D_SHADOW_CAST_ON_DOUBLE_SIDED; + + R3D_Material matSprite = R3D_GetDefaultMaterial(); + matSprite.albedo = R3D_LoadAlbedoMap(RESOURCES_PATH "images/spritesheet.png", WHITE); + matSprite.billboardMode = R3D_BILLBOARD_Y_AXIS; + + // Setup spotlight + R3D_Light light = R3D_CreateLight(R3D_LIGHT_SPOT); + R3D_LightLookAt(light, (Vector3){0,10,10}, (Vector3){0}); + R3D_SetLightRange(light, 64.0f); + R3D_EnableShadow(light); + R3D_SetLightActive(light, true); + + // Setup camera + Camera3D camera = { + .position = {0, 2, 5}, + .target = {0, 0.5f, 0}, + .up = {0, 1, 0}, + .fovy = 45 + }; + + // Bird data + Vector3 birdPos = {0, 0.5f, 0}; + float birdDirX = 1.0f; + + // Main loop + while (!WindowShouldClose()) + { + // Update bird position + Vector3 birdPrev = birdPos; + birdPos.x = 2.0f * sinf((float)GetTime()); + birdPos.y = 1.0f + cosf((float)GetTime() * 4.0f) * 0.5f; + birdDirX = (birdPos.x - birdPrev.x >= 0.0f) ? 1.0f : -1.0f; + + // Update sprite UVs + // We multiply by the sign of the X direction to invert the uvScale.x + float currentFrame = 10.0f * (float)GetTime(); + GetTexCoordScaleOffset(&matSprite.uvScale, &matSprite.uvOffset, (int)(4 * birdDirX), 1, currentFrame); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + // Draw scene + R3D_Begin(camera); + R3D_DrawMesh(meshGround, matGround, (Vector3) {0, -0.5f, 0}, 1.0f); + R3D_DrawMesh(meshSprite, matSprite, (Vector3) {birdPos.x, birdPos.y, 0}, 1.0f); + R3D_End(); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadMaterial(matSprite); + R3D_UnloadMesh(meshSprite); + R3D_UnloadMesh(meshGround); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +void GetTexCoordScaleOffset(Vector2* uvScale, Vector2* uvOffset, int xFrameCount, int yFrameCount, float currentFrame) +{ + uvScale->x = 1.0f / xFrameCount; + uvScale->y = 1.0f / yFrameCount; + + int frameIndex = (int)(currentFrame + 0.5f) % (xFrameCount * yFrameCount); + int frameX = frameIndex % xFrameCount; + int frameY = frameIndex / xFrameCount; + + uvOffset->x = frameX * uvScale->x; + uvOffset->y = frameY * uvScale->y; +} + +``` + +`examples/stencil.c`: + +```c +#include +#include + +int main(void) +{ + InitWindow(800, 450, "[r3d] - Stencil Effects"); + SetTargetFPS(60); + + R3D_Init(GetScreenWidth(), GetScreenHeight()); + R3D_SetAntiAliasingMode(R3D_ANTI_ALIASING_MODE_SMAA); + + // Create the meshes used in the scene + R3D_Mesh plane = R3D_GenMeshPlane(20, 20, 1, 1); + R3D_Mesh box = R3D_GenMeshCube(1.5f, 2.0f, 0.3f); + R3D_Mesh sphere = R3D_GenMeshSphere(0.5f, 32, 32); + + R3D_Material matGround = R3D_GetDefaultMaterial(); + matGround.albedo.color = (Color){160, 160, 160, 255}; + + R3D_Material matWall = R3D_GetDefaultMaterial(); + matWall.albedo.color = (Color){120, 80, 60, 255}; + + // Main X-Ray sphere material + // The first pass draws the sphere normally and marks its visible pixels + // in the stencil buffer with the value 0x01 + R3D_Material matXraySolid = R3D_GetDefaultMaterial(); + matXraySolid.albedo.color = (Color){80, 140, 220, 255}; + matXraySolid.stencil.mode = R3D_COMPARE_ALWAYS; + matXraySolid.stencil.ref = 0x01; + matXraySolid.stencil.opPass = R3D_STENCIL_REPLACE; + + // Ghost X-Ray sphere material + // The second pass ignores depth so the sphere can be drawn through the wall, + // but only where the first pass did not already mark the stencil buffer + R3D_Material matXrayGhost = R3D_GetDefaultMaterial(); + matXrayGhost.albedo.color = (Color){80, 140, 220, 60}; + matXrayGhost.depth.mode = R3D_COMPARE_ALWAYS; + matXrayGhost.stencil.mode = R3D_COMPARE_NOTEQUAL; + matXrayGhost.stencil.ref = 0x01; + matXrayGhost.transparencyMode = R3D_TRANSPARENCY_ALPHA; + matXrayGhost.unlit = true; + + // Main outline sphere material + // The first pass draws the red sphere and marks its silhouette + // in the stencil buffer with the value 0x02 + R3D_Material matOutlineSolid = R3D_GetDefaultMaterial(); + matOutlineSolid.albedo.color = (Color){220, 100, 80, 255}; + matOutlineSolid.stencil.mode = R3D_COMPARE_ALWAYS; + matOutlineSolid.stencil.ref = 0x02; + matOutlineSolid.stencil.opPass = R3D_STENCIL_REPLACE; + + // Outline material + // The second pass draws the same sphere slightly larger, only on pixels + // outside the silhouette already marked by the first pass + R3D_Material matOutlineRing = R3D_GetDefaultMaterial(); + matOutlineRing.albedo.color = (Color){255, 220, 0, 255}; + matOutlineRing.stencil.mode = R3D_COMPARE_NOTEQUAL; + matOutlineRing.stencil.ref = 0x02; + matOutlineRing.cullMode = R3D_CULL_FRONT; + matOutlineRing.unlit = true; + + // Configure lighting, shadows, and ambient color + R3D_ENVIRONMENT_SET(ambient.color, (Color){10, 10, 15, 255}); + + R3D_Light light = R3D_CreateLight(R3D_LIGHT_SPOT); + R3D_LightLookAt(light, (Vector3){4, 8, 5}, (Vector3){0}); + R3D_SetShadowSoftness(light, 8.0f); + R3D_SetLightActive(light, true); + R3D_EnableShadow(light); + + Camera3D camera = { + .position = {0, 3, 5}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 55 + }; + + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_ORBITAL); + + BeginDrawing(); + ClearBackground(BLACK); + + R3D_Begin(camera); + // Base scene geometry + R3D_DrawMesh(plane, matGround, (Vector3){ 0.0f, -0.5f, 0.0f }, 1.0f); + R3D_DrawMesh(box, matWall, (Vector3){ 0.0f, 0.5f, 0.0f }, 1.0f); + + // X-Ray sphere: visible solid pass, then transparent pass through the wall + R3D_DrawMesh(sphere, matXraySolid, (Vector3){ 0.0f, 0.5f, -1.5f }, 1.0f); + R3D_DrawMesh(sphere, matXrayGhost, (Vector3){ 0.0f, 0.5f, -1.5f }, 1.0f); + + // Outline sphere: normal object pass, then slightly enlarged outline pass + R3D_DrawMesh(sphere, matOutlineSolid, (Vector3){ 2.2f, 0.2f, 0.8f }, 1.00f); + R3D_DrawMesh(sphere, matOutlineRing, (Vector3){ 2.2f, 0.2f, 0.8f }, 1.08f); + R3D_End(); + EndDrawing(); + } + + R3D_UnloadMesh(sphere); + R3D_UnloadMesh(box); + R3D_UnloadMesh(plane); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/sun.c`: + +```c +#include +#include + +#define X_INSTANCES 50 +#define Y_INSTANCES 50 +#define INSTANCE_COUNT (X_INSTANCES * Y_INSTANCES) + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Sun example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + R3D_SetAntiAliasingMode(R3D_ANTI_ALIASING_MODE_FXAA); + + // Create meshes and material + R3D_Mesh plane = R3D_GenMeshPlane(1000, 1000, 1, 1); + R3D_Mesh sphere = R3D_GenMeshSphere(0.35f, 16, 32); + R3D_Material material = R3D_GetDefaultMaterial(); + + // Create transforms for instanced spheres + R3D_InstanceBuffer instances = R3D_LoadInstanceBuffer(INSTANCE_COUNT, R3D_INSTANCE_POSITION); + Vector3* positions = R3D_MapInstances(instances, R3D_INSTANCE_POSITION, false); + float spacing = 1.5f; + float offsetX = (X_INSTANCES * spacing) / 2.0f; + float offsetZ = (Y_INSTANCES * spacing) / 2.0f; + int idx = 0; + for (int x = 0; x < X_INSTANCES; x++) { + for (int y = 0; y < Y_INSTANCES; y++) { + positions[idx] = (Vector3) {x * spacing - offsetX, 0, y * spacing - offsetZ}; + idx++; + } + } + R3D_UnmapInstances(instances, R3D_INSTANCE_POSITION); + + // Setup environment + R3D_Cubemap skybox = R3D_GenProceduralSky(1024, R3D_PROCEDURAL_SKY_BASE); + R3D_ENVIRONMENT_SET(background.sky, skybox); + + R3D_AmbientMap ambientMap = R3D_GenAmbientMap(skybox, R3D_AMBIENT_ILLUMINATION | R3D_AMBIENT_REFLECTION); + R3D_ENVIRONMENT_SET(ambient.map, ambientMap);; + + // Create directional light with shadows + R3D_Light light = R3D_CreateLight(R3D_LIGHT_DIR); + R3D_SetLightDirection(light, (Vector3){-1, -1, -1}); + R3D_SetLightActive(light, true); + R3D_SetLightRange(light, 16.0f); + R3D_SetShadowSoftness(light, 2.0f); + R3D_EnableShadow(light); + + // Setup camera + Camera3D camera = { + .position = {0, 1, 0}, + .target = {1, 1.25f, 1}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Capture mouse + DisableCursor(); + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_FREE); + + BeginDrawing(); + ClearBackground(RAYWHITE); + R3D_Begin(camera); + R3D_DrawMesh(plane, material, (Vector3) {0, -0.5f, 0}, 1.0f); + R3D_DrawMeshInstanced(sphere, material, instances, INSTANCE_COUNT); + R3D_End(); + EndDrawing(); + } + + // Cleanup + R3D_UnloadInstanceBuffer(instances); + R3D_UnloadMaterial(material); + R3D_UnloadMesh(sphere); + R3D_UnloadMesh(plane); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/to_texture.c`: + +```c +#include +#include + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Render to texture"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Create meshes + R3D_Mesh plane = R3D_GenMeshPlane(1000, 1000, 1, 1); + R3D_Mesh sphere = R3D_GenMeshSphere(0.5f, 64, 64); + R3D_Material material = R3D_GetDefaultMaterial(); + + // Setup environment + R3D_ENVIRONMENT_SET(ambient.color, (Color){10, 10, 10, 255}); + + // Create light + R3D_Light light = R3D_CreateLight(R3D_LIGHT_SPOT); + R3D_LightLookAt(light, (Vector3){0, 10, 5}, (Vector3){0}); + R3D_EnableShadow(light); + R3D_SetLightActive(light, true); + + // Render texture + RenderTexture target = LoadRenderTexture(1024, 512); + + // Setup cameras + Camera3D r3dCamera = { + .position = {0, 2, 2}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + Camera3D rlCamera = { + .position = {0, 1, 4}, + .target = {0, 1, -1}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + DisableCursor(); + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&r3dCamera, CAMERA_ORBITAL); + UpdateCamera(&rlCamera, CAMERA_FREE); + + BeginDrawing(); + ClearBackground(DARKGRAY); + + R3D_View view = { + .camera = R3D_CameraFromRL(r3dCamera), + .target = target, + }; + + R3D_BeginPro(view); + R3D_DrawMesh(plane, material, (Vector3) {0, -0.5f, 0}, 1.0f); + R3D_DrawMesh(sphere, material, Vector3Zero(), 1.0f); + R3D_End(); + + BeginMode3D(rlCamera); + DrawBillboard(rlCamera, target.texture, (Vector3) {0, 1, 0}, -2, WHITE); + DrawGrid(10, 1); + EndMode3D(); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadMesh(sphere); + R3D_UnloadMesh(plane); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`examples/transparency.c`: + +```c +#include +#include + +int main(void) +{ + // Initialize window + InitWindow(800, 450, "[r3d] - Transparency example"); + SetTargetFPS(60); + + // Initialize R3D + R3D_Init(GetScreenWidth(), GetScreenHeight()); + + // Create cube model + R3D_Mesh cube = R3D_GenMeshCube(1, 1, 1); + R3D_Material matCube = R3D_MATERIAL_BASE; + matCube.transparencyMode = R3D_TRANSPARENCY_ALPHA; + matCube.albedo.color = (Color){150, 150, 255, 100}; + matCube.orm.occlusion = 1.0f; + matCube.orm.roughness = 0.2f; + matCube.orm.metalness = 0.2f; + + // Create plane model + R3D_Mesh plane = R3D_GenMeshPlane(1000, 1000, 1, 1); + R3D_Material matPlane = R3D_MATERIAL_BASE; + matPlane.orm.occlusion = 1.0f; + matPlane.orm.roughness = 1.0f; + matPlane.orm.metalness = 0.0f; + + // Create sphere model + R3D_Mesh sphere = R3D_GenMeshSphere(0.5f, 64, 64); + R3D_Material matSphere = R3D_MATERIAL_BASE; + matSphere.orm.occlusion = 1.0f; + matSphere.orm.roughness = 0.25f; + matSphere.orm.metalness = 0.75f; + + // Setup camera + Camera3D camera = { + .position = {0, 2, 2}, + .target = {0, 0, 0}, + .up = {0, 1, 0}, + .fovy = 60 + }; + + // Setup lighting + R3D_ENVIRONMENT_SET(ambient.color, (Color){10, 10, 10, 255}); + R3D_Light light = R3D_CreateLight(R3D_LIGHT_SPOT); + R3D_LightLookAt(light, (Vector3){0, 10, 5}, (Vector3){0}); + R3D_SetLightActive(light, true); + R3D_EnableShadow(light); + + // Main loop + while (!WindowShouldClose()) + { + UpdateCamera(&camera, CAMERA_ORBITAL); + + BeginDrawing(); + ClearBackground(RAYWHITE); + + R3D_Begin(camera); + R3D_DrawMesh(plane, matPlane, (Vector3){0, -0.5f, 0}, 1.0f); + R3D_DrawMesh(sphere, matSphere, Vector3Zero(), 1.0f); + R3D_DrawMesh(cube, matCube, Vector3Zero(), 1.0f); + R3D_End(); + + EndDrawing(); + } + + // Cleanup + R3D_UnloadMesh(sphere); + R3D_UnloadMesh(plane); + R3D_UnloadMesh(cube); + R3D_Close(); + + CloseWindow(); + + return 0; +} + +``` + +`external/glad/glad.h`: + +```h +/** + * Loader generated by glad 2.0.0-beta on Sun Sep 18 18:12:12 2022 + * + * Generator: C/C++ + * Specification: gl + * Extensions: 116 + * + * APIs: + * - gl:core=4.3 + * + * Options: + * - ALIAS = False + * - DEBUG = False + * - HEADER_ONLY = True + * - LOADER = False + * - MX = False + * - MX_GLOBAL = False + * - ON_DEMAND = False + * + * Commandline: + * --api='gl:core=4.3' --extensions='GL_ARB_ES2_compatibility,GL_ARB_ES3_1_compatibility,GL_ARB_ES3_2_compatibility,GL_ARB_ES3_compatibility,GL_ARB_blend_func_extended,GL_ARB_buffer_storage,GL_ARB_clear_buffer_object,GL_ARB_clear_texture,GL_ARB_color_buffer_float,GL_ARB_compatibility,GL_ARB_compressed_texture_pixel_storage,GL_ARB_compute_shader,GL_ARB_compute_variable_group_size,GL_ARB_copy_buffer,GL_ARB_copy_image,GL_ARB_debug_output,GL_ARB_depth_buffer_float,GL_ARB_depth_clamp,GL_ARB_depth_texture,GL_ARB_direct_state_access,GL_ARB_draw_buffers,GL_ARB_draw_buffers_blend,GL_ARB_draw_elements_base_vertex,GL_ARB_draw_indirect,GL_ARB_draw_instanced,GL_ARB_enhanced_layouts,GL_ARB_explicit_attrib_location,GL_ARB_explicit_uniform_location,GL_ARB_fragment_coord_conventions,GL_ARB_fragment_layer_viewport,GL_ARB_fragment_program,GL_ARB_fragment_program_shadow,GL_ARB_fragment_shader,GL_ARB_fragment_shader_interlock,GL_ARB_framebuffer_no_attachments,GL_ARB_framebuffer_object,GL_ARB_framebuffer_sRGB,GL_ARB_geometry_shader4,GL_ARB_get_program_binary,GL_ARB_get_texture_sub_image,GL_ARB_gl_spirv,GL_ARB_gpu_shader5,GL_ARB_gpu_shader_fp64,GL_ARB_gpu_shader_int64,GL_ARB_half_float_pixel,GL_ARB_half_float_vertex,GL_ARB_instanced_arrays,GL_ARB_internalformat_query,GL_ARB_internalformat_query2,GL_ARB_map_buffer_range,GL_ARB_multi_bind,GL_ARB_multi_draw_indirect,GL_ARB_multisample,GL_ARB_multitexture,GL_ARB_occlusion_query,GL_ARB_occlusion_query2,GL_ARB_pipeline_statistics_query,GL_ARB_query_buffer_object,GL_ARB_sample_locations,GL_ARB_sample_shading,GL_ARB_seamless_cube_map,GL_ARB_seamless_cubemap_per_texture,GL_ARB_shader_atomic_counter_ops,GL_ARB_shader_atomic_counters,GL_ARB_shader_bit_encoding,GL_ARB_shader_clock,GL_ARB_shader_image_load_store,GL_ARB_shader_image_size,GL_ARB_shader_objects,GL_ARB_shader_storage_buffer_object,GL_ARB_shader_texture_lod,GL_ARB_shading_language_100,GL_ARB_shading_language_420pack,GL_ARB_shading_language_include,GL_ARB_shading_language_packing,GL_ARB_spirv_extensions,GL_ARB_tessellation_shader,GL_ARB_texture_border_clamp,GL_ARB_texture_buffer_object_rgb32,GL_ARB_texture_compression,GL_ARB_texture_cube_map,GL_ARB_texture_cube_map_array,GL_ARB_texture_env_add,GL_ARB_texture_filter_anisotropic,GL_ARB_texture_filter_minmax,GL_ARB_texture_float,GL_ARB_texture_mirror_clamp_to_edge,GL_ARB_texture_mirrored_repeat,GL_ARB_texture_multisample,GL_ARB_texture_non_power_of_two,GL_ARB_texture_rg,GL_ARB_texture_storage,GL_ARB_texture_swizzle,GL_ARB_texture_view,GL_ARB_timer_query,GL_ARB_transpose_matrix,GL_ARB_uniform_buffer_object,GL_ARB_vertex_array_bgra,GL_ARB_vertex_array_object,GL_ARB_vertex_attrib_binding,GL_ARB_vertex_buffer_object,GL_ARB_vertex_program,GL_ARB_vertex_shader,GL_EXT_draw_instanced,GL_EXT_fog_coord,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_EXT_framebuffer_sRGB,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_mirror_clamp,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_OES_compressed_paletted_texture,GL_OES_fixed_point' c --header-only + * + * Online: + * http://glad.sh/#api=gl%3Acore%3D4.3&generator=c&options=HEADER_ONLY + * + */ + +#ifndef GLAD_GL_H_ +#define GLAD_GL_H_ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#ifdef __gl_h_ + #error OpenGL (gl.h) header already included (API: gl), remove previous include! +#endif +#define __gl_h_ 1 +#ifdef __gl3_h_ + #error OpenGL (gl3.h) header already included (API: gl), remove previous include! +#endif +#define __gl3_h_ 1 +#ifdef __glext_h_ + #error OpenGL (glext.h) header already included (API: gl), remove previous include! +#endif +#define __glext_h_ 1 +#ifdef __gl3ext_h_ + #error OpenGL (gl3ext.h) header already included (API: gl), remove previous include! +#endif +#define __gl3ext_h_ 1 +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#define GLAD_GL +#define GLAD_OPTION_GL_HEADER_ONLY + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef GLAD_PLATFORM_H_ +#define GLAD_PLATFORM_H_ + +#ifndef GLAD_PLATFORM_WIN32 + #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__) + #define GLAD_PLATFORM_WIN32 1 + #else + #define GLAD_PLATFORM_WIN32 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_APPLE + #ifdef __APPLE__ + #define GLAD_PLATFORM_APPLE 1 + #else + #define GLAD_PLATFORM_APPLE 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_EMSCRIPTEN + #ifdef __EMSCRIPTEN__ + #define GLAD_PLATFORM_EMSCRIPTEN 1 + #else + #define GLAD_PLATFORM_EMSCRIPTEN 0 + #endif +#endif + +#ifndef GLAD_PLATFORM_UWP + #if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY) + #ifdef __has_include + #if __has_include() + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ + #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 + #endif + #endif + + #ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY + #include + #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + #define GLAD_PLATFORM_UWP 1 + #endif + #endif + + #ifndef GLAD_PLATFORM_UWP + #define GLAD_PLATFORM_UWP 0 + #endif +#endif + +#ifdef __GNUC__ + #define GLAD_GNUC_EXTENSION __extension__ +#else + #define GLAD_GNUC_EXTENSION +#endif + +#ifndef GLAD_API_CALL + #if defined(GLAD_API_CALL_EXPORT) + #if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__) + #if defined(GLAD_API_CALL_EXPORT_BUILD) + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllexport)) extern + #else + #define GLAD_API_CALL __declspec(dllexport) extern + #endif + #else + #if defined(__GNUC__) + #define GLAD_API_CALL __attribute__ ((dllimport)) extern + #else + #define GLAD_API_CALL __declspec(dllimport) extern + #endif + #endif + #elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD) + #define GLAD_API_CALL __attribute__ ((visibility ("default"))) extern + #else + #define GLAD_API_CALL extern + #endif + #else + #define GLAD_API_CALL extern + #endif +#endif + +#ifdef APIENTRY + #define GLAD_API_PTR APIENTRY +#elif GLAD_PLATFORM_WIN32 + #define GLAD_API_PTR __stdcall +#else + #define GLAD_API_PTR +#endif + +#ifndef GLAPI +#define GLAPI GLAD_API_CALL +#endif + +#ifndef GLAPIENTRY +#define GLAPIENTRY GLAD_API_PTR +#endif + +#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) +#define GLAD_VERSION_MAJOR(version) (version / 10000) +#define GLAD_VERSION_MINOR(version) (version % 10000) + +#define GLAD_GENERATOR_VERSION "2.0.0-beta" + +typedef void (*GLADapiproc)(void); + +typedef GLADapiproc (*GLADloadfunc)(const char *name); +typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); + +typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); +typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); + +#endif /* GLAD_PLATFORM_H_ */ + +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_ALPHA 0x1906 +#define GL_ALPHA16F_ARB 0x881C +#define GL_ALPHA32F_ARB 0x8816 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_ALWAYS 0x0207 +#define GL_AND 0x1501 +#define GL_AND_INVERTED 0x1504 +#define GL_AND_REVERSE 0x1502 +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ARRAY_SIZE 0x92FB +#define GL_ARRAY_STRIDE 0x92FE +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_BACK 0x0405 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_BGRA_INTEGER 0x8D9B +#define GL_BGR_INTEGER 0x8D9A +#define GL_BLEND 0x0BE2 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_EQUATION 0x8009 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLOCK_INDEX 0x92FD +#define GL_BLUE 0x1905 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_BOOL 0x8B56 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_BUFFER 0x82E0 +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_BUFFER_USAGE 0x8765 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_BYTE 0x1400 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_CCW 0x0901 +#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_CLAMP_READ_COLOR_ARB 0x891C +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A +#define GL_CLEAR 0x1500 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6 +#define GL_CLIPPING_INPUT_PRIMITIVES_ARB 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7 +#define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7 +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_COLOR 0x1800 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_COLOR_ENCODING 0x8296 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_COLOR_SUM_ARB 0x8458 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_COMPILE_STATUS 0x8B81 +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_RG 0x8226 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5 +#define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5 +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_CONDITION_SATISFIED 0x911C +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_FLAGS 0x821E +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_COPY 0x1503 +#define GL_COPY_INVERTED 0x150C +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 +#define GL_CURRENT_MATRIX_ARB 0x8641 +#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_CURRENT_QUERY 0x8865 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 +#define GL_CW 0x0900 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DECR 0x1E03 +#define GL_DECR_WRAP 0x8508 +#define GL_DELETE_STATUS 0x8B80 +#define GL_DEPTH 0x1801 +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_DEPTH_CLAMP 0x864F +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT16_ARB 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT24_ARB 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH_COMPONENT32_ARB 0x81A7 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_DITHER 0x0BD0 +#define GL_DONT_CARE 0x1100 +#define GL_DOUBLE 0x140A +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DRAW_BUFFER 0x0C01 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER0_ARB 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER10_ARB 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER11_ARB 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER12_ARB 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER13_ARB 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER14_ARB 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_DRAW_BUFFER15_ARB 0x8834 +#define GL_DRAW_BUFFER1_ARB 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER2_ARB 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER3_ARB 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER4_ARB 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER5_ARB 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER6_ARB 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER7_ARB 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER8_ARB 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER9_ARB 0x882E +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_DST_ALPHA 0x0304 +#define GL_DST_COLOR 0x0306 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_DYNAMIC_COPY_ARB 0x88EA +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_EQUAL 0x0202 +#define GL_EQUIV 0x1509 +#define GL_EXTENSIONS 0x1F03 +#define GL_FALSE 0 +#define GL_FASTEST 0x1101 +#define GL_FILL 0x1B02 +#define GL_FILTER 0x829A +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_FIXED 0x140C +#define GL_FIXED_OES 0x140C +#define GL_FIXED_ONLY 0x891D +#define GL_FIXED_ONLY_ARB 0x891D +#define GL_FLOAT 0x1406 +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4 0x8B5C +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 +#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 +#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 +#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 +#define GL_FOG_COORDINATE_EXT 0x8451 +#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B +#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4 +#define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4 +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_FRAMEBUFFER 0x8D40 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB 0x9342 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB 0x9343 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_FRONT 0x0404 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_FRONT_FACE 0x0B46 +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_SHADER_ARB 0x8DD9 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3 +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA +#define GL_GEQUAL 0x0206 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_GREATER 0x0204 +#define GL_GREEN 0x1904 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_HALF_FLOAT 0x140B +#define GL_HALF_FLOAT_ARB 0x140B +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_HIGH_INT 0x8DF5 +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_INCR 0x1E02 +#define GL_INCR_WRAP 0x8507 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_INT 0x1404 +#define GL_INT64_ARB 0x140E +#define GL_INT64_VEC2_ARB 0x8FE9 +#define GL_INT64_VEC3_ARB 0x8FEA +#define GL_INT64_VEC4_ARB 0x8FEB +#define GL_INTENSITY16F_ARB 0x881D +#define GL_INTENSITY32F_ARB 0x8817 +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INT_2_10_10_10_REV 0x8D9F +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#define GL_INVALID_INDEX 0xFFFFFFFF +#define GL_INVALID_OPERATION 0x0502 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVERT 0x150A +#define GL_ISOLINES 0x8E7A +#define GL_IS_PER_PATCH 0x92E7 +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_KEEP 0x1E00 +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_LEFT 0x0406 +#define GL_LEQUAL 0x0203 +#define GL_LESS 0x0201 +#define GL_LINE 0x1B01 +#define GL_LINEAR 0x2601 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINES 0x0001 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINES_ADJACENCY_ARB 0x000A +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_LINE_STRIP 0x0003 +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_LINE_WIDTH_RANGE 0x0B22 +#define GL_LINK_STATUS 0x8B82 +#define GL_LOCATION 0x930E +#define GL_LOCATION_COMPONENT 0x934A +#define GL_LOCATION_INDEX 0x930F +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_LOW_FLOAT 0x8DF0 +#define GL_LOW_INT 0x8DF3 +#define GL_LUMINANCE16F_ARB 0x881E +#define GL_LUMINANCE32F_ARB 0x8818 +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 +#define GL_MAJOR_VERSION 0x821B +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MATRIX0_ARB 0x88C0 +#define GL_MATRIX10_ARB 0x88CA +#define GL_MATRIX11_ARB 0x88CB +#define GL_MATRIX12_ARB 0x88CC +#define GL_MATRIX13_ARB 0x88CD +#define GL_MATRIX14_ARB 0x88CE +#define GL_MATRIX15_ARB 0x88CF +#define GL_MATRIX16_ARB 0x88D0 +#define GL_MATRIX17_ARB 0x88D1 +#define GL_MATRIX18_ARB 0x88D2 +#define GL_MATRIX19_ARB 0x88D3 +#define GL_MATRIX1_ARB 0x88C1 +#define GL_MATRIX20_ARB 0x88D4 +#define GL_MATRIX21_ARB 0x88D5 +#define GL_MATRIX22_ARB 0x88D6 +#define GL_MATRIX23_ARB 0x88D7 +#define GL_MATRIX24_ARB 0x88D8 +#define GL_MATRIX25_ARB 0x88D9 +#define GL_MATRIX26_ARB 0x88DA +#define GL_MATRIX27_ARB 0x88DB +#define GL_MATRIX28_ARB 0x88DC +#define GL_MATRIX29_ARB 0x88DD +#define GL_MATRIX2_ARB 0x88C2 +#define GL_MATRIX30_ARB 0x88DE +#define GL_MATRIX31_ARB 0x88DF +#define GL_MATRIX3_ARB 0x88C3 +#define GL_MATRIX4_ARB 0x88C4 +#define GL_MATRIX5_ARB 0x88C5 +#define GL_MATRIX6_ARB 0x88C6 +#define GL_MATRIX7_ARB 0x88C7 +#define GL_MATRIX8_ARB 0x88C8 +#define GL_MATRIX9_ARB 0x88C9 +#define GL_MATRIX_STRIDE 0x92FF +#define GL_MAX 0x8008 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB +#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 +#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_INTEGER_SAMPLES 0x9110 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 +#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B +#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD +#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 +#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 +#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 +#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F +#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E +#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 +#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E +#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF +#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 +#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB +#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 +#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 +#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F +#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 +#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D +#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_MAX_SAMPLES_EXT 0x8D57 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE +#define GL_MAX_VIEWPORTS 0x825B +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MAX_WIDTH 0x827E +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_MIN 0x8007 +#define GL_MINOR_VERSION 0x821C +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 +#define GL_MIPMAP 0x8293 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MULTISAMPLE 0x809D +#define GL_MULTISAMPLE_ARB 0x809D +#define GL_MULTISAMPLE_BIT_ARB 0x20000000 +#define GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY_ARB 0x9382 +#define GL_MULTISAMPLE_LINE_WIDTH_RANGE_ARB 0x9381 +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA +#define GL_NAME_LENGTH 0x92F9 +#define GL_NAND 0x150E +#define GL_NEAREST 0x2600 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEVER 0x0200 +#define GL_NICEST 0x1102 +#define GL_NONE 0 +#define GL_NOOP 0x1505 +#define GL_NOR 0x1508 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_NORMAL_MAP_ARB 0x8511 +#define GL_NOTEQUAL 0x0205 +#define GL_NO_ERROR 0 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_NUM_EXTENSIONS 0x821D +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_NUM_SPIR_V_EXTENSIONS 0x9554 +#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 +#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_OBJECT_TYPE 0x9112 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OFFSET 0x92FC +#define GL_ONE 1 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_OR 0x1507 +#define GL_OR_INVERTED 0x150D +#define GL_OR_REVERSE 0x150B +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PATCHES 0x000E +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_POINT 0x1B00 +#define GL_POINTS 0x0000 +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_POINT_SIZE_RANGE 0x0B12 +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_PRIMITIVES_SUBMITTED 0x82EF +#define GL_PRIMITIVES_SUBMITTED_ARB 0x82EF +#define GL_PRIMITIVE_BOUNDING_BOX_ARB 0x92BE +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_PROGRAM 0x82E2 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_ARB 0x9341 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_ARB 0x9340 +#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 +#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 +#define GL_PROGRAM_ATTRIBS_ARB 0x88AC +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINDING_ARB 0x8677 +#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B +#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#define GL_PROGRAM_FORMAT_ARB 0x8876 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 +#define GL_PROGRAM_LENGTH_ARB 0x8627 +#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 +#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 +#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE +#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 +#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA +#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 +#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A +#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_PROGRAM_STRING_ARB 0x8628 +#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 +#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 +#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 +#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_PROXY_TEXTURE_2D 0x8064 +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_QUADS 0x0007 +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_QUERY 0x82E3 +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_QUERY_TARGET 0x82EA +#define GL_QUERY_WAIT 0x8E13 +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_R16 0x822A +#define GL_R16F 0x822D +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R16_SNORM 0x8F98 +#define GL_R32F 0x822E +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_R3_G3_B2 0x2A10 +#define GL_R8 0x8229 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R8_SNORM 0x8F94 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_READ_BUFFER 0x0C02 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_READ_ONLY 0x88B8 +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_READ_WRITE 0x88BA +#define GL_READ_WRITE_ARB 0x88BA +#define GL_RED 0x1903 +#define GL_RED_INTEGER 0x8D94 +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFLECTION_MAP_ARB 0x8512 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERER 0x1F01 +#define GL_REPEAT 0x2901 +#define GL_REPLACE 0x1E01 +#define GL_RG 0x8227 +#define GL_RG16 0x822C +#define GL_RG16F 0x822F +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG16_SNORM 0x8F99 +#define GL_RG32F 0x8230 +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_RG8 0x822B +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB 0x1907 +#define GL_RGB10 0x8052 +#define GL_RGB10_A2 0x8059 +#define GL_RGB10_A2UI 0x906F +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGB16F 0x881B +#define GL_RGB16F_ARB 0x881B +#define GL_RGB16I 0x8D89 +#define GL_RGB16UI 0x8D77 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGB32F 0x8815 +#define GL_RGB32F_ARB 0x8815 +#define GL_RGB32I 0x8D83 +#define GL_RGB32UI 0x8D71 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB565 0x8D62 +#define GL_RGB5_A1 0x8057 +#define GL_RGB8 0x8051 +#define GL_RGB8I 0x8D8F +#define GL_RGB8UI 0x8D7D +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGB9_E5 0x8C3D +#define GL_RGBA 0x1908 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#define GL_RGBA16F 0x881A +#define GL_RGBA16F_ARB 0x881A +#define GL_RGBA16I 0x8D88 +#define GL_RGBA16UI 0x8D76 +#define GL_RGBA16_SNORM 0x8F9B +#define GL_RGBA2 0x8055 +#define GL_RGBA32F 0x8814 +#define GL_RGBA32F_ARB 0x8814 +#define GL_RGBA32I 0x8D82 +#define GL_RGBA32UI 0x8D70 +#define GL_RGBA4 0x8056 +#define GL_RGBA8 0x8058 +#define GL_RGBA8I 0x8D8E +#define GL_RGBA8UI 0x8D7C +#define GL_RGBA8_SNORM 0x8F97 +#define GL_RGBA_FLOAT_MODE_ARB 0x8820 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RG_INTEGER 0x8228 +#define GL_RIGHT 0x0407 +#define GL_SAMPLER 0x82E6 +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLES_ARB 0x80A9 +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SAMPLES_PASSED_ARB 0x8914 +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLE_BUFFERS_ARB 0x80A8 +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_COVERAGE_ARB 0x80A0 +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA +#define GL_SAMPLE_LOCATION_ARB 0x8E50 +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_ARB 0x933F +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_ARB 0x933E +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_ARB 0x933D +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_SET 0x150F +#define GL_SHADER 0x82E1 +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551 +#define GL_SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_TYPE 0x8B4F +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C +#define GL_SHORT 0x1402 +#define GL_SIGNALED 0x9119 +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SPIR_V_BINARY 0x9552 +#define GL_SPIR_V_BINARY_ARB 0x9552 +#define GL_SPIR_V_EXTENSIONS 0x9553 +#define GL_SRC1_ALPHA 0x8589 +#define GL_SRC1_COLOR 0x88F9 +#define GL_SRC_ALPHA 0x0302 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_SRC_COLOR 0x0300 +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB_DECODE_ARB 0x8299 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_STATIC_COPY 0x88E6 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STENCIL 0x1802 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_INDEX 0x1901 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STEREO 0x0C33 +#define GL_STREAM_COPY 0x88E2 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_SYNC_STATUS 0x9114 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1 +#define GL_TESS_CONTROL_SHADER_PATCHES_ARB 0x82F1 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB 0x82F2 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TEXTURE 0x1702 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_TEXTURE31_ARB 0x84DF +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_TEXTURE_3D 0x806F +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_REDUCTION_MODE_ARB 0x9366 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TEXTURE_TARGET 0x1006 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF +#define GL_TIMESTAMP 0x8E28 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 +#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 +#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLES_ADJACENCY_ARB 0x000C +#define GL_TRIANGLE_FAN 0x0006 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D +#define GL_TRUE 1 +#define GL_TYPE 0x92FA +#define GL_UNDEFINED_VERTEX 0x8260 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_UNSIGNALED 0x9118 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_INT64_ARB 0x140F +#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FF7 +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_VENDOR 0x1F00 +#define GL_VERSION 0x1F02 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_BUFFER 0x8F4F +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_VERTEX_PROGRAM_ARB 0x8620 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_VERTEX_SHADER_ARB 0x8B31 +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0 +#define GL_VERTEX_SHADER_INVOCATIONS_ARB 0x82F0 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_VERTEX_TEXTURE 0x829B +#define GL_VERTICES_SUBMITTED 0x82EE +#define GL_VERTICES_SUBMITTED_ARB 0x82EE +#define GL_VIEWPORT 0x0BA2 +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_ASTC_10x10_RGBA 0x9393 +#define GL_VIEW_CLASS_ASTC_10x5_RGBA 0x9390 +#define GL_VIEW_CLASS_ASTC_10x6_RGBA 0x9391 +#define GL_VIEW_CLASS_ASTC_10x8_RGBA 0x9392 +#define GL_VIEW_CLASS_ASTC_12x10_RGBA 0x9394 +#define GL_VIEW_CLASS_ASTC_12x12_RGBA 0x9395 +#define GL_VIEW_CLASS_ASTC_4x4_RGBA 0x9388 +#define GL_VIEW_CLASS_ASTC_5x4_RGBA 0x9389 +#define GL_VIEW_CLASS_ASTC_5x5_RGBA 0x938A +#define GL_VIEW_CLASS_ASTC_6x5_RGBA 0x938B +#define GL_VIEW_CLASS_ASTC_6x6_RGBA 0x938C +#define GL_VIEW_CLASS_ASTC_8x5_RGBA 0x938D +#define GL_VIEW_CLASS_ASTC_8x6_RGBA 0x938E +#define GL_VIEW_CLASS_ASTC_8x8_RGBA 0x938F +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_EAC_R11 0x9383 +#define GL_VIEW_CLASS_EAC_RG11 0x9384 +#define GL_VIEW_CLASS_ETC2_EAC_RGBA 0x9387 +#define GL_VIEW_CLASS_ETC2_RGB 0x9385 +#define GL_VIEW_CLASS_ETC2_RGBA 0x9386 +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_WAIT_FAILED 0x911D +#define GL_WEIGHTED_AVERAGE_ARB 0x9367 +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_WRITE_ONLY 0x88B9 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_XOR 0x1506 +#define GL_ZERO 0 + + +#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 + * 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_GLAD_API_PTR + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_GLAD_API_PTR 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_GLAD_API_PTR + *------------------------------------------------------------------------- + * 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_GLAD_API_PTR __stdcall +#else +# define KHRONOS_GLAD_API_PTR +#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 + */ +#include +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 + */ +#include +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 +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_ */ +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef void GLvoid; +typedef khronos_int8_t GLbyte; +typedef khronos_uint8_t GLubyte; +typedef khronos_int16_t GLshort; +typedef khronos_uint16_t GLushort; +typedef int GLint; +typedef unsigned int GLuint; +typedef khronos_int32_t GLclampx; +typedef int GLsizei; +typedef khronos_float_t GLfloat; +typedef khronos_float_t GLclampf; +typedef double GLdouble; +typedef double GLclampd; +typedef void *GLeglClientBufferEXT; +typedef void *GLeglImageOES; +typedef char GLchar; +typedef char GLcharARB; +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif +typedef khronos_uint16_t GLhalf; +typedef khronos_uint16_t GLhalfARB; +typedef khronos_int32_t GLfixed; +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptr; +#else +typedef khronos_intptr_t GLintptr; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_intptr_t GLintptrARB; +#else +typedef khronos_intptr_t GLintptrARB; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptr; +#else +typedef khronos_ssize_t GLsizeiptr; +#endif +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) +typedef khronos_ssize_t GLsizeiptrARB; +#else +typedef khronos_ssize_t GLsizeiptrARB; +#endif +typedef khronos_int64_t GLint64; +typedef khronos_int64_t GLint64EXT; +typedef khronos_uint64_t GLuint64; +typedef khronos_uint64_t GLuint64EXT; +typedef struct __GLsync *GLsync; +struct _cl_context; +struct _cl_event; +typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); +typedef unsigned short GLhalfNV; +typedef GLintptr GLvdpauSurfaceNV; +typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void); + + +#define GL_VERSION_1_0 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_0; +#define GL_VERSION_1_1 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_1; +#define GL_VERSION_1_2 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_2; +#define GL_VERSION_1_3 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_3; +#define GL_VERSION_1_4 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_4; +#define GL_VERSION_1_5 1 +GLAD_API_CALL int GLAD_GL_VERSION_1_5; +#define GL_VERSION_2_0 1 +GLAD_API_CALL int GLAD_GL_VERSION_2_0; +#define GL_VERSION_2_1 1 +GLAD_API_CALL int GLAD_GL_VERSION_2_1; +#define GL_VERSION_3_0 1 +GLAD_API_CALL int GLAD_GL_VERSION_3_0; +#define GL_VERSION_3_1 1 +GLAD_API_CALL int GLAD_GL_VERSION_3_1; +#define GL_VERSION_3_2 1 +GLAD_API_CALL int GLAD_GL_VERSION_3_2; +#define GL_VERSION_3_3 1 +GLAD_API_CALL int GLAD_GL_VERSION_3_3; +#define GL_VERSION_4_0 1 +GLAD_API_CALL int GLAD_GL_VERSION_4_0; +#define GL_VERSION_4_1 1 +GLAD_API_CALL int GLAD_GL_VERSION_4_1; +#define GL_VERSION_4_2 1 +GLAD_API_CALL int GLAD_GL_VERSION_4_2; +#define GL_VERSION_4_3 1 +GLAD_API_CALL int GLAD_GL_VERSION_4_3; +#define GL_ARB_ES2_compatibility 1 +GLAD_API_CALL int GLAD_GL_ARB_ES2_compatibility; +#define GL_ARB_ES3_1_compatibility 1 +GLAD_API_CALL int GLAD_GL_ARB_ES3_1_compatibility; +#define GL_ARB_ES3_2_compatibility 1 +GLAD_API_CALL int GLAD_GL_ARB_ES3_2_compatibility; +#define GL_ARB_ES3_compatibility 1 +GLAD_API_CALL int GLAD_GL_ARB_ES3_compatibility; +#define GL_ARB_blend_func_extended 1 +GLAD_API_CALL int GLAD_GL_ARB_blend_func_extended; +#define GL_ARB_buffer_storage 1 +GLAD_API_CALL int GLAD_GL_ARB_buffer_storage; +#define GL_ARB_clear_buffer_object 1 +GLAD_API_CALL int GLAD_GL_ARB_clear_buffer_object; +#define GL_ARB_clear_texture 1 +GLAD_API_CALL int GLAD_GL_ARB_clear_texture; +#define GL_ARB_color_buffer_float 1 +GLAD_API_CALL int GLAD_GL_ARB_color_buffer_float; +#define GL_ARB_compatibility 1 +GLAD_API_CALL int GLAD_GL_ARB_compatibility; +#define GL_ARB_compressed_texture_pixel_storage 1 +GLAD_API_CALL int GLAD_GL_ARB_compressed_texture_pixel_storage; +#define GL_ARB_compute_shader 1 +GLAD_API_CALL int GLAD_GL_ARB_compute_shader; +#define GL_ARB_compute_variable_group_size 1 +GLAD_API_CALL int GLAD_GL_ARB_compute_variable_group_size; +#define GL_ARB_copy_buffer 1 +GLAD_API_CALL int GLAD_GL_ARB_copy_buffer; +#define GL_ARB_copy_image 1 +GLAD_API_CALL int GLAD_GL_ARB_copy_image; +#define GL_ARB_debug_output 1 +GLAD_API_CALL int GLAD_GL_ARB_debug_output; +#define GL_ARB_depth_buffer_float 1 +GLAD_API_CALL int GLAD_GL_ARB_depth_buffer_float; +#define GL_ARB_depth_clamp 1 +GLAD_API_CALL int GLAD_GL_ARB_depth_clamp; +#define GL_ARB_depth_texture 1 +GLAD_API_CALL int GLAD_GL_ARB_depth_texture; +#define GL_ARB_direct_state_access 1 +GLAD_API_CALL int GLAD_GL_ARB_direct_state_access; +#define GL_ARB_draw_buffers 1 +GLAD_API_CALL int GLAD_GL_ARB_draw_buffers; +#define GL_ARB_draw_buffers_blend 1 +GLAD_API_CALL int GLAD_GL_ARB_draw_buffers_blend; +#define GL_ARB_draw_elements_base_vertex 1 +GLAD_API_CALL int GLAD_GL_ARB_draw_elements_base_vertex; +#define GL_ARB_draw_indirect 1 +GLAD_API_CALL int GLAD_GL_ARB_draw_indirect; +#define GL_ARB_draw_instanced 1 +GLAD_API_CALL int GLAD_GL_ARB_draw_instanced; +#define GL_ARB_enhanced_layouts 1 +GLAD_API_CALL int GLAD_GL_ARB_enhanced_layouts; +#define GL_ARB_explicit_attrib_location 1 +GLAD_API_CALL int GLAD_GL_ARB_explicit_attrib_location; +#define GL_ARB_explicit_uniform_location 1 +GLAD_API_CALL int GLAD_GL_ARB_explicit_uniform_location; +#define GL_ARB_fragment_coord_conventions 1 +GLAD_API_CALL int GLAD_GL_ARB_fragment_coord_conventions; +#define GL_ARB_fragment_layer_viewport 1 +GLAD_API_CALL int GLAD_GL_ARB_fragment_layer_viewport; +#define GL_ARB_fragment_program 1 +GLAD_API_CALL int GLAD_GL_ARB_fragment_program; +#define GL_ARB_fragment_program_shadow 1 +GLAD_API_CALL int GLAD_GL_ARB_fragment_program_shadow; +#define GL_ARB_fragment_shader 1 +GLAD_API_CALL int GLAD_GL_ARB_fragment_shader; +#define GL_ARB_fragment_shader_interlock 1 +GLAD_API_CALL int GLAD_GL_ARB_fragment_shader_interlock; +#define GL_ARB_framebuffer_no_attachments 1 +GLAD_API_CALL int GLAD_GL_ARB_framebuffer_no_attachments; +#define GL_ARB_framebuffer_object 1 +GLAD_API_CALL int GLAD_GL_ARB_framebuffer_object; +#define GL_ARB_framebuffer_sRGB 1 +GLAD_API_CALL int GLAD_GL_ARB_framebuffer_sRGB; +#define GL_ARB_geometry_shader4 1 +GLAD_API_CALL int GLAD_GL_ARB_geometry_shader4; +#define GL_ARB_get_program_binary 1 +GLAD_API_CALL int GLAD_GL_ARB_get_program_binary; +#define GL_ARB_get_texture_sub_image 1 +GLAD_API_CALL int GLAD_GL_ARB_get_texture_sub_image; +#define GL_ARB_gl_spirv 1 +GLAD_API_CALL int GLAD_GL_ARB_gl_spirv; +#define GL_ARB_gpu_shader5 1 +GLAD_API_CALL int GLAD_GL_ARB_gpu_shader5; +#define GL_ARB_gpu_shader_fp64 1 +GLAD_API_CALL int GLAD_GL_ARB_gpu_shader_fp64; +#define GL_ARB_gpu_shader_int64 1 +GLAD_API_CALL int GLAD_GL_ARB_gpu_shader_int64; +#define GL_ARB_half_float_pixel 1 +GLAD_API_CALL int GLAD_GL_ARB_half_float_pixel; +#define GL_ARB_half_float_vertex 1 +GLAD_API_CALL int GLAD_GL_ARB_half_float_vertex; +#define GL_ARB_instanced_arrays 1 +GLAD_API_CALL int GLAD_GL_ARB_instanced_arrays; +#define GL_ARB_internalformat_query 1 +GLAD_API_CALL int GLAD_GL_ARB_internalformat_query; +#define GL_ARB_internalformat_query2 1 +GLAD_API_CALL int GLAD_GL_ARB_internalformat_query2; +#define GL_ARB_map_buffer_range 1 +GLAD_API_CALL int GLAD_GL_ARB_map_buffer_range; +#define GL_ARB_multi_bind 1 +GLAD_API_CALL int GLAD_GL_ARB_multi_bind; +#define GL_ARB_multi_draw_indirect 1 +GLAD_API_CALL int GLAD_GL_ARB_multi_draw_indirect; +#define GL_ARB_multisample 1 +GLAD_API_CALL int GLAD_GL_ARB_multisample; +#define GL_ARB_multitexture 1 +GLAD_API_CALL int GLAD_GL_ARB_multitexture; +#define GL_ARB_occlusion_query 1 +GLAD_API_CALL int GLAD_GL_ARB_occlusion_query; +#define GL_ARB_occlusion_query2 1 +GLAD_API_CALL int GLAD_GL_ARB_occlusion_query2; +#define GL_ARB_pipeline_statistics_query 1 +GLAD_API_CALL int GLAD_GL_ARB_pipeline_statistics_query; +#define GL_ARB_query_buffer_object 1 +GLAD_API_CALL int GLAD_GL_ARB_query_buffer_object; +#define GL_ARB_sample_locations 1 +GLAD_API_CALL int GLAD_GL_ARB_sample_locations; +#define GL_ARB_sample_shading 1 +GLAD_API_CALL int GLAD_GL_ARB_sample_shading; +#define GL_ARB_seamless_cube_map 1 +GLAD_API_CALL int GLAD_GL_ARB_seamless_cube_map; +#define GL_ARB_seamless_cubemap_per_texture 1 +GLAD_API_CALL int GLAD_GL_ARB_seamless_cubemap_per_texture; +#define GL_ARB_shader_atomic_counter_ops 1 +GLAD_API_CALL int GLAD_GL_ARB_shader_atomic_counter_ops; +#define GL_ARB_shader_atomic_counters 1 +GLAD_API_CALL int GLAD_GL_ARB_shader_atomic_counters; +#define GL_ARB_shader_bit_encoding 1 +GLAD_API_CALL int GLAD_GL_ARB_shader_bit_encoding; +#define GL_ARB_shader_clock 1 +GLAD_API_CALL int GLAD_GL_ARB_shader_clock; +#define GL_ARB_shader_image_load_store 1 +GLAD_API_CALL int GLAD_GL_ARB_shader_image_load_store; +#define GL_ARB_shader_image_size 1 +GLAD_API_CALL int GLAD_GL_ARB_shader_image_size; +#define GL_ARB_shader_objects 1 +GLAD_API_CALL int GLAD_GL_ARB_shader_objects; +#define GL_ARB_shader_storage_buffer_object 1 +GLAD_API_CALL int GLAD_GL_ARB_shader_storage_buffer_object; +#define GL_ARB_shader_texture_lod 1 +GLAD_API_CALL int GLAD_GL_ARB_shader_texture_lod; +#define GL_ARB_shading_language_100 1 +GLAD_API_CALL int GLAD_GL_ARB_shading_language_100; +#define GL_ARB_shading_language_420pack 1 +GLAD_API_CALL int GLAD_GL_ARB_shading_language_420pack; +#define GL_ARB_shading_language_include 1 +GLAD_API_CALL int GLAD_GL_ARB_shading_language_include; +#define GL_ARB_shading_language_packing 1 +GLAD_API_CALL int GLAD_GL_ARB_shading_language_packing; +#define GL_ARB_spirv_extensions 1 +GLAD_API_CALL int GLAD_GL_ARB_spirv_extensions; +#define GL_ARB_tessellation_shader 1 +GLAD_API_CALL int GLAD_GL_ARB_tessellation_shader; +#define GL_ARB_texture_border_clamp 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_border_clamp; +#define GL_ARB_texture_buffer_object_rgb32 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_buffer_object_rgb32; +#define GL_ARB_texture_compression 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_compression; +#define GL_ARB_texture_cube_map 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_cube_map; +#define GL_ARB_texture_cube_map_array 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_cube_map_array; +#define GL_ARB_texture_env_add 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_env_add; +#define GL_ARB_texture_filter_anisotropic 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_filter_anisotropic; +#define GL_ARB_texture_filter_minmax 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_filter_minmax; +#define GL_ARB_texture_float 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_float; +#define GL_ARB_texture_mirror_clamp_to_edge 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_mirror_clamp_to_edge; +#define GL_ARB_texture_mirrored_repeat 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_mirrored_repeat; +#define GL_ARB_texture_multisample 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_multisample; +#define GL_ARB_texture_non_power_of_two 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_non_power_of_two; +#define GL_ARB_texture_rg 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_rg; +#define GL_ARB_texture_storage 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_storage; +#define GL_ARB_texture_swizzle 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_swizzle; +#define GL_ARB_texture_view 1 +GLAD_API_CALL int GLAD_GL_ARB_texture_view; +#define GL_ARB_timer_query 1 +GLAD_API_CALL int GLAD_GL_ARB_timer_query; +#define GL_ARB_transpose_matrix 1 +GLAD_API_CALL int GLAD_GL_ARB_transpose_matrix; +#define GL_ARB_uniform_buffer_object 1 +GLAD_API_CALL int GLAD_GL_ARB_uniform_buffer_object; +#define GL_ARB_vertex_array_bgra 1 +GLAD_API_CALL int GLAD_GL_ARB_vertex_array_bgra; +#define GL_ARB_vertex_array_object 1 +GLAD_API_CALL int GLAD_GL_ARB_vertex_array_object; +#define GL_ARB_vertex_attrib_binding 1 +GLAD_API_CALL int GLAD_GL_ARB_vertex_attrib_binding; +#define GL_ARB_vertex_buffer_object 1 +GLAD_API_CALL int GLAD_GL_ARB_vertex_buffer_object; +#define GL_ARB_vertex_program 1 +GLAD_API_CALL int GLAD_GL_ARB_vertex_program; +#define GL_ARB_vertex_shader 1 +GLAD_API_CALL int GLAD_GL_ARB_vertex_shader; +#define GL_EXT_draw_instanced 1 +GLAD_API_CALL int GLAD_GL_EXT_draw_instanced; +#define GL_EXT_fog_coord 1 +GLAD_API_CALL int GLAD_GL_EXT_fog_coord; +#define GL_EXT_framebuffer_blit 1 +GLAD_API_CALL int GLAD_GL_EXT_framebuffer_blit; +#define GL_EXT_framebuffer_multisample 1 +GLAD_API_CALL int GLAD_GL_EXT_framebuffer_multisample; +#define GL_EXT_framebuffer_object 1 +GLAD_API_CALL int GLAD_GL_EXT_framebuffer_object; +#define GL_EXT_framebuffer_sRGB 1 +GLAD_API_CALL int GLAD_GL_EXT_framebuffer_sRGB; +#define GL_EXT_texture_compression_s3tc 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_compression_s3tc; +#define GL_EXT_texture_filter_anisotropic 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_filter_anisotropic; +#define GL_EXT_texture_mirror_clamp 1 +GLAD_API_CALL int GLAD_GL_EXT_texture_mirror_clamp; +#define GL_KHR_texture_compression_astc_hdr 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_hdr; +#define GL_KHR_texture_compression_astc_ldr 1 +GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_ldr; +#define GL_OES_compressed_paletted_texture 1 +GLAD_API_CALL int GLAD_GL_OES_compressed_paletted_texture; +#define GL_OES_fixed_point 1 +GLAD_API_CALL int GLAD_GL_OES_fixed_point; + + +typedef void (GLAD_API_PTR *PFNGLACCUMXOESPROC)(GLenum op, GLfixed value); +typedef void (GLAD_API_PTR *PFNGLACTIVESHADERPROGRAMPROC)(GLuint pipeline, GLuint program); +typedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREPROC)(GLenum texture); +typedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREARBPROC)(GLenum texture); +typedef void (GLAD_API_PTR *PFNGLALPHAFUNCXOESPROC)(GLenum func, GLfixed ref); +typedef void (GLAD_API_PTR *PFNGLATTACHOBJECTARBPROC)(GLhandleARB containerObj, GLhandleARB obj); +typedef void (GLAD_API_PTR *PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBEGINQUERYPROC)(GLenum target, GLuint id); +typedef void (GLAD_API_PTR *PFNGLBEGINQUERYARBPROC)(GLenum target, GLuint id); +typedef void (GLAD_API_PTR *PFNGLBEGINQUERYINDEXEDPROC)(GLenum target, GLuint index, GLuint id); +typedef void (GLAD_API_PTR *PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); +typedef void (GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONARBPROC)(GLhandleARB programObj, GLuint index, const GLcharARB * name); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERARBPROC)(GLenum target, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERSBASEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLBINDBUFFERSRANGEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint * buffers, const GLintptr * offsets, const GLsizeiptr * sizes); +typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); +typedef void (GLAD_API_PTR *PFNGLBINDFRAMEBUFFEREXTPROC)(GLenum target, GLuint framebuffer); +typedef void (GLAD_API_PTR *PFNGLBINDIMAGETEXTUREPROC)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +typedef void (GLAD_API_PTR *PFNGLBINDIMAGETEXTURESPROC)(GLuint first, GLsizei count, const GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLBINDPROGRAMARBPROC)(GLenum target, GLuint program); +typedef void (GLAD_API_PTR *PFNGLBINDPROGRAMPIPELINEPROC)(GLuint pipeline); +typedef void (GLAD_API_PTR *PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLBINDRENDERBUFFEREXTPROC)(GLenum target, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler); +typedef void (GLAD_API_PTR *PFNGLBINDSAMPLERSPROC)(GLuint first, GLsizei count, const GLuint * samplers); +typedef void (GLAD_API_PTR *PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); +typedef void (GLAD_API_PTR *PFNGLBINDTEXTUREUNITPROC)(GLuint unit, GLuint texture); +typedef void (GLAD_API_PTR *PFNGLBINDTEXTURESPROC)(GLuint first, GLsizei count, const GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLBINDTRANSFORMFEEDBACKPROC)(GLenum target, GLuint id); +typedef void (GLAD_API_PTR *PFNGLBINDVERTEXARRAYPROC)(GLuint array); +typedef void (GLAD_API_PTR *PFNGLBINDVERTEXBUFFERPROC)(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (GLAD_API_PTR *PFNGLBINDVERTEXBUFFERSPROC)(GLuint first, GLsizei count, const GLuint * buffers, const GLintptr * offsets, const GLsizei * strides); +typedef void (GLAD_API_PTR *PFNGLBITMAPXOESPROC)(GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte * bitmap); +typedef void (GLAD_API_PTR *PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLBLENDCOLORXOESPROC)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEIPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEIARBPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONIPROC)(GLuint buf, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONIARBPROC)(GLuint buf, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEIPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEIARBPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCIPROC)(GLuint buf, GLenum src, GLenum dst); +typedef void (GLAD_API_PTR *PFNGLBLENDFUNCIARBPROC)(GLuint buf, GLenum src, GLenum dst); +typedef void (GLAD_API_PTR *PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (GLAD_API_PTR *PFNGLBLITFRAMEBUFFEREXTPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (GLAD_API_PTR *PFNGLBLITNAMEDFRAMEBUFFERPROC)(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (GLAD_API_PTR *PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); +typedef void (GLAD_API_PTR *PFNGLBUFFERDATAARBPROC)(GLenum target, GLsizeiptrARB size, const void * data, GLenum usage); +typedef void (GLAD_API_PTR *PFNGLBUFFERSTORAGEPROC)(GLenum target, GLsizeiptr size, const void * data, GLbitfield flags); +typedef void (GLAD_API_PTR *PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); +typedef void (GLAD_API_PTR *PFNGLBUFFERSUBDATAARBPROC)(GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void * data); +typedef GLenum (GLAD_API_PTR *PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); +typedef GLenum (GLAD_API_PTR *PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)(GLenum target); +typedef GLenum (GLAD_API_PTR *PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)(GLuint framebuffer, GLenum target); +typedef void (GLAD_API_PTR *PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); +typedef void (GLAD_API_PTR *PFNGLCLAMPCOLORARBPROC)(GLenum target, GLenum clamp); +typedef void (GLAD_API_PTR *PFNGLCLEARPROC)(GLbitfield mask); +typedef void (GLAD_API_PTR *PFNGLCLEARACCUMXOESPROC)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERDATAPROC)(GLenum target, GLenum internalformat, GLenum format, GLenum type, const void * data); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERSUBDATAPROC)(GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void * data); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (GLAD_API_PTR *PFNGLCLEARCOLORXOESPROC)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (GLAD_API_PTR *PFNGLCLEARDEPTHPROC)(GLdouble depth); +typedef void (GLAD_API_PTR *PFNGLCLEARDEPTHFPROC)(GLfloat d); +typedef void (GLAD_API_PTR *PFNGLCLEARDEPTHXOESPROC)(GLfixed depth); +typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDBUFFERDATAPROC)(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void * data); +typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void * data); +typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLCLEARSTENCILPROC)(GLint s); +typedef void (GLAD_API_PTR *PFNGLCLEARTEXIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, const void * data); +typedef void (GLAD_API_PTR *PFNGLCLEARTEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * data); +typedef void (GLAD_API_PTR *PFNGLCLIENTACTIVETEXTUREARBPROC)(GLenum texture); +typedef GLenum (GLAD_API_PTR *PFNGLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (GLAD_API_PTR *PFNGLCLIPPLANEXOESPROC)(GLenum plane, const GLfixed * equation); +typedef void (GLAD_API_PTR *PFNGLCOLOR3XOESPROC)(GLfixed red, GLfixed green, GLfixed blue); +typedef void (GLAD_API_PTR *PFNGLCOLOR3XVOESPROC)(const GLfixed * components); +typedef void (GLAD_API_PTR *PFNGLCOLOR4XOESPROC)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (GLAD_API_PTR *PFNGLCOLOR4XVOESPROC)(const GLfixed * components); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +typedef void (GLAD_API_PTR *PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERARBPROC)(GLhandleARB shaderObj); +typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERINCLUDEARBPROC)(GLuint shader, GLsizei count, const GLchar *const* path, const GLint * length); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE1DARBPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DARBPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE3DARBPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); +typedef void (GLAD_API_PTR *PFNGLCONVOLUTIONPARAMETERXOESPROC)(GLenum target, GLenum pname, GLfixed param); +typedef void (GLAD_API_PTR *PFNGLCONVOLUTIONPARAMETERXVOESPROC)(GLenum target, GLenum pname, const GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLCOPYIMAGESUBDATAPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (GLAD_API_PTR *PFNGLCOPYNAMEDBUFFERSUBDATAPROC)(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLCOPYTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLCREATEBUFFERSPROC)(GLsizei n, GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLCREATEFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); +typedef GLuint (GLAD_API_PTR *PFNGLCREATEPROGRAMPROC)(void); +typedef GLhandleARB (GLAD_API_PTR *PFNGLCREATEPROGRAMOBJECTARBPROC)(void); +typedef void (GLAD_API_PTR *PFNGLCREATEPROGRAMPIPELINESPROC)(GLsizei n, GLuint * pipelines); +typedef void (GLAD_API_PTR *PFNGLCREATEQUERIESPROC)(GLenum target, GLsizei n, GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLCREATERENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLCREATESAMPLERSPROC)(GLsizei n, GLuint * samplers); +typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROC)(GLenum type); +typedef GLhandleARB (GLAD_API_PTR *PFNGLCREATESHADEROBJECTARBPROC)(GLenum shaderType); +typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROGRAMVPROC)(GLenum type, GLsizei count, const GLchar *const* strings); +typedef void (GLAD_API_PTR *PFNGLCREATETEXTURESPROC)(GLenum target, GLsizei n, GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLCREATETRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLCREATEVERTEXARRAYSPROC)(GLsizei n, GLuint * arrays); +typedef void (GLAD_API_PTR *PFNGLCULLFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void * userParam); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECALLBACKARBPROC)(GLDEBUGPROCARB callback, const void * userParam); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECONTROLARBPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf); +typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGEINSERTARBPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf); +typedef void (GLAD_API_PTR *PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLDELETEBUFFERSARBPROC)(GLsizei n, const GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLDELETEFRAMEBUFFERSEXTPROC)(GLsizei n, const GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLDELETENAMEDSTRINGARBPROC)(GLint namelen, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLDELETEOBJECTARBPROC)(GLhandleARB obj); +typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPIPELINESPROC)(GLsizei n, const GLuint * pipelines); +typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMSARBPROC)(GLsizei n, const GLuint * programs); +typedef void (GLAD_API_PTR *PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLDELETEQUERIESARBPROC)(GLsizei n, const GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLDELETERENDERBUFFERSEXTPROC)(GLsizei n, const GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint * samplers); +typedef void (GLAD_API_PTR *PFNGLDELETESHADERPROC)(GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDELETESYNCPROC)(GLsync sync); +typedef void (GLAD_API_PTR *PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLDELETETRANSFORMFEEDBACKSPROC)(GLsizei n, const GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint * arrays); +typedef void (GLAD_API_PTR *PFNGLDEPTHFUNCPROC)(GLenum func); +typedef void (GLAD_API_PTR *PFNGLDEPTHMASKPROC)(GLboolean flag); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEARRAYVPROC)(GLuint first, GLsizei count, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEINDEXEDPROC)(GLuint index, GLdouble n, GLdouble f); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f); +typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEXOESPROC)(GLfixed n, GLfixed f); +typedef void (GLAD_API_PTR *PFNGLDETACHOBJECTARBPROC)(GLhandleARB containerObj, GLhandleARB attachedObj); +typedef void (GLAD_API_PTR *PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (GLAD_API_PTR *PFNGLDISABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYARBPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISABLEIPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLDISPATCHCOMPUTEPROC)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void (GLAD_API_PTR *PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +typedef void (GLAD_API_PTR *PFNGLDISPATCHCOMPUTEINDIRECTPROC)(GLintptr indirect); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINDIRECTPROC)(GLenum mode, const void * indirect); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDARBPROC)(GLenum mode, GLint first, GLsizei count, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDEXTPROC)(GLenum mode, GLint start, GLsizei count, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERPROC)(GLenum buf); +typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum * bufs); +typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSARBPROC)(GLsizei n, const GLenum * bufs); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINDIRECTPROC)(GLenum mode, GLenum type, const void * indirect); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDARBPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei primcount); +typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices); +typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKPROC)(GLenum mode, GLuint id); +typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)(GLenum mode, GLuint id, GLsizei instancecount); +typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)(GLenum mode, GLuint id, GLuint stream); +typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)(GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +typedef void (GLAD_API_PTR *PFNGLENABLEPROC)(GLenum cap); +typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index); +typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYARBPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLENABLEIPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLENDCONDITIONALRENDERPROC)(void); +typedef void (GLAD_API_PTR *PFNGLENDQUERYPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLENDQUERYARBPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLENDQUERYINDEXEDPROC)(GLenum target, GLuint index); +typedef void (GLAD_API_PTR *PFNGLENDTRANSFORMFEEDBACKPROC)(void); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD1XOESPROC)(GLfixed u); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD1XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD2XOESPROC)(GLfixed u, GLfixed v); +typedef void (GLAD_API_PTR *PFNGLEVALCOORD2XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLEVALUATEDEPTHVALUESARBPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFEEDBACKBUFFERXOESPROC)(GLsizei n, GLenum type, const GLfixed * buffer); +typedef GLsync (GLAD_API_PTR *PFNGLFENCESYNCPROC)(GLenum condition, GLbitfield flags); +typedef void (GLAD_API_PTR *PFNGLFINISHPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFLUSHPROC)(void); +typedef void (GLAD_API_PTR *PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (GLAD_API_PTR *PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (GLAD_API_PTR *PFNGLFOGCOORDPOINTEREXTPROC)(GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLFOGCOORDDEXTPROC)(GLdouble coord); +typedef void (GLAD_API_PTR *PFNGLFOGCOORDDVEXTPROC)(const GLdouble * coord); +typedef void (GLAD_API_PTR *PFNGLFOGCOORDFEXTPROC)(GLfloat coord); +typedef void (GLAD_API_PTR *PFNGLFOGCOORDFVEXTPROC)(const GLfloat * coord); +typedef void (GLAD_API_PTR *PFNGLFOGXOESPROC)(GLenum pname, GLfixed param); +typedef void (GLAD_API_PTR *PFNGLFOGXVOESPROC)(GLenum pname, const GLfixed * param); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC)(GLenum target, GLuint start, GLsizei count, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE1DEXTPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE3DEXTPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREARBPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREFACEARBPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURELAYERARBPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (GLAD_API_PTR *PFNGLFRONTFACEPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLFRUSTUMXOESPROC)(GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (GLAD_API_PTR *PFNGLGENBUFFERSPROC)(GLsizei n, GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLGENBUFFERSARBPROC)(GLsizei n, GLuint * buffers); +typedef void (GLAD_API_PTR *PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLGENFRAMEBUFFERSEXTPROC)(GLsizei n, GLuint * framebuffers); +typedef void (GLAD_API_PTR *PFNGLGENPROGRAMPIPELINESPROC)(GLsizei n, GLuint * pipelines); +typedef void (GLAD_API_PTR *PFNGLGENPROGRAMSARBPROC)(GLsizei n, GLuint * programs); +typedef void (GLAD_API_PTR *PFNGLGENQUERIESPROC)(GLsizei n, GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLGENQUERIESARBPROC)(GLsizei n, GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLGENRENDERBUFFERSEXTPROC)(GLsizei n, GLuint * renderbuffers); +typedef void (GLAD_API_PTR *PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint * samplers); +typedef void (GLAD_API_PTR *PFNGLGENTEXTURESPROC)(GLsizei n, GLuint * textures); +typedef void (GLAD_API_PTR *PFNGLGENTRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint * ids); +typedef void (GLAD_API_PTR *PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint * arrays); +typedef void (GLAD_API_PTR *PFNGLGENERATEMIPMAPPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLGENERATEMIPMAPEXTPROC)(GLenum target); +typedef void (GLAD_API_PTR *PFNGLGENERATETEXTUREMIPMAPPROC)(GLuint texture); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)(GLuint program, GLuint bufferIndex, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEATTRIBARBPROC)(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, GLint * size, GLenum * type, GLcharARB * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVESUBROUTINENAMEPROC)(GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei * length, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)(GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei * length, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)(GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint * values); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMARBPROC)(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, GLint * size, GLenum * type, GLcharARB * name); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformBlockName); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformName); +typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint * uniformIndices, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETATTACHEDOBJECTSARBPROC)(GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, GLhandleARB * obj); +typedef void (GLAD_API_PTR *PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders); +typedef GLint (GLAD_API_PTR *PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar * name); +typedef GLint (GLAD_API_PTR *PFNGLGETATTRIBLOCATIONARBPROC)(GLhandleARB programObj, const GLcharARB * name); +typedef void (GLAD_API_PTR *PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean * data); +typedef void (GLAD_API_PTR *PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean * data); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVARBPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void ** params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERPOINTERVARBPROC)(GLenum target, GLenum pname, void ** params); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void * data); +typedef void (GLAD_API_PTR *PFNGLGETBUFFERSUBDATAARBPROC)(GLenum target, GLintptrARB offset, GLsizeiptrARB size, void * data); +typedef void (GLAD_API_PTR *PFNGLGETCLIPPLANEXOESPROC)(GLenum plane, GLfixed * equation); +typedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void * img); +typedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXIMAGEARBPROC)(GLenum target, GLint level, void * img); +typedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLsizei bufSize, void * pixels); +typedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void * pixels); +typedef void (GLAD_API_PTR *PFNGLGETCONVOLUTIONPARAMETERXVOESPROC)(GLenum target, GLenum pname, GLfixed * params); +typedef GLuint (GLAD_API_PTR *PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog); +typedef GLuint (GLAD_API_PTR *PFNGLGETDEBUGMESSAGELOGARBPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog); +typedef void (GLAD_API_PTR *PFNGLGETDOUBLEI_VPROC)(GLenum target, GLuint index, GLdouble * data); +typedef void (GLAD_API_PTR *PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble * data); +typedef GLenum (GLAD_API_PTR *PFNGLGETERRORPROC)(void); +typedef void (GLAD_API_PTR *PFNGLGETFIXEDVOESPROC)(GLenum pname, GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLGETFLOATI_VPROC)(GLenum target, GLuint index, GLfloat * data); +typedef void (GLAD_API_PTR *PFNGLGETFLOATVPROC)(GLenum pname, GLfloat * data); +typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar * name); +typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef GLhandleARB (GLAD_API_PTR *PFNGLGETHANDLEARBPROC)(GLenum pname); +typedef void (GLAD_API_PTR *PFNGLGETHISTOGRAMPARAMETERXVOESPROC)(GLenum target, GLenum pname, GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLGETINFOLOGARBPROC)(GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint * data); +typedef void (GLAD_API_PTR *PFNGLGETINTEGERVPROC)(GLenum pname, GLint * data); +typedef void (GLAD_API_PTR *PFNGLGETINTERNALFORMATI64VPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETINTERNALFORMATIVPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETLIGHTXOESPROC)(GLenum light, GLenum pname, GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLGETMAPXVOESPROC)(GLenum target, GLenum query, GLfixed * v); +typedef void (GLAD_API_PTR *PFNGLGETMATERIALXOESPROC)(GLenum face, GLenum pname, GLfixed param); +typedef void (GLAD_API_PTR *PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat * val); +typedef void (GLAD_API_PTR *PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)(GLuint buffer, GLenum pname, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETNAMEDBUFFERPARAMETERIVPROC)(GLuint buffer, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNAMEDBUFFERPOINTERVPROC)(GLuint buffer, GLenum pname, void ** params); +typedef void (GLAD_API_PTR *PFNGLGETNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, void * data); +typedef void (GLAD_API_PTR *PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLuint framebuffer, GLenum attachment, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)(GLuint framebuffer, GLenum pname, GLint * param); +typedef void (GLAD_API_PTR *PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)(GLuint renderbuffer, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNAMEDSTRINGARBPROC)(GLint namelen, const GLchar * name, GLsizei bufSize, GLint * stringlen, GLchar * string); +typedef void (GLAD_API_PTR *PFNGLGETNAMEDSTRINGIVARBPROC)(GLint namelen, const GLchar * name, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTPARAMETERFVARBPROC)(GLhandleARB obj, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTPARAMETERIVARBPROC)(GLhandleARB obj, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETOBJECTPTRLABELPROC)(const void * ptr, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPXVPROC)(GLenum map, GLint size, GLfixed * values); +typedef void (GLAD_API_PTR *PFNGLGETPOINTERVPROC)(GLenum pname, void ** params); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMBINARYPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLenum * binaryFormat, void * binary); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMENVPARAMETERDVARBPROC)(GLenum target, GLuint index, GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMENVPARAMETERFVARBPROC)(GLenum target, GLuint index, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMINTERFACEIVPROC)(GLuint program, GLenum programInterface, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC)(GLenum target, GLuint index, GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC)(GLenum target, GLuint index, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMPIPELINEINFOLOGPROC)(GLuint pipeline, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMPIPELINEIVPROC)(GLuint pipeline, GLenum pname, GLint * params); +typedef GLuint (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCEINDEXPROC)(GLuint program, GLenum programInterface, const GLchar * name); +typedef GLint (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCELOCATIONPROC)(GLuint program, GLenum programInterface, const GLchar * name); +typedef GLint (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)(GLuint program, GLenum programInterface, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCENAMEPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei * length, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCEIVPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum * props, GLsizei count, GLsizei * length, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMSTAGEIVPROC)(GLuint program, GLenum shadertype, GLenum pname, GLint * values); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMSTRINGARBPROC)(GLenum target, GLenum pname, void * string); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVARBPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTUI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTUIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (GLAD_API_PTR *PFNGLGETQUERYINDEXEDIVPROC)(GLenum target, GLuint index, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTIVARBPROC)(GLuint id, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUIVARBPROC)(GLuint id, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETQUERYIVARBPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (GLAD_API_PTR *PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint * range, GLint * precision); +typedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * source); +typedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEARBPROC)(GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * source); +typedef void (GLAD_API_PTR *PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint * params); +typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGPROC)(GLenum name); +typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); +typedef GLuint (GLAD_API_PTR *PFNGLGETSUBROUTINEINDEXPROC)(GLuint program, GLenum shadertype, const GLchar * name); +typedef GLint (GLAD_API_PTR *PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)(GLuint program, GLenum shadertype, const GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei count, GLsizei * length, GLint * values); +typedef void (GLAD_API_PTR *PFNGLGETTEXENVXVOESPROC)(GLenum target, GLenum pname, GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXGENXVOESPROC)(GLenum coord, GLenum pname, GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void * pixels); +typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERXVOESPROC)(GLenum target, GLint level, GLenum pname, GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERXVOESPROC)(GLenum target, GLenum pname, GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void * pixels); +typedef void (GLAD_API_PTR *PFNGLGETTEXTURELEVELPARAMETERFVPROC)(GLuint texture, GLint level, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXTURELEVELPARAMETERIVPROC)(GLuint texture, GLint level, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void * pixels); +typedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLsizei * size, GLenum * type, GLchar * name); +typedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKI64_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint64 * param); +typedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKI_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint * param); +typedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKIVPROC)(GLuint xfb, GLenum pname, GLint * param); +typedef GLuint (GLAD_API_PTR *PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar * uniformBlockName); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const* uniformNames, GLuint * uniformIndices); +typedef GLint (GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar * name); +typedef GLint (GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONARBPROC)(GLhandleARB programObj, const GLcharARB * name); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMSUBROUTINEUIVPROC)(GLenum shadertype, GLint location, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMDVPROC)(GLuint program, GLint location, GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMFVARBPROC)(GLhandleARB programObj, GLint location, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMI64VARBPROC)(GLuint program, GLint location, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMIVARBPROC)(GLhandleARB programObj, GLint location, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMUI64VARBPROC)(GLuint program, GLint location, GLuint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXARRAYINDEXED64IVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint64 * param); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXARRAYINDEXEDIVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint * param); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXARRAYIVPROC)(GLuint vaobj, GLenum pname, GLint * param); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBLDVPROC)(GLuint index, GLenum pname, GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void ** pointer); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVARBPROC)(GLuint index, GLenum pname, void ** pointer); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBDVARBPROC)(GLuint index, GLenum pname, GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVARBPROC)(GLuint index, GLenum pname, GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVARBPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMI64VARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLint64 * params); +typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMUI64VARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint64 * params); +typedef void (GLAD_API_PTR *PFNGLHINTPROC)(GLenum target, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLINDEXXOESPROC)(GLfixed component); +typedef void (GLAD_API_PTR *PFNGLINDEXXVOESPROC)(const GLfixed * component); +typedef void (GLAD_API_PTR *PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLINVALIDATEBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (GLAD_API_PTR *PFNGLINVALIDATEFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum * attachments); +typedef void (GLAD_API_PTR *PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum * attachments); +typedef void (GLAD_API_PTR *PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum * attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLINVALIDATESUBFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum * attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLINVALIDATETEXIMAGEPROC)(GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLINVALIDATETEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +typedef GLboolean (GLAD_API_PTR *PFNGLISBUFFERPROC)(GLuint buffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISBUFFERARBPROC)(GLuint buffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDPROC)(GLenum cap); +typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDIPROC)(GLenum target, GLuint index); +typedef GLboolean (GLAD_API_PTR *PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISFRAMEBUFFEREXTPROC)(GLuint framebuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISNAMEDSTRINGARBPROC)(GLint namelen, const GLchar * name); +typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPROC)(GLuint program); +typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMARBPROC)(GLuint program); +typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPIPELINEPROC)(GLuint pipeline); +typedef GLboolean (GLAD_API_PTR *PFNGLISQUERYPROC)(GLuint id); +typedef GLboolean (GLAD_API_PTR *PFNGLISQUERYARBPROC)(GLuint id); +typedef GLboolean (GLAD_API_PTR *PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISRENDERBUFFEREXTPROC)(GLuint renderbuffer); +typedef GLboolean (GLAD_API_PTR *PFNGLISSAMPLERPROC)(GLuint sampler); +typedef GLboolean (GLAD_API_PTR *PFNGLISSHADERPROC)(GLuint shader); +typedef GLboolean (GLAD_API_PTR *PFNGLISSYNCPROC)(GLsync sync); +typedef GLboolean (GLAD_API_PTR *PFNGLISTEXTUREPROC)(GLuint texture); +typedef GLboolean (GLAD_API_PTR *PFNGLISTRANSFORMFEEDBACKPROC)(GLuint id); +typedef GLboolean (GLAD_API_PTR *PFNGLISVERTEXARRAYPROC)(GLuint array); +typedef void (GLAD_API_PTR *PFNGLLIGHTMODELXOESPROC)(GLenum pname, GLfixed param); +typedef void (GLAD_API_PTR *PFNGLLIGHTMODELXVOESPROC)(GLenum pname, const GLfixed * param); +typedef void (GLAD_API_PTR *PFNGLLIGHTXOESPROC)(GLenum light, GLenum pname, GLfixed param); +typedef void (GLAD_API_PTR *PFNGLLIGHTXVOESPROC)(GLenum light, GLenum pname, const GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLLINEWIDTHPROC)(GLfloat width); +typedef void (GLAD_API_PTR *PFNGLLINEWIDTHXOESPROC)(GLfixed width); +typedef void (GLAD_API_PTR *PFNGLLINKPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLLINKPROGRAMARBPROC)(GLhandleARB programObj); +typedef void (GLAD_API_PTR *PFNGLLOADMATRIXXOESPROC)(const GLfixed * m); +typedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXDARBPROC)(const GLdouble * m); +typedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXFARBPROC)(const GLfloat * m); +typedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXXOESPROC)(const GLfixed * m); +typedef void (GLAD_API_PTR *PFNGLLOGICOPPROC)(GLenum opcode); +typedef void (GLAD_API_PTR *PFNGLMAP1XOESPROC)(GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +typedef void (GLAD_API_PTR *PFNGLMAP2XOESPROC)(GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); +typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERARBPROC)(GLenum target, GLenum access); +typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (GLAD_API_PTR *PFNGLMAPGRID1XOESPROC)(GLint n, GLfixed u1, GLfixed u2); +typedef void (GLAD_API_PTR *PFNGLMAPGRID2XOESPROC)(GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +typedef void * (GLAD_API_PTR *PFNGLMAPNAMEDBUFFERPROC)(GLuint buffer, GLenum access); +typedef void * (GLAD_API_PTR *PFNGLMAPNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (GLAD_API_PTR *PFNGLMATERIALXOESPROC)(GLenum face, GLenum pname, GLfixed param); +typedef void (GLAD_API_PTR *PFNGLMATERIALXVOESPROC)(GLenum face, GLenum pname, const GLfixed * param); +typedef void (GLAD_API_PTR *PFNGLMEMORYBARRIERPROC)(GLbitfield barriers); +typedef void (GLAD_API_PTR *PFNGLMEMORYBARRIERBYREGIONPROC)(GLbitfield barriers); +typedef void (GLAD_API_PTR *PFNGLMINSAMPLESHADINGPROC)(GLfloat value); +typedef void (GLAD_API_PTR *PFNGLMINSAMPLESHADINGARBPROC)(GLfloat value); +typedef void (GLAD_API_PTR *PFNGLMULTMATRIXXOESPROC)(const GLfixed * m); +typedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXDARBPROC)(const GLdouble * m); +typedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXFARBPROC)(const GLfloat * m); +typedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXXOESPROC)(const GLfixed * m); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint * first, const GLsizei * count, GLsizei drawcount); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSINDIRECTPROC)(GLenum mode, const void * indirect, GLsizei drawcount, GLsizei stride); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount, const GLint * basevertex); +typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSINDIRECTPROC)(GLenum mode, GLenum type, const void * indirect, GLsizei drawcount, GLsizei stride); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DARBPROC)(GLenum target, GLdouble s); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DVARBPROC)(GLenum target, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FARBPROC)(GLenum target, GLfloat s); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FVARBPROC)(GLenum target, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IARBPROC)(GLenum target, GLint s); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IVARBPROC)(GLenum target, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SARBPROC)(GLenum target, GLshort s); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SVARBPROC)(GLenum target, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1XOESPROC)(GLenum texture, GLfixed s); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1XVOESPROC)(GLenum texture, const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DARBPROC)(GLenum target, GLdouble s, GLdouble t); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DVARBPROC)(GLenum target, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FARBPROC)(GLenum target, GLfloat s, GLfloat t); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FVARBPROC)(GLenum target, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IARBPROC)(GLenum target, GLint s, GLint t); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IVARBPROC)(GLenum target, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SARBPROC)(GLenum target, GLshort s, GLshort t); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SVARBPROC)(GLenum target, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2XOESPROC)(GLenum texture, GLfixed s, GLfixed t); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2XVOESPROC)(GLenum texture, const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DARBPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DVARBPROC)(GLenum target, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FARBPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FVARBPROC)(GLenum target, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IARBPROC)(GLenum target, GLint s, GLint t, GLint r); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IVARBPROC)(GLenum target, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SARBPROC)(GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SVARBPROC)(GLenum target, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3XOESPROC)(GLenum texture, GLfixed s, GLfixed t, GLfixed r); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3XVOESPROC)(GLenum texture, const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DARBPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DVARBPROC)(GLenum target, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FARBPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FVARBPROC)(GLenum target, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IARBPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IVARBPROC)(GLenum target, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SARBPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SVARBPROC)(GLenum target, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4XOESPROC)(GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4XVOESPROC)(GLenum texture, const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERDATAPROC)(GLuint buffer, GLsizeiptr size, const void * data, GLenum usage); +typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERSTORAGEPROC)(GLuint buffer, GLsizeiptr size, const void * data, GLbitfield flags); +typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, const void * data); +typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)(GLuint framebuffer, GLenum buf); +typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)(GLuint framebuffer, GLsizei n, const GLenum * bufs); +typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)(GLuint framebuffer, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)(GLuint framebuffer, GLenum src); +typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC)(GLuint framebuffer, GLuint start, GLsizei count, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (GLAD_API_PTR *PFNGLNAMEDRENDERBUFFERSTORAGEPROC)(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLNAMEDSTRINGARBPROC)(GLenum type, GLint namelen, const GLchar * name, GLint stringlen, const GLchar * string); +typedef void (GLAD_API_PTR *PFNGLNORMAL3XOESPROC)(GLfixed nx, GLfixed ny, GLfixed nz); +typedef void (GLAD_API_PTR *PFNGLNORMAL3XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar * label); +typedef void (GLAD_API_PTR *PFNGLOBJECTPTRLABELPROC)(const void * ptr, GLsizei length, const GLchar * label); +typedef void (GLAD_API_PTR *PFNGLORTHOXOESPROC)(GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (GLAD_API_PTR *PFNGLPASSTHROUGHXOESPROC)(GLfixed token); +typedef void (GLAD_API_PTR *PFNGLPATCHPARAMETERFVPROC)(GLenum pname, const GLfloat * values); +typedef void (GLAD_API_PTR *PFNGLPATCHPARAMETERIPROC)(GLenum pname, GLint value); +typedef void (GLAD_API_PTR *PFNGLPAUSETRANSFORMFEEDBACKPROC)(void); +typedef void (GLAD_API_PTR *PFNGLPIXELMAPXPROC)(GLenum map, GLint size, const GLfixed * values); +typedef void (GLAD_API_PTR *PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLPIXELSTOREXPROC)(GLenum pname, GLfixed param); +typedef void (GLAD_API_PTR *PFNGLPIXELTRANSFERXOESPROC)(GLenum pname, GLfixed param); +typedef void (GLAD_API_PTR *PFNGLPIXELZOOMXOESPROC)(GLfixed xfactor, GLfixed yfactor); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERXVOESPROC)(GLenum pname, const GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLPOINTSIZEPROC)(GLfloat size); +typedef void (GLAD_API_PTR *PFNGLPOINTSIZEXOESPROC)(GLfixed size); +typedef void (GLAD_API_PTR *PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode); +typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); +typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETXOESPROC)(GLfixed factor, GLfixed units); +typedef void (GLAD_API_PTR *PFNGLPOPDEBUGGROUPPROC)(void); +typedef void (GLAD_API_PTR *PFNGLPRIMITIVEBOUNDINGBOXARBPROC)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +typedef void (GLAD_API_PTR *PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index); +typedef void (GLAD_API_PTR *PFNGLPRIORITIZETEXTURESXOESPROC)(GLsizei n, const GLuint * textures, const GLfixed * priorities); +typedef void (GLAD_API_PTR *PFNGLPROGRAMBINARYPROC)(GLuint program, GLenum binaryFormat, const void * binary, GLsizei length); +typedef void (GLAD_API_PTR *PFNGLPROGRAMENVPARAMETER4DARBPROC)(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (GLAD_API_PTR *PFNGLPROGRAMENVPARAMETER4DVARBPROC)(GLenum target, GLuint index, const GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLPROGRAMENVPARAMETER4FARBPROC)(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAD_API_PTR *PFNGLPROGRAMENVPARAMETER4FVARBPROC)(GLenum target, GLuint index, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLPROGRAMLOCALPARAMETER4DARBPROC)(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (GLAD_API_PTR *PFNGLPROGRAMLOCALPARAMETER4DVARBPROC)(GLenum target, GLuint index, const GLdouble * params); +typedef void (GLAD_API_PTR *PFNGLPROGRAMLOCALPARAMETER4FARBPROC)(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAD_API_PTR *PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)(GLenum target, GLuint index, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLPROGRAMPARAMETERIPROC)(GLuint program, GLenum pname, GLint value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMPARAMETERIARBPROC)(GLuint program, GLenum pname, GLint value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMSTRINGARBPROC)(GLenum target, GLenum format, GLsizei len, const void * string); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1DPROC)(GLuint program, GLint location, GLdouble v0); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1FPROC)(GLuint program, GLint location, GLfloat v0); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1IPROC)(GLuint program, GLint location, GLint v0); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1I64ARBPROC)(GLuint program, GLint location, GLint64 x); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UIPROC)(GLuint program, GLint location, GLuint v0); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UI64ARBPROC)(GLuint program, GLint location, GLuint64 x); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2IPROC)(GLuint program, GLint location, GLint v0, GLint v1); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2I64ARBPROC)(GLuint program, GLint location, GLint64 x, GLint64 y); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UI64ARBPROC)(GLuint program, GLint location, GLuint64 x, GLuint64 y); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3I64ARBPROC)(GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UI64ARBPROC)(GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4I64ARBPROC)(GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UI64ARBPROC)(GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLPROVOKINGVERTEXPROC)(GLenum mode); +typedef void (GLAD_API_PTR *PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar * message); +typedef void (GLAD_API_PTR *PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS2XOESPROC)(GLfixed x, GLfixed y); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS2XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS3XOESPROC)(GLfixed x, GLfixed y, GLfixed z); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS3XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS4XOESPROC)(GLfixed x, GLfixed y, GLfixed z, GLfixed w); +typedef void (GLAD_API_PTR *PFNGLRASTERPOS4XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLREADBUFFERPROC)(GLenum src); +typedef void (GLAD_API_PTR *PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels); +typedef void (GLAD_API_PTR *PFNGLRECTXOESPROC)(GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +typedef void (GLAD_API_PTR *PFNGLRECTXVOESPROC)(const GLfixed * v1, const GLfixed * v2); +typedef void (GLAD_API_PTR *PFNGLRELEASESHADERCOMPILERPROC)(void); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEEXTPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLRESUMETRANSFORMFEEDBACKPROC)(void); +typedef void (GLAD_API_PTR *PFNGLROTATEXOESPROC)(GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); +typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEARBPROC)(GLfloat value, GLboolean invert); +typedef void (GLAD_API_PTR *PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, const GLuint * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat * param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (GLAD_API_PTR *PFNGLSCALEXOESPROC)(GLfixed x, GLfixed y, GLfixed z); +typedef void (GLAD_API_PTR *PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSCISSORARRAYVPROC)(GLuint first, GLsizei count, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLSCISSORINDEXEDPROC)(GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLSCISSORINDEXEDVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint * shaders, GLenum binaryFormat, const void * binary, GLsizei length); +typedef void (GLAD_API_PTR *PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); +typedef void (GLAD_API_PTR *PFNGLSHADERSOURCEARBPROC)(GLhandleARB shaderObj, GLsizei count, const GLcharARB ** string, const GLint * length); +typedef void (GLAD_API_PTR *PFNGLSHADERSTORAGEBLOCKBINDINGPROC)(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +typedef void (GLAD_API_PTR *PFNGLSPECIALIZESHADERARBPROC)(GLuint shader, const GLchar * pEntryPoint, GLuint numSpecializationConstants, const GLuint * pConstantIndex, const GLuint * pConstantValue); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKPROC)(GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); +typedef void (GLAD_API_PTR *PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLTEXBUFFERRANGEPROC)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD1XOESPROC)(GLfixed s); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD1XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD2XOESPROC)(GLfixed s, GLfixed t); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD2XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD3XOESPROC)(GLfixed s, GLfixed t, GLfixed r); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD3XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD4XOESPROC)(GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (GLAD_API_PTR *PFNGLTEXCOORD4XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLTEXENVXOESPROC)(GLenum target, GLenum pname, GLfixed param); +typedef void (GLAD_API_PTR *PFNGLTEXENVXVOESPROC)(GLenum target, GLenum pname, const GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLTEXGENXOESPROC)(GLenum coord, GLenum pname, GLfixed param); +typedef void (GLAD_API_PTR *PFNGLTEXGENXVOESPROC)(GLenum coord, GLenum pname, const GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERXOESPROC)(GLenum target, GLenum pname, GLfixed param); +typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERXVOESPROC)(GLenum target, GLenum pname, const GLfixed * params); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE1DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE2DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE3DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXTUREBUFFERPROC)(GLuint texture, GLenum internalformat, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLTEXTUREBUFFERRANGEPROC)(GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, const GLint * params); +typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, const GLuint * params); +typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERFPROC)(GLuint texture, GLenum pname, GLfloat param); +typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, const GLfloat * param); +typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERIPROC)(GLuint texture, GLenum pname, GLint param); +typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, const GLint * param); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE1DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE2DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE3DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (GLAD_API_PTR *PFNGLTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); +typedef void (GLAD_API_PTR *PFNGLTEXTUREVIEWPROC)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)(GLuint xfb, GLuint index, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)(GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const* varyings, GLenum bufferMode); +typedef void (GLAD_API_PTR *PFNGLTRANSLATEXOESPROC)(GLfixed x, GLfixed y, GLfixed z); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1DPROC)(GLint location, GLdouble x); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1DVPROC)(GLint location, GLsizei count, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FARBPROC)(GLint location, GLfloat v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IPROC)(GLint location, GLint v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1I64ARBPROC)(GLint location, GLint64 x); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IARBPROC)(GLint location, GLint v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1IVARBPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1UI64ARBPROC)(GLint location, GLuint64 x); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2DPROC)(GLint location, GLdouble x, GLdouble y); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2DVPROC)(GLint location, GLsizei count, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FARBPROC)(GLint location, GLfloat v0, GLfloat v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2I64ARBPROC)(GLint location, GLint64 x, GLint64 y); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IARBPROC)(GLint location, GLint v0, GLint v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2IVARBPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2UI64ARBPROC)(GLint location, GLuint64 x, GLuint64 y); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3DPROC)(GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3DVPROC)(GLint location, GLsizei count, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FARBPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3I64ARBPROC)(GLint location, GLint64 x, GLint64 y, GLint64 z); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IARBPROC)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3IVARBPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3UI64ARBPROC)(GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4DPROC)(GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4DVPROC)(GLint location, GLsizei count, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FARBPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4I64ARBPROC)(GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IARBPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4IVARBPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4UI64ARBPROC)(GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (GLAD_API_PTR *PFNGLUNIFORMSUBROUTINESUIVPROC)(GLenum shadertype, GLsizei count, const GLuint * indices); +typedef GLboolean (GLAD_API_PTR *PFNGLUNMAPBUFFERPROC)(GLenum target); +typedef GLboolean (GLAD_API_PTR *PFNGLUNMAPBUFFERARBPROC)(GLenum target); +typedef GLboolean (GLAD_API_PTR *PFNGLUNMAPNAMEDBUFFERPROC)(GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMOBJECTARBPROC)(GLhandleARB programObj); +typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMSTAGESPROC)(GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPROC)(GLuint program); +typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMARBPROC)(GLhandleARB programObj); +typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPIPELINEPROC)(GLuint pipeline); +typedef void (GLAD_API_PTR *PFNGLVERTEX2XOESPROC)(GLfixed x); +typedef void (GLAD_API_PTR *PFNGLVERTEX2XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLVERTEX3XOESPROC)(GLfixed x, GLfixed y); +typedef void (GLAD_API_PTR *PFNGLVERTEX3XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLVERTEX4XOESPROC)(GLfixed x, GLfixed y, GLfixed z); +typedef void (GLAD_API_PTR *PFNGLVERTEX4XVOESPROC)(const GLfixed * coords); +typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBBINDINGPROC)(GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBIFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBLFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYBINDINGDIVISORPROC)(GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYELEMENTBUFFERPROC)(GLuint vaobj, GLuint buffer); +typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYVERTEXBUFFERPROC)(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYVERTEXBUFFERSPROC)(GLuint vaobj, GLuint first, GLsizei count, const GLuint * buffers, const GLintptr * offsets, const GLsizei * strides); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DARBPROC)(GLuint index, GLdouble x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DVARBPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FARBPROC)(GLuint index, GLfloat x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FVARBPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SARBPROC)(GLuint index, GLshort x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SVARBPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DARBPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DVARBPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FARBPROC)(GLuint index, GLfloat x, GLfloat y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FVARBPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SARBPROC)(GLuint index, GLshort x, GLshort y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SVARBPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DARBPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DVARBPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FARBPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FVARBPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SARBPROC)(GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SVARBPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NBVARBPROC)(GLuint index, const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NIVARBPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NSVARBPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBARBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBVARBPROC)(GLuint index, const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUIVARBPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUSVARBPROC)(GLuint index, const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4BVARBPROC)(GLuint index, const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DARBPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DVARBPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FARBPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FVARBPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4IVARBPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SARBPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SVARBPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UBVARBPROC)(GLuint index, const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UIVARBPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4USVARBPROC)(GLuint index, const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBBINDINGPROC)(GLuint attribindex, GLuint bindingindex); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBDIVISORARBPROC)(GLuint index, GLuint divisor); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBIFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL1DPROC)(GLuint index, GLdouble x); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL1DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL2DPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL2DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL3DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL4DVPROC)(GLuint index, const GLdouble * v); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBLFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBLPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERARBPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); +typedef void (GLAD_API_PTR *PFNGLVERTEXBINDINGDIVISORPROC)(GLuint bindingindex, GLuint divisor); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTARRAYVPROC)(GLuint first, GLsizei count, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTINDEXEDFPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (GLAD_API_PTR *PFNGLVIEWPORTINDEXEDFVPROC)(GLuint index, const GLfloat * v); +typedef void (GLAD_API_PTR *PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); + +GLAD_API_CALL PFNGLACCUMXOESPROC glad_glAccumxOES; +#define glAccumxOES glad_glAccumxOES +GLAD_API_CALL PFNGLACTIVESHADERPROGRAMPROC glad_glActiveShaderProgram; +#define glActiveShaderProgram glad_glActiveShaderProgram +GLAD_API_CALL PFNGLACTIVETEXTUREPROC glad_glActiveTexture; +#define glActiveTexture glad_glActiveTexture +GLAD_API_CALL PFNGLACTIVETEXTUREARBPROC glad_glActiveTextureARB; +#define glActiveTextureARB glad_glActiveTextureARB +GLAD_API_CALL PFNGLALPHAFUNCXOESPROC glad_glAlphaFuncxOES; +#define glAlphaFuncxOES glad_glAlphaFuncxOES +GLAD_API_CALL PFNGLATTACHOBJECTARBPROC glad_glAttachObjectARB; +#define glAttachObjectARB glad_glAttachObjectARB +GLAD_API_CALL PFNGLATTACHSHADERPROC glad_glAttachShader; +#define glAttachShader glad_glAttachShader +GLAD_API_CALL PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender; +#define glBeginConditionalRender glad_glBeginConditionalRender +GLAD_API_CALL PFNGLBEGINQUERYPROC glad_glBeginQuery; +#define glBeginQuery glad_glBeginQuery +GLAD_API_CALL PFNGLBEGINQUERYARBPROC glad_glBeginQueryARB; +#define glBeginQueryARB glad_glBeginQueryARB +GLAD_API_CALL PFNGLBEGINQUERYINDEXEDPROC glad_glBeginQueryIndexed; +#define glBeginQueryIndexed glad_glBeginQueryIndexed +GLAD_API_CALL PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback; +#define glBeginTransformFeedback glad_glBeginTransformFeedback +GLAD_API_CALL PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation; +#define glBindAttribLocation glad_glBindAttribLocation +GLAD_API_CALL PFNGLBINDATTRIBLOCATIONARBPROC glad_glBindAttribLocationARB; +#define glBindAttribLocationARB glad_glBindAttribLocationARB +GLAD_API_CALL PFNGLBINDBUFFERPROC glad_glBindBuffer; +#define glBindBuffer glad_glBindBuffer +GLAD_API_CALL PFNGLBINDBUFFERARBPROC glad_glBindBufferARB; +#define glBindBufferARB glad_glBindBufferARB +GLAD_API_CALL PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase; +#define glBindBufferBase glad_glBindBufferBase +GLAD_API_CALL PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange; +#define glBindBufferRange glad_glBindBufferRange +GLAD_API_CALL PFNGLBINDBUFFERSBASEPROC glad_glBindBuffersBase; +#define glBindBuffersBase glad_glBindBuffersBase +GLAD_API_CALL PFNGLBINDBUFFERSRANGEPROC glad_glBindBuffersRange; +#define glBindBuffersRange glad_glBindBuffersRange +GLAD_API_CALL PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation; +#define glBindFragDataLocation glad_glBindFragDataLocation +GLAD_API_CALL PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed; +#define glBindFragDataLocationIndexed glad_glBindFragDataLocationIndexed +GLAD_API_CALL PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; +#define glBindFramebuffer glad_glBindFramebuffer +GLAD_API_CALL PFNGLBINDFRAMEBUFFEREXTPROC glad_glBindFramebufferEXT; +#define glBindFramebufferEXT glad_glBindFramebufferEXT +GLAD_API_CALL PFNGLBINDIMAGETEXTUREPROC glad_glBindImageTexture; +#define glBindImageTexture glad_glBindImageTexture +GLAD_API_CALL PFNGLBINDIMAGETEXTURESPROC glad_glBindImageTextures; +#define glBindImageTextures glad_glBindImageTextures +GLAD_API_CALL PFNGLBINDPROGRAMARBPROC glad_glBindProgramARB; +#define glBindProgramARB glad_glBindProgramARB +GLAD_API_CALL PFNGLBINDPROGRAMPIPELINEPROC glad_glBindProgramPipeline; +#define glBindProgramPipeline glad_glBindProgramPipeline +GLAD_API_CALL PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; +#define glBindRenderbuffer glad_glBindRenderbuffer +GLAD_API_CALL PFNGLBINDRENDERBUFFEREXTPROC glad_glBindRenderbufferEXT; +#define glBindRenderbufferEXT glad_glBindRenderbufferEXT +GLAD_API_CALL PFNGLBINDSAMPLERPROC glad_glBindSampler; +#define glBindSampler glad_glBindSampler +GLAD_API_CALL PFNGLBINDSAMPLERSPROC glad_glBindSamplers; +#define glBindSamplers glad_glBindSamplers +GLAD_API_CALL PFNGLBINDTEXTUREPROC glad_glBindTexture; +#define glBindTexture glad_glBindTexture +GLAD_API_CALL PFNGLBINDTEXTUREUNITPROC glad_glBindTextureUnit; +#define glBindTextureUnit glad_glBindTextureUnit +GLAD_API_CALL PFNGLBINDTEXTURESPROC glad_glBindTextures; +#define glBindTextures glad_glBindTextures +GLAD_API_CALL PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback; +#define glBindTransformFeedback glad_glBindTransformFeedback +GLAD_API_CALL PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray; +#define glBindVertexArray glad_glBindVertexArray +GLAD_API_CALL PFNGLBINDVERTEXBUFFERPROC glad_glBindVertexBuffer; +#define glBindVertexBuffer glad_glBindVertexBuffer +GLAD_API_CALL PFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers; +#define glBindVertexBuffers glad_glBindVertexBuffers +GLAD_API_CALL PFNGLBITMAPXOESPROC glad_glBitmapxOES; +#define glBitmapxOES glad_glBitmapxOES +GLAD_API_CALL PFNGLBLENDCOLORPROC glad_glBlendColor; +#define glBlendColor glad_glBlendColor +GLAD_API_CALL PFNGLBLENDCOLORXOESPROC glad_glBlendColorxOES; +#define glBlendColorxOES glad_glBlendColorxOES +GLAD_API_CALL PFNGLBLENDEQUATIONPROC glad_glBlendEquation; +#define glBlendEquation glad_glBlendEquation +GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; +#define glBlendEquationSeparate glad_glBlendEquationSeparate +GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEIPROC glad_glBlendEquationSeparatei; +#define glBlendEquationSeparatei glad_glBlendEquationSeparatei +GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEIARBPROC glad_glBlendEquationSeparateiARB; +#define glBlendEquationSeparateiARB glad_glBlendEquationSeparateiARB +GLAD_API_CALL PFNGLBLENDEQUATIONIPROC glad_glBlendEquationi; +#define glBlendEquationi glad_glBlendEquationi +GLAD_API_CALL PFNGLBLENDEQUATIONIARBPROC glad_glBlendEquationiARB; +#define glBlendEquationiARB glad_glBlendEquationiARB +GLAD_API_CALL PFNGLBLENDFUNCPROC glad_glBlendFunc; +#define glBlendFunc glad_glBlendFunc +GLAD_API_CALL PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; +#define glBlendFuncSeparate glad_glBlendFuncSeparate +GLAD_API_CALL PFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei; +#define glBlendFuncSeparatei glad_glBlendFuncSeparatei +GLAD_API_CALL PFNGLBLENDFUNCSEPARATEIARBPROC glad_glBlendFuncSeparateiARB; +#define glBlendFuncSeparateiARB glad_glBlendFuncSeparateiARB +GLAD_API_CALL PFNGLBLENDFUNCIPROC glad_glBlendFunci; +#define glBlendFunci glad_glBlendFunci +GLAD_API_CALL PFNGLBLENDFUNCIARBPROC glad_glBlendFunciARB; +#define glBlendFunciARB glad_glBlendFunciARB +GLAD_API_CALL PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer; +#define glBlitFramebuffer glad_glBlitFramebuffer +GLAD_API_CALL PFNGLBLITFRAMEBUFFEREXTPROC glad_glBlitFramebufferEXT; +#define glBlitFramebufferEXT glad_glBlitFramebufferEXT +GLAD_API_CALL PFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer; +#define glBlitNamedFramebuffer glad_glBlitNamedFramebuffer +GLAD_API_CALL PFNGLBUFFERDATAPROC glad_glBufferData; +#define glBufferData glad_glBufferData +GLAD_API_CALL PFNGLBUFFERDATAARBPROC glad_glBufferDataARB; +#define glBufferDataARB glad_glBufferDataARB +GLAD_API_CALL PFNGLBUFFERSTORAGEPROC glad_glBufferStorage; +#define glBufferStorage glad_glBufferStorage +GLAD_API_CALL PFNGLBUFFERSUBDATAPROC glad_glBufferSubData; +#define glBufferSubData glad_glBufferSubData +GLAD_API_CALL PFNGLBUFFERSUBDATAARBPROC glad_glBufferSubDataARB; +#define glBufferSubDataARB glad_glBufferSubDataARB +GLAD_API_CALL PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus; +#define glCheckFramebufferStatus glad_glCheckFramebufferStatus +GLAD_API_CALL PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glad_glCheckFramebufferStatusEXT; +#define glCheckFramebufferStatusEXT glad_glCheckFramebufferStatusEXT +GLAD_API_CALL PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glad_glCheckNamedFramebufferStatus; +#define glCheckNamedFramebufferStatus glad_glCheckNamedFramebufferStatus +GLAD_API_CALL PFNGLCLAMPCOLORPROC glad_glClampColor; +#define glClampColor glad_glClampColor +GLAD_API_CALL PFNGLCLAMPCOLORARBPROC glad_glClampColorARB; +#define glClampColorARB glad_glClampColorARB +GLAD_API_CALL PFNGLCLEARPROC glad_glClear; +#define glClear glad_glClear +GLAD_API_CALL PFNGLCLEARACCUMXOESPROC glad_glClearAccumxOES; +#define glClearAccumxOES glad_glClearAccumxOES +GLAD_API_CALL PFNGLCLEARBUFFERDATAPROC glad_glClearBufferData; +#define glClearBufferData glad_glClearBufferData +GLAD_API_CALL PFNGLCLEARBUFFERSUBDATAPROC glad_glClearBufferSubData; +#define glClearBufferSubData glad_glClearBufferSubData +GLAD_API_CALL PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi; +#define glClearBufferfi glad_glClearBufferfi +GLAD_API_CALL PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv; +#define glClearBufferfv glad_glClearBufferfv +GLAD_API_CALL PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv; +#define glClearBufferiv glad_glClearBufferiv +GLAD_API_CALL PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv; +#define glClearBufferuiv glad_glClearBufferuiv +GLAD_API_CALL PFNGLCLEARCOLORPROC glad_glClearColor; +#define glClearColor glad_glClearColor +GLAD_API_CALL PFNGLCLEARCOLORXOESPROC glad_glClearColorxOES; +#define glClearColorxOES glad_glClearColorxOES +GLAD_API_CALL PFNGLCLEARDEPTHPROC glad_glClearDepth; +#define glClearDepth glad_glClearDepth +GLAD_API_CALL PFNGLCLEARDEPTHFPROC glad_glClearDepthf; +#define glClearDepthf glad_glClearDepthf +GLAD_API_CALL PFNGLCLEARDEPTHXOESPROC glad_glClearDepthxOES; +#define glClearDepthxOES glad_glClearDepthxOES +GLAD_API_CALL PFNGLCLEARNAMEDBUFFERDATAPROC glad_glClearNamedBufferData; +#define glClearNamedBufferData glad_glClearNamedBufferData +GLAD_API_CALL PFNGLCLEARNAMEDBUFFERSUBDATAPROC glad_glClearNamedBufferSubData; +#define glClearNamedBufferSubData glad_glClearNamedBufferSubData +GLAD_API_CALL PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glad_glClearNamedFramebufferfi; +#define glClearNamedFramebufferfi glad_glClearNamedFramebufferfi +GLAD_API_CALL PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glad_glClearNamedFramebufferfv; +#define glClearNamedFramebufferfv glad_glClearNamedFramebufferfv +GLAD_API_CALL PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glad_glClearNamedFramebufferiv; +#define glClearNamedFramebufferiv glad_glClearNamedFramebufferiv +GLAD_API_CALL PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glad_glClearNamedFramebufferuiv; +#define glClearNamedFramebufferuiv glad_glClearNamedFramebufferuiv +GLAD_API_CALL PFNGLCLEARSTENCILPROC glad_glClearStencil; +#define glClearStencil glad_glClearStencil +GLAD_API_CALL PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage; +#define glClearTexImage glad_glClearTexImage +GLAD_API_CALL PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage; +#define glClearTexSubImage glad_glClearTexSubImage +GLAD_API_CALL PFNGLCLIENTACTIVETEXTUREARBPROC glad_glClientActiveTextureARB; +#define glClientActiveTextureARB glad_glClientActiveTextureARB +GLAD_API_CALL PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync; +#define glClientWaitSync glad_glClientWaitSync +GLAD_API_CALL PFNGLCLIPPLANEXOESPROC glad_glClipPlanexOES; +#define glClipPlanexOES glad_glClipPlanexOES +GLAD_API_CALL PFNGLCOLOR3XOESPROC glad_glColor3xOES; +#define glColor3xOES glad_glColor3xOES +GLAD_API_CALL PFNGLCOLOR3XVOESPROC glad_glColor3xvOES; +#define glColor3xvOES glad_glColor3xvOES +GLAD_API_CALL PFNGLCOLOR4XOESPROC glad_glColor4xOES; +#define glColor4xOES glad_glColor4xOES +GLAD_API_CALL PFNGLCOLOR4XVOESPROC glad_glColor4xvOES; +#define glColor4xvOES glad_glColor4xvOES +GLAD_API_CALL PFNGLCOLORMASKPROC glad_glColorMask; +#define glColorMask glad_glColorMask +GLAD_API_CALL PFNGLCOLORMASKIPROC glad_glColorMaski; +#define glColorMaski glad_glColorMaski +GLAD_API_CALL PFNGLCOMPILESHADERPROC glad_glCompileShader; +#define glCompileShader glad_glCompileShader +GLAD_API_CALL PFNGLCOMPILESHADERARBPROC glad_glCompileShaderARB; +#define glCompileShaderARB glad_glCompileShaderARB +GLAD_API_CALL PFNGLCOMPILESHADERINCLUDEARBPROC glad_glCompileShaderIncludeARB; +#define glCompileShaderIncludeARB glad_glCompileShaderIncludeARB +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D; +#define glCompressedTexImage1D glad_glCompressedTexImage1D +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE1DARBPROC glad_glCompressedTexImage1DARB; +#define glCompressedTexImage1DARB glad_glCompressedTexImage1DARB +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D; +#define glCompressedTexImage2D glad_glCompressedTexImage2D +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glad_glCompressedTexImage2DARB; +#define glCompressedTexImage2DARB glad_glCompressedTexImage2DARB +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D; +#define glCompressedTexImage3D glad_glCompressedTexImage3D +GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE3DARBPROC glad_glCompressedTexImage3DARB; +#define glCompressedTexImage3DARB glad_glCompressedTexImage3DARB +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D; +#define glCompressedTexSubImage1D glad_glCompressedTexSubImage1D +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glad_glCompressedTexSubImage1DARB; +#define glCompressedTexSubImage1DARB glad_glCompressedTexSubImage1DARB +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D; +#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glad_glCompressedTexSubImage2DARB; +#define glCompressedTexSubImage2DARB glad_glCompressedTexSubImage2DARB +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D; +#define glCompressedTexSubImage3D glad_glCompressedTexSubImage3D +GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC glad_glCompressedTexSubImage3DARB; +#define glCompressedTexSubImage3DARB glad_glCompressedTexSubImage3DARB +GLAD_API_CALL PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glad_glCompressedTextureSubImage1D; +#define glCompressedTextureSubImage1D glad_glCompressedTextureSubImage1D +GLAD_API_CALL PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glad_glCompressedTextureSubImage2D; +#define glCompressedTextureSubImage2D glad_glCompressedTextureSubImage2D +GLAD_API_CALL PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glad_glCompressedTextureSubImage3D; +#define glCompressedTextureSubImage3D glad_glCompressedTextureSubImage3D +GLAD_API_CALL PFNGLCONVOLUTIONPARAMETERXOESPROC glad_glConvolutionParameterxOES; +#define glConvolutionParameterxOES glad_glConvolutionParameterxOES +GLAD_API_CALL PFNGLCONVOLUTIONPARAMETERXVOESPROC glad_glConvolutionParameterxvOES; +#define glConvolutionParameterxvOES glad_glConvolutionParameterxvOES +GLAD_API_CALL PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData; +#define glCopyBufferSubData glad_glCopyBufferSubData +GLAD_API_CALL PFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData; +#define glCopyImageSubData glad_glCopyImageSubData +GLAD_API_CALL PFNGLCOPYNAMEDBUFFERSUBDATAPROC glad_glCopyNamedBufferSubData; +#define glCopyNamedBufferSubData glad_glCopyNamedBufferSubData +GLAD_API_CALL PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D; +#define glCopyTexImage1D glad_glCopyTexImage1D +GLAD_API_CALL PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D; +#define glCopyTexImage2D glad_glCopyTexImage2D +GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D; +#define glCopyTexSubImage1D glad_glCopyTexSubImage1D +GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D; +#define glCopyTexSubImage2D glad_glCopyTexSubImage2D +GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D; +#define glCopyTexSubImage3D glad_glCopyTexSubImage3D +GLAD_API_CALL PFNGLCOPYTEXTURESUBIMAGE1DPROC glad_glCopyTextureSubImage1D; +#define glCopyTextureSubImage1D glad_glCopyTextureSubImage1D +GLAD_API_CALL PFNGLCOPYTEXTURESUBIMAGE2DPROC glad_glCopyTextureSubImage2D; +#define glCopyTextureSubImage2D glad_glCopyTextureSubImage2D +GLAD_API_CALL PFNGLCOPYTEXTURESUBIMAGE3DPROC glad_glCopyTextureSubImage3D; +#define glCopyTextureSubImage3D glad_glCopyTextureSubImage3D +GLAD_API_CALL PFNGLCREATEBUFFERSPROC glad_glCreateBuffers; +#define glCreateBuffers glad_glCreateBuffers +GLAD_API_CALL PFNGLCREATEFRAMEBUFFERSPROC glad_glCreateFramebuffers; +#define glCreateFramebuffers glad_glCreateFramebuffers +GLAD_API_CALL PFNGLCREATEPROGRAMPROC glad_glCreateProgram; +#define glCreateProgram glad_glCreateProgram +GLAD_API_CALL PFNGLCREATEPROGRAMOBJECTARBPROC glad_glCreateProgramObjectARB; +#define glCreateProgramObjectARB glad_glCreateProgramObjectARB +GLAD_API_CALL PFNGLCREATEPROGRAMPIPELINESPROC glad_glCreateProgramPipelines; +#define glCreateProgramPipelines glad_glCreateProgramPipelines +GLAD_API_CALL PFNGLCREATEQUERIESPROC glad_glCreateQueries; +#define glCreateQueries glad_glCreateQueries +GLAD_API_CALL PFNGLCREATERENDERBUFFERSPROC glad_glCreateRenderbuffers; +#define glCreateRenderbuffers glad_glCreateRenderbuffers +GLAD_API_CALL PFNGLCREATESAMPLERSPROC glad_glCreateSamplers; +#define glCreateSamplers glad_glCreateSamplers +GLAD_API_CALL PFNGLCREATESHADERPROC glad_glCreateShader; +#define glCreateShader glad_glCreateShader +GLAD_API_CALL PFNGLCREATESHADEROBJECTARBPROC glad_glCreateShaderObjectARB; +#define glCreateShaderObjectARB glad_glCreateShaderObjectARB +GLAD_API_CALL PFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv; +#define glCreateShaderProgramv glad_glCreateShaderProgramv +GLAD_API_CALL PFNGLCREATETEXTURESPROC glad_glCreateTextures; +#define glCreateTextures glad_glCreateTextures +GLAD_API_CALL PFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks; +#define glCreateTransformFeedbacks glad_glCreateTransformFeedbacks +GLAD_API_CALL PFNGLCREATEVERTEXARRAYSPROC glad_glCreateVertexArrays; +#define glCreateVertexArrays glad_glCreateVertexArrays +GLAD_API_CALL PFNGLCULLFACEPROC glad_glCullFace; +#define glCullFace glad_glCullFace +GLAD_API_CALL PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback; +#define glDebugMessageCallback glad_glDebugMessageCallback +GLAD_API_CALL PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB; +#define glDebugMessageCallbackARB glad_glDebugMessageCallbackARB +GLAD_API_CALL PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl; +#define glDebugMessageControl glad_glDebugMessageControl +GLAD_API_CALL PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB; +#define glDebugMessageControlARB glad_glDebugMessageControlARB +GLAD_API_CALL PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert; +#define glDebugMessageInsert glad_glDebugMessageInsert +GLAD_API_CALL PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB; +#define glDebugMessageInsertARB glad_glDebugMessageInsertARB +GLAD_API_CALL PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; +#define glDeleteBuffers glad_glDeleteBuffers +GLAD_API_CALL PFNGLDELETEBUFFERSARBPROC glad_glDeleteBuffersARB; +#define glDeleteBuffersARB glad_glDeleteBuffersARB +GLAD_API_CALL PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; +#define glDeleteFramebuffers glad_glDeleteFramebuffers +GLAD_API_CALL PFNGLDELETEFRAMEBUFFERSEXTPROC glad_glDeleteFramebuffersEXT; +#define glDeleteFramebuffersEXT glad_glDeleteFramebuffersEXT +GLAD_API_CALL PFNGLDELETENAMEDSTRINGARBPROC glad_glDeleteNamedStringARB; +#define glDeleteNamedStringARB glad_glDeleteNamedStringARB +GLAD_API_CALL PFNGLDELETEOBJECTARBPROC glad_glDeleteObjectARB; +#define glDeleteObjectARB glad_glDeleteObjectARB +GLAD_API_CALL PFNGLDELETEPROGRAMPROC glad_glDeleteProgram; +#define glDeleteProgram glad_glDeleteProgram +GLAD_API_CALL PFNGLDELETEPROGRAMPIPELINESPROC glad_glDeleteProgramPipelines; +#define glDeleteProgramPipelines glad_glDeleteProgramPipelines +GLAD_API_CALL PFNGLDELETEPROGRAMSARBPROC glad_glDeleteProgramsARB; +#define glDeleteProgramsARB glad_glDeleteProgramsARB +GLAD_API_CALL PFNGLDELETEQUERIESPROC glad_glDeleteQueries; +#define glDeleteQueries glad_glDeleteQueries +GLAD_API_CALL PFNGLDELETEQUERIESARBPROC glad_glDeleteQueriesARB; +#define glDeleteQueriesARB glad_glDeleteQueriesARB +GLAD_API_CALL PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; +#define glDeleteRenderbuffers glad_glDeleteRenderbuffers +GLAD_API_CALL PFNGLDELETERENDERBUFFERSEXTPROC glad_glDeleteRenderbuffersEXT; +#define glDeleteRenderbuffersEXT glad_glDeleteRenderbuffersEXT +GLAD_API_CALL PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers; +#define glDeleteSamplers glad_glDeleteSamplers +GLAD_API_CALL PFNGLDELETESHADERPROC glad_glDeleteShader; +#define glDeleteShader glad_glDeleteShader +GLAD_API_CALL PFNGLDELETESYNCPROC glad_glDeleteSync; +#define glDeleteSync glad_glDeleteSync +GLAD_API_CALL PFNGLDELETETEXTURESPROC glad_glDeleteTextures; +#define glDeleteTextures glad_glDeleteTextures +GLAD_API_CALL PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks; +#define glDeleteTransformFeedbacks glad_glDeleteTransformFeedbacks +GLAD_API_CALL PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays; +#define glDeleteVertexArrays glad_glDeleteVertexArrays +GLAD_API_CALL PFNGLDEPTHFUNCPROC glad_glDepthFunc; +#define glDepthFunc glad_glDepthFunc +GLAD_API_CALL PFNGLDEPTHMASKPROC glad_glDepthMask; +#define glDepthMask glad_glDepthMask +GLAD_API_CALL PFNGLDEPTHRANGEPROC glad_glDepthRange; +#define glDepthRange glad_glDepthRange +GLAD_API_CALL PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv; +#define glDepthRangeArrayv glad_glDepthRangeArrayv +GLAD_API_CALL PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed; +#define glDepthRangeIndexed glad_glDepthRangeIndexed +GLAD_API_CALL PFNGLDEPTHRANGEFPROC glad_glDepthRangef; +#define glDepthRangef glad_glDepthRangef +GLAD_API_CALL PFNGLDEPTHRANGEXOESPROC glad_glDepthRangexOES; +#define glDepthRangexOES glad_glDepthRangexOES +GLAD_API_CALL PFNGLDETACHOBJECTARBPROC glad_glDetachObjectARB; +#define glDetachObjectARB glad_glDetachObjectARB +GLAD_API_CALL PFNGLDETACHSHADERPROC glad_glDetachShader; +#define glDetachShader glad_glDetachShader +GLAD_API_CALL PFNGLDISABLEPROC glad_glDisable; +#define glDisable glad_glDisable +GLAD_API_CALL PFNGLDISABLEVERTEXARRAYATTRIBPROC glad_glDisableVertexArrayAttrib; +#define glDisableVertexArrayAttrib glad_glDisableVertexArrayAttrib +GLAD_API_CALL PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; +#define glDisableVertexAttribArray glad_glDisableVertexAttribArray +GLAD_API_CALL PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glad_glDisableVertexAttribArrayARB; +#define glDisableVertexAttribArrayARB glad_glDisableVertexAttribArrayARB +GLAD_API_CALL PFNGLDISABLEIPROC glad_glDisablei; +#define glDisablei glad_glDisablei +GLAD_API_CALL PFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute; +#define glDispatchCompute glad_glDispatchCompute +GLAD_API_CALL PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC glad_glDispatchComputeGroupSizeARB; +#define glDispatchComputeGroupSizeARB glad_glDispatchComputeGroupSizeARB +GLAD_API_CALL PFNGLDISPATCHCOMPUTEINDIRECTPROC glad_glDispatchComputeIndirect; +#define glDispatchComputeIndirect glad_glDispatchComputeIndirect +GLAD_API_CALL PFNGLDRAWARRAYSPROC glad_glDrawArrays; +#define glDrawArrays glad_glDrawArrays +GLAD_API_CALL PFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect; +#define glDrawArraysIndirect glad_glDrawArraysIndirect +GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced; +#define glDrawArraysInstanced glad_glDrawArraysInstanced +GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDARBPROC glad_glDrawArraysInstancedARB; +#define glDrawArraysInstancedARB glad_glDrawArraysInstancedARB +GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance; +#define glDrawArraysInstancedBaseInstance glad_glDrawArraysInstancedBaseInstance +GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT; +#define glDrawArraysInstancedEXT glad_glDrawArraysInstancedEXT +GLAD_API_CALL PFNGLDRAWBUFFERPROC glad_glDrawBuffer; +#define glDrawBuffer glad_glDrawBuffer +GLAD_API_CALL PFNGLDRAWBUFFERSPROC glad_glDrawBuffers; +#define glDrawBuffers glad_glDrawBuffers +GLAD_API_CALL PFNGLDRAWBUFFERSARBPROC glad_glDrawBuffersARB; +#define glDrawBuffersARB glad_glDrawBuffersARB +GLAD_API_CALL PFNGLDRAWELEMENTSPROC glad_glDrawElements; +#define glDrawElements glad_glDrawElements +GLAD_API_CALL PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex; +#define glDrawElementsBaseVertex glad_glDrawElementsBaseVertex +GLAD_API_CALL PFNGLDRAWELEMENTSINDIRECTPROC glad_glDrawElementsIndirect; +#define glDrawElementsIndirect glad_glDrawElementsIndirect +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced; +#define glDrawElementsInstanced glad_glDrawElementsInstanced +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDARBPROC glad_glDrawElementsInstancedARB; +#define glDrawElementsInstancedARB glad_glDrawElementsInstancedARB +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glad_glDrawElementsInstancedBaseInstance; +#define glDrawElementsInstancedBaseInstance glad_glDrawElementsInstancedBaseInstance +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex; +#define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance; +#define glDrawElementsInstancedBaseVertexBaseInstance glad_glDrawElementsInstancedBaseVertexBaseInstance +GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT; +#define glDrawElementsInstancedEXT glad_glDrawElementsInstancedEXT +GLAD_API_CALL PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements; +#define glDrawRangeElements glad_glDrawRangeElements +GLAD_API_CALL PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex; +#define glDrawRangeElementsBaseVertex glad_glDrawRangeElementsBaseVertex +GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKPROC glad_glDrawTransformFeedback; +#define glDrawTransformFeedback glad_glDrawTransformFeedback +GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glad_glDrawTransformFeedbackInstanced; +#define glDrawTransformFeedbackInstanced glad_glDrawTransformFeedbackInstanced +GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glad_glDrawTransformFeedbackStream; +#define glDrawTransformFeedbackStream glad_glDrawTransformFeedbackStream +GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glad_glDrawTransformFeedbackStreamInstanced; +#define glDrawTransformFeedbackStreamInstanced glad_glDrawTransformFeedbackStreamInstanced +GLAD_API_CALL PFNGLENABLEPROC glad_glEnable; +#define glEnable glad_glEnable +GLAD_API_CALL PFNGLENABLEVERTEXARRAYATTRIBPROC glad_glEnableVertexArrayAttrib; +#define glEnableVertexArrayAttrib glad_glEnableVertexArrayAttrib +GLAD_API_CALL PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; +#define glEnableVertexAttribArray glad_glEnableVertexAttribArray +GLAD_API_CALL PFNGLENABLEVERTEXATTRIBARRAYARBPROC glad_glEnableVertexAttribArrayARB; +#define glEnableVertexAttribArrayARB glad_glEnableVertexAttribArrayARB +GLAD_API_CALL PFNGLENABLEIPROC glad_glEnablei; +#define glEnablei glad_glEnablei +GLAD_API_CALL PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender; +#define glEndConditionalRender glad_glEndConditionalRender +GLAD_API_CALL PFNGLENDQUERYPROC glad_glEndQuery; +#define glEndQuery glad_glEndQuery +GLAD_API_CALL PFNGLENDQUERYARBPROC glad_glEndQueryARB; +#define glEndQueryARB glad_glEndQueryARB +GLAD_API_CALL PFNGLENDQUERYINDEXEDPROC glad_glEndQueryIndexed; +#define glEndQueryIndexed glad_glEndQueryIndexed +GLAD_API_CALL PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback; +#define glEndTransformFeedback glad_glEndTransformFeedback +GLAD_API_CALL PFNGLEVALCOORD1XOESPROC glad_glEvalCoord1xOES; +#define glEvalCoord1xOES glad_glEvalCoord1xOES +GLAD_API_CALL PFNGLEVALCOORD1XVOESPROC glad_glEvalCoord1xvOES; +#define glEvalCoord1xvOES glad_glEvalCoord1xvOES +GLAD_API_CALL PFNGLEVALCOORD2XOESPROC glad_glEvalCoord2xOES; +#define glEvalCoord2xOES glad_glEvalCoord2xOES +GLAD_API_CALL PFNGLEVALCOORD2XVOESPROC glad_glEvalCoord2xvOES; +#define glEvalCoord2xvOES glad_glEvalCoord2xvOES +GLAD_API_CALL PFNGLEVALUATEDEPTHVALUESARBPROC glad_glEvaluateDepthValuesARB; +#define glEvaluateDepthValuesARB glad_glEvaluateDepthValuesARB +GLAD_API_CALL PFNGLFEEDBACKBUFFERXOESPROC glad_glFeedbackBufferxOES; +#define glFeedbackBufferxOES glad_glFeedbackBufferxOES +GLAD_API_CALL PFNGLFENCESYNCPROC glad_glFenceSync; +#define glFenceSync glad_glFenceSync +GLAD_API_CALL PFNGLFINISHPROC glad_glFinish; +#define glFinish glad_glFinish +GLAD_API_CALL PFNGLFLUSHPROC glad_glFlush; +#define glFlush glad_glFlush +GLAD_API_CALL PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange; +#define glFlushMappedBufferRange glad_glFlushMappedBufferRange +GLAD_API_CALL PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glad_glFlushMappedNamedBufferRange; +#define glFlushMappedNamedBufferRange glad_glFlushMappedNamedBufferRange +GLAD_API_CALL PFNGLFOGCOORDPOINTEREXTPROC glad_glFogCoordPointerEXT; +#define glFogCoordPointerEXT glad_glFogCoordPointerEXT +GLAD_API_CALL PFNGLFOGCOORDDEXTPROC glad_glFogCoorddEXT; +#define glFogCoorddEXT glad_glFogCoorddEXT +GLAD_API_CALL PFNGLFOGCOORDDVEXTPROC glad_glFogCoorddvEXT; +#define glFogCoorddvEXT glad_glFogCoorddvEXT +GLAD_API_CALL PFNGLFOGCOORDFEXTPROC glad_glFogCoordfEXT; +#define glFogCoordfEXT glad_glFogCoordfEXT +GLAD_API_CALL PFNGLFOGCOORDFVEXTPROC glad_glFogCoordfvEXT; +#define glFogCoordfvEXT glad_glFogCoordfvEXT +GLAD_API_CALL PFNGLFOGXOESPROC glad_glFogxOES; +#define glFogxOES glad_glFogxOES +GLAD_API_CALL PFNGLFOGXVOESPROC glad_glFogxvOES; +#define glFogxvOES glad_glFogxvOES +GLAD_API_CALL PFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri; +#define glFramebufferParameteri glad_glFramebufferParameteri +GLAD_API_CALL PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; +#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer +GLAD_API_CALL PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glad_glFramebufferRenderbufferEXT; +#define glFramebufferRenderbufferEXT glad_glFramebufferRenderbufferEXT +GLAD_API_CALL PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glad_glFramebufferSampleLocationsfvARB; +#define glFramebufferSampleLocationsfvARB glad_glFramebufferSampleLocationsfvARB +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture; +#define glFramebufferTexture glad_glFramebufferTexture +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D; +#define glFramebufferTexture1D glad_glFramebufferTexture1D +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glad_glFramebufferTexture1DEXT; +#define glFramebufferTexture1DEXT glad_glFramebufferTexture1DEXT +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D; +#define glFramebufferTexture2D glad_glFramebufferTexture2D +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glad_glFramebufferTexture2DEXT; +#define glFramebufferTexture2DEXT glad_glFramebufferTexture2DEXT +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D; +#define glFramebufferTexture3D glad_glFramebufferTexture3D +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glad_glFramebufferTexture3DEXT; +#define glFramebufferTexture3DEXT glad_glFramebufferTexture3DEXT +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTUREARBPROC glad_glFramebufferTextureARB; +#define glFramebufferTextureARB glad_glFramebufferTextureARB +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTUREFACEARBPROC glad_glFramebufferTextureFaceARB; +#define glFramebufferTextureFaceARB glad_glFramebufferTextureFaceARB +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer; +#define glFramebufferTextureLayer glad_glFramebufferTextureLayer +GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURELAYERARBPROC glad_glFramebufferTextureLayerARB; +#define glFramebufferTextureLayerARB glad_glFramebufferTextureLayerARB +GLAD_API_CALL PFNGLFRONTFACEPROC glad_glFrontFace; +#define glFrontFace glad_glFrontFace +GLAD_API_CALL PFNGLFRUSTUMXOESPROC glad_glFrustumxOES; +#define glFrustumxOES glad_glFrustumxOES +GLAD_API_CALL PFNGLGENBUFFERSPROC glad_glGenBuffers; +#define glGenBuffers glad_glGenBuffers +GLAD_API_CALL PFNGLGENBUFFERSARBPROC glad_glGenBuffersARB; +#define glGenBuffersARB glad_glGenBuffersARB +GLAD_API_CALL PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; +#define glGenFramebuffers glad_glGenFramebuffers +GLAD_API_CALL PFNGLGENFRAMEBUFFERSEXTPROC glad_glGenFramebuffersEXT; +#define glGenFramebuffersEXT glad_glGenFramebuffersEXT +GLAD_API_CALL PFNGLGENPROGRAMPIPELINESPROC glad_glGenProgramPipelines; +#define glGenProgramPipelines glad_glGenProgramPipelines +GLAD_API_CALL PFNGLGENPROGRAMSARBPROC glad_glGenProgramsARB; +#define glGenProgramsARB glad_glGenProgramsARB +GLAD_API_CALL PFNGLGENQUERIESPROC glad_glGenQueries; +#define glGenQueries glad_glGenQueries +GLAD_API_CALL PFNGLGENQUERIESARBPROC glad_glGenQueriesARB; +#define glGenQueriesARB glad_glGenQueriesARB +GLAD_API_CALL PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; +#define glGenRenderbuffers glad_glGenRenderbuffers +GLAD_API_CALL PFNGLGENRENDERBUFFERSEXTPROC glad_glGenRenderbuffersEXT; +#define glGenRenderbuffersEXT glad_glGenRenderbuffersEXT +GLAD_API_CALL PFNGLGENSAMPLERSPROC glad_glGenSamplers; +#define glGenSamplers glad_glGenSamplers +GLAD_API_CALL PFNGLGENTEXTURESPROC glad_glGenTextures; +#define glGenTextures glad_glGenTextures +GLAD_API_CALL PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks; +#define glGenTransformFeedbacks glad_glGenTransformFeedbacks +GLAD_API_CALL PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays; +#define glGenVertexArrays glad_glGenVertexArrays +GLAD_API_CALL PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap; +#define glGenerateMipmap glad_glGenerateMipmap +GLAD_API_CALL PFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT; +#define glGenerateMipmapEXT glad_glGenerateMipmapEXT +GLAD_API_CALL PFNGLGENERATETEXTUREMIPMAPPROC glad_glGenerateTextureMipmap; +#define glGenerateTextureMipmap glad_glGenerateTextureMipmap +GLAD_API_CALL PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glad_glGetActiveAtomicCounterBufferiv; +#define glGetActiveAtomicCounterBufferiv glad_glGetActiveAtomicCounterBufferiv +GLAD_API_CALL PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib; +#define glGetActiveAttrib glad_glGetActiveAttrib +GLAD_API_CALL PFNGLGETACTIVEATTRIBARBPROC glad_glGetActiveAttribARB; +#define glGetActiveAttribARB glad_glGetActiveAttribARB +GLAD_API_CALL PFNGLGETACTIVESUBROUTINENAMEPROC glad_glGetActiveSubroutineName; +#define glGetActiveSubroutineName glad_glGetActiveSubroutineName +GLAD_API_CALL PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glad_glGetActiveSubroutineUniformName; +#define glGetActiveSubroutineUniformName glad_glGetActiveSubroutineUniformName +GLAD_API_CALL PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glad_glGetActiveSubroutineUniformiv; +#define glGetActiveSubroutineUniformiv glad_glGetActiveSubroutineUniformiv +GLAD_API_CALL PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform; +#define glGetActiveUniform glad_glGetActiveUniform +GLAD_API_CALL PFNGLGETACTIVEUNIFORMARBPROC glad_glGetActiveUniformARB; +#define glGetActiveUniformARB glad_glGetActiveUniformARB +GLAD_API_CALL PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName; +#define glGetActiveUniformBlockName glad_glGetActiveUniformBlockName +GLAD_API_CALL PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv; +#define glGetActiveUniformBlockiv glad_glGetActiveUniformBlockiv +GLAD_API_CALL PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName; +#define glGetActiveUniformName glad_glGetActiveUniformName +GLAD_API_CALL PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv; +#define glGetActiveUniformsiv glad_glGetActiveUniformsiv +GLAD_API_CALL PFNGLGETATTACHEDOBJECTSARBPROC glad_glGetAttachedObjectsARB; +#define glGetAttachedObjectsARB glad_glGetAttachedObjectsARB +GLAD_API_CALL PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders; +#define glGetAttachedShaders glad_glGetAttachedShaders +GLAD_API_CALL PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation; +#define glGetAttribLocation glad_glGetAttribLocation +GLAD_API_CALL PFNGLGETATTRIBLOCATIONARBPROC glad_glGetAttribLocationARB; +#define glGetAttribLocationARB glad_glGetAttribLocationARB +GLAD_API_CALL PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v; +#define glGetBooleani_v glad_glGetBooleani_v +GLAD_API_CALL PFNGLGETBOOLEANVPROC glad_glGetBooleanv; +#define glGetBooleanv glad_glGetBooleanv +GLAD_API_CALL PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v; +#define glGetBufferParameteri64v glad_glGetBufferParameteri64v +GLAD_API_CALL PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; +#define glGetBufferParameteriv glad_glGetBufferParameteriv +GLAD_API_CALL PFNGLGETBUFFERPARAMETERIVARBPROC glad_glGetBufferParameterivARB; +#define glGetBufferParameterivARB glad_glGetBufferParameterivARB +GLAD_API_CALL PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv; +#define glGetBufferPointerv glad_glGetBufferPointerv +GLAD_API_CALL PFNGLGETBUFFERPOINTERVARBPROC glad_glGetBufferPointervARB; +#define glGetBufferPointervARB glad_glGetBufferPointervARB +GLAD_API_CALL PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData; +#define glGetBufferSubData glad_glGetBufferSubData +GLAD_API_CALL PFNGLGETBUFFERSUBDATAARBPROC glad_glGetBufferSubDataARB; +#define glGetBufferSubDataARB glad_glGetBufferSubDataARB +GLAD_API_CALL PFNGLGETCLIPPLANEXOESPROC glad_glGetClipPlanexOES; +#define glGetClipPlanexOES glad_glGetClipPlanexOES +GLAD_API_CALL PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage; +#define glGetCompressedTexImage glad_glGetCompressedTexImage +GLAD_API_CALL PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glad_glGetCompressedTexImageARB; +#define glGetCompressedTexImageARB glad_glGetCompressedTexImageARB +GLAD_API_CALL PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glad_glGetCompressedTextureImage; +#define glGetCompressedTextureImage glad_glGetCompressedTextureImage +GLAD_API_CALL PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage; +#define glGetCompressedTextureSubImage glad_glGetCompressedTextureSubImage +GLAD_API_CALL PFNGLGETCONVOLUTIONPARAMETERXVOESPROC glad_glGetConvolutionParameterxvOES; +#define glGetConvolutionParameterxvOES glad_glGetConvolutionParameterxvOES +GLAD_API_CALL PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog; +#define glGetDebugMessageLog glad_glGetDebugMessageLog +GLAD_API_CALL PFNGLGETDEBUGMESSAGELOGARBPROC glad_glGetDebugMessageLogARB; +#define glGetDebugMessageLogARB glad_glGetDebugMessageLogARB +GLAD_API_CALL PFNGLGETDOUBLEI_VPROC glad_glGetDoublei_v; +#define glGetDoublei_v glad_glGetDoublei_v +GLAD_API_CALL PFNGLGETDOUBLEVPROC glad_glGetDoublev; +#define glGetDoublev glad_glGetDoublev +GLAD_API_CALL PFNGLGETERRORPROC glad_glGetError; +#define glGetError glad_glGetError +GLAD_API_CALL PFNGLGETFIXEDVOESPROC glad_glGetFixedvOES; +#define glGetFixedvOES glad_glGetFixedvOES +GLAD_API_CALL PFNGLGETFLOATI_VPROC glad_glGetFloati_v; +#define glGetFloati_v glad_glGetFloati_v +GLAD_API_CALL PFNGLGETFLOATVPROC glad_glGetFloatv; +#define glGetFloatv glad_glGetFloatv +GLAD_API_CALL PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex; +#define glGetFragDataIndex glad_glGetFragDataIndex +GLAD_API_CALL PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation; +#define glGetFragDataLocation glad_glGetFragDataLocation +GLAD_API_CALL PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; +#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv +GLAD_API_CALL PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glad_glGetFramebufferAttachmentParameterivEXT; +#define glGetFramebufferAttachmentParameterivEXT glad_glGetFramebufferAttachmentParameterivEXT +GLAD_API_CALL PFNGLGETFRAMEBUFFERPARAMETERIVPROC glad_glGetFramebufferParameteriv; +#define glGetFramebufferParameteriv glad_glGetFramebufferParameteriv +GLAD_API_CALL PFNGLGETHANDLEARBPROC glad_glGetHandleARB; +#define glGetHandleARB glad_glGetHandleARB +GLAD_API_CALL PFNGLGETHISTOGRAMPARAMETERXVOESPROC glad_glGetHistogramParameterxvOES; +#define glGetHistogramParameterxvOES glad_glGetHistogramParameterxvOES +GLAD_API_CALL PFNGLGETINFOLOGARBPROC glad_glGetInfoLogARB; +#define glGetInfoLogARB glad_glGetInfoLogARB +GLAD_API_CALL PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v; +#define glGetInteger64i_v glad_glGetInteger64i_v +GLAD_API_CALL PFNGLGETINTEGER64VPROC glad_glGetInteger64v; +#define glGetInteger64v glad_glGetInteger64v +GLAD_API_CALL PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v; +#define glGetIntegeri_v glad_glGetIntegeri_v +GLAD_API_CALL PFNGLGETINTEGERVPROC glad_glGetIntegerv; +#define glGetIntegerv glad_glGetIntegerv +GLAD_API_CALL PFNGLGETINTERNALFORMATI64VPROC glad_glGetInternalformati64v; +#define glGetInternalformati64v glad_glGetInternalformati64v +GLAD_API_CALL PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ; +#define glGetInternalformativ glad_glGetInternalformativ +GLAD_API_CALL PFNGLGETLIGHTXOESPROC glad_glGetLightxOES; +#define glGetLightxOES glad_glGetLightxOES +GLAD_API_CALL PFNGLGETMAPXVOESPROC glad_glGetMapxvOES; +#define glGetMapxvOES glad_glGetMapxvOES +GLAD_API_CALL PFNGLGETMATERIALXOESPROC glad_glGetMaterialxOES; +#define glGetMaterialxOES glad_glGetMaterialxOES +GLAD_API_CALL PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv; +#define glGetMultisamplefv glad_glGetMultisamplefv +GLAD_API_CALL PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glad_glGetNamedBufferParameteri64v; +#define glGetNamedBufferParameteri64v glad_glGetNamedBufferParameteri64v +GLAD_API_CALL PFNGLGETNAMEDBUFFERPARAMETERIVPROC glad_glGetNamedBufferParameteriv; +#define glGetNamedBufferParameteriv glad_glGetNamedBufferParameteriv +GLAD_API_CALL PFNGLGETNAMEDBUFFERPOINTERVPROC glad_glGetNamedBufferPointerv; +#define glGetNamedBufferPointerv glad_glGetNamedBufferPointerv +GLAD_API_CALL PFNGLGETNAMEDBUFFERSUBDATAPROC glad_glGetNamedBufferSubData; +#define glGetNamedBufferSubData glad_glGetNamedBufferSubData +GLAD_API_CALL PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetNamedFramebufferAttachmentParameteriv; +#define glGetNamedFramebufferAttachmentParameteriv glad_glGetNamedFramebufferAttachmentParameteriv +GLAD_API_CALL PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glad_glGetNamedFramebufferParameteriv; +#define glGetNamedFramebufferParameteriv glad_glGetNamedFramebufferParameteriv +GLAD_API_CALL PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glad_glGetNamedRenderbufferParameteriv; +#define glGetNamedRenderbufferParameteriv glad_glGetNamedRenderbufferParameteriv +GLAD_API_CALL PFNGLGETNAMEDSTRINGARBPROC glad_glGetNamedStringARB; +#define glGetNamedStringARB glad_glGetNamedStringARB +GLAD_API_CALL PFNGLGETNAMEDSTRINGIVARBPROC glad_glGetNamedStringivARB; +#define glGetNamedStringivARB glad_glGetNamedStringivARB +GLAD_API_CALL PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel; +#define glGetObjectLabel glad_glGetObjectLabel +GLAD_API_CALL PFNGLGETOBJECTPARAMETERFVARBPROC glad_glGetObjectParameterfvARB; +#define glGetObjectParameterfvARB glad_glGetObjectParameterfvARB +GLAD_API_CALL PFNGLGETOBJECTPARAMETERIVARBPROC glad_glGetObjectParameterivARB; +#define glGetObjectParameterivARB glad_glGetObjectParameterivARB +GLAD_API_CALL PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel; +#define glGetObjectPtrLabel glad_glGetObjectPtrLabel +GLAD_API_CALL PFNGLGETPIXELMAPXVPROC glad_glGetPixelMapxv; +#define glGetPixelMapxv glad_glGetPixelMapxv +GLAD_API_CALL PFNGLGETPOINTERVPROC glad_glGetPointerv; +#define glGetPointerv glad_glGetPointerv +GLAD_API_CALL PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary; +#define glGetProgramBinary glad_glGetProgramBinary +GLAD_API_CALL PFNGLGETPROGRAMENVPARAMETERDVARBPROC glad_glGetProgramEnvParameterdvARB; +#define glGetProgramEnvParameterdvARB glad_glGetProgramEnvParameterdvARB +GLAD_API_CALL PFNGLGETPROGRAMENVPARAMETERFVARBPROC glad_glGetProgramEnvParameterfvARB; +#define glGetProgramEnvParameterfvARB glad_glGetProgramEnvParameterfvARB +GLAD_API_CALL PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog; +#define glGetProgramInfoLog glad_glGetProgramInfoLog +GLAD_API_CALL PFNGLGETPROGRAMINTERFACEIVPROC glad_glGetProgramInterfaceiv; +#define glGetProgramInterfaceiv glad_glGetProgramInterfaceiv +GLAD_API_CALL PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glad_glGetProgramLocalParameterdvARB; +#define glGetProgramLocalParameterdvARB glad_glGetProgramLocalParameterdvARB +GLAD_API_CALL PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glad_glGetProgramLocalParameterfvARB; +#define glGetProgramLocalParameterfvARB glad_glGetProgramLocalParameterfvARB +GLAD_API_CALL PFNGLGETPROGRAMPIPELINEINFOLOGPROC glad_glGetProgramPipelineInfoLog; +#define glGetProgramPipelineInfoLog glad_glGetProgramPipelineInfoLog +GLAD_API_CALL PFNGLGETPROGRAMPIPELINEIVPROC glad_glGetProgramPipelineiv; +#define glGetProgramPipelineiv glad_glGetProgramPipelineiv +GLAD_API_CALL PFNGLGETPROGRAMRESOURCEINDEXPROC glad_glGetProgramResourceIndex; +#define glGetProgramResourceIndex glad_glGetProgramResourceIndex +GLAD_API_CALL PFNGLGETPROGRAMRESOURCELOCATIONPROC glad_glGetProgramResourceLocation; +#define glGetProgramResourceLocation glad_glGetProgramResourceLocation +GLAD_API_CALL PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glad_glGetProgramResourceLocationIndex; +#define glGetProgramResourceLocationIndex glad_glGetProgramResourceLocationIndex +GLAD_API_CALL PFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName; +#define glGetProgramResourceName glad_glGetProgramResourceName +GLAD_API_CALL PFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv; +#define glGetProgramResourceiv glad_glGetProgramResourceiv +GLAD_API_CALL PFNGLGETPROGRAMSTAGEIVPROC glad_glGetProgramStageiv; +#define glGetProgramStageiv glad_glGetProgramStageiv +GLAD_API_CALL PFNGLGETPROGRAMSTRINGARBPROC glad_glGetProgramStringARB; +#define glGetProgramStringARB glad_glGetProgramStringARB +GLAD_API_CALL PFNGLGETPROGRAMIVPROC glad_glGetProgramiv; +#define glGetProgramiv glad_glGetProgramiv +GLAD_API_CALL PFNGLGETPROGRAMIVARBPROC glad_glGetProgramivARB; +#define glGetProgramivARB glad_glGetProgramivARB +GLAD_API_CALL PFNGLGETQUERYBUFFEROBJECTI64VPROC glad_glGetQueryBufferObjecti64v; +#define glGetQueryBufferObjecti64v glad_glGetQueryBufferObjecti64v +GLAD_API_CALL PFNGLGETQUERYBUFFEROBJECTIVPROC glad_glGetQueryBufferObjectiv; +#define glGetQueryBufferObjectiv glad_glGetQueryBufferObjectiv +GLAD_API_CALL PFNGLGETQUERYBUFFEROBJECTUI64VPROC glad_glGetQueryBufferObjectui64v; +#define glGetQueryBufferObjectui64v glad_glGetQueryBufferObjectui64v +GLAD_API_CALL PFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv; +#define glGetQueryBufferObjectuiv glad_glGetQueryBufferObjectuiv +GLAD_API_CALL PFNGLGETQUERYINDEXEDIVPROC glad_glGetQueryIndexediv; +#define glGetQueryIndexediv glad_glGetQueryIndexediv +GLAD_API_CALL PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v; +#define glGetQueryObjecti64v glad_glGetQueryObjecti64v +GLAD_API_CALL PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv; +#define glGetQueryObjectiv glad_glGetQueryObjectiv +GLAD_API_CALL PFNGLGETQUERYOBJECTIVARBPROC glad_glGetQueryObjectivARB; +#define glGetQueryObjectivARB glad_glGetQueryObjectivARB +GLAD_API_CALL PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v; +#define glGetQueryObjectui64v glad_glGetQueryObjectui64v +GLAD_API_CALL PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv; +#define glGetQueryObjectuiv glad_glGetQueryObjectuiv +GLAD_API_CALL PFNGLGETQUERYOBJECTUIVARBPROC glad_glGetQueryObjectuivARB; +#define glGetQueryObjectuivARB glad_glGetQueryObjectuivARB +GLAD_API_CALL PFNGLGETQUERYIVPROC glad_glGetQueryiv; +#define glGetQueryiv glad_glGetQueryiv +GLAD_API_CALL PFNGLGETQUERYIVARBPROC glad_glGetQueryivARB; +#define glGetQueryivARB glad_glGetQueryivARB +GLAD_API_CALL PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; +#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv +GLAD_API_CALL PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glad_glGetRenderbufferParameterivEXT; +#define glGetRenderbufferParameterivEXT glad_glGetRenderbufferParameterivEXT +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv; +#define glGetSamplerParameterIiv glad_glGetSamplerParameterIiv +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv; +#define glGetSamplerParameterIuiv glad_glGetSamplerParameterIuiv +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv; +#define glGetSamplerParameterfv glad_glGetSamplerParameterfv +GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv; +#define glGetSamplerParameteriv glad_glGetSamplerParameteriv +GLAD_API_CALL PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog; +#define glGetShaderInfoLog glad_glGetShaderInfoLog +GLAD_API_CALL PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat; +#define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat +GLAD_API_CALL PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; +#define glGetShaderSource glad_glGetShaderSource +GLAD_API_CALL PFNGLGETSHADERSOURCEARBPROC glad_glGetShaderSourceARB; +#define glGetShaderSourceARB glad_glGetShaderSourceARB +GLAD_API_CALL PFNGLGETSHADERIVPROC glad_glGetShaderiv; +#define glGetShaderiv glad_glGetShaderiv +GLAD_API_CALL PFNGLGETSTRINGPROC glad_glGetString; +#define glGetString glad_glGetString +GLAD_API_CALL PFNGLGETSTRINGIPROC glad_glGetStringi; +#define glGetStringi glad_glGetStringi +GLAD_API_CALL PFNGLGETSUBROUTINEINDEXPROC glad_glGetSubroutineIndex; +#define glGetSubroutineIndex glad_glGetSubroutineIndex +GLAD_API_CALL PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glad_glGetSubroutineUniformLocation; +#define glGetSubroutineUniformLocation glad_glGetSubroutineUniformLocation +GLAD_API_CALL PFNGLGETSYNCIVPROC glad_glGetSynciv; +#define glGetSynciv glad_glGetSynciv +GLAD_API_CALL PFNGLGETTEXENVXVOESPROC glad_glGetTexEnvxvOES; +#define glGetTexEnvxvOES glad_glGetTexEnvxvOES +GLAD_API_CALL PFNGLGETTEXGENXVOESPROC glad_glGetTexGenxvOES; +#define glGetTexGenxvOES glad_glGetTexGenxvOES +GLAD_API_CALL PFNGLGETTEXIMAGEPROC glad_glGetTexImage; +#define glGetTexImage glad_glGetTexImage +GLAD_API_CALL PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv; +#define glGetTexLevelParameterfv glad_glGetTexLevelParameterfv +GLAD_API_CALL PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv; +#define glGetTexLevelParameteriv glad_glGetTexLevelParameteriv +GLAD_API_CALL PFNGLGETTEXLEVELPARAMETERXVOESPROC glad_glGetTexLevelParameterxvOES; +#define glGetTexLevelParameterxvOES glad_glGetTexLevelParameterxvOES +GLAD_API_CALL PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv; +#define glGetTexParameterIiv glad_glGetTexParameterIiv +GLAD_API_CALL PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv; +#define glGetTexParameterIuiv glad_glGetTexParameterIuiv +GLAD_API_CALL PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv; +#define glGetTexParameterfv glad_glGetTexParameterfv +GLAD_API_CALL PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv; +#define glGetTexParameteriv glad_glGetTexParameteriv +GLAD_API_CALL PFNGLGETTEXPARAMETERXVOESPROC glad_glGetTexParameterxvOES; +#define glGetTexParameterxvOES glad_glGetTexParameterxvOES +GLAD_API_CALL PFNGLGETTEXTUREIMAGEPROC glad_glGetTextureImage; +#define glGetTextureImage glad_glGetTextureImage +GLAD_API_CALL PFNGLGETTEXTURELEVELPARAMETERFVPROC glad_glGetTextureLevelParameterfv; +#define glGetTextureLevelParameterfv glad_glGetTextureLevelParameterfv +GLAD_API_CALL PFNGLGETTEXTURELEVELPARAMETERIVPROC glad_glGetTextureLevelParameteriv; +#define glGetTextureLevelParameteriv glad_glGetTextureLevelParameteriv +GLAD_API_CALL PFNGLGETTEXTUREPARAMETERIIVPROC glad_glGetTextureParameterIiv; +#define glGetTextureParameterIiv glad_glGetTextureParameterIiv +GLAD_API_CALL PFNGLGETTEXTUREPARAMETERIUIVPROC glad_glGetTextureParameterIuiv; +#define glGetTextureParameterIuiv glad_glGetTextureParameterIuiv +GLAD_API_CALL PFNGLGETTEXTUREPARAMETERFVPROC glad_glGetTextureParameterfv; +#define glGetTextureParameterfv glad_glGetTextureParameterfv +GLAD_API_CALL PFNGLGETTEXTUREPARAMETERIVPROC glad_glGetTextureParameteriv; +#define glGetTextureParameteriv glad_glGetTextureParameteriv +GLAD_API_CALL PFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage; +#define glGetTextureSubImage glad_glGetTextureSubImage +GLAD_API_CALL PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying; +#define glGetTransformFeedbackVarying glad_glGetTransformFeedbackVarying +GLAD_API_CALL PFNGLGETTRANSFORMFEEDBACKI64_VPROC glad_glGetTransformFeedbacki64_v; +#define glGetTransformFeedbacki64_v glad_glGetTransformFeedbacki64_v +GLAD_API_CALL PFNGLGETTRANSFORMFEEDBACKI_VPROC glad_glGetTransformFeedbacki_v; +#define glGetTransformFeedbacki_v glad_glGetTransformFeedbacki_v +GLAD_API_CALL PFNGLGETTRANSFORMFEEDBACKIVPROC glad_glGetTransformFeedbackiv; +#define glGetTransformFeedbackiv glad_glGetTransformFeedbackiv +GLAD_API_CALL PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex; +#define glGetUniformBlockIndex glad_glGetUniformBlockIndex +GLAD_API_CALL PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices; +#define glGetUniformIndices glad_glGetUniformIndices +GLAD_API_CALL PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; +#define glGetUniformLocation glad_glGetUniformLocation +GLAD_API_CALL PFNGLGETUNIFORMLOCATIONARBPROC glad_glGetUniformLocationARB; +#define glGetUniformLocationARB glad_glGetUniformLocationARB +GLAD_API_CALL PFNGLGETUNIFORMSUBROUTINEUIVPROC glad_glGetUniformSubroutineuiv; +#define glGetUniformSubroutineuiv glad_glGetUniformSubroutineuiv +GLAD_API_CALL PFNGLGETUNIFORMDVPROC glad_glGetUniformdv; +#define glGetUniformdv glad_glGetUniformdv +GLAD_API_CALL PFNGLGETUNIFORMFVPROC glad_glGetUniformfv; +#define glGetUniformfv glad_glGetUniformfv +GLAD_API_CALL PFNGLGETUNIFORMFVARBPROC glad_glGetUniformfvARB; +#define glGetUniformfvARB glad_glGetUniformfvARB +GLAD_API_CALL PFNGLGETUNIFORMI64VARBPROC glad_glGetUniformi64vARB; +#define glGetUniformi64vARB glad_glGetUniformi64vARB +GLAD_API_CALL PFNGLGETUNIFORMIVPROC glad_glGetUniformiv; +#define glGetUniformiv glad_glGetUniformiv +GLAD_API_CALL PFNGLGETUNIFORMIVARBPROC glad_glGetUniformivARB; +#define glGetUniformivARB glad_glGetUniformivARB +GLAD_API_CALL PFNGLGETUNIFORMUI64VARBPROC glad_glGetUniformui64vARB; +#define glGetUniformui64vARB glad_glGetUniformui64vARB +GLAD_API_CALL PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv; +#define glGetUniformuiv glad_glGetUniformuiv +GLAD_API_CALL PFNGLGETVERTEXARRAYINDEXED64IVPROC glad_glGetVertexArrayIndexed64iv; +#define glGetVertexArrayIndexed64iv glad_glGetVertexArrayIndexed64iv +GLAD_API_CALL PFNGLGETVERTEXARRAYINDEXEDIVPROC glad_glGetVertexArrayIndexediv; +#define glGetVertexArrayIndexediv glad_glGetVertexArrayIndexediv +GLAD_API_CALL PFNGLGETVERTEXARRAYIVPROC glad_glGetVertexArrayiv; +#define glGetVertexArrayiv glad_glGetVertexArrayiv +GLAD_API_CALL PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv; +#define glGetVertexAttribIiv glad_glGetVertexAttribIiv +GLAD_API_CALL PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv; +#define glGetVertexAttribIuiv glad_glGetVertexAttribIuiv +GLAD_API_CALL PFNGLGETVERTEXATTRIBLDVPROC glad_glGetVertexAttribLdv; +#define glGetVertexAttribLdv glad_glGetVertexAttribLdv +GLAD_API_CALL PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv; +#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv +GLAD_API_CALL PFNGLGETVERTEXATTRIBPOINTERVARBPROC glad_glGetVertexAttribPointervARB; +#define glGetVertexAttribPointervARB glad_glGetVertexAttribPointervARB +GLAD_API_CALL PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv; +#define glGetVertexAttribdv glad_glGetVertexAttribdv +GLAD_API_CALL PFNGLGETVERTEXATTRIBDVARBPROC glad_glGetVertexAttribdvARB; +#define glGetVertexAttribdvARB glad_glGetVertexAttribdvARB +GLAD_API_CALL PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv; +#define glGetVertexAttribfv glad_glGetVertexAttribfv +GLAD_API_CALL PFNGLGETVERTEXATTRIBFVARBPROC glad_glGetVertexAttribfvARB; +#define glGetVertexAttribfvARB glad_glGetVertexAttribfvARB +GLAD_API_CALL PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; +#define glGetVertexAttribiv glad_glGetVertexAttribiv +GLAD_API_CALL PFNGLGETVERTEXATTRIBIVARBPROC glad_glGetVertexAttribivARB; +#define glGetVertexAttribivARB glad_glGetVertexAttribivARB +GLAD_API_CALL PFNGLGETNUNIFORMI64VARBPROC glad_glGetnUniformi64vARB; +#define glGetnUniformi64vARB glad_glGetnUniformi64vARB +GLAD_API_CALL PFNGLGETNUNIFORMUI64VARBPROC glad_glGetnUniformui64vARB; +#define glGetnUniformui64vARB glad_glGetnUniformui64vARB +GLAD_API_CALL PFNGLHINTPROC glad_glHint; +#define glHint glad_glHint +GLAD_API_CALL PFNGLINDEXXOESPROC glad_glIndexxOES; +#define glIndexxOES glad_glIndexxOES +GLAD_API_CALL PFNGLINDEXXVOESPROC glad_glIndexxvOES; +#define glIndexxvOES glad_glIndexxvOES +GLAD_API_CALL PFNGLINVALIDATEBUFFERDATAPROC glad_glInvalidateBufferData; +#define glInvalidateBufferData glad_glInvalidateBufferData +GLAD_API_CALL PFNGLINVALIDATEBUFFERSUBDATAPROC glad_glInvalidateBufferSubData; +#define glInvalidateBufferSubData glad_glInvalidateBufferSubData +GLAD_API_CALL PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer; +#define glInvalidateFramebuffer glad_glInvalidateFramebuffer +GLAD_API_CALL PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glad_glInvalidateNamedFramebufferData; +#define glInvalidateNamedFramebufferData glad_glInvalidateNamedFramebufferData +GLAD_API_CALL PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glad_glInvalidateNamedFramebufferSubData; +#define glInvalidateNamedFramebufferSubData glad_glInvalidateNamedFramebufferSubData +GLAD_API_CALL PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer; +#define glInvalidateSubFramebuffer glad_glInvalidateSubFramebuffer +GLAD_API_CALL PFNGLINVALIDATETEXIMAGEPROC glad_glInvalidateTexImage; +#define glInvalidateTexImage glad_glInvalidateTexImage +GLAD_API_CALL PFNGLINVALIDATETEXSUBIMAGEPROC glad_glInvalidateTexSubImage; +#define glInvalidateTexSubImage glad_glInvalidateTexSubImage +GLAD_API_CALL PFNGLISBUFFERPROC glad_glIsBuffer; +#define glIsBuffer glad_glIsBuffer +GLAD_API_CALL PFNGLISBUFFERARBPROC glad_glIsBufferARB; +#define glIsBufferARB glad_glIsBufferARB +GLAD_API_CALL PFNGLISENABLEDPROC glad_glIsEnabled; +#define glIsEnabled glad_glIsEnabled +GLAD_API_CALL PFNGLISENABLEDIPROC glad_glIsEnabledi; +#define glIsEnabledi glad_glIsEnabledi +GLAD_API_CALL PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; +#define glIsFramebuffer glad_glIsFramebuffer +GLAD_API_CALL PFNGLISFRAMEBUFFEREXTPROC glad_glIsFramebufferEXT; +#define glIsFramebufferEXT glad_glIsFramebufferEXT +GLAD_API_CALL PFNGLISNAMEDSTRINGARBPROC glad_glIsNamedStringARB; +#define glIsNamedStringARB glad_glIsNamedStringARB +GLAD_API_CALL PFNGLISPROGRAMPROC glad_glIsProgram; +#define glIsProgram glad_glIsProgram +GLAD_API_CALL PFNGLISPROGRAMARBPROC glad_glIsProgramARB; +#define glIsProgramARB glad_glIsProgramARB +GLAD_API_CALL PFNGLISPROGRAMPIPELINEPROC glad_glIsProgramPipeline; +#define glIsProgramPipeline glad_glIsProgramPipeline +GLAD_API_CALL PFNGLISQUERYPROC glad_glIsQuery; +#define glIsQuery glad_glIsQuery +GLAD_API_CALL PFNGLISQUERYARBPROC glad_glIsQueryARB; +#define glIsQueryARB glad_glIsQueryARB +GLAD_API_CALL PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; +#define glIsRenderbuffer glad_glIsRenderbuffer +GLAD_API_CALL PFNGLISRENDERBUFFEREXTPROC glad_glIsRenderbufferEXT; +#define glIsRenderbufferEXT glad_glIsRenderbufferEXT +GLAD_API_CALL PFNGLISSAMPLERPROC glad_glIsSampler; +#define glIsSampler glad_glIsSampler +GLAD_API_CALL PFNGLISSHADERPROC glad_glIsShader; +#define glIsShader glad_glIsShader +GLAD_API_CALL PFNGLISSYNCPROC glad_glIsSync; +#define glIsSync glad_glIsSync +GLAD_API_CALL PFNGLISTEXTUREPROC glad_glIsTexture; +#define glIsTexture glad_glIsTexture +GLAD_API_CALL PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback; +#define glIsTransformFeedback glad_glIsTransformFeedback +GLAD_API_CALL PFNGLISVERTEXARRAYPROC glad_glIsVertexArray; +#define glIsVertexArray glad_glIsVertexArray +GLAD_API_CALL PFNGLLIGHTMODELXOESPROC glad_glLightModelxOES; +#define glLightModelxOES glad_glLightModelxOES +GLAD_API_CALL PFNGLLIGHTMODELXVOESPROC glad_glLightModelxvOES; +#define glLightModelxvOES glad_glLightModelxvOES +GLAD_API_CALL PFNGLLIGHTXOESPROC glad_glLightxOES; +#define glLightxOES glad_glLightxOES +GLAD_API_CALL PFNGLLIGHTXVOESPROC glad_glLightxvOES; +#define glLightxvOES glad_glLightxvOES +GLAD_API_CALL PFNGLLINEWIDTHPROC glad_glLineWidth; +#define glLineWidth glad_glLineWidth +GLAD_API_CALL PFNGLLINEWIDTHXOESPROC glad_glLineWidthxOES; +#define glLineWidthxOES glad_glLineWidthxOES +GLAD_API_CALL PFNGLLINKPROGRAMPROC glad_glLinkProgram; +#define glLinkProgram glad_glLinkProgram +GLAD_API_CALL PFNGLLINKPROGRAMARBPROC glad_glLinkProgramARB; +#define glLinkProgramARB glad_glLinkProgramARB +GLAD_API_CALL PFNGLLOADMATRIXXOESPROC glad_glLoadMatrixxOES; +#define glLoadMatrixxOES glad_glLoadMatrixxOES +GLAD_API_CALL PFNGLLOADTRANSPOSEMATRIXDARBPROC glad_glLoadTransposeMatrixdARB; +#define glLoadTransposeMatrixdARB glad_glLoadTransposeMatrixdARB +GLAD_API_CALL PFNGLLOADTRANSPOSEMATRIXFARBPROC glad_glLoadTransposeMatrixfARB; +#define glLoadTransposeMatrixfARB glad_glLoadTransposeMatrixfARB +GLAD_API_CALL PFNGLLOADTRANSPOSEMATRIXXOESPROC glad_glLoadTransposeMatrixxOES; +#define glLoadTransposeMatrixxOES glad_glLoadTransposeMatrixxOES +GLAD_API_CALL PFNGLLOGICOPPROC glad_glLogicOp; +#define glLogicOp glad_glLogicOp +GLAD_API_CALL PFNGLMAP1XOESPROC glad_glMap1xOES; +#define glMap1xOES glad_glMap1xOES +GLAD_API_CALL PFNGLMAP2XOESPROC glad_glMap2xOES; +#define glMap2xOES glad_glMap2xOES +GLAD_API_CALL PFNGLMAPBUFFERPROC glad_glMapBuffer; +#define glMapBuffer glad_glMapBuffer +GLAD_API_CALL PFNGLMAPBUFFERARBPROC glad_glMapBufferARB; +#define glMapBufferARB glad_glMapBufferARB +GLAD_API_CALL PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange; +#define glMapBufferRange glad_glMapBufferRange +GLAD_API_CALL PFNGLMAPGRID1XOESPROC glad_glMapGrid1xOES; +#define glMapGrid1xOES glad_glMapGrid1xOES +GLAD_API_CALL PFNGLMAPGRID2XOESPROC glad_glMapGrid2xOES; +#define glMapGrid2xOES glad_glMapGrid2xOES +GLAD_API_CALL PFNGLMAPNAMEDBUFFERPROC glad_glMapNamedBuffer; +#define glMapNamedBuffer glad_glMapNamedBuffer +GLAD_API_CALL PFNGLMAPNAMEDBUFFERRANGEPROC glad_glMapNamedBufferRange; +#define glMapNamedBufferRange glad_glMapNamedBufferRange +GLAD_API_CALL PFNGLMATERIALXOESPROC glad_glMaterialxOES; +#define glMaterialxOES glad_glMaterialxOES +GLAD_API_CALL PFNGLMATERIALXVOESPROC glad_glMaterialxvOES; +#define glMaterialxvOES glad_glMaterialxvOES +GLAD_API_CALL PFNGLMEMORYBARRIERPROC glad_glMemoryBarrier; +#define glMemoryBarrier glad_glMemoryBarrier +GLAD_API_CALL PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion; +#define glMemoryBarrierByRegion glad_glMemoryBarrierByRegion +GLAD_API_CALL PFNGLMINSAMPLESHADINGPROC glad_glMinSampleShading; +#define glMinSampleShading glad_glMinSampleShading +GLAD_API_CALL PFNGLMINSAMPLESHADINGARBPROC glad_glMinSampleShadingARB; +#define glMinSampleShadingARB glad_glMinSampleShadingARB +GLAD_API_CALL PFNGLMULTMATRIXXOESPROC glad_glMultMatrixxOES; +#define glMultMatrixxOES glad_glMultMatrixxOES +GLAD_API_CALL PFNGLMULTTRANSPOSEMATRIXDARBPROC glad_glMultTransposeMatrixdARB; +#define glMultTransposeMatrixdARB glad_glMultTransposeMatrixdARB +GLAD_API_CALL PFNGLMULTTRANSPOSEMATRIXFARBPROC glad_glMultTransposeMatrixfARB; +#define glMultTransposeMatrixfARB glad_glMultTransposeMatrixfARB +GLAD_API_CALL PFNGLMULTTRANSPOSEMATRIXXOESPROC glad_glMultTransposeMatrixxOES; +#define glMultTransposeMatrixxOES glad_glMultTransposeMatrixxOES +GLAD_API_CALL PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays; +#define glMultiDrawArrays glad_glMultiDrawArrays +GLAD_API_CALL PFNGLMULTIDRAWARRAYSINDIRECTPROC glad_glMultiDrawArraysIndirect; +#define glMultiDrawArraysIndirect glad_glMultiDrawArraysIndirect +GLAD_API_CALL PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements; +#define glMultiDrawElements glad_glMultiDrawElements +GLAD_API_CALL PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex; +#define glMultiDrawElementsBaseVertex glad_glMultiDrawElementsBaseVertex +GLAD_API_CALL PFNGLMULTIDRAWELEMENTSINDIRECTPROC glad_glMultiDrawElementsIndirect; +#define glMultiDrawElementsIndirect glad_glMultiDrawElementsIndirect +GLAD_API_CALL PFNGLMULTITEXCOORD1DARBPROC glad_glMultiTexCoord1dARB; +#define glMultiTexCoord1dARB glad_glMultiTexCoord1dARB +GLAD_API_CALL PFNGLMULTITEXCOORD1DVARBPROC glad_glMultiTexCoord1dvARB; +#define glMultiTexCoord1dvARB glad_glMultiTexCoord1dvARB +GLAD_API_CALL PFNGLMULTITEXCOORD1FARBPROC glad_glMultiTexCoord1fARB; +#define glMultiTexCoord1fARB glad_glMultiTexCoord1fARB +GLAD_API_CALL PFNGLMULTITEXCOORD1FVARBPROC glad_glMultiTexCoord1fvARB; +#define glMultiTexCoord1fvARB glad_glMultiTexCoord1fvARB +GLAD_API_CALL PFNGLMULTITEXCOORD1IARBPROC glad_glMultiTexCoord1iARB; +#define glMultiTexCoord1iARB glad_glMultiTexCoord1iARB +GLAD_API_CALL PFNGLMULTITEXCOORD1IVARBPROC glad_glMultiTexCoord1ivARB; +#define glMultiTexCoord1ivARB glad_glMultiTexCoord1ivARB +GLAD_API_CALL PFNGLMULTITEXCOORD1SARBPROC glad_glMultiTexCoord1sARB; +#define glMultiTexCoord1sARB glad_glMultiTexCoord1sARB +GLAD_API_CALL PFNGLMULTITEXCOORD1SVARBPROC glad_glMultiTexCoord1svARB; +#define glMultiTexCoord1svARB glad_glMultiTexCoord1svARB +GLAD_API_CALL PFNGLMULTITEXCOORD1XOESPROC glad_glMultiTexCoord1xOES; +#define glMultiTexCoord1xOES glad_glMultiTexCoord1xOES +GLAD_API_CALL PFNGLMULTITEXCOORD1XVOESPROC glad_glMultiTexCoord1xvOES; +#define glMultiTexCoord1xvOES glad_glMultiTexCoord1xvOES +GLAD_API_CALL PFNGLMULTITEXCOORD2DARBPROC glad_glMultiTexCoord2dARB; +#define glMultiTexCoord2dARB glad_glMultiTexCoord2dARB +GLAD_API_CALL PFNGLMULTITEXCOORD2DVARBPROC glad_glMultiTexCoord2dvARB; +#define glMultiTexCoord2dvARB glad_glMultiTexCoord2dvARB +GLAD_API_CALL PFNGLMULTITEXCOORD2FARBPROC glad_glMultiTexCoord2fARB; +#define glMultiTexCoord2fARB glad_glMultiTexCoord2fARB +GLAD_API_CALL PFNGLMULTITEXCOORD2FVARBPROC glad_glMultiTexCoord2fvARB; +#define glMultiTexCoord2fvARB glad_glMultiTexCoord2fvARB +GLAD_API_CALL PFNGLMULTITEXCOORD2IARBPROC glad_glMultiTexCoord2iARB; +#define glMultiTexCoord2iARB glad_glMultiTexCoord2iARB +GLAD_API_CALL PFNGLMULTITEXCOORD2IVARBPROC glad_glMultiTexCoord2ivARB; +#define glMultiTexCoord2ivARB glad_glMultiTexCoord2ivARB +GLAD_API_CALL PFNGLMULTITEXCOORD2SARBPROC glad_glMultiTexCoord2sARB; +#define glMultiTexCoord2sARB glad_glMultiTexCoord2sARB +GLAD_API_CALL PFNGLMULTITEXCOORD2SVARBPROC glad_glMultiTexCoord2svARB; +#define glMultiTexCoord2svARB glad_glMultiTexCoord2svARB +GLAD_API_CALL PFNGLMULTITEXCOORD2XOESPROC glad_glMultiTexCoord2xOES; +#define glMultiTexCoord2xOES glad_glMultiTexCoord2xOES +GLAD_API_CALL PFNGLMULTITEXCOORD2XVOESPROC glad_glMultiTexCoord2xvOES; +#define glMultiTexCoord2xvOES glad_glMultiTexCoord2xvOES +GLAD_API_CALL PFNGLMULTITEXCOORD3DARBPROC glad_glMultiTexCoord3dARB; +#define glMultiTexCoord3dARB glad_glMultiTexCoord3dARB +GLAD_API_CALL PFNGLMULTITEXCOORD3DVARBPROC glad_glMultiTexCoord3dvARB; +#define glMultiTexCoord3dvARB glad_glMultiTexCoord3dvARB +GLAD_API_CALL PFNGLMULTITEXCOORD3FARBPROC glad_glMultiTexCoord3fARB; +#define glMultiTexCoord3fARB glad_glMultiTexCoord3fARB +GLAD_API_CALL PFNGLMULTITEXCOORD3FVARBPROC glad_glMultiTexCoord3fvARB; +#define glMultiTexCoord3fvARB glad_glMultiTexCoord3fvARB +GLAD_API_CALL PFNGLMULTITEXCOORD3IARBPROC glad_glMultiTexCoord3iARB; +#define glMultiTexCoord3iARB glad_glMultiTexCoord3iARB +GLAD_API_CALL PFNGLMULTITEXCOORD3IVARBPROC glad_glMultiTexCoord3ivARB; +#define glMultiTexCoord3ivARB glad_glMultiTexCoord3ivARB +GLAD_API_CALL PFNGLMULTITEXCOORD3SARBPROC glad_glMultiTexCoord3sARB; +#define glMultiTexCoord3sARB glad_glMultiTexCoord3sARB +GLAD_API_CALL PFNGLMULTITEXCOORD3SVARBPROC glad_glMultiTexCoord3svARB; +#define glMultiTexCoord3svARB glad_glMultiTexCoord3svARB +GLAD_API_CALL PFNGLMULTITEXCOORD3XOESPROC glad_glMultiTexCoord3xOES; +#define glMultiTexCoord3xOES glad_glMultiTexCoord3xOES +GLAD_API_CALL PFNGLMULTITEXCOORD3XVOESPROC glad_glMultiTexCoord3xvOES; +#define glMultiTexCoord3xvOES glad_glMultiTexCoord3xvOES +GLAD_API_CALL PFNGLMULTITEXCOORD4DARBPROC glad_glMultiTexCoord4dARB; +#define glMultiTexCoord4dARB glad_glMultiTexCoord4dARB +GLAD_API_CALL PFNGLMULTITEXCOORD4DVARBPROC glad_glMultiTexCoord4dvARB; +#define glMultiTexCoord4dvARB glad_glMultiTexCoord4dvARB +GLAD_API_CALL PFNGLMULTITEXCOORD4FARBPROC glad_glMultiTexCoord4fARB; +#define glMultiTexCoord4fARB glad_glMultiTexCoord4fARB +GLAD_API_CALL PFNGLMULTITEXCOORD4FVARBPROC glad_glMultiTexCoord4fvARB; +#define glMultiTexCoord4fvARB glad_glMultiTexCoord4fvARB +GLAD_API_CALL PFNGLMULTITEXCOORD4IARBPROC glad_glMultiTexCoord4iARB; +#define glMultiTexCoord4iARB glad_glMultiTexCoord4iARB +GLAD_API_CALL PFNGLMULTITEXCOORD4IVARBPROC glad_glMultiTexCoord4ivARB; +#define glMultiTexCoord4ivARB glad_glMultiTexCoord4ivARB +GLAD_API_CALL PFNGLMULTITEXCOORD4SARBPROC glad_glMultiTexCoord4sARB; +#define glMultiTexCoord4sARB glad_glMultiTexCoord4sARB +GLAD_API_CALL PFNGLMULTITEXCOORD4SVARBPROC glad_glMultiTexCoord4svARB; +#define glMultiTexCoord4svARB glad_glMultiTexCoord4svARB +GLAD_API_CALL PFNGLMULTITEXCOORD4XOESPROC glad_glMultiTexCoord4xOES; +#define glMultiTexCoord4xOES glad_glMultiTexCoord4xOES +GLAD_API_CALL PFNGLMULTITEXCOORD4XVOESPROC glad_glMultiTexCoord4xvOES; +#define glMultiTexCoord4xvOES glad_glMultiTexCoord4xvOES +GLAD_API_CALL PFNGLNAMEDBUFFERDATAPROC glad_glNamedBufferData; +#define glNamedBufferData glad_glNamedBufferData +GLAD_API_CALL PFNGLNAMEDBUFFERSTORAGEPROC glad_glNamedBufferStorage; +#define glNamedBufferStorage glad_glNamedBufferStorage +GLAD_API_CALL PFNGLNAMEDBUFFERSUBDATAPROC glad_glNamedBufferSubData; +#define glNamedBufferSubData glad_glNamedBufferSubData +GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glad_glNamedFramebufferDrawBuffer; +#define glNamedFramebufferDrawBuffer glad_glNamedFramebufferDrawBuffer +GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glad_glNamedFramebufferDrawBuffers; +#define glNamedFramebufferDrawBuffers glad_glNamedFramebufferDrawBuffers +GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glad_glNamedFramebufferParameteri; +#define glNamedFramebufferParameteri glad_glNamedFramebufferParameteri +GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glad_glNamedFramebufferReadBuffer; +#define glNamedFramebufferReadBuffer glad_glNamedFramebufferReadBuffer +GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glad_glNamedFramebufferRenderbuffer; +#define glNamedFramebufferRenderbuffer glad_glNamedFramebufferRenderbuffer +GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glad_glNamedFramebufferSampleLocationsfvARB; +#define glNamedFramebufferSampleLocationsfvARB glad_glNamedFramebufferSampleLocationsfvARB +GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glad_glNamedFramebufferTexture; +#define glNamedFramebufferTexture glad_glNamedFramebufferTexture +GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glad_glNamedFramebufferTextureLayer; +#define glNamedFramebufferTextureLayer glad_glNamedFramebufferTextureLayer +GLAD_API_CALL PFNGLNAMEDRENDERBUFFERSTORAGEPROC glad_glNamedRenderbufferStorage; +#define glNamedRenderbufferStorage glad_glNamedRenderbufferStorage +GLAD_API_CALL PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glNamedRenderbufferStorageMultisample; +#define glNamedRenderbufferStorageMultisample glad_glNamedRenderbufferStorageMultisample +GLAD_API_CALL PFNGLNAMEDSTRINGARBPROC glad_glNamedStringARB; +#define glNamedStringARB glad_glNamedStringARB +GLAD_API_CALL PFNGLNORMAL3XOESPROC glad_glNormal3xOES; +#define glNormal3xOES glad_glNormal3xOES +GLAD_API_CALL PFNGLNORMAL3XVOESPROC glad_glNormal3xvOES; +#define glNormal3xvOES glad_glNormal3xvOES +GLAD_API_CALL PFNGLOBJECTLABELPROC glad_glObjectLabel; +#define glObjectLabel glad_glObjectLabel +GLAD_API_CALL PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel; +#define glObjectPtrLabel glad_glObjectPtrLabel +GLAD_API_CALL PFNGLORTHOXOESPROC glad_glOrthoxOES; +#define glOrthoxOES glad_glOrthoxOES +GLAD_API_CALL PFNGLPASSTHROUGHXOESPROC glad_glPassThroughxOES; +#define glPassThroughxOES glad_glPassThroughxOES +GLAD_API_CALL PFNGLPATCHPARAMETERFVPROC glad_glPatchParameterfv; +#define glPatchParameterfv glad_glPatchParameterfv +GLAD_API_CALL PFNGLPATCHPARAMETERIPROC glad_glPatchParameteri; +#define glPatchParameteri glad_glPatchParameteri +GLAD_API_CALL PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback; +#define glPauseTransformFeedback glad_glPauseTransformFeedback +GLAD_API_CALL PFNGLPIXELMAPXPROC glad_glPixelMapx; +#define glPixelMapx glad_glPixelMapx +GLAD_API_CALL PFNGLPIXELSTOREFPROC glad_glPixelStoref; +#define glPixelStoref glad_glPixelStoref +GLAD_API_CALL PFNGLPIXELSTOREIPROC glad_glPixelStorei; +#define glPixelStorei glad_glPixelStorei +GLAD_API_CALL PFNGLPIXELSTOREXPROC glad_glPixelStorex; +#define glPixelStorex glad_glPixelStorex +GLAD_API_CALL PFNGLPIXELTRANSFERXOESPROC glad_glPixelTransferxOES; +#define glPixelTransferxOES glad_glPixelTransferxOES +GLAD_API_CALL PFNGLPIXELZOOMXOESPROC glad_glPixelZoomxOES; +#define glPixelZoomxOES glad_glPixelZoomxOES +GLAD_API_CALL PFNGLPOINTPARAMETERFPROC glad_glPointParameterf; +#define glPointParameterf glad_glPointParameterf +GLAD_API_CALL PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv; +#define glPointParameterfv glad_glPointParameterfv +GLAD_API_CALL PFNGLPOINTPARAMETERIPROC glad_glPointParameteri; +#define glPointParameteri glad_glPointParameteri +GLAD_API_CALL PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv; +#define glPointParameteriv glad_glPointParameteriv +GLAD_API_CALL PFNGLPOINTPARAMETERXVOESPROC glad_glPointParameterxvOES; +#define glPointParameterxvOES glad_glPointParameterxvOES +GLAD_API_CALL PFNGLPOINTSIZEPROC glad_glPointSize; +#define glPointSize glad_glPointSize +GLAD_API_CALL PFNGLPOINTSIZEXOESPROC glad_glPointSizexOES; +#define glPointSizexOES glad_glPointSizexOES +GLAD_API_CALL PFNGLPOLYGONMODEPROC glad_glPolygonMode; +#define glPolygonMode glad_glPolygonMode +GLAD_API_CALL PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset; +#define glPolygonOffset glad_glPolygonOffset +GLAD_API_CALL PFNGLPOLYGONOFFSETXOESPROC glad_glPolygonOffsetxOES; +#define glPolygonOffsetxOES glad_glPolygonOffsetxOES +GLAD_API_CALL PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup; +#define glPopDebugGroup glad_glPopDebugGroup +GLAD_API_CALL PFNGLPRIMITIVEBOUNDINGBOXARBPROC glad_glPrimitiveBoundingBoxARB; +#define glPrimitiveBoundingBoxARB glad_glPrimitiveBoundingBoxARB +GLAD_API_CALL PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex; +#define glPrimitiveRestartIndex glad_glPrimitiveRestartIndex +GLAD_API_CALL PFNGLPRIORITIZETEXTURESXOESPROC glad_glPrioritizeTexturesxOES; +#define glPrioritizeTexturesxOES glad_glPrioritizeTexturesxOES +GLAD_API_CALL PFNGLPROGRAMBINARYPROC glad_glProgramBinary; +#define glProgramBinary glad_glProgramBinary +GLAD_API_CALL PFNGLPROGRAMENVPARAMETER4DARBPROC glad_glProgramEnvParameter4dARB; +#define glProgramEnvParameter4dARB glad_glProgramEnvParameter4dARB +GLAD_API_CALL PFNGLPROGRAMENVPARAMETER4DVARBPROC glad_glProgramEnvParameter4dvARB; +#define glProgramEnvParameter4dvARB glad_glProgramEnvParameter4dvARB +GLAD_API_CALL PFNGLPROGRAMENVPARAMETER4FARBPROC glad_glProgramEnvParameter4fARB; +#define glProgramEnvParameter4fARB glad_glProgramEnvParameter4fARB +GLAD_API_CALL PFNGLPROGRAMENVPARAMETER4FVARBPROC glad_glProgramEnvParameter4fvARB; +#define glProgramEnvParameter4fvARB glad_glProgramEnvParameter4fvARB +GLAD_API_CALL PFNGLPROGRAMLOCALPARAMETER4DARBPROC glad_glProgramLocalParameter4dARB; +#define glProgramLocalParameter4dARB glad_glProgramLocalParameter4dARB +GLAD_API_CALL PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glad_glProgramLocalParameter4dvARB; +#define glProgramLocalParameter4dvARB glad_glProgramLocalParameter4dvARB +GLAD_API_CALL PFNGLPROGRAMLOCALPARAMETER4FARBPROC glad_glProgramLocalParameter4fARB; +#define glProgramLocalParameter4fARB glad_glProgramLocalParameter4fARB +GLAD_API_CALL PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glad_glProgramLocalParameter4fvARB; +#define glProgramLocalParameter4fvARB glad_glProgramLocalParameter4fvARB +GLAD_API_CALL PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri; +#define glProgramParameteri glad_glProgramParameteri +GLAD_API_CALL PFNGLPROGRAMPARAMETERIARBPROC glad_glProgramParameteriARB; +#define glProgramParameteriARB glad_glProgramParameteriARB +GLAD_API_CALL PFNGLPROGRAMSTRINGARBPROC glad_glProgramStringARB; +#define glProgramStringARB glad_glProgramStringARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM1DPROC glad_glProgramUniform1d; +#define glProgramUniform1d glad_glProgramUniform1d +GLAD_API_CALL PFNGLPROGRAMUNIFORM1DVPROC glad_glProgramUniform1dv; +#define glProgramUniform1dv glad_glProgramUniform1dv +GLAD_API_CALL PFNGLPROGRAMUNIFORM1FPROC glad_glProgramUniform1f; +#define glProgramUniform1f glad_glProgramUniform1f +GLAD_API_CALL PFNGLPROGRAMUNIFORM1FVPROC glad_glProgramUniform1fv; +#define glProgramUniform1fv glad_glProgramUniform1fv +GLAD_API_CALL PFNGLPROGRAMUNIFORM1IPROC glad_glProgramUniform1i; +#define glProgramUniform1i glad_glProgramUniform1i +GLAD_API_CALL PFNGLPROGRAMUNIFORM1I64ARBPROC glad_glProgramUniform1i64ARB; +#define glProgramUniform1i64ARB glad_glProgramUniform1i64ARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM1I64VARBPROC glad_glProgramUniform1i64vARB; +#define glProgramUniform1i64vARB glad_glProgramUniform1i64vARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM1IVPROC glad_glProgramUniform1iv; +#define glProgramUniform1iv glad_glProgramUniform1iv +GLAD_API_CALL PFNGLPROGRAMUNIFORM1UIPROC glad_glProgramUniform1ui; +#define glProgramUniform1ui glad_glProgramUniform1ui +GLAD_API_CALL PFNGLPROGRAMUNIFORM1UI64ARBPROC glad_glProgramUniform1ui64ARB; +#define glProgramUniform1ui64ARB glad_glProgramUniform1ui64ARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM1UI64VARBPROC glad_glProgramUniform1ui64vARB; +#define glProgramUniform1ui64vARB glad_glProgramUniform1ui64vARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM1UIVPROC glad_glProgramUniform1uiv; +#define glProgramUniform1uiv glad_glProgramUniform1uiv +GLAD_API_CALL PFNGLPROGRAMUNIFORM2DPROC glad_glProgramUniform2d; +#define glProgramUniform2d glad_glProgramUniform2d +GLAD_API_CALL PFNGLPROGRAMUNIFORM2DVPROC glad_glProgramUniform2dv; +#define glProgramUniform2dv glad_glProgramUniform2dv +GLAD_API_CALL PFNGLPROGRAMUNIFORM2FPROC glad_glProgramUniform2f; +#define glProgramUniform2f glad_glProgramUniform2f +GLAD_API_CALL PFNGLPROGRAMUNIFORM2FVPROC glad_glProgramUniform2fv; +#define glProgramUniform2fv glad_glProgramUniform2fv +GLAD_API_CALL PFNGLPROGRAMUNIFORM2IPROC glad_glProgramUniform2i; +#define glProgramUniform2i glad_glProgramUniform2i +GLAD_API_CALL PFNGLPROGRAMUNIFORM2I64ARBPROC glad_glProgramUniform2i64ARB; +#define glProgramUniform2i64ARB glad_glProgramUniform2i64ARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM2I64VARBPROC glad_glProgramUniform2i64vARB; +#define glProgramUniform2i64vARB glad_glProgramUniform2i64vARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM2IVPROC glad_glProgramUniform2iv; +#define glProgramUniform2iv glad_glProgramUniform2iv +GLAD_API_CALL PFNGLPROGRAMUNIFORM2UIPROC glad_glProgramUniform2ui; +#define glProgramUniform2ui glad_glProgramUniform2ui +GLAD_API_CALL PFNGLPROGRAMUNIFORM2UI64ARBPROC glad_glProgramUniform2ui64ARB; +#define glProgramUniform2ui64ARB glad_glProgramUniform2ui64ARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM2UI64VARBPROC glad_glProgramUniform2ui64vARB; +#define glProgramUniform2ui64vARB glad_glProgramUniform2ui64vARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM2UIVPROC glad_glProgramUniform2uiv; +#define glProgramUniform2uiv glad_glProgramUniform2uiv +GLAD_API_CALL PFNGLPROGRAMUNIFORM3DPROC glad_glProgramUniform3d; +#define glProgramUniform3d glad_glProgramUniform3d +GLAD_API_CALL PFNGLPROGRAMUNIFORM3DVPROC glad_glProgramUniform3dv; +#define glProgramUniform3dv glad_glProgramUniform3dv +GLAD_API_CALL PFNGLPROGRAMUNIFORM3FPROC glad_glProgramUniform3f; +#define glProgramUniform3f glad_glProgramUniform3f +GLAD_API_CALL PFNGLPROGRAMUNIFORM3FVPROC glad_glProgramUniform3fv; +#define glProgramUniform3fv glad_glProgramUniform3fv +GLAD_API_CALL PFNGLPROGRAMUNIFORM3IPROC glad_glProgramUniform3i; +#define glProgramUniform3i glad_glProgramUniform3i +GLAD_API_CALL PFNGLPROGRAMUNIFORM3I64ARBPROC glad_glProgramUniform3i64ARB; +#define glProgramUniform3i64ARB glad_glProgramUniform3i64ARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM3I64VARBPROC glad_glProgramUniform3i64vARB; +#define glProgramUniform3i64vARB glad_glProgramUniform3i64vARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM3IVPROC glad_glProgramUniform3iv; +#define glProgramUniform3iv glad_glProgramUniform3iv +GLAD_API_CALL PFNGLPROGRAMUNIFORM3UIPROC glad_glProgramUniform3ui; +#define glProgramUniform3ui glad_glProgramUniform3ui +GLAD_API_CALL PFNGLPROGRAMUNIFORM3UI64ARBPROC glad_glProgramUniform3ui64ARB; +#define glProgramUniform3ui64ARB glad_glProgramUniform3ui64ARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM3UI64VARBPROC glad_glProgramUniform3ui64vARB; +#define glProgramUniform3ui64vARB glad_glProgramUniform3ui64vARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM3UIVPROC glad_glProgramUniform3uiv; +#define glProgramUniform3uiv glad_glProgramUniform3uiv +GLAD_API_CALL PFNGLPROGRAMUNIFORM4DPROC glad_glProgramUniform4d; +#define glProgramUniform4d glad_glProgramUniform4d +GLAD_API_CALL PFNGLPROGRAMUNIFORM4DVPROC glad_glProgramUniform4dv; +#define glProgramUniform4dv glad_glProgramUniform4dv +GLAD_API_CALL PFNGLPROGRAMUNIFORM4FPROC glad_glProgramUniform4f; +#define glProgramUniform4f glad_glProgramUniform4f +GLAD_API_CALL PFNGLPROGRAMUNIFORM4FVPROC glad_glProgramUniform4fv; +#define glProgramUniform4fv glad_glProgramUniform4fv +GLAD_API_CALL PFNGLPROGRAMUNIFORM4IPROC glad_glProgramUniform4i; +#define glProgramUniform4i glad_glProgramUniform4i +GLAD_API_CALL PFNGLPROGRAMUNIFORM4I64ARBPROC glad_glProgramUniform4i64ARB; +#define glProgramUniform4i64ARB glad_glProgramUniform4i64ARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM4I64VARBPROC glad_glProgramUniform4i64vARB; +#define glProgramUniform4i64vARB glad_glProgramUniform4i64vARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM4IVPROC glad_glProgramUniform4iv; +#define glProgramUniform4iv glad_glProgramUniform4iv +GLAD_API_CALL PFNGLPROGRAMUNIFORM4UIPROC glad_glProgramUniform4ui; +#define glProgramUniform4ui glad_glProgramUniform4ui +GLAD_API_CALL PFNGLPROGRAMUNIFORM4UI64ARBPROC glad_glProgramUniform4ui64ARB; +#define glProgramUniform4ui64ARB glad_glProgramUniform4ui64ARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM4UI64VARBPROC glad_glProgramUniform4ui64vARB; +#define glProgramUniform4ui64vARB glad_glProgramUniform4ui64vARB +GLAD_API_CALL PFNGLPROGRAMUNIFORM4UIVPROC glad_glProgramUniform4uiv; +#define glProgramUniform4uiv glad_glProgramUniform4uiv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2DVPROC glad_glProgramUniformMatrix2dv; +#define glProgramUniformMatrix2dv glad_glProgramUniformMatrix2dv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2FVPROC glad_glProgramUniformMatrix2fv; +#define glProgramUniformMatrix2fv glad_glProgramUniformMatrix2fv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glad_glProgramUniformMatrix2x3dv; +#define glProgramUniformMatrix2x3dv glad_glProgramUniformMatrix2x3dv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glad_glProgramUniformMatrix2x3fv; +#define glProgramUniformMatrix2x3fv glad_glProgramUniformMatrix2x3fv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glad_glProgramUniformMatrix2x4dv; +#define glProgramUniformMatrix2x4dv glad_glProgramUniformMatrix2x4dv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glad_glProgramUniformMatrix2x4fv; +#define glProgramUniformMatrix2x4fv glad_glProgramUniformMatrix2x4fv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3DVPROC glad_glProgramUniformMatrix3dv; +#define glProgramUniformMatrix3dv glad_glProgramUniformMatrix3dv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3FVPROC glad_glProgramUniformMatrix3fv; +#define glProgramUniformMatrix3fv glad_glProgramUniformMatrix3fv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glad_glProgramUniformMatrix3x2dv; +#define glProgramUniformMatrix3x2dv glad_glProgramUniformMatrix3x2dv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glad_glProgramUniformMatrix3x2fv; +#define glProgramUniformMatrix3x2fv glad_glProgramUniformMatrix3x2fv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glad_glProgramUniformMatrix3x4dv; +#define glProgramUniformMatrix3x4dv glad_glProgramUniformMatrix3x4dv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glad_glProgramUniformMatrix3x4fv; +#define glProgramUniformMatrix3x4fv glad_glProgramUniformMatrix3x4fv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4DVPROC glad_glProgramUniformMatrix4dv; +#define glProgramUniformMatrix4dv glad_glProgramUniformMatrix4dv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4FVPROC glad_glProgramUniformMatrix4fv; +#define glProgramUniformMatrix4fv glad_glProgramUniformMatrix4fv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glad_glProgramUniformMatrix4x2dv; +#define glProgramUniformMatrix4x2dv glad_glProgramUniformMatrix4x2dv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glad_glProgramUniformMatrix4x2fv; +#define glProgramUniformMatrix4x2fv glad_glProgramUniformMatrix4x2fv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glad_glProgramUniformMatrix4x3dv; +#define glProgramUniformMatrix4x3dv glad_glProgramUniformMatrix4x3dv +GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glad_glProgramUniformMatrix4x3fv; +#define glProgramUniformMatrix4x3fv glad_glProgramUniformMatrix4x3fv +GLAD_API_CALL PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex; +#define glProvokingVertex glad_glProvokingVertex +GLAD_API_CALL PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup; +#define glPushDebugGroup glad_glPushDebugGroup +GLAD_API_CALL PFNGLQUERYCOUNTERPROC glad_glQueryCounter; +#define glQueryCounter glad_glQueryCounter +GLAD_API_CALL PFNGLRASTERPOS2XOESPROC glad_glRasterPos2xOES; +#define glRasterPos2xOES glad_glRasterPos2xOES +GLAD_API_CALL PFNGLRASTERPOS2XVOESPROC glad_glRasterPos2xvOES; +#define glRasterPos2xvOES glad_glRasterPos2xvOES +GLAD_API_CALL PFNGLRASTERPOS3XOESPROC glad_glRasterPos3xOES; +#define glRasterPos3xOES glad_glRasterPos3xOES +GLAD_API_CALL PFNGLRASTERPOS3XVOESPROC glad_glRasterPos3xvOES; +#define glRasterPos3xvOES glad_glRasterPos3xvOES +GLAD_API_CALL PFNGLRASTERPOS4XOESPROC glad_glRasterPos4xOES; +#define glRasterPos4xOES glad_glRasterPos4xOES +GLAD_API_CALL PFNGLRASTERPOS4XVOESPROC glad_glRasterPos4xvOES; +#define glRasterPos4xvOES glad_glRasterPos4xvOES +GLAD_API_CALL PFNGLREADBUFFERPROC glad_glReadBuffer; +#define glReadBuffer glad_glReadBuffer +GLAD_API_CALL PFNGLREADPIXELSPROC glad_glReadPixels; +#define glReadPixels glad_glReadPixels +GLAD_API_CALL PFNGLRECTXOESPROC glad_glRectxOES; +#define glRectxOES glad_glRectxOES +GLAD_API_CALL PFNGLRECTXVOESPROC glad_glRectxvOES; +#define glRectxvOES glad_glRectxvOES +GLAD_API_CALL PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler; +#define glReleaseShaderCompiler glad_glReleaseShaderCompiler +GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; +#define glRenderbufferStorage glad_glRenderbufferStorage +GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEEXTPROC glad_glRenderbufferStorageEXT; +#define glRenderbufferStorageEXT glad_glRenderbufferStorageEXT +GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample; +#define glRenderbufferStorageMultisample glad_glRenderbufferStorageMultisample +GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMultisampleEXT; +#define glRenderbufferStorageMultisampleEXT glad_glRenderbufferStorageMultisampleEXT +GLAD_API_CALL PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback; +#define glResumeTransformFeedback glad_glResumeTransformFeedback +GLAD_API_CALL PFNGLROTATEXOESPROC glad_glRotatexOES; +#define glRotatexOES glad_glRotatexOES +GLAD_API_CALL PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; +#define glSampleCoverage glad_glSampleCoverage +GLAD_API_CALL PFNGLSAMPLECOVERAGEARBPROC glad_glSampleCoverageARB; +#define glSampleCoverageARB glad_glSampleCoverageARB +GLAD_API_CALL PFNGLSAMPLEMASKIPROC glad_glSampleMaski; +#define glSampleMaski glad_glSampleMaski +GLAD_API_CALL PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv; +#define glSamplerParameterIiv glad_glSamplerParameterIiv +GLAD_API_CALL PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv; +#define glSamplerParameterIuiv glad_glSamplerParameterIuiv +GLAD_API_CALL PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf; +#define glSamplerParameterf glad_glSamplerParameterf +GLAD_API_CALL PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv; +#define glSamplerParameterfv glad_glSamplerParameterfv +GLAD_API_CALL PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri; +#define glSamplerParameteri glad_glSamplerParameteri +GLAD_API_CALL PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv; +#define glSamplerParameteriv glad_glSamplerParameteriv +GLAD_API_CALL PFNGLSCALEXOESPROC glad_glScalexOES; +#define glScalexOES glad_glScalexOES +GLAD_API_CALL PFNGLSCISSORPROC glad_glScissor; +#define glScissor glad_glScissor +GLAD_API_CALL PFNGLSCISSORARRAYVPROC glad_glScissorArrayv; +#define glScissorArrayv glad_glScissorArrayv +GLAD_API_CALL PFNGLSCISSORINDEXEDPROC glad_glScissorIndexed; +#define glScissorIndexed glad_glScissorIndexed +GLAD_API_CALL PFNGLSCISSORINDEXEDVPROC glad_glScissorIndexedv; +#define glScissorIndexedv glad_glScissorIndexedv +GLAD_API_CALL PFNGLSHADERBINARYPROC glad_glShaderBinary; +#define glShaderBinary glad_glShaderBinary +GLAD_API_CALL PFNGLSHADERSOURCEPROC glad_glShaderSource; +#define glShaderSource glad_glShaderSource +GLAD_API_CALL PFNGLSHADERSOURCEARBPROC glad_glShaderSourceARB; +#define glShaderSourceARB glad_glShaderSourceARB +GLAD_API_CALL PFNGLSHADERSTORAGEBLOCKBINDINGPROC glad_glShaderStorageBlockBinding; +#define glShaderStorageBlockBinding glad_glShaderStorageBlockBinding +GLAD_API_CALL PFNGLSPECIALIZESHADERARBPROC glad_glSpecializeShaderARB; +#define glSpecializeShaderARB glad_glSpecializeShaderARB +GLAD_API_CALL PFNGLSTENCILFUNCPROC glad_glStencilFunc; +#define glStencilFunc glad_glStencilFunc +GLAD_API_CALL PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate; +#define glStencilFuncSeparate glad_glStencilFuncSeparate +GLAD_API_CALL PFNGLSTENCILMASKPROC glad_glStencilMask; +#define glStencilMask glad_glStencilMask +GLAD_API_CALL PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; +#define glStencilMaskSeparate glad_glStencilMaskSeparate +GLAD_API_CALL PFNGLSTENCILOPPROC glad_glStencilOp; +#define glStencilOp glad_glStencilOp +GLAD_API_CALL PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate; +#define glStencilOpSeparate glad_glStencilOpSeparate +GLAD_API_CALL PFNGLTEXBUFFERPROC glad_glTexBuffer; +#define glTexBuffer glad_glTexBuffer +GLAD_API_CALL PFNGLTEXBUFFERRANGEPROC glad_glTexBufferRange; +#define glTexBufferRange glad_glTexBufferRange +GLAD_API_CALL PFNGLTEXCOORD1XOESPROC glad_glTexCoord1xOES; +#define glTexCoord1xOES glad_glTexCoord1xOES +GLAD_API_CALL PFNGLTEXCOORD1XVOESPROC glad_glTexCoord1xvOES; +#define glTexCoord1xvOES glad_glTexCoord1xvOES +GLAD_API_CALL PFNGLTEXCOORD2XOESPROC glad_glTexCoord2xOES; +#define glTexCoord2xOES glad_glTexCoord2xOES +GLAD_API_CALL PFNGLTEXCOORD2XVOESPROC glad_glTexCoord2xvOES; +#define glTexCoord2xvOES glad_glTexCoord2xvOES +GLAD_API_CALL PFNGLTEXCOORD3XOESPROC glad_glTexCoord3xOES; +#define glTexCoord3xOES glad_glTexCoord3xOES +GLAD_API_CALL PFNGLTEXCOORD3XVOESPROC glad_glTexCoord3xvOES; +#define glTexCoord3xvOES glad_glTexCoord3xvOES +GLAD_API_CALL PFNGLTEXCOORD4XOESPROC glad_glTexCoord4xOES; +#define glTexCoord4xOES glad_glTexCoord4xOES +GLAD_API_CALL PFNGLTEXCOORD4XVOESPROC glad_glTexCoord4xvOES; +#define glTexCoord4xvOES glad_glTexCoord4xvOES +GLAD_API_CALL PFNGLTEXENVXOESPROC glad_glTexEnvxOES; +#define glTexEnvxOES glad_glTexEnvxOES +GLAD_API_CALL PFNGLTEXENVXVOESPROC glad_glTexEnvxvOES; +#define glTexEnvxvOES glad_glTexEnvxvOES +GLAD_API_CALL PFNGLTEXGENXOESPROC glad_glTexGenxOES; +#define glTexGenxOES glad_glTexGenxOES +GLAD_API_CALL PFNGLTEXGENXVOESPROC glad_glTexGenxvOES; +#define glTexGenxvOES glad_glTexGenxvOES +GLAD_API_CALL PFNGLTEXIMAGE1DPROC glad_glTexImage1D; +#define glTexImage1D glad_glTexImage1D +GLAD_API_CALL PFNGLTEXIMAGE2DPROC glad_glTexImage2D; +#define glTexImage2D glad_glTexImage2D +GLAD_API_CALL PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample; +#define glTexImage2DMultisample glad_glTexImage2DMultisample +GLAD_API_CALL PFNGLTEXIMAGE3DPROC glad_glTexImage3D; +#define glTexImage3D glad_glTexImage3D +GLAD_API_CALL PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample; +#define glTexImage3DMultisample glad_glTexImage3DMultisample +GLAD_API_CALL PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv; +#define glTexParameterIiv glad_glTexParameterIiv +GLAD_API_CALL PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv; +#define glTexParameterIuiv glad_glTexParameterIuiv +GLAD_API_CALL PFNGLTEXPARAMETERFPROC glad_glTexParameterf; +#define glTexParameterf glad_glTexParameterf +GLAD_API_CALL PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv; +#define glTexParameterfv glad_glTexParameterfv +GLAD_API_CALL PFNGLTEXPARAMETERIPROC glad_glTexParameteri; +#define glTexParameteri glad_glTexParameteri +GLAD_API_CALL PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv; +#define glTexParameteriv glad_glTexParameteriv +GLAD_API_CALL PFNGLTEXPARAMETERXOESPROC glad_glTexParameterxOES; +#define glTexParameterxOES glad_glTexParameterxOES +GLAD_API_CALL PFNGLTEXPARAMETERXVOESPROC glad_glTexParameterxvOES; +#define glTexParameterxvOES glad_glTexParameterxvOES +GLAD_API_CALL PFNGLTEXSTORAGE1DPROC glad_glTexStorage1D; +#define glTexStorage1D glad_glTexStorage1D +GLAD_API_CALL PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D; +#define glTexStorage2D glad_glTexStorage2D +GLAD_API_CALL PFNGLTEXSTORAGE2DMULTISAMPLEPROC glad_glTexStorage2DMultisample; +#define glTexStorage2DMultisample glad_glTexStorage2DMultisample +GLAD_API_CALL PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D; +#define glTexStorage3D glad_glTexStorage3D +GLAD_API_CALL PFNGLTEXSTORAGE3DMULTISAMPLEPROC glad_glTexStorage3DMultisample; +#define glTexStorage3DMultisample glad_glTexStorage3DMultisample +GLAD_API_CALL PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D; +#define glTexSubImage1D glad_glTexSubImage1D +GLAD_API_CALL PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D; +#define glTexSubImage2D glad_glTexSubImage2D +GLAD_API_CALL PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D; +#define glTexSubImage3D glad_glTexSubImage3D +GLAD_API_CALL PFNGLTEXTUREBUFFERPROC glad_glTextureBuffer; +#define glTextureBuffer glad_glTextureBuffer +GLAD_API_CALL PFNGLTEXTUREBUFFERRANGEPROC glad_glTextureBufferRange; +#define glTextureBufferRange glad_glTextureBufferRange +GLAD_API_CALL PFNGLTEXTUREPARAMETERIIVPROC glad_glTextureParameterIiv; +#define glTextureParameterIiv glad_glTextureParameterIiv +GLAD_API_CALL PFNGLTEXTUREPARAMETERIUIVPROC glad_glTextureParameterIuiv; +#define glTextureParameterIuiv glad_glTextureParameterIuiv +GLAD_API_CALL PFNGLTEXTUREPARAMETERFPROC glad_glTextureParameterf; +#define glTextureParameterf glad_glTextureParameterf +GLAD_API_CALL PFNGLTEXTUREPARAMETERFVPROC glad_glTextureParameterfv; +#define glTextureParameterfv glad_glTextureParameterfv +GLAD_API_CALL PFNGLTEXTUREPARAMETERIPROC glad_glTextureParameteri; +#define glTextureParameteri glad_glTextureParameteri +GLAD_API_CALL PFNGLTEXTUREPARAMETERIVPROC glad_glTextureParameteriv; +#define glTextureParameteriv glad_glTextureParameteriv +GLAD_API_CALL PFNGLTEXTURESTORAGE1DPROC glad_glTextureStorage1D; +#define glTextureStorage1D glad_glTextureStorage1D +GLAD_API_CALL PFNGLTEXTURESTORAGE2DPROC glad_glTextureStorage2D; +#define glTextureStorage2D glad_glTextureStorage2D +GLAD_API_CALL PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glad_glTextureStorage2DMultisample; +#define glTextureStorage2DMultisample glad_glTextureStorage2DMultisample +GLAD_API_CALL PFNGLTEXTURESTORAGE3DPROC glad_glTextureStorage3D; +#define glTextureStorage3D glad_glTextureStorage3D +GLAD_API_CALL PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glad_glTextureStorage3DMultisample; +#define glTextureStorage3DMultisample glad_glTextureStorage3DMultisample +GLAD_API_CALL PFNGLTEXTURESUBIMAGE1DPROC glad_glTextureSubImage1D; +#define glTextureSubImage1D glad_glTextureSubImage1D +GLAD_API_CALL PFNGLTEXTURESUBIMAGE2DPROC glad_glTextureSubImage2D; +#define glTextureSubImage2D glad_glTextureSubImage2D +GLAD_API_CALL PFNGLTEXTURESUBIMAGE3DPROC glad_glTextureSubImage3D; +#define glTextureSubImage3D glad_glTextureSubImage3D +GLAD_API_CALL PFNGLTEXTUREVIEWPROC glad_glTextureView; +#define glTextureView glad_glTextureView +GLAD_API_CALL PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glad_glTransformFeedbackBufferBase; +#define glTransformFeedbackBufferBase glad_glTransformFeedbackBufferBase +GLAD_API_CALL PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glad_glTransformFeedbackBufferRange; +#define glTransformFeedbackBufferRange glad_glTransformFeedbackBufferRange +GLAD_API_CALL PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings; +#define glTransformFeedbackVaryings glad_glTransformFeedbackVaryings +GLAD_API_CALL PFNGLTRANSLATEXOESPROC glad_glTranslatexOES; +#define glTranslatexOES glad_glTranslatexOES +GLAD_API_CALL PFNGLUNIFORM1DPROC glad_glUniform1d; +#define glUniform1d glad_glUniform1d +GLAD_API_CALL PFNGLUNIFORM1DVPROC glad_glUniform1dv; +#define glUniform1dv glad_glUniform1dv +GLAD_API_CALL PFNGLUNIFORM1FPROC glad_glUniform1f; +#define glUniform1f glad_glUniform1f +GLAD_API_CALL PFNGLUNIFORM1FARBPROC glad_glUniform1fARB; +#define glUniform1fARB glad_glUniform1fARB +GLAD_API_CALL PFNGLUNIFORM1FVPROC glad_glUniform1fv; +#define glUniform1fv glad_glUniform1fv +GLAD_API_CALL PFNGLUNIFORM1FVARBPROC glad_glUniform1fvARB; +#define glUniform1fvARB glad_glUniform1fvARB +GLAD_API_CALL PFNGLUNIFORM1IPROC glad_glUniform1i; +#define glUniform1i glad_glUniform1i +GLAD_API_CALL PFNGLUNIFORM1I64ARBPROC glad_glUniform1i64ARB; +#define glUniform1i64ARB glad_glUniform1i64ARB +GLAD_API_CALL PFNGLUNIFORM1I64VARBPROC glad_glUniform1i64vARB; +#define glUniform1i64vARB glad_glUniform1i64vARB +GLAD_API_CALL PFNGLUNIFORM1IARBPROC glad_glUniform1iARB; +#define glUniform1iARB glad_glUniform1iARB +GLAD_API_CALL PFNGLUNIFORM1IVPROC glad_glUniform1iv; +#define glUniform1iv glad_glUniform1iv +GLAD_API_CALL PFNGLUNIFORM1IVARBPROC glad_glUniform1ivARB; +#define glUniform1ivARB glad_glUniform1ivARB +GLAD_API_CALL PFNGLUNIFORM1UIPROC glad_glUniform1ui; +#define glUniform1ui glad_glUniform1ui +GLAD_API_CALL PFNGLUNIFORM1UI64ARBPROC glad_glUniform1ui64ARB; +#define glUniform1ui64ARB glad_glUniform1ui64ARB +GLAD_API_CALL PFNGLUNIFORM1UI64VARBPROC glad_glUniform1ui64vARB; +#define glUniform1ui64vARB glad_glUniform1ui64vARB +GLAD_API_CALL PFNGLUNIFORM1UIVPROC glad_glUniform1uiv; +#define glUniform1uiv glad_glUniform1uiv +GLAD_API_CALL PFNGLUNIFORM2DPROC glad_glUniform2d; +#define glUniform2d glad_glUniform2d +GLAD_API_CALL PFNGLUNIFORM2DVPROC glad_glUniform2dv; +#define glUniform2dv glad_glUniform2dv +GLAD_API_CALL PFNGLUNIFORM2FPROC glad_glUniform2f; +#define glUniform2f glad_glUniform2f +GLAD_API_CALL PFNGLUNIFORM2FARBPROC glad_glUniform2fARB; +#define glUniform2fARB glad_glUniform2fARB +GLAD_API_CALL PFNGLUNIFORM2FVPROC glad_glUniform2fv; +#define glUniform2fv glad_glUniform2fv +GLAD_API_CALL PFNGLUNIFORM2FVARBPROC glad_glUniform2fvARB; +#define glUniform2fvARB glad_glUniform2fvARB +GLAD_API_CALL PFNGLUNIFORM2IPROC glad_glUniform2i; +#define glUniform2i glad_glUniform2i +GLAD_API_CALL PFNGLUNIFORM2I64ARBPROC glad_glUniform2i64ARB; +#define glUniform2i64ARB glad_glUniform2i64ARB +GLAD_API_CALL PFNGLUNIFORM2I64VARBPROC glad_glUniform2i64vARB; +#define glUniform2i64vARB glad_glUniform2i64vARB +GLAD_API_CALL PFNGLUNIFORM2IARBPROC glad_glUniform2iARB; +#define glUniform2iARB glad_glUniform2iARB +GLAD_API_CALL PFNGLUNIFORM2IVPROC glad_glUniform2iv; +#define glUniform2iv glad_glUniform2iv +GLAD_API_CALL PFNGLUNIFORM2IVARBPROC glad_glUniform2ivARB; +#define glUniform2ivARB glad_glUniform2ivARB +GLAD_API_CALL PFNGLUNIFORM2UIPROC glad_glUniform2ui; +#define glUniform2ui glad_glUniform2ui +GLAD_API_CALL PFNGLUNIFORM2UI64ARBPROC glad_glUniform2ui64ARB; +#define glUniform2ui64ARB glad_glUniform2ui64ARB +GLAD_API_CALL PFNGLUNIFORM2UI64VARBPROC glad_glUniform2ui64vARB; +#define glUniform2ui64vARB glad_glUniform2ui64vARB +GLAD_API_CALL PFNGLUNIFORM2UIVPROC glad_glUniform2uiv; +#define glUniform2uiv glad_glUniform2uiv +GLAD_API_CALL PFNGLUNIFORM3DPROC glad_glUniform3d; +#define glUniform3d glad_glUniform3d +GLAD_API_CALL PFNGLUNIFORM3DVPROC glad_glUniform3dv; +#define glUniform3dv glad_glUniform3dv +GLAD_API_CALL PFNGLUNIFORM3FPROC glad_glUniform3f; +#define glUniform3f glad_glUniform3f +GLAD_API_CALL PFNGLUNIFORM3FARBPROC glad_glUniform3fARB; +#define glUniform3fARB glad_glUniform3fARB +GLAD_API_CALL PFNGLUNIFORM3FVPROC glad_glUniform3fv; +#define glUniform3fv glad_glUniform3fv +GLAD_API_CALL PFNGLUNIFORM3FVARBPROC glad_glUniform3fvARB; +#define glUniform3fvARB glad_glUniform3fvARB +GLAD_API_CALL PFNGLUNIFORM3IPROC glad_glUniform3i; +#define glUniform3i glad_glUniform3i +GLAD_API_CALL PFNGLUNIFORM3I64ARBPROC glad_glUniform3i64ARB; +#define glUniform3i64ARB glad_glUniform3i64ARB +GLAD_API_CALL PFNGLUNIFORM3I64VARBPROC glad_glUniform3i64vARB; +#define glUniform3i64vARB glad_glUniform3i64vARB +GLAD_API_CALL PFNGLUNIFORM3IARBPROC glad_glUniform3iARB; +#define glUniform3iARB glad_glUniform3iARB +GLAD_API_CALL PFNGLUNIFORM3IVPROC glad_glUniform3iv; +#define glUniform3iv glad_glUniform3iv +GLAD_API_CALL PFNGLUNIFORM3IVARBPROC glad_glUniform3ivARB; +#define glUniform3ivARB glad_glUniform3ivARB +GLAD_API_CALL PFNGLUNIFORM3UIPROC glad_glUniform3ui; +#define glUniform3ui glad_glUniform3ui +GLAD_API_CALL PFNGLUNIFORM3UI64ARBPROC glad_glUniform3ui64ARB; +#define glUniform3ui64ARB glad_glUniform3ui64ARB +GLAD_API_CALL PFNGLUNIFORM3UI64VARBPROC glad_glUniform3ui64vARB; +#define glUniform3ui64vARB glad_glUniform3ui64vARB +GLAD_API_CALL PFNGLUNIFORM3UIVPROC glad_glUniform3uiv; +#define glUniform3uiv glad_glUniform3uiv +GLAD_API_CALL PFNGLUNIFORM4DPROC glad_glUniform4d; +#define glUniform4d glad_glUniform4d +GLAD_API_CALL PFNGLUNIFORM4DVPROC glad_glUniform4dv; +#define glUniform4dv glad_glUniform4dv +GLAD_API_CALL PFNGLUNIFORM4FPROC glad_glUniform4f; +#define glUniform4f glad_glUniform4f +GLAD_API_CALL PFNGLUNIFORM4FARBPROC glad_glUniform4fARB; +#define glUniform4fARB glad_glUniform4fARB +GLAD_API_CALL PFNGLUNIFORM4FVPROC glad_glUniform4fv; +#define glUniform4fv glad_glUniform4fv +GLAD_API_CALL PFNGLUNIFORM4FVARBPROC glad_glUniform4fvARB; +#define glUniform4fvARB glad_glUniform4fvARB +GLAD_API_CALL PFNGLUNIFORM4IPROC glad_glUniform4i; +#define glUniform4i glad_glUniform4i +GLAD_API_CALL PFNGLUNIFORM4I64ARBPROC glad_glUniform4i64ARB; +#define glUniform4i64ARB glad_glUniform4i64ARB +GLAD_API_CALL PFNGLUNIFORM4I64VARBPROC glad_glUniform4i64vARB; +#define glUniform4i64vARB glad_glUniform4i64vARB +GLAD_API_CALL PFNGLUNIFORM4IARBPROC glad_glUniform4iARB; +#define glUniform4iARB glad_glUniform4iARB +GLAD_API_CALL PFNGLUNIFORM4IVPROC glad_glUniform4iv; +#define glUniform4iv glad_glUniform4iv +GLAD_API_CALL PFNGLUNIFORM4IVARBPROC glad_glUniform4ivARB; +#define glUniform4ivARB glad_glUniform4ivARB +GLAD_API_CALL PFNGLUNIFORM4UIPROC glad_glUniform4ui; +#define glUniform4ui glad_glUniform4ui +GLAD_API_CALL PFNGLUNIFORM4UI64ARBPROC glad_glUniform4ui64ARB; +#define glUniform4ui64ARB glad_glUniform4ui64ARB +GLAD_API_CALL PFNGLUNIFORM4UI64VARBPROC glad_glUniform4ui64vARB; +#define glUniform4ui64vARB glad_glUniform4ui64vARB +GLAD_API_CALL PFNGLUNIFORM4UIVPROC glad_glUniform4uiv; +#define glUniform4uiv glad_glUniform4uiv +GLAD_API_CALL PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding; +#define glUniformBlockBinding glad_glUniformBlockBinding +GLAD_API_CALL PFNGLUNIFORMMATRIX2DVPROC glad_glUniformMatrix2dv; +#define glUniformMatrix2dv glad_glUniformMatrix2dv +GLAD_API_CALL PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv; +#define glUniformMatrix2fv glad_glUniformMatrix2fv +GLAD_API_CALL PFNGLUNIFORMMATRIX2FVARBPROC glad_glUniformMatrix2fvARB; +#define glUniformMatrix2fvARB glad_glUniformMatrix2fvARB +GLAD_API_CALL PFNGLUNIFORMMATRIX2X3DVPROC glad_glUniformMatrix2x3dv; +#define glUniformMatrix2x3dv glad_glUniformMatrix2x3dv +GLAD_API_CALL PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv; +#define glUniformMatrix2x3fv glad_glUniformMatrix2x3fv +GLAD_API_CALL PFNGLUNIFORMMATRIX2X4DVPROC glad_glUniformMatrix2x4dv; +#define glUniformMatrix2x4dv glad_glUniformMatrix2x4dv +GLAD_API_CALL PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv; +#define glUniformMatrix2x4fv glad_glUniformMatrix2x4fv +GLAD_API_CALL PFNGLUNIFORMMATRIX3DVPROC glad_glUniformMatrix3dv; +#define glUniformMatrix3dv glad_glUniformMatrix3dv +GLAD_API_CALL PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv; +#define glUniformMatrix3fv glad_glUniformMatrix3fv +GLAD_API_CALL PFNGLUNIFORMMATRIX3FVARBPROC glad_glUniformMatrix3fvARB; +#define glUniformMatrix3fvARB glad_glUniformMatrix3fvARB +GLAD_API_CALL PFNGLUNIFORMMATRIX3X2DVPROC glad_glUniformMatrix3x2dv; +#define glUniformMatrix3x2dv glad_glUniformMatrix3x2dv +GLAD_API_CALL PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv; +#define glUniformMatrix3x2fv glad_glUniformMatrix3x2fv +GLAD_API_CALL PFNGLUNIFORMMATRIX3X4DVPROC glad_glUniformMatrix3x4dv; +#define glUniformMatrix3x4dv glad_glUniformMatrix3x4dv +GLAD_API_CALL PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv; +#define glUniformMatrix3x4fv glad_glUniformMatrix3x4fv +GLAD_API_CALL PFNGLUNIFORMMATRIX4DVPROC glad_glUniformMatrix4dv; +#define glUniformMatrix4dv glad_glUniformMatrix4dv +GLAD_API_CALL PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv; +#define glUniformMatrix4fv glad_glUniformMatrix4fv +GLAD_API_CALL PFNGLUNIFORMMATRIX4FVARBPROC glad_glUniformMatrix4fvARB; +#define glUniformMatrix4fvARB glad_glUniformMatrix4fvARB +GLAD_API_CALL PFNGLUNIFORMMATRIX4X2DVPROC glad_glUniformMatrix4x2dv; +#define glUniformMatrix4x2dv glad_glUniformMatrix4x2dv +GLAD_API_CALL PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv; +#define glUniformMatrix4x2fv glad_glUniformMatrix4x2fv +GLAD_API_CALL PFNGLUNIFORMMATRIX4X3DVPROC glad_glUniformMatrix4x3dv; +#define glUniformMatrix4x3dv glad_glUniformMatrix4x3dv +GLAD_API_CALL PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv; +#define glUniformMatrix4x3fv glad_glUniformMatrix4x3fv +GLAD_API_CALL PFNGLUNIFORMSUBROUTINESUIVPROC glad_glUniformSubroutinesuiv; +#define glUniformSubroutinesuiv glad_glUniformSubroutinesuiv +GLAD_API_CALL PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer; +#define glUnmapBuffer glad_glUnmapBuffer +GLAD_API_CALL PFNGLUNMAPBUFFERARBPROC glad_glUnmapBufferARB; +#define glUnmapBufferARB glad_glUnmapBufferARB +GLAD_API_CALL PFNGLUNMAPNAMEDBUFFERPROC glad_glUnmapNamedBuffer; +#define glUnmapNamedBuffer glad_glUnmapNamedBuffer +GLAD_API_CALL PFNGLUSEPROGRAMPROC glad_glUseProgram; +#define glUseProgram glad_glUseProgram +GLAD_API_CALL PFNGLUSEPROGRAMOBJECTARBPROC glad_glUseProgramObjectARB; +#define glUseProgramObjectARB glad_glUseProgramObjectARB +GLAD_API_CALL PFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages; +#define glUseProgramStages glad_glUseProgramStages +GLAD_API_CALL PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; +#define glValidateProgram glad_glValidateProgram +GLAD_API_CALL PFNGLVALIDATEPROGRAMARBPROC glad_glValidateProgramARB; +#define glValidateProgramARB glad_glValidateProgramARB +GLAD_API_CALL PFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline; +#define glValidateProgramPipeline glad_glValidateProgramPipeline +GLAD_API_CALL PFNGLVERTEX2XOESPROC glad_glVertex2xOES; +#define glVertex2xOES glad_glVertex2xOES +GLAD_API_CALL PFNGLVERTEX2XVOESPROC glad_glVertex2xvOES; +#define glVertex2xvOES glad_glVertex2xvOES +GLAD_API_CALL PFNGLVERTEX3XOESPROC glad_glVertex3xOES; +#define glVertex3xOES glad_glVertex3xOES +GLAD_API_CALL PFNGLVERTEX3XVOESPROC glad_glVertex3xvOES; +#define glVertex3xvOES glad_glVertex3xvOES +GLAD_API_CALL PFNGLVERTEX4XOESPROC glad_glVertex4xOES; +#define glVertex4xOES glad_glVertex4xOES +GLAD_API_CALL PFNGLVERTEX4XVOESPROC glad_glVertex4xvOES; +#define glVertex4xvOES glad_glVertex4xvOES +GLAD_API_CALL PFNGLVERTEXARRAYATTRIBBINDINGPROC glad_glVertexArrayAttribBinding; +#define glVertexArrayAttribBinding glad_glVertexArrayAttribBinding +GLAD_API_CALL PFNGLVERTEXARRAYATTRIBFORMATPROC glad_glVertexArrayAttribFormat; +#define glVertexArrayAttribFormat glad_glVertexArrayAttribFormat +GLAD_API_CALL PFNGLVERTEXARRAYATTRIBIFORMATPROC glad_glVertexArrayAttribIFormat; +#define glVertexArrayAttribIFormat glad_glVertexArrayAttribIFormat +GLAD_API_CALL PFNGLVERTEXARRAYATTRIBLFORMATPROC glad_glVertexArrayAttribLFormat; +#define glVertexArrayAttribLFormat glad_glVertexArrayAttribLFormat +GLAD_API_CALL PFNGLVERTEXARRAYBINDINGDIVISORPROC glad_glVertexArrayBindingDivisor; +#define glVertexArrayBindingDivisor glad_glVertexArrayBindingDivisor +GLAD_API_CALL PFNGLVERTEXARRAYELEMENTBUFFERPROC glad_glVertexArrayElementBuffer; +#define glVertexArrayElementBuffer glad_glVertexArrayElementBuffer +GLAD_API_CALL PFNGLVERTEXARRAYVERTEXBUFFERPROC glad_glVertexArrayVertexBuffer; +#define glVertexArrayVertexBuffer glad_glVertexArrayVertexBuffer +GLAD_API_CALL PFNGLVERTEXARRAYVERTEXBUFFERSPROC glad_glVertexArrayVertexBuffers; +#define glVertexArrayVertexBuffers glad_glVertexArrayVertexBuffers +GLAD_API_CALL PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d; +#define glVertexAttrib1d glad_glVertexAttrib1d +GLAD_API_CALL PFNGLVERTEXATTRIB1DARBPROC glad_glVertexAttrib1dARB; +#define glVertexAttrib1dARB glad_glVertexAttrib1dARB +GLAD_API_CALL PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv; +#define glVertexAttrib1dv glad_glVertexAttrib1dv +GLAD_API_CALL PFNGLVERTEXATTRIB1DVARBPROC glad_glVertexAttrib1dvARB; +#define glVertexAttrib1dvARB glad_glVertexAttrib1dvARB +GLAD_API_CALL PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f; +#define glVertexAttrib1f glad_glVertexAttrib1f +GLAD_API_CALL PFNGLVERTEXATTRIB1FARBPROC glad_glVertexAttrib1fARB; +#define glVertexAttrib1fARB glad_glVertexAttrib1fARB +GLAD_API_CALL PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv; +#define glVertexAttrib1fv glad_glVertexAttrib1fv +GLAD_API_CALL PFNGLVERTEXATTRIB1FVARBPROC glad_glVertexAttrib1fvARB; +#define glVertexAttrib1fvARB glad_glVertexAttrib1fvARB +GLAD_API_CALL PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s; +#define glVertexAttrib1s glad_glVertexAttrib1s +GLAD_API_CALL PFNGLVERTEXATTRIB1SARBPROC glad_glVertexAttrib1sARB; +#define glVertexAttrib1sARB glad_glVertexAttrib1sARB +GLAD_API_CALL PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv; +#define glVertexAttrib1sv glad_glVertexAttrib1sv +GLAD_API_CALL PFNGLVERTEXATTRIB1SVARBPROC glad_glVertexAttrib1svARB; +#define glVertexAttrib1svARB glad_glVertexAttrib1svARB +GLAD_API_CALL PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d; +#define glVertexAttrib2d glad_glVertexAttrib2d +GLAD_API_CALL PFNGLVERTEXATTRIB2DARBPROC glad_glVertexAttrib2dARB; +#define glVertexAttrib2dARB glad_glVertexAttrib2dARB +GLAD_API_CALL PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv; +#define glVertexAttrib2dv glad_glVertexAttrib2dv +GLAD_API_CALL PFNGLVERTEXATTRIB2DVARBPROC glad_glVertexAttrib2dvARB; +#define glVertexAttrib2dvARB glad_glVertexAttrib2dvARB +GLAD_API_CALL PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f; +#define glVertexAttrib2f glad_glVertexAttrib2f +GLAD_API_CALL PFNGLVERTEXATTRIB2FARBPROC glad_glVertexAttrib2fARB; +#define glVertexAttrib2fARB glad_glVertexAttrib2fARB +GLAD_API_CALL PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv; +#define glVertexAttrib2fv glad_glVertexAttrib2fv +GLAD_API_CALL PFNGLVERTEXATTRIB2FVARBPROC glad_glVertexAttrib2fvARB; +#define glVertexAttrib2fvARB glad_glVertexAttrib2fvARB +GLAD_API_CALL PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s; +#define glVertexAttrib2s glad_glVertexAttrib2s +GLAD_API_CALL PFNGLVERTEXATTRIB2SARBPROC glad_glVertexAttrib2sARB; +#define glVertexAttrib2sARB glad_glVertexAttrib2sARB +GLAD_API_CALL PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv; +#define glVertexAttrib2sv glad_glVertexAttrib2sv +GLAD_API_CALL PFNGLVERTEXATTRIB2SVARBPROC glad_glVertexAttrib2svARB; +#define glVertexAttrib2svARB glad_glVertexAttrib2svARB +GLAD_API_CALL PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d; +#define glVertexAttrib3d glad_glVertexAttrib3d +GLAD_API_CALL PFNGLVERTEXATTRIB3DARBPROC glad_glVertexAttrib3dARB; +#define glVertexAttrib3dARB glad_glVertexAttrib3dARB +GLAD_API_CALL PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv; +#define glVertexAttrib3dv glad_glVertexAttrib3dv +GLAD_API_CALL PFNGLVERTEXATTRIB3DVARBPROC glad_glVertexAttrib3dvARB; +#define glVertexAttrib3dvARB glad_glVertexAttrib3dvARB +GLAD_API_CALL PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f; +#define glVertexAttrib3f glad_glVertexAttrib3f +GLAD_API_CALL PFNGLVERTEXATTRIB3FARBPROC glad_glVertexAttrib3fARB; +#define glVertexAttrib3fARB glad_glVertexAttrib3fARB +GLAD_API_CALL PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv; +#define glVertexAttrib3fv glad_glVertexAttrib3fv +GLAD_API_CALL PFNGLVERTEXATTRIB3FVARBPROC glad_glVertexAttrib3fvARB; +#define glVertexAttrib3fvARB glad_glVertexAttrib3fvARB +GLAD_API_CALL PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s; +#define glVertexAttrib3s glad_glVertexAttrib3s +GLAD_API_CALL PFNGLVERTEXATTRIB3SARBPROC glad_glVertexAttrib3sARB; +#define glVertexAttrib3sARB glad_glVertexAttrib3sARB +GLAD_API_CALL PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv; +#define glVertexAttrib3sv glad_glVertexAttrib3sv +GLAD_API_CALL PFNGLVERTEXATTRIB3SVARBPROC glad_glVertexAttrib3svARB; +#define glVertexAttrib3svARB glad_glVertexAttrib3svARB +GLAD_API_CALL PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv; +#define glVertexAttrib4Nbv glad_glVertexAttrib4Nbv +GLAD_API_CALL PFNGLVERTEXATTRIB4NBVARBPROC glad_glVertexAttrib4NbvARB; +#define glVertexAttrib4NbvARB glad_glVertexAttrib4NbvARB +GLAD_API_CALL PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv; +#define glVertexAttrib4Niv glad_glVertexAttrib4Niv +GLAD_API_CALL PFNGLVERTEXATTRIB4NIVARBPROC glad_glVertexAttrib4NivARB; +#define glVertexAttrib4NivARB glad_glVertexAttrib4NivARB +GLAD_API_CALL PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv; +#define glVertexAttrib4Nsv glad_glVertexAttrib4Nsv +GLAD_API_CALL PFNGLVERTEXATTRIB4NSVARBPROC glad_glVertexAttrib4NsvARB; +#define glVertexAttrib4NsvARB glad_glVertexAttrib4NsvARB +GLAD_API_CALL PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub; +#define glVertexAttrib4Nub glad_glVertexAttrib4Nub +GLAD_API_CALL PFNGLVERTEXATTRIB4NUBARBPROC glad_glVertexAttrib4NubARB; +#define glVertexAttrib4NubARB glad_glVertexAttrib4NubARB +GLAD_API_CALL PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv; +#define glVertexAttrib4Nubv glad_glVertexAttrib4Nubv +GLAD_API_CALL PFNGLVERTEXATTRIB4NUBVARBPROC glad_glVertexAttrib4NubvARB; +#define glVertexAttrib4NubvARB glad_glVertexAttrib4NubvARB +GLAD_API_CALL PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv; +#define glVertexAttrib4Nuiv glad_glVertexAttrib4Nuiv +GLAD_API_CALL PFNGLVERTEXATTRIB4NUIVARBPROC glad_glVertexAttrib4NuivARB; +#define glVertexAttrib4NuivARB glad_glVertexAttrib4NuivARB +GLAD_API_CALL PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv; +#define glVertexAttrib4Nusv glad_glVertexAttrib4Nusv +GLAD_API_CALL PFNGLVERTEXATTRIB4NUSVARBPROC glad_glVertexAttrib4NusvARB; +#define glVertexAttrib4NusvARB glad_glVertexAttrib4NusvARB +GLAD_API_CALL PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv; +#define glVertexAttrib4bv glad_glVertexAttrib4bv +GLAD_API_CALL PFNGLVERTEXATTRIB4BVARBPROC glad_glVertexAttrib4bvARB; +#define glVertexAttrib4bvARB glad_glVertexAttrib4bvARB +GLAD_API_CALL PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d; +#define glVertexAttrib4d glad_glVertexAttrib4d +GLAD_API_CALL PFNGLVERTEXATTRIB4DARBPROC glad_glVertexAttrib4dARB; +#define glVertexAttrib4dARB glad_glVertexAttrib4dARB +GLAD_API_CALL PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv; +#define glVertexAttrib4dv glad_glVertexAttrib4dv +GLAD_API_CALL PFNGLVERTEXATTRIB4DVARBPROC glad_glVertexAttrib4dvARB; +#define glVertexAttrib4dvARB glad_glVertexAttrib4dvARB +GLAD_API_CALL PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f; +#define glVertexAttrib4f glad_glVertexAttrib4f +GLAD_API_CALL PFNGLVERTEXATTRIB4FARBPROC glad_glVertexAttrib4fARB; +#define glVertexAttrib4fARB glad_glVertexAttrib4fARB +GLAD_API_CALL PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv; +#define glVertexAttrib4fv glad_glVertexAttrib4fv +GLAD_API_CALL PFNGLVERTEXATTRIB4FVARBPROC glad_glVertexAttrib4fvARB; +#define glVertexAttrib4fvARB glad_glVertexAttrib4fvARB +GLAD_API_CALL PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv; +#define glVertexAttrib4iv glad_glVertexAttrib4iv +GLAD_API_CALL PFNGLVERTEXATTRIB4IVARBPROC glad_glVertexAttrib4ivARB; +#define glVertexAttrib4ivARB glad_glVertexAttrib4ivARB +GLAD_API_CALL PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s; +#define glVertexAttrib4s glad_glVertexAttrib4s +GLAD_API_CALL PFNGLVERTEXATTRIB4SARBPROC glad_glVertexAttrib4sARB; +#define glVertexAttrib4sARB glad_glVertexAttrib4sARB +GLAD_API_CALL PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv; +#define glVertexAttrib4sv glad_glVertexAttrib4sv +GLAD_API_CALL PFNGLVERTEXATTRIB4SVARBPROC glad_glVertexAttrib4svARB; +#define glVertexAttrib4svARB glad_glVertexAttrib4svARB +GLAD_API_CALL PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv; +#define glVertexAttrib4ubv glad_glVertexAttrib4ubv +GLAD_API_CALL PFNGLVERTEXATTRIB4UBVARBPROC glad_glVertexAttrib4ubvARB; +#define glVertexAttrib4ubvARB glad_glVertexAttrib4ubvARB +GLAD_API_CALL PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv; +#define glVertexAttrib4uiv glad_glVertexAttrib4uiv +GLAD_API_CALL PFNGLVERTEXATTRIB4UIVARBPROC glad_glVertexAttrib4uivARB; +#define glVertexAttrib4uivARB glad_glVertexAttrib4uivARB +GLAD_API_CALL PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv; +#define glVertexAttrib4usv glad_glVertexAttrib4usv +GLAD_API_CALL PFNGLVERTEXATTRIB4USVARBPROC glad_glVertexAttrib4usvARB; +#define glVertexAttrib4usvARB glad_glVertexAttrib4usvARB +GLAD_API_CALL PFNGLVERTEXATTRIBBINDINGPROC glad_glVertexAttribBinding; +#define glVertexAttribBinding glad_glVertexAttribBinding +GLAD_API_CALL PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor; +#define glVertexAttribDivisor glad_glVertexAttribDivisor +GLAD_API_CALL PFNGLVERTEXATTRIBDIVISORARBPROC glad_glVertexAttribDivisorARB; +#define glVertexAttribDivisorARB glad_glVertexAttribDivisorARB +GLAD_API_CALL PFNGLVERTEXATTRIBFORMATPROC glad_glVertexAttribFormat; +#define glVertexAttribFormat glad_glVertexAttribFormat +GLAD_API_CALL PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i; +#define glVertexAttribI1i glad_glVertexAttribI1i +GLAD_API_CALL PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv; +#define glVertexAttribI1iv glad_glVertexAttribI1iv +GLAD_API_CALL PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui; +#define glVertexAttribI1ui glad_glVertexAttribI1ui +GLAD_API_CALL PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv; +#define glVertexAttribI1uiv glad_glVertexAttribI1uiv +GLAD_API_CALL PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i; +#define glVertexAttribI2i glad_glVertexAttribI2i +GLAD_API_CALL PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv; +#define glVertexAttribI2iv glad_glVertexAttribI2iv +GLAD_API_CALL PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui; +#define glVertexAttribI2ui glad_glVertexAttribI2ui +GLAD_API_CALL PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv; +#define glVertexAttribI2uiv glad_glVertexAttribI2uiv +GLAD_API_CALL PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i; +#define glVertexAttribI3i glad_glVertexAttribI3i +GLAD_API_CALL PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv; +#define glVertexAttribI3iv glad_glVertexAttribI3iv +GLAD_API_CALL PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui; +#define glVertexAttribI3ui glad_glVertexAttribI3ui +GLAD_API_CALL PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv; +#define glVertexAttribI3uiv glad_glVertexAttribI3uiv +GLAD_API_CALL PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv; +#define glVertexAttribI4bv glad_glVertexAttribI4bv +GLAD_API_CALL PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i; +#define glVertexAttribI4i glad_glVertexAttribI4i +GLAD_API_CALL PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv; +#define glVertexAttribI4iv glad_glVertexAttribI4iv +GLAD_API_CALL PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv; +#define glVertexAttribI4sv glad_glVertexAttribI4sv +GLAD_API_CALL PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv; +#define glVertexAttribI4ubv glad_glVertexAttribI4ubv +GLAD_API_CALL PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui; +#define glVertexAttribI4ui glad_glVertexAttribI4ui +GLAD_API_CALL PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv; +#define glVertexAttribI4uiv glad_glVertexAttribI4uiv +GLAD_API_CALL PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv; +#define glVertexAttribI4usv glad_glVertexAttribI4usv +GLAD_API_CALL PFNGLVERTEXATTRIBIFORMATPROC glad_glVertexAttribIFormat; +#define glVertexAttribIFormat glad_glVertexAttribIFormat +GLAD_API_CALL PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer; +#define glVertexAttribIPointer glad_glVertexAttribIPointer +GLAD_API_CALL PFNGLVERTEXATTRIBL1DPROC glad_glVertexAttribL1d; +#define glVertexAttribL1d glad_glVertexAttribL1d +GLAD_API_CALL PFNGLVERTEXATTRIBL1DVPROC glad_glVertexAttribL1dv; +#define glVertexAttribL1dv glad_glVertexAttribL1dv +GLAD_API_CALL PFNGLVERTEXATTRIBL2DPROC glad_glVertexAttribL2d; +#define glVertexAttribL2d glad_glVertexAttribL2d +GLAD_API_CALL PFNGLVERTEXATTRIBL2DVPROC glad_glVertexAttribL2dv; +#define glVertexAttribL2dv glad_glVertexAttribL2dv +GLAD_API_CALL PFNGLVERTEXATTRIBL3DPROC glad_glVertexAttribL3d; +#define glVertexAttribL3d glad_glVertexAttribL3d +GLAD_API_CALL PFNGLVERTEXATTRIBL3DVPROC glad_glVertexAttribL3dv; +#define glVertexAttribL3dv glad_glVertexAttribL3dv +GLAD_API_CALL PFNGLVERTEXATTRIBL4DPROC glad_glVertexAttribL4d; +#define glVertexAttribL4d glad_glVertexAttribL4d +GLAD_API_CALL PFNGLVERTEXATTRIBL4DVPROC glad_glVertexAttribL4dv; +#define glVertexAttribL4dv glad_glVertexAttribL4dv +GLAD_API_CALL PFNGLVERTEXATTRIBLFORMATPROC glad_glVertexAttribLFormat; +#define glVertexAttribLFormat glad_glVertexAttribLFormat +GLAD_API_CALL PFNGLVERTEXATTRIBLPOINTERPROC glad_glVertexAttribLPointer; +#define glVertexAttribLPointer glad_glVertexAttribLPointer +GLAD_API_CALL PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui; +#define glVertexAttribP1ui glad_glVertexAttribP1ui +GLAD_API_CALL PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv; +#define glVertexAttribP1uiv glad_glVertexAttribP1uiv +GLAD_API_CALL PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui; +#define glVertexAttribP2ui glad_glVertexAttribP2ui +GLAD_API_CALL PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv; +#define glVertexAttribP2uiv glad_glVertexAttribP2uiv +GLAD_API_CALL PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui; +#define glVertexAttribP3ui glad_glVertexAttribP3ui +GLAD_API_CALL PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv; +#define glVertexAttribP3uiv glad_glVertexAttribP3uiv +GLAD_API_CALL PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui; +#define glVertexAttribP4ui glad_glVertexAttribP4ui +GLAD_API_CALL PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv; +#define glVertexAttribP4uiv glad_glVertexAttribP4uiv +GLAD_API_CALL PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; +#define glVertexAttribPointer glad_glVertexAttribPointer +GLAD_API_CALL PFNGLVERTEXATTRIBPOINTERARBPROC glad_glVertexAttribPointerARB; +#define glVertexAttribPointerARB glad_glVertexAttribPointerARB +GLAD_API_CALL PFNGLVERTEXBINDINGDIVISORPROC glad_glVertexBindingDivisor; +#define glVertexBindingDivisor glad_glVertexBindingDivisor +GLAD_API_CALL PFNGLVIEWPORTPROC glad_glViewport; +#define glViewport glad_glViewport +GLAD_API_CALL PFNGLVIEWPORTARRAYVPROC glad_glViewportArrayv; +#define glViewportArrayv glad_glViewportArrayv +GLAD_API_CALL PFNGLVIEWPORTINDEXEDFPROC glad_glViewportIndexedf; +#define glViewportIndexedf glad_glViewportIndexedf +GLAD_API_CALL PFNGLVIEWPORTINDEXEDFVPROC glad_glViewportIndexedfv; +#define glViewportIndexedfv glad_glViewportIndexedfv +GLAD_API_CALL PFNGLWAITSYNCPROC glad_glWaitSync; +#define glWaitSync glad_glWaitSync + + + + + +GLAD_API_CALL int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr); +GLAD_API_CALL int gladLoadGL( GLADloadfunc load); + + + +#ifdef __cplusplus +} +#endif +#endif + +/* Source */ +#ifdef GLAD_GL_IMPLEMENTATION +#include +#include +#include + +#ifndef GLAD_IMPL_UTIL_C_ +#define GLAD_IMPL_UTIL_C_ + +#ifdef _MSC_VER +#define GLAD_IMPL_UTIL_SSCANF sscanf_s +#else +#define GLAD_IMPL_UTIL_SSCANF sscanf +#endif + +#endif /* GLAD_IMPL_UTIL_C_ */ + +#ifdef __cplusplus +extern "C" { +#endif + + + +int GLAD_GL_VERSION_1_0 = 0; +int GLAD_GL_VERSION_1_1 = 0; +int GLAD_GL_VERSION_1_2 = 0; +int GLAD_GL_VERSION_1_3 = 0; +int GLAD_GL_VERSION_1_4 = 0; +int GLAD_GL_VERSION_1_5 = 0; +int GLAD_GL_VERSION_2_0 = 0; +int GLAD_GL_VERSION_2_1 = 0; +int GLAD_GL_VERSION_3_0 = 0; +int GLAD_GL_VERSION_3_1 = 0; +int GLAD_GL_VERSION_3_2 = 0; +int GLAD_GL_VERSION_3_3 = 0; +int GLAD_GL_VERSION_4_0 = 0; +int GLAD_GL_VERSION_4_1 = 0; +int GLAD_GL_VERSION_4_2 = 0; +int GLAD_GL_VERSION_4_3 = 0; +int GLAD_GL_ARB_ES2_compatibility = 0; +int GLAD_GL_ARB_ES3_1_compatibility = 0; +int GLAD_GL_ARB_ES3_2_compatibility = 0; +int GLAD_GL_ARB_ES3_compatibility = 0; +int GLAD_GL_ARB_blend_func_extended = 0; +int GLAD_GL_ARB_buffer_storage = 0; +int GLAD_GL_ARB_clear_buffer_object = 0; +int GLAD_GL_ARB_clear_texture = 0; +int GLAD_GL_ARB_color_buffer_float = 0; +int GLAD_GL_ARB_compatibility = 0; +int GLAD_GL_ARB_compressed_texture_pixel_storage = 0; +int GLAD_GL_ARB_compute_shader = 0; +int GLAD_GL_ARB_compute_variable_group_size = 0; +int GLAD_GL_ARB_copy_buffer = 0; +int GLAD_GL_ARB_copy_image = 0; +int GLAD_GL_ARB_debug_output = 0; +int GLAD_GL_ARB_depth_buffer_float = 0; +int GLAD_GL_ARB_depth_clamp = 0; +int GLAD_GL_ARB_depth_texture = 0; +int GLAD_GL_ARB_direct_state_access = 0; +int GLAD_GL_ARB_draw_buffers = 0; +int GLAD_GL_ARB_draw_buffers_blend = 0; +int GLAD_GL_ARB_draw_elements_base_vertex = 0; +int GLAD_GL_ARB_draw_indirect = 0; +int GLAD_GL_ARB_draw_instanced = 0; +int GLAD_GL_ARB_enhanced_layouts = 0; +int GLAD_GL_ARB_explicit_attrib_location = 0; +int GLAD_GL_ARB_explicit_uniform_location = 0; +int GLAD_GL_ARB_fragment_coord_conventions = 0; +int GLAD_GL_ARB_fragment_layer_viewport = 0; +int GLAD_GL_ARB_fragment_program = 0; +int GLAD_GL_ARB_fragment_program_shadow = 0; +int GLAD_GL_ARB_fragment_shader = 0; +int GLAD_GL_ARB_fragment_shader_interlock = 0; +int GLAD_GL_ARB_framebuffer_no_attachments = 0; +int GLAD_GL_ARB_framebuffer_object = 0; +int GLAD_GL_ARB_framebuffer_sRGB = 0; +int GLAD_GL_ARB_geometry_shader4 = 0; +int GLAD_GL_ARB_get_program_binary = 0; +int GLAD_GL_ARB_get_texture_sub_image = 0; +int GLAD_GL_ARB_gl_spirv = 0; +int GLAD_GL_ARB_gpu_shader5 = 0; +int GLAD_GL_ARB_gpu_shader_fp64 = 0; +int GLAD_GL_ARB_gpu_shader_int64 = 0; +int GLAD_GL_ARB_half_float_pixel = 0; +int GLAD_GL_ARB_half_float_vertex = 0; +int GLAD_GL_ARB_instanced_arrays = 0; +int GLAD_GL_ARB_internalformat_query = 0; +int GLAD_GL_ARB_internalformat_query2 = 0; +int GLAD_GL_ARB_map_buffer_range = 0; +int GLAD_GL_ARB_multi_bind = 0; +int GLAD_GL_ARB_multi_draw_indirect = 0; +int GLAD_GL_ARB_multisample = 0; +int GLAD_GL_ARB_multitexture = 0; +int GLAD_GL_ARB_occlusion_query = 0; +int GLAD_GL_ARB_occlusion_query2 = 0; +int GLAD_GL_ARB_pipeline_statistics_query = 0; +int GLAD_GL_ARB_query_buffer_object = 0; +int GLAD_GL_ARB_sample_locations = 0; +int GLAD_GL_ARB_sample_shading = 0; +int GLAD_GL_ARB_seamless_cube_map = 0; +int GLAD_GL_ARB_seamless_cubemap_per_texture = 0; +int GLAD_GL_ARB_shader_atomic_counter_ops = 0; +int GLAD_GL_ARB_shader_atomic_counters = 0; +int GLAD_GL_ARB_shader_bit_encoding = 0; +int GLAD_GL_ARB_shader_clock = 0; +int GLAD_GL_ARB_shader_image_load_store = 0; +int GLAD_GL_ARB_shader_image_size = 0; +int GLAD_GL_ARB_shader_objects = 0; +int GLAD_GL_ARB_shader_storage_buffer_object = 0; +int GLAD_GL_ARB_shader_texture_lod = 0; +int GLAD_GL_ARB_shading_language_100 = 0; +int GLAD_GL_ARB_shading_language_420pack = 0; +int GLAD_GL_ARB_shading_language_include = 0; +int GLAD_GL_ARB_shading_language_packing = 0; +int GLAD_GL_ARB_spirv_extensions = 0; +int GLAD_GL_ARB_tessellation_shader = 0; +int GLAD_GL_ARB_texture_border_clamp = 0; +int GLAD_GL_ARB_texture_buffer_object_rgb32 = 0; +int GLAD_GL_ARB_texture_compression = 0; +int GLAD_GL_ARB_texture_cube_map = 0; +int GLAD_GL_ARB_texture_cube_map_array = 0; +int GLAD_GL_ARB_texture_env_add = 0; +int GLAD_GL_ARB_texture_filter_anisotropic = 0; +int GLAD_GL_ARB_texture_filter_minmax = 0; +int GLAD_GL_ARB_texture_float = 0; +int GLAD_GL_ARB_texture_mirror_clamp_to_edge = 0; +int GLAD_GL_ARB_texture_mirrored_repeat = 0; +int GLAD_GL_ARB_texture_multisample = 0; +int GLAD_GL_ARB_texture_non_power_of_two = 0; +int GLAD_GL_ARB_texture_rg = 0; +int GLAD_GL_ARB_texture_storage = 0; +int GLAD_GL_ARB_texture_swizzle = 0; +int GLAD_GL_ARB_texture_view = 0; +int GLAD_GL_ARB_timer_query = 0; +int GLAD_GL_ARB_transpose_matrix = 0; +int GLAD_GL_ARB_uniform_buffer_object = 0; +int GLAD_GL_ARB_vertex_array_bgra = 0; +int GLAD_GL_ARB_vertex_array_object = 0; +int GLAD_GL_ARB_vertex_attrib_binding = 0; +int GLAD_GL_ARB_vertex_buffer_object = 0; +int GLAD_GL_ARB_vertex_program = 0; +int GLAD_GL_ARB_vertex_shader = 0; +int GLAD_GL_EXT_draw_instanced = 0; +int GLAD_GL_EXT_fog_coord = 0; +int GLAD_GL_EXT_framebuffer_blit = 0; +int GLAD_GL_EXT_framebuffer_multisample = 0; +int GLAD_GL_EXT_framebuffer_object = 0; +int GLAD_GL_EXT_framebuffer_sRGB = 0; +int GLAD_GL_EXT_texture_compression_s3tc = 0; +int GLAD_GL_EXT_texture_filter_anisotropic = 0; +int GLAD_GL_EXT_texture_mirror_clamp = 0; +int GLAD_GL_KHR_texture_compression_astc_hdr = 0; +int GLAD_GL_KHR_texture_compression_astc_ldr = 0; +int GLAD_GL_OES_compressed_paletted_texture = 0; +int GLAD_GL_OES_fixed_point = 0; + + + +PFNGLACCUMXOESPROC glad_glAccumxOES = NULL; +PFNGLACTIVESHADERPROGRAMPROC glad_glActiveShaderProgram = NULL; +PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; +PFNGLACTIVETEXTUREARBPROC glad_glActiveTextureARB = NULL; +PFNGLALPHAFUNCXOESPROC glad_glAlphaFuncxOES = NULL; +PFNGLATTACHOBJECTARBPROC glad_glAttachObjectARB = NULL; +PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; +PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL; +PFNGLBEGINQUERYPROC glad_glBeginQuery = NULL; +PFNGLBEGINQUERYARBPROC glad_glBeginQueryARB = NULL; +PFNGLBEGINQUERYINDEXEDPROC glad_glBeginQueryIndexed = NULL; +PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL; +PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; +PFNGLBINDATTRIBLOCATIONARBPROC glad_glBindAttribLocationARB = NULL; +PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; +PFNGLBINDBUFFERARBPROC glad_glBindBufferARB = NULL; +PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL; +PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL; +PFNGLBINDBUFFERSBASEPROC glad_glBindBuffersBase = NULL; +PFNGLBINDBUFFERSRANGEPROC glad_glBindBuffersRange = NULL; +PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL; +PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed = NULL; +PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; +PFNGLBINDFRAMEBUFFEREXTPROC glad_glBindFramebufferEXT = NULL; +PFNGLBINDIMAGETEXTUREPROC glad_glBindImageTexture = NULL; +PFNGLBINDIMAGETEXTURESPROC glad_glBindImageTextures = NULL; +PFNGLBINDPROGRAMARBPROC glad_glBindProgramARB = NULL; +PFNGLBINDPROGRAMPIPELINEPROC glad_glBindProgramPipeline = NULL; +PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; +PFNGLBINDRENDERBUFFEREXTPROC glad_glBindRenderbufferEXT = NULL; +PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL; +PFNGLBINDSAMPLERSPROC glad_glBindSamplers = NULL; +PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; +PFNGLBINDTEXTUREUNITPROC glad_glBindTextureUnit = NULL; +PFNGLBINDTEXTURESPROC glad_glBindTextures = NULL; +PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback = NULL; +PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL; +PFNGLBINDVERTEXBUFFERPROC glad_glBindVertexBuffer = NULL; +PFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers = NULL; +PFNGLBITMAPXOESPROC glad_glBitmapxOES = NULL; +PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; +PFNGLBLENDCOLORXOESPROC glad_glBlendColorxOES = NULL; +PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; +PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; +PFNGLBLENDEQUATIONSEPARATEIPROC glad_glBlendEquationSeparatei = NULL; +PFNGLBLENDEQUATIONSEPARATEIARBPROC glad_glBlendEquationSeparateiARB = NULL; +PFNGLBLENDEQUATIONIPROC glad_glBlendEquationi = NULL; +PFNGLBLENDEQUATIONIARBPROC glad_glBlendEquationiARB = NULL; +PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; +PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; +PFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei = NULL; +PFNGLBLENDFUNCSEPARATEIARBPROC glad_glBlendFuncSeparateiARB = NULL; +PFNGLBLENDFUNCIPROC glad_glBlendFunci = NULL; +PFNGLBLENDFUNCIARBPROC glad_glBlendFunciARB = NULL; +PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL; +PFNGLBLITFRAMEBUFFEREXTPROC glad_glBlitFramebufferEXT = NULL; +PFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer = NULL; +PFNGLBUFFERDATAPROC glad_glBufferData = NULL; +PFNGLBUFFERDATAARBPROC glad_glBufferDataARB = NULL; +PFNGLBUFFERSTORAGEPROC glad_glBufferStorage = NULL; +PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; +PFNGLBUFFERSUBDATAARBPROC glad_glBufferSubDataARB = NULL; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; +PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glad_glCheckFramebufferStatusEXT = NULL; +PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glad_glCheckNamedFramebufferStatus = NULL; +PFNGLCLAMPCOLORPROC glad_glClampColor = NULL; +PFNGLCLAMPCOLORARBPROC glad_glClampColorARB = NULL; +PFNGLCLEARPROC glad_glClear = NULL; +PFNGLCLEARACCUMXOESPROC glad_glClearAccumxOES = NULL; +PFNGLCLEARBUFFERDATAPROC glad_glClearBufferData = NULL; +PFNGLCLEARBUFFERSUBDATAPROC glad_glClearBufferSubData = NULL; +PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL; +PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL; +PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL; +PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL; +PFNGLCLEARCOLORPROC glad_glClearColor = NULL; +PFNGLCLEARCOLORXOESPROC glad_glClearColorxOES = NULL; +PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL; +PFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL; +PFNGLCLEARDEPTHXOESPROC glad_glClearDepthxOES = NULL; +PFNGLCLEARNAMEDBUFFERDATAPROC glad_glClearNamedBufferData = NULL; +PFNGLCLEARNAMEDBUFFERSUBDATAPROC glad_glClearNamedBufferSubData = NULL; +PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glad_glClearNamedFramebufferfi = NULL; +PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glad_glClearNamedFramebufferfv = NULL; +PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glad_glClearNamedFramebufferiv = NULL; +PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glad_glClearNamedFramebufferuiv = NULL; +PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; +PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage = NULL; +PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage = NULL; +PFNGLCLIENTACTIVETEXTUREARBPROC glad_glClientActiveTextureARB = NULL; +PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL; +PFNGLCLIPPLANEXOESPROC glad_glClipPlanexOES = NULL; +PFNGLCOLOR3XOESPROC glad_glColor3xOES = NULL; +PFNGLCOLOR3XVOESPROC glad_glColor3xvOES = NULL; +PFNGLCOLOR4XOESPROC glad_glColor4xOES = NULL; +PFNGLCOLOR4XVOESPROC glad_glColor4xvOES = NULL; +PFNGLCOLORMASKPROC glad_glColorMask = NULL; +PFNGLCOLORMASKIPROC glad_glColorMaski = NULL; +PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; +PFNGLCOMPILESHADERARBPROC glad_glCompileShaderARB = NULL; +PFNGLCOMPILESHADERINCLUDEARBPROC glad_glCompileShaderIncludeARB = NULL; +PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL; +PFNGLCOMPRESSEDTEXIMAGE1DARBPROC glad_glCompressedTexImage1DARB = NULL; +PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; +PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glad_glCompressedTexImage2DARB = NULL; +PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL; +PFNGLCOMPRESSEDTEXIMAGE3DARBPROC glad_glCompressedTexImage3DARB = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glad_glCompressedTexSubImage1DARB = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glad_glCompressedTexSubImage2DARB = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC glad_glCompressedTexSubImage3DARB = NULL; +PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glad_glCompressedTextureSubImage1D = NULL; +PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glad_glCompressedTextureSubImage2D = NULL; +PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glad_glCompressedTextureSubImage3D = NULL; +PFNGLCONVOLUTIONPARAMETERXOESPROC glad_glConvolutionParameterxOES = NULL; +PFNGLCONVOLUTIONPARAMETERXVOESPROC glad_glConvolutionParameterxvOES = NULL; +PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL; +PFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData = NULL; +PFNGLCOPYNAMEDBUFFERSUBDATAPROC glad_glCopyNamedBufferSubData = NULL; +PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL; +PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL; +PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL; +PFNGLCOPYTEXTURESUBIMAGE1DPROC glad_glCopyTextureSubImage1D = NULL; +PFNGLCOPYTEXTURESUBIMAGE2DPROC glad_glCopyTextureSubImage2D = NULL; +PFNGLCOPYTEXTURESUBIMAGE3DPROC glad_glCopyTextureSubImage3D = NULL; +PFNGLCREATEBUFFERSPROC glad_glCreateBuffers = NULL; +PFNGLCREATEFRAMEBUFFERSPROC glad_glCreateFramebuffers = NULL; +PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; +PFNGLCREATEPROGRAMOBJECTARBPROC glad_glCreateProgramObjectARB = NULL; +PFNGLCREATEPROGRAMPIPELINESPROC glad_glCreateProgramPipelines = NULL; +PFNGLCREATEQUERIESPROC glad_glCreateQueries = NULL; +PFNGLCREATERENDERBUFFERSPROC glad_glCreateRenderbuffers = NULL; +PFNGLCREATESAMPLERSPROC glad_glCreateSamplers = NULL; +PFNGLCREATESHADERPROC glad_glCreateShader = NULL; +PFNGLCREATESHADEROBJECTARBPROC glad_glCreateShaderObjectARB = NULL; +PFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv = NULL; +PFNGLCREATETEXTURESPROC glad_glCreateTextures = NULL; +PFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks = NULL; +PFNGLCREATEVERTEXARRAYSPROC glad_glCreateVertexArrays = NULL; +PFNGLCULLFACEPROC glad_glCullFace = NULL; +PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL; +PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB = NULL; +PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL; +PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB = NULL; +PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert = NULL; +PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB = NULL; +PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; +PFNGLDELETEBUFFERSARBPROC glad_glDeleteBuffersARB = NULL; +PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; +PFNGLDELETEFRAMEBUFFERSEXTPROC glad_glDeleteFramebuffersEXT = NULL; +PFNGLDELETENAMEDSTRINGARBPROC glad_glDeleteNamedStringARB = NULL; +PFNGLDELETEOBJECTARBPROC glad_glDeleteObjectARB = NULL; +PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; +PFNGLDELETEPROGRAMPIPELINESPROC glad_glDeleteProgramPipelines = NULL; +PFNGLDELETEPROGRAMSARBPROC glad_glDeleteProgramsARB = NULL; +PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL; +PFNGLDELETEQUERIESARBPROC glad_glDeleteQueriesARB = NULL; +PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; +PFNGLDELETERENDERBUFFERSEXTPROC glad_glDeleteRenderbuffersEXT = NULL; +PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers = NULL; +PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; +PFNGLDELETESYNCPROC glad_glDeleteSync = NULL; +PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; +PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks = NULL; +PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL; +PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; +PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; +PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL; +PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv = NULL; +PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed = NULL; +PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL; +PFNGLDEPTHRANGEXOESPROC glad_glDepthRangexOES = NULL; +PFNGLDETACHOBJECTARBPROC glad_glDetachObjectARB = NULL; +PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; +PFNGLDISABLEPROC glad_glDisable = NULL; +PFNGLDISABLEVERTEXARRAYATTRIBPROC glad_glDisableVertexArrayAttrib = NULL; +PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; +PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glad_glDisableVertexAttribArrayARB = NULL; +PFNGLDISABLEIPROC glad_glDisablei = NULL; +PFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute = NULL; +PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC glad_glDispatchComputeGroupSizeARB = NULL; +PFNGLDISPATCHCOMPUTEINDIRECTPROC glad_glDispatchComputeIndirect = NULL; +PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; +PFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect = NULL; +PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL; +PFNGLDRAWARRAYSINSTANCEDARBPROC glad_glDrawArraysInstancedARB = NULL; +PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance = NULL; +PFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT = NULL; +PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL; +PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL; +PFNGLDRAWBUFFERSARBPROC glad_glDrawBuffersARB = NULL; +PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; +PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex = NULL; +PFNGLDRAWELEMENTSINDIRECTPROC glad_glDrawElementsIndirect = NULL; +PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL; +PFNGLDRAWELEMENTSINSTANCEDARBPROC glad_glDrawElementsInstancedARB = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glad_glDrawElementsInstancedBaseInstance = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance = NULL; +PFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT = NULL; +PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL; +PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL; +PFNGLDRAWTRANSFORMFEEDBACKPROC glad_glDrawTransformFeedback = NULL; +PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glad_glDrawTransformFeedbackInstanced = NULL; +PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glad_glDrawTransformFeedbackStream = NULL; +PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glad_glDrawTransformFeedbackStreamInstanced = NULL; +PFNGLENABLEPROC glad_glEnable = NULL; +PFNGLENABLEVERTEXARRAYATTRIBPROC glad_glEnableVertexArrayAttrib = NULL; +PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; +PFNGLENABLEVERTEXATTRIBARRAYARBPROC glad_glEnableVertexAttribArrayARB = NULL; +PFNGLENABLEIPROC glad_glEnablei = NULL; +PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL; +PFNGLENDQUERYPROC glad_glEndQuery = NULL; +PFNGLENDQUERYARBPROC glad_glEndQueryARB = NULL; +PFNGLENDQUERYINDEXEDPROC glad_glEndQueryIndexed = NULL; +PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL; +PFNGLEVALCOORD1XOESPROC glad_glEvalCoord1xOES = NULL; +PFNGLEVALCOORD1XVOESPROC glad_glEvalCoord1xvOES = NULL; +PFNGLEVALCOORD2XOESPROC glad_glEvalCoord2xOES = NULL; +PFNGLEVALCOORD2XVOESPROC glad_glEvalCoord2xvOES = NULL; +PFNGLEVALUATEDEPTHVALUESARBPROC glad_glEvaluateDepthValuesARB = NULL; +PFNGLFEEDBACKBUFFERXOESPROC glad_glFeedbackBufferxOES = NULL; +PFNGLFENCESYNCPROC glad_glFenceSync = NULL; +PFNGLFINISHPROC glad_glFinish = NULL; +PFNGLFLUSHPROC glad_glFlush = NULL; +PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL; +PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glad_glFlushMappedNamedBufferRange = NULL; +PFNGLFOGCOORDPOINTEREXTPROC glad_glFogCoordPointerEXT = NULL; +PFNGLFOGCOORDDEXTPROC glad_glFogCoorddEXT = NULL; +PFNGLFOGCOORDDVEXTPROC glad_glFogCoorddvEXT = NULL; +PFNGLFOGCOORDFEXTPROC glad_glFogCoordfEXT = NULL; +PFNGLFOGCOORDFVEXTPROC glad_glFogCoordfvEXT = NULL; +PFNGLFOGXOESPROC glad_glFogxOES = NULL; +PFNGLFOGXVOESPROC glad_glFogxvOES = NULL; +PFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri = NULL; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; +PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glad_glFramebufferRenderbufferEXT = NULL; +PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glad_glFramebufferSampleLocationsfvARB = NULL; +PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL; +PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL; +PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glad_glFramebufferTexture1DEXT = NULL; +PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; +PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glad_glFramebufferTexture2DEXT = NULL; +PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL; +PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glad_glFramebufferTexture3DEXT = NULL; +PFNGLFRAMEBUFFERTEXTUREARBPROC glad_glFramebufferTextureARB = NULL; +PFNGLFRAMEBUFFERTEXTUREFACEARBPROC glad_glFramebufferTextureFaceARB = NULL; +PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL; +PFNGLFRAMEBUFFERTEXTURELAYERARBPROC glad_glFramebufferTextureLayerARB = NULL; +PFNGLFRONTFACEPROC glad_glFrontFace = NULL; +PFNGLFRUSTUMXOESPROC glad_glFrustumxOES = NULL; +PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; +PFNGLGENBUFFERSARBPROC glad_glGenBuffersARB = NULL; +PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; +PFNGLGENFRAMEBUFFERSEXTPROC glad_glGenFramebuffersEXT = NULL; +PFNGLGENPROGRAMPIPELINESPROC glad_glGenProgramPipelines = NULL; +PFNGLGENPROGRAMSARBPROC glad_glGenProgramsARB = NULL; +PFNGLGENQUERIESPROC glad_glGenQueries = NULL; +PFNGLGENQUERIESARBPROC glad_glGenQueriesARB = NULL; +PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; +PFNGLGENRENDERBUFFERSEXTPROC glad_glGenRenderbuffersEXT = NULL; +PFNGLGENSAMPLERSPROC glad_glGenSamplers = NULL; +PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; +PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks = NULL; +PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL; +PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; +PFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT = NULL; +PFNGLGENERATETEXTUREMIPMAPPROC glad_glGenerateTextureMipmap = NULL; +PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glad_glGetActiveAtomicCounterBufferiv = NULL; +PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; +PFNGLGETACTIVEATTRIBARBPROC glad_glGetActiveAttribARB = NULL; +PFNGLGETACTIVESUBROUTINENAMEPROC glad_glGetActiveSubroutineName = NULL; +PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glad_glGetActiveSubroutineUniformName = NULL; +PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glad_glGetActiveSubroutineUniformiv = NULL; +PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; +PFNGLGETACTIVEUNIFORMARBPROC glad_glGetActiveUniformARB = NULL; +PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL; +PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL; +PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL; +PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL; +PFNGLGETATTACHEDOBJECTSARBPROC glad_glGetAttachedObjectsARB = NULL; +PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; +PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; +PFNGLGETATTRIBLOCATIONARBPROC glad_glGetAttribLocationARB = NULL; +PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL; +PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; +PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v = NULL; +PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; +PFNGLGETBUFFERPARAMETERIVARBPROC glad_glGetBufferParameterivARB = NULL; +PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL; +PFNGLGETBUFFERPOINTERVARBPROC glad_glGetBufferPointervARB = NULL; +PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL; +PFNGLGETBUFFERSUBDATAARBPROC glad_glGetBufferSubDataARB = NULL; +PFNGLGETCLIPPLANEXOESPROC glad_glGetClipPlanexOES = NULL; +PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL; +PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glad_glGetCompressedTexImageARB = NULL; +PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glad_glGetCompressedTextureImage = NULL; +PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage = NULL; +PFNGLGETCONVOLUTIONPARAMETERXVOESPROC glad_glGetConvolutionParameterxvOES = NULL; +PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL; +PFNGLGETDEBUGMESSAGELOGARBPROC glad_glGetDebugMessageLogARB = NULL; +PFNGLGETDOUBLEI_VPROC glad_glGetDoublei_v = NULL; +PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL; +PFNGLGETERRORPROC glad_glGetError = NULL; +PFNGLGETFIXEDVOESPROC glad_glGetFixedvOES = NULL; +PFNGLGETFLOATI_VPROC glad_glGetFloati_v = NULL; +PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; +PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex = NULL; +PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glad_glGetFramebufferAttachmentParameterivEXT = NULL; +PFNGLGETFRAMEBUFFERPARAMETERIVPROC glad_glGetFramebufferParameteriv = NULL; +PFNGLGETHANDLEARBPROC glad_glGetHandleARB = NULL; +PFNGLGETHISTOGRAMPARAMETERXVOESPROC glad_glGetHistogramParameterxvOES = NULL; +PFNGLGETINFOLOGARBPROC glad_glGetInfoLogARB = NULL; +PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL; +PFNGLGETINTEGER64VPROC glad_glGetInteger64v = NULL; +PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL; +PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; +PFNGLGETINTERNALFORMATI64VPROC glad_glGetInternalformati64v = NULL; +PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ = NULL; +PFNGLGETLIGHTXOESPROC glad_glGetLightxOES = NULL; +PFNGLGETMAPXVOESPROC glad_glGetMapxvOES = NULL; +PFNGLGETMATERIALXOESPROC glad_glGetMaterialxOES = NULL; +PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL; +PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glad_glGetNamedBufferParameteri64v = NULL; +PFNGLGETNAMEDBUFFERPARAMETERIVPROC glad_glGetNamedBufferParameteriv = NULL; +PFNGLGETNAMEDBUFFERPOINTERVPROC glad_glGetNamedBufferPointerv = NULL; +PFNGLGETNAMEDBUFFERSUBDATAPROC glad_glGetNamedBufferSubData = NULL; +PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetNamedFramebufferAttachmentParameteriv = NULL; +PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glad_glGetNamedFramebufferParameteriv = NULL; +PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glad_glGetNamedRenderbufferParameteriv = NULL; +PFNGLGETNAMEDSTRINGARBPROC glad_glGetNamedStringARB = NULL; +PFNGLGETNAMEDSTRINGIVARBPROC glad_glGetNamedStringivARB = NULL; +PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL; +PFNGLGETOBJECTPARAMETERFVARBPROC glad_glGetObjectParameterfvARB = NULL; +PFNGLGETOBJECTPARAMETERIVARBPROC glad_glGetObjectParameterivARB = NULL; +PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL; +PFNGLGETPIXELMAPXVPROC glad_glGetPixelMapxv = NULL; +PFNGLGETPOINTERVPROC glad_glGetPointerv = NULL; +PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary = NULL; +PFNGLGETPROGRAMENVPARAMETERDVARBPROC glad_glGetProgramEnvParameterdvARB = NULL; +PFNGLGETPROGRAMENVPARAMETERFVARBPROC glad_glGetProgramEnvParameterfvARB = NULL; +PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; +PFNGLGETPROGRAMINTERFACEIVPROC glad_glGetProgramInterfaceiv = NULL; +PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glad_glGetProgramLocalParameterdvARB = NULL; +PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glad_glGetProgramLocalParameterfvARB = NULL; +PFNGLGETPROGRAMPIPELINEINFOLOGPROC glad_glGetProgramPipelineInfoLog = NULL; +PFNGLGETPROGRAMPIPELINEIVPROC glad_glGetProgramPipelineiv = NULL; +PFNGLGETPROGRAMRESOURCEINDEXPROC glad_glGetProgramResourceIndex = NULL; +PFNGLGETPROGRAMRESOURCELOCATIONPROC glad_glGetProgramResourceLocation = NULL; +PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glad_glGetProgramResourceLocationIndex = NULL; +PFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName = NULL; +PFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv = NULL; +PFNGLGETPROGRAMSTAGEIVPROC glad_glGetProgramStageiv = NULL; +PFNGLGETPROGRAMSTRINGARBPROC glad_glGetProgramStringARB = NULL; +PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; +PFNGLGETPROGRAMIVARBPROC glad_glGetProgramivARB = NULL; +PFNGLGETQUERYBUFFEROBJECTI64VPROC glad_glGetQueryBufferObjecti64v = NULL; +PFNGLGETQUERYBUFFEROBJECTIVPROC glad_glGetQueryBufferObjectiv = NULL; +PFNGLGETQUERYBUFFEROBJECTUI64VPROC glad_glGetQueryBufferObjectui64v = NULL; +PFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv = NULL; +PFNGLGETQUERYINDEXEDIVPROC glad_glGetQueryIndexediv = NULL; +PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL; +PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL; +PFNGLGETQUERYOBJECTIVARBPROC glad_glGetQueryObjectivARB = NULL; +PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v = NULL; +PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL; +PFNGLGETQUERYOBJECTUIVARBPROC glad_glGetQueryObjectuivARB = NULL; +PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL; +PFNGLGETQUERYIVARBPROC glad_glGetQueryivARB = NULL; +PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; +PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glad_glGetRenderbufferParameterivEXT = NULL; +PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv = NULL; +PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv = NULL; +PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv = NULL; +PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv = NULL; +PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; +PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat = NULL; +PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; +PFNGLGETSHADERSOURCEARBPROC glad_glGetShaderSourceARB = NULL; +PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; +PFNGLGETSTRINGPROC glad_glGetString = NULL; +PFNGLGETSTRINGIPROC glad_glGetStringi = NULL; +PFNGLGETSUBROUTINEINDEXPROC glad_glGetSubroutineIndex = NULL; +PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glad_glGetSubroutineUniformLocation = NULL; +PFNGLGETSYNCIVPROC glad_glGetSynciv = NULL; +PFNGLGETTEXENVXVOESPROC glad_glGetTexEnvxvOES = NULL; +PFNGLGETTEXGENXVOESPROC glad_glGetTexGenxvOES = NULL; +PFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL; +PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL; +PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL; +PFNGLGETTEXLEVELPARAMETERXVOESPROC glad_glGetTexLevelParameterxvOES = NULL; +PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL; +PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL; +PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; +PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; +PFNGLGETTEXPARAMETERXVOESPROC glad_glGetTexParameterxvOES = NULL; +PFNGLGETTEXTUREIMAGEPROC glad_glGetTextureImage = NULL; +PFNGLGETTEXTURELEVELPARAMETERFVPROC glad_glGetTextureLevelParameterfv = NULL; +PFNGLGETTEXTURELEVELPARAMETERIVPROC glad_glGetTextureLevelParameteriv = NULL; +PFNGLGETTEXTUREPARAMETERIIVPROC glad_glGetTextureParameterIiv = NULL; +PFNGLGETTEXTUREPARAMETERIUIVPROC glad_glGetTextureParameterIuiv = NULL; +PFNGLGETTEXTUREPARAMETERFVPROC glad_glGetTextureParameterfv = NULL; +PFNGLGETTEXTUREPARAMETERIVPROC glad_glGetTextureParameteriv = NULL; +PFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage = NULL; +PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL; +PFNGLGETTRANSFORMFEEDBACKI64_VPROC glad_glGetTransformFeedbacki64_v = NULL; +PFNGLGETTRANSFORMFEEDBACKI_VPROC glad_glGetTransformFeedbacki_v = NULL; +PFNGLGETTRANSFORMFEEDBACKIVPROC glad_glGetTransformFeedbackiv = NULL; +PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL; +PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL; +PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; +PFNGLGETUNIFORMLOCATIONARBPROC glad_glGetUniformLocationARB = NULL; +PFNGLGETUNIFORMSUBROUTINEUIVPROC glad_glGetUniformSubroutineuiv = NULL; +PFNGLGETUNIFORMDVPROC glad_glGetUniformdv = NULL; +PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; +PFNGLGETUNIFORMFVARBPROC glad_glGetUniformfvARB = NULL; +PFNGLGETUNIFORMI64VARBPROC glad_glGetUniformi64vARB = NULL; +PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; +PFNGLGETUNIFORMIVARBPROC glad_glGetUniformivARB = NULL; +PFNGLGETUNIFORMUI64VARBPROC glad_glGetUniformui64vARB = NULL; +PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL; +PFNGLGETVERTEXARRAYINDEXED64IVPROC glad_glGetVertexArrayIndexed64iv = NULL; +PFNGLGETVERTEXARRAYINDEXEDIVPROC glad_glGetVertexArrayIndexediv = NULL; +PFNGLGETVERTEXARRAYIVPROC glad_glGetVertexArrayiv = NULL; +PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL; +PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL; +PFNGLGETVERTEXATTRIBLDVPROC glad_glGetVertexAttribLdv = NULL; +PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; +PFNGLGETVERTEXATTRIBPOINTERVARBPROC glad_glGetVertexAttribPointervARB = NULL; +PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL; +PFNGLGETVERTEXATTRIBDVARBPROC glad_glGetVertexAttribdvARB = NULL; +PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; +PFNGLGETVERTEXATTRIBFVARBPROC glad_glGetVertexAttribfvARB = NULL; +PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; +PFNGLGETVERTEXATTRIBIVARBPROC glad_glGetVertexAttribivARB = NULL; +PFNGLGETNUNIFORMI64VARBPROC glad_glGetnUniformi64vARB = NULL; +PFNGLGETNUNIFORMUI64VARBPROC glad_glGetnUniformui64vARB = NULL; +PFNGLHINTPROC glad_glHint = NULL; +PFNGLINDEXXOESPROC glad_glIndexxOES = NULL; +PFNGLINDEXXVOESPROC glad_glIndexxvOES = NULL; +PFNGLINVALIDATEBUFFERDATAPROC glad_glInvalidateBufferData = NULL; +PFNGLINVALIDATEBUFFERSUBDATAPROC glad_glInvalidateBufferSubData = NULL; +PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer = NULL; +PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glad_glInvalidateNamedFramebufferData = NULL; +PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glad_glInvalidateNamedFramebufferSubData = NULL; +PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer = NULL; +PFNGLINVALIDATETEXIMAGEPROC glad_glInvalidateTexImage = NULL; +PFNGLINVALIDATETEXSUBIMAGEPROC glad_glInvalidateTexSubImage = NULL; +PFNGLISBUFFERPROC glad_glIsBuffer = NULL; +PFNGLISBUFFERARBPROC glad_glIsBufferARB = NULL; +PFNGLISENABLEDPROC glad_glIsEnabled = NULL; +PFNGLISENABLEDIPROC glad_glIsEnabledi = NULL; +PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; +PFNGLISFRAMEBUFFEREXTPROC glad_glIsFramebufferEXT = NULL; +PFNGLISNAMEDSTRINGARBPROC glad_glIsNamedStringARB = NULL; +PFNGLISPROGRAMPROC glad_glIsProgram = NULL; +PFNGLISPROGRAMARBPROC glad_glIsProgramARB = NULL; +PFNGLISPROGRAMPIPELINEPROC glad_glIsProgramPipeline = NULL; +PFNGLISQUERYPROC glad_glIsQuery = NULL; +PFNGLISQUERYARBPROC glad_glIsQueryARB = NULL; +PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; +PFNGLISRENDERBUFFEREXTPROC glad_glIsRenderbufferEXT = NULL; +PFNGLISSAMPLERPROC glad_glIsSampler = NULL; +PFNGLISSHADERPROC glad_glIsShader = NULL; +PFNGLISSYNCPROC glad_glIsSync = NULL; +PFNGLISTEXTUREPROC glad_glIsTexture = NULL; +PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback = NULL; +PFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL; +PFNGLLIGHTMODELXOESPROC glad_glLightModelxOES = NULL; +PFNGLLIGHTMODELXVOESPROC glad_glLightModelxvOES = NULL; +PFNGLLIGHTXOESPROC glad_glLightxOES = NULL; +PFNGLLIGHTXVOESPROC glad_glLightxvOES = NULL; +PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; +PFNGLLINEWIDTHXOESPROC glad_glLineWidthxOES = NULL; +PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; +PFNGLLINKPROGRAMARBPROC glad_glLinkProgramARB = NULL; +PFNGLLOADMATRIXXOESPROC glad_glLoadMatrixxOES = NULL; +PFNGLLOADTRANSPOSEMATRIXDARBPROC glad_glLoadTransposeMatrixdARB = NULL; +PFNGLLOADTRANSPOSEMATRIXFARBPROC glad_glLoadTransposeMatrixfARB = NULL; +PFNGLLOADTRANSPOSEMATRIXXOESPROC glad_glLoadTransposeMatrixxOES = NULL; +PFNGLLOGICOPPROC glad_glLogicOp = NULL; +PFNGLMAP1XOESPROC glad_glMap1xOES = NULL; +PFNGLMAP2XOESPROC glad_glMap2xOES = NULL; +PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL; +PFNGLMAPBUFFERARBPROC glad_glMapBufferARB = NULL; +PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL; +PFNGLMAPGRID1XOESPROC glad_glMapGrid1xOES = NULL; +PFNGLMAPGRID2XOESPROC glad_glMapGrid2xOES = NULL; +PFNGLMAPNAMEDBUFFERPROC glad_glMapNamedBuffer = NULL; +PFNGLMAPNAMEDBUFFERRANGEPROC glad_glMapNamedBufferRange = NULL; +PFNGLMATERIALXOESPROC glad_glMaterialxOES = NULL; +PFNGLMATERIALXVOESPROC glad_glMaterialxvOES = NULL; +PFNGLMEMORYBARRIERPROC glad_glMemoryBarrier = NULL; +PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion = NULL; +PFNGLMINSAMPLESHADINGPROC glad_glMinSampleShading = NULL; +PFNGLMINSAMPLESHADINGARBPROC glad_glMinSampleShadingARB = NULL; +PFNGLMULTMATRIXXOESPROC glad_glMultMatrixxOES = NULL; +PFNGLMULTTRANSPOSEMATRIXDARBPROC glad_glMultTransposeMatrixdARB = NULL; +PFNGLMULTTRANSPOSEMATRIXFARBPROC glad_glMultTransposeMatrixfARB = NULL; +PFNGLMULTTRANSPOSEMATRIXXOESPROC glad_glMultTransposeMatrixxOES = NULL; +PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL; +PFNGLMULTIDRAWARRAYSINDIRECTPROC glad_glMultiDrawArraysIndirect = NULL; +PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL; +PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL; +PFNGLMULTIDRAWELEMENTSINDIRECTPROC glad_glMultiDrawElementsIndirect = NULL; +PFNGLMULTITEXCOORD1DARBPROC glad_glMultiTexCoord1dARB = NULL; +PFNGLMULTITEXCOORD1DVARBPROC glad_glMultiTexCoord1dvARB = NULL; +PFNGLMULTITEXCOORD1FARBPROC glad_glMultiTexCoord1fARB = NULL; +PFNGLMULTITEXCOORD1FVARBPROC glad_glMultiTexCoord1fvARB = NULL; +PFNGLMULTITEXCOORD1IARBPROC glad_glMultiTexCoord1iARB = NULL; +PFNGLMULTITEXCOORD1IVARBPROC glad_glMultiTexCoord1ivARB = NULL; +PFNGLMULTITEXCOORD1SARBPROC glad_glMultiTexCoord1sARB = NULL; +PFNGLMULTITEXCOORD1SVARBPROC glad_glMultiTexCoord1svARB = NULL; +PFNGLMULTITEXCOORD1XOESPROC glad_glMultiTexCoord1xOES = NULL; +PFNGLMULTITEXCOORD1XVOESPROC glad_glMultiTexCoord1xvOES = NULL; +PFNGLMULTITEXCOORD2DARBPROC glad_glMultiTexCoord2dARB = NULL; +PFNGLMULTITEXCOORD2DVARBPROC glad_glMultiTexCoord2dvARB = NULL; +PFNGLMULTITEXCOORD2FARBPROC glad_glMultiTexCoord2fARB = NULL; +PFNGLMULTITEXCOORD2FVARBPROC glad_glMultiTexCoord2fvARB = NULL; +PFNGLMULTITEXCOORD2IARBPROC glad_glMultiTexCoord2iARB = NULL; +PFNGLMULTITEXCOORD2IVARBPROC glad_glMultiTexCoord2ivARB = NULL; +PFNGLMULTITEXCOORD2SARBPROC glad_glMultiTexCoord2sARB = NULL; +PFNGLMULTITEXCOORD2SVARBPROC glad_glMultiTexCoord2svARB = NULL; +PFNGLMULTITEXCOORD2XOESPROC glad_glMultiTexCoord2xOES = NULL; +PFNGLMULTITEXCOORD2XVOESPROC glad_glMultiTexCoord2xvOES = NULL; +PFNGLMULTITEXCOORD3DARBPROC glad_glMultiTexCoord3dARB = NULL; +PFNGLMULTITEXCOORD3DVARBPROC glad_glMultiTexCoord3dvARB = NULL; +PFNGLMULTITEXCOORD3FARBPROC glad_glMultiTexCoord3fARB = NULL; +PFNGLMULTITEXCOORD3FVARBPROC glad_glMultiTexCoord3fvARB = NULL; +PFNGLMULTITEXCOORD3IARBPROC glad_glMultiTexCoord3iARB = NULL; +PFNGLMULTITEXCOORD3IVARBPROC glad_glMultiTexCoord3ivARB = NULL; +PFNGLMULTITEXCOORD3SARBPROC glad_glMultiTexCoord3sARB = NULL; +PFNGLMULTITEXCOORD3SVARBPROC glad_glMultiTexCoord3svARB = NULL; +PFNGLMULTITEXCOORD3XOESPROC glad_glMultiTexCoord3xOES = NULL; +PFNGLMULTITEXCOORD3XVOESPROC glad_glMultiTexCoord3xvOES = NULL; +PFNGLMULTITEXCOORD4DARBPROC glad_glMultiTexCoord4dARB = NULL; +PFNGLMULTITEXCOORD4DVARBPROC glad_glMultiTexCoord4dvARB = NULL; +PFNGLMULTITEXCOORD4FARBPROC glad_glMultiTexCoord4fARB = NULL; +PFNGLMULTITEXCOORD4FVARBPROC glad_glMultiTexCoord4fvARB = NULL; +PFNGLMULTITEXCOORD4IARBPROC glad_glMultiTexCoord4iARB = NULL; +PFNGLMULTITEXCOORD4IVARBPROC glad_glMultiTexCoord4ivARB = NULL; +PFNGLMULTITEXCOORD4SARBPROC glad_glMultiTexCoord4sARB = NULL; +PFNGLMULTITEXCOORD4SVARBPROC glad_glMultiTexCoord4svARB = NULL; +PFNGLMULTITEXCOORD4XOESPROC glad_glMultiTexCoord4xOES = NULL; +PFNGLMULTITEXCOORD4XVOESPROC glad_glMultiTexCoord4xvOES = NULL; +PFNGLNAMEDBUFFERDATAPROC glad_glNamedBufferData = NULL; +PFNGLNAMEDBUFFERSTORAGEPROC glad_glNamedBufferStorage = NULL; +PFNGLNAMEDBUFFERSUBDATAPROC glad_glNamedBufferSubData = NULL; +PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glad_glNamedFramebufferDrawBuffer = NULL; +PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glad_glNamedFramebufferDrawBuffers = NULL; +PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glad_glNamedFramebufferParameteri = NULL; +PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glad_glNamedFramebufferReadBuffer = NULL; +PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glad_glNamedFramebufferRenderbuffer = NULL; +PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glad_glNamedFramebufferSampleLocationsfvARB = NULL; +PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glad_glNamedFramebufferTexture = NULL; +PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glad_glNamedFramebufferTextureLayer = NULL; +PFNGLNAMEDRENDERBUFFERSTORAGEPROC glad_glNamedRenderbufferStorage = NULL; +PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glNamedRenderbufferStorageMultisample = NULL; +PFNGLNAMEDSTRINGARBPROC glad_glNamedStringARB = NULL; +PFNGLNORMAL3XOESPROC glad_glNormal3xOES = NULL; +PFNGLNORMAL3XVOESPROC glad_glNormal3xvOES = NULL; +PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL; +PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel = NULL; +PFNGLORTHOXOESPROC glad_glOrthoxOES = NULL; +PFNGLPASSTHROUGHXOESPROC glad_glPassThroughxOES = NULL; +PFNGLPATCHPARAMETERFVPROC glad_glPatchParameterfv = NULL; +PFNGLPATCHPARAMETERIPROC glad_glPatchParameteri = NULL; +PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback = NULL; +PFNGLPIXELMAPXPROC glad_glPixelMapx = NULL; +PFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL; +PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; +PFNGLPIXELSTOREXPROC glad_glPixelStorex = NULL; +PFNGLPIXELTRANSFERXOESPROC glad_glPixelTransferxOES = NULL; +PFNGLPIXELZOOMXOESPROC glad_glPixelZoomxOES = NULL; +PFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL; +PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL; +PFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL; +PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL; +PFNGLPOINTPARAMETERXVOESPROC glad_glPointParameterxvOES = NULL; +PFNGLPOINTSIZEPROC glad_glPointSize = NULL; +PFNGLPOINTSIZEXOESPROC glad_glPointSizexOES = NULL; +PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL; +PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; +PFNGLPOLYGONOFFSETXOESPROC glad_glPolygonOffsetxOES = NULL; +PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL; +PFNGLPRIMITIVEBOUNDINGBOXARBPROC glad_glPrimitiveBoundingBoxARB = NULL; +PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL; +PFNGLPRIORITIZETEXTURESXOESPROC glad_glPrioritizeTexturesxOES = NULL; +PFNGLPROGRAMBINARYPROC glad_glProgramBinary = NULL; +PFNGLPROGRAMENVPARAMETER4DARBPROC glad_glProgramEnvParameter4dARB = NULL; +PFNGLPROGRAMENVPARAMETER4DVARBPROC glad_glProgramEnvParameter4dvARB = NULL; +PFNGLPROGRAMENVPARAMETER4FARBPROC glad_glProgramEnvParameter4fARB = NULL; +PFNGLPROGRAMENVPARAMETER4FVARBPROC glad_glProgramEnvParameter4fvARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4DARBPROC glad_glProgramLocalParameter4dARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glad_glProgramLocalParameter4dvARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4FARBPROC glad_glProgramLocalParameter4fARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glad_glProgramLocalParameter4fvARB = NULL; +PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri = NULL; +PFNGLPROGRAMPARAMETERIARBPROC glad_glProgramParameteriARB = NULL; +PFNGLPROGRAMSTRINGARBPROC glad_glProgramStringARB = NULL; +PFNGLPROGRAMUNIFORM1DPROC glad_glProgramUniform1d = NULL; +PFNGLPROGRAMUNIFORM1DVPROC glad_glProgramUniform1dv = NULL; +PFNGLPROGRAMUNIFORM1FPROC glad_glProgramUniform1f = NULL; +PFNGLPROGRAMUNIFORM1FVPROC glad_glProgramUniform1fv = NULL; +PFNGLPROGRAMUNIFORM1IPROC glad_glProgramUniform1i = NULL; +PFNGLPROGRAMUNIFORM1I64ARBPROC glad_glProgramUniform1i64ARB = NULL; +PFNGLPROGRAMUNIFORM1I64VARBPROC glad_glProgramUniform1i64vARB = NULL; +PFNGLPROGRAMUNIFORM1IVPROC glad_glProgramUniform1iv = NULL; +PFNGLPROGRAMUNIFORM1UIPROC glad_glProgramUniform1ui = NULL; +PFNGLPROGRAMUNIFORM1UI64ARBPROC glad_glProgramUniform1ui64ARB = NULL; +PFNGLPROGRAMUNIFORM1UI64VARBPROC glad_glProgramUniform1ui64vARB = NULL; +PFNGLPROGRAMUNIFORM1UIVPROC glad_glProgramUniform1uiv = NULL; +PFNGLPROGRAMUNIFORM2DPROC glad_glProgramUniform2d = NULL; +PFNGLPROGRAMUNIFORM2DVPROC glad_glProgramUniform2dv = NULL; +PFNGLPROGRAMUNIFORM2FPROC glad_glProgramUniform2f = NULL; +PFNGLPROGRAMUNIFORM2FVPROC glad_glProgramUniform2fv = NULL; +PFNGLPROGRAMUNIFORM2IPROC glad_glProgramUniform2i = NULL; +PFNGLPROGRAMUNIFORM2I64ARBPROC glad_glProgramUniform2i64ARB = NULL; +PFNGLPROGRAMUNIFORM2I64VARBPROC glad_glProgramUniform2i64vARB = NULL; +PFNGLPROGRAMUNIFORM2IVPROC glad_glProgramUniform2iv = NULL; +PFNGLPROGRAMUNIFORM2UIPROC glad_glProgramUniform2ui = NULL; +PFNGLPROGRAMUNIFORM2UI64ARBPROC glad_glProgramUniform2ui64ARB = NULL; +PFNGLPROGRAMUNIFORM2UI64VARBPROC glad_glProgramUniform2ui64vARB = NULL; +PFNGLPROGRAMUNIFORM2UIVPROC glad_glProgramUniform2uiv = NULL; +PFNGLPROGRAMUNIFORM3DPROC glad_glProgramUniform3d = NULL; +PFNGLPROGRAMUNIFORM3DVPROC glad_glProgramUniform3dv = NULL; +PFNGLPROGRAMUNIFORM3FPROC glad_glProgramUniform3f = NULL; +PFNGLPROGRAMUNIFORM3FVPROC glad_glProgramUniform3fv = NULL; +PFNGLPROGRAMUNIFORM3IPROC glad_glProgramUniform3i = NULL; +PFNGLPROGRAMUNIFORM3I64ARBPROC glad_glProgramUniform3i64ARB = NULL; +PFNGLPROGRAMUNIFORM3I64VARBPROC glad_glProgramUniform3i64vARB = NULL; +PFNGLPROGRAMUNIFORM3IVPROC glad_glProgramUniform3iv = NULL; +PFNGLPROGRAMUNIFORM3UIPROC glad_glProgramUniform3ui = NULL; +PFNGLPROGRAMUNIFORM3UI64ARBPROC glad_glProgramUniform3ui64ARB = NULL; +PFNGLPROGRAMUNIFORM3UI64VARBPROC glad_glProgramUniform3ui64vARB = NULL; +PFNGLPROGRAMUNIFORM3UIVPROC glad_glProgramUniform3uiv = NULL; +PFNGLPROGRAMUNIFORM4DPROC glad_glProgramUniform4d = NULL; +PFNGLPROGRAMUNIFORM4DVPROC glad_glProgramUniform4dv = NULL; +PFNGLPROGRAMUNIFORM4FPROC glad_glProgramUniform4f = NULL; +PFNGLPROGRAMUNIFORM4FVPROC glad_glProgramUniform4fv = NULL; +PFNGLPROGRAMUNIFORM4IPROC glad_glProgramUniform4i = NULL; +PFNGLPROGRAMUNIFORM4I64ARBPROC glad_glProgramUniform4i64ARB = NULL; +PFNGLPROGRAMUNIFORM4I64VARBPROC glad_glProgramUniform4i64vARB = NULL; +PFNGLPROGRAMUNIFORM4IVPROC glad_glProgramUniform4iv = NULL; +PFNGLPROGRAMUNIFORM4UIPROC glad_glProgramUniform4ui = NULL; +PFNGLPROGRAMUNIFORM4UI64ARBPROC glad_glProgramUniform4ui64ARB = NULL; +PFNGLPROGRAMUNIFORM4UI64VARBPROC glad_glProgramUniform4ui64vARB = NULL; +PFNGLPROGRAMUNIFORM4UIVPROC glad_glProgramUniform4uiv = NULL; +PFNGLPROGRAMUNIFORMMATRIX2DVPROC glad_glProgramUniformMatrix2dv = NULL; +PFNGLPROGRAMUNIFORMMATRIX2FVPROC glad_glProgramUniformMatrix2fv = NULL; +PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glad_glProgramUniformMatrix2x3dv = NULL; +PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glad_glProgramUniformMatrix2x3fv = NULL; +PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glad_glProgramUniformMatrix2x4dv = NULL; +PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glad_glProgramUniformMatrix2x4fv = NULL; +PFNGLPROGRAMUNIFORMMATRIX3DVPROC glad_glProgramUniformMatrix3dv = NULL; +PFNGLPROGRAMUNIFORMMATRIX3FVPROC glad_glProgramUniformMatrix3fv = NULL; +PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glad_glProgramUniformMatrix3x2dv = NULL; +PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glad_glProgramUniformMatrix3x2fv = NULL; +PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glad_glProgramUniformMatrix3x4dv = NULL; +PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glad_glProgramUniformMatrix3x4fv = NULL; +PFNGLPROGRAMUNIFORMMATRIX4DVPROC glad_glProgramUniformMatrix4dv = NULL; +PFNGLPROGRAMUNIFORMMATRIX4FVPROC glad_glProgramUniformMatrix4fv = NULL; +PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glad_glProgramUniformMatrix4x2dv = NULL; +PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glad_glProgramUniformMatrix4x2fv = NULL; +PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glad_glProgramUniformMatrix4x3dv = NULL; +PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glad_glProgramUniformMatrix4x3fv = NULL; +PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL; +PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup = NULL; +PFNGLQUERYCOUNTERPROC glad_glQueryCounter = NULL; +PFNGLRASTERPOS2XOESPROC glad_glRasterPos2xOES = NULL; +PFNGLRASTERPOS2XVOESPROC glad_glRasterPos2xvOES = NULL; +PFNGLRASTERPOS3XOESPROC glad_glRasterPos3xOES = NULL; +PFNGLRASTERPOS3XVOESPROC glad_glRasterPos3xvOES = NULL; +PFNGLRASTERPOS4XOESPROC glad_glRasterPos4xOES = NULL; +PFNGLRASTERPOS4XVOESPROC glad_glRasterPos4xvOES = NULL; +PFNGLREADBUFFERPROC glad_glReadBuffer = NULL; +PFNGLREADPIXELSPROC glad_glReadPixels = NULL; +PFNGLRECTXOESPROC glad_glRectxOES = NULL; +PFNGLRECTXVOESPROC glad_glRectxvOES = NULL; +PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler = NULL; +PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; +PFNGLRENDERBUFFERSTORAGEEXTPROC glad_glRenderbufferStorageEXT = NULL; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMultisampleEXT = NULL; +PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback = NULL; +PFNGLROTATEXOESPROC glad_glRotatexOES = NULL; +PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; +PFNGLSAMPLECOVERAGEARBPROC glad_glSampleCoverageARB = NULL; +PFNGLSAMPLEMASKIPROC glad_glSampleMaski = NULL; +PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv = NULL; +PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv = NULL; +PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf = NULL; +PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv = NULL; +PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri = NULL; +PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv = NULL; +PFNGLSCALEXOESPROC glad_glScalexOES = NULL; +PFNGLSCISSORPROC glad_glScissor = NULL; +PFNGLSCISSORARRAYVPROC glad_glScissorArrayv = NULL; +PFNGLSCISSORINDEXEDPROC glad_glScissorIndexed = NULL; +PFNGLSCISSORINDEXEDVPROC glad_glScissorIndexedv = NULL; +PFNGLSHADERBINARYPROC glad_glShaderBinary = NULL; +PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; +PFNGLSHADERSOURCEARBPROC glad_glShaderSourceARB = NULL; +PFNGLSHADERSTORAGEBLOCKBINDINGPROC glad_glShaderStorageBlockBinding = NULL; +PFNGLSPECIALIZESHADERARBPROC glad_glSpecializeShaderARB = NULL; +PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; +PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; +PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; +PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; +PFNGLSTENCILOPPROC glad_glStencilOp = NULL; +PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; +PFNGLTEXBUFFERPROC glad_glTexBuffer = NULL; +PFNGLTEXBUFFERRANGEPROC glad_glTexBufferRange = NULL; +PFNGLTEXCOORD1XOESPROC glad_glTexCoord1xOES = NULL; +PFNGLTEXCOORD1XVOESPROC glad_glTexCoord1xvOES = NULL; +PFNGLTEXCOORD2XOESPROC glad_glTexCoord2xOES = NULL; +PFNGLTEXCOORD2XVOESPROC glad_glTexCoord2xvOES = NULL; +PFNGLTEXCOORD3XOESPROC glad_glTexCoord3xOES = NULL; +PFNGLTEXCOORD3XVOESPROC glad_glTexCoord3xvOES = NULL; +PFNGLTEXCOORD4XOESPROC glad_glTexCoord4xOES = NULL; +PFNGLTEXCOORD4XVOESPROC glad_glTexCoord4xvOES = NULL; +PFNGLTEXENVXOESPROC glad_glTexEnvxOES = NULL; +PFNGLTEXENVXVOESPROC glad_glTexEnvxvOES = NULL; +PFNGLTEXGENXOESPROC glad_glTexGenxOES = NULL; +PFNGLTEXGENXVOESPROC glad_glTexGenxvOES = NULL; +PFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL; +PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; +PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample = NULL; +PFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL; +PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample = NULL; +PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL; +PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL; +PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; +PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; +PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; +PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; +PFNGLTEXPARAMETERXOESPROC glad_glTexParameterxOES = NULL; +PFNGLTEXPARAMETERXVOESPROC glad_glTexParameterxvOES = NULL; +PFNGLTEXSTORAGE1DPROC glad_glTexStorage1D = NULL; +PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D = NULL; +PFNGLTEXSTORAGE2DMULTISAMPLEPROC glad_glTexStorage2DMultisample = NULL; +PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D = NULL; +PFNGLTEXSTORAGE3DMULTISAMPLEPROC glad_glTexStorage3DMultisample = NULL; +PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL; +PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; +PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL; +PFNGLTEXTUREBUFFERPROC glad_glTextureBuffer = NULL; +PFNGLTEXTUREBUFFERRANGEPROC glad_glTextureBufferRange = NULL; +PFNGLTEXTUREPARAMETERIIVPROC glad_glTextureParameterIiv = NULL; +PFNGLTEXTUREPARAMETERIUIVPROC glad_glTextureParameterIuiv = NULL; +PFNGLTEXTUREPARAMETERFPROC glad_glTextureParameterf = NULL; +PFNGLTEXTUREPARAMETERFVPROC glad_glTextureParameterfv = NULL; +PFNGLTEXTUREPARAMETERIPROC glad_glTextureParameteri = NULL; +PFNGLTEXTUREPARAMETERIVPROC glad_glTextureParameteriv = NULL; +PFNGLTEXTURESTORAGE1DPROC glad_glTextureStorage1D = NULL; +PFNGLTEXTURESTORAGE2DPROC glad_glTextureStorage2D = NULL; +PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glad_glTextureStorage2DMultisample = NULL; +PFNGLTEXTURESTORAGE3DPROC glad_glTextureStorage3D = NULL; +PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glad_glTextureStorage3DMultisample = NULL; +PFNGLTEXTURESUBIMAGE1DPROC glad_glTextureSubImage1D = NULL; +PFNGLTEXTURESUBIMAGE2DPROC glad_glTextureSubImage2D = NULL; +PFNGLTEXTURESUBIMAGE3DPROC glad_glTextureSubImage3D = NULL; +PFNGLTEXTUREVIEWPROC glad_glTextureView = NULL; +PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glad_glTransformFeedbackBufferBase = NULL; +PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glad_glTransformFeedbackBufferRange = NULL; +PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL; +PFNGLTRANSLATEXOESPROC glad_glTranslatexOES = NULL; +PFNGLUNIFORM1DPROC glad_glUniform1d = NULL; +PFNGLUNIFORM1DVPROC glad_glUniform1dv = NULL; +PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; +PFNGLUNIFORM1FARBPROC glad_glUniform1fARB = NULL; +PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; +PFNGLUNIFORM1FVARBPROC glad_glUniform1fvARB = NULL; +PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; +PFNGLUNIFORM1I64ARBPROC glad_glUniform1i64ARB = NULL; +PFNGLUNIFORM1I64VARBPROC glad_glUniform1i64vARB = NULL; +PFNGLUNIFORM1IARBPROC glad_glUniform1iARB = NULL; +PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; +PFNGLUNIFORM1IVARBPROC glad_glUniform1ivARB = NULL; +PFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL; +PFNGLUNIFORM1UI64ARBPROC glad_glUniform1ui64ARB = NULL; +PFNGLUNIFORM1UI64VARBPROC glad_glUniform1ui64vARB = NULL; +PFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL; +PFNGLUNIFORM2DPROC glad_glUniform2d = NULL; +PFNGLUNIFORM2DVPROC glad_glUniform2dv = NULL; +PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; +PFNGLUNIFORM2FARBPROC glad_glUniform2fARB = NULL; +PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; +PFNGLUNIFORM2FVARBPROC glad_glUniform2fvARB = NULL; +PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; +PFNGLUNIFORM2I64ARBPROC glad_glUniform2i64ARB = NULL; +PFNGLUNIFORM2I64VARBPROC glad_glUniform2i64vARB = NULL; +PFNGLUNIFORM2IARBPROC glad_glUniform2iARB = NULL; +PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; +PFNGLUNIFORM2IVARBPROC glad_glUniform2ivARB = NULL; +PFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL; +PFNGLUNIFORM2UI64ARBPROC glad_glUniform2ui64ARB = NULL; +PFNGLUNIFORM2UI64VARBPROC glad_glUniform2ui64vARB = NULL; +PFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL; +PFNGLUNIFORM3DPROC glad_glUniform3d = NULL; +PFNGLUNIFORM3DVPROC glad_glUniform3dv = NULL; +PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; +PFNGLUNIFORM3FARBPROC glad_glUniform3fARB = NULL; +PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; +PFNGLUNIFORM3FVARBPROC glad_glUniform3fvARB = NULL; +PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; +PFNGLUNIFORM3I64ARBPROC glad_glUniform3i64ARB = NULL; +PFNGLUNIFORM3I64VARBPROC glad_glUniform3i64vARB = NULL; +PFNGLUNIFORM3IARBPROC glad_glUniform3iARB = NULL; +PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; +PFNGLUNIFORM3IVARBPROC glad_glUniform3ivARB = NULL; +PFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL; +PFNGLUNIFORM3UI64ARBPROC glad_glUniform3ui64ARB = NULL; +PFNGLUNIFORM3UI64VARBPROC glad_glUniform3ui64vARB = NULL; +PFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL; +PFNGLUNIFORM4DPROC glad_glUniform4d = NULL; +PFNGLUNIFORM4DVPROC glad_glUniform4dv = NULL; +PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; +PFNGLUNIFORM4FARBPROC glad_glUniform4fARB = NULL; +PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; +PFNGLUNIFORM4FVARBPROC glad_glUniform4fvARB = NULL; +PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; +PFNGLUNIFORM4I64ARBPROC glad_glUniform4i64ARB = NULL; +PFNGLUNIFORM4I64VARBPROC glad_glUniform4i64vARB = NULL; +PFNGLUNIFORM4IARBPROC glad_glUniform4iARB = NULL; +PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; +PFNGLUNIFORM4IVARBPROC glad_glUniform4ivARB = NULL; +PFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL; +PFNGLUNIFORM4UI64ARBPROC glad_glUniform4ui64ARB = NULL; +PFNGLUNIFORM4UI64VARBPROC glad_glUniform4ui64vARB = NULL; +PFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL; +PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL; +PFNGLUNIFORMMATRIX2DVPROC glad_glUniformMatrix2dv = NULL; +PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; +PFNGLUNIFORMMATRIX2FVARBPROC glad_glUniformMatrix2fvARB = NULL; +PFNGLUNIFORMMATRIX2X3DVPROC glad_glUniformMatrix2x3dv = NULL; +PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL; +PFNGLUNIFORMMATRIX2X4DVPROC glad_glUniformMatrix2x4dv = NULL; +PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL; +PFNGLUNIFORMMATRIX3DVPROC glad_glUniformMatrix3dv = NULL; +PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; +PFNGLUNIFORMMATRIX3FVARBPROC glad_glUniformMatrix3fvARB = NULL; +PFNGLUNIFORMMATRIX3X2DVPROC glad_glUniformMatrix3x2dv = NULL; +PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL; +PFNGLUNIFORMMATRIX3X4DVPROC glad_glUniformMatrix3x4dv = NULL; +PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL; +PFNGLUNIFORMMATRIX4DVPROC glad_glUniformMatrix4dv = NULL; +PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; +PFNGLUNIFORMMATRIX4FVARBPROC glad_glUniformMatrix4fvARB = NULL; +PFNGLUNIFORMMATRIX4X2DVPROC glad_glUniformMatrix4x2dv = NULL; +PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL; +PFNGLUNIFORMMATRIX4X3DVPROC glad_glUniformMatrix4x3dv = NULL; +PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL; +PFNGLUNIFORMSUBROUTINESUIVPROC glad_glUniformSubroutinesuiv = NULL; +PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL; +PFNGLUNMAPBUFFERARBPROC glad_glUnmapBufferARB = NULL; +PFNGLUNMAPNAMEDBUFFERPROC glad_glUnmapNamedBuffer = NULL; +PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; +PFNGLUSEPROGRAMOBJECTARBPROC glad_glUseProgramObjectARB = NULL; +PFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages = NULL; +PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; +PFNGLVALIDATEPROGRAMARBPROC glad_glValidateProgramARB = NULL; +PFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline = NULL; +PFNGLVERTEX2XOESPROC glad_glVertex2xOES = NULL; +PFNGLVERTEX2XVOESPROC glad_glVertex2xvOES = NULL; +PFNGLVERTEX3XOESPROC glad_glVertex3xOES = NULL; +PFNGLVERTEX3XVOESPROC glad_glVertex3xvOES = NULL; +PFNGLVERTEX4XOESPROC glad_glVertex4xOES = NULL; +PFNGLVERTEX4XVOESPROC glad_glVertex4xvOES = NULL; +PFNGLVERTEXARRAYATTRIBBINDINGPROC glad_glVertexArrayAttribBinding = NULL; +PFNGLVERTEXARRAYATTRIBFORMATPROC glad_glVertexArrayAttribFormat = NULL; +PFNGLVERTEXARRAYATTRIBIFORMATPROC glad_glVertexArrayAttribIFormat = NULL; +PFNGLVERTEXARRAYATTRIBLFORMATPROC glad_glVertexArrayAttribLFormat = NULL; +PFNGLVERTEXARRAYBINDINGDIVISORPROC glad_glVertexArrayBindingDivisor = NULL; +PFNGLVERTEXARRAYELEMENTBUFFERPROC glad_glVertexArrayElementBuffer = NULL; +PFNGLVERTEXARRAYVERTEXBUFFERPROC glad_glVertexArrayVertexBuffer = NULL; +PFNGLVERTEXARRAYVERTEXBUFFERSPROC glad_glVertexArrayVertexBuffers = NULL; +PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL; +PFNGLVERTEXATTRIB1DARBPROC glad_glVertexAttrib1dARB = NULL; +PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL; +PFNGLVERTEXATTRIB1DVARBPROC glad_glVertexAttrib1dvARB = NULL; +PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; +PFNGLVERTEXATTRIB1FARBPROC glad_glVertexAttrib1fARB = NULL; +PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; +PFNGLVERTEXATTRIB1FVARBPROC glad_glVertexAttrib1fvARB = NULL; +PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL; +PFNGLVERTEXATTRIB1SARBPROC glad_glVertexAttrib1sARB = NULL; +PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL; +PFNGLVERTEXATTRIB1SVARBPROC glad_glVertexAttrib1svARB = NULL; +PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL; +PFNGLVERTEXATTRIB2DARBPROC glad_glVertexAttrib2dARB = NULL; +PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL; +PFNGLVERTEXATTRIB2DVARBPROC glad_glVertexAttrib2dvARB = NULL; +PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; +PFNGLVERTEXATTRIB2FARBPROC glad_glVertexAttrib2fARB = NULL; +PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; +PFNGLVERTEXATTRIB2FVARBPROC glad_glVertexAttrib2fvARB = NULL; +PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL; +PFNGLVERTEXATTRIB2SARBPROC glad_glVertexAttrib2sARB = NULL; +PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL; +PFNGLVERTEXATTRIB2SVARBPROC glad_glVertexAttrib2svARB = NULL; +PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL; +PFNGLVERTEXATTRIB3DARBPROC glad_glVertexAttrib3dARB = NULL; +PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL; +PFNGLVERTEXATTRIB3DVARBPROC glad_glVertexAttrib3dvARB = NULL; +PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; +PFNGLVERTEXATTRIB3FARBPROC glad_glVertexAttrib3fARB = NULL; +PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; +PFNGLVERTEXATTRIB3FVARBPROC glad_glVertexAttrib3fvARB = NULL; +PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL; +PFNGLVERTEXATTRIB3SARBPROC glad_glVertexAttrib3sARB = NULL; +PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL; +PFNGLVERTEXATTRIB3SVARBPROC glad_glVertexAttrib3svARB = NULL; +PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL; +PFNGLVERTEXATTRIB4NBVARBPROC glad_glVertexAttrib4NbvARB = NULL; +PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL; +PFNGLVERTEXATTRIB4NIVARBPROC glad_glVertexAttrib4NivARB = NULL; +PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL; +PFNGLVERTEXATTRIB4NSVARBPROC glad_glVertexAttrib4NsvARB = NULL; +PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL; +PFNGLVERTEXATTRIB4NUBARBPROC glad_glVertexAttrib4NubARB = NULL; +PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL; +PFNGLVERTEXATTRIB4NUBVARBPROC glad_glVertexAttrib4NubvARB = NULL; +PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL; +PFNGLVERTEXATTRIB4NUIVARBPROC glad_glVertexAttrib4NuivARB = NULL; +PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL; +PFNGLVERTEXATTRIB4NUSVARBPROC glad_glVertexAttrib4NusvARB = NULL; +PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL; +PFNGLVERTEXATTRIB4BVARBPROC glad_glVertexAttrib4bvARB = NULL; +PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL; +PFNGLVERTEXATTRIB4DARBPROC glad_glVertexAttrib4dARB = NULL; +PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL; +PFNGLVERTEXATTRIB4DVARBPROC glad_glVertexAttrib4dvARB = NULL; +PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; +PFNGLVERTEXATTRIB4FARBPROC glad_glVertexAttrib4fARB = NULL; +PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; +PFNGLVERTEXATTRIB4FVARBPROC glad_glVertexAttrib4fvARB = NULL; +PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL; +PFNGLVERTEXATTRIB4IVARBPROC glad_glVertexAttrib4ivARB = NULL; +PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL; +PFNGLVERTEXATTRIB4SARBPROC glad_glVertexAttrib4sARB = NULL; +PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL; +PFNGLVERTEXATTRIB4SVARBPROC glad_glVertexAttrib4svARB = NULL; +PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL; +PFNGLVERTEXATTRIB4UBVARBPROC glad_glVertexAttrib4ubvARB = NULL; +PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL; +PFNGLVERTEXATTRIB4UIVARBPROC glad_glVertexAttrib4uivARB = NULL; +PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL; +PFNGLVERTEXATTRIB4USVARBPROC glad_glVertexAttrib4usvARB = NULL; +PFNGLVERTEXATTRIBBINDINGPROC glad_glVertexAttribBinding = NULL; +PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor = NULL; +PFNGLVERTEXATTRIBDIVISORARBPROC glad_glVertexAttribDivisorARB = NULL; +PFNGLVERTEXATTRIBFORMATPROC glad_glVertexAttribFormat = NULL; +PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL; +PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL; +PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL; +PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL; +PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL; +PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL; +PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL; +PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL; +PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL; +PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL; +PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL; +PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL; +PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL; +PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL; +PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL; +PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL; +PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL; +PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL; +PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL; +PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL; +PFNGLVERTEXATTRIBIFORMATPROC glad_glVertexAttribIFormat = NULL; +PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL; +PFNGLVERTEXATTRIBL1DPROC glad_glVertexAttribL1d = NULL; +PFNGLVERTEXATTRIBL1DVPROC glad_glVertexAttribL1dv = NULL; +PFNGLVERTEXATTRIBL2DPROC glad_glVertexAttribL2d = NULL; +PFNGLVERTEXATTRIBL2DVPROC glad_glVertexAttribL2dv = NULL; +PFNGLVERTEXATTRIBL3DPROC glad_glVertexAttribL3d = NULL; +PFNGLVERTEXATTRIBL3DVPROC glad_glVertexAttribL3dv = NULL; +PFNGLVERTEXATTRIBL4DPROC glad_glVertexAttribL4d = NULL; +PFNGLVERTEXATTRIBL4DVPROC glad_glVertexAttribL4dv = NULL; +PFNGLVERTEXATTRIBLFORMATPROC glad_glVertexAttribLFormat = NULL; +PFNGLVERTEXATTRIBLPOINTERPROC glad_glVertexAttribLPointer = NULL; +PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui = NULL; +PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv = NULL; +PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui = NULL; +PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv = NULL; +PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui = NULL; +PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv = NULL; +PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui = NULL; +PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv = NULL; +PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; +PFNGLVERTEXATTRIBPOINTERARBPROC glad_glVertexAttribPointerARB = NULL; +PFNGLVERTEXBINDINGDIVISORPROC glad_glVertexBindingDivisor = NULL; +PFNGLVIEWPORTPROC glad_glViewport = NULL; +PFNGLVIEWPORTARRAYVPROC glad_glViewportArrayv = NULL; +PFNGLVIEWPORTINDEXEDFPROC glad_glViewportIndexedf = NULL; +PFNGLVIEWPORTINDEXEDFVPROC glad_glViewportIndexedfv = NULL; +PFNGLWAITSYNCPROC glad_glWaitSync = NULL; + + +static void glad_gl_load_GL_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_0) return; + glad_glBlendFunc = (PFNGLBLENDFUNCPROC) load(userptr, "glBlendFunc"); + glad_glClear = (PFNGLCLEARPROC) load(userptr, "glClear"); + glad_glClearColor = (PFNGLCLEARCOLORPROC) load(userptr, "glClearColor"); + glad_glClearDepth = (PFNGLCLEARDEPTHPROC) load(userptr, "glClearDepth"); + glad_glClearStencil = (PFNGLCLEARSTENCILPROC) load(userptr, "glClearStencil"); + glad_glColorMask = (PFNGLCOLORMASKPROC) load(userptr, "glColorMask"); + glad_glCullFace = (PFNGLCULLFACEPROC) load(userptr, "glCullFace"); + glad_glDepthFunc = (PFNGLDEPTHFUNCPROC) load(userptr, "glDepthFunc"); + glad_glDepthMask = (PFNGLDEPTHMASKPROC) load(userptr, "glDepthMask"); + glad_glDepthRange = (PFNGLDEPTHRANGEPROC) load(userptr, "glDepthRange"); + glad_glDisable = (PFNGLDISABLEPROC) load(userptr, "glDisable"); + glad_glDrawBuffer = (PFNGLDRAWBUFFERPROC) load(userptr, "glDrawBuffer"); + glad_glEnable = (PFNGLENABLEPROC) load(userptr, "glEnable"); + glad_glFinish = (PFNGLFINISHPROC) load(userptr, "glFinish"); + glad_glFlush = (PFNGLFLUSHPROC) load(userptr, "glFlush"); + glad_glFrontFace = (PFNGLFRONTFACEPROC) load(userptr, "glFrontFace"); + glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC) load(userptr, "glGetBooleanv"); + glad_glGetDoublev = (PFNGLGETDOUBLEVPROC) load(userptr, "glGetDoublev"); + glad_glGetError = (PFNGLGETERRORPROC) load(userptr, "glGetError"); + glad_glGetFloatv = (PFNGLGETFLOATVPROC) load(userptr, "glGetFloatv"); + glad_glGetIntegerv = (PFNGLGETINTEGERVPROC) load(userptr, "glGetIntegerv"); + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + glad_glGetTexImage = (PFNGLGETTEXIMAGEPROC) load(userptr, "glGetTexImage"); + glad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC) load(userptr, "glGetTexLevelParameterfv"); + glad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC) load(userptr, "glGetTexLevelParameteriv"); + glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC) load(userptr, "glGetTexParameterfv"); + glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC) load(userptr, "glGetTexParameteriv"); + glad_glHint = (PFNGLHINTPROC) load(userptr, "glHint"); + glad_glIsEnabled = (PFNGLISENABLEDPROC) load(userptr, "glIsEnabled"); + glad_glLineWidth = (PFNGLLINEWIDTHPROC) load(userptr, "glLineWidth"); + glad_glLogicOp = (PFNGLLOGICOPPROC) load(userptr, "glLogicOp"); + glad_glPixelStoref = (PFNGLPIXELSTOREFPROC) load(userptr, "glPixelStoref"); + glad_glPixelStorei = (PFNGLPIXELSTOREIPROC) load(userptr, "glPixelStorei"); + glad_glPointSize = (PFNGLPOINTSIZEPROC) load(userptr, "glPointSize"); + glad_glPolygonMode = (PFNGLPOLYGONMODEPROC) load(userptr, "glPolygonMode"); + glad_glReadBuffer = (PFNGLREADBUFFERPROC) load(userptr, "glReadBuffer"); + glad_glReadPixels = (PFNGLREADPIXELSPROC) load(userptr, "glReadPixels"); + glad_glScissor = (PFNGLSCISSORPROC) load(userptr, "glScissor"); + glad_glStencilFunc = (PFNGLSTENCILFUNCPROC) load(userptr, "glStencilFunc"); + glad_glStencilMask = (PFNGLSTENCILMASKPROC) load(userptr, "glStencilMask"); + glad_glStencilOp = (PFNGLSTENCILOPPROC) load(userptr, "glStencilOp"); + glad_glTexImage1D = (PFNGLTEXIMAGE1DPROC) load(userptr, "glTexImage1D"); + glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC) load(userptr, "glTexImage2D"); + glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC) load(userptr, "glTexParameterf"); + glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC) load(userptr, "glTexParameterfv"); + glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC) load(userptr, "glTexParameteri"); + glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC) load(userptr, "glTexParameteriv"); + glad_glViewport = (PFNGLVIEWPORTPROC) load(userptr, "glViewport"); +} +static void glad_gl_load_GL_VERSION_1_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_1) return; + glad_glBindTexture = (PFNGLBINDTEXTUREPROC) load(userptr, "glBindTexture"); + glad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC) load(userptr, "glCopyTexImage1D"); + glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC) load(userptr, "glCopyTexImage2D"); + glad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC) load(userptr, "glCopyTexSubImage1D"); + glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC) load(userptr, "glCopyTexSubImage2D"); + glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC) load(userptr, "glDeleteTextures"); + glad_glDrawArrays = (PFNGLDRAWARRAYSPROC) load(userptr, "glDrawArrays"); + glad_glDrawElements = (PFNGLDRAWELEMENTSPROC) load(userptr, "glDrawElements"); + glad_glGenTextures = (PFNGLGENTEXTURESPROC) load(userptr, "glGenTextures"); + glad_glGetPointerv = (PFNGLGETPOINTERVPROC) load(userptr, "glGetPointerv"); + glad_glIsTexture = (PFNGLISTEXTUREPROC) load(userptr, "glIsTexture"); + glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC) load(userptr, "glPolygonOffset"); + glad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC) load(userptr, "glTexSubImage1D"); + glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) load(userptr, "glTexSubImage2D"); +} +static void glad_gl_load_GL_VERSION_1_2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_2) return; + glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC) load(userptr, "glCopyTexSubImage3D"); + glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC) load(userptr, "glDrawRangeElements"); + glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC) load(userptr, "glTexImage3D"); + glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC) load(userptr, "glTexSubImage3D"); +} +static void glad_gl_load_GL_VERSION_1_3( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_3) return; + glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC) load(userptr, "glActiveTexture"); + glad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC) load(userptr, "glCompressedTexImage1D"); + glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) load(userptr, "glCompressedTexImage2D"); + glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC) load(userptr, "glCompressedTexImage3D"); + glad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) load(userptr, "glCompressedTexSubImage1D"); + glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) load(userptr, "glCompressedTexSubImage2D"); + glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) load(userptr, "glCompressedTexSubImage3D"); + glad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC) load(userptr, "glGetCompressedTexImage"); + glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load(userptr, "glSampleCoverage"); +} +static void glad_gl_load_GL_VERSION_1_4( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_4) return; + glad_glBlendColor = (PFNGLBLENDCOLORPROC) load(userptr, "glBlendColor"); + glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC) load(userptr, "glBlendEquation"); + glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) load(userptr, "glBlendFuncSeparate"); + glad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC) load(userptr, "glMultiDrawArrays"); + glad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC) load(userptr, "glMultiDrawElements"); + glad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC) load(userptr, "glPointParameterf"); + glad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC) load(userptr, "glPointParameterfv"); + glad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC) load(userptr, "glPointParameteri"); + glad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC) load(userptr, "glPointParameteriv"); +} +static void glad_gl_load_GL_VERSION_1_5( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_1_5) return; + glad_glBeginQuery = (PFNGLBEGINQUERYPROC) load(userptr, "glBeginQuery"); + glad_glBindBuffer = (PFNGLBINDBUFFERPROC) load(userptr, "glBindBuffer"); + glad_glBufferData = (PFNGLBUFFERDATAPROC) load(userptr, "glBufferData"); + glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) load(userptr, "glBufferSubData"); + glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) load(userptr, "glDeleteBuffers"); + glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC) load(userptr, "glDeleteQueries"); + glad_glEndQuery = (PFNGLENDQUERYPROC) load(userptr, "glEndQuery"); + glad_glGenBuffers = (PFNGLGENBUFFERSPROC) load(userptr, "glGenBuffers"); + glad_glGenQueries = (PFNGLGENQUERIESPROC) load(userptr, "glGenQueries"); + glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) load(userptr, "glGetBufferParameteriv"); + glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC) load(userptr, "glGetBufferPointerv"); + glad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC) load(userptr, "glGetBufferSubData"); + glad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC) load(userptr, "glGetQueryObjectiv"); + glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC) load(userptr, "glGetQueryObjectuiv"); + glad_glGetQueryiv = (PFNGLGETQUERYIVPROC) load(userptr, "glGetQueryiv"); + glad_glIsBuffer = (PFNGLISBUFFERPROC) load(userptr, "glIsBuffer"); + glad_glIsQuery = (PFNGLISQUERYPROC) load(userptr, "glIsQuery"); + glad_glMapBuffer = (PFNGLMAPBUFFERPROC) load(userptr, "glMapBuffer"); + glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) load(userptr, "glUnmapBuffer"); +} +static void glad_gl_load_GL_VERSION_2_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_2_0) return; + glad_glAttachShader = (PFNGLATTACHSHADERPROC) load(userptr, "glAttachShader"); + glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) load(userptr, "glBindAttribLocation"); + glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC) load(userptr, "glBlendEquationSeparate"); + glad_glCompileShader = (PFNGLCOMPILESHADERPROC) load(userptr, "glCompileShader"); + glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC) load(userptr, "glCreateProgram"); + glad_glCreateShader = (PFNGLCREATESHADERPROC) load(userptr, "glCreateShader"); + glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC) load(userptr, "glDeleteProgram"); + glad_glDeleteShader = (PFNGLDELETESHADERPROC) load(userptr, "glDeleteShader"); + glad_glDetachShader = (PFNGLDETACHSHADERPROC) load(userptr, "glDetachShader"); + glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load(userptr, "glDisableVertexAttribArray"); + glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC) load(userptr, "glDrawBuffers"); + glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load(userptr, "glEnableVertexAttribArray"); + glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) load(userptr, "glGetActiveAttrib"); + glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) load(userptr, "glGetActiveUniform"); + glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) load(userptr, "glGetAttachedShaders"); + glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) load(userptr, "glGetAttribLocation"); + glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) load(userptr, "glGetProgramInfoLog"); + glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC) load(userptr, "glGetProgramiv"); + glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) load(userptr, "glGetShaderInfoLog"); + glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC) load(userptr, "glGetShaderSource"); + glad_glGetShaderiv = (PFNGLGETSHADERIVPROC) load(userptr, "glGetShaderiv"); + glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) load(userptr, "glGetUniformLocation"); + glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC) load(userptr, "glGetUniformfv"); + glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC) load(userptr, "glGetUniformiv"); + glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load(userptr, "glGetVertexAttribPointerv"); + glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC) load(userptr, "glGetVertexAttribdv"); + glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load(userptr, "glGetVertexAttribfv"); + glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load(userptr, "glGetVertexAttribiv"); + glad_glIsProgram = (PFNGLISPROGRAMPROC) load(userptr, "glIsProgram"); + glad_glIsShader = (PFNGLISSHADERPROC) load(userptr, "glIsShader"); + glad_glLinkProgram = (PFNGLLINKPROGRAMPROC) load(userptr, "glLinkProgram"); + glad_glShaderSource = (PFNGLSHADERSOURCEPROC) load(userptr, "glShaderSource"); + glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) load(userptr, "glStencilFuncSeparate"); + glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) load(userptr, "glStencilMaskSeparate"); + glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) load(userptr, "glStencilOpSeparate"); + glad_glUniform1f = (PFNGLUNIFORM1FPROC) load(userptr, "glUniform1f"); + glad_glUniform1fv = (PFNGLUNIFORM1FVPROC) load(userptr, "glUniform1fv"); + glad_glUniform1i = (PFNGLUNIFORM1IPROC) load(userptr, "glUniform1i"); + glad_glUniform1iv = (PFNGLUNIFORM1IVPROC) load(userptr, "glUniform1iv"); + glad_glUniform2f = (PFNGLUNIFORM2FPROC) load(userptr, "glUniform2f"); + glad_glUniform2fv = (PFNGLUNIFORM2FVPROC) load(userptr, "glUniform2fv"); + glad_glUniform2i = (PFNGLUNIFORM2IPROC) load(userptr, "glUniform2i"); + glad_glUniform2iv = (PFNGLUNIFORM2IVPROC) load(userptr, "glUniform2iv"); + glad_glUniform3f = (PFNGLUNIFORM3FPROC) load(userptr, "glUniform3f"); + glad_glUniform3fv = (PFNGLUNIFORM3FVPROC) load(userptr, "glUniform3fv"); + glad_glUniform3i = (PFNGLUNIFORM3IPROC) load(userptr, "glUniform3i"); + glad_glUniform3iv = (PFNGLUNIFORM3IVPROC) load(userptr, "glUniform3iv"); + glad_glUniform4f = (PFNGLUNIFORM4FPROC) load(userptr, "glUniform4f"); + glad_glUniform4fv = (PFNGLUNIFORM4FVPROC) load(userptr, "glUniform4fv"); + glad_glUniform4i = (PFNGLUNIFORM4IPROC) load(userptr, "glUniform4i"); + glad_glUniform4iv = (PFNGLUNIFORM4IVPROC) load(userptr, "glUniform4iv"); + glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) load(userptr, "glUniformMatrix2fv"); + glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) load(userptr, "glUniformMatrix3fv"); + glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) load(userptr, "glUniformMatrix4fv"); + glad_glUseProgram = (PFNGLUSEPROGRAMPROC) load(userptr, "glUseProgram"); + glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC) load(userptr, "glValidateProgram"); + glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC) load(userptr, "glVertexAttrib1d"); + glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC) load(userptr, "glVertexAttrib1dv"); + glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load(userptr, "glVertexAttrib1f"); + glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load(userptr, "glVertexAttrib1fv"); + glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC) load(userptr, "glVertexAttrib1s"); + glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC) load(userptr, "glVertexAttrib1sv"); + glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC) load(userptr, "glVertexAttrib2d"); + glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC) load(userptr, "glVertexAttrib2dv"); + glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load(userptr, "glVertexAttrib2f"); + glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load(userptr, "glVertexAttrib2fv"); + glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC) load(userptr, "glVertexAttrib2s"); + glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC) load(userptr, "glVertexAttrib2sv"); + glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC) load(userptr, "glVertexAttrib3d"); + glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC) load(userptr, "glVertexAttrib3dv"); + glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load(userptr, "glVertexAttrib3f"); + glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load(userptr, "glVertexAttrib3fv"); + glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC) load(userptr, "glVertexAttrib3s"); + glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC) load(userptr, "glVertexAttrib3sv"); + glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC) load(userptr, "glVertexAttrib4Nbv"); + glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC) load(userptr, "glVertexAttrib4Niv"); + glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC) load(userptr, "glVertexAttrib4Nsv"); + glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC) load(userptr, "glVertexAttrib4Nub"); + glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC) load(userptr, "glVertexAttrib4Nubv"); + glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC) load(userptr, "glVertexAttrib4Nuiv"); + glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC) load(userptr, "glVertexAttrib4Nusv"); + glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC) load(userptr, "glVertexAttrib4bv"); + glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC) load(userptr, "glVertexAttrib4d"); + glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC) load(userptr, "glVertexAttrib4dv"); + glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load(userptr, "glVertexAttrib4f"); + glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load(userptr, "glVertexAttrib4fv"); + glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC) load(userptr, "glVertexAttrib4iv"); + glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC) load(userptr, "glVertexAttrib4s"); + glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC) load(userptr, "glVertexAttrib4sv"); + glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC) load(userptr, "glVertexAttrib4ubv"); + glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC) load(userptr, "glVertexAttrib4uiv"); + glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC) load(userptr, "glVertexAttrib4usv"); + glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load(userptr, "glVertexAttribPointer"); +} +static void glad_gl_load_GL_VERSION_2_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_2_1) return; + glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC) load(userptr, "glUniformMatrix2x3fv"); + glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC) load(userptr, "glUniformMatrix2x4fv"); + glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC) load(userptr, "glUniformMatrix3x2fv"); + glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC) load(userptr, "glUniformMatrix3x4fv"); + glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC) load(userptr, "glUniformMatrix4x2fv"); + glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC) load(userptr, "glUniformMatrix4x3fv"); +} +static void glad_gl_load_GL_VERSION_3_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_0) return; + glad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC) load(userptr, "glBeginConditionalRender"); + glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC) load(userptr, "glBeginTransformFeedback"); + glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); + glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); + glad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC) load(userptr, "glBindFragDataLocation"); + glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load(userptr, "glBindFramebuffer"); + glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load(userptr, "glBindRenderbuffer"); + glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC) load(userptr, "glBindVertexArray"); + glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) load(userptr, "glBlitFramebuffer"); + glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckFramebufferStatus"); + glad_glClampColor = (PFNGLCLAMPCOLORPROC) load(userptr, "glClampColor"); + glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC) load(userptr, "glClearBufferfi"); + glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC) load(userptr, "glClearBufferfv"); + glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC) load(userptr, "glClearBufferiv"); + glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC) load(userptr, "glClearBufferuiv"); + glad_glColorMaski = (PFNGLCOLORMASKIPROC) load(userptr, "glColorMaski"); + glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load(userptr, "glDeleteFramebuffers"); + glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load(userptr, "glDeleteRenderbuffers"); + glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) load(userptr, "glDeleteVertexArrays"); + glad_glDisablei = (PFNGLDISABLEIPROC) load(userptr, "glDisablei"); + glad_glEnablei = (PFNGLENABLEIPROC) load(userptr, "glEnablei"); + glad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC) load(userptr, "glEndConditionalRender"); + glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC) load(userptr, "glEndTransformFeedback"); + glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) load(userptr, "glFlushMappedBufferRange"); + glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glFramebufferRenderbuffer"); + glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) load(userptr, "glFramebufferTexture1D"); + glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load(userptr, "glFramebufferTexture2D"); + glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) load(userptr, "glFramebufferTexture3D"); + glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) load(userptr, "glFramebufferTextureLayer"); + glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load(userptr, "glGenFramebuffers"); + glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load(userptr, "glGenRenderbuffers"); + glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) load(userptr, "glGenVertexArrays"); + glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load(userptr, "glGenerateMipmap"); + glad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC) load(userptr, "glGetBooleani_v"); + glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC) load(userptr, "glGetFragDataLocation"); + glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetFramebufferAttachmentParameteriv"); + glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); + glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetRenderbufferParameteriv"); + glad_glGetStringi = (PFNGLGETSTRINGIPROC) load(userptr, "glGetStringi"); + glad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC) load(userptr, "glGetTexParameterIiv"); + glad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC) load(userptr, "glGetTexParameterIuiv"); + glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) load(userptr, "glGetTransformFeedbackVarying"); + glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC) load(userptr, "glGetUniformuiv"); + glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC) load(userptr, "glGetVertexAttribIiv"); + glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC) load(userptr, "glGetVertexAttribIuiv"); + glad_glIsEnabledi = (PFNGLISENABLEDIPROC) load(userptr, "glIsEnabledi"); + glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load(userptr, "glIsFramebuffer"); + glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load(userptr, "glIsRenderbuffer"); + glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC) load(userptr, "glIsVertexArray"); + glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) load(userptr, "glMapBufferRange"); + glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage"); + glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glRenderbufferStorageMultisample"); + glad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC) load(userptr, "glTexParameterIiv"); + glad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC) load(userptr, "glTexParameterIuiv"); + glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC) load(userptr, "glTransformFeedbackVaryings"); + glad_glUniform1ui = (PFNGLUNIFORM1UIPROC) load(userptr, "glUniform1ui"); + glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC) load(userptr, "glUniform1uiv"); + glad_glUniform2ui = (PFNGLUNIFORM2UIPROC) load(userptr, "glUniform2ui"); + glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC) load(userptr, "glUniform2uiv"); + glad_glUniform3ui = (PFNGLUNIFORM3UIPROC) load(userptr, "glUniform3ui"); + glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC) load(userptr, "glUniform3uiv"); + glad_glUniform4ui = (PFNGLUNIFORM4UIPROC) load(userptr, "glUniform4ui"); + glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC) load(userptr, "glUniform4uiv"); + glad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC) load(userptr, "glVertexAttribI1i"); + glad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC) load(userptr, "glVertexAttribI1iv"); + glad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC) load(userptr, "glVertexAttribI1ui"); + glad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC) load(userptr, "glVertexAttribI1uiv"); + glad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC) load(userptr, "glVertexAttribI2i"); + glad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC) load(userptr, "glVertexAttribI2iv"); + glad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC) load(userptr, "glVertexAttribI2ui"); + glad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC) load(userptr, "glVertexAttribI2uiv"); + glad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC) load(userptr, "glVertexAttribI3i"); + glad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC) load(userptr, "glVertexAttribI3iv"); + glad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC) load(userptr, "glVertexAttribI3ui"); + glad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC) load(userptr, "glVertexAttribI3uiv"); + glad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC) load(userptr, "glVertexAttribI4bv"); + glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC) load(userptr, "glVertexAttribI4i"); + glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC) load(userptr, "glVertexAttribI4iv"); + glad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC) load(userptr, "glVertexAttribI4sv"); + glad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC) load(userptr, "glVertexAttribI4ubv"); + glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC) load(userptr, "glVertexAttribI4ui"); + glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC) load(userptr, "glVertexAttribI4uiv"); + glad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC) load(userptr, "glVertexAttribI4usv"); + glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC) load(userptr, "glVertexAttribIPointer"); +} +static void glad_gl_load_GL_VERSION_3_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_1) return; + glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); + glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); + glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC) load(userptr, "glCopyBufferSubData"); + glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC) load(userptr, "glDrawArraysInstanced"); + glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC) load(userptr, "glDrawElementsInstanced"); + glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) load(userptr, "glGetActiveUniformBlockName"); + glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC) load(userptr, "glGetActiveUniformBlockiv"); + glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC) load(userptr, "glGetActiveUniformName"); + glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC) load(userptr, "glGetActiveUniformsiv"); + glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); + glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC) load(userptr, "glGetUniformBlockIndex"); + glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC) load(userptr, "glGetUniformIndices"); + glad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC) load(userptr, "glPrimitiveRestartIndex"); + glad_glTexBuffer = (PFNGLTEXBUFFERPROC) load(userptr, "glTexBuffer"); + glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC) load(userptr, "glUniformBlockBinding"); +} +static void glad_gl_load_GL_VERSION_3_2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_2) return; + glad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC) load(userptr, "glClientWaitSync"); + glad_glDeleteSync = (PFNGLDELETESYNCPROC) load(userptr, "glDeleteSync"); + glad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glDrawElementsBaseVertex"); + glad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) load(userptr, "glDrawElementsInstancedBaseVertex"); + glad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) load(userptr, "glDrawRangeElementsBaseVertex"); + glad_glFenceSync = (PFNGLFENCESYNCPROC) load(userptr, "glFenceSync"); + glad_glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC) load(userptr, "glFramebufferTexture"); + glad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC) load(userptr, "glGetBufferParameteri64v"); + glad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC) load(userptr, "glGetInteger64i_v"); + glad_glGetInteger64v = (PFNGLGETINTEGER64VPROC) load(userptr, "glGetInteger64v"); + glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) load(userptr, "glGetMultisamplefv"); + glad_glGetSynciv = (PFNGLGETSYNCIVPROC) load(userptr, "glGetSynciv"); + glad_glIsSync = (PFNGLISSYNCPROC) load(userptr, "glIsSync"); + glad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glMultiDrawElementsBaseVertex"); + glad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC) load(userptr, "glProvokingVertex"); + glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC) load(userptr, "glSampleMaski"); + glad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC) load(userptr, "glTexImage2DMultisample"); + glad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) load(userptr, "glTexImage3DMultisample"); + glad_glWaitSync = (PFNGLWAITSYNCPROC) load(userptr, "glWaitSync"); +} +static void glad_gl_load_GL_VERSION_3_3( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_3_3) return; + glad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) load(userptr, "glBindFragDataLocationIndexed"); + glad_glBindSampler = (PFNGLBINDSAMPLERPROC) load(userptr, "glBindSampler"); + glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC) load(userptr, "glDeleteSamplers"); + glad_glGenSamplers = (PFNGLGENSAMPLERSPROC) load(userptr, "glGenSamplers"); + glad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC) load(userptr, "glGetFragDataIndex"); + glad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC) load(userptr, "glGetQueryObjecti64v"); + glad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC) load(userptr, "glGetQueryObjectui64v"); + glad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC) load(userptr, "glGetSamplerParameterIiv"); + glad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC) load(userptr, "glGetSamplerParameterIuiv"); + glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC) load(userptr, "glGetSamplerParameterfv"); + glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC) load(userptr, "glGetSamplerParameteriv"); + glad_glIsSampler = (PFNGLISSAMPLERPROC) load(userptr, "glIsSampler"); + glad_glQueryCounter = (PFNGLQUERYCOUNTERPROC) load(userptr, "glQueryCounter"); + glad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC) load(userptr, "glSamplerParameterIiv"); + glad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC) load(userptr, "glSamplerParameterIuiv"); + glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC) load(userptr, "glSamplerParameterf"); + glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC) load(userptr, "glSamplerParameterfv"); + glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC) load(userptr, "glSamplerParameteri"); + glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC) load(userptr, "glSamplerParameteriv"); + glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC) load(userptr, "glVertexAttribDivisor"); + glad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC) load(userptr, "glVertexAttribP1ui"); + glad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC) load(userptr, "glVertexAttribP1uiv"); + glad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC) load(userptr, "glVertexAttribP2ui"); + glad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC) load(userptr, "glVertexAttribP2uiv"); + glad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC) load(userptr, "glVertexAttribP3ui"); + glad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC) load(userptr, "glVertexAttribP3uiv"); + glad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC) load(userptr, "glVertexAttribP4ui"); + glad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC) load(userptr, "glVertexAttribP4uiv"); +} +static void glad_gl_load_GL_VERSION_4_0( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_4_0) return; + glad_glBeginQueryIndexed = (PFNGLBEGINQUERYINDEXEDPROC) load(userptr, "glBeginQueryIndexed"); + glad_glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC) load(userptr, "glBindTransformFeedback"); + glad_glBlendEquationSeparatei = (PFNGLBLENDEQUATIONSEPARATEIPROC) load(userptr, "glBlendEquationSeparatei"); + glad_glBlendEquationi = (PFNGLBLENDEQUATIONIPROC) load(userptr, "glBlendEquationi"); + glad_glBlendFuncSeparatei = (PFNGLBLENDFUNCSEPARATEIPROC) load(userptr, "glBlendFuncSeparatei"); + glad_glBlendFunci = (PFNGLBLENDFUNCIPROC) load(userptr, "glBlendFunci"); + glad_glDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC) load(userptr, "glDeleteTransformFeedbacks"); + glad_glDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC) load(userptr, "glDrawArraysIndirect"); + glad_glDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC) load(userptr, "glDrawElementsIndirect"); + glad_glDrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC) load(userptr, "glDrawTransformFeedback"); + glad_glDrawTransformFeedbackStream = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) load(userptr, "glDrawTransformFeedbackStream"); + glad_glEndQueryIndexed = (PFNGLENDQUERYINDEXEDPROC) load(userptr, "glEndQueryIndexed"); + glad_glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC) load(userptr, "glGenTransformFeedbacks"); + glad_glGetActiveSubroutineName = (PFNGLGETACTIVESUBROUTINENAMEPROC) load(userptr, "glGetActiveSubroutineName"); + glad_glGetActiveSubroutineUniformName = (PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) load(userptr, "glGetActiveSubroutineUniformName"); + glad_glGetActiveSubroutineUniformiv = (PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) load(userptr, "glGetActiveSubroutineUniformiv"); + glad_glGetProgramStageiv = (PFNGLGETPROGRAMSTAGEIVPROC) load(userptr, "glGetProgramStageiv"); + glad_glGetQueryIndexediv = (PFNGLGETQUERYINDEXEDIVPROC) load(userptr, "glGetQueryIndexediv"); + glad_glGetSubroutineIndex = (PFNGLGETSUBROUTINEINDEXPROC) load(userptr, "glGetSubroutineIndex"); + glad_glGetSubroutineUniformLocation = (PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) load(userptr, "glGetSubroutineUniformLocation"); + glad_glGetUniformSubroutineuiv = (PFNGLGETUNIFORMSUBROUTINEUIVPROC) load(userptr, "glGetUniformSubroutineuiv"); + glad_glGetUniformdv = (PFNGLGETUNIFORMDVPROC) load(userptr, "glGetUniformdv"); + glad_glIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC) load(userptr, "glIsTransformFeedback"); + glad_glMinSampleShading = (PFNGLMINSAMPLESHADINGPROC) load(userptr, "glMinSampleShading"); + glad_glPatchParameterfv = (PFNGLPATCHPARAMETERFVPROC) load(userptr, "glPatchParameterfv"); + glad_glPatchParameteri = (PFNGLPATCHPARAMETERIPROC) load(userptr, "glPatchParameteri"); + glad_glPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC) load(userptr, "glPauseTransformFeedback"); + glad_glResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC) load(userptr, "glResumeTransformFeedback"); + glad_glUniform1d = (PFNGLUNIFORM1DPROC) load(userptr, "glUniform1d"); + glad_glUniform1dv = (PFNGLUNIFORM1DVPROC) load(userptr, "glUniform1dv"); + glad_glUniform2d = (PFNGLUNIFORM2DPROC) load(userptr, "glUniform2d"); + glad_glUniform2dv = (PFNGLUNIFORM2DVPROC) load(userptr, "glUniform2dv"); + glad_glUniform3d = (PFNGLUNIFORM3DPROC) load(userptr, "glUniform3d"); + glad_glUniform3dv = (PFNGLUNIFORM3DVPROC) load(userptr, "glUniform3dv"); + glad_glUniform4d = (PFNGLUNIFORM4DPROC) load(userptr, "glUniform4d"); + glad_glUniform4dv = (PFNGLUNIFORM4DVPROC) load(userptr, "glUniform4dv"); + glad_glUniformMatrix2dv = (PFNGLUNIFORMMATRIX2DVPROC) load(userptr, "glUniformMatrix2dv"); + glad_glUniformMatrix2x3dv = (PFNGLUNIFORMMATRIX2X3DVPROC) load(userptr, "glUniformMatrix2x3dv"); + glad_glUniformMatrix2x4dv = (PFNGLUNIFORMMATRIX2X4DVPROC) load(userptr, "glUniformMatrix2x4dv"); + glad_glUniformMatrix3dv = (PFNGLUNIFORMMATRIX3DVPROC) load(userptr, "glUniformMatrix3dv"); + glad_glUniformMatrix3x2dv = (PFNGLUNIFORMMATRIX3X2DVPROC) load(userptr, "glUniformMatrix3x2dv"); + glad_glUniformMatrix3x4dv = (PFNGLUNIFORMMATRIX3X4DVPROC) load(userptr, "glUniformMatrix3x4dv"); + glad_glUniformMatrix4dv = (PFNGLUNIFORMMATRIX4DVPROC) load(userptr, "glUniformMatrix4dv"); + glad_glUniformMatrix4x2dv = (PFNGLUNIFORMMATRIX4X2DVPROC) load(userptr, "glUniformMatrix4x2dv"); + glad_glUniformMatrix4x3dv = (PFNGLUNIFORMMATRIX4X3DVPROC) load(userptr, "glUniformMatrix4x3dv"); + glad_glUniformSubroutinesuiv = (PFNGLUNIFORMSUBROUTINESUIVPROC) load(userptr, "glUniformSubroutinesuiv"); +} +static void glad_gl_load_GL_VERSION_4_1( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_4_1) return; + glad_glActiveShaderProgram = (PFNGLACTIVESHADERPROGRAMPROC) load(userptr, "glActiveShaderProgram"); + glad_glBindProgramPipeline = (PFNGLBINDPROGRAMPIPELINEPROC) load(userptr, "glBindProgramPipeline"); + glad_glClearDepthf = (PFNGLCLEARDEPTHFPROC) load(userptr, "glClearDepthf"); + glad_glCreateShaderProgramv = (PFNGLCREATESHADERPROGRAMVPROC) load(userptr, "glCreateShaderProgramv"); + glad_glDeleteProgramPipelines = (PFNGLDELETEPROGRAMPIPELINESPROC) load(userptr, "glDeleteProgramPipelines"); + glad_glDepthRangeArrayv = (PFNGLDEPTHRANGEARRAYVPROC) load(userptr, "glDepthRangeArrayv"); + glad_glDepthRangeIndexed = (PFNGLDEPTHRANGEINDEXEDPROC) load(userptr, "glDepthRangeIndexed"); + glad_glDepthRangef = (PFNGLDEPTHRANGEFPROC) load(userptr, "glDepthRangef"); + glad_glGenProgramPipelines = (PFNGLGENPROGRAMPIPELINESPROC) load(userptr, "glGenProgramPipelines"); + glad_glGetDoublei_v = (PFNGLGETDOUBLEI_VPROC) load(userptr, "glGetDoublei_v"); + glad_glGetFloati_v = (PFNGLGETFLOATI_VPROC) load(userptr, "glGetFloati_v"); + glad_glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC) load(userptr, "glGetProgramBinary"); + glad_glGetProgramPipelineInfoLog = (PFNGLGETPROGRAMPIPELINEINFOLOGPROC) load(userptr, "glGetProgramPipelineInfoLog"); + glad_glGetProgramPipelineiv = (PFNGLGETPROGRAMPIPELINEIVPROC) load(userptr, "glGetProgramPipelineiv"); + glad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC) load(userptr, "glGetShaderPrecisionFormat"); + glad_glGetVertexAttribLdv = (PFNGLGETVERTEXATTRIBLDVPROC) load(userptr, "glGetVertexAttribLdv"); + glad_glIsProgramPipeline = (PFNGLISPROGRAMPIPELINEPROC) load(userptr, "glIsProgramPipeline"); + glad_glProgramBinary = (PFNGLPROGRAMBINARYPROC) load(userptr, "glProgramBinary"); + glad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC) load(userptr, "glProgramParameteri"); + glad_glProgramUniform1d = (PFNGLPROGRAMUNIFORM1DPROC) load(userptr, "glProgramUniform1d"); + glad_glProgramUniform1dv = (PFNGLPROGRAMUNIFORM1DVPROC) load(userptr, "glProgramUniform1dv"); + glad_glProgramUniform1f = (PFNGLPROGRAMUNIFORM1FPROC) load(userptr, "glProgramUniform1f"); + glad_glProgramUniform1fv = (PFNGLPROGRAMUNIFORM1FVPROC) load(userptr, "glProgramUniform1fv"); + glad_glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC) load(userptr, "glProgramUniform1i"); + glad_glProgramUniform1iv = (PFNGLPROGRAMUNIFORM1IVPROC) load(userptr, "glProgramUniform1iv"); + glad_glProgramUniform1ui = (PFNGLPROGRAMUNIFORM1UIPROC) load(userptr, "glProgramUniform1ui"); + glad_glProgramUniform1uiv = (PFNGLPROGRAMUNIFORM1UIVPROC) load(userptr, "glProgramUniform1uiv"); + glad_glProgramUniform2d = (PFNGLPROGRAMUNIFORM2DPROC) load(userptr, "glProgramUniform2d"); + glad_glProgramUniform2dv = (PFNGLPROGRAMUNIFORM2DVPROC) load(userptr, "glProgramUniform2dv"); + glad_glProgramUniform2f = (PFNGLPROGRAMUNIFORM2FPROC) load(userptr, "glProgramUniform2f"); + glad_glProgramUniform2fv = (PFNGLPROGRAMUNIFORM2FVPROC) load(userptr, "glProgramUniform2fv"); + glad_glProgramUniform2i = (PFNGLPROGRAMUNIFORM2IPROC) load(userptr, "glProgramUniform2i"); + glad_glProgramUniform2iv = (PFNGLPROGRAMUNIFORM2IVPROC) load(userptr, "glProgramUniform2iv"); + glad_glProgramUniform2ui = (PFNGLPROGRAMUNIFORM2UIPROC) load(userptr, "glProgramUniform2ui"); + glad_glProgramUniform2uiv = (PFNGLPROGRAMUNIFORM2UIVPROC) load(userptr, "glProgramUniform2uiv"); + glad_glProgramUniform3d = (PFNGLPROGRAMUNIFORM3DPROC) load(userptr, "glProgramUniform3d"); + glad_glProgramUniform3dv = (PFNGLPROGRAMUNIFORM3DVPROC) load(userptr, "glProgramUniform3dv"); + glad_glProgramUniform3f = (PFNGLPROGRAMUNIFORM3FPROC) load(userptr, "glProgramUniform3f"); + glad_glProgramUniform3fv = (PFNGLPROGRAMUNIFORM3FVPROC) load(userptr, "glProgramUniform3fv"); + glad_glProgramUniform3i = (PFNGLPROGRAMUNIFORM3IPROC) load(userptr, "glProgramUniform3i"); + glad_glProgramUniform3iv = (PFNGLPROGRAMUNIFORM3IVPROC) load(userptr, "glProgramUniform3iv"); + glad_glProgramUniform3ui = (PFNGLPROGRAMUNIFORM3UIPROC) load(userptr, "glProgramUniform3ui"); + glad_glProgramUniform3uiv = (PFNGLPROGRAMUNIFORM3UIVPROC) load(userptr, "glProgramUniform3uiv"); + glad_glProgramUniform4d = (PFNGLPROGRAMUNIFORM4DPROC) load(userptr, "glProgramUniform4d"); + glad_glProgramUniform4dv = (PFNGLPROGRAMUNIFORM4DVPROC) load(userptr, "glProgramUniform4dv"); + glad_glProgramUniform4f = (PFNGLPROGRAMUNIFORM4FPROC) load(userptr, "glProgramUniform4f"); + glad_glProgramUniform4fv = (PFNGLPROGRAMUNIFORM4FVPROC) load(userptr, "glProgramUniform4fv"); + glad_glProgramUniform4i = (PFNGLPROGRAMUNIFORM4IPROC) load(userptr, "glProgramUniform4i"); + glad_glProgramUniform4iv = (PFNGLPROGRAMUNIFORM4IVPROC) load(userptr, "glProgramUniform4iv"); + glad_glProgramUniform4ui = (PFNGLPROGRAMUNIFORM4UIPROC) load(userptr, "glProgramUniform4ui"); + glad_glProgramUniform4uiv = (PFNGLPROGRAMUNIFORM4UIVPROC) load(userptr, "glProgramUniform4uiv"); + glad_glProgramUniformMatrix2dv = (PFNGLPROGRAMUNIFORMMATRIX2DVPROC) load(userptr, "glProgramUniformMatrix2dv"); + glad_glProgramUniformMatrix2fv = (PFNGLPROGRAMUNIFORMMATRIX2FVPROC) load(userptr, "glProgramUniformMatrix2fv"); + glad_glProgramUniformMatrix2x3dv = (PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) load(userptr, "glProgramUniformMatrix2x3dv"); + glad_glProgramUniformMatrix2x3fv = (PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) load(userptr, "glProgramUniformMatrix2x3fv"); + glad_glProgramUniformMatrix2x4dv = (PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) load(userptr, "glProgramUniformMatrix2x4dv"); + glad_glProgramUniformMatrix2x4fv = (PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) load(userptr, "glProgramUniformMatrix2x4fv"); + glad_glProgramUniformMatrix3dv = (PFNGLPROGRAMUNIFORMMATRIX3DVPROC) load(userptr, "glProgramUniformMatrix3dv"); + glad_glProgramUniformMatrix3fv = (PFNGLPROGRAMUNIFORMMATRIX3FVPROC) load(userptr, "glProgramUniformMatrix3fv"); + glad_glProgramUniformMatrix3x2dv = (PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) load(userptr, "glProgramUniformMatrix3x2dv"); + glad_glProgramUniformMatrix3x2fv = (PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) load(userptr, "glProgramUniformMatrix3x2fv"); + glad_glProgramUniformMatrix3x4dv = (PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) load(userptr, "glProgramUniformMatrix3x4dv"); + glad_glProgramUniformMatrix3x4fv = (PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) load(userptr, "glProgramUniformMatrix3x4fv"); + glad_glProgramUniformMatrix4dv = (PFNGLPROGRAMUNIFORMMATRIX4DVPROC) load(userptr, "glProgramUniformMatrix4dv"); + glad_glProgramUniformMatrix4fv = (PFNGLPROGRAMUNIFORMMATRIX4FVPROC) load(userptr, "glProgramUniformMatrix4fv"); + glad_glProgramUniformMatrix4x2dv = (PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) load(userptr, "glProgramUniformMatrix4x2dv"); + glad_glProgramUniformMatrix4x2fv = (PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) load(userptr, "glProgramUniformMatrix4x2fv"); + glad_glProgramUniformMatrix4x3dv = (PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) load(userptr, "glProgramUniformMatrix4x3dv"); + glad_glProgramUniformMatrix4x3fv = (PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) load(userptr, "glProgramUniformMatrix4x3fv"); + glad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC) load(userptr, "glReleaseShaderCompiler"); + glad_glScissorArrayv = (PFNGLSCISSORARRAYVPROC) load(userptr, "glScissorArrayv"); + glad_glScissorIndexed = (PFNGLSCISSORINDEXEDPROC) load(userptr, "glScissorIndexed"); + glad_glScissorIndexedv = (PFNGLSCISSORINDEXEDVPROC) load(userptr, "glScissorIndexedv"); + glad_glShaderBinary = (PFNGLSHADERBINARYPROC) load(userptr, "glShaderBinary"); + glad_glUseProgramStages = (PFNGLUSEPROGRAMSTAGESPROC) load(userptr, "glUseProgramStages"); + glad_glValidateProgramPipeline = (PFNGLVALIDATEPROGRAMPIPELINEPROC) load(userptr, "glValidateProgramPipeline"); + glad_glVertexAttribL1d = (PFNGLVERTEXATTRIBL1DPROC) load(userptr, "glVertexAttribL1d"); + glad_glVertexAttribL1dv = (PFNGLVERTEXATTRIBL1DVPROC) load(userptr, "glVertexAttribL1dv"); + glad_glVertexAttribL2d = (PFNGLVERTEXATTRIBL2DPROC) load(userptr, "glVertexAttribL2d"); + glad_glVertexAttribL2dv = (PFNGLVERTEXATTRIBL2DVPROC) load(userptr, "glVertexAttribL2dv"); + glad_glVertexAttribL3d = (PFNGLVERTEXATTRIBL3DPROC) load(userptr, "glVertexAttribL3d"); + glad_glVertexAttribL3dv = (PFNGLVERTEXATTRIBL3DVPROC) load(userptr, "glVertexAttribL3dv"); + glad_glVertexAttribL4d = (PFNGLVERTEXATTRIBL4DPROC) load(userptr, "glVertexAttribL4d"); + glad_glVertexAttribL4dv = (PFNGLVERTEXATTRIBL4DVPROC) load(userptr, "glVertexAttribL4dv"); + glad_glVertexAttribLPointer = (PFNGLVERTEXATTRIBLPOINTERPROC) load(userptr, "glVertexAttribLPointer"); + glad_glViewportArrayv = (PFNGLVIEWPORTARRAYVPROC) load(userptr, "glViewportArrayv"); + glad_glViewportIndexedf = (PFNGLVIEWPORTINDEXEDFPROC) load(userptr, "glViewportIndexedf"); + glad_glViewportIndexedfv = (PFNGLVIEWPORTINDEXEDFVPROC) load(userptr, "glViewportIndexedfv"); +} +static void glad_gl_load_GL_VERSION_4_2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_4_2) return; + glad_glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC) load(userptr, "glBindImageTexture"); + glad_glDrawArraysInstancedBaseInstance = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) load(userptr, "glDrawArraysInstancedBaseInstance"); + glad_glDrawElementsInstancedBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) load(userptr, "glDrawElementsInstancedBaseInstance"); + glad_glDrawElementsInstancedBaseVertexBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) load(userptr, "glDrawElementsInstancedBaseVertexBaseInstance"); + glad_glDrawTransformFeedbackInstanced = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) load(userptr, "glDrawTransformFeedbackInstanced"); + glad_glDrawTransformFeedbackStreamInstanced = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) load(userptr, "glDrawTransformFeedbackStreamInstanced"); + glad_glGetActiveAtomicCounterBufferiv = (PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) load(userptr, "glGetActiveAtomicCounterBufferiv"); + glad_glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC) load(userptr, "glGetInternalformativ"); + glad_glMemoryBarrier = (PFNGLMEMORYBARRIERPROC) load(userptr, "glMemoryBarrier"); + glad_glTexStorage1D = (PFNGLTEXSTORAGE1DPROC) load(userptr, "glTexStorage1D"); + glad_glTexStorage2D = (PFNGLTEXSTORAGE2DPROC) load(userptr, "glTexStorage2D"); + glad_glTexStorage3D = (PFNGLTEXSTORAGE3DPROC) load(userptr, "glTexStorage3D"); +} +static void glad_gl_load_GL_VERSION_4_3( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_VERSION_4_3) return; + glad_glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC) load(userptr, "glBindVertexBuffer"); + glad_glClearBufferData = (PFNGLCLEARBUFFERDATAPROC) load(userptr, "glClearBufferData"); + glad_glClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC) load(userptr, "glClearBufferSubData"); + glad_glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC) load(userptr, "glCopyImageSubData"); + glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC) load(userptr, "glDebugMessageCallback"); + glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC) load(userptr, "glDebugMessageControl"); + glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC) load(userptr, "glDebugMessageInsert"); + glad_glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC) load(userptr, "glDispatchCompute"); + glad_glDispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC) load(userptr, "glDispatchComputeIndirect"); + glad_glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC) load(userptr, "glFramebufferParameteri"); + glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC) load(userptr, "glGetDebugMessageLog"); + glad_glGetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC) load(userptr, "glGetFramebufferParameteriv"); + glad_glGetInternalformati64v = (PFNGLGETINTERNALFORMATI64VPROC) load(userptr, "glGetInternalformati64v"); + glad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC) load(userptr, "glGetObjectLabel"); + glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC) load(userptr, "glGetObjectPtrLabel"); + glad_glGetPointerv = (PFNGLGETPOINTERVPROC) load(userptr, "glGetPointerv"); + glad_glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC) load(userptr, "glGetProgramInterfaceiv"); + glad_glGetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC) load(userptr, "glGetProgramResourceIndex"); + glad_glGetProgramResourceLocation = (PFNGLGETPROGRAMRESOURCELOCATIONPROC) load(userptr, "glGetProgramResourceLocation"); + glad_glGetProgramResourceLocationIndex = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) load(userptr, "glGetProgramResourceLocationIndex"); + glad_glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC) load(userptr, "glGetProgramResourceName"); + glad_glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC) load(userptr, "glGetProgramResourceiv"); + glad_glInvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC) load(userptr, "glInvalidateBufferData"); + glad_glInvalidateBufferSubData = (PFNGLINVALIDATEBUFFERSUBDATAPROC) load(userptr, "glInvalidateBufferSubData"); + glad_glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC) load(userptr, "glInvalidateFramebuffer"); + glad_glInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC) load(userptr, "glInvalidateSubFramebuffer"); + glad_glInvalidateTexImage = (PFNGLINVALIDATETEXIMAGEPROC) load(userptr, "glInvalidateTexImage"); + glad_glInvalidateTexSubImage = (PFNGLINVALIDATETEXSUBIMAGEPROC) load(userptr, "glInvalidateTexSubImage"); + glad_glMultiDrawArraysIndirect = (PFNGLMULTIDRAWARRAYSINDIRECTPROC) load(userptr, "glMultiDrawArraysIndirect"); + glad_glMultiDrawElementsIndirect = (PFNGLMULTIDRAWELEMENTSINDIRECTPROC) load(userptr, "glMultiDrawElementsIndirect"); + glad_glObjectLabel = (PFNGLOBJECTLABELPROC) load(userptr, "glObjectLabel"); + glad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC) load(userptr, "glObjectPtrLabel"); + glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC) load(userptr, "glPopDebugGroup"); + glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC) load(userptr, "glPushDebugGroup"); + glad_glShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC) load(userptr, "glShaderStorageBlockBinding"); + glad_glTexBufferRange = (PFNGLTEXBUFFERRANGEPROC) load(userptr, "glTexBufferRange"); + glad_glTexStorage2DMultisample = (PFNGLTEXSTORAGE2DMULTISAMPLEPROC) load(userptr, "glTexStorage2DMultisample"); + glad_glTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC) load(userptr, "glTexStorage3DMultisample"); + glad_glTextureView = (PFNGLTEXTUREVIEWPROC) load(userptr, "glTextureView"); + glad_glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC) load(userptr, "glVertexAttribBinding"); + glad_glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC) load(userptr, "glVertexAttribFormat"); + glad_glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC) load(userptr, "glVertexAttribIFormat"); + glad_glVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC) load(userptr, "glVertexAttribLFormat"); + glad_glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC) load(userptr, "glVertexBindingDivisor"); +} +static void glad_gl_load_GL_ARB_ES2_compatibility( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_ES2_compatibility) return; + glad_glClearDepthf = (PFNGLCLEARDEPTHFPROC) load(userptr, "glClearDepthf"); + glad_glDepthRangef = (PFNGLDEPTHRANGEFPROC) load(userptr, "glDepthRangef"); + glad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC) load(userptr, "glGetShaderPrecisionFormat"); + glad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC) load(userptr, "glReleaseShaderCompiler"); + glad_glShaderBinary = (PFNGLSHADERBINARYPROC) load(userptr, "glShaderBinary"); +} +static void glad_gl_load_GL_ARB_ES3_1_compatibility( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_ES3_1_compatibility) return; + glad_glMemoryBarrierByRegion = (PFNGLMEMORYBARRIERBYREGIONPROC) load(userptr, "glMemoryBarrierByRegion"); +} +static void glad_gl_load_GL_ARB_ES3_2_compatibility( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_ES3_2_compatibility) return; + glad_glPrimitiveBoundingBoxARB = (PFNGLPRIMITIVEBOUNDINGBOXARBPROC) load(userptr, "glPrimitiveBoundingBoxARB"); +} +static void glad_gl_load_GL_ARB_blend_func_extended( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_blend_func_extended) return; + glad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) load(userptr, "glBindFragDataLocationIndexed"); + glad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC) load(userptr, "glGetFragDataIndex"); +} +static void glad_gl_load_GL_ARB_buffer_storage( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_buffer_storage) return; + glad_glBufferStorage = (PFNGLBUFFERSTORAGEPROC) load(userptr, "glBufferStorage"); +} +static void glad_gl_load_GL_ARB_clear_buffer_object( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_clear_buffer_object) return; + glad_glClearBufferData = (PFNGLCLEARBUFFERDATAPROC) load(userptr, "glClearBufferData"); + glad_glClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC) load(userptr, "glClearBufferSubData"); +} +static void glad_gl_load_GL_ARB_clear_texture( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_clear_texture) return; + glad_glClearTexImage = (PFNGLCLEARTEXIMAGEPROC) load(userptr, "glClearTexImage"); + glad_glClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC) load(userptr, "glClearTexSubImage"); +} +static void glad_gl_load_GL_ARB_color_buffer_float( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_color_buffer_float) return; + glad_glClampColorARB = (PFNGLCLAMPCOLORARBPROC) load(userptr, "glClampColorARB"); +} +static void glad_gl_load_GL_ARB_compute_shader( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_compute_shader) return; + glad_glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC) load(userptr, "glDispatchCompute"); + glad_glDispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC) load(userptr, "glDispatchComputeIndirect"); +} +static void glad_gl_load_GL_ARB_compute_variable_group_size( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_compute_variable_group_size) return; + glad_glDispatchComputeGroupSizeARB = (PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) load(userptr, "glDispatchComputeGroupSizeARB"); +} +static void glad_gl_load_GL_ARB_copy_buffer( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_copy_buffer) return; + glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC) load(userptr, "glCopyBufferSubData"); +} +static void glad_gl_load_GL_ARB_copy_image( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_copy_image) return; + glad_glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC) load(userptr, "glCopyImageSubData"); +} +static void glad_gl_load_GL_ARB_debug_output( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_debug_output) return; + glad_glDebugMessageCallbackARB = (PFNGLDEBUGMESSAGECALLBACKARBPROC) load(userptr, "glDebugMessageCallbackARB"); + glad_glDebugMessageControlARB = (PFNGLDEBUGMESSAGECONTROLARBPROC) load(userptr, "glDebugMessageControlARB"); + glad_glDebugMessageInsertARB = (PFNGLDEBUGMESSAGEINSERTARBPROC) load(userptr, "glDebugMessageInsertARB"); + glad_glGetDebugMessageLogARB = (PFNGLGETDEBUGMESSAGELOGARBPROC) load(userptr, "glGetDebugMessageLogARB"); +} +static void glad_gl_load_GL_ARB_direct_state_access( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_direct_state_access) return; + glad_glBindTextureUnit = (PFNGLBINDTEXTUREUNITPROC) load(userptr, "glBindTextureUnit"); + glad_glBlitNamedFramebuffer = (PFNGLBLITNAMEDFRAMEBUFFERPROC) load(userptr, "glBlitNamedFramebuffer"); + glad_glCheckNamedFramebufferStatus = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckNamedFramebufferStatus"); + glad_glClearNamedBufferData = (PFNGLCLEARNAMEDBUFFERDATAPROC) load(userptr, "glClearNamedBufferData"); + glad_glClearNamedBufferSubData = (PFNGLCLEARNAMEDBUFFERSUBDATAPROC) load(userptr, "glClearNamedBufferSubData"); + glad_glClearNamedFramebufferfi = (PFNGLCLEARNAMEDFRAMEBUFFERFIPROC) load(userptr, "glClearNamedFramebufferfi"); + glad_glClearNamedFramebufferfv = (PFNGLCLEARNAMEDFRAMEBUFFERFVPROC) load(userptr, "glClearNamedFramebufferfv"); + glad_glClearNamedFramebufferiv = (PFNGLCLEARNAMEDFRAMEBUFFERIVPROC) load(userptr, "glClearNamedFramebufferiv"); + glad_glClearNamedFramebufferuiv = (PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC) load(userptr, "glClearNamedFramebufferuiv"); + glad_glCompressedTextureSubImage1D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC) load(userptr, "glCompressedTextureSubImage1D"); + glad_glCompressedTextureSubImage2D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC) load(userptr, "glCompressedTextureSubImage2D"); + glad_glCompressedTextureSubImage3D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC) load(userptr, "glCompressedTextureSubImage3D"); + glad_glCopyNamedBufferSubData = (PFNGLCOPYNAMEDBUFFERSUBDATAPROC) load(userptr, "glCopyNamedBufferSubData"); + glad_glCopyTextureSubImage1D = (PFNGLCOPYTEXTURESUBIMAGE1DPROC) load(userptr, "glCopyTextureSubImage1D"); + glad_glCopyTextureSubImage2D = (PFNGLCOPYTEXTURESUBIMAGE2DPROC) load(userptr, "glCopyTextureSubImage2D"); + glad_glCopyTextureSubImage3D = (PFNGLCOPYTEXTURESUBIMAGE3DPROC) load(userptr, "glCopyTextureSubImage3D"); + glad_glCreateBuffers = (PFNGLCREATEBUFFERSPROC) load(userptr, "glCreateBuffers"); + glad_glCreateFramebuffers = (PFNGLCREATEFRAMEBUFFERSPROC) load(userptr, "glCreateFramebuffers"); + glad_glCreateProgramPipelines = (PFNGLCREATEPROGRAMPIPELINESPROC) load(userptr, "glCreateProgramPipelines"); + glad_glCreateQueries = (PFNGLCREATEQUERIESPROC) load(userptr, "glCreateQueries"); + glad_glCreateRenderbuffers = (PFNGLCREATERENDERBUFFERSPROC) load(userptr, "glCreateRenderbuffers"); + glad_glCreateSamplers = (PFNGLCREATESAMPLERSPROC) load(userptr, "glCreateSamplers"); + glad_glCreateTextures = (PFNGLCREATETEXTURESPROC) load(userptr, "glCreateTextures"); + glad_glCreateTransformFeedbacks = (PFNGLCREATETRANSFORMFEEDBACKSPROC) load(userptr, "glCreateTransformFeedbacks"); + glad_glCreateVertexArrays = (PFNGLCREATEVERTEXARRAYSPROC) load(userptr, "glCreateVertexArrays"); + glad_glDisableVertexArrayAttrib = (PFNGLDISABLEVERTEXARRAYATTRIBPROC) load(userptr, "glDisableVertexArrayAttrib"); + glad_glEnableVertexArrayAttrib = (PFNGLENABLEVERTEXARRAYATTRIBPROC) load(userptr, "glEnableVertexArrayAttrib"); + glad_glFlushMappedNamedBufferRange = (PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC) load(userptr, "glFlushMappedNamedBufferRange"); + glad_glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC) load(userptr, "glGenerateTextureMipmap"); + glad_glGetCompressedTextureImage = (PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC) load(userptr, "glGetCompressedTextureImage"); + glad_glGetNamedBufferParameteri64v = (PFNGLGETNAMEDBUFFERPARAMETERI64VPROC) load(userptr, "glGetNamedBufferParameteri64v"); + glad_glGetNamedBufferParameteriv = (PFNGLGETNAMEDBUFFERPARAMETERIVPROC) load(userptr, "glGetNamedBufferParameteriv"); + glad_glGetNamedBufferPointerv = (PFNGLGETNAMEDBUFFERPOINTERVPROC) load(userptr, "glGetNamedBufferPointerv"); + glad_glGetNamedBufferSubData = (PFNGLGETNAMEDBUFFERSUBDATAPROC) load(userptr, "glGetNamedBufferSubData"); + glad_glGetNamedFramebufferAttachmentParameteriv = (PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetNamedFramebufferAttachmentParameteriv"); + glad_glGetNamedFramebufferParameteriv = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC) load(userptr, "glGetNamedFramebufferParameteriv"); + glad_glGetNamedRenderbufferParameteriv = (PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetNamedRenderbufferParameteriv"); + glad_glGetQueryBufferObjecti64v = (PFNGLGETQUERYBUFFEROBJECTI64VPROC) load(userptr, "glGetQueryBufferObjecti64v"); + glad_glGetQueryBufferObjectiv = (PFNGLGETQUERYBUFFEROBJECTIVPROC) load(userptr, "glGetQueryBufferObjectiv"); + glad_glGetQueryBufferObjectui64v = (PFNGLGETQUERYBUFFEROBJECTUI64VPROC) load(userptr, "glGetQueryBufferObjectui64v"); + glad_glGetQueryBufferObjectuiv = (PFNGLGETQUERYBUFFEROBJECTUIVPROC) load(userptr, "glGetQueryBufferObjectuiv"); + glad_glGetTextureImage = (PFNGLGETTEXTUREIMAGEPROC) load(userptr, "glGetTextureImage"); + glad_glGetTextureLevelParameterfv = (PFNGLGETTEXTURELEVELPARAMETERFVPROC) load(userptr, "glGetTextureLevelParameterfv"); + glad_glGetTextureLevelParameteriv = (PFNGLGETTEXTURELEVELPARAMETERIVPROC) load(userptr, "glGetTextureLevelParameteriv"); + glad_glGetTextureParameterIiv = (PFNGLGETTEXTUREPARAMETERIIVPROC) load(userptr, "glGetTextureParameterIiv"); + glad_glGetTextureParameterIuiv = (PFNGLGETTEXTUREPARAMETERIUIVPROC) load(userptr, "glGetTextureParameterIuiv"); + glad_glGetTextureParameterfv = (PFNGLGETTEXTUREPARAMETERFVPROC) load(userptr, "glGetTextureParameterfv"); + glad_glGetTextureParameteriv = (PFNGLGETTEXTUREPARAMETERIVPROC) load(userptr, "glGetTextureParameteriv"); + glad_glGetTransformFeedbacki64_v = (PFNGLGETTRANSFORMFEEDBACKI64_VPROC) load(userptr, "glGetTransformFeedbacki64_v"); + glad_glGetTransformFeedbacki_v = (PFNGLGETTRANSFORMFEEDBACKI_VPROC) load(userptr, "glGetTransformFeedbacki_v"); + glad_glGetTransformFeedbackiv = (PFNGLGETTRANSFORMFEEDBACKIVPROC) load(userptr, "glGetTransformFeedbackiv"); + glad_glGetVertexArrayIndexed64iv = (PFNGLGETVERTEXARRAYINDEXED64IVPROC) load(userptr, "glGetVertexArrayIndexed64iv"); + glad_glGetVertexArrayIndexediv = (PFNGLGETVERTEXARRAYINDEXEDIVPROC) load(userptr, "glGetVertexArrayIndexediv"); + glad_glGetVertexArrayiv = (PFNGLGETVERTEXARRAYIVPROC) load(userptr, "glGetVertexArrayiv"); + glad_glInvalidateNamedFramebufferData = (PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC) load(userptr, "glInvalidateNamedFramebufferData"); + glad_glInvalidateNamedFramebufferSubData = (PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC) load(userptr, "glInvalidateNamedFramebufferSubData"); + glad_glMapNamedBuffer = (PFNGLMAPNAMEDBUFFERPROC) load(userptr, "glMapNamedBuffer"); + glad_glMapNamedBufferRange = (PFNGLMAPNAMEDBUFFERRANGEPROC) load(userptr, "glMapNamedBufferRange"); + glad_glNamedBufferData = (PFNGLNAMEDBUFFERDATAPROC) load(userptr, "glNamedBufferData"); + glad_glNamedBufferStorage = (PFNGLNAMEDBUFFERSTORAGEPROC) load(userptr, "glNamedBufferStorage"); + glad_glNamedBufferSubData = (PFNGLNAMEDBUFFERSUBDATAPROC) load(userptr, "glNamedBufferSubData"); + glad_glNamedFramebufferDrawBuffer = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC) load(userptr, "glNamedFramebufferDrawBuffer"); + glad_glNamedFramebufferDrawBuffers = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC) load(userptr, "glNamedFramebufferDrawBuffers"); + glad_glNamedFramebufferParameteri = (PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC) load(userptr, "glNamedFramebufferParameteri"); + glad_glNamedFramebufferReadBuffer = (PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC) load(userptr, "glNamedFramebufferReadBuffer"); + glad_glNamedFramebufferRenderbuffer = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glNamedFramebufferRenderbuffer"); + glad_glNamedFramebufferTexture = (PFNGLNAMEDFRAMEBUFFERTEXTUREPROC) load(userptr, "glNamedFramebufferTexture"); + glad_glNamedFramebufferTextureLayer = (PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC) load(userptr, "glNamedFramebufferTextureLayer"); + glad_glNamedRenderbufferStorage = (PFNGLNAMEDRENDERBUFFERSTORAGEPROC) load(userptr, "glNamedRenderbufferStorage"); + glad_glNamedRenderbufferStorageMultisample = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glNamedRenderbufferStorageMultisample"); + glad_glTextureBuffer = (PFNGLTEXTUREBUFFERPROC) load(userptr, "glTextureBuffer"); + glad_glTextureBufferRange = (PFNGLTEXTUREBUFFERRANGEPROC) load(userptr, "glTextureBufferRange"); + glad_glTextureParameterIiv = (PFNGLTEXTUREPARAMETERIIVPROC) load(userptr, "glTextureParameterIiv"); + glad_glTextureParameterIuiv = (PFNGLTEXTUREPARAMETERIUIVPROC) load(userptr, "glTextureParameterIuiv"); + glad_glTextureParameterf = (PFNGLTEXTUREPARAMETERFPROC) load(userptr, "glTextureParameterf"); + glad_glTextureParameterfv = (PFNGLTEXTUREPARAMETERFVPROC) load(userptr, "glTextureParameterfv"); + glad_glTextureParameteri = (PFNGLTEXTUREPARAMETERIPROC) load(userptr, "glTextureParameteri"); + glad_glTextureParameteriv = (PFNGLTEXTUREPARAMETERIVPROC) load(userptr, "glTextureParameteriv"); + glad_glTextureStorage1D = (PFNGLTEXTURESTORAGE1DPROC) load(userptr, "glTextureStorage1D"); + glad_glTextureStorage2D = (PFNGLTEXTURESTORAGE2DPROC) load(userptr, "glTextureStorage2D"); + glad_glTextureStorage2DMultisample = (PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC) load(userptr, "glTextureStorage2DMultisample"); + glad_glTextureStorage3D = (PFNGLTEXTURESTORAGE3DPROC) load(userptr, "glTextureStorage3D"); + glad_glTextureStorage3DMultisample = (PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC) load(userptr, "glTextureStorage3DMultisample"); + glad_glTextureSubImage1D = (PFNGLTEXTURESUBIMAGE1DPROC) load(userptr, "glTextureSubImage1D"); + glad_glTextureSubImage2D = (PFNGLTEXTURESUBIMAGE2DPROC) load(userptr, "glTextureSubImage2D"); + glad_glTextureSubImage3D = (PFNGLTEXTURESUBIMAGE3DPROC) load(userptr, "glTextureSubImage3D"); + glad_glTransformFeedbackBufferBase = (PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC) load(userptr, "glTransformFeedbackBufferBase"); + glad_glTransformFeedbackBufferRange = (PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC) load(userptr, "glTransformFeedbackBufferRange"); + glad_glUnmapNamedBuffer = (PFNGLUNMAPNAMEDBUFFERPROC) load(userptr, "glUnmapNamedBuffer"); + glad_glVertexArrayAttribBinding = (PFNGLVERTEXARRAYATTRIBBINDINGPROC) load(userptr, "glVertexArrayAttribBinding"); + glad_glVertexArrayAttribFormat = (PFNGLVERTEXARRAYATTRIBFORMATPROC) load(userptr, "glVertexArrayAttribFormat"); + glad_glVertexArrayAttribIFormat = (PFNGLVERTEXARRAYATTRIBIFORMATPROC) load(userptr, "glVertexArrayAttribIFormat"); + glad_glVertexArrayAttribLFormat = (PFNGLVERTEXARRAYATTRIBLFORMATPROC) load(userptr, "glVertexArrayAttribLFormat"); + glad_glVertexArrayBindingDivisor = (PFNGLVERTEXARRAYBINDINGDIVISORPROC) load(userptr, "glVertexArrayBindingDivisor"); + glad_glVertexArrayElementBuffer = (PFNGLVERTEXARRAYELEMENTBUFFERPROC) load(userptr, "glVertexArrayElementBuffer"); + glad_glVertexArrayVertexBuffer = (PFNGLVERTEXARRAYVERTEXBUFFERPROC) load(userptr, "glVertexArrayVertexBuffer"); + glad_glVertexArrayVertexBuffers = (PFNGLVERTEXARRAYVERTEXBUFFERSPROC) load(userptr, "glVertexArrayVertexBuffers"); +} +static void glad_gl_load_GL_ARB_draw_buffers( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_draw_buffers) return; + glad_glDrawBuffersARB = (PFNGLDRAWBUFFERSARBPROC) load(userptr, "glDrawBuffersARB"); +} +static void glad_gl_load_GL_ARB_draw_buffers_blend( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_draw_buffers_blend) return; + glad_glBlendEquationSeparateiARB = (PFNGLBLENDEQUATIONSEPARATEIARBPROC) load(userptr, "glBlendEquationSeparateiARB"); + glad_glBlendEquationiARB = (PFNGLBLENDEQUATIONIARBPROC) load(userptr, "glBlendEquationiARB"); + glad_glBlendFuncSeparateiARB = (PFNGLBLENDFUNCSEPARATEIARBPROC) load(userptr, "glBlendFuncSeparateiARB"); + glad_glBlendFunciARB = (PFNGLBLENDFUNCIARBPROC) load(userptr, "glBlendFunciARB"); +} +static void glad_gl_load_GL_ARB_draw_elements_base_vertex( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_draw_elements_base_vertex) return; + glad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glDrawElementsBaseVertex"); + glad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) load(userptr, "glDrawElementsInstancedBaseVertex"); + glad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) load(userptr, "glDrawRangeElementsBaseVertex"); + glad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glMultiDrawElementsBaseVertex"); +} +static void glad_gl_load_GL_ARB_draw_indirect( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_draw_indirect) return; + glad_glDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC) load(userptr, "glDrawArraysIndirect"); + glad_glDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC) load(userptr, "glDrawElementsIndirect"); +} +static void glad_gl_load_GL_ARB_draw_instanced( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_draw_instanced) return; + glad_glDrawArraysInstancedARB = (PFNGLDRAWARRAYSINSTANCEDARBPROC) load(userptr, "glDrawArraysInstancedARB"); + glad_glDrawElementsInstancedARB = (PFNGLDRAWELEMENTSINSTANCEDARBPROC) load(userptr, "glDrawElementsInstancedARB"); +} +static void glad_gl_load_GL_ARB_fragment_program( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_fragment_program) return; + glad_glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) load(userptr, "glBindProgramARB"); + glad_glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) load(userptr, "glDeleteProgramsARB"); + glad_glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) load(userptr, "glGenProgramsARB"); + glad_glGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC) load(userptr, "glGetProgramEnvParameterdvARB"); + glad_glGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC) load(userptr, "glGetProgramEnvParameterfvARB"); + glad_glGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) load(userptr, "glGetProgramLocalParameterdvARB"); + glad_glGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) load(userptr, "glGetProgramLocalParameterfvARB"); + glad_glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) load(userptr, "glGetProgramStringARB"); + glad_glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) load(userptr, "glGetProgramivARB"); + glad_glIsProgramARB = (PFNGLISPROGRAMARBPROC) load(userptr, "glIsProgramARB"); + glad_glProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC) load(userptr, "glProgramEnvParameter4dARB"); + glad_glProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC) load(userptr, "glProgramEnvParameter4dvARB"); + glad_glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC) load(userptr, "glProgramEnvParameter4fARB"); + glad_glProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC) load(userptr, "glProgramEnvParameter4fvARB"); + glad_glProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) load(userptr, "glProgramLocalParameter4dARB"); + glad_glProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) load(userptr, "glProgramLocalParameter4dvARB"); + glad_glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC) load(userptr, "glProgramLocalParameter4fARB"); + glad_glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) load(userptr, "glProgramLocalParameter4fvARB"); + glad_glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) load(userptr, "glProgramStringARB"); +} +static void glad_gl_load_GL_ARB_framebuffer_no_attachments( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_framebuffer_no_attachments) return; + glad_glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC) load(userptr, "glFramebufferParameteri"); + glad_glGetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC) load(userptr, "glGetFramebufferParameteriv"); +} +static void glad_gl_load_GL_ARB_framebuffer_object( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_framebuffer_object) return; + glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load(userptr, "glBindFramebuffer"); + glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load(userptr, "glBindRenderbuffer"); + glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) load(userptr, "glBlitFramebuffer"); + glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckFramebufferStatus"); + glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load(userptr, "glDeleteFramebuffers"); + glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load(userptr, "glDeleteRenderbuffers"); + glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glFramebufferRenderbuffer"); + glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) load(userptr, "glFramebufferTexture1D"); + glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load(userptr, "glFramebufferTexture2D"); + glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) load(userptr, "glFramebufferTexture3D"); + glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) load(userptr, "glFramebufferTextureLayer"); + glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load(userptr, "glGenFramebuffers"); + glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load(userptr, "glGenRenderbuffers"); + glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load(userptr, "glGenerateMipmap"); + glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetFramebufferAttachmentParameteriv"); + glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetRenderbufferParameteriv"); + glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load(userptr, "glIsFramebuffer"); + glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load(userptr, "glIsRenderbuffer"); + glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage"); + glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glRenderbufferStorageMultisample"); +} +static void glad_gl_load_GL_ARB_geometry_shader4( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_geometry_shader4) return; + glad_glFramebufferTextureARB = (PFNGLFRAMEBUFFERTEXTUREARBPROC) load(userptr, "glFramebufferTextureARB"); + glad_glFramebufferTextureFaceARB = (PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) load(userptr, "glFramebufferTextureFaceARB"); + glad_glFramebufferTextureLayerARB = (PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) load(userptr, "glFramebufferTextureLayerARB"); + glad_glProgramParameteriARB = (PFNGLPROGRAMPARAMETERIARBPROC) load(userptr, "glProgramParameteriARB"); +} +static void glad_gl_load_GL_ARB_get_program_binary( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_get_program_binary) return; + glad_glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC) load(userptr, "glGetProgramBinary"); + glad_glProgramBinary = (PFNGLPROGRAMBINARYPROC) load(userptr, "glProgramBinary"); + glad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC) load(userptr, "glProgramParameteri"); +} +static void glad_gl_load_GL_ARB_get_texture_sub_image( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_get_texture_sub_image) return; + glad_glGetCompressedTextureSubImage = (PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC) load(userptr, "glGetCompressedTextureSubImage"); + glad_glGetTextureSubImage = (PFNGLGETTEXTURESUBIMAGEPROC) load(userptr, "glGetTextureSubImage"); +} +static void glad_gl_load_GL_ARB_gl_spirv( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_gl_spirv) return; + glad_glSpecializeShaderARB = (PFNGLSPECIALIZESHADERARBPROC) load(userptr, "glSpecializeShaderARB"); +} +static void glad_gl_load_GL_ARB_gpu_shader_fp64( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_gpu_shader_fp64) return; + glad_glGetUniformdv = (PFNGLGETUNIFORMDVPROC) load(userptr, "glGetUniformdv"); + glad_glUniform1d = (PFNGLUNIFORM1DPROC) load(userptr, "glUniform1d"); + glad_glUniform1dv = (PFNGLUNIFORM1DVPROC) load(userptr, "glUniform1dv"); + glad_glUniform2d = (PFNGLUNIFORM2DPROC) load(userptr, "glUniform2d"); + glad_glUniform2dv = (PFNGLUNIFORM2DVPROC) load(userptr, "glUniform2dv"); + glad_glUniform3d = (PFNGLUNIFORM3DPROC) load(userptr, "glUniform3d"); + glad_glUniform3dv = (PFNGLUNIFORM3DVPROC) load(userptr, "glUniform3dv"); + glad_glUniform4d = (PFNGLUNIFORM4DPROC) load(userptr, "glUniform4d"); + glad_glUniform4dv = (PFNGLUNIFORM4DVPROC) load(userptr, "glUniform4dv"); + glad_glUniformMatrix2dv = (PFNGLUNIFORMMATRIX2DVPROC) load(userptr, "glUniformMatrix2dv"); + glad_glUniformMatrix2x3dv = (PFNGLUNIFORMMATRIX2X3DVPROC) load(userptr, "glUniformMatrix2x3dv"); + glad_glUniformMatrix2x4dv = (PFNGLUNIFORMMATRIX2X4DVPROC) load(userptr, "glUniformMatrix2x4dv"); + glad_glUniformMatrix3dv = (PFNGLUNIFORMMATRIX3DVPROC) load(userptr, "glUniformMatrix3dv"); + glad_glUniformMatrix3x2dv = (PFNGLUNIFORMMATRIX3X2DVPROC) load(userptr, "glUniformMatrix3x2dv"); + glad_glUniformMatrix3x4dv = (PFNGLUNIFORMMATRIX3X4DVPROC) load(userptr, "glUniformMatrix3x4dv"); + glad_glUniformMatrix4dv = (PFNGLUNIFORMMATRIX4DVPROC) load(userptr, "glUniformMatrix4dv"); + glad_glUniformMatrix4x2dv = (PFNGLUNIFORMMATRIX4X2DVPROC) load(userptr, "glUniformMatrix4x2dv"); + glad_glUniformMatrix4x3dv = (PFNGLUNIFORMMATRIX4X3DVPROC) load(userptr, "glUniformMatrix4x3dv"); +} +static void glad_gl_load_GL_ARB_gpu_shader_int64( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_gpu_shader_int64) return; + glad_glGetUniformi64vARB = (PFNGLGETUNIFORMI64VARBPROC) load(userptr, "glGetUniformi64vARB"); + glad_glGetUniformui64vARB = (PFNGLGETUNIFORMUI64VARBPROC) load(userptr, "glGetUniformui64vARB"); + glad_glGetnUniformi64vARB = (PFNGLGETNUNIFORMI64VARBPROC) load(userptr, "glGetnUniformi64vARB"); + glad_glGetnUniformui64vARB = (PFNGLGETNUNIFORMUI64VARBPROC) load(userptr, "glGetnUniformui64vARB"); + glad_glProgramUniform1i64ARB = (PFNGLPROGRAMUNIFORM1I64ARBPROC) load(userptr, "glProgramUniform1i64ARB"); + glad_glProgramUniform1i64vARB = (PFNGLPROGRAMUNIFORM1I64VARBPROC) load(userptr, "glProgramUniform1i64vARB"); + glad_glProgramUniform1ui64ARB = (PFNGLPROGRAMUNIFORM1UI64ARBPROC) load(userptr, "glProgramUniform1ui64ARB"); + glad_glProgramUniform1ui64vARB = (PFNGLPROGRAMUNIFORM1UI64VARBPROC) load(userptr, "glProgramUniform1ui64vARB"); + glad_glProgramUniform2i64ARB = (PFNGLPROGRAMUNIFORM2I64ARBPROC) load(userptr, "glProgramUniform2i64ARB"); + glad_glProgramUniform2i64vARB = (PFNGLPROGRAMUNIFORM2I64VARBPROC) load(userptr, "glProgramUniform2i64vARB"); + glad_glProgramUniform2ui64ARB = (PFNGLPROGRAMUNIFORM2UI64ARBPROC) load(userptr, "glProgramUniform2ui64ARB"); + glad_glProgramUniform2ui64vARB = (PFNGLPROGRAMUNIFORM2UI64VARBPROC) load(userptr, "glProgramUniform2ui64vARB"); + glad_glProgramUniform3i64ARB = (PFNGLPROGRAMUNIFORM3I64ARBPROC) load(userptr, "glProgramUniform3i64ARB"); + glad_glProgramUniform3i64vARB = (PFNGLPROGRAMUNIFORM3I64VARBPROC) load(userptr, "glProgramUniform3i64vARB"); + glad_glProgramUniform3ui64ARB = (PFNGLPROGRAMUNIFORM3UI64ARBPROC) load(userptr, "glProgramUniform3ui64ARB"); + glad_glProgramUniform3ui64vARB = (PFNGLPROGRAMUNIFORM3UI64VARBPROC) load(userptr, "glProgramUniform3ui64vARB"); + glad_glProgramUniform4i64ARB = (PFNGLPROGRAMUNIFORM4I64ARBPROC) load(userptr, "glProgramUniform4i64ARB"); + glad_glProgramUniform4i64vARB = (PFNGLPROGRAMUNIFORM4I64VARBPROC) load(userptr, "glProgramUniform4i64vARB"); + glad_glProgramUniform4ui64ARB = (PFNGLPROGRAMUNIFORM4UI64ARBPROC) load(userptr, "glProgramUniform4ui64ARB"); + glad_glProgramUniform4ui64vARB = (PFNGLPROGRAMUNIFORM4UI64VARBPROC) load(userptr, "glProgramUniform4ui64vARB"); + glad_glUniform1i64ARB = (PFNGLUNIFORM1I64ARBPROC) load(userptr, "glUniform1i64ARB"); + glad_glUniform1i64vARB = (PFNGLUNIFORM1I64VARBPROC) load(userptr, "glUniform1i64vARB"); + glad_glUniform1ui64ARB = (PFNGLUNIFORM1UI64ARBPROC) load(userptr, "glUniform1ui64ARB"); + glad_glUniform1ui64vARB = (PFNGLUNIFORM1UI64VARBPROC) load(userptr, "glUniform1ui64vARB"); + glad_glUniform2i64ARB = (PFNGLUNIFORM2I64ARBPROC) load(userptr, "glUniform2i64ARB"); + glad_glUniform2i64vARB = (PFNGLUNIFORM2I64VARBPROC) load(userptr, "glUniform2i64vARB"); + glad_glUniform2ui64ARB = (PFNGLUNIFORM2UI64ARBPROC) load(userptr, "glUniform2ui64ARB"); + glad_glUniform2ui64vARB = (PFNGLUNIFORM2UI64VARBPROC) load(userptr, "glUniform2ui64vARB"); + glad_glUniform3i64ARB = (PFNGLUNIFORM3I64ARBPROC) load(userptr, "glUniform3i64ARB"); + glad_glUniform3i64vARB = (PFNGLUNIFORM3I64VARBPROC) load(userptr, "glUniform3i64vARB"); + glad_glUniform3ui64ARB = (PFNGLUNIFORM3UI64ARBPROC) load(userptr, "glUniform3ui64ARB"); + glad_glUniform3ui64vARB = (PFNGLUNIFORM3UI64VARBPROC) load(userptr, "glUniform3ui64vARB"); + glad_glUniform4i64ARB = (PFNGLUNIFORM4I64ARBPROC) load(userptr, "glUniform4i64ARB"); + glad_glUniform4i64vARB = (PFNGLUNIFORM4I64VARBPROC) load(userptr, "glUniform4i64vARB"); + glad_glUniform4ui64ARB = (PFNGLUNIFORM4UI64ARBPROC) load(userptr, "glUniform4ui64ARB"); + glad_glUniform4ui64vARB = (PFNGLUNIFORM4UI64VARBPROC) load(userptr, "glUniform4ui64vARB"); +} +static void glad_gl_load_GL_ARB_instanced_arrays( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_instanced_arrays) return; + glad_glVertexAttribDivisorARB = (PFNGLVERTEXATTRIBDIVISORARBPROC) load(userptr, "glVertexAttribDivisorARB"); +} +static void glad_gl_load_GL_ARB_internalformat_query( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_internalformat_query) return; + glad_glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC) load(userptr, "glGetInternalformativ"); +} +static void glad_gl_load_GL_ARB_internalformat_query2( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_internalformat_query2) return; + glad_glGetInternalformati64v = (PFNGLGETINTERNALFORMATI64VPROC) load(userptr, "glGetInternalformati64v"); +} +static void glad_gl_load_GL_ARB_map_buffer_range( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_map_buffer_range) return; + glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) load(userptr, "glFlushMappedBufferRange"); + glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) load(userptr, "glMapBufferRange"); +} +static void glad_gl_load_GL_ARB_multi_bind( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_multi_bind) return; + glad_glBindBuffersBase = (PFNGLBINDBUFFERSBASEPROC) load(userptr, "glBindBuffersBase"); + glad_glBindBuffersRange = (PFNGLBINDBUFFERSRANGEPROC) load(userptr, "glBindBuffersRange"); + glad_glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC) load(userptr, "glBindImageTextures"); + glad_glBindSamplers = (PFNGLBINDSAMPLERSPROC) load(userptr, "glBindSamplers"); + glad_glBindTextures = (PFNGLBINDTEXTURESPROC) load(userptr, "glBindTextures"); + glad_glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC) load(userptr, "glBindVertexBuffers"); +} +static void glad_gl_load_GL_ARB_multi_draw_indirect( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_multi_draw_indirect) return; + glad_glMultiDrawArraysIndirect = (PFNGLMULTIDRAWARRAYSINDIRECTPROC) load(userptr, "glMultiDrawArraysIndirect"); + glad_glMultiDrawElementsIndirect = (PFNGLMULTIDRAWELEMENTSINDIRECTPROC) load(userptr, "glMultiDrawElementsIndirect"); +} +static void glad_gl_load_GL_ARB_multisample( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_multisample) return; + glad_glSampleCoverageARB = (PFNGLSAMPLECOVERAGEARBPROC) load(userptr, "glSampleCoverageARB"); +} +static void glad_gl_load_GL_ARB_multitexture( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_multitexture) return; + glad_glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) load(userptr, "glActiveTextureARB"); + glad_glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) load(userptr, "glClientActiveTextureARB"); + glad_glMultiTexCoord1dARB = (PFNGLMULTITEXCOORD1DARBPROC) load(userptr, "glMultiTexCoord1dARB"); + glad_glMultiTexCoord1dvARB = (PFNGLMULTITEXCOORD1DVARBPROC) load(userptr, "glMultiTexCoord1dvARB"); + glad_glMultiTexCoord1fARB = (PFNGLMULTITEXCOORD1FARBPROC) load(userptr, "glMultiTexCoord1fARB"); + glad_glMultiTexCoord1fvARB = (PFNGLMULTITEXCOORD1FVARBPROC) load(userptr, "glMultiTexCoord1fvARB"); + glad_glMultiTexCoord1iARB = (PFNGLMULTITEXCOORD1IARBPROC) load(userptr, "glMultiTexCoord1iARB"); + glad_glMultiTexCoord1ivARB = (PFNGLMULTITEXCOORD1IVARBPROC) load(userptr, "glMultiTexCoord1ivARB"); + glad_glMultiTexCoord1sARB = (PFNGLMULTITEXCOORD1SARBPROC) load(userptr, "glMultiTexCoord1sARB"); + glad_glMultiTexCoord1svARB = (PFNGLMULTITEXCOORD1SVARBPROC) load(userptr, "glMultiTexCoord1svARB"); + glad_glMultiTexCoord2dARB = (PFNGLMULTITEXCOORD2DARBPROC) load(userptr, "glMultiTexCoord2dARB"); + glad_glMultiTexCoord2dvARB = (PFNGLMULTITEXCOORD2DVARBPROC) load(userptr, "glMultiTexCoord2dvARB"); + glad_glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) load(userptr, "glMultiTexCoord2fARB"); + glad_glMultiTexCoord2fvARB = (PFNGLMULTITEXCOORD2FVARBPROC) load(userptr, "glMultiTexCoord2fvARB"); + glad_glMultiTexCoord2iARB = (PFNGLMULTITEXCOORD2IARBPROC) load(userptr, "glMultiTexCoord2iARB"); + glad_glMultiTexCoord2ivARB = (PFNGLMULTITEXCOORD2IVARBPROC) load(userptr, "glMultiTexCoord2ivARB"); + glad_glMultiTexCoord2sARB = (PFNGLMULTITEXCOORD2SARBPROC) load(userptr, "glMultiTexCoord2sARB"); + glad_glMultiTexCoord2svARB = (PFNGLMULTITEXCOORD2SVARBPROC) load(userptr, "glMultiTexCoord2svARB"); + glad_glMultiTexCoord3dARB = (PFNGLMULTITEXCOORD3DARBPROC) load(userptr, "glMultiTexCoord3dARB"); + glad_glMultiTexCoord3dvARB = (PFNGLMULTITEXCOORD3DVARBPROC) load(userptr, "glMultiTexCoord3dvARB"); + glad_glMultiTexCoord3fARB = (PFNGLMULTITEXCOORD3FARBPROC) load(userptr, "glMultiTexCoord3fARB"); + glad_glMultiTexCoord3fvARB = (PFNGLMULTITEXCOORD3FVARBPROC) load(userptr, "glMultiTexCoord3fvARB"); + glad_glMultiTexCoord3iARB = (PFNGLMULTITEXCOORD3IARBPROC) load(userptr, "glMultiTexCoord3iARB"); + glad_glMultiTexCoord3ivARB = (PFNGLMULTITEXCOORD3IVARBPROC) load(userptr, "glMultiTexCoord3ivARB"); + glad_glMultiTexCoord3sARB = (PFNGLMULTITEXCOORD3SARBPROC) load(userptr, "glMultiTexCoord3sARB"); + glad_glMultiTexCoord3svARB = (PFNGLMULTITEXCOORD3SVARBPROC) load(userptr, "glMultiTexCoord3svARB"); + glad_glMultiTexCoord4dARB = (PFNGLMULTITEXCOORD4DARBPROC) load(userptr, "glMultiTexCoord4dARB"); + glad_glMultiTexCoord4dvARB = (PFNGLMULTITEXCOORD4DVARBPROC) load(userptr, "glMultiTexCoord4dvARB"); + glad_glMultiTexCoord4fARB = (PFNGLMULTITEXCOORD4FARBPROC) load(userptr, "glMultiTexCoord4fARB"); + glad_glMultiTexCoord4fvARB = (PFNGLMULTITEXCOORD4FVARBPROC) load(userptr, "glMultiTexCoord4fvARB"); + glad_glMultiTexCoord4iARB = (PFNGLMULTITEXCOORD4IARBPROC) load(userptr, "glMultiTexCoord4iARB"); + glad_glMultiTexCoord4ivARB = (PFNGLMULTITEXCOORD4IVARBPROC) load(userptr, "glMultiTexCoord4ivARB"); + glad_glMultiTexCoord4sARB = (PFNGLMULTITEXCOORD4SARBPROC) load(userptr, "glMultiTexCoord4sARB"); + glad_glMultiTexCoord4svARB = (PFNGLMULTITEXCOORD4SVARBPROC) load(userptr, "glMultiTexCoord4svARB"); +} +static void glad_gl_load_GL_ARB_occlusion_query( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_occlusion_query) return; + glad_glBeginQueryARB = (PFNGLBEGINQUERYARBPROC) load(userptr, "glBeginQueryARB"); + glad_glDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC) load(userptr, "glDeleteQueriesARB"); + glad_glEndQueryARB = (PFNGLENDQUERYARBPROC) load(userptr, "glEndQueryARB"); + glad_glGenQueriesARB = (PFNGLGENQUERIESARBPROC) load(userptr, "glGenQueriesARB"); + glad_glGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC) load(userptr, "glGetQueryObjectivARB"); + glad_glGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC) load(userptr, "glGetQueryObjectuivARB"); + glad_glGetQueryivARB = (PFNGLGETQUERYIVARBPROC) load(userptr, "glGetQueryivARB"); + glad_glIsQueryARB = (PFNGLISQUERYARBPROC) load(userptr, "glIsQueryARB"); +} +static void glad_gl_load_GL_ARB_sample_locations( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_sample_locations) return; + glad_glEvaluateDepthValuesARB = (PFNGLEVALUATEDEPTHVALUESARBPROC) load(userptr, "glEvaluateDepthValuesARB"); + glad_glFramebufferSampleLocationsfvARB = (PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) load(userptr, "glFramebufferSampleLocationsfvARB"); + glad_glNamedFramebufferSampleLocationsfvARB = (PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) load(userptr, "glNamedFramebufferSampleLocationsfvARB"); +} +static void glad_gl_load_GL_ARB_sample_shading( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_sample_shading) return; + glad_glMinSampleShadingARB = (PFNGLMINSAMPLESHADINGARBPROC) load(userptr, "glMinSampleShadingARB"); +} +static void glad_gl_load_GL_ARB_shader_atomic_counters( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_shader_atomic_counters) return; + glad_glGetActiveAtomicCounterBufferiv = (PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) load(userptr, "glGetActiveAtomicCounterBufferiv"); +} +static void glad_gl_load_GL_ARB_shader_image_load_store( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_shader_image_load_store) return; + glad_glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC) load(userptr, "glBindImageTexture"); + glad_glMemoryBarrier = (PFNGLMEMORYBARRIERPROC) load(userptr, "glMemoryBarrier"); +} +static void glad_gl_load_GL_ARB_shader_objects( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_shader_objects) return; + glad_glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) load(userptr, "glAttachObjectARB"); + glad_glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) load(userptr, "glCompileShaderARB"); + glad_glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) load(userptr, "glCreateProgramObjectARB"); + glad_glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) load(userptr, "glCreateShaderObjectARB"); + glad_glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) load(userptr, "glDeleteObjectARB"); + glad_glDetachObjectARB = (PFNGLDETACHOBJECTARBPROC) load(userptr, "glDetachObjectARB"); + glad_glGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) load(userptr, "glGetActiveUniformARB"); + glad_glGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) load(userptr, "glGetAttachedObjectsARB"); + glad_glGetHandleARB = (PFNGLGETHANDLEARBPROC) load(userptr, "glGetHandleARB"); + glad_glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) load(userptr, "glGetInfoLogARB"); + glad_glGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC) load(userptr, "glGetObjectParameterfvARB"); + glad_glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) load(userptr, "glGetObjectParameterivARB"); + glad_glGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC) load(userptr, "glGetShaderSourceARB"); + glad_glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) load(userptr, "glGetUniformLocationARB"); + glad_glGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC) load(userptr, "glGetUniformfvARB"); + glad_glGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC) load(userptr, "glGetUniformivARB"); + glad_glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) load(userptr, "glLinkProgramARB"); + glad_glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) load(userptr, "glShaderSourceARB"); + glad_glUniform1fARB = (PFNGLUNIFORM1FARBPROC) load(userptr, "glUniform1fARB"); + glad_glUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) load(userptr, "glUniform1fvARB"); + glad_glUniform1iARB = (PFNGLUNIFORM1IARBPROC) load(userptr, "glUniform1iARB"); + glad_glUniform1ivARB = (PFNGLUNIFORM1IVARBPROC) load(userptr, "glUniform1ivARB"); + glad_glUniform2fARB = (PFNGLUNIFORM2FARBPROC) load(userptr, "glUniform2fARB"); + glad_glUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) load(userptr, "glUniform2fvARB"); + glad_glUniform2iARB = (PFNGLUNIFORM2IARBPROC) load(userptr, "glUniform2iARB"); + glad_glUniform2ivARB = (PFNGLUNIFORM2IVARBPROC) load(userptr, "glUniform2ivARB"); + glad_glUniform3fARB = (PFNGLUNIFORM3FARBPROC) load(userptr, "glUniform3fARB"); + glad_glUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) load(userptr, "glUniform3fvARB"); + glad_glUniform3iARB = (PFNGLUNIFORM3IARBPROC) load(userptr, "glUniform3iARB"); + glad_glUniform3ivARB = (PFNGLUNIFORM3IVARBPROC) load(userptr, "glUniform3ivARB"); + glad_glUniform4fARB = (PFNGLUNIFORM4FARBPROC) load(userptr, "glUniform4fARB"); + glad_glUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) load(userptr, "glUniform4fvARB"); + glad_glUniform4iARB = (PFNGLUNIFORM4IARBPROC) load(userptr, "glUniform4iARB"); + glad_glUniform4ivARB = (PFNGLUNIFORM4IVARBPROC) load(userptr, "glUniform4ivARB"); + glad_glUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) load(userptr, "glUniformMatrix2fvARB"); + glad_glUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) load(userptr, "glUniformMatrix3fvARB"); + glad_glUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) load(userptr, "glUniformMatrix4fvARB"); + glad_glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) load(userptr, "glUseProgramObjectARB"); + glad_glValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC) load(userptr, "glValidateProgramARB"); +} +static void glad_gl_load_GL_ARB_shader_storage_buffer_object( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_shader_storage_buffer_object) return; + glad_glShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC) load(userptr, "glShaderStorageBlockBinding"); +} +static void glad_gl_load_GL_ARB_shading_language_include( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_shading_language_include) return; + glad_glCompileShaderIncludeARB = (PFNGLCOMPILESHADERINCLUDEARBPROC) load(userptr, "glCompileShaderIncludeARB"); + glad_glDeleteNamedStringARB = (PFNGLDELETENAMEDSTRINGARBPROC) load(userptr, "glDeleteNamedStringARB"); + glad_glGetNamedStringARB = (PFNGLGETNAMEDSTRINGARBPROC) load(userptr, "glGetNamedStringARB"); + glad_glGetNamedStringivARB = (PFNGLGETNAMEDSTRINGIVARBPROC) load(userptr, "glGetNamedStringivARB"); + glad_glIsNamedStringARB = (PFNGLISNAMEDSTRINGARBPROC) load(userptr, "glIsNamedStringARB"); + glad_glNamedStringARB = (PFNGLNAMEDSTRINGARBPROC) load(userptr, "glNamedStringARB"); +} +static void glad_gl_load_GL_ARB_tessellation_shader( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_tessellation_shader) return; + glad_glPatchParameterfv = (PFNGLPATCHPARAMETERFVPROC) load(userptr, "glPatchParameterfv"); + glad_glPatchParameteri = (PFNGLPATCHPARAMETERIPROC) load(userptr, "glPatchParameteri"); +} +static void glad_gl_load_GL_ARB_texture_compression( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_texture_compression) return; + glad_glCompressedTexImage1DARB = (PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) load(userptr, "glCompressedTexImage1DARB"); + glad_glCompressedTexImage2DARB = (PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) load(userptr, "glCompressedTexImage2DARB"); + glad_glCompressedTexImage3DARB = (PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) load(userptr, "glCompressedTexImage3DARB"); + glad_glCompressedTexSubImage1DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) load(userptr, "glCompressedTexSubImage1DARB"); + glad_glCompressedTexSubImage2DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) load(userptr, "glCompressedTexSubImage2DARB"); + glad_glCompressedTexSubImage3DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) load(userptr, "glCompressedTexSubImage3DARB"); + glad_glGetCompressedTexImageARB = (PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) load(userptr, "glGetCompressedTexImageARB"); +} +static void glad_gl_load_GL_ARB_texture_multisample( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_texture_multisample) return; + glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) load(userptr, "glGetMultisamplefv"); + glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC) load(userptr, "glSampleMaski"); + glad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC) load(userptr, "glTexImage2DMultisample"); + glad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) load(userptr, "glTexImage3DMultisample"); +} +static void glad_gl_load_GL_ARB_texture_storage( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_texture_storage) return; + glad_glTexStorage1D = (PFNGLTEXSTORAGE1DPROC) load(userptr, "glTexStorage1D"); + glad_glTexStorage2D = (PFNGLTEXSTORAGE2DPROC) load(userptr, "glTexStorage2D"); + glad_glTexStorage3D = (PFNGLTEXSTORAGE3DPROC) load(userptr, "glTexStorage3D"); +} +static void glad_gl_load_GL_ARB_texture_view( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_texture_view) return; + glad_glTextureView = (PFNGLTEXTUREVIEWPROC) load(userptr, "glTextureView"); +} +static void glad_gl_load_GL_ARB_timer_query( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_timer_query) return; + glad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC) load(userptr, "glGetQueryObjecti64v"); + glad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC) load(userptr, "glGetQueryObjectui64v"); + glad_glQueryCounter = (PFNGLQUERYCOUNTERPROC) load(userptr, "glQueryCounter"); +} +static void glad_gl_load_GL_ARB_transpose_matrix( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_transpose_matrix) return; + glad_glLoadTransposeMatrixdARB = (PFNGLLOADTRANSPOSEMATRIXDARBPROC) load(userptr, "glLoadTransposeMatrixdARB"); + glad_glLoadTransposeMatrixfARB = (PFNGLLOADTRANSPOSEMATRIXFARBPROC) load(userptr, "glLoadTransposeMatrixfARB"); + glad_glMultTransposeMatrixdARB = (PFNGLMULTTRANSPOSEMATRIXDARBPROC) load(userptr, "glMultTransposeMatrixdARB"); + glad_glMultTransposeMatrixfARB = (PFNGLMULTTRANSPOSEMATRIXFARBPROC) load(userptr, "glMultTransposeMatrixfARB"); +} +static void glad_gl_load_GL_ARB_uniform_buffer_object( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_uniform_buffer_object) return; + glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); + glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); + glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) load(userptr, "glGetActiveUniformBlockName"); + glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC) load(userptr, "glGetActiveUniformBlockiv"); + glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC) load(userptr, "glGetActiveUniformName"); + glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC) load(userptr, "glGetActiveUniformsiv"); + glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); + glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC) load(userptr, "glGetUniformBlockIndex"); + glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC) load(userptr, "glGetUniformIndices"); + glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC) load(userptr, "glUniformBlockBinding"); +} +static void glad_gl_load_GL_ARB_vertex_array_object( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_vertex_array_object) return; + glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC) load(userptr, "glBindVertexArray"); + glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) load(userptr, "glDeleteVertexArrays"); + glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) load(userptr, "glGenVertexArrays"); + glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC) load(userptr, "glIsVertexArray"); +} +static void glad_gl_load_GL_ARB_vertex_attrib_binding( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_vertex_attrib_binding) return; + glad_glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC) load(userptr, "glBindVertexBuffer"); + glad_glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC) load(userptr, "glVertexAttribBinding"); + glad_glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC) load(userptr, "glVertexAttribFormat"); + glad_glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC) load(userptr, "glVertexAttribIFormat"); + glad_glVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC) load(userptr, "glVertexAttribLFormat"); + glad_glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC) load(userptr, "glVertexBindingDivisor"); +} +static void glad_gl_load_GL_ARB_vertex_buffer_object( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_vertex_buffer_object) return; + glad_glBindBufferARB = (PFNGLBINDBUFFERARBPROC) load(userptr, "glBindBufferARB"); + glad_glBufferDataARB = (PFNGLBUFFERDATAARBPROC) load(userptr, "glBufferDataARB"); + glad_glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC) load(userptr, "glBufferSubDataARB"); + glad_glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) load(userptr, "glDeleteBuffersARB"); + glad_glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) load(userptr, "glGenBuffersARB"); + glad_glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC) load(userptr, "glGetBufferParameterivARB"); + glad_glGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC) load(userptr, "glGetBufferPointervARB"); + glad_glGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC) load(userptr, "glGetBufferSubDataARB"); + glad_glIsBufferARB = (PFNGLISBUFFERARBPROC) load(userptr, "glIsBufferARB"); + glad_glMapBufferARB = (PFNGLMAPBUFFERARBPROC) load(userptr, "glMapBufferARB"); + glad_glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC) load(userptr, "glUnmapBufferARB"); +} +static void glad_gl_load_GL_ARB_vertex_program( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_vertex_program) return; + glad_glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) load(userptr, "glBindProgramARB"); + glad_glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) load(userptr, "glDeleteProgramsARB"); + glad_glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) load(userptr, "glDisableVertexAttribArrayARB"); + glad_glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) load(userptr, "glEnableVertexAttribArrayARB"); + glad_glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) load(userptr, "glGenProgramsARB"); + glad_glGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC) load(userptr, "glGetProgramEnvParameterdvARB"); + glad_glGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC) load(userptr, "glGetProgramEnvParameterfvARB"); + glad_glGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) load(userptr, "glGetProgramLocalParameterdvARB"); + glad_glGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) load(userptr, "glGetProgramLocalParameterfvARB"); + glad_glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) load(userptr, "glGetProgramStringARB"); + glad_glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) load(userptr, "glGetProgramivARB"); + glad_glGetVertexAttribPointervARB = (PFNGLGETVERTEXATTRIBPOINTERVARBPROC) load(userptr, "glGetVertexAttribPointervARB"); + glad_glGetVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC) load(userptr, "glGetVertexAttribdvARB"); + glad_glGetVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) load(userptr, "glGetVertexAttribfvARB"); + glad_glGetVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC) load(userptr, "glGetVertexAttribivARB"); + glad_glIsProgramARB = (PFNGLISPROGRAMARBPROC) load(userptr, "glIsProgramARB"); + glad_glProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC) load(userptr, "glProgramEnvParameter4dARB"); + glad_glProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC) load(userptr, "glProgramEnvParameter4dvARB"); + glad_glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC) load(userptr, "glProgramEnvParameter4fARB"); + glad_glProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC) load(userptr, "glProgramEnvParameter4fvARB"); + glad_glProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) load(userptr, "glProgramLocalParameter4dARB"); + glad_glProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) load(userptr, "glProgramLocalParameter4dvARB"); + glad_glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC) load(userptr, "glProgramLocalParameter4fARB"); + glad_glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) load(userptr, "glProgramLocalParameter4fvARB"); + glad_glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) load(userptr, "glProgramStringARB"); + glad_glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC) load(userptr, "glVertexAttrib1dARB"); + glad_glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC) load(userptr, "glVertexAttrib1dvARB"); + glad_glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC) load(userptr, "glVertexAttrib1fARB"); + glad_glVertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC) load(userptr, "glVertexAttrib1fvARB"); + glad_glVertexAttrib1sARB = (PFNGLVERTEXATTRIB1SARBPROC) load(userptr, "glVertexAttrib1sARB"); + glad_glVertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC) load(userptr, "glVertexAttrib1svARB"); + glad_glVertexAttrib2dARB = (PFNGLVERTEXATTRIB2DARBPROC) load(userptr, "glVertexAttrib2dARB"); + glad_glVertexAttrib2dvARB = (PFNGLVERTEXATTRIB2DVARBPROC) load(userptr, "glVertexAttrib2dvARB"); + glad_glVertexAttrib2fARB = (PFNGLVERTEXATTRIB2FARBPROC) load(userptr, "glVertexAttrib2fARB"); + glad_glVertexAttrib2fvARB = (PFNGLVERTEXATTRIB2FVARBPROC) load(userptr, "glVertexAttrib2fvARB"); + glad_glVertexAttrib2sARB = (PFNGLVERTEXATTRIB2SARBPROC) load(userptr, "glVertexAttrib2sARB"); + glad_glVertexAttrib2svARB = (PFNGLVERTEXATTRIB2SVARBPROC) load(userptr, "glVertexAttrib2svARB"); + glad_glVertexAttrib3dARB = (PFNGLVERTEXATTRIB3DARBPROC) load(userptr, "glVertexAttrib3dARB"); + glad_glVertexAttrib3dvARB = (PFNGLVERTEXATTRIB3DVARBPROC) load(userptr, "glVertexAttrib3dvARB"); + glad_glVertexAttrib3fARB = (PFNGLVERTEXATTRIB3FARBPROC) load(userptr, "glVertexAttrib3fARB"); + glad_glVertexAttrib3fvARB = (PFNGLVERTEXATTRIB3FVARBPROC) load(userptr, "glVertexAttrib3fvARB"); + glad_glVertexAttrib3sARB = (PFNGLVERTEXATTRIB3SARBPROC) load(userptr, "glVertexAttrib3sARB"); + glad_glVertexAttrib3svARB = (PFNGLVERTEXATTRIB3SVARBPROC) load(userptr, "glVertexAttrib3svARB"); + glad_glVertexAttrib4NbvARB = (PFNGLVERTEXATTRIB4NBVARBPROC) load(userptr, "glVertexAttrib4NbvARB"); + glad_glVertexAttrib4NivARB = (PFNGLVERTEXATTRIB4NIVARBPROC) load(userptr, "glVertexAttrib4NivARB"); + glad_glVertexAttrib4NsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC) load(userptr, "glVertexAttrib4NsvARB"); + glad_glVertexAttrib4NubARB = (PFNGLVERTEXATTRIB4NUBARBPROC) load(userptr, "glVertexAttrib4NubARB"); + glad_glVertexAttrib4NubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC) load(userptr, "glVertexAttrib4NubvARB"); + glad_glVertexAttrib4NuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC) load(userptr, "glVertexAttrib4NuivARB"); + glad_glVertexAttrib4NusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC) load(userptr, "glVertexAttrib4NusvARB"); + glad_glVertexAttrib4bvARB = (PFNGLVERTEXATTRIB4BVARBPROC) load(userptr, "glVertexAttrib4bvARB"); + glad_glVertexAttrib4dARB = (PFNGLVERTEXATTRIB4DARBPROC) load(userptr, "glVertexAttrib4dARB"); + glad_glVertexAttrib4dvARB = (PFNGLVERTEXATTRIB4DVARBPROC) load(userptr, "glVertexAttrib4dvARB"); + glad_glVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) load(userptr, "glVertexAttrib4fARB"); + glad_glVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) load(userptr, "glVertexAttrib4fvARB"); + glad_glVertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC) load(userptr, "glVertexAttrib4ivARB"); + glad_glVertexAttrib4sARB = (PFNGLVERTEXATTRIB4SARBPROC) load(userptr, "glVertexAttrib4sARB"); + glad_glVertexAttrib4svARB = (PFNGLVERTEXATTRIB4SVARBPROC) load(userptr, "glVertexAttrib4svARB"); + glad_glVertexAttrib4ubvARB = (PFNGLVERTEXATTRIB4UBVARBPROC) load(userptr, "glVertexAttrib4ubvARB"); + glad_glVertexAttrib4uivARB = (PFNGLVERTEXATTRIB4UIVARBPROC) load(userptr, "glVertexAttrib4uivARB"); + glad_glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC) load(userptr, "glVertexAttrib4usvARB"); + glad_glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) load(userptr, "glVertexAttribPointerARB"); +} +static void glad_gl_load_GL_ARB_vertex_shader( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_ARB_vertex_shader) return; + glad_glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) load(userptr, "glBindAttribLocationARB"); + glad_glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) load(userptr, "glDisableVertexAttribArrayARB"); + glad_glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) load(userptr, "glEnableVertexAttribArrayARB"); + glad_glGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC) load(userptr, "glGetActiveAttribARB"); + glad_glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) load(userptr, "glGetAttribLocationARB"); + glad_glGetVertexAttribPointervARB = (PFNGLGETVERTEXATTRIBPOINTERVARBPROC) load(userptr, "glGetVertexAttribPointervARB"); + glad_glGetVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC) load(userptr, "glGetVertexAttribdvARB"); + glad_glGetVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) load(userptr, "glGetVertexAttribfvARB"); + glad_glGetVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC) load(userptr, "glGetVertexAttribivARB"); + glad_glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC) load(userptr, "glVertexAttrib1dARB"); + glad_glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC) load(userptr, "glVertexAttrib1dvARB"); + glad_glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC) load(userptr, "glVertexAttrib1fARB"); + glad_glVertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC) load(userptr, "glVertexAttrib1fvARB"); + glad_glVertexAttrib1sARB = (PFNGLVERTEXATTRIB1SARBPROC) load(userptr, "glVertexAttrib1sARB"); + glad_glVertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC) load(userptr, "glVertexAttrib1svARB"); + glad_glVertexAttrib2dARB = (PFNGLVERTEXATTRIB2DARBPROC) load(userptr, "glVertexAttrib2dARB"); + glad_glVertexAttrib2dvARB = (PFNGLVERTEXATTRIB2DVARBPROC) load(userptr, "glVertexAttrib2dvARB"); + glad_glVertexAttrib2fARB = (PFNGLVERTEXATTRIB2FARBPROC) load(userptr, "glVertexAttrib2fARB"); + glad_glVertexAttrib2fvARB = (PFNGLVERTEXATTRIB2FVARBPROC) load(userptr, "glVertexAttrib2fvARB"); + glad_glVertexAttrib2sARB = (PFNGLVERTEXATTRIB2SARBPROC) load(userptr, "glVertexAttrib2sARB"); + glad_glVertexAttrib2svARB = (PFNGLVERTEXATTRIB2SVARBPROC) load(userptr, "glVertexAttrib2svARB"); + glad_glVertexAttrib3dARB = (PFNGLVERTEXATTRIB3DARBPROC) load(userptr, "glVertexAttrib3dARB"); + glad_glVertexAttrib3dvARB = (PFNGLVERTEXATTRIB3DVARBPROC) load(userptr, "glVertexAttrib3dvARB"); + glad_glVertexAttrib3fARB = (PFNGLVERTEXATTRIB3FARBPROC) load(userptr, "glVertexAttrib3fARB"); + glad_glVertexAttrib3fvARB = (PFNGLVERTEXATTRIB3FVARBPROC) load(userptr, "glVertexAttrib3fvARB"); + glad_glVertexAttrib3sARB = (PFNGLVERTEXATTRIB3SARBPROC) load(userptr, "glVertexAttrib3sARB"); + glad_glVertexAttrib3svARB = (PFNGLVERTEXATTRIB3SVARBPROC) load(userptr, "glVertexAttrib3svARB"); + glad_glVertexAttrib4NbvARB = (PFNGLVERTEXATTRIB4NBVARBPROC) load(userptr, "glVertexAttrib4NbvARB"); + glad_glVertexAttrib4NivARB = (PFNGLVERTEXATTRIB4NIVARBPROC) load(userptr, "glVertexAttrib4NivARB"); + glad_glVertexAttrib4NsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC) load(userptr, "glVertexAttrib4NsvARB"); + glad_glVertexAttrib4NubARB = (PFNGLVERTEXATTRIB4NUBARBPROC) load(userptr, "glVertexAttrib4NubARB"); + glad_glVertexAttrib4NubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC) load(userptr, "glVertexAttrib4NubvARB"); + glad_glVertexAttrib4NuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC) load(userptr, "glVertexAttrib4NuivARB"); + glad_glVertexAttrib4NusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC) load(userptr, "glVertexAttrib4NusvARB"); + glad_glVertexAttrib4bvARB = (PFNGLVERTEXATTRIB4BVARBPROC) load(userptr, "glVertexAttrib4bvARB"); + glad_glVertexAttrib4dARB = (PFNGLVERTEXATTRIB4DARBPROC) load(userptr, "glVertexAttrib4dARB"); + glad_glVertexAttrib4dvARB = (PFNGLVERTEXATTRIB4DVARBPROC) load(userptr, "glVertexAttrib4dvARB"); + glad_glVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) load(userptr, "glVertexAttrib4fARB"); + glad_glVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) load(userptr, "glVertexAttrib4fvARB"); + glad_glVertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC) load(userptr, "glVertexAttrib4ivARB"); + glad_glVertexAttrib4sARB = (PFNGLVERTEXATTRIB4SARBPROC) load(userptr, "glVertexAttrib4sARB"); + glad_glVertexAttrib4svARB = (PFNGLVERTEXATTRIB4SVARBPROC) load(userptr, "glVertexAttrib4svARB"); + glad_glVertexAttrib4ubvARB = (PFNGLVERTEXATTRIB4UBVARBPROC) load(userptr, "glVertexAttrib4ubvARB"); + glad_glVertexAttrib4uivARB = (PFNGLVERTEXATTRIB4UIVARBPROC) load(userptr, "glVertexAttrib4uivARB"); + glad_glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC) load(userptr, "glVertexAttrib4usvARB"); + glad_glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) load(userptr, "glVertexAttribPointerARB"); +} +static void glad_gl_load_GL_EXT_draw_instanced( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_draw_instanced) return; + glad_glDrawArraysInstancedEXT = (PFNGLDRAWARRAYSINSTANCEDEXTPROC) load(userptr, "glDrawArraysInstancedEXT"); + glad_glDrawElementsInstancedEXT = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC) load(userptr, "glDrawElementsInstancedEXT"); +} +static void glad_gl_load_GL_EXT_fog_coord( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_fog_coord) return; + glad_glFogCoordPointerEXT = (PFNGLFOGCOORDPOINTEREXTPROC) load(userptr, "glFogCoordPointerEXT"); + glad_glFogCoorddEXT = (PFNGLFOGCOORDDEXTPROC) load(userptr, "glFogCoorddEXT"); + glad_glFogCoorddvEXT = (PFNGLFOGCOORDDVEXTPROC) load(userptr, "glFogCoorddvEXT"); + glad_glFogCoordfEXT = (PFNGLFOGCOORDFEXTPROC) load(userptr, "glFogCoordfEXT"); + glad_glFogCoordfvEXT = (PFNGLFOGCOORDFVEXTPROC) load(userptr, "glFogCoordfvEXT"); +} +static void glad_gl_load_GL_EXT_framebuffer_blit( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_framebuffer_blit) return; + glad_glBlitFramebufferEXT = (PFNGLBLITFRAMEBUFFEREXTPROC) load(userptr, "glBlitFramebufferEXT"); +} +static void glad_gl_load_GL_EXT_framebuffer_multisample( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_framebuffer_multisample) return; + glad_glRenderbufferStorageMultisampleEXT = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) load(userptr, "glRenderbufferStorageMultisampleEXT"); +} +static void glad_gl_load_GL_EXT_framebuffer_object( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_EXT_framebuffer_object) return; + glad_glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) load(userptr, "glBindFramebufferEXT"); + glad_glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC) load(userptr, "glBindRenderbufferEXT"); + glad_glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) load(userptr, "glCheckFramebufferStatusEXT"); + glad_glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC) load(userptr, "glDeleteFramebuffersEXT"); + glad_glDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC) load(userptr, "glDeleteRenderbuffersEXT"); + glad_glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) load(userptr, "glFramebufferRenderbufferEXT"); + glad_glFramebufferTexture1DEXT = (PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) load(userptr, "glFramebufferTexture1DEXT"); + glad_glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) load(userptr, "glFramebufferTexture2DEXT"); + glad_glFramebufferTexture3DEXT = (PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) load(userptr, "glFramebufferTexture3DEXT"); + glad_glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) load(userptr, "glGenFramebuffersEXT"); + glad_glGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC) load(userptr, "glGenRenderbuffersEXT"); + glad_glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC) load(userptr, "glGenerateMipmapEXT"); + glad_glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) load(userptr, "glGetFramebufferAttachmentParameterivEXT"); + glad_glGetRenderbufferParameterivEXT = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) load(userptr, "glGetRenderbufferParameterivEXT"); + glad_glIsFramebufferEXT = (PFNGLISFRAMEBUFFEREXTPROC) load(userptr, "glIsFramebufferEXT"); + glad_glIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC) load(userptr, "glIsRenderbufferEXT"); + glad_glRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC) load(userptr, "glRenderbufferStorageEXT"); +} +static void glad_gl_load_GL_OES_fixed_point( GLADuserptrloadfunc load, void* userptr) { + if(!GLAD_GL_OES_fixed_point) return; + glad_glAccumxOES = (PFNGLACCUMXOESPROC) load(userptr, "glAccumxOES"); + glad_glAlphaFuncxOES = (PFNGLALPHAFUNCXOESPROC) load(userptr, "glAlphaFuncxOES"); + glad_glBitmapxOES = (PFNGLBITMAPXOESPROC) load(userptr, "glBitmapxOES"); + glad_glBlendColorxOES = (PFNGLBLENDCOLORXOESPROC) load(userptr, "glBlendColorxOES"); + glad_glClearAccumxOES = (PFNGLCLEARACCUMXOESPROC) load(userptr, "glClearAccumxOES"); + glad_glClearColorxOES = (PFNGLCLEARCOLORXOESPROC) load(userptr, "glClearColorxOES"); + glad_glClearDepthxOES = (PFNGLCLEARDEPTHXOESPROC) load(userptr, "glClearDepthxOES"); + glad_glClipPlanexOES = (PFNGLCLIPPLANEXOESPROC) load(userptr, "glClipPlanexOES"); + glad_glColor3xOES = (PFNGLCOLOR3XOESPROC) load(userptr, "glColor3xOES"); + glad_glColor3xvOES = (PFNGLCOLOR3XVOESPROC) load(userptr, "glColor3xvOES"); + glad_glColor4xOES = (PFNGLCOLOR4XOESPROC) load(userptr, "glColor4xOES"); + glad_glColor4xvOES = (PFNGLCOLOR4XVOESPROC) load(userptr, "glColor4xvOES"); + glad_glConvolutionParameterxOES = (PFNGLCONVOLUTIONPARAMETERXOESPROC) load(userptr, "glConvolutionParameterxOES"); + glad_glConvolutionParameterxvOES = (PFNGLCONVOLUTIONPARAMETERXVOESPROC) load(userptr, "glConvolutionParameterxvOES"); + glad_glDepthRangexOES = (PFNGLDEPTHRANGEXOESPROC) load(userptr, "glDepthRangexOES"); + glad_glEvalCoord1xOES = (PFNGLEVALCOORD1XOESPROC) load(userptr, "glEvalCoord1xOES"); + glad_glEvalCoord1xvOES = (PFNGLEVALCOORD1XVOESPROC) load(userptr, "glEvalCoord1xvOES"); + glad_glEvalCoord2xOES = (PFNGLEVALCOORD2XOESPROC) load(userptr, "glEvalCoord2xOES"); + glad_glEvalCoord2xvOES = (PFNGLEVALCOORD2XVOESPROC) load(userptr, "glEvalCoord2xvOES"); + glad_glFeedbackBufferxOES = (PFNGLFEEDBACKBUFFERXOESPROC) load(userptr, "glFeedbackBufferxOES"); + glad_glFogxOES = (PFNGLFOGXOESPROC) load(userptr, "glFogxOES"); + glad_glFogxvOES = (PFNGLFOGXVOESPROC) load(userptr, "glFogxvOES"); + glad_glFrustumxOES = (PFNGLFRUSTUMXOESPROC) load(userptr, "glFrustumxOES"); + glad_glGetClipPlanexOES = (PFNGLGETCLIPPLANEXOESPROC) load(userptr, "glGetClipPlanexOES"); + glad_glGetConvolutionParameterxvOES = (PFNGLGETCONVOLUTIONPARAMETERXVOESPROC) load(userptr, "glGetConvolutionParameterxvOES"); + glad_glGetFixedvOES = (PFNGLGETFIXEDVOESPROC) load(userptr, "glGetFixedvOES"); + glad_glGetHistogramParameterxvOES = (PFNGLGETHISTOGRAMPARAMETERXVOESPROC) load(userptr, "glGetHistogramParameterxvOES"); + glad_glGetLightxOES = (PFNGLGETLIGHTXOESPROC) load(userptr, "glGetLightxOES"); + glad_glGetMapxvOES = (PFNGLGETMAPXVOESPROC) load(userptr, "glGetMapxvOES"); + glad_glGetMaterialxOES = (PFNGLGETMATERIALXOESPROC) load(userptr, "glGetMaterialxOES"); + glad_glGetPixelMapxv = (PFNGLGETPIXELMAPXVPROC) load(userptr, "glGetPixelMapxv"); + glad_glGetTexEnvxvOES = (PFNGLGETTEXENVXVOESPROC) load(userptr, "glGetTexEnvxvOES"); + glad_glGetTexGenxvOES = (PFNGLGETTEXGENXVOESPROC) load(userptr, "glGetTexGenxvOES"); + glad_glGetTexLevelParameterxvOES = (PFNGLGETTEXLEVELPARAMETERXVOESPROC) load(userptr, "glGetTexLevelParameterxvOES"); + glad_glGetTexParameterxvOES = (PFNGLGETTEXPARAMETERXVOESPROC) load(userptr, "glGetTexParameterxvOES"); + glad_glIndexxOES = (PFNGLINDEXXOESPROC) load(userptr, "glIndexxOES"); + glad_glIndexxvOES = (PFNGLINDEXXVOESPROC) load(userptr, "glIndexxvOES"); + glad_glLightModelxOES = (PFNGLLIGHTMODELXOESPROC) load(userptr, "glLightModelxOES"); + glad_glLightModelxvOES = (PFNGLLIGHTMODELXVOESPROC) load(userptr, "glLightModelxvOES"); + glad_glLightxOES = (PFNGLLIGHTXOESPROC) load(userptr, "glLightxOES"); + glad_glLightxvOES = (PFNGLLIGHTXVOESPROC) load(userptr, "glLightxvOES"); + glad_glLineWidthxOES = (PFNGLLINEWIDTHXOESPROC) load(userptr, "glLineWidthxOES"); + glad_glLoadMatrixxOES = (PFNGLLOADMATRIXXOESPROC) load(userptr, "glLoadMatrixxOES"); + glad_glLoadTransposeMatrixxOES = (PFNGLLOADTRANSPOSEMATRIXXOESPROC) load(userptr, "glLoadTransposeMatrixxOES"); + glad_glMap1xOES = (PFNGLMAP1XOESPROC) load(userptr, "glMap1xOES"); + glad_glMap2xOES = (PFNGLMAP2XOESPROC) load(userptr, "glMap2xOES"); + glad_glMapGrid1xOES = (PFNGLMAPGRID1XOESPROC) load(userptr, "glMapGrid1xOES"); + glad_glMapGrid2xOES = (PFNGLMAPGRID2XOESPROC) load(userptr, "glMapGrid2xOES"); + glad_glMaterialxOES = (PFNGLMATERIALXOESPROC) load(userptr, "glMaterialxOES"); + glad_glMaterialxvOES = (PFNGLMATERIALXVOESPROC) load(userptr, "glMaterialxvOES"); + glad_glMultMatrixxOES = (PFNGLMULTMATRIXXOESPROC) load(userptr, "glMultMatrixxOES"); + glad_glMultTransposeMatrixxOES = (PFNGLMULTTRANSPOSEMATRIXXOESPROC) load(userptr, "glMultTransposeMatrixxOES"); + glad_glMultiTexCoord1xOES = (PFNGLMULTITEXCOORD1XOESPROC) load(userptr, "glMultiTexCoord1xOES"); + glad_glMultiTexCoord1xvOES = (PFNGLMULTITEXCOORD1XVOESPROC) load(userptr, "glMultiTexCoord1xvOES"); + glad_glMultiTexCoord2xOES = (PFNGLMULTITEXCOORD2XOESPROC) load(userptr, "glMultiTexCoord2xOES"); + glad_glMultiTexCoord2xvOES = (PFNGLMULTITEXCOORD2XVOESPROC) load(userptr, "glMultiTexCoord2xvOES"); + glad_glMultiTexCoord3xOES = (PFNGLMULTITEXCOORD3XOESPROC) load(userptr, "glMultiTexCoord3xOES"); + glad_glMultiTexCoord3xvOES = (PFNGLMULTITEXCOORD3XVOESPROC) load(userptr, "glMultiTexCoord3xvOES"); + glad_glMultiTexCoord4xOES = (PFNGLMULTITEXCOORD4XOESPROC) load(userptr, "glMultiTexCoord4xOES"); + glad_glMultiTexCoord4xvOES = (PFNGLMULTITEXCOORD4XVOESPROC) load(userptr, "glMultiTexCoord4xvOES"); + glad_glNormal3xOES = (PFNGLNORMAL3XOESPROC) load(userptr, "glNormal3xOES"); + glad_glNormal3xvOES = (PFNGLNORMAL3XVOESPROC) load(userptr, "glNormal3xvOES"); + glad_glOrthoxOES = (PFNGLORTHOXOESPROC) load(userptr, "glOrthoxOES"); + glad_glPassThroughxOES = (PFNGLPASSTHROUGHXOESPROC) load(userptr, "glPassThroughxOES"); + glad_glPixelMapx = (PFNGLPIXELMAPXPROC) load(userptr, "glPixelMapx"); + glad_glPixelStorex = (PFNGLPIXELSTOREXPROC) load(userptr, "glPixelStorex"); + glad_glPixelTransferxOES = (PFNGLPIXELTRANSFERXOESPROC) load(userptr, "glPixelTransferxOES"); + glad_glPixelZoomxOES = (PFNGLPIXELZOOMXOESPROC) load(userptr, "glPixelZoomxOES"); + glad_glPointParameterxvOES = (PFNGLPOINTPARAMETERXVOESPROC) load(userptr, "glPointParameterxvOES"); + glad_glPointSizexOES = (PFNGLPOINTSIZEXOESPROC) load(userptr, "glPointSizexOES"); + glad_glPolygonOffsetxOES = (PFNGLPOLYGONOFFSETXOESPROC) load(userptr, "glPolygonOffsetxOES"); + glad_glPrioritizeTexturesxOES = (PFNGLPRIORITIZETEXTURESXOESPROC) load(userptr, "glPrioritizeTexturesxOES"); + glad_glRasterPos2xOES = (PFNGLRASTERPOS2XOESPROC) load(userptr, "glRasterPos2xOES"); + glad_glRasterPos2xvOES = (PFNGLRASTERPOS2XVOESPROC) load(userptr, "glRasterPos2xvOES"); + glad_glRasterPos3xOES = (PFNGLRASTERPOS3XOESPROC) load(userptr, "glRasterPos3xOES"); + glad_glRasterPos3xvOES = (PFNGLRASTERPOS3XVOESPROC) load(userptr, "glRasterPos3xvOES"); + glad_glRasterPos4xOES = (PFNGLRASTERPOS4XOESPROC) load(userptr, "glRasterPos4xOES"); + glad_glRasterPos4xvOES = (PFNGLRASTERPOS4XVOESPROC) load(userptr, "glRasterPos4xvOES"); + glad_glRectxOES = (PFNGLRECTXOESPROC) load(userptr, "glRectxOES"); + glad_glRectxvOES = (PFNGLRECTXVOESPROC) load(userptr, "glRectxvOES"); + glad_glRotatexOES = (PFNGLROTATEXOESPROC) load(userptr, "glRotatexOES"); + glad_glScalexOES = (PFNGLSCALEXOESPROC) load(userptr, "glScalexOES"); + glad_glTexCoord1xOES = (PFNGLTEXCOORD1XOESPROC) load(userptr, "glTexCoord1xOES"); + glad_glTexCoord1xvOES = (PFNGLTEXCOORD1XVOESPROC) load(userptr, "glTexCoord1xvOES"); + glad_glTexCoord2xOES = (PFNGLTEXCOORD2XOESPROC) load(userptr, "glTexCoord2xOES"); + glad_glTexCoord2xvOES = (PFNGLTEXCOORD2XVOESPROC) load(userptr, "glTexCoord2xvOES"); + glad_glTexCoord3xOES = (PFNGLTEXCOORD3XOESPROC) load(userptr, "glTexCoord3xOES"); + glad_glTexCoord3xvOES = (PFNGLTEXCOORD3XVOESPROC) load(userptr, "glTexCoord3xvOES"); + glad_glTexCoord4xOES = (PFNGLTEXCOORD4XOESPROC) load(userptr, "glTexCoord4xOES"); + glad_glTexCoord4xvOES = (PFNGLTEXCOORD4XVOESPROC) load(userptr, "glTexCoord4xvOES"); + glad_glTexEnvxOES = (PFNGLTEXENVXOESPROC) load(userptr, "glTexEnvxOES"); + glad_glTexEnvxvOES = (PFNGLTEXENVXVOESPROC) load(userptr, "glTexEnvxvOES"); + glad_glTexGenxOES = (PFNGLTEXGENXOESPROC) load(userptr, "glTexGenxOES"); + glad_glTexGenxvOES = (PFNGLTEXGENXVOESPROC) load(userptr, "glTexGenxvOES"); + glad_glTexParameterxOES = (PFNGLTEXPARAMETERXOESPROC) load(userptr, "glTexParameterxOES"); + glad_glTexParameterxvOES = (PFNGLTEXPARAMETERXVOESPROC) load(userptr, "glTexParameterxvOES"); + glad_glTranslatexOES = (PFNGLTRANSLATEXOESPROC) load(userptr, "glTranslatexOES"); + glad_glVertex2xOES = (PFNGLVERTEX2XOESPROC) load(userptr, "glVertex2xOES"); + glad_glVertex2xvOES = (PFNGLVERTEX2XVOESPROC) load(userptr, "glVertex2xvOES"); + glad_glVertex3xOES = (PFNGLVERTEX3XOESPROC) load(userptr, "glVertex3xOES"); + glad_glVertex3xvOES = (PFNGLVERTEX3XVOESPROC) load(userptr, "glVertex3xvOES"); + glad_glVertex4xOES = (PFNGLVERTEX4XOESPROC) load(userptr, "glVertex4xOES"); + glad_glVertex4xvOES = (PFNGLVERTEX4XVOESPROC) load(userptr, "glVertex4xvOES"); +} + + + +#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) +#define GLAD_GL_IS_SOME_NEW_VERSION 1 +#else +#define GLAD_GL_IS_SOME_NEW_VERSION 0 +#endif + +static int glad_gl_get_extensions( int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) { +#if GLAD_GL_IS_SOME_NEW_VERSION + if(GLAD_VERSION_MAJOR(version) < 3) { +#else + (void) version; + (void) out_num_exts_i; + (void) out_exts_i; +#endif + if (glad_glGetString == NULL) { + return 0; + } + *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS); +#if GLAD_GL_IS_SOME_NEW_VERSION + } else { + unsigned int index = 0; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (glad_glGetStringi == NULL || glad_glGetIntegerv == NULL) { + return 0; + } + glad_glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i); + if (num_exts_i > 0) { + exts_i = (char **) GLAD_MALLOC(num_exts_i * (sizeof *exts_i)); + } + if (exts_i == NULL) { + return 0; + } + for(index = 0; index < num_exts_i; index++) { + const char *gl_str_tmp = (const char*) glad_glGetStringi(GL_EXTENSIONS, index); + size_t len = strlen(gl_str_tmp) + 1; + + char *local_str = (char*) GLAD_MALLOC(len * sizeof(char)); + if(local_str != NULL) { + memcpy(local_str, gl_str_tmp, len * sizeof(char)); + } + + exts_i[index] = local_str; + } + + *out_num_exts_i = num_exts_i; + *out_exts_i = exts_i; + } +#endif + return 1; +} +static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) { + if (exts_i != NULL) { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + GLAD_FREE((void *) (exts_i[index])); + } + GLAD_FREE((void *)exts_i); + exts_i = NULL; + } +} +static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) { + if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) { + const char *extensions; + const char *loc; + const char *terminator; + extensions = exts; + if(extensions == NULL || ext == NULL) { + return 0; + } + while(1) { + loc = strstr(extensions, ext); + if(loc == NULL) { + return 0; + } + terminator = loc + strlen(ext); + if((loc == extensions || *(loc - 1) == ' ') && + (*terminator == ' ' || *terminator == '\0')) { + return 1; + } + extensions = terminator; + } + } else { + unsigned int index; + for(index = 0; index < num_exts_i; index++) { + const char *e = exts_i[index]; + if(strcmp(e, ext) == 0) { + return 1; + } + } + } + return 0; +} + +static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name) { + return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); +} + +static int glad_gl_find_extensions_gl( int version) { + const char *exts = NULL; + unsigned int num_exts_i = 0; + char **exts_i = NULL; + if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0; + + GLAD_GL_ARB_ES2_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_ES2_compatibility"); + GLAD_GL_ARB_ES3_1_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_ES3_1_compatibility"); + GLAD_GL_ARB_ES3_2_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_ES3_2_compatibility"); + GLAD_GL_ARB_ES3_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_ES3_compatibility"); + GLAD_GL_ARB_blend_func_extended = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_blend_func_extended"); + GLAD_GL_ARB_buffer_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_buffer_storage"); + GLAD_GL_ARB_clear_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_clear_buffer_object"); + GLAD_GL_ARB_clear_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_clear_texture"); + GLAD_GL_ARB_color_buffer_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_color_buffer_float"); + GLAD_GL_ARB_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_compatibility"); + GLAD_GL_ARB_compressed_texture_pixel_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_compressed_texture_pixel_storage"); + GLAD_GL_ARB_compute_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_compute_shader"); + GLAD_GL_ARB_compute_variable_group_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_compute_variable_group_size"); + GLAD_GL_ARB_copy_buffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_copy_buffer"); + GLAD_GL_ARB_copy_image = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_copy_image"); + GLAD_GL_ARB_debug_output = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_debug_output"); + GLAD_GL_ARB_depth_buffer_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_depth_buffer_float"); + GLAD_GL_ARB_depth_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_depth_clamp"); + GLAD_GL_ARB_depth_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_depth_texture"); + GLAD_GL_ARB_direct_state_access = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_direct_state_access"); + GLAD_GL_ARB_draw_buffers = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_draw_buffers"); + GLAD_GL_ARB_draw_buffers_blend = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_draw_buffers_blend"); + GLAD_GL_ARB_draw_elements_base_vertex = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_draw_elements_base_vertex"); + GLAD_GL_ARB_draw_indirect = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_draw_indirect"); + GLAD_GL_ARB_draw_instanced = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_draw_instanced"); + GLAD_GL_ARB_enhanced_layouts = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_enhanced_layouts"); + GLAD_GL_ARB_explicit_attrib_location = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_explicit_attrib_location"); + GLAD_GL_ARB_explicit_uniform_location = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_explicit_uniform_location"); + GLAD_GL_ARB_fragment_coord_conventions = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_coord_conventions"); + GLAD_GL_ARB_fragment_layer_viewport = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_layer_viewport"); + GLAD_GL_ARB_fragment_program = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_program"); + GLAD_GL_ARB_fragment_program_shadow = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_program_shadow"); + GLAD_GL_ARB_fragment_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_shader"); + GLAD_GL_ARB_fragment_shader_interlock = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_shader_interlock"); + GLAD_GL_ARB_framebuffer_no_attachments = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_framebuffer_no_attachments"); + GLAD_GL_ARB_framebuffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_framebuffer_object"); + GLAD_GL_ARB_framebuffer_sRGB = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_framebuffer_sRGB"); + GLAD_GL_ARB_geometry_shader4 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_geometry_shader4"); + GLAD_GL_ARB_get_program_binary = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_get_program_binary"); + GLAD_GL_ARB_get_texture_sub_image = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_get_texture_sub_image"); + GLAD_GL_ARB_gl_spirv = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_gl_spirv"); + GLAD_GL_ARB_gpu_shader5 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_gpu_shader5"); + GLAD_GL_ARB_gpu_shader_fp64 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_gpu_shader_fp64"); + GLAD_GL_ARB_gpu_shader_int64 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_gpu_shader_int64"); + GLAD_GL_ARB_half_float_pixel = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_half_float_pixel"); + GLAD_GL_ARB_half_float_vertex = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_half_float_vertex"); + GLAD_GL_ARB_instanced_arrays = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_instanced_arrays"); + GLAD_GL_ARB_internalformat_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_internalformat_query"); + GLAD_GL_ARB_internalformat_query2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_internalformat_query2"); + GLAD_GL_ARB_map_buffer_range = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_map_buffer_range"); + GLAD_GL_ARB_multi_bind = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_multi_bind"); + GLAD_GL_ARB_multi_draw_indirect = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_multi_draw_indirect"); + GLAD_GL_ARB_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_multisample"); + GLAD_GL_ARB_multitexture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_multitexture"); + GLAD_GL_ARB_occlusion_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_occlusion_query"); + GLAD_GL_ARB_occlusion_query2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_occlusion_query2"); + GLAD_GL_ARB_pipeline_statistics_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_pipeline_statistics_query"); + GLAD_GL_ARB_query_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_query_buffer_object"); + GLAD_GL_ARB_sample_locations = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_sample_locations"); + GLAD_GL_ARB_sample_shading = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_sample_shading"); + GLAD_GL_ARB_seamless_cube_map = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_seamless_cube_map"); + GLAD_GL_ARB_seamless_cubemap_per_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_seamless_cubemap_per_texture"); + GLAD_GL_ARB_shader_atomic_counter_ops = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_atomic_counter_ops"); + GLAD_GL_ARB_shader_atomic_counters = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_atomic_counters"); + GLAD_GL_ARB_shader_bit_encoding = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_bit_encoding"); + GLAD_GL_ARB_shader_clock = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_clock"); + GLAD_GL_ARB_shader_image_load_store = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_image_load_store"); + GLAD_GL_ARB_shader_image_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_image_size"); + GLAD_GL_ARB_shader_objects = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_objects"); + GLAD_GL_ARB_shader_storage_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_storage_buffer_object"); + GLAD_GL_ARB_shader_texture_lod = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_texture_lod"); + GLAD_GL_ARB_shading_language_100 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shading_language_100"); + GLAD_GL_ARB_shading_language_420pack = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shading_language_420pack"); + GLAD_GL_ARB_shading_language_include = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shading_language_include"); + GLAD_GL_ARB_shading_language_packing = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shading_language_packing"); + GLAD_GL_ARB_spirv_extensions = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_spirv_extensions"); + GLAD_GL_ARB_tessellation_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_tessellation_shader"); + GLAD_GL_ARB_texture_border_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_border_clamp"); + GLAD_GL_ARB_texture_buffer_object_rgb32 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_buffer_object_rgb32"); + GLAD_GL_ARB_texture_compression = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_compression"); + GLAD_GL_ARB_texture_cube_map = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_cube_map"); + GLAD_GL_ARB_texture_cube_map_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_cube_map_array"); + GLAD_GL_ARB_texture_env_add = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_env_add"); + GLAD_GL_ARB_texture_filter_anisotropic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_filter_anisotropic"); + GLAD_GL_ARB_texture_filter_minmax = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_filter_minmax"); + GLAD_GL_ARB_texture_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_float"); + GLAD_GL_ARB_texture_mirror_clamp_to_edge = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_mirror_clamp_to_edge"); + GLAD_GL_ARB_texture_mirrored_repeat = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_mirrored_repeat"); + GLAD_GL_ARB_texture_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_multisample"); + GLAD_GL_ARB_texture_non_power_of_two = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_non_power_of_two"); + GLAD_GL_ARB_texture_rg = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_rg"); + GLAD_GL_ARB_texture_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_storage"); + GLAD_GL_ARB_texture_swizzle = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_swizzle"); + GLAD_GL_ARB_texture_view = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_view"); + GLAD_GL_ARB_timer_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_timer_query"); + GLAD_GL_ARB_transpose_matrix = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_transpose_matrix"); + GLAD_GL_ARB_uniform_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_uniform_buffer_object"); + GLAD_GL_ARB_vertex_array_bgra = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_array_bgra"); + GLAD_GL_ARB_vertex_array_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_array_object"); + GLAD_GL_ARB_vertex_attrib_binding = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_attrib_binding"); + GLAD_GL_ARB_vertex_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_buffer_object"); + GLAD_GL_ARB_vertex_program = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_program"); + GLAD_GL_ARB_vertex_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_shader"); + GLAD_GL_EXT_draw_instanced = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_draw_instanced"); + GLAD_GL_EXT_fog_coord = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_fog_coord"); + GLAD_GL_EXT_framebuffer_blit = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_blit"); + GLAD_GL_EXT_framebuffer_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_multisample"); + GLAD_GL_EXT_framebuffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_object"); + GLAD_GL_EXT_framebuffer_sRGB = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_framebuffer_sRGB"); + GLAD_GL_EXT_texture_compression_s3tc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_compression_s3tc"); + GLAD_GL_EXT_texture_filter_anisotropic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_filter_anisotropic"); + GLAD_GL_EXT_texture_mirror_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_mirror_clamp"); + GLAD_GL_KHR_texture_compression_astc_hdr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_hdr"); + GLAD_GL_KHR_texture_compression_astc_ldr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_ldr"); + GLAD_GL_OES_compressed_paletted_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_compressed_paletted_texture"); + GLAD_GL_OES_fixed_point = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_OES_fixed_point"); + + glad_gl_free_extensions(exts_i, num_exts_i); + + return 1; +} + +static int glad_gl_find_core_gl(void) { + int i; + const char* version; + const char* prefixes[] = { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + "OpenGL SC ", + NULL + }; + int major = 0; + int minor = 0; + version = (const char*) glad_glGetString(GL_VERSION); + if (!version) return 0; + for (i = 0; prefixes[i]; i++) { + const size_t length = strlen(prefixes[i]); + if (strncmp(version, prefixes[i], length) == 0) { + version += length; + break; + } + } + + GLAD_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor); + + GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; + GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; + GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; + GLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1; + GLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1; + GLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1; + GLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; + GLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2; + GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3; + GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3; + GLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3; + GLAD_GL_VERSION_3_3 = (major == 3 && minor >= 3) || major > 3; + GLAD_GL_VERSION_4_0 = (major == 4 && minor >= 0) || major > 4; + GLAD_GL_VERSION_4_1 = (major == 4 && minor >= 1) || major > 4; + GLAD_GL_VERSION_4_2 = (major == 4 && minor >= 2) || major > 4; + GLAD_GL_VERSION_4_3 = (major == 4 && minor >= 3) || major > 4; + + return GLAD_MAKE_VERSION(major, minor); +} + +int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) { + int version; + + glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); + if(glad_glGetString == NULL) return 0; + if(glad_glGetString(GL_VERSION) == NULL) return 0; + version = glad_gl_find_core_gl(); + + glad_gl_load_GL_VERSION_1_0(load, userptr); + glad_gl_load_GL_VERSION_1_1(load, userptr); + glad_gl_load_GL_VERSION_1_2(load, userptr); + glad_gl_load_GL_VERSION_1_3(load, userptr); + glad_gl_load_GL_VERSION_1_4(load, userptr); + glad_gl_load_GL_VERSION_1_5(load, userptr); + glad_gl_load_GL_VERSION_2_0(load, userptr); + glad_gl_load_GL_VERSION_2_1(load, userptr); + glad_gl_load_GL_VERSION_3_0(load, userptr); + glad_gl_load_GL_VERSION_3_1(load, userptr); + glad_gl_load_GL_VERSION_3_2(load, userptr); + glad_gl_load_GL_VERSION_3_3(load, userptr); + glad_gl_load_GL_VERSION_4_0(load, userptr); + glad_gl_load_GL_VERSION_4_1(load, userptr); + glad_gl_load_GL_VERSION_4_2(load, userptr); + glad_gl_load_GL_VERSION_4_3(load, userptr); + + if (!glad_gl_find_extensions_gl(version)) return 0; + glad_gl_load_GL_ARB_ES2_compatibility(load, userptr); + glad_gl_load_GL_ARB_ES3_1_compatibility(load, userptr); + glad_gl_load_GL_ARB_ES3_2_compatibility(load, userptr); + glad_gl_load_GL_ARB_blend_func_extended(load, userptr); + glad_gl_load_GL_ARB_buffer_storage(load, userptr); + glad_gl_load_GL_ARB_clear_buffer_object(load, userptr); + glad_gl_load_GL_ARB_clear_texture(load, userptr); + glad_gl_load_GL_ARB_color_buffer_float(load, userptr); + glad_gl_load_GL_ARB_compute_shader(load, userptr); + glad_gl_load_GL_ARB_compute_variable_group_size(load, userptr); + glad_gl_load_GL_ARB_copy_buffer(load, userptr); + glad_gl_load_GL_ARB_copy_image(load, userptr); + glad_gl_load_GL_ARB_debug_output(load, userptr); + glad_gl_load_GL_ARB_direct_state_access(load, userptr); + glad_gl_load_GL_ARB_draw_buffers(load, userptr); + glad_gl_load_GL_ARB_draw_buffers_blend(load, userptr); + glad_gl_load_GL_ARB_draw_elements_base_vertex(load, userptr); + glad_gl_load_GL_ARB_draw_indirect(load, userptr); + glad_gl_load_GL_ARB_draw_instanced(load, userptr); + glad_gl_load_GL_ARB_fragment_program(load, userptr); + glad_gl_load_GL_ARB_framebuffer_no_attachments(load, userptr); + glad_gl_load_GL_ARB_framebuffer_object(load, userptr); + glad_gl_load_GL_ARB_geometry_shader4(load, userptr); + glad_gl_load_GL_ARB_get_program_binary(load, userptr); + glad_gl_load_GL_ARB_get_texture_sub_image(load, userptr); + glad_gl_load_GL_ARB_gl_spirv(load, userptr); + glad_gl_load_GL_ARB_gpu_shader_fp64(load, userptr); + glad_gl_load_GL_ARB_gpu_shader_int64(load, userptr); + glad_gl_load_GL_ARB_instanced_arrays(load, userptr); + glad_gl_load_GL_ARB_internalformat_query(load, userptr); + glad_gl_load_GL_ARB_internalformat_query2(load, userptr); + glad_gl_load_GL_ARB_map_buffer_range(load, userptr); + glad_gl_load_GL_ARB_multi_bind(load, userptr); + glad_gl_load_GL_ARB_multi_draw_indirect(load, userptr); + glad_gl_load_GL_ARB_multisample(load, userptr); + glad_gl_load_GL_ARB_multitexture(load, userptr); + glad_gl_load_GL_ARB_occlusion_query(load, userptr); + glad_gl_load_GL_ARB_sample_locations(load, userptr); + glad_gl_load_GL_ARB_sample_shading(load, userptr); + glad_gl_load_GL_ARB_shader_atomic_counters(load, userptr); + glad_gl_load_GL_ARB_shader_image_load_store(load, userptr); + glad_gl_load_GL_ARB_shader_objects(load, userptr); + glad_gl_load_GL_ARB_shader_storage_buffer_object(load, userptr); + glad_gl_load_GL_ARB_shading_language_include(load, userptr); + glad_gl_load_GL_ARB_tessellation_shader(load, userptr); + glad_gl_load_GL_ARB_texture_compression(load, userptr); + glad_gl_load_GL_ARB_texture_multisample(load, userptr); + glad_gl_load_GL_ARB_texture_storage(load, userptr); + glad_gl_load_GL_ARB_texture_view(load, userptr); + glad_gl_load_GL_ARB_timer_query(load, userptr); + glad_gl_load_GL_ARB_transpose_matrix(load, userptr); + glad_gl_load_GL_ARB_uniform_buffer_object(load, userptr); + glad_gl_load_GL_ARB_vertex_array_object(load, userptr); + glad_gl_load_GL_ARB_vertex_attrib_binding(load, userptr); + glad_gl_load_GL_ARB_vertex_buffer_object(load, userptr); + glad_gl_load_GL_ARB_vertex_program(load, userptr); + glad_gl_load_GL_ARB_vertex_shader(load, userptr); + glad_gl_load_GL_EXT_draw_instanced(load, userptr); + glad_gl_load_GL_EXT_fog_coord(load, userptr); + glad_gl_load_GL_EXT_framebuffer_blit(load, userptr); + glad_gl_load_GL_EXT_framebuffer_multisample(load, userptr); + glad_gl_load_GL_EXT_framebuffer_object(load, userptr); + glad_gl_load_GL_OES_fixed_point(load, userptr); + + + + return version; +} + + +int gladLoadGL( GLADloadfunc load) { + return gladLoadGLUserPtr( glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); +} + + + + + + +#ifdef __cplusplus +} +#endif + +#endif /* GLAD_GL_IMPLEMENTATION */ + + +``` + +`external/tinycthread/README.txt`: + +```txt +TinyCThread v1.2 +================ + +https://tinycthread.github.io + + +About +----- + +TinyCThread is a minimalist, portable, threading library for C, intended to +make it easy to create multi threaded C applications. + +The library is closesly modeled after the C11 standard, but only a subset is +implemented at the moment. + +See the documentation in the doc/html directory for more information. + + +Using TinyCThread +----------------- + +To use TinyCThread in your own project, just add tinycthread.c and +tinycthread.h to your project. In your own code, do: + +#include + +TinyCThread also includes CMake support, so if your project uses CMake +you can just `add_subdirectory(tinycthread)`. Then simply add the +tinycthread target (using `target_link_libraries`) where necessary and +CMake will take care of everything else, including adding the correct +include directory and CTest integration. + + +Building the test programs +-------------------------- + +From the test folder, issue one of the following commands: + +Linux, Mac OS X, OpenSolaris etc: + make (you may need to use gmake on some systems) + +Windows/MinGW: + mingw32-make + +Windows/MS Visual Studio: + nmake /f Makefile.msvc + + +History +------- + +v1.2 - Unreleased + - Updated API to better match the final specification (e.g. removed mtx_try) + - Improved Windows support, including TSS destructors + - Added once support + - Improved unit testing + - Added CMake support + - Assorted bug fixes + +v1.1 - 2012.9.8 + - First release. + - Updated API to better match the final specification (e.g. removed xtime). + - Some functionality still missing (mtx_timedlock, TSS destructors under + Windows, ...). + +v1.0 - Never released + - Development version based on C11 specification draft. + + + +License +------- + +Copyright (c) 2012 Marcus Geelnard + 2013-2016 Evan Nemerson + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + +``` + +`external/tinycthread/tinycthread.c`: + +```c +/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- +Copyright (c) 2012 Marcus Geelnard +Copyright (c) 2013-2016 Evan Nemerson + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#include "tinycthread.h" +#include + +/* Platform specific includes */ +#if defined(_TTHREAD_POSIX_) + #include + #include + #include + #include + #include +#elif defined(_TTHREAD_WIN32_) + #include + #include +#endif + +/* Standard, good-to-have defines */ +#ifndef NULL + #define NULL (void*)0 +#endif +#ifndef TRUE + #define TRUE 1 +#endif +#ifndef FALSE + #define FALSE 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +int mtx_init(mtx_t *mtx, int type) +{ +#if defined(_TTHREAD_WIN32_) + mtx->mAlreadyLocked = FALSE; + mtx->mRecursive = type & mtx_recursive; + mtx->mTimed = type & mtx_timed; + if (!mtx->mTimed) + { + InitializeCriticalSection(&(mtx->mHandle.cs)); + } + else + { + mtx->mHandle.mut = CreateMutex(NULL, FALSE, NULL); + if (mtx->mHandle.mut == NULL) + { + return thrd_error; + } + } + return thrd_success; +#else + int ret; + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + if (type & mtx_recursive) + { + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + } + ret = pthread_mutex_init(mtx, &attr); + pthread_mutexattr_destroy(&attr); + return ret == 0 ? thrd_success : thrd_error; +#endif +} + +void mtx_destroy(mtx_t *mtx) +{ +#if defined(_TTHREAD_WIN32_) + if (!mtx->mTimed) + { + DeleteCriticalSection(&(mtx->mHandle.cs)); + } + else + { + CloseHandle(mtx->mHandle.mut); + } +#else + pthread_mutex_destroy(mtx); +#endif +} + +int mtx_lock(mtx_t *mtx) +{ +#if defined(_TTHREAD_WIN32_) + if (!mtx->mTimed) + { + EnterCriticalSection(&(mtx->mHandle.cs)); + } + else + { + switch (WaitForSingleObject(mtx->mHandle.mut, INFINITE)) + { + case WAIT_OBJECT_0: + break; + case WAIT_ABANDONED: + default: + return thrd_error; + } + } + + if (!mtx->mRecursive) + { + while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */ + mtx->mAlreadyLocked = TRUE; + } + return thrd_success; +#else + return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error; +#endif +} + +int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) +{ +#if defined(_TTHREAD_WIN32_) + struct timespec current_ts; + DWORD timeoutMs; + + if (!mtx->mTimed) + { + return thrd_error; + } + + timespec_get(¤t_ts, TIME_UTC); + + if ((current_ts.tv_sec > ts->tv_sec) || ((current_ts.tv_sec == ts->tv_sec) && (current_ts.tv_nsec >= ts->tv_nsec))) + { + timeoutMs = 0; + } + else + { + timeoutMs = (DWORD)(ts->tv_sec - current_ts.tv_sec) * 1000; + timeoutMs += (ts->tv_nsec - current_ts.tv_nsec) / 1000000; + timeoutMs += 1; + } + + /* TODO: the timeout for WaitForSingleObject doesn't include time + while the computer is asleep. */ + switch (WaitForSingleObject(mtx->mHandle.mut, timeoutMs)) + { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + return thrd_timedout; + case WAIT_ABANDONED: + default: + return thrd_error; + } + + if (!mtx->mRecursive) + { + while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */ + mtx->mAlreadyLocked = TRUE; + } + + return thrd_success; +#elif defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS >= 200112L) && defined(_POSIX_THREADS) && (_POSIX_THREADS >= 200112L) + switch (pthread_mutex_timedlock(mtx, ts)) { + case 0: + return thrd_success; + case ETIMEDOUT: + return thrd_timedout; + default: + return thrd_error; + } +#else + int rc; + struct timespec cur, dur; + + /* Try to acquire the lock and, if we fail, sleep for 5ms. */ + while ((rc = pthread_mutex_trylock (mtx)) == EBUSY) { + timespec_get(&cur, TIME_UTC); + + if ((cur.tv_sec > ts->tv_sec) || ((cur.tv_sec == ts->tv_sec) && (cur.tv_nsec >= ts->tv_nsec))) + { + break; + } + + dur.tv_sec = ts->tv_sec - cur.tv_sec; + dur.tv_nsec = ts->tv_nsec - cur.tv_nsec; + if (dur.tv_nsec < 0) + { + dur.tv_sec--; + dur.tv_nsec += 1000000000; + } + + if ((dur.tv_sec != 0) || (dur.tv_nsec > 5000000)) + { + dur.tv_sec = 0; + dur.tv_nsec = 5000000; + } + + nanosleep(&dur, NULL); + } + + switch (rc) { + case 0: + return thrd_success; + case ETIMEDOUT: + case EBUSY: + return thrd_timedout; + default: + return thrd_error; + } +#endif +} + +int mtx_trylock(mtx_t *mtx) +{ +#if defined(_TTHREAD_WIN32_) + int ret; + + if (!mtx->mTimed) + { + ret = TryEnterCriticalSection(&(mtx->mHandle.cs)) ? thrd_success : thrd_busy; + } + else + { + ret = (WaitForSingleObject(mtx->mHandle.mut, 0) == WAIT_OBJECT_0) ? thrd_success : thrd_busy; + } + + if ((!mtx->mRecursive) && (ret == thrd_success)) + { + if (mtx->mAlreadyLocked) + { + LeaveCriticalSection(&(mtx->mHandle.cs)); + ret = thrd_busy; + } + else + { + mtx->mAlreadyLocked = TRUE; + } + } + return ret; +#else + return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; +#endif +} + +int mtx_unlock(mtx_t *mtx) +{ +#if defined(_TTHREAD_WIN32_) + mtx->mAlreadyLocked = FALSE; + if (!mtx->mTimed) + { + LeaveCriticalSection(&(mtx->mHandle.cs)); + } + else + { + if (!ReleaseMutex(mtx->mHandle.mut)) + { + return thrd_error; + } + } + return thrd_success; +#else + return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;; +#endif +} + +#if defined(_TTHREAD_WIN32_) +#define _CONDITION_EVENT_ONE 0 +#define _CONDITION_EVENT_ALL 1 +#endif + +int cnd_init(cnd_t *cond) +{ +#if defined(_TTHREAD_WIN32_) + cond->mWaitersCount = 0; + + /* Init critical section */ + InitializeCriticalSection(&cond->mWaitersCountLock); + + /* Init events */ + cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); + if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL) + { + cond->mEvents[_CONDITION_EVENT_ALL] = NULL; + return thrd_error; + } + cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL) + { + CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]); + cond->mEvents[_CONDITION_EVENT_ONE] = NULL; + return thrd_error; + } + + return thrd_success; +#else + return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error; +#endif +} + +void cnd_destroy(cnd_t *cond) +{ +#if defined(_TTHREAD_WIN32_) + if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL) + { + CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]); + } + if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL) + { + CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]); + } + DeleteCriticalSection(&cond->mWaitersCountLock); +#else + pthread_cond_destroy(cond); +#endif +} + +int cnd_signal(cnd_t *cond) +{ +#if defined(_TTHREAD_WIN32_) + int haveWaiters; + + /* Are there any waiters? */ + EnterCriticalSection(&cond->mWaitersCountLock); + haveWaiters = (cond->mWaitersCount > 0); + LeaveCriticalSection(&cond->mWaitersCountLock); + + /* If we have any waiting threads, send them a signal */ + if(haveWaiters) + { + if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0) + { + return thrd_error; + } + } + + return thrd_success; +#else + return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error; +#endif +} + +int cnd_broadcast(cnd_t *cond) +{ +#if defined(_TTHREAD_WIN32_) + int haveWaiters; + + /* Are there any waiters? */ + EnterCriticalSection(&cond->mWaitersCountLock); + haveWaiters = (cond->mWaitersCount > 0); + LeaveCriticalSection(&cond->mWaitersCountLock); + + /* If we have any waiting threads, send them a signal */ + if(haveWaiters) + { + if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0) + { + return thrd_error; + } + } + + return thrd_success; +#else + return pthread_cond_broadcast(cond) == 0 ? thrd_success : thrd_error; +#endif +} + +#if defined(_TTHREAD_WIN32_) +static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout) +{ + DWORD result; + int lastWaiter; + + /* Increment number of waiters */ + EnterCriticalSection(&cond->mWaitersCountLock); + ++ cond->mWaitersCount; + LeaveCriticalSection(&cond->mWaitersCountLock); + + /* Release the mutex while waiting for the condition (will decrease + the number of waiters when done)... */ + mtx_unlock(mtx); + + /* Wait for either event to become signaled due to cnd_signal() or + cnd_broadcast() being called */ + result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout); + if (result == WAIT_TIMEOUT) + { + /* The mutex is locked again before the function returns, even if an error occurred */ + mtx_lock(mtx); + return thrd_timedout; + } + else if (result == WAIT_FAILED) + { + /* The mutex is locked again before the function returns, even if an error occurred */ + mtx_lock(mtx); + return thrd_error; + } + + /* Check if we are the last waiter */ + EnterCriticalSection(&cond->mWaitersCountLock); + -- cond->mWaitersCount; + lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && + (cond->mWaitersCount == 0); + LeaveCriticalSection(&cond->mWaitersCountLock); + + /* If we are the last waiter to be notified to stop waiting, reset the event */ + if (lastWaiter) + { + if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0) + { + /* The mutex is locked again before the function returns, even if an error occurred */ + mtx_lock(mtx); + return thrd_error; + } + } + + /* Re-acquire the mutex */ + mtx_lock(mtx); + + return thrd_success; +} +#endif + +int cnd_wait(cnd_t *cond, mtx_t *mtx) +{ +#if defined(_TTHREAD_WIN32_) + return _cnd_timedwait_win32(cond, mtx, INFINITE); +#else + return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error; +#endif +} + +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) +{ +#if defined(_TTHREAD_WIN32_) + struct timespec now; + if (timespec_get(&now, TIME_UTC) == TIME_UTC) + { + unsigned long long nowInMilliseconds = now.tv_sec * 1000 + now.tv_nsec / 1000000; + unsigned long long tsInMilliseconds = ts->tv_sec * 1000 + ts->tv_nsec / 1000000; + DWORD delta = (tsInMilliseconds > nowInMilliseconds) ? + (DWORD)(tsInMilliseconds - nowInMilliseconds) : 0; + return _cnd_timedwait_win32(cond, mtx, delta); + } + else + return thrd_error; +#else + int ret; + ret = pthread_cond_timedwait(cond, mtx, ts); + if (ret == ETIMEDOUT) + { + return thrd_timedout; + } + return ret == 0 ? thrd_success : thrd_error; +#endif +} + +#if defined(_TTHREAD_WIN32_) +struct TinyCThreadTSSData { + void* value; + tss_t key; + struct TinyCThreadTSSData* next; +}; + +static tss_dtor_t _tinycthread_tss_dtors[1088] = { NULL, }; + +static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_head = NULL; +static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_tail = NULL; + +static void _tinycthread_tss_cleanup (void); + +static void _tinycthread_tss_cleanup (void) { + struct TinyCThreadTSSData* data; + int iteration; + unsigned int again = 1; + void* value; + + for (iteration = 0 ; iteration < TSS_DTOR_ITERATIONS && again > 0 ; iteration++) + { + again = 0; + for (data = _tinycthread_tss_head ; data != NULL ; data = data->next) + { + if (data->value != NULL) + { + value = data->value; + data->value = NULL; + + if (_tinycthread_tss_dtors[data->key] != NULL) + { + again = 1; + _tinycthread_tss_dtors[data->key](value); + } + } + } + } + + while (_tinycthread_tss_head != NULL) { + data = _tinycthread_tss_head->next; + free (_tinycthread_tss_head); + _tinycthread_tss_head = data; + } + _tinycthread_tss_head = NULL; + _tinycthread_tss_tail = NULL; +} + +static void NTAPI _tinycthread_tss_callback(PVOID h, DWORD dwReason, PVOID pv) +{ + (void)h; + (void)pv; + + if (_tinycthread_tss_head != NULL && (dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH)) + { + _tinycthread_tss_cleanup(); + } +} + +#if defined(_MSC_VER) + #ifdef _M_X64 + #pragma const_seg(".CRT$XLB") + #else + #pragma data_seg(".CRT$XLB") + #endif + PIMAGE_TLS_CALLBACK p_thread_callback = _tinycthread_tss_callback; + #ifdef _M_X64 + #pragma data_seg() + #else + #pragma const_seg() + #endif +#else + PIMAGE_TLS_CALLBACK p_thread_callback __attribute__((section(".CRT$XLB"))) = _tinycthread_tss_callback; +#endif + +#endif /* defined(_TTHREAD_WIN32_) */ + +/** Information to pass to the new thread (what to run). */ +typedef struct { + thrd_start_t mFunction; /**< Pointer to the function to be executed. */ + void * mArg; /**< Function argument for the thread function. */ +} _thread_start_info; + +/* Thread wrapper function. */ +#if defined(_TTHREAD_WIN32_) +static DWORD WINAPI _thrd_wrapper_function(LPVOID aArg) +#elif defined(_TTHREAD_POSIX_) +static void * _thrd_wrapper_function(void * aArg) +#endif +{ + thrd_start_t fun; + void *arg; + int res; + + /* Get thread startup information */ + _thread_start_info *ti = (_thread_start_info *) aArg; + fun = ti->mFunction; + arg = ti->mArg; + + /* The thread is responsible for freeing the startup information */ + free((void *)ti); + + /* Call the actual client thread function */ + res = fun(arg); + +#if defined(_TTHREAD_WIN32_) + if (_tinycthread_tss_head != NULL) + { + _tinycthread_tss_cleanup(); + } + + return (DWORD)res; +#else + return (void*)(intptr_t)res; +#endif +} + +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + /* Fill out the thread startup information (passed to the thread wrapper, + which will eventually free it) */ + _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info)); + if (ti == NULL) + { + return thrd_nomem; + } + ti->mFunction = func; + ti->mArg = arg; + + /* Create the thread */ +#if defined(_TTHREAD_WIN32_) + *thr = CreateThread(NULL, 0, _thrd_wrapper_function, (LPVOID) ti, 0, NULL); +#elif defined(_TTHREAD_POSIX_) + if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0) + { + *thr = 0; + } +#endif + + /* Did we fail to create the thread? */ + if(!*thr) + { + free(ti); + return thrd_error; + } + + return thrd_success; +} + +thrd_t thrd_current(void) +{ +#if defined(_TTHREAD_WIN32_) + return GetCurrentThread(); +#else + return pthread_self(); +#endif +} + +int thrd_detach(thrd_t thr) +{ +#if defined(_TTHREAD_WIN32_) + /* https://stackoverflow.com/questions/12744324/how-to-detach-a-thread-on-windows-c#answer-12746081 */ + return CloseHandle(thr) != 0 ? thrd_success : thrd_error; +#else + return pthread_detach(thr) == 0 ? thrd_success : thrd_error; +#endif +} + +int thrd_equal(thrd_t thr0, thrd_t thr1) +{ +#if defined(_TTHREAD_WIN32_) + return GetThreadId(thr0) == GetThreadId(thr1); +#else + return pthread_equal(thr0, thr1); +#endif +} + +void thrd_exit(int res) +{ +#if defined(_TTHREAD_WIN32_) + if (_tinycthread_tss_head != NULL) + { + _tinycthread_tss_cleanup(); + } + + ExitThread((DWORD)res); +#else + pthread_exit((void*)(intptr_t)res); +#endif +} + +int thrd_join(thrd_t thr, int *res) +{ +#if defined(_TTHREAD_WIN32_) + DWORD dwRes; + + if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED) + { + return thrd_error; + } + if (res != NULL) + { + if (GetExitCodeThread(thr, &dwRes) != 0) + { + *res = (int) dwRes; + } + else + { + return thrd_error; + } + } + CloseHandle(thr); +#elif defined(_TTHREAD_POSIX_) + void *pres; + if (pthread_join(thr, &pres) != 0) + { + return thrd_error; + } + if (res != NULL) + { + *res = (int)(intptr_t)pres; + } +#endif + return thrd_success; +} + +int thrd_sleep(const struct timespec *duration, struct timespec *remaining) +{ +#if !defined(_TTHREAD_WIN32_) + int res = nanosleep(duration, remaining); + if (res == 0) { + return 0; + } else if (errno == EINTR) { + return -1; + } else { + return -2; + } +#else + struct timespec start; + DWORD t; + + timespec_get(&start, TIME_UTC); + + t = SleepEx((DWORD)(duration->tv_sec * 1000 + + duration->tv_nsec / 1000000 + + (((duration->tv_nsec % 1000000) == 0) ? 0 : 1)), + TRUE); + + if (t == 0) { + return 0; + } else { + if (remaining != NULL) { + timespec_get(remaining, TIME_UTC); + remaining->tv_sec -= start.tv_sec; + remaining->tv_nsec -= start.tv_nsec; + if (remaining->tv_nsec < 0) + { + remaining->tv_nsec += 1000000000; + remaining->tv_sec -= 1; + } + } + + return (t == WAIT_IO_COMPLETION) ? -1 : -2; + } +#endif +} + +void thrd_yield(void) +{ +#if defined(_TTHREAD_WIN32_) + Sleep(0); +#else + sched_yield(); +#endif +} + +int tss_create(tss_t *key, tss_dtor_t dtor) +{ +#if defined(_TTHREAD_WIN32_) + *key = TlsAlloc(); + if (*key == TLS_OUT_OF_INDEXES) + { + return thrd_error; + } + _tinycthread_tss_dtors[*key] = dtor; +#else + if (pthread_key_create(key, dtor) != 0) + { + return thrd_error; + } +#endif + return thrd_success; +} + +void tss_delete(tss_t key) +{ +#if defined(_TTHREAD_WIN32_) + struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*) TlsGetValue (key); + struct TinyCThreadTSSData* prev = NULL; + if (data != NULL) + { + if (data == _tinycthread_tss_head) + { + _tinycthread_tss_head = data->next; + } + else + { + prev = _tinycthread_tss_head; + if (prev != NULL) + { + while (prev->next != data) + { + prev = prev->next; + } + } + } + + if (data == _tinycthread_tss_tail) + { + _tinycthread_tss_tail = prev; + } + + free (data); + } + _tinycthread_tss_dtors[key] = NULL; + TlsFree(key); +#else + pthread_key_delete(key); +#endif +} + +void *tss_get(tss_t key) +{ +#if defined(_TTHREAD_WIN32_) + struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key); + if (data == NULL) + { + return NULL; + } + return data->value; +#else + return pthread_getspecific(key); +#endif +} + +int tss_set(tss_t key, void *val) +{ +#if defined(_TTHREAD_WIN32_) + struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key); + if (data == NULL) + { + data = (struct TinyCThreadTSSData*)malloc(sizeof(struct TinyCThreadTSSData)); + if (data == NULL) + { + return thrd_error; + } + + data->value = NULL; + data->key = key; + data->next = NULL; + + if (_tinycthread_tss_tail != NULL) + { + _tinycthread_tss_tail->next = data; + } + else + { + _tinycthread_tss_tail = data; + } + + if (_tinycthread_tss_head == NULL) + { + _tinycthread_tss_head = data; + } + + if (!TlsSetValue(key, data)) + { + free (data); + return thrd_error; + } + } + data->value = val; +#else + if (pthread_setspecific(key, val) != 0) + { + return thrd_error; + } +#endif + return thrd_success; +} + +#if defined(_TTHREAD_EMULATE_TIMESPEC_GET_) +int _tthread_timespec_get(struct timespec *ts, int base) +{ +#if defined(_TTHREAD_WIN32_) + struct _timeb tb; +#elif !defined(CLOCK_REALTIME) + struct timeval tv; +#endif + + if (base != TIME_UTC) + { + return 0; + } + +#if defined(_TTHREAD_WIN32_) + _ftime_s(&tb); + ts->tv_sec = (time_t)tb.time; + ts->tv_nsec = 1000000L * (long)tb.millitm; +#elif defined(CLOCK_REALTIME) + base = (clock_gettime(CLOCK_REALTIME, ts) == 0) ? base : 0; +#else + gettimeofday(&tv, NULL); + ts->tv_sec = (time_t)tv.tv_sec; + ts->tv_nsec = 1000L * (long)tv.tv_usec; +#endif + + return base; +} +#endif /* _TTHREAD_EMULATE_TIMESPEC_GET_ */ + +#if defined(_TTHREAD_WIN32_) +void call_once(once_flag *flag, void (*func)(void)) +{ + /* The idea here is that we use a spin lock (via the + InterlockedCompareExchange function) to restrict access to the + critical section until we have initialized it, then we use the + critical section to block until the callback has completed + execution. */ + while (flag->status < 3) + { + switch (flag->status) + { + case 0: + if (InterlockedCompareExchange (&(flag->status), 1, 0) == 0) { + InitializeCriticalSection(&(flag->lock)); + EnterCriticalSection(&(flag->lock)); + flag->status = 2; + func(); + flag->status = 3; + LeaveCriticalSection(&(flag->lock)); + return; + } + break; + case 1: + break; + case 2: + EnterCriticalSection(&(flag->lock)); + LeaveCriticalSection(&(flag->lock)); + break; + } + } +} +#endif /* defined(_TTHREAD_WIN32_) */ + +#ifdef __cplusplus +} +#endif + +``` + +`external/tinycthread/tinycthread.h`: + +```h +/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- +Copyright (c) 2012 Marcus Geelnard +Copyright (c) 2013-2016 Evan Nemerson + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef _TINYCTHREAD_H_ +#define _TINYCTHREAD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @file +* @mainpage TinyCThread API Reference +* +* @section intro_sec Introduction +* TinyCThread is a minimal, portable implementation of basic threading +* classes for C. +* +* They closely mimic the functionality and naming of the C11 standard, and +* should be easily replaceable with the corresponding standard variants. +* +* @section port_sec Portability +* The Win32 variant uses the native Win32 API for implementing the thread +* classes, while for other systems, the POSIX threads API (pthread) is used. +* +* @section misc_sec Miscellaneous +* The following special keywords are available: #_Thread_local. +* +* For more detailed information, browse the different sections of this +* documentation. A good place to start is: +* tinycthread.h. +*/ + +/* Which platform are we on? */ +#if !defined(_TTHREAD_PLATFORM_DEFINED_) + #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) + #define _TTHREAD_WIN32_ + #else + #define _TTHREAD_POSIX_ + #endif + #define _TTHREAD_PLATFORM_DEFINED_ +#endif + +/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */ +#if defined(_TTHREAD_POSIX_) + #undef _FEATURES_H + #if !defined(_GNU_SOURCE) + #define _GNU_SOURCE + #endif + #if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L) + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L + #endif + #if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500) + #undef _XOPEN_SOURCE + #define _XOPEN_SOURCE 500 + #endif + #define _XPG6 +#endif + +/* Generic includes */ +#include + +/* Platform specific includes */ +#if defined(_TTHREAD_POSIX_) + #include +#elif defined(_TTHREAD_WIN32_) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #define __UNDEF_LEAN_AND_MEAN + #endif + #include + #ifdef __UNDEF_LEAN_AND_MEAN + #undef WIN32_LEAN_AND_MEAN + #undef __UNDEF_LEAN_AND_MEAN + #endif +#endif + +/* Compiler-specific information */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define TTHREAD_NORETURN _Noreturn +#elif defined(__GNUC__) + #define TTHREAD_NORETURN __attribute__((__noreturn__)) +#else + #define TTHREAD_NORETURN +#endif + +/* If TIME_UTC is missing, provide it and provide a wrapper for + timespec_get. */ +#ifndef TIME_UTC +#define TIME_UTC 1 +#define _TTHREAD_EMULATE_TIMESPEC_GET_ + +#if defined(_TTHREAD_WIN32_) +struct _tthread_timespec { + time_t tv_sec; + long tv_nsec; +}; +#define timespec _tthread_timespec +#endif + +int _tthread_timespec_get(struct timespec *ts, int base); +#define timespec_get _tthread_timespec_get +#endif + +/** TinyCThread version (major number). */ +#define TINYCTHREAD_VERSION_MAJOR 1 +/** TinyCThread version (minor number). */ +#define TINYCTHREAD_VERSION_MINOR 2 +/** TinyCThread version (full version). */ +#define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR) + +/** +* @def _Thread_local +* Thread local storage keyword. +* A variable that is declared with the @c _Thread_local keyword makes the +* value of the variable local to each thread (known as thread-local storage, +* or TLS). Example usage: +* @code +* // This variable is local to each thread. +* _Thread_local int variable; +* @endcode +* @note The @c _Thread_local keyword is a macro that maps to the corresponding +* compiler directive (e.g. @c __declspec(thread)). +* @note This directive is currently not supported on Mac OS X (it will give +* a compiler error), since compile-time TLS is not supported in the Mac OS X +* executable format. Also, some older versions of MinGW (before GCC 4.x) do +* not support this directive, nor does the Tiny C Compiler. +* @hideinitializer +*/ + +#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local) + #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) + #define _Thread_local __thread + #else + #define _Thread_local __declspec(thread) + #endif +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && (((__GNUC__ << 8) | __GNUC_MINOR__) < ((4 << 8) | 9)) + #define _Thread_local __thread +#endif + +/* Macros */ +#if defined(_TTHREAD_WIN32_) +#define TSS_DTOR_ITERATIONS (4) +#else +#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS +#endif + +/* Function return values */ +#define thrd_error 0 /**< The requested operation failed */ +#define thrd_success 1 /**< The requested operation succeeded */ +#define thrd_timedout 2 /**< The time specified in the call was reached without acquiring the requested resource */ +#define thrd_busy 3 /**< The requested operation failed because a tesource requested by a test and return function is already in use */ +#define thrd_nomem 4 /**< The requested operation failed because it was unable to allocate memory */ + +/* Mutex types */ +#define mtx_plain 0 +#define mtx_timed 1 +#define mtx_recursive 2 + +/* Mutex */ +#if defined(_TTHREAD_WIN32_) +typedef struct { + union { + CRITICAL_SECTION cs; /* Critical section handle (used for non-timed mutexes) */ + HANDLE mut; /* Mutex handle (used for timed mutex) */ + } mHandle; /* Mutex handle */ + int mAlreadyLocked; /* TRUE if the mutex is already locked */ + int mRecursive; /* TRUE if the mutex is recursive */ + int mTimed; /* TRUE if the mutex is timed */ +} mtx_t; +#else +typedef pthread_mutex_t mtx_t; +#endif + +/** Create a mutex object. +* @param mtx A mutex object. +* @param type Bit-mask that must have one of the following six values: +* @li @c mtx_plain for a simple non-recursive mutex +* @li @c mtx_timed for a non-recursive mutex that supports timeout +* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive) +* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive) +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int mtx_init(mtx_t *mtx, int type); + +/** Release any resources used by the given mutex. +* @param mtx A mutex object. +*/ +void mtx_destroy(mtx_t *mtx); + +/** Lock the given mutex. +* Blocks until the given mutex can be locked. If the mutex is non-recursive, and +* the calling thread already has a lock on the mutex, this call will block +* forever. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int mtx_lock(mtx_t *mtx); + +/** Lock the given mutex, or block until a specific point in time. +* Blocks until either the given mutex can be locked, or the specified TIME_UTC +* based time. +* @param mtx A mutex object. +* @param ts A UTC based calendar time +* @return @ref The mtx_timedlock function returns thrd_success on success, or +* thrd_timedout if the time specified was reached without acquiring the +* requested resource, or thrd_error if the request could not be honored. +*/ +int mtx_timedlock(mtx_t *mtx, const struct timespec *ts); + +/** Try to lock the given mutex. +* The specified mutex shall support either test and return or timeout. If the +* mutex is already locked, the function returns without blocking. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_busy if the resource +* requested is already in use, or @ref thrd_error if the request could not be +* honored. +*/ +int mtx_trylock(mtx_t *mtx); + +/** Unlock the given mutex. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int mtx_unlock(mtx_t *mtx); + +/* Condition variable */ +#if defined(_TTHREAD_WIN32_) +typedef struct { + HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */ + unsigned int mWaitersCount; /* Count of the number of waiters. */ + CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */ +} cnd_t; +#else +typedef pthread_cond_t cnd_t; +#endif + +/** Create a condition variable object. +* @param cond A condition variable object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_init(cnd_t *cond); + +/** Release any resources used by the given condition variable. +* @param cond A condition variable object. +*/ +void cnd_destroy(cnd_t *cond); + +/** Signal a condition variable. +* Unblocks one of the threads that are blocked on the given condition variable +* at the time of the call. If no threads are blocked on the condition variable +* at the time of the call, the function does nothing and return success. +* @param cond A condition variable object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_signal(cnd_t *cond); + +/** Broadcast a condition variable. +* Unblocks all of the threads that are blocked on the given condition variable +* at the time of the call. If no threads are blocked on the condition variable +* at the time of the call, the function does nothing and return success. +* @param cond A condition variable object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_broadcast(cnd_t *cond); + +/** Wait for a condition variable to become signaled. +* The function atomically unlocks the given mutex and endeavors to block until +* the given condition variable is signaled by a call to cnd_signal or to +* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex +* before it returns. +* @param cond A condition variable object. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_wait(cnd_t *cond, mtx_t *mtx); + +/** Wait for a condition variable to become signaled. +* The function atomically unlocks the given mutex and endeavors to block until +* the given condition variable is signaled by a call to cnd_signal or to +* cnd_broadcast, or until after the specified time. When the calling thread +* becomes unblocked it locks the mutex before it returns. +* @param cond A condition variable object. +* @param mtx A mutex object. +* @param xt A point in time at which the request will time out (absolute time). +* @return @ref thrd_success upon success, or @ref thrd_timeout if the time +* specified in the call was reached without acquiring the requested resource, or +* @ref thrd_error if the request could not be honored. +*/ +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + +/* Thread */ +#if defined(_TTHREAD_WIN32_) +typedef HANDLE thrd_t; +#else +typedef pthread_t thrd_t; +#endif + +/** Thread start function. +* Any thread that is started with the @ref thrd_create() function must be +* started through a function of this type. +* @param arg The thread argument (the @c arg argument of the corresponding +* @ref thrd_create() call). +* @return The thread return value, which can be obtained by another thread +* by using the @ref thrd_join() function. +*/ +typedef int (*thrd_start_t)(void *arg); + +/** Create a new thread. +* @param thr Identifier of the newly created thread. +* @param func A function pointer to the function that will be executed in +* the new thread. +* @param arg An argument to the thread function. +* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could +* be allocated for the thread requested, or @ref thrd_error if the request +* could not be honored. +* @note A thread’s identifier may be reused for a different thread once the +* original thread has exited and either been detached or joined to another +* thread. +*/ +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + +/** Identify the calling thread. +* @return The identifier of the calling thread. +*/ +thrd_t thrd_current(void); + +/** Dispose of any resources allocated to the thread when that thread exits. + * @return thrd_success, or thrd_error on error +*/ +int thrd_detach(thrd_t thr); + +/** Compare two thread identifiers. +* The function determines if two thread identifiers refer to the same thread. +* @return Zero if the two thread identifiers refer to different threads. +* Otherwise a nonzero value is returned. +*/ +int thrd_equal(thrd_t thr0, thrd_t thr1); + +/** Terminate execution of the calling thread. +* @param res Result code of the calling thread. +*/ +TTHREAD_NORETURN void thrd_exit(int res); + +/** Wait for a thread to terminate. +* The function joins the given thread with the current thread by blocking +* until the other thread has terminated. +* @param thr The thread to join with. +* @param res If this pointer is not NULL, the function will store the result +* code of the given thread in the integer pointed to by @c res. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int thrd_join(thrd_t thr, int *res); + +/** Put the calling thread to sleep. +* Suspend execution of the calling thread. +* @param duration Interval to sleep for +* @param remaining If non-NULL, this parameter will hold the remaining +* time until time_point upon return. This will +* typically be zero, but if the thread was woken up +* by a signal that is not ignored before duration was +* reached @c remaining will hold a positive time. +* @return 0 (zero) on successful sleep, -1 if an interrupt occurred, +* or a negative value if the operation fails. +*/ +int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + +/** Yield execution to another thread. +* Permit other threads to run, even if the current thread would ordinarily +* continue to run. +*/ +void thrd_yield(void); + +/* Thread local storage */ +#if defined(_TTHREAD_WIN32_) +typedef DWORD tss_t; +#else +typedef pthread_key_t tss_t; +#endif + +/** Destructor function for a thread-specific storage. +* @param val The value of the destructed thread-specific storage. +*/ +typedef void (*tss_dtor_t)(void *val); + +/** Create a thread-specific storage. +* @param key The unique key identifier that will be set if the function is +* successful. +* @param dtor Destructor function. This can be NULL. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +* @note On Windows, the @c dtor will definitely be called when +* appropriate for threads created with @ref thrd_create. It will be +* called for other threads in most cases, the possible exception being +* for DLLs loaded with LoadLibraryEx. In order to be certain, you +* should use @ref thrd_create whenever possible. +*/ +int tss_create(tss_t *key, tss_dtor_t dtor); + +/** Delete a thread-specific storage. +* The function releases any resources used by the given thread-specific +* storage. +* @param key The key that shall be deleted. +*/ +void tss_delete(tss_t key); + +/** Get the value for a thread-specific storage. +* @param key The thread-specific storage identifier. +* @return The value for the current thread held in the given thread-specific +* storage. +*/ +void *tss_get(tss_t key); + +/** Set the value for a thread-specific storage. +* @param key The thread-specific storage identifier. +* @param val The value of the thread-specific storage to set for the current +* thread. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int tss_set(tss_t key, void *val); + +#if defined(_TTHREAD_WIN32_) + typedef struct { + LONG volatile status; + CRITICAL_SECTION lock; + } once_flag; + #define ONCE_FLAG_INIT {0,} +#else + #define once_flag pthread_once_t + #define ONCE_FLAG_INIT PTHREAD_ONCE_INIT +#endif + +/** Invoke a callback exactly once + * @param flag Flag used to ensure the callback is invoked exactly + * once. + * @param func Callback to invoke. + */ +#if defined(_TTHREAD_WIN32_) + void call_once(once_flag *flag, void (*func)(void)); +#else + #define call_once(flag,func) pthread_once(flag,func) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _TINYTHREAD_H_ */ + +``` + +`external/uthash/LICENSE`: + +``` +Copyright (c) 2005-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +``` + +`external/uthash/utarray.h`: + +```h +/* +Copyright (c) 2008-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* a dynamic array implementation using macros + */ +#ifndef UTARRAY_H +#define UTARRAY_H + +#define UTARRAY_VERSION 2.3.0 + +#include /* size_t */ +#include /* memset, etc */ +#include /* exit */ + +#ifdef __GNUC__ +#define UTARRAY_UNUSED __attribute__((__unused__)) +#else +#define UTARRAY_UNUSED +#endif + +#ifndef utarray_oom +#define utarray_oom() exit(-1) +#endif + +typedef void (ctor_f)(void *dst, const void *src); +typedef void (dtor_f)(void *elt); +typedef void (init_f)(void *elt); +typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; +} UT_icd; + +typedef struct { + unsigned i,n;/* i: index of next available slot, n: num slots */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz*/ +} UT_array; + +#define utarray_init(a,_icd) do { \ + memset(a,0,sizeof(UT_array)); \ + (a)->icd = *(_icd); \ +} while(0) + +#define utarray_done(a) do { \ + if ((a)->n) { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + free((a)->d); \ + } \ + (a)->n=0; \ +} while(0) + +#define utarray_new(a,_icd) do { \ + (a) = (UT_array*)malloc(sizeof(UT_array)); \ + if ((a) == NULL) { \ + utarray_oom(); \ + } \ + utarray_init(a,_icd); \ +} while(0) + +#define utarray_free(a) do { \ + utarray_done(a); \ + free(a); \ +} while(0) + +#define utarray_reserve(a,by) do { \ + if (((a)->i+(by)) > (a)->n) { \ + char *utarray_tmp; \ + while (((a)->i+(by)) > (a)->n) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ + utarray_tmp=(char*)realloc((a)->d, (a)->n*(a)->icd.sz); \ + if (utarray_tmp == NULL) { \ + utarray_oom(); \ + } \ + (a)->d=utarray_tmp; \ + } \ +} while(0) + +#define utarray_push_back(a,p) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ + else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ +} while(0) + +#define utarray_pop_back(a) do { \ + if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ + else { (a)->i--; } \ +} while(0) + +#define utarray_extend_back(a) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ + else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ + (a)->i++; \ +} while(0) + +#define utarray_len(a) ((a)->i) + +#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) +#define _utarray_eltptr(a,j) ((void*)((a)->d + ((a)->icd.sz * (j)))) + +#define utarray_insert(a,p,j) do { \ + if ((j) > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,1); \ + if ((j) < (a)->i) { \ + memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ + else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ + (a)->i++; \ +} while(0) + +#define utarray_replace(a,p,j) do { \ + if ((a)->icd.dtor) { (a)->icd.dtor(_utarray_eltptr(a,j)); } \ + if ((a)->icd.copy) { (a)->icd.copy(_utarray_eltptr(a,j), p); } \ + else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); } \ +} while(0) + +#define utarray_inserta(a,w,j) do { \ + if (utarray_len(w) == 0) break; \ + if ((j) > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,utarray_len(w)); \ + if ((j) < (a)->i) { \ + memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ + _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { \ + unsigned _ut_i; \ + for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ + (a)->icd.copy(_utarray_eltptr(a, (j) + _ut_i), _utarray_eltptr(w, _ut_i)); \ + } \ + } else { \ + memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ + utarray_len(w)*((a)->icd.sz)); \ + } \ + (a)->i += utarray_len(w); \ +} while(0) + +#define utarray_resize(dst,num) do { \ + unsigned _ut_i; \ + if ((dst)->i > (unsigned)(num)) { \ + if ((dst)->icd.dtor) { \ + for (_ut_i = (num); _ut_i < (dst)->i; ++_ut_i) { \ + (dst)->icd.dtor(_utarray_eltptr(dst, _ut_i)); \ + } \ + } \ + } else if ((dst)->i < (unsigned)(num)) { \ + utarray_reserve(dst, (num) - (dst)->i); \ + if ((dst)->icd.init) { \ + for (_ut_i = (dst)->i; _ut_i < (unsigned)(num); ++_ut_i) { \ + (dst)->icd.init(_utarray_eltptr(dst, _ut_i)); \ + } \ + } else { \ + memset(_utarray_eltptr(dst, (dst)->i), 0, (dst)->icd.sz*((num) - (dst)->i)); \ + } \ + } \ + (dst)->i = (num); \ +} while(0) + +#define utarray_concat(dst,src) do { \ + utarray_inserta(dst, src, utarray_len(dst)); \ +} while(0) + +#define utarray_erase(a,pos,len) do { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for (_ut_i = 0; _ut_i < (len); _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a, (pos) + _ut_i)); \ + } \ + } \ + if ((a)->i > ((pos) + (len))) { \ + memmove(_utarray_eltptr(a, pos), _utarray_eltptr(a, (pos) + (len)), \ + ((a)->i - ((pos) + (len))) * (a)->icd.sz); \ + } \ + (a)->i -= (len); \ +} while(0) + +#define utarray_renew(a,u) do { \ + if (a) utarray_clear(a); \ + else utarray_new(a, u); \ +} while(0) + +#define utarray_clear(a) do { \ + if ((a)->i > 0) { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(_utarray_eltptr(a, _ut_i)); \ + } \ + } \ + (a)->i = 0; \ + } \ +} while(0) + +#define utarray_sort(a,cmp) do { \ + qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ +} while(0) + +#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) + +#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) +#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : (((a)->i != utarray_eltidx(a,e)+1) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) +#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) != 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) +#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) +#define utarray_eltidx(a,e) (((char*)(e) - (a)->d) / (a)->icd.sz) + +/* last we pre-define a few icd for common utarrays of ints and strings */ +static void utarray_str_cpy(void *dst, const void *src) { + char *const *srcc = (char *const *)src; + char **dstc = (char**)dst; + if (*srcc == NULL) { + *dstc = NULL; + } else { + *dstc = (char*)malloc(strlen(*srcc) + 1); + if (*dstc == NULL) { + utarray_oom(); + } else { + strcpy(*dstc, *srcc); + } + } +} +static void utarray_str_dtor(void *elt) { + char **eltc = (char**)elt; + if (*eltc != NULL) free(*eltc); +} +static const UT_icd ut_str_icd UTARRAY_UNUSED = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; +static const UT_icd ut_int_icd UTARRAY_UNUSED = {sizeof(int),NULL,NULL,NULL}; +static const UT_icd ut_ptr_icd UTARRAY_UNUSED = {sizeof(void*),NULL,NULL,NULL}; + + +#endif /* UTARRAY_H */ + +``` + +`external/uthash/uthash.h`: + +```h +/* +Copyright (c) 2003-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.3.0 + +#include /* memcmp, memset, strlen */ +#include /* ptrdiff_t */ +#include /* exit */ + +#if defined(HASH_NO_STDINT) && HASH_NO_STDINT +/* The user doesn't have , and must figure out their own way + to provide definitions for uint8_t and uint32_t. */ +#else +#include /* uint8_t, uint32_t */ +#endif + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__MCST__) /* Elbrus C Compiler */ +#define DECLTYPE(x) (__typeof(x)) +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE(x) +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_bzero +#define uthash_bzero(a,n) memset(a,'\0',n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif + +#ifndef HASH_FUNCTION +#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv) +#endif + +#ifndef HASH_KEYCMP +#define HASH_KEYCMP(a,b,n) memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ +} while (0) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FUNCTION(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl,oomed) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!(tbl)->bloom_bv) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) ((bv[(idx)/8U] & (1U << ((idx)%8U))) != 0) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl,oomed) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) 1 +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head,oomed) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } else { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + ) \ + } \ + } \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + do { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + char *_hs_saved_head = (char*)(head); \ + do { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + if (!(oomed)) { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + if (oomed) { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } else { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } else { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ +} while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ +} while (0) + +#endif + + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (const void*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ + HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh,head,delptrhh) \ +do { \ + const struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } else { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) { \ + (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ + } else { \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ + } \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ +} while (0) + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ +do { \ + unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ + HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ +} while (0) +#define HASH_ADD_STR(head,strfield,add) \ +do { \ + unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ +} while (0) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ +do { \ + unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ +} while (0) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#include /* fprintf, stderr */ +#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head,where) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void*)_thh->hh_prev, (void*)_prev); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev != (char*)_thh->prev) { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", \ + (where), (void*)_thh->prev, (void*)_prev); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head,where) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx + * (archive link: https://archive.is/Ivcan ) + */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key = (const unsigned char*)(key); \ + hashv = 0; \ + for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key = (const unsigned char*)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \ + default: ; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + break; \ + default: ; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ +do { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ + && !(addhh)->tbl->noexpand) { \ + HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + HASH_DEL_IN_BKT(head,addhh); \ + } \ + ) \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(head,delhh) \ +do { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ +} while (0) + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + if (!_he_new_buckets) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero(_he_new_buckets, \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ + _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ + (tbl)->nonideal_items++; \ + if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ + _he_newbkt->expand_mult++; \ + } \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ + } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ + ((tbl)->ineff_expands+1U) : 0U; \ + if ((tbl)->ineff_expands > 1U) { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ + } \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ + _hs_psize++; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + if (_hs_q == NULL) { \ + break; \ + } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else if ((cmpfcn( \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ + )) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt = NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if ((src) != NULL) { \ + for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ + _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { \ + _last_elt_hh->next = _elt; \ + } \ + if ((dst) == NULL) { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + } \ + ) \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + } \ + ) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if ((head) != NULL) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + (((head) != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + const void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ + +``` + +`external/uthash/utlist.h`: + +```h +/* +Copyright (c) 2007-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 2.3.0 + +#include + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(LDECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__MCST__) /* Elbrus C Compiler */ +#define LDECLTYPE(x) __typeof(x) +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define IF_NO_DECLTYPE(x) x +#define LDECLTYPE(x) char* +#define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define UTLIST_NEXT(elt,list,next) ((char*)((list)->next)) +#define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define IF_NO_DECLTYPE(x) +#define UTLIST_SV(elt,list) +#define UTLIST_NEXT(elt,list,next) ((elt)->next) +#define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */ +#define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define UTLIST_RS(list) +#define UTLIST_CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ + LL_SORT2(list, cmp, next) + +#define LL_SORT2(list, cmp, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + } \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + + +#define DL_SORT(list, cmp) \ + DL_SORT2(list, cmp, prev, next) + +#define DL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if ((_ls_qsize == 0) || (!_ls_q)) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev, _ls_tail); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +#define CDL_SORT(list, cmp) \ + CDL_SORT2(list, cmp, prev, next) + +#define CDL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + UTLIST_CASTASGN(_ls_oldhead,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); \ + if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = UTLIST_NEXT(_ls_q,list,next); \ + } \ + UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev,_ls_tail); \ + UTLIST_CASTASGN(_tmp,list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ + LL_PREPEND2(head,add,next) + +#define LL_PREPEND2(head,add,next) \ +do { \ + (add)->next = (head); \ + (head) = (add); \ +} while (0) + +#define LL_CONCAT(head1,head2) \ + LL_CONCAT2(head1,head2,next) + +#define LL_CONCAT2(head1,head2,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = (head1); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ + LL_APPEND2(head,add,next) + +#define LL_APPEND2(head,add,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = (head); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_INSERT_INORDER(head,add,cmp) \ + LL_INSERT_INORDER2(head,add,cmp,next) + +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + LL_APPEND_ELEM2(head, _tmp, add, next); \ + } else { \ + (head) = (add); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define LL_LOWER_BOUND(head,elt,like,cmp) \ + LL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define LL_LOWER_BOUND2(head,elt,like,cmp,next) \ + do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if (cmp((elt)->next, like) >= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) + +#define LL_DELETE2(head,del,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (del)->next; \ + } \ + } \ +} while (0) + +#define LL_COUNT(head,el,counter) \ + LL_COUNT2(head,el,counter,next) \ + +#define LL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + LL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define LL_FOREACH(head,el) \ + LL_FOREACH2(head,el,next) + +#define LL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + LL_FOREACH_SAFE2(head,el,tmp,next) + +#define LL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ + LL_SEARCH_SCALAR2(head,out,field,val,next) + +#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define LL_SEARCH(head,out,elt,cmp) \ + LL_SEARCH2(head,out,elt,cmp,next) + +#define LL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) + +#define LL_REPLACE_ELEM(head, el, add) \ + LL_REPLACE_ELEM2(head, el, add, next) + +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_PREPEND_ELEM(head, el, add) \ + LL_PREPEND_ELEM2(head, el, add, next) + +#define LL_APPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (el)->next = (add); \ + } else { \ + LL_PREPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_APPEND_ELEM(head, el, add) \ + LL_APPEND_ELEM2(head, el, add, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef LL_CONCAT2 +#define LL_CONCAT2(head1,head2,next) \ +do { \ + char *_tmp; \ + if (head1) { \ + _tmp = (char*)(head1); \ + while ((head1)->next) { (head1) = (head1)->next; } \ + (head1)->next = (head2); \ + UTLIST_RS(head1); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#undef LL_APPEND2 +#define LL_APPEND2(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#undef LL_INSERT_INORDER2 +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, add)) >= 0) { \ + (add)->next = (head); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_DELETE2 +#define LL_DELETE2(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + (head) = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_REPLACE_ELEM2 +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = head; \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el)->next; \ +} while (0) + +#undef LL_PREPEND_ELEM2 +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = (head); \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el); \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ + DL_PREPEND2(head,add,prev,next) + +#define DL_PREPEND2(head,add,prev,next) \ +do { \ + (add)->next = (head); \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ + DL_APPEND2(head,add,prev,next) + +#define DL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_INSERT_INORDER(head,add,cmp) \ + DL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + DL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_LOWER_BOUND(head,elt,like,cmp) \ + DL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define DL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ + DL_CONCAT2(head1,head2,prev,next) + +#define DL_CONCAT2(head1,head2,prev,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + UTLIST_CASTASGN(_tmp, (head2)->prev); \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + UTLIST_CASTASGN((head1)->prev, _tmp); \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ + DL_DELETE2(head,del,prev,next) + +#define DL_DELETE2(head,del,prev,next) \ +do { \ + assert((head) != NULL); \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del) == (head)) { \ + assert((del)->next != NULL); \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + +#define DL_COUNT(head,el,counter) \ + DL_COUNT2(head,el,counter,next) \ + +#define DL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + DL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define DL_FOREACH(head,el) \ + DL_FOREACH2(head,el,next) + +#define DL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + DL_FOREACH_SAFE2(head,el,tmp,next) + +#define DL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH +#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 +#define DL_SEARCH2 LL_SEARCH2 + +#define DL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + (add)->next = (el)->next; \ + if ((el)->next == NULL) { \ + (add)->prev = (add); \ + } else { \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + } \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->prev->next = (add); \ + if ((el)->next == NULL) { \ + (head)->prev = (add); \ + } else { \ + (add)->next->prev = (add); \ + } \ + } \ +} while (0) + +#define DL_REPLACE_ELEM(head, el, add) \ + DL_REPLACE_ELEM2(head, el, add, prev, next) + +#define DL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ + } else { \ + DL_APPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_PREPEND_ELEM(head, el, add) \ + DL_PREPEND_ELEM2(head, el, add, prev, next) + +#define DL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } else { \ + DL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_APPEND_ELEM(head, el, add) \ + DL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef DL_INSERT_INORDER2 +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = NULL; \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_APPEND(head,add) \ + CDL_APPEND2(head,add,prev,next) + +#define CDL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } \ +} while (0) + +#define CDL_PREPEND(head,add) \ + CDL_PREPEND2(head,add,prev,next) + +#define CDL_PREPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define CDL_INSERT_INORDER(head,add,cmp) \ + CDL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->next = (head); \ + (head)->prev = (head); \ + } \ +} while (0) + +#define CDL_LOWER_BOUND(head,elt,like,cmp) \ + CDL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define CDL_DELETE(head,del) \ + CDL_DELETE2(head,del,prev,next) + +#define CDL_DELETE2(head,del,prev,next) \ +do { \ + if (((head)==(del)) && ((head)->next == (head))) { \ + (head) = NULL; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_COUNT(head,el,counter) \ + CDL_COUNT2(head,el,counter,next) \ + +#define CDL_COUNT2(head, el, counter,next) \ +do { \ + (counter) = 0; \ + CDL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define CDL_FOREACH(head,el) \ + CDL_FOREACH2(head,el,next) + +#define CDL_FOREACH2(head,el,next) \ + for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) + +#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ + for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \ + (el) && ((tmp2) = (el)->next, 1); \ + (el) = ((el) == (tmp1) ? NULL : (tmp2))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ + CDL_SEARCH_SCALAR2(head,out,field,val,next) + +#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define CDL_SEARCH(head,out,elt,cmp) \ + CDL_SEARCH2(head,out,elt,cmp,next) + +#define CDL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((el)->next == (el)) { \ + (add)->next = (add); \ + (add)->prev = (add); \ + (head) = (add); \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM(head, el, add) \ + CDL_REPLACE_ELEM2(head, el, add, prev, next) + +#define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } else { \ + CDL_APPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_PREPEND_ELEM(head, el, add) \ + CDL_PREPEND_ELEM2(head, el, add, prev, next) + +#define CDL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + (add)->next->prev = (add); \ + } else { \ + CDL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_APPEND_ELEM(head, el, add) \ + CDL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef CDL_INSERT_INORDER2 +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (add)->prev->next = (add); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (add)->next->prev = (add); \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +#endif /* UTLIST_H */ + +``` + +`external/uthash/utringbuffer.h`: + +```h +/* +Copyright (c) 2015-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* a ring-buffer implementation using macros + */ +#ifndef UTRINGBUFFER_H +#define UTRINGBUFFER_H + +#define UTRINGBUFFER_VERSION 2.3.0 + +#include +#include +#include "utarray.h" // for "UT_icd" + +typedef struct { + unsigned i; /* index of next available slot; wraps at n */ + unsigned n; /* capacity */ + unsigned char f; /* full */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz */ +} UT_ringbuffer; + +#define utringbuffer_init(a, _n, _icd) do { \ + memset(a, 0, sizeof(UT_ringbuffer)); \ + (a)->icd = *(_icd); \ + (a)->n = (_n); \ + if ((a)->n) { (a)->d = (char*)malloc((a)->n * (_icd)->sz); } \ +} while(0) + +#define utringbuffer_clear(a) do { \ + if ((a)->icd.dtor) { \ + if ((a)->f) { \ + unsigned _ut_i; \ + for (_ut_i = 0; _ut_i < (a)->n; ++_ut_i) { \ + (a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \ + } \ + } else { \ + unsigned _ut_i; \ + for (_ut_i = 0; _ut_i < (a)->i; ++_ut_i) { \ + (a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \ + } \ + } \ + } \ + (a)->i = 0; \ + (a)->f = 0; \ +} while(0) + +#define utringbuffer_done(a) do { \ + utringbuffer_clear(a); \ + free((a)->d); (a)->d = NULL; \ + (a)->n = 0; \ +} while(0) + +#define utringbuffer_new(a,n,_icd) do { \ + a = (UT_ringbuffer*)malloc(sizeof(UT_ringbuffer)); \ + utringbuffer_init(a, n, _icd); \ +} while(0) + +#define utringbuffer_free(a) do { \ + utringbuffer_done(a); \ + free(a); \ +} while(0) + +#define utringbuffer_push_back(a,p) do { \ + if ((a)->icd.dtor && (a)->f) { (a)->icd.dtor(_utringbuffer_internalptr(a,(a)->i)); } \ + if ((a)->icd.copy) { (a)->icd.copy( _utringbuffer_internalptr(a,(a)->i), p); } \ + else { memcpy(_utringbuffer_internalptr(a,(a)->i), p, (a)->icd.sz); }; \ + if (++(a)->i == (a)->n) { (a)->i = 0; (a)->f = 1; } \ +} while(0) + +#define utringbuffer_len(a) ((a)->f ? (a)->n : (a)->i) +#define utringbuffer_empty(a) ((a)->i == 0 && !(a)->f) +#define utringbuffer_full(a) ((a)->f != 0) + +#define _utringbuffer_real_idx(a,j) ((a)->f ? ((j) + (a)->i) % (a)->n : (j)) +#define _utringbuffer_internalptr(a,j) ((void*)((a)->d + ((a)->icd.sz * (j)))) +#define utringbuffer_eltptr(a,j) ((0 <= (j) && (j) < utringbuffer_len(a)) ? _utringbuffer_internalptr(a,_utringbuffer_real_idx(a,j)) : NULL) + +#define _utringbuffer_fake_idx(a,j) ((a)->f ? ((j) + (a)->n - (a)->i) % (a)->n : (j)) +#define _utringbuffer_internalidx(a,e) (((char*)(e) >= (a)->d) ? (((char*)(e) - (a)->d)/(a)->icd.sz) : -1) +#define utringbuffer_eltidx(a,e) _utringbuffer_fake_idx(a, _utringbuffer_internalidx(a,e)) + +#define utringbuffer_front(a) utringbuffer_eltptr(a,0) +#define utringbuffer_next(a,e) ((e)==NULL ? utringbuffer_front(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)+1)) +#define utringbuffer_prev(a,e) ((e)==NULL ? utringbuffer_back(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)-1)) +#define utringbuffer_back(a) (utringbuffer_empty(a) ? NULL : utringbuffer_eltptr(a, utringbuffer_len(a) - 1)) + +#endif /* UTRINGBUFFER_H */ + +``` + +`external/uthash/utstack.h`: + +```h +/* +Copyright (c) 2018-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTSTACK_H +#define UTSTACK_H + +#define UTSTACK_VERSION 2.3.0 + +/* + * This file contains macros to manipulate a singly-linked list as a stack. + * + * To use utstack, your structure must have a "next" pointer. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *next; + * }; + * + * struct item *stack = NULL; + * + * int main() { + * int count; + * struct item *tmp; + * struct item *item = malloc(sizeof *item); + * item->id = 42; + * STACK_COUNT(stack, tmp, count); assert(count == 0); + * STACK_PUSH(stack, item); + * STACK_COUNT(stack, tmp, count); assert(count == 1); + * STACK_POP(stack, item); + * free(item); + * STACK_COUNT(stack, tmp, count); assert(count == 0); + * } + * -------------------------------------------------- + */ + +#define STACK_TOP(head) (head) + +#define STACK_EMPTY(head) (!(head)) + +#define STACK_PUSH(head,add) \ + STACK_PUSH2(head,add,next) + +#define STACK_PUSH2(head,add,next) \ +do { \ + (add)->next = (head); \ + (head) = (add); \ +} while (0) + +#define STACK_POP(head,result) \ + STACK_POP2(head,result,next) + +#define STACK_POP2(head,result,next) \ +do { \ + (result) = (head); \ + (head) = (head)->next; \ +} while (0) + +#define STACK_COUNT(head,el,counter) \ + STACK_COUNT2(head,el,counter,next) \ + +#define STACK_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + for ((el) = (head); el; (el) = (el)->next) { ++(counter); } \ +} while (0) + +#endif /* UTSTACK_H */ + +``` + +`external/uthash/utstring.h`: + +```h +/* +Copyright (c) 2008-2025, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* a dynamic string implementation using macros + */ +#ifndef UTSTRING_H +#define UTSTRING_H + +#define UTSTRING_VERSION 2.3.0 + +#include +#include +#include +#include + +#ifdef __GNUC__ +#define UTSTRING_UNUSED __attribute__((__unused__)) +#else +#define UTSTRING_UNUSED +#endif + +#ifdef oom +#error "The name of macro 'oom' has been changed to 'utstring_oom'. Please update your code." +#define utstring_oom() oom() +#endif + +#ifndef utstring_oom +#define utstring_oom() exit(-1) +#endif + +typedef struct { + char *d; /* pointer to allocated buffer */ + size_t n; /* allocated capacity */ + size_t i; /* index of first unused byte */ +} UT_string; + +#define utstring_reserve(s,amt) \ +do { \ + if (((s)->n - (s)->i) < (size_t)(amt)) { \ + char *utstring_tmp = (char*)realloc( \ + (s)->d, (s)->n + (amt)); \ + if (!utstring_tmp) { \ + utstring_oom(); \ + } \ + (s)->d = utstring_tmp; \ + (s)->n += (amt); \ + } \ +} while(0) + +#define utstring_init(s) \ +do { \ + (s)->n = 0; (s)->i = 0; (s)->d = NULL; \ + utstring_reserve(s,100); \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_done(s) \ +do { \ + if ((s)->d != NULL) free((s)->d); \ + (s)->n = 0; \ +} while(0) + +#define utstring_free(s) \ +do { \ + utstring_done(s); \ + free(s); \ +} while(0) + +#define utstring_new(s) \ +do { \ + (s) = (UT_string*)malloc(sizeof(UT_string)); \ + if (!(s)) { \ + utstring_oom(); \ + } \ + utstring_init(s); \ +} while(0) + +#define utstring_renew(s) \ +do { \ + if (s) { \ + utstring_clear(s); \ + } else { \ + utstring_new(s); \ + } \ +} while(0) + +#define utstring_clear(s) \ +do { \ + (s)->i = 0; \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_bincpy(s,b,l) \ +do { \ + utstring_reserve((s),(l)+1); \ + if (l) memcpy(&(s)->d[(s)->i], b, l); \ + (s)->i += (l); \ + (s)->d[(s)->i]='\0'; \ +} while(0) + +#define utstring_concat(dst,src) \ +do { \ + utstring_reserve((dst),((src)->i)+1); \ + if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \ + (dst)->i += (src)->i; \ + (dst)->d[(dst)->i]='\0'; \ +} while(0) + +#define utstring_len(s) ((s)->i) + +#define utstring_body(s) ((s)->d) + +UTSTRING_UNUSED static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) { + int n; + va_list cp; + for (;;) { +#ifdef _WIN32 + cp = ap; +#else + va_copy(cp, ap); +#endif + n = vsnprintf(&s->d[s->i], s->n-s->i, fmt, cp); + va_end(cp); + + if ((n > -1) && ((size_t) n < (s->n-s->i))) { + s->i += n; + return; + } + + /* Else try again with more space. */ + if (n > -1) utstring_reserve(s,n+1); /* exact */ + else utstring_reserve(s,(s->n)*2); /* 2x */ + } +} +#ifdef __GNUC__ +/* support printf format checking (2=the format string, 3=start of varargs) */ +static void utstring_printf(UT_string *s, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#endif +UTSTRING_UNUSED static void utstring_printf(UT_string *s, const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + utstring_printf_va(s,fmt,ap); + va_end(ap); +} + +/******************************************************************************* + * begin substring search functions * + ******************************************************************************/ +/* Build KMP table from left to right. */ +UTSTRING_UNUSED static void _utstring_BuildTable( + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = 0; + j = i - 1; + P_KMP_Table[i] = j; + while (i < (long) P_NeedleLen) + { + while ( (j > -1) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j]; + } + i++; + j++; + if (i < (long) P_NeedleLen) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i] = P_KMP_Table[j]; + } + else + { + P_KMP_Table[i] = j; + } + } + else + { + P_KMP_Table[i] = j; + } + } + + return; +} + + +/* Build KMP table from right to left. */ +UTSTRING_UNUSED static void _utstring_BuildTableR( + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = P_NeedleLen - 1; + j = i + 1; + P_KMP_Table[i + 1] = j; + while (i >= 0) + { + while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j + 1]; + } + i--; + j--; + if (i >= 0) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i + 1] = P_KMP_Table[j + 1]; + } + else + { + P_KMP_Table[i + 1] = j; + } + } + else + { + P_KMP_Table[i + 1] = j; + } + } + + return; +} + + +/* Search data from left to right. ( Multiple search mode. ) */ +UTSTRING_UNUSED static long _utstring_find( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + const long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from left to right. */ + i = j = 0; + while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) ) + { + while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i]; + } + i++; + j++; + if (i >= (int)P_NeedleLen) + { + /* Found. */ + V_FindPosition = j - i; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( Multiple search mode. ) */ +UTSTRING_UNUSED static long _utstring_findR( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + const long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from right to left. */ + j = (P_HaystackLen - 1); + i = (P_NeedleLen - 1); + while ( (j >= 0) && (j >= i) ) + { + while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i + 1]; + } + i--; + j--; + if (i < 0) + { + /* Found. */ + V_FindPosition = j + 1; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from left to right. ( One time search mode. ) */ +UTSTRING_UNUSED static long utstring_find( + const UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + size_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = s->i - V_StartPosition; + if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_find(s->d + V_StartPosition, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + if (V_FindPosition >= 0) + { + V_FindPosition += V_StartPosition; + } + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( One time search mode. ) */ +UTSTRING_UNUSED static long utstring_findR( + const UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + size_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = V_StartPosition + 1; + if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_findR(s->d, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} +/******************************************************************************* + * end substring search functions * + ******************************************************************************/ + +#endif /* UTSTRING_H */ + +``` + +`include/r3d/r3d.h`: + +```h +/* + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided "as-is", without any express or implied warranty. In no event + * will the authors be held liable for any damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, including commercial + * applications, and to alter it and redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not claim that you + * wrote the original software. If you use this software in a product, an acknowledgment + * in the product documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be misrepresented + * as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef R3D_H +#define R3D_H + +#include "r3d_ambient_map.h" +#include "r3d_animation.h" +#include "r3d_animation_player.h" +#include "r3d_animation_tree.h" +#include "r3d_camera.h" +#include "r3d_core.h" +#include "r3d_cubemap.h" +#include "r3d_decal.h" +#include "r3d_draw.h" +#include "r3d_environment.h" +#include "r3d_frustum.h" +#include "r3d_instance.h" +#include "r3d_kinematics.h" +#include "r3d_lighting.h" +#include "r3d_material.h" +#include "r3d_mesh_data.h" +#include "r3d_mesh.h" +#include "r3d_model.h" +#include "r3d_pack.h" +#include "r3d_probe.h" +#include "r3d_screen_shader.h" +#include "r3d_shape.h" +#include "r3d_skeleton.h" +#include "r3d_sky.h" +#include "r3d_surface_shader.h" +#include "r3d_utils.h" + +#endif // R3D_H + +``` + +`include/r3d/r3d_ambient_map.h`: + +```h +/* r3d_ambient_map.h -- R3D Ambient Map Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_AMBIENT_MAP_H +#define R3D_AMBIENT_MAP_H + +#include "./r3d_cubemap.h" +#include +#include + +/** + * @defgroup EnvironmentMap + * @{ + */ + +// ======================================== +// FLAG TYPES +// ======================================== + +/** + * @brief Bit-flags controlling what components are generated. + * + * - R3D_AMBIENT_ILLUMINATION -> generate diffuse irradiance + * - R3D_AMBIENT_REFLECTION -> generate specular prefiltered map + */ +typedef uint32_t R3D_AmbientFlags; + +#define R3D_AMBIENT_ILLUMINATION (1 << 0) +#define R3D_AMBIENT_REFLECTION (1 << 1) + +// ======================================== +// STRUCT TYPES +// ======================================== + +/** + * @brief Global environment lighting data. + * + * An ambient map is built from a cubemap (like a skybox) + * and preprocessed into two specialized textures: + * + * - irradiance: + * Low-frequency lighting used for diffuse IBL. + * Captures soft ambient light from all directions. + * + * - prefilter: + * Mipmapped environment used for specular reflections. + * Higher mip levels simulate rougher surfaces. + * + * Both textures are derived from the same source cubemap, + * but serve different shading purposes. + */ +typedef struct R3D_AmbientMap { + R3D_AmbientFlags flags; ///< Components generated for this map + uint32_t irradiance; ///< Diffuse IBL cubemap (may be 0 if not generated) + uint32_t prefilter; ///< Specular prefiltered cubemap (may be 0 if not generated) +} R3D_AmbientMap; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Loads a ambient map from an image file. + * + * The layout parameter tells how faces are arranged inside the source image. + */ +R3DAPI R3D_AmbientMap R3D_LoadAmbientMap(const char* fileName, R3D_CubemapLayout layout, R3D_AmbientFlags flags); + +/** + * @brief Builds a ambient map from an existing Image. + * + * Same behavior as R3D_LoadAmbientMap(), but without loading from disk. + */ +R3DAPI R3D_AmbientMap R3D_LoadAmbientMapFromImage(Image image, R3D_CubemapLayout layout, R3D_AmbientFlags flags); + +/** + * @brief Generates an ambient map from a cubemap. + * + * The source cubemap should usually be an HDR sky/environment. + * + * Depending on the provided flags, this function: + * - convolves the cubemap into diffuse irradiance + * - builds a mipmapped prefiltered cubemap for reflections + * + * @param cubemap Source cubemap (environment / sky). + * @param flags Which components to generate (irradiance, reflection, or both). + * @return A fully initialized ambient map. + */ +R3DAPI R3D_AmbientMap R3D_GenAmbientMap(R3D_Cubemap cubemap, R3D_AmbientFlags flags); + +/** + * @brief Frees the textures used by an ambient map. + * + * After this call, the ambient map is no longer valid. + */ +R3DAPI void R3D_UnloadAmbientMap(R3D_AmbientMap ambientMap); + +/** + * @brief Rebuilds an existing ambient map from a new cubemap. + * + * Use this when the environment changes dynamically (time of day, + * weather, interior/exterior transitions, etc). + * + * Only the components enabled in `ambientMap.flags` are regenerated. + * + * @param ambientMap Existing ambient map to update. + * @param cubemap New cubemap source. + */ +R3DAPI void R3D_UpdateAmbientMap(R3D_AmbientMap ambientMap, R3D_Cubemap cubemap); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of EnvironmentMap + +#endif // R3D_AMBIENT_MAP_H + +``` + +`include/r3d/r3d_animation.h`: + +```h +/* r3d_animation.h -- R3D Animation Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_ANIMATION_H +#define R3D_ANIMATION_H + +#include "./r3d_platform.h" +#include "./r3d_importer.h" +#include +#include + +/** + * @defgroup Animation + * @{ + */ + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Animation track storing keyframe times and values. + * + * Represents a single animated property (translation, rotation or scale). + * Keys are sampled by time and interpolated at runtime. + */ +typedef struct R3D_AnimationTrack { + const float* times; ///< Keyframe times (sorted, in animation ticks). + const void* values; ///< Keyframe values (Vector3 or Quaternion). + int count; ///< Number of keyframes. +} R3D_AnimationTrack; + +/** + * @brief Animation channel controlling a single bone. + * + * Contains animation tracks for translation, rotation and scale. + * The sampled tracks are combined to produce the bone local transform. + */ +typedef struct R3D_AnimationChannel { + R3D_AnimationTrack translation; ///< Translation track (Vector3). + R3D_AnimationTrack rotation; ///< Rotation track (Quaternion). + R3D_AnimationTrack scale; ///< Scale track (Vector3). + int boneIndex; ///< Index of the affected bone. +} R3D_AnimationChannel; + +/** + * @brief Represents a skeletal animation for a model. + * + * Contains all animation channels required to animate a skeleton. + * Each channel corresponds to one bone and defines its transformation + * (translation, rotation, scale) over time. + */ +typedef struct R3D_Animation { + R3D_AnimationChannel* channels; ///< Array of animation channels, one per animated bone. + int channelCount; ///< Total number of channels in this animation. + float ticksPerSecond; ///< Playback rate; number of animation ticks per second. + float duration; ///< Total length of the animation, in ticks. + int boneCount; ///< Number of bones in the target skeleton. + char name[32]; ///< Animation name (null-terminated string). +} R3D_Animation; + +/** + * @brief Represents a collection of skeletal animations sharing the same skeleton. + * + * Holds multiple animations that can be applied to compatible models or skeletons. + * Typically loaded together from a single 3D model file (e.g., GLTF, FBX) containing several animation clips. + */ +typedef struct R3D_AnimationLib { + R3D_Animation* animations; ///< Array of animations included in this library. + int count; ///< Number of animations contained in the library. +} R3D_AnimationLib; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Loads animations from a model file. + * @param filePath Path to the model file containing animations. + * @param targetFrameRate Desired frame rate (FPS) for sampling the animations. + * @return Pointer to an array of R3D_Animation, or NULL on failure. + * @note Free the returned array using R3D_UnloadAnimationLib(). + */ +R3DAPI R3D_AnimationLib R3D_LoadAnimationLib(const char* filePath); + +/** + * @brief Loads animations from memory data. + * @param data Pointer to memory buffer containing model animation data. + * @param size Size of the buffer in bytes. + * @param hint Hint on the model format (can be NULL). + * @param targetFrameRate Desired frame rate (FPS) for sampling the animations. + * @return Pointer to an array of R3D_Animation, or NULL on failure. + * @note Free the returned array using R3D_UnloadAnimationLib(). + */ +R3DAPI R3D_AnimationLib R3D_LoadAnimationLibFromMemory(const void* data, unsigned int size, const char* hint); + +/** + * @brief Loads animations from an existing importer. + * @param importer Importer instance containing animation data. + * @return Pointer to an array of R3D_Animation, or NULL on failure. + * @note Free the returned array using R3D_UnloadAnimationLib(). + */ +R3DAPI R3D_AnimationLib R3D_LoadAnimationLibFromImporter(const R3D_Importer* importer); + +/** + * @brief Releases all resources associated with an animation library. + * @param animLib Animation library to unload. + */ +R3DAPI void R3D_UnloadAnimationLib(R3D_AnimationLib animLib); + +/** + * @brief Returns the index of an animation by name. + * @param animLib Animation library to search. + * @param name Name of the animation (case-sensitive). + * @return Zero-based index if found, or -1 if not found. + */ +R3DAPI int R3D_GetAnimationIndex(R3D_AnimationLib animLib, const char* name); + +/** + * @brief Retrieves an animation by name. + * @param animLib Animation library to search. + * @param name Name of the animation (case-sensitive). + * @return Pointer to the animation, or NULL if not found. + */ +R3DAPI R3D_Animation* R3D_GetAnimation(R3D_AnimationLib animLib, const char* name); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Animation + +#endif // R3D_ANIMATION_H + +``` + +`include/r3d/r3d_animation_player.h`: + +```h +/* r3d_animation_player.h -- R3D Animation Player Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_ANIMATION_PLAYER_H +#define R3D_ANIMATION_PLAYER_H + +#include "./r3d_animation.h" +#include "./r3d_platform.h" +#include "./r3d_skeleton.h" +#include +#include + +/** + * @defgroup AnimationPlayer + * @{ + */ + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Types of events that an animation player can emit. + */ +typedef enum { + R3D_ANIM_EVENT_FINISHED, ///< Animation has finished playing (non-looping). + R3D_ANIM_EVENT_LOOPED, ///< Animation has completed a loop. +} R3D_AnimationEvent; + +// ======================================== +// FORWARD DECLARATIONS +// ======================================== + +struct R3D_AnimationPlayer; + +// ======================================== +// CALLBACKS TYPES +// ======================================== + +/** + * @brief Callback type for receiving animation events. + * + * @param player Pointer to the animation player emitting the event. + * @param eventType Type of the event (finished, looped). + * @param animIndex Index of the animation triggering the event. + * @param userData Optional user-defined data passed when the callback was registered. + */ +typedef void (*R3D_AnimationEventCallback)( + struct R3D_AnimationPlayer* player, + R3D_AnimationEvent eventType, + int animIndex, void* userData +); + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Describes the playback state of a single animation within a player. + * + * Tracks the current time, speed, play/pause state, and looping behavior. + */ +typedef struct R3D_AnimationState { + float currentTime; ///< Current playback time in animation ticks. + float speed; ///< Playback speed; can be negative for reverse playback. + bool play; ///< Whether the animation is currently playing. + bool loop; ///< True to enable looping playback. +} R3D_AnimationState; + +/** + * @brief Manages playback of multiple animations for a skeleton. + * + * The animation player updates animation states, interpolates keyframes, + * blends animations according to their weights, and stores the resulting + * local and global bone transforms. It also supports GPU skinning by uploading + * the global pose into a texture. + */ +typedef struct R3D_AnimationPlayer { + + R3D_AnimationLib animLib; ///< Animation library providing the available animations. + R3D_Skeleton skeleton; ///< Skeleton to animate. + + R3D_AnimationState* states; ///< Array of animation states, one per animation. + int activeAnimIndex; ///< Index of the current animation. + + Matrix* localPose; ///< Array of bone transforms representing the blended local pose. + Matrix* modelPose; ///< Array of bone transforms in model space, obtained by hierarchical accumulation. + Matrix* skinBuffer; ///< Array of final skinning matrices (invBind * modelPose), sent to the GPU. + uint32_t skinTexture; ///< GPU texture ID storing the skinning matrices as a 1D RGBA16F texture. + + R3D_AnimationEventCallback eventCallback; ///< Callback function to receive animation events. + void* eventUserData; ///< Optional user data pointer passed to the callback. + +} R3D_AnimationPlayer; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Creates an animation player for a skeleton and animation library. + * + * Allocates memory for animation states and pose buffers. + * + * @param skeleton Skeleton to animate. + * @param animLib Animation library providing animations. + * @return Newly created animation player, or a zeroed struct on failure. + */ +R3DAPI R3D_AnimationPlayer R3D_LoadAnimationPlayer(R3D_Skeleton skeleton, R3D_AnimationLib animLib); + +/** + * @brief Releases all resources used by an animation player. + * + * @param player Animation player to unload. + */ +R3DAPI void R3D_UnloadAnimationPlayer(R3D_AnimationPlayer player); + +/** + * @brief Checks whether an animation player is valid. + * + * @param player Animation player to check. + * @return true if valid, false otherwise. + */ +R3DAPI bool R3D_IsAnimationPlayerValid(R3D_AnimationPlayer player); + +/** + * @brief Returns whether an animation is currently playing. + * + * @param player Animation player. + * @return true if playing, false otherwise. + */ +R3DAPI bool R3D_IsAnimationPlaying(R3D_AnimationPlayer player); + +/** + * @brief Starts playback of the specified animation. + * + * @param player Animation player. + * @param animIndex Index of the animation to play. + */ +R3DAPI void R3D_PlayAnimation(R3D_AnimationPlayer* player, int animIndex); + +/** + * @brief Pauses the current animation. + * + * @param player Animation player. + * @param animIndex Index of the animation to pause. + */ +R3DAPI void R3D_PauseAnimation(R3D_AnimationPlayer* player); + +/** + * @brief Stops the current animation and clamps its time. + * + * @param player Animation player. + * @param animIndex Index of the animation to stop. + */ +R3DAPI void R3D_StopAnimation(R3D_AnimationPlayer* player); + +/** + * @brief Rewinds the animation to the start or end depending on playback direction. + * + * @param player Animation player. + * @param animIndex Index of the animation to rewind. + */ +R3DAPI void R3D_RewindAnimation(R3D_AnimationPlayer* player); + +/** + * @brief Gets the current playback time of an animation. + * + * @param player Animation player. + * @param animIndex Index of the animation. + * @return Current time in animation ticks. + */ +R3DAPI float R3D_GetAnimationTime(R3D_AnimationPlayer player, int animIndex); + +/** + * @brief Sets the current playback time of an animation. + * + * @param player Animation player. + * @param animIndex Index of the animation. + * @param time Time in animation ticks. + */ +R3DAPI void R3D_SetAnimationTime(R3D_AnimationPlayer* player, int animIndex, float time); + +/** + * @brief Gets the playback speed of an animation. + * + * @param player Animation player. + * @param animIndex Index of the animation. + * @return Current speed (may be negative for reverse playback). + */ +R3DAPI float R3D_GetAnimationSpeed(R3D_AnimationPlayer player, int animIndex); + +/** + * @brief Sets the playback speed of an animation. + * + * Negative values play the animation backwards. If setting a negative speed + * on a stopped animation, consider calling RewindAnimation() to start at the end. + * + * @param player Animation player. + * @param animIndex Index of the animation. + * @param speed Playback speed. + */ +R3DAPI void R3D_SetAnimationSpeed(R3D_AnimationPlayer* player, int animIndex, float speed); + +/** + * @brief Gets whether the animation is set to loop. + * + * @param player Animation player. + * @param animIndex Index of the animation. + * @return True if looping is enabled. + */ +R3DAPI bool R3D_GetAnimationLoop(R3D_AnimationPlayer player, int animIndex); + +/** + * @brief Enables or disables looping for the animation. + * + * @param player Animation player. + * @param animIndex Index of the animation. + * @param loop True to enable looping. + */ +R3DAPI void R3D_SetAnimationLoop(R3D_AnimationPlayer* player, int animIndex, bool loop); + +/** + * @brief Advances the time of the current animation. + * + * Updates animation timer based on speed and delta time. + * Does NOT recalculate the skeleton pose. + * + * @param player Animation player. + * @param dt Delta time in seconds. + */ +R3DAPI void R3D_AdvanceAnimationTime(R3D_AnimationPlayer* player, float dt); + +/** + * @brief Computes the local-space transform of each bone for the current animation. + * + * Samples and interpolates the current animation keyframes at the current playback time, + * and stores the resulting bone transforms in local space into @p player->localPose. + * Does NOT advance animation time, and does NOT compute model-space transforms. + * + * @param player Animation player whose local pose will be updated. + */ +R3DAPI void R3D_ComputeAnimationLocalPose(R3D_AnimationPlayer* player); + +/** + * @brief Computes the model-space transform of each bone from the current local pose. + * + * Traverses the bone hierarchy and accumulates local transforms into model-space matrices, + * stored into @p player->modelPose. This assumes @p player->localPose is already up-to-date. + * Does NOT sample animation keyframes, and does NOT advance animation time. + * + * @param player Animation player whose model pose will be updated. + */ +R3DAPI void R3D_ComputeAnimationModelPose(R3D_AnimationPlayer* player); + +/** + * @brief Computes both the local and model-space transforms for the current animation. + * + * Equivalent to calling R3D_ComputeAnimationLocalPose() followed by R3D_ComputeAnimationModelPose(). + * Does NOT advance animation time. + * + * @param player Animation player whose local and model poses will be updated. + */ +R3DAPI void R3D_ComputeAnimationPose(R3D_AnimationPlayer* player); + +/** + * @brief Computes the final skinning matrices and uploads them to the GPU. + * + * Multiplies each bone's model-space transform by its inverse bind matrix to produce + * the skinning matrices, then uploads them to the GPU skin texture. + * This assumes @p player->modelPose is already up-to-date. + * + * @param player Animation player whose skinning matrices will be uploaded. + */ +R3DAPI void R3D_UploadAnimationPose(R3D_AnimationPlayer* player); + +/** + * @brief Updates the animation player: calculates and upload the current pose pose, then advances time. + * + * Equivalent to calling R3D_ComputeAnimationLocalPose() followed by + * R3D_ComputeAnimationModelPose() and R3D_AdvanceAnimationTime(). + * + * @param player Animation player. + * @param dt Delta time in seconds. + */ +R3DAPI void R3D_UpdateAnimationPlayer(R3D_AnimationPlayer* player, float dt); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of AnimationPlayer + +#endif // R3D_ANIMATION_PLAYER_H + +``` + +`include/r3d/r3d_animation_tree.h`: + +```h +/* r3d_animation_tree.h -- R3D Animation Tree Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_ANIMATION_TREE_H +#define R3D_ANIMATION_TREE_H + +#include "./r3d_animation_player.h" + +/** + * @defgroup AnimationTree + * @{ + */ + +// ======================================== +// FORWARD DECLARATIONS +// ======================================== + +typedef union R3D_AnimationTreeNode R3D_AnimationTreeNode; + +typedef int R3D_AnimationStmIndex; + +// ======================================== +// CALLBACKS TYPES +// ======================================== + +/** + * @brief Callback type for manipulating the animation before it is used by the animation tree. + * + * @param animation Pointer to the processed animation. + * @param state Current animation state. + * @param boneIndex Index of the processed bone. + * @param out Transformation of the processed bone. + * @param userData Optional user-defined data passed when the callback was registered. + */ +typedef void (*R3D_AnimationNodeCallback)(const R3D_Animation* animation, R3D_AnimationState state, + int boneIndex, Transform* out, void* userData); + +/** + * @brief Callback type for manipulating the final animation. + * + * @param player Pointer to the animation player used by the animation tree. + * @param boneIndex Index of the processed bone. + * @param out Transformation of the processed bone. + * @param userData Optional user-defined data passed when the callback was registered. + */ +typedef void (*R3D_AnimationTreeCallback)(const R3D_AnimationPlayer* player, int boneIndex, + Transform* out, void* userData); + +// ======================================== +// ENUM TYPES +// ======================================== + +/** + * @brief Types of operation modes for state machine edge. + */ +typedef enum { + R3D_STM_EDGE_INSTANT = 0, ///< Switch to next state instantly, with respecting cross fade time. + R3D_STM_EDGE_ONDONE ///< Switch to next state when associated animation is done or looped with looper parameter set true. +} R3D_StmEdgeMode; + +/** + * @brief Types of travel status for state machine edge. + */ +typedef enum { + R3D_STM_EDGE_ON = 0, ///< Edge is traversable by travel function. + R3D_STM_EDGE_AUTO, ///< Edge is traversable automatically and by travel function. + R3D_STM_EDGE_ONCE, ///< Edge is traversable automatically and by travel function, but only once; edge status changes to nextStatus when traversed. + R3D_STM_EDGE_OFF ///< Edge is not traversable. +} R3D_StmEdgeStatus; + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Bone mask for Blend2 and Add2 animation nodes. + * + * Bone mask structure, can by created by R3D_ComputeBoneMask. + */ +typedef struct { + int32_t mask[8]; ///< Bit mask buffer for maximum of 256 bones (32bits * 8). + int boneCount; ///< Actual bones count. +} R3D_BoneMask; + +/** + * @brief Parameters for animation tree node Animation. + * + * Animation is a leaf node, holding the R3D_Animation structure. + */ +typedef struct { + char name[32]; ///< Animation name (null-terminated string). + R3D_AnimationState state; ///< Animation state. + bool looper; ///< Flag to control whether the animation is considered done when looped; yes when true. + + R3D_AnimationNodeCallback evalCallback; ///< Callback function to receive and modify animation transformation before been used. + void* evalUserData; ///< Optional user data pointer passed to the callback. +} R3D_AnimationNodeParams; + +/** + * @brief Parameters for animation tree node Blend2. + * + * Blend2 node blends channels of any two animation nodes together, with respecting optional bone mask. + */ +typedef struct { + R3D_BoneMask* boneMask; ///< Pointer to bone mask structure, can be NULL; calculated by R3D_ComputeBoneMask(). + float blend; ///< Blend weight value, can be in interval from 0.0 to 1.0. +} R3D_Blend2NodeParams; + +/** + * @brief Parameters for animation tree node Add2. + * + * Add2 node adds channels of any two animation nodes together, with respecting optional bone mask. + */ +typedef struct { + R3D_BoneMask* boneMask; ///< Pointer to bone mask structure, can be NULL; calculated by R3D_ComputeBoneMask(). + float weight; ///< Add weight value, can be in interval from 0.0 to 1.0. +} R3D_Add2NodeParams; + +/** + * @brief Parameters for animation tree node Switch. + * + * Switch node allows instant or blended/faded transition between any animation nodes connected to inputs. + */ +typedef struct { + bool synced; ///< Flag to control input animation nodes synchronization; activated input is reset when false. + int activeInput; ///< Active input zero-based index. + float xFadeTime; ///< Animation nodes cross fade blending time, in seconds. +} R3D_SwitchNodeParams; + +/** + * @brief Parameters for animation state machine edge. + */ +typedef struct { + R3D_StmEdgeMode mode; ///< Operation mode. + R3D_StmEdgeStatus status; ///< Current travel status. + R3D_StmEdgeStatus nextStatus; ///< Travel status used after machine traversed through this edge with status set to R3D_STM_EDGE_ONCE. + float xFadeTime; ///< Cross fade blending time between connected animation nodes, in seconds. +} R3D_StmEdgeParams; + +/** + * @brief Manages a tree structure of animation nodes. + * + * Animation tree allows to define complex logic for switching and blending animations of + * associated animation player. It supports 5 different animation node types: Animation, Blend2, Add2, + * Switch and State Machine. It also fully supports root motion and bone masking in Blend2/Add2. + */ +typedef struct { + R3D_AnimationPlayer player; ///< Animation player and skeleton used by all animation nodes. + R3D_AnimationTreeNode* rootNode; ///< Pointer to root animation node of the tree. + R3D_AnimationTreeNode* nodePool; ///< Animation node pool of size nodePoolMaxSize. + int nodePoolSize; ///< Current animation node pool size. + int nodePoolMaxSize; ///< Maximum number of animation nodes, defined during load. + int rootBone; ///< Optional root bone index, -1 if not defined. + + R3D_AnimationTreeCallback updateCallback; ///< Callback function to receive and modify final animation transformation. + void* updateUserData; ///< Optional user data pointer passed to the callback. +} R3D_AnimationTree; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Creates an animation tree using given animation player. + * + * Allocates memory for animation nodes pool. + * + * @param player Animation player used by the animation tree. + * @param maxSize Size of the animation nodes pool; maximum number of animation nodes in the tree. + * @return Newly created animation tree, or a zeroed struct on failure. + */ +R3DAPI R3D_AnimationTree R3D_LoadAnimationTree(R3D_AnimationPlayer player, int maxSize); + +/** + * @brief Creates an animation tree using given animation player, with optional root motion support. + * + * Allocates memory for animation nodes pool and sets root bone index for root motion. + * + * @param player Animation player used by the animation tree. + * @param maxSize Size of the animation nodes pool; maximum number of animation nodes in the tree. + * @param rootBone Root bone index; set -1 to disable root motion. + * @return Newly created animation tree, or a zeroed struct on failure. + */ +R3DAPI R3D_AnimationTree R3D_LoadAnimationTreeEx(R3D_AnimationPlayer player, int maxSize, int rootBone); + +/** + * @brief Creates an animation tree using given animation player, with optional root motion support and callback. + * + * Allocates memory for animation nodes pool, sets root bone index and update callback. + * + * @param player Animation player used by the animation tree. + * @param maxSize Size of the animation nodes pool; maximum number of animation nodes in the tree. + * @param rootBone Root bone index; set -1 to disable root motion. + * @param updateCallback Callback function pointer to receive and modify final animation transformation, can be NULL. + * @param updateUserData User data pointer passed to the callback, can be NULL. + * @return Newly created animation tree, or a zeroed struct on failure. + */ +R3DAPI R3D_AnimationTree R3D_LoadAnimationTreePro(R3D_AnimationPlayer player, int maxSize, int rootBone, + R3D_AnimationTreeCallback updateCallback, void* updateUserData); + +/** + * @brief Releases all resources used by an animation tree, including all animation nodes. + * + * @param tree Animation tree to unload. + */ +R3DAPI void R3D_UnloadAnimationTree(R3D_AnimationTree tree); + +/** + * @brief Updates the animation tree: calculates blended pose, sets and uploads the pose through associated animation player. + * + * @param tree Animation tree. + * @param dt Delta time in seconds. + */ +R3DAPI void R3D_UpdateAnimationTree(R3D_AnimationTree* tree, float dt); + +/** + * @brief Updates the animation tree: calculates blended pose, sets and uploads the pose through associated animation player. + * + * Set the rootMotion and/or rootDistance pointers to get root motion information. + * Divide rootMotion translation vector by dt to get root bone speed. + * + * @param tree Animation tree. + * @param dt Delta time in seconds. + * @param rootMotion Pointer to root bone motion transformation. + * @param rootDistance Pointer to transformation containing root bone distance from rest pose. + */ +R3DAPI void R3D_UpdateAnimationTreeEx(R3D_AnimationTree* tree, float dt, + Transform* rootMotion, Transform* rootDistance); + +/** + * @brief Sets root animation node of the animation tree. + * + * @param tree Animation tree. + * @param node Root animation node. + */ +R3DAPI void R3D_AddRootAnimationNode(R3D_AnimationTree* tree, R3D_AnimationTreeNode* node); + +/** + * @brief Connects two animation nodes of animation tree hierarchy. + * + * @param parent Parent animation node. + * @param node Child animation node. + * @param inputIndex Index of the parent node input used for connection. + * @return True on success; false if parent node or inputIndex is invalid. + */ +R3DAPI bool R3D_AddAnimationNode(R3D_AnimationTreeNode* parent, R3D_AnimationTreeNode* node, int inputIndex); + +/** + * @brief Creates animation node of type Animation. + * + * Animation is a leaf node, holding the R3D_Animation structure. + * + * @param tree Animation tree. + * @param params Animation node initial parameters. + * @return Pointer to created animation node; NULL if maximum number of nodes was exceeded or animation name is not found. + */ +R3DAPI R3D_AnimationTreeNode* R3D_CreateAnimationNode(R3D_AnimationTree* tree, R3D_AnimationNodeParams params); + +/** + * @brief Creates animation node of type Animation, optionally sets animation currentTime. + * + * Animation is a leaf node, holding the R3D_Animation structure. + * + * @param tree Animation tree. + * @param params Animation node initial parameters. + * @param setTime Flag to set currentTime value based on animation speed; useful for animations with negative speed in State Machine. + * @return Pointer to created animation node; NULL if maximum number of nodes was exceeded or animation name is not found. + */ +R3DAPI R3D_AnimationTreeNode* R3D_CreateAnimationNodeEx(R3D_AnimationTree* tree, R3D_AnimationNodeParams params, + bool setTime); + +/** + * @brief Creates animation node of type Blend2. + * + * Blend2 node blends channels of any two animation nodes together, with respecting optional bone mask. + * + * @param tree Animation tree. + * @param params Blend2 node initial parameters. + * @return Pointer to created animation node; NULL if maximum number of nodes was exceeded. + */ +R3DAPI R3D_AnimationTreeNode* R3D_CreateBlend2Node(R3D_AnimationTree* tree, R3D_Blend2NodeParams params); + +/** + * @brief Creates animation node of type Add2. + * + * Add2 node adds channels of any two animation nodes together, with respecting optional bone mask. + * + * @param tree Animation tree. + * @param params Add2 node initial parameters. + * @return Pointer to created animation node; NULL if maximum number of nodes was exceeded. + */ +R3DAPI R3D_AnimationTreeNode* R3D_CreateAdd2Node(R3D_AnimationTree* tree, R3D_Add2NodeParams params); + +/** + * @brief Creates animation node of type Switch. + * + * Switch node allows instant or blended/faded transition between any animation nodes connected to inputs. + * + * @param tree Animation tree. + * @param inputCount Number of available inputs. + * @param params Switch node initial parameters. + * @return Pointer to created animation node; NULL if maximum number of nodes was exceeded. + */ +R3DAPI R3D_AnimationTreeNode* R3D_CreateSwitchNode(R3D_AnimationTree* tree, int inputCount, + R3D_SwitchNodeParams params); + +/** + * @brief Creates animation node of type State Machine (Stm). + * + * Stm node allows to create a state machine graph of any animation nodes. + * + * @param tree Animation tree. + * @param statesCount Maximum number of states in the state machine. + * @param edgesCount Maximum number of edges in the state machine. + * @return Pointer to created animation node; NULL if maximum number of nodes was exceeded. + */ +R3DAPI R3D_AnimationTreeNode* R3D_CreateStmNode(R3D_AnimationTree* tree, int statesCount, + int edgesCount); + +/** + * @brief Creates animation node of type State Machine (Stm), with option to disable travel feature (enabled by default). + * + * @param tree Animation tree. + * @param statesCount Maximum number of states in the state machine. + * @param edgesCount Maximum number of edges in the state machine. + * @param enableTravel Flag to enable or disable travel feature; enabled when set true. + * @return Pointer to created animation node; NULL if maximum number of nodes was exceeded. + */ +R3DAPI R3D_AnimationTreeNode* R3D_CreateStmNodeEx(R3D_AnimationTree* tree, int statesCount, + int edgesCount, bool enableTravel); + +/** + * @brief Creates animation node of type State Machine Stop/Done (StmX). + * + * StmX is a helper animation node, that allows stopping state machine evaluation in the current update. + * Sets animation node done status of the state machine to true. + * + * @param tree Animation tree. + * @param nestedNode Actual animation node with animation channels. + * @return Pointer to created animation node; NULL if maximum number of nodes was exceeded. + */ +R3DAPI R3D_AnimationTreeNode* R3D_CreateStmXNode(R3D_AnimationTree* tree, + R3D_AnimationTreeNode* nestedNode); + +/** + * @brief Creates state in a State Machine animation node. + * + * @param stmNode Animation node of type State Machine. + * @param stateNode Associated animation node of the state. + * @param outEdgesCount Number of output edges of the state. + * @return Index of created state; -1 if maximum number of states was exceeded. + */ +R3DAPI R3D_AnimationStmIndex R3D_CreateStmNodeState(R3D_AnimationTreeNode* stmNode, + R3D_AnimationTreeNode* stateNode, + int outEdgesCount); + +/** + * @brief Creates edge in a State Machine animation node. + * + * @param stmNode Animation node of type State Machine. + * @param beginStateIndex Index of state connected to the edge beginning. + * @param endStateIndex Index of state connected to the edge end. + * @param params Edge initial parameters. + * @return Index of created edge; -1 if maximum number of edges was exceeded. + */ +R3DAPI R3D_AnimationStmIndex R3D_CreateStmNodeEdge(R3D_AnimationTreeNode* stmNode, + R3D_AnimationStmIndex beginStateIndex, + R3D_AnimationStmIndex endStateIndex, + R3D_StmEdgeParams params); + +/** + * @brief Sets parameters of animation node Animation. + * + * @param node Animation node of type Animation. + * @param params New parameters. + */ +R3DAPI void R3D_SetAnimationNodeParams(R3D_AnimationTreeNode* node, + R3D_AnimationNodeParams params); + +/** + * @brief Gets parameters of animation node Animation. + * + * @param node Animation node of type Animation. + * @return Current parameters. + */ +R3DAPI R3D_AnimationNodeParams R3D_GetAnimationNodeParams(R3D_AnimationTreeNode* node); + +/** + * @brief Sets parameters of animation node Blend2. + * + * @param node Animation node of type Blend2. + * @param params New parameters. + */ +R3DAPI void R3D_SetBlend2NodeParams(R3D_AnimationTreeNode* node, + R3D_Blend2NodeParams params); + +/** + * @brief Gets parameters of animation node Blend2. + * + * @param node Animation node of type Blend2. + * @return Current parameters. + */ +R3DAPI R3D_Blend2NodeParams R3D_GetBlend2NodeParams(R3D_AnimationTreeNode* node); + +/** + * @brief Sets parameters of animation node Add2. + * + * @param node Animation node of type Add2. + * @param params New parameters. + */ +R3DAPI void R3D_SetAdd2NodeParams(R3D_AnimationTreeNode* node, + R3D_Add2NodeParams params); + +/** + * @brief Gets parameters of animation node Add2. + * + * @param node Animation node of type Add2. + * @return Current parameters. + */ +R3DAPI R3D_Add2NodeParams R3D_GetAdd2NodeParams(R3D_AnimationTreeNode* node); + +/** + * @brief Sets parameters of animation node Switch. + * + * @param node Animation node of type Switch. + * @param params New parameters. + */ +R3DAPI void R3D_SetSwitchNodeParams(R3D_AnimationTreeNode* node, + R3D_SwitchNodeParams params); + +/** + * @brief Gets parameters of animation node Switch. + * + * @param node Animation node of type Switch. + * @return Current parameters. + */ +R3DAPI R3D_SwitchNodeParams R3D_GetSwitchNodeParams(R3D_AnimationTreeNode* node); + +/** + * @brief Sets parameters of State Machine edge. + * + * @param node Animation node of type State Machine (Stm). + * @param edgeIndex Index of the State Machine edge. + * @param params New parameters of the edge. + */ +R3DAPI void R3D_SetStmNodeEdgeParams(R3D_AnimationTreeNode* node, + R3D_AnimationStmIndex edgeIndex, + R3D_StmEdgeParams params); + +/** + * @brief Gets parameters of State Machine edge. + * + * @param node Animation node of type State Machine. + * @param edgeIndex Index of the State Machine edge. + * @return Current parameters of the edge. + */ +R3DAPI R3D_StmEdgeParams R3D_GetStmNodeEdgeParams(R3D_AnimationTreeNode* node, + R3D_AnimationStmIndex edgeIndex); + +/** + * @brief Gets active state index of State Machine. + * + * @param node Animation node of type State Machine. + * @return Index of current active state. + */ +R3DAPI R3D_AnimationStmIndex R3D_GetStmStateActiveIndex(R3D_AnimationTreeNode* node); + +/** + * @brief Sets travel path inside State Machine, from current state to target. + * + * If travel path is not found, target is set as current state instantly (teleport). + * + * @param node Animation node of type State Machine. + * @param targetStateIndex Index of the targeted state. + */ +R3DAPI void R3D_TravelToStmState(R3D_AnimationTreeNode* node, + R3D_AnimationStmIndex targetStateIndex); + +/** + * @brief Computes bone mask from list of bone names. + * + * Only listed bones will be included in evaluation of animation node with this bone mask. + * Can be used in Blend2 and Add2 animation nodes. + * + * @param skeleton Skeleton of associated animation player. + * @param boneNames Array of pointers to null-terminated strings with bone names. + * @param boneNameCount Count of strings in boneNames array. + * @return Calculated bone mask, or zeroed structure on failure. + */ +R3DAPI R3D_BoneMask R3D_ComputeBoneMask(const R3D_Skeleton* skeleton, const char* boneNames[], + int boneNameCount); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of AnimationTree + +#endif // R3D_ANIMATION_TREE_H + +``` + +`include/r3d/r3d_camera.h`: + +```h +/* r3d_camera.h -- R3D Camera Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided "as-is", without any express or implied warranty. + * For conditions of distribution and use, see the accompanying LICENSE file. + */ + +#ifndef R3D_CAMERA_H +#define R3D_CAMERA_H + +#include "./r3d_platform.h" + +#include +#include +#include + +/** + * @defgroup Camera Camera + * @{ + */ + +// ======================================== +// CONSTANTS +// ======================================== + +#define R3D_CAMERA_BASE \ + R3D_LITERAL(R3D_Camera) { \ + .position = {0, 0, 0}, \ + .rotation = {0, 0, 0, 1}, \ + .fovy = 60.0, \ + .nearPlane = 0.05, \ + .farPlane = 4000.0, \ + .cullMask = R3D_LAYER_ALL, \ + .projection = R3D_PROJECTION_PERSPECTIVE, \ + } + +// ======================================== +// ENUM TYPES +// ======================================== + +/** + * @brief Projection mode used by an R3D camera. + */ +typedef enum R3D_Projection { + R3D_PROJECTION_PERSPECTIVE = 0, ///< Perspective projection. + R3D_PROJECTION_ORTHOGRAPHIC ///< Orthographic projection. +} R3D_Projection; + +// ======================================== +// BITFIELD TYPES +// ======================================== + +/** + * @brief Bitfield type used to specify rendering layers. + * + * This type is used by cameras and renderable objects to control visibility. + * A renderable object is visible to a camera when at least one bit is shared + * between the object's layer mask and the camera's layer mask. + * + * For convenience, 16 predefined layers are provided here, but the underlying + * type supports up to 32 layers. + */ +typedef uint32_t R3D_Layer; + +#define R3D_LAYER_01 (1u << 0) +#define R3D_LAYER_02 (1u << 1) +#define R3D_LAYER_03 (1u << 2) +#define R3D_LAYER_04 (1u << 3) +#define R3D_LAYER_05 (1u << 4) +#define R3D_LAYER_06 (1u << 5) +#define R3D_LAYER_07 (1u << 6) +#define R3D_LAYER_08 (1u << 7) +#define R3D_LAYER_09 (1u << 8) +#define R3D_LAYER_10 (1u << 9) +#define R3D_LAYER_11 (1u << 10) +#define R3D_LAYER_12 (1u << 11) +#define R3D_LAYER_13 (1u << 12) +#define R3D_LAYER_14 (1u << 13) +#define R3D_LAYER_15 (1u << 14) +#define R3D_LAYER_16 (1u << 15) + +#define R3D_LAYER_ALL UINT32_C(0xFFFFFFFF) + +// ======================================== +// STRUCT TYPES +// ======================================== + +/** + * @brief Quaternion-based 3D camera used by R3D. + * + * Unlike raylib's Camera3D, this camera stores its orientation directly as a + * quaternion instead of using a target/up pair. It also stores near/far clipping + * planes and a layer mask used to filter visible renderables. + */ +typedef struct R3D_Camera { + Vector3 position; ///< Camera world-space position. + Quaternion rotation; ///< Camera world-space orientation. + double fovy; ///< Vertical field of view in degrees for perspective projection; vertical size for orthographic projection. + double nearPlane; ///< Distance to the near clipping plane. + double farPlane; ///< Distance to the far clipping plane. + R3D_Layer cullMask; ///< Camera visibility layer mask. + R3D_Projection projection; ///< Camera projection mode. +} R3D_Camera; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Creates an R3D camera from a raylib camera. + * + * The camera position, orientation, field of view and projection mode are + * derived from the given raylib camera. + * + * Since raylib's Camera3D does not store near/far clipping planes or layer + * masks, the near/far planes are initialized from the current rlgl culling + * distances, while the layer mask is set to the default R3D camera layer mask. + * + * @param camera Raylib camera to convert. + * + * @return Converted R3D camera. + */ +R3DAPI R3D_Camera R3D_CameraFromRL(Camera3D camera); + +/** + * @brief Converts an R3D camera to a raylib camera. + * + * The raylib target/up vectors are derived from the camera quaternion. + * Near/far clipping planes and layer masks are not represented by Camera3D. + */ +R3DAPI Camera3D R3D_CameraToRL(R3D_Camera camera); + +/** + * @brief Sets the camera orientation so it looks at a target point. + */ +R3DAPI void R3D_CameraLookAt(R3D_Camera* camera, Vector3 target, Vector3 up); + +/** + * @brief Returns the camera forward direction in world space. + */ +R3DAPI Vector3 R3D_GetCameraForward(R3D_Camera camera); + +/** + * @brief Returns the camera right direction in world space. + */ +R3DAPI Vector3 R3D_GetCameraRight(R3D_Camera camera); + +/** + * @brief Returns the camera up direction in world space. + */ +R3DAPI Vector3 R3D_GetCameraUp(R3D_Camera camera); + +/** + * @brief Returns the camera view matrix. + */ +R3DAPI Matrix R3D_GetCameraView(R3D_Camera camera); + +/** + * @brief Returns the camera projection matrix. + */ +R3DAPI Matrix R3D_GetCameraProj(R3D_Camera camera, double aspect); + +/** + * @brief Returns the combined view-projection matrix. + */ +R3DAPI Matrix R3D_GetCameraViewProj(R3D_Camera camera, double aspect); + +/** + * @brief Moves the camera in world space. + */ +R3DAPI void R3D_MoveCamera(R3D_Camera* camera, Vector3 delta); + +/** + * @brief Moves the camera in local space. + */ +R3DAPI void R3D_MoveCameraLocal(R3D_Camera* camera, Vector3 delta); + +/** + * @brief Rotates the camera using a quaternion. + */ +R3DAPI void R3D_CameraRotate(R3D_Camera* camera, Quaternion rotation); + +/** + * @brief Rotates the camera around its local X axis. + */ +R3DAPI void R3D_CameraPitch(R3D_Camera* camera, float angle); + +/** + * @brief Rotates the camera around its local Y axis. + */ +R3DAPI void R3D_CameraYaw(R3D_Camera* camera, float angle); + +/** + * @brief Rotates the camera around its local Z axis. + */ +R3DAPI void R3D_CameraRoll(R3D_Camera* camera, float angle); + +/** + * @brief Replaces the camera culling mask. + */ +R3DAPI void R3D_SetCameraCullMask(R3D_Camera* camera, R3D_Layer cullMask); + +/** + * @brief Enables one or more layers in the camera culling mask. + */ +R3DAPI void R3D_EnableCameraCullLayers(R3D_Camera* camera, R3D_Layer layerMask); + +/** + * @brief Disables one or more layers from the camera culling mask. + */ +R3DAPI void R3D_DisableCameraCullLayers(R3D_Camera* camera, R3D_Layer layerMask); + +/** + * @brief Toggles one or more layers in the camera culling mask. + */ +R3DAPI void R3D_ToggleCameraCullLayers(R3D_Camera* camera, R3D_Layer layerMask); + +/** + * @brief Checks whether at least one object layer is visible to the camera. + */ +R3DAPI bool R3D_IsCameraLayerVisible(R3D_Camera camera, R3D_Layer layerMask); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Camera + +#endif // R3D_CAMERA_H + +``` + +`include/r3d/r3d_color.h`: + +```h +/* r3d_color.h -- R3D Color Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_COLOR_H +#define R3D_COLOR_H + +#include "./r3d_platform.h" +#include + +/** + * @defgroup Color + * @{ + */ + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Converts an sRGB color to linear space. + * + * @param color The sRGB color to convert. + * @return The converted linear color as a Vector4. + */ +R3DAPI Vector4 R3D_ColorSrgbToLinear(Color color); + +/** + * @brief Converts an sRGB color to linear space. + * + * @param color The sRGB color to convert. + * @return The converted linear color as a Vector3. + */ +R3DAPI Vector3 R3D_ColorSrgbToLinearVector3(Color color); + +/** + * @brief Converts a linear color to sRGB space. + * + * @param color The linear color to convert. + * @return The converted sRGB color. + */ +R3DAPI Color R3D_ColorLinearToSrgb(Vector4 color); + +/** + * @brief Converts a color from the current color space to linear space. + * + * @param color The color to convert. + * @return The converted linear color as a Vector4. + */ +R3DAPI Vector4 R3D_ColorFromCurrentSpace(Color color); + +/** + * @brief Converts a color from the current color space to linear space. + * + * @param color The color to convert. + * @return The converted linear color as a Vector3. + */ +R3DAPI Vector3 R3D_ColorFromCurrentSpaceVector3(Color color); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Color + +#endif // R3D_COLOR_H + +``` + +`include/r3d/r3d_core.h`: + +```h +/* r3d_core.h -- R3D Core Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_CORE_H +#define R3D_CORE_H + +#include "./r3d_platform.h" +#include +#include + +/** + * @defgroup Core + * @{ + */ + +// ======================================== +// ENUMS TYPES +// ======================================== + +/** + * @brief Anti-aliasing modes used during rendering. + * + * Anti-aliasing reduces visible jagged edges (aliasing artifacts) + * in the final rendered image. + */ +typedef enum R3D_AntiAliasingMode { + R3D_ANTI_ALIASING_MODE_NONE, ///< No anti-aliasing. Best performance, visible jagged edges. + R3D_ANTI_ALIASING_MODE_FXAA, ///< Fast Approximate AA. Smooths edges efficiently but may appear blurry. + R3D_ANTI_ALIASING_MODE_SMAA, ///< Subpixel Morphological AA. Sharper than FXAA, moderate performance cost. +} R3D_AntiAliasingMode; + +/** + * @brief Quality presets for anti-aliasing. + * + * Presets adjust internal algorithm parameters (e.g. edge detection, + * search steps, thresholds). Higher presets increase quality and GPU cost. + */ +typedef enum R3D_AntiAliasingPreset { + R3D_ANTI_ALIASING_PRESET_LOW, ///< Performance-oriented preset with reduced quality. + R3D_ANTI_ALIASING_PRESET_MEDIUM, ///< Balanced quality/performance preset. + R3D_ANTI_ALIASING_PRESET_HIGH, ///< High quality preset with increased GPU cost. + R3D_ANTI_ALIASING_PRESET_ULTRA, ///< Maximum quality preset, highest performance cost. + R3D_ANTI_ALIASING_PRESET_COUNT, ///< Number of presets (not a valid preset value). +} R3D_AntiAliasingPreset; + +/** + * @brief Aspect ratio handling modes for rendering. + */ +typedef enum R3D_AspectMode { + R3D_ASPECT_EXPAND, ///< Expands the rendered output to fully fill the target (render texture or window). + R3D_ASPECT_KEEP ///< Preserves the target's aspect ratio without distortion, adding empty gaps if necessary. +} R3D_AspectMode; + +/** + * @brief Upscaling/filtering methods for rendering output. + * + * Upscale mode to apply when the output window is larger than the internal render resolution. + */ +typedef enum R3D_UpscaleMode { + R3D_UPSCALE_NEAREST, ///< Nearest-neighbor upscaling: very fast, but produces blocky pixels. + R3D_UPSCALE_LINEAR, ///< Bilinear upscaling: very fast, smoother than nearest, but can appear blurry. + R3D_UPSCALE_BICUBIC, ///< Bicubic upscaling: slower, smoother, and less blurry than linear. + R3D_UPSCALE_LANCZOS ///< Lanczos-2 upscaling: preserves more fine details, but is the most expensive. +} R3D_UpscaleMode; + +/** + * @brief Downscaling/filtering methods for rendering output. + * + * Downscale mode to apply when the output window is smaller than the internal render resolution. + */ +typedef enum R3D_DownscaleMode { + R3D_DOWNSCALE_NEAREST, ///< Nearest-neighbor downscaling: very fast, but produces aliasing. + R3D_DOWNSCALE_LINEAR, ///< Bilinear filtering. Fast, may show moire on high-frequency content. + R3D_DOWNSCALE_RGSS, ///< 4-sample supersampling. Reduces aliasing and moire, low cost. Recommended default. + R3D_DOWNSCALE_PDSS ///< 16-sample supersampling. Better color accuracy than RGSS, higher cost. +} R3D_DownscaleMode; + +/** + * @brief Defines the buffer to output (render texture or window). + * @note Nothing will be output if the requested target has not been created / used. + */ +typedef enum R3D_OutputMode { + R3D_OUTPUT_SCENE, + R3D_OUTPUT_ALBEDO, + R3D_OUTPUT_NORMAL, + R3D_OUTPUT_ORM, + R3D_OUTPUT_DIFFUSE, + R3D_OUTPUT_SPECULAR, + R3D_OUTPUT_SSAO, + R3D_OUTPUT_SSIL, + R3D_OUTPUT_SSGI, + R3D_OUTPUT_SSR, + R3D_OUTPUT_BLOOM, + R3D_OUTPUT_DOF, +} R3D_OutputMode; + +/** + * @brief Specifies the color space for user-provided colors and color textures. + * + * This enum defines how colors are interpreted for material inputs: + * - Surface colors (e.g., albedo or emission tint) + * - Color textures (albedo, emission maps) + * + * Lighting values (direct or indirect light) are always linear and + * are not affected by this setting. + * + * Used with `R3D_SetColorSpace()` to control whether input colors + * should be treated as linear or sRGB. + */ +typedef enum R3D_ColorSpace { + R3D_COLORSPACE_LINEAR, ///< Linear color space: values are used as-is. + R3D_COLORSPACE_SRGB ///< sRGB color space: values are converted to linear on load. +} R3D_ColorSpace; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes the rendering engine. + * + * This function sets up the internal rendering system with the provided resolution. + * + * @param resWidth Width of the internal resolution. + * @param resHeight Height of the internal resolution. + * + * @return True if the initialization is successful. + */ +R3DAPI bool R3D_Init(int resWidth, int resHeight); + +/** + * @brief Closes the rendering engine and deallocates all resources. + * + * This function shuts down the rendering system and frees all allocated memory, + * including the resources associated with the created lights. + */ +R3DAPI void R3D_Close(void); + +/** + * @brief Gets the current internal resolution. + * + * This function retrieves the current internal resolution being used by the + * rendering engine. + * + * @param width Pointer to store the width of the internal resolution. + * @param height Pointer to store the height of the internal resolution. + */ +R3DAPI void R3D_GetResolution(int* width, int* height); + +/** + * @brief Sets the internal rendering resolution. + * + * Reallocates all internal render targets to the new resolution. + * This operation may cause a stall, this is acceptable when called + * infrequently (like window resize events), but should never be called per-frame. + * + * @param width New internal width in pixels. + * @param height New internal height in pixels. + */ +R3DAPI void R3D_SetResolution(int width, int height); + +/** + * @brief Retrieves the current anti-aliasing mode used for rendering. + * + * @return The currently active R3D_AntiAliasingMode. + */ +R3DAPI R3D_AntiAliasingMode R3D_GetAntiAliasingMode(void); + +/** + * @brief Sets the anti-aliasing mode for rendering. + * + * The new mode takes effect on subsequent frames. + * + * @param mode The desired R3D_AntiAliasingMode. + * @note If the mode is invalid, no AA will be applied. + */ +R3DAPI void R3D_SetAntiAliasingMode(R3D_AntiAliasingMode mode); + +/** + * @brief Retrieves the current anti-aliasing quality preset. + * + * @return The currently active R3D_AntiAliasingPreset. + */ +R3DAPI R3D_AntiAliasingPreset R3D_GetAntiAliasingPreset(void); + +/** + * @brief Sets the anti-aliasing quality preset. + * + * Changing the preset triggers an internal shader recompilation. + * Compiled variants are cached and reused if the preset is set again. + * + * @param preset The desired R3D_AntiAliasingPreset. + * @note The preset will be a clamp between low and ultra. + */ +R3DAPI void R3D_SetAntiAliasingPreset(R3D_AntiAliasingPreset preset); + +/** + * @brief Retrieves the current aspect ratio handling mode. + * @return The currently active R3D_AspectMode. + */ +R3DAPI R3D_AspectMode R3D_GetAspectMode(void); + +/** + * @brief Sets the aspect ratio handling mode for rendering. + * @param mode The desired R3D_AspectMode. + */ +R3DAPI void R3D_SetAspectMode(R3D_AspectMode mode); + +/** + * @brief Retrieves the current upscaling/filtering method. + * @return The currently active R3D_UpscaleMode. + */ +R3DAPI R3D_UpscaleMode R3D_GetUpscaleMode(void); + +/** + * @brief Sets the upscaling/filtering method for rendering output. + * @param mode The desired R3D_UpscaleMode. + */ +R3DAPI void R3D_SetUpscaleMode(R3D_UpscaleMode mode); + +/** + * @brief Retrieves the current downscaling mode used for rendering. + * @return The currently active R3D_DownscaleMode. + */ +R3DAPI R3D_DownscaleMode R3D_GetDownscaleMode(void); + +/** + * @brief Sets the downscaling mode for rendering output. + * @param mode The desired R3D_DownscaleMode. + */ +R3DAPI void R3D_SetDownscaleMode(R3D_DownscaleMode mode); + +/** + * @brief Gets the current output mode. + * @return The currently active R3D_OutputMode. + */ +R3DAPI R3D_OutputMode R3D_GetOutputMode(void); + +/** + * @brief Sets the output mode for rendering. + * @param mode The R3D_OutputMode to use. + * @note Nothing will be output if the requested target has not been created / used. + */ +R3DAPI void R3D_SetOutputMode(R3D_OutputMode mode); + +/** + * @brief Sets the default texture filtering mode. + * + * This function defines the default texture filter that will be applied to all subsequently + * loaded textures, including those used in materials, sprites, and other resources. + * + * If a trilinear or anisotropic filter is selected, mipmaps will be automatically generated + * for the textures, but they will not be generated when using nearest or bilinear filtering. + * + * The default texture filter mode is `TEXTURE_FILTER_TRILINEAR`. + * + * @param filter The texture filtering mode to be applied by default. + */ +R3DAPI void R3D_SetTextureFilter(TextureFilter filter); + +/** + * @brief Sets the default texture wrap mode. + * + * This function only affects textures that are loaded manually for material maps. + * Textures loaded automatically during model import will use the wrap mode + * defined in the model file itself. + * + * The default texture wrap mode is `TEXTURE_WRAP_CLAMP`. + * + * @param wrap The texture wrap mode to apply by default. + */ +R3DAPI void R3D_SetTextureWrap(TextureWrap wrap); + +/** + * @brief Set the working color space for user-provided surface colors and color textures. + * + * Defines how all *color inputs* should be interpreted: + * - surface colors provided in materials (e.g. albedo/emission tints) + * - color textures such as albedo and emission maps + * + * When set to sRGB, these values are converted to linear before shading. + * When set to linear, values are used as-is. + * + * This does NOT affect lighting inputs (direct or indirect light), + * which are always expected to be provided in linear space. + * + * The default color space is `R3D_COLORSPACE_SRGB`. + * + * @param space Color space to use for color inputs (linear or sRGB). + */ +R3DAPI void R3D_SetColorSpace(R3D_ColorSpace space); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Core + +#endif // R3D_CORE_H + +``` + +`include/r3d/r3d_cubemap.h`: + +```h +/* r3d_cubemap.h -- R3D Cubemap Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_CUBEMAP_H +#define R3D_CUBEMAP_H + +#include "./r3d_platform.h" +#include +#include + +/** + * @defgroup Cubemap + * @{ + */ + +// ======================================== +// ENUM TYPES +// ======================================== + +/** + * @brief Supported cubemap source layouts. + * + * Used when converting an image into a cubemap. AUTO_DETECT tries to guess + * the layout based on image dimensions. + */ +typedef enum R3D_CubemapLayout { + R3D_CUBEMAP_LAYOUT_AUTO_DETECT = 0, ///< Automatically detect layout type + R3D_CUBEMAP_LAYOUT_LINE_VERTICAL, ///< Layout is defined by a vertical line with faces + R3D_CUBEMAP_LAYOUT_LINE_HORIZONTAL, ///< Layout is defined by a horizontal line with faces + R3D_CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR, ///< Layout is defined by a 3x4 cross with cubemap faces + R3D_CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE, ///< Layout is defined by a 4x3 cross with cubemap faces + R3D_CUBEMAP_LAYOUT_PANORAMA ///< Layout is defined by an equirectangular panorama +} R3D_CubemapLayout; + +// ======================================== +// STRUCT TYPES +// ======================================== + +/** + * @brief GPU cubemap texture. + * + * Holds the OpenGL texture handle and its base resolution (per face). + */ +typedef struct R3D_Cubemap { + uint32_t texture; + uint32_t fbo; + int size; +} R3D_Cubemap; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Loads a cubemap from an image file. + * + * The layout parameter tells how faces are arranged inside the source image. + */ +R3DAPI R3D_Cubemap R3D_LoadCubemap(const char* fileName, R3D_CubemapLayout layout); + +/** + * @brief Builds a cubemap from an existing Image. + * + * Same behavior as R3D_LoadCubemap(), but without loading from disk. + */ +R3DAPI R3D_Cubemap R3D_LoadCubemapFromImage(Image image, R3D_CubemapLayout layout); + +/** + * @brief Releases GPU resources associated with a cubemap. + */ +R3DAPI void R3D_UnloadCubemap(R3D_Cubemap cubemap); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Cubemap + +#endif // R3D_CUBEMAP_H + +``` + +`include/r3d/r3d_decal.h`: + +```h +/* r3d_decal.h -- R3D Decal Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_DECAL_H +#define R3D_DECAL_H + +#include "./r3d_surface_shader.h" +#include "./r3d_platform.h" +#include "./r3d_material.h" + +/** + * @defgroup Decal + * @{ + */ + +// ======================================== +// CONSTANTS +// ======================================== + +/** + * @brief Default decal configuration. + * + * Contains a R3D_Decal structure with sensible default values for all rendering parameters. + */ +#define R3D_DECAL_BASE \ + R3D_LITERAL(R3D_Decal) { \ + .albedo = { \ + .texture = {0}, \ + .color = {255, 255, 255, 255}, \ + }, \ + .emission = { \ + .texture = {0}, \ + .color = {255, 255, 255, 255}, \ + .energy = 0.0f, \ + }, \ + .normal = { \ + .texture = {0}, \ + .scale = 1.0f, \ + }, \ + .orm = { \ + .texture = {0}, \ + .occlusion = 1.0f, \ + .roughness = 1.0f, \ + .metalness = 0.0f, \ + }, \ + .uvOffset = {0.0f, 0.0f}, \ + .uvScale = {1.0f, 1.0f}, \ + .alphaCutoff = 0.01f, \ + .normalThreshold = 0, \ + .fadeWidth = 0, \ + .applyColor = true, \ + .shader = 0 \ + } + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Represents a decal and its properties. + * + * This structure defines a decal that can be projected onto geometry that has already been rendered. + * + * @note Decals are drawn using deferred screen space rendering and do not interact with any + * forward rendered or non-opaque objects. + */ +typedef struct R3D_Decal { + R3D_AlbedoMap albedo; ///< Albedo map (if the texture is undefined, implicitly treat `applyColor` as false, with alpha = 1.0) + R3D_EmissionMap emission; ///< Emission map + R3D_NormalMap normal; ///< Normal map + R3D_OrmMap orm; ///< Occlusion-Roughness-Metalness map + Vector2 uvOffset; ///< UV offset (default: {0.0f, 0.0f}) + Vector2 uvScale; ///< UV scale (default: {1.0f, 1.0f}) + float alphaCutoff; ///< Alpha cutoff threshold (default: 0.01f) + float normalThreshold; ///< Maximum angle against the surface normal to draw decal. 0.0f disables threshold. (default: 0.0f) + float fadeWidth; ///< The width of fading along the normal threshold (default: 0.0f) + bool applyColor; ///< Indicates that the albedo color will not be rendered, only the alpha component of the albedo will be used as a mask. (default: true) + R3D_SurfaceShader* shader; ///< Custom shader applied to the decal (default: NULL) +} R3D_Decal; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Unload all map textures assigned to a R3D_Decal. + * + * Frees all underlying textures in a R3D_Decal that are not a default texture. + * + * @param decal to unload maps from. + */ +R3DAPI void R3D_UnloadDecalMaps(R3D_Decal decal); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Decal + +#endif // R3D_DECAL_H + +``` + +`include/r3d/r3d_draw.h`: + +```h +/* r3d_draw.h -- R3D Draw Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_DRAW_H +#define R3D_DRAW_H + +#include "./r3d_animation_player.h" +#include "./r3d_instance.h" +#include "./r3d_platform.h" +#include "./r3d_camera.h" +#include "./r3d_model.h" +#include "./r3d_decal.h" +#include + +/** + * @defgroup Draw + * @{ + */ + +// ======================================== +// STRUCT TYPES +// ======================================== + +/** + * @brief Describes an R3D rendering view. + * + * A view defines the camera and output area used for a single rendering + * session. It combines an R3D camera, an optional render target and an optional + * viewport inside that target. + * + * If `target` is zero-initialized, rendering is directed to the default + * framebuffer. If `viewport.width` or `viewport.height` is less than or equal + * to zero, the full target size is used. + */ +typedef struct R3D_View { + R3D_Camera camera; ///< Camera used for this view. + RenderTexture target; ///< Render target. Zero-initialized means screen/backbuffer. + Rectangle viewport; ///< Viewport inside the target. If width or height <= 0, the full target is used. +} R3D_View; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Begins a rendering session using the given raylib camera. + * + * Rendering output is directed to the default framebuffer. + * + * The given Camera3D is converted internally to an R3D_Camera. Since raylib + * cameras do not store near/far clipping planes, the converted camera uses the + * current rlgl culling distances for those values. + * + * @param camera Camera used to render the scene. + */ +R3DAPI void R3D_Begin(Camera3D camera); + +/** + * @brief Begins a rendering session using an R3D camera. + * + * Rendering output is directed to the default framebuffer. + * + * This entry point provides access to R3D-specific camera features such as + * layer masks, custom near/far clipping planes and quaternion-based orientation. + * + * @param camera Camera used to render the scene. + */ +R3DAPI void R3D_BeginEx(R3D_Camera camera); + +/** + * @brief Begins a rendering session using a complete R3D view descriptor. + * + * This is the advanced entry point. It allows the caller to specify the camera, + * render target and viewport used for the rendering session. + * + * The view camera is used as-is, including its near/far clipping planes and + * layer mask. If the camera was created from a raylib Camera3D using + * `R3D_CameraFromRL()`, its near/far planes come from the current rlgl culling + * distances because raylib cameras do not store those values directly. + * + * Use this function for render-to-texture workflows, custom viewports, + * multipass rendering, editor views, minimaps, probes or any case where the + * default framebuffer is not enough. + * + * @param view View descriptor used to render the scene. + */ +R3DAPI void R3D_BeginPro(R3D_View view); + +/** + * @brief Ends the current rendering session. + * + * This function is the one that actually performs the full + * rendering of the described scene. It carries out culling, + * sorting, shadow rendering, scene rendering, and screen / + * post-processing effects. + */ +R3DAPI void R3D_End(void); + +/** + * @brief Begins a clustered draw pass. + * + * All draw calls submitted in this pass are first tested against the + * cluster AABB. If the cluster fails the scene/shadow frustum test, + * none of the contained objects are tested or drawn. + * + * @param aabb Bounding box used as the cluster-level frustum test. + */ +R3DAPI void R3D_BeginCluster(BoundingBox aabb); + +/** + * @brief Ends the current clustered draw pass. + * + * Stops submitting draw calls to the active cluster. + */ +R3DAPI void R3D_EndCluster(void); + +/** + * @brief Queues a mesh draw command with position and uniform scale. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawMesh(R3D_Mesh mesh, R3D_Material material, Vector3 position, float scale); + +/** + * @brief Queues a mesh draw command with position, rotation and non-uniform scale. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawMeshEx(R3D_Mesh mesh, R3D_Material material, Vector3 position, Quaternion rotation, Vector3 scale); + +/** + * @brief Queues a mesh draw command using a full transform matrix. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawMeshPro(R3D_Mesh mesh, R3D_Material material, Matrix transform); + +/** + * @brief Queues an instanced mesh draw command. + * + * Draws multiple instances using the provided instance buffer. + * Does nothing if the number of instances is <= 0. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawMeshInstanced(R3D_Mesh mesh, R3D_Material material, R3D_InstanceBuffer instances, int count); + +/** + * @brief Queues an instanced mesh draw command with an instance range. + * + * Draws 'count' instances starting at 'offset' in the instance buffer. + * Both 'offset' and 'count' are clamped to stay within [0, instances.capacity]: + * - offset is clamped to [0, capacity] + * - count is clamped to [0, capacity - offset] + * Does nothing if the resulting count is <= 0. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawMeshInstancedEx(R3D_Mesh mesh, R3D_Material material, R3D_InstanceBuffer instances, int offset, int count); + +/** + * @brief Queues an instanced mesh draw command with an instance range and an additional transform. + * + * Draws 'count' instances starting at 'offset' in the instance buffer. + * Both 'offset' and 'count' are clamped to stay within [0, instances.capacity]: + * - offset is clamped to [0, capacity] + * - count is clamped to [0, capacity - offset] + * Does nothing if the resulting count is <= 0. + * The transform is applied to all instances. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawMeshInstancedPro(R3D_Mesh mesh, R3D_Material material, R3D_InstanceBuffer instances, int offset, int count, Matrix transform); + +/** + * @brief Queues a model draw command with position and uniform scale. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawModel(R3D_Model model, Vector3 position, float scale); + +/** + * @brief Queues a model draw command with position, rotation and non-uniform scale. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawModelEx(R3D_Model model, Vector3 position, Quaternion rotation, Vector3 scale); + +/** + * @brief Queues a model draw command using a full transform matrix. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawModelPro(R3D_Model model, Matrix transform); + +/** + * @brief Queues an instanced model draw command. + * + * Draws multiple instances using the provided instance buffer. + * Does nothing if the number of instances is <= 0. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawModelInstanced(R3D_Model model, R3D_InstanceBuffer instances, int count); + +/** + * @brief Queues an instanced model draw command with an instance range. + * + * Draws 'count' instances starting at 'offset' in the instance buffer. + * Both 'offset' and 'count' are clamped to stay within [0, instances.capacity]: + * - offset is clamped to [0, capacity] + * - count is clamped to [0, capacity - offset] + * Does nothing if the resulting count is <= 0. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawModelInstancedEx(R3D_Model model, R3D_InstanceBuffer instances, int offset, int count); + +/** + * @brief Queues an instanced model draw command with an instance range and an additional transform. + * + * Draws 'count' instances starting at 'offset' in the instance buffer. + * Both 'offset' and 'count' are clamped to stay within [0, instances.capacity]: + * - offset is clamped to [0, capacity] + * - count is clamped to [0, capacity - offset] + * Does nothing if the resulting count is <= 0. + * The transform is applied to all instances. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawModelInstancedPro(R3D_Model model, R3D_InstanceBuffer instances, int offset, int count, Matrix transform); + +/** + * @brief Queues an animated model draw command. + * + * Uses the provided animation player to compute the pose. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawAnimatedModel(R3D_Model model, R3D_AnimationPlayer player, Vector3 position, float scale); + +/** + * @brief Queues an animated model draw command with position, rotation and non-uniform scale. + * + * Uses the provided animation player to compute the pose. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawAnimatedModelEx(R3D_Model model, R3D_AnimationPlayer player, Vector3 position, Quaternion rotation, Vector3 scale); + +/** + * @brief Queues an animated model draw command using a full transform matrix. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawAnimatedModelPro(R3D_Model model, R3D_AnimationPlayer player, Matrix transform); + +/** + * @brief Queues an instanced animated model draw command. + * + * Draws multiple animated instances using the provided instance buffer. + * Does nothing if the number of instances is <= 0. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawAnimatedModelInstanced(R3D_Model model, R3D_AnimationPlayer player, R3D_InstanceBuffer instances, int count); + +/** + * @brief Queues an instanced animated model draw command with an instance range. + * + * Draws 'count' animated instances starting at 'offset' in the instance buffer. + * Both 'offset' and 'count' are clamped to stay within [0, instances.capacity]: + * - offset is clamped to [0, capacity] + * - count is clamped to [0, capacity - offset] + * Does nothing if the resulting count is <= 0. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawAnimatedModelInstancedEx(R3D_Model model, R3D_AnimationPlayer player, R3D_InstanceBuffer instances, int offset, int count); + +/** + * @brief Queues an instanced animated model draw command with an instance range and an additional transform. + * + * Draws 'count' animated instances starting at 'offset' in the instance buffer. + * Both 'offset' and 'count' are clamped to stay within [0, instances.capacity]: + * - offset is clamped to [0, capacity] + * - count is clamped to [0, capacity - offset] + * Does nothing if the resulting count is <= 0. + * The transform is applied to all instances. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawAnimatedModelInstancedPro(R3D_Model model, R3D_AnimationPlayer player, R3D_InstanceBuffer instances, int offset, int count, Matrix transform); + +/** + * @brief Queues a decal draw command with position and uniform scale. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawDecal(R3D_Decal decal, Vector3 position, float scale); + +/** + * @brief Queues a decal draw command with position, rotation and non-uniform scale. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawDecalEx(R3D_Decal decal, Vector3 position, Quaternion rotation, Vector3 scale); + +/** + * @brief Queues a decal draw command using a full transform matrix. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawDecalPro(R3D_Decal decal, Matrix transform); + +/** + * @brief Queues an instanced decal draw command. + * + * Draws multiple instances using the provided instance buffer. + * Does nothing if the number of instances is <= 0. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawDecalInstanced(R3D_Decal decal, R3D_InstanceBuffer instances, int count); + +/** + * @brief Queues an instanced decal draw command with an instance range. + * + * Draws 'count' instances starting at 'offset' in the instance buffer. + * Both 'offset' and 'count' are clamped to stay within [0, instances.capacity]: + * - offset is clamped to [0, capacity] + * - count is clamped to [0, capacity - offset] + * Does nothing if the resulting count is <= 0. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawDecalInstancedEx(R3D_Decal decal, R3D_InstanceBuffer instances, int offset, int count); + +/** + * @brief Queues an instanced decal draw command with an instance range and an additional transform. + * + * Draws 'count' instances starting at 'offset' in the instance buffer. + * Both 'offset' and 'count' are clamped to stay within [0, instances.capacity]: + * - offset is clamped to [0, capacity] + * - count is clamped to [0, capacity - offset] + * Does nothing if the resulting count is <= 0. + * The transform is applied to all instances. + * + * The command is executed during R3D_End(). + */ +R3DAPI void R3D_DrawDecalInstancedPro(R3D_Decal decal, R3D_InstanceBuffer instances, int offset, int count, Matrix transform); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Draw + +#endif // R3D_DRAW_H + +``` + +`include/r3d/r3d_environment.h`: + +```h +/* r3d_environment.h -- R3D Environment Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_ENVIRONMENT_H +#define R3D_ENVIRONMENT_H + +#include "./r3d_ambient_map.h" +#include "./r3d_platform.h" +#include "./r3d_cubemap.h" + +/** + * @defgroup Environment + * @brief Scene rendering and post-processing configuration. + * + * Controls background, lighting, and visual effects through the R3D_Environment structure. + * Includes skybox, ambient light, SSAO, bloom, SSR, fog, depth of field, tone mapping, + * and color grading. + * + * ## Usage + * + * ```c + * // Direct access + * R3D_Environment* env = R3D_GetEnvironment(); + * env->bloom.intensity = 0.8f; + * + * // Quick macro update + * R3D_ENVIRONMENT_SET(bloom.intensity, 0.8f); + * + * // Batch configuration + * R3D_Environment custom = R3D_ENVIRONMENT_BASE; + * custom.bloom.mode = R3D_BLOOM_ADDITIVE; + * custom.ssao.enabled = true; + * R3D_SetEnvironment(&custom); + * ``` + * + * @{ + */ + +// ======================================== +// CONSTANTS +// ======================================== + +/** + * @brief Default environment configuration. + * + * Initializes an R3D_Environment structure with sensible default values for all + * rendering parameters. Use this as a starting point for custom configurations. + */ +#define R3D_ENVIRONMENT_BASE \ + R3D_LITERAL(R3D_Environment) { \ + .background = { \ + .color = GRAY, \ + .energy = 1.0f, \ + .skyBlur = 0.0f, \ + .sky = {0}, \ + .rotation = {0.0f, 0.0f, 0.0f, 1.0f}, \ + }, \ + .ambient = { \ + .color = BLACK, \ + .energy = 1.0f, \ + .map = {0}, \ + }, \ + .ssao = { \ + .sampleCount = 16, \ + .intensity = 1.0f, \ + .power = 1.0f, \ + .maxRadius = 0.2f, \ + .radius = 1.0f, \ + .bias = 0.03f, \ + .enabled = false, \ + }, \ + .ssil = { \ + .sampleCount = 16, \ + .giIntensity = 1.0f, \ + .aoIntensity = 1.0f, \ + .aoPower = 1.0f, \ + .maxRadius = 0.2f, \ + .radius = 4.0f, \ + .bias = 0.03f, \ + .enabled = false, \ + }, \ + .ssgi = { \ + .sliceCount = 4, \ + .edgeFade = 0.1f, \ + .distanceFalloff = 1.0f, \ + .normalRejection = 0.0f, \ + .intensity = 1.0f, \ + .denoiseSteps = 4, \ + .enabled = false, \ + }, \ + .ssr = { \ + .maxRaySteps = 32, \ + .binarySteps = 4, \ + .stepSize = 0.125f, \ + .thickness = 0.2f, \ + .maxDistance = 4.0f, \ + .edgeFade = 0.25f, \ + .enabled = false, \ + }, \ + .fog = { \ + .mode = R3D_FOG_DISABLED, \ + .color = {255, 255, 255, 255}, \ + .start = 1.0f, \ + .end = 50.0f, \ + .density = 0.05f, \ + .skyAffect = 0.5f, \ + }, \ + .dof = { \ + .mode = R3D_DOF_DISABLED, \ + .focusPoint = 10.0f, \ + .focusScale = 1.0f, \ + .nearScale = 1.0f, \ + .maxBlurSize = 20.0f, \ + }, \ + .bloom = { \ + .mode = R3D_BLOOM_DISABLED, \ + .levels = 0.5f, \ + .intensity = 0.05f, \ + .threshold = 0.0f, \ + .softThreshold = 0.5f, \ + .filterRadius = 1.0f, \ + }, \ + .autoExposure = { \ + .minEV = -1.0f, \ + .maxEV = 1.0f, \ + .exposureCompensation = 0.0f, \ + .adaptationToBright = 0.5f, \ + .adaptationToDark = 1.0f, \ + }, \ + .tonemap = { \ + .mode = R3D_TONEMAP_LINEAR, \ + .exposure = 1.0f, \ + .white = 1.0f, \ + }, \ + .color = { \ + .brightness = 1.0f, \ + .contrast = 1.0f, \ + .saturation = 1.0f, \ + }, \ + } + +// ======================================== +// ENUMS TYPES +// ======================================== + +/** + * @brief Bloom effect modes. + * + * Different blending methods for the bloom glow effect. + */ +typedef enum R3D_Bloom { + R3D_BLOOM_DISABLED, ///< No bloom effect applied + R3D_BLOOM_MIX, ///< Linear interpolation blend between scene and bloom + R3D_BLOOM_ADDITIVE, ///< Additive blending, intensifying bright regions + R3D_BLOOM_SCREEN ///< Screen blending for softer highlight enhancement +} R3D_Bloom; + +/** + * @brief Fog effect modes. + * + * Distance-based fog density distribution methods. + */ +typedef enum R3D_Fog { + R3D_FOG_DISABLED, ///< No fog effect + R3D_FOG_LINEAR, ///< Linear density increase between start and end distances + R3D_FOG_EXP2, ///< Exponential squared density (exp2), more realistic + R3D_FOG_EXP ///< Simple exponential density increase +} R3D_Fog; + +/** + * @brief Depth of field modes. + */ +typedef enum R3D_DoF { + R3D_DOF_DISABLED, ///< No depth of field effect + R3D_DOF_ENABLED ///< Depth of field enabled with focus point and blur +} R3D_DoF; + +/** + * @brief Tone mapping algorithms. + * + * HDR to LDR color compression methods. + */ +typedef enum R3D_Tonemap { + R3D_TONEMAP_LINEAR, ///< Direct linear mapping (no compression) + R3D_TONEMAP_REINHARD, ///< Reinhard operator, balanced HDR compression + R3D_TONEMAP_FILMIC, ///< Film-like response curve + R3D_TONEMAP_ACES, ///< Academy Color Encoding System (cinematic standard) + R3D_TONEMAP_AGX, ///< Modern algorithm preserving highlights and shadows + R3D_TONEMAP_COUNT ///< Internal: number of tonemap modes +} R3D_Tonemap; + +// ======================================== +// STRUCT TYPES +// ======================================== + +/** + * @brief Background and skybox configuration. + */ +typedef struct R3D_EnvBackground { + Color color; ///< Background color when there is no skybox + float energy; ///< Energy multiplier applied to background (skybox or color) + float skyBlur; ///< Sky blur factor [0,1], based on mipmaps, very fast + R3D_Cubemap sky; ///< Skybox asset (used if ID is non-zero) + Quaternion rotation; ///< Skybox rotation (pitch, yaw, roll as quaternion) +} R3D_EnvBackground; + +/** + * @brief Ambient lighting configuration. + */ +typedef struct R3D_EnvAmbient { + Color color; ///< Ambient light color when there is no ambient map + float energy; ///< Energy multiplier for ambient light (map or color) + R3D_AmbientMap map; ///< IBL environment map, can be generated from skybox +} R3D_EnvAmbient; + +/** + * @brief Screen Space Ambient Occlusion (SSAO) settings. + * + * Darkens areas where surfaces are close together, such as corners and crevices. + */ +typedef struct R3D_EnvSSAO { + int sampleCount; ///< Number of samples to compute SSAO (default: 16) + float intensity; ///< Base occlusion strength multiplier (default: 1.0) + float power; ///< Exponential falloff for sharper darkening (default: 1.0) + float maxRadius; ///< Fraction of screen height beyond which the sampling radius is clamped (default: 0.2) + float radius; ///< Sampling radius in world space (default: 1.0) + float bias; ///< Depth bias to prevent self-occlusion artifacts, in world-space units (default: 0.03) + bool enabled; ///< Enable/disable SSAO effect (default: false) +} R3D_EnvSSAO; + +/** + * @brief Screen Space Indirect Lighting (SSIL) settings. + * + * Extends the SSAO algorithm with a global illumination component: occluding + * surfaces not only darken the fragment (ambient occlusion) but also transfer + * their color to it (indirect light bounce). A larger radius than SSAO is + * generally preferable to capture meaningful indirect lighting contributions. + */ +typedef struct R3D_EnvSSIL { + int sampleCount; ///< Number of samples to compute SSIL (default: 16) + float giIntensity; ///< Indirect light strength multiplier (default: 1.0) + float aoIntensity; ///< Ambient occlusion strength multiplier (default: 1.0) + float aoPower; ///< Exponential falloff for sharper occlusion darkening (default: 1.0) + float maxRadius; ///< Fraction of screen height beyond which the sampling radius is clamped (default: 0.2) + float radius; ///< Sampling radius in world space (default: 4.0) + float bias; ///< Depth bias to prevent self-occlusion artifacts, in world-space units (default: 0.03) + bool enabled; ///< Enable/disable SSIL effect (default: false) +} R3D_EnvSSIL; + +/** + * @brief Screen Space Global Illumination (SSGI) settings. + * + * Computes indirect lighting from the scene's visible surfaces in real time. + */ +typedef struct R3D_EnvSSGI { + int sliceCount; ///< Number of directions sampled per pixel. Higher = fewer noise streaks, higher cost. (default: 4) + float edgeFade; ///< Fades out GI near screen edges to hide emissive objects partially off-screen. (default: 0.1) + float distanceFalloff; ///< How quickly indirect light fades with distance. Higher = shorter reach, darker result. (default: 1.0) + float normalRejection; ///< Prevents surfaces from receiving light through their own backside. 0 = off, 1 = physically correct. May look inconsistent with non-directional emissives. (default: 0.0) + float intensity; ///< Brightness of the indirect lighting. Dimly lit scenes may require significantly higher values to show probable contribution. (default: 1.0) + int denoiseSteps; ///< Number of denoiser passes. Higher = smoother result, slightly higher cost. (default: 4) + bool enabled; ///< Enable or disable SSGI entirely. (default: false) +} R3D_EnvSSGI; + +/** + * @brief Screen Space Reflections (SSR) settings. + * + * Real-time reflections calculated in screen space. + */ +typedef struct R3D_EnvSSR { + int maxRaySteps; ///< Maximum ray marching steps (default: 32) + int binarySteps; ///< Binary search refinement steps (default: 4) + float stepSize; ///< Ray step size (default: 0.125) + float thickness; ///< Depth tolerance for valid hits (default: 0.2) + float maxDistance; ///< Maximum ray distance (default: 4.0) + float edgeFade; ///< Screen edge fade start [0,1] (default: 0.25) + bool enabled; ///< Enable/disable SSR (default: false) +} R3D_EnvSSR; + +/** + * @brief Fog atmospheric effect settings. + */ +typedef struct R3D_EnvFog { + R3D_Fog mode; ///< Fog distribution mode (default: R3D_FOG_DISABLED) + Color color; ///< Fog tint color (default: white) + float start; ///< Linear mode: distance where fog begins (default: 1.0) + float end; ///< Linear mode: distance of full fog density (default: 50.0) + float density; ///< Exponential modes: fog thickness factor (default: 0.05) + float skyAffect; ///< Fog influence on skybox [0-1] (default: 0.5) +} R3D_EnvFog; + +/** + * @brief Depth of Field (DoF) camera focus settings. + * + * Blurs objects outside the focal plane. + */ +typedef struct R3D_EnvDoF { + R3D_DoF mode; ///< Enable/disable state (default: R3D_DOF_DISABLED) + float focusPoint; ///< Focus distance in meters from camera (default: 10.0) + float focusScale; ///< Depth of field depth: lower = shallower (default: 1.0) + float nearScale; ///< Near blur intensity: 0.0 = disabled, 1.0 = symmetric to far (default: 1.0) + float maxBlurSize; ///< Maximum blur radius, similar to aperture (default: 20.0) +} R3D_EnvDoF; + +/** + * @brief Bloom post-processing settings. + * + * Glow effect around bright areas in the scene. + */ +typedef struct R3D_EnvBloom { + R3D_Bloom mode; ///< Bloom blending mode (default: R3D_BLOOM_DISABLED) + float levels; ///< Mipmap spread factor [0-1]: higher = wider glow (default: 0.5) + float intensity; ///< Bloom strength multiplier (default: 0.05) + float threshold; ///< Minimum brightness to trigger bloom (default: 0.0) + float softThreshold; ///< Softness of brightness cutoff transition (default: 0.5) + float filterRadius; ///< Blur filter radius during upscaling (default: 1.0) +} R3D_EnvBloom; + +/** + * @brief Auto exposure post-processing settings. + * + * Automatically adjusts scene exposure from average luminance, + * simulating eye adaptation. Adaptation should physically be + * faster toward bright scenes than toward dark scenes, + * as dark adaptation is slower. + * + * @warning Current implementation keeps a single temporal history. Enabling + * auto exposure for multiple scene passes in the same frame, or across + * different scenes, may produce incorrect adaptation. For now, use it only on + * one continuous begin/end scene render path. + */ +typedef struct R3D_EnvAutoExposure { + float minEV; ///< Minimum measured luminance in EV stops, relative to middle gray (default: -1.0) + float maxEV; ///< Maximum measured luminance in EV stops, relative to middle gray (default: 1.0) + float exposureCompensation; ///< Artistic exposure bias in EV stops: +1 = one stop brighter (default: 0.0) + float adaptationToBright; ///< Time constant in seconds when scene luminance increases; lower = faster (default: 0.5) + float adaptationToDark; ///< Time constant in seconds when scene luminance decreases; lower = faster (default: 1.0) + bool enabled; ///< Enable auto exposure (default: false) +} R3D_EnvAutoExposure; + +/** + * @brief Tone mapping and exposure settings. + * + * Converts HDR colors to displayable LDR range. + */ +typedef struct R3D_EnvTonemap { + R3D_Tonemap mode; ///< Tone mapping algorithm (default: R3D_TONEMAP_LINEAR) + float exposure; ///< Scene brightness multiplier (default: 1.0) + float white; ///< Reference white point (not used for AGX) (default: 1.0) +} R3D_EnvTonemap; + +/** + * @brief Color grading adjustments. + * + * Final color correction applied after all other effects. + */ +typedef struct R3D_EnvColor { + float brightness; ///< Overall brightness multiplier (default: 1.0) + float contrast; ///< Contrast between dark and bright areas (default: 1.0) + float saturation; ///< Color intensity (default: 1.0) +} R3D_EnvColor; + +/** + * @brief Complete environment configuration structure. + * + * Contains all rendering environment parameters: background, lighting, and post-processing effects. + * Initialize with R3D_ENVIRONMENT_BASE for default values. + */ +typedef struct R3D_Environment { + R3D_EnvBackground background; ///< Background and skybox settings + R3D_EnvAmbient ambient; ///< Ambient lighting configuration + R3D_EnvSSAO ssao; ///< Screen space ambient occlusion + R3D_EnvSSIL ssil; ///< Screen space indirect lighting + R3D_EnvSSGI ssgi; ///< Screen space global illumination + R3D_EnvSSR ssr; ///< Screen space reflections + R3D_EnvFog fog; ///< Atmospheric fog + R3D_EnvDoF dof; ///< Depth of field focus effect + R3D_EnvBloom bloom; ///< Bloom glow effect + R3D_EnvAutoExposure autoExposure; ///< Auto exposure effect + R3D_EnvTonemap tonemap; ///< HDR tone mapping + R3D_EnvColor color; ///< Color grading adjustments +} R3D_Environment; + +// ======================================== +// HELPER MACROS +// ======================================== + +/** + * @brief Quick read access to environment members. + * + * @param member The environment member path (e.g., bloom.intensity) + * @return The current value of the specified member + * + * Example: `float intensity = R3D_ENVIRONMENT_GET(bloom.intensity);` + */ +#define R3D_ENVIRONMENT_GET(member) (R3D_GetEnvironment()->member) + +/** + * @brief Quick write access to environment members. + * + * @param member The environment member path (e.g., bloom.intensity) + * @param ... The value to assign + * + * Example: `R3D_ENVIRONMENT_SET(bloom.intensity, 0.05f);` + */ +#define R3D_ENVIRONMENT_SET(member, ...) (R3D_GetEnvironment()->member = (__VA_ARGS__)) + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Retrieves a pointer to the current environment configuration. + * + * Provides direct read/write access to environment settings. + * Modifications take effect immediately. + * + * @return Pointer to the active R3D_Environment structure + */ +R3DAPI R3D_Environment* R3D_GetEnvironment(void); + +/** + * @brief Replaces the entire environment configuration. + * + * Copies all settings from the provided structure to the active environment. + * Useful for switching between presets or restoring saved states. + * + * @param env Pointer to the R3D_Environment structure to copy from + */ +R3DAPI void R3D_SetEnvironment(const R3D_Environment* env); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Environment + +#endif // R3D_ENVIRONMENT_H + +``` + +`include/r3d/r3d_frustum.h`: + +```h +/* r3d_frustum.h -- R3D Frustum Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_FRUSTUM_H +#define R3D_FRUSTUM_H + +#include "./r3d_platform.h" +#include "./r3d_shape.h" +#include + +/** + * @defgroup Frustum Frustum + * @brief Frustum extraction and intersection tests. + * + * @note + * R3D_Draw* functions already perform frustum culling internally. + * This module is intended for advanced use cases (e.g. instancing or custom culling). + * + * Be careful when manually culling objects: visibility depends on context. + * An object outside the camera frustum may still contribute to shadows or probes. + * Avoid removing instances solely based on camera visibility. + * + * @{ + */ + +// ======================================== +// ENUM TYPES +// ======================================== + +/** + * @brief Frustum plane indices. + */ +typedef enum { + R3D_PLANE_BACK = 0, + R3D_PLANE_FRONT, + R3D_PLANE_BOTTOM, + R3D_PLANE_TOP, + R3D_PLANE_RIGHT, + R3D_PLANE_LEFT, + R3D_PLANE_COUNT +} R3D_FrustumPlane; + +// ======================================== +// STRUCT TYPES +// ======================================== + +/** + * @brief View frustum defined by its clipping planes. + * + * Planes are stored as Vector4 (xyz = normal, w = distance). + */ +typedef struct { + Vector4 planes[R3D_PLANE_COUNT]; +} R3D_Frustum; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get the current frustum. + * + * Returns the frustum computed during the last call to R3D_Begin(). + */ +R3DAPI R3D_Frustum R3D_GetFrustum(void); + +/** + * @brief Compute a frustum from a view-projection matrix. + */ +R3DAPI R3D_Frustum R3D_ComputeFrustum(Matrix viewProj); + +/** + * @brief Compute the axis-aligned bounding box of a frustum. + * + * @param invViewProj Inverse view-projection matrix. + */ +R3DAPI BoundingBox R3D_ComputeFrustumBoundingBox(Matrix invViewProj); + +/** + * @brief Compute the eight corner points of a frustum. + * + * @param invViewProj Inverse view-projection matrix. + * @param corners Output array of 8 points. + */ +R3DAPI void R3D_ComputeFrustumCorners(Matrix invViewProj, Vector3 corners[8]); + +/** + * @brief Check if a point is inside the frustum. + * + * @param frustum Frustum to test against. Must not be NULL. + */ +R3DAPI bool R3D_FrustumContainsPoint(const R3D_Frustum* frustum, Vector3 position); + +/** + * @brief Check if any point from a set is inside the frustum. + * + * @param frustum Frustum to test against. Must not be NULL. + */ +R3DAPI bool R3D_FrustumContainsAnyPoint(const R3D_Frustum* frustum, const Vector3* positions, int count); + +/** + * @brief Check if a sphere intersects the frustum. + * + * @param frustum Frustum to test against. Must not be NULL. + */ +R3DAPI bool R3D_FrustumIntersectsSphere(const R3D_Frustum* frustum, Vector3 position, float radius); + +/** + * @brief Check if a bounding box intersects the frustum. + * + * @param frustum Frustum to test against. Must not be NULL. + */ +R3DAPI bool R3D_FrustumIntersectsBoundingBox(const R3D_Frustum* frustum, BoundingBox aabb); + +/** + * @brief Check if an oriented box intersects the frustum. + * + * @param frustum Frustum to test against. Must not be NULL. + */ +R3DAPI bool R3D_FrustumIntersectsOrientedBox(const R3D_Frustum* frustum, R3D_OrientedBox obb); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Frustum + +#endif // R3D_FRUSTUM_H + +``` + +`include/r3d/r3d_importer.h`: + +```h +/* r3d_importer.h -- R3D Importer Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_IMPORTER_H +#define R3D_IMPORTER_H + +#include "./r3d_platform.h" +#include + +/** + * @defgroup Importer + * @{ + */ + +// ======================================== +// ENUMS TYPES +// ======================================== + +/** + * @typedef R3D_ImportFlags + * @brief Flags controlling importer behavior. + * + * These flags define how the importer processes the source asset. + */ +typedef uint32_t R3D_ImportFlags; + +/** + * @brief Keep a CPU-side copy of mesh data. + * + * When enabled, raw mesh data is preserved in RAM after model import and stored in @c meshData. + */ +#define R3D_IMPORT_MESH_DATA (1 << 0) + +/** + * @brief Import and store mesh names from the model file. + * + * When enabled, mesh names are retrieved during import and stored in @c meshNames. + */ +#define R3D_IMPORT_MESH_NAMES (1 << 1) + +/** + * @brief Enable high-quality import processing. + * + * When enabled, the importer uses a higher-quality post-processing + * (e.g. smooth normals, mesh optimization, data validation). + * This mode is intended for editor usage and offline processing. + * + * When disabled, a faster import preset is used, suitable for runtime. + */ +#define R3D_IMPORT_QUALITY (1 << 2) + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Opaque importer handle. + * + * Represents a loaded asset file that can be used to extract multiple + * resources (models, skeletons, animations) without re-importing the file. + */ +typedef struct R3D_Importer R3D_Importer; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Load an importer from a file. + * + * Creates an importer instance from the specified file path. + * The file is parsed once and can be reused to extract multiple + * resources such as models and animations. + * + * @param filePath Path to the asset file. + * @param flags Importer behavior flags. + * + * @return Pointer to a new importer instance, or NULL on failure. + */ +R3DAPI R3D_Importer* R3D_LoadImporter(const char* filePath, R3D_ImportFlags flags); + +/** + * @brief Load an importer from a memory buffer. + * + * Creates an importer instance from in-memory asset data. + * This is useful for embedded assets or streamed content. + * + * @param data Pointer to the asset data. + * @param size Size of the data buffer in bytes. + * @param hint Optional file format hint (may be NULL). + * @param flags Importer behavior flags. + * + * @return Pointer to a new importer instance, or NULL on failure. + */ +R3DAPI R3D_Importer* R3D_LoadImporterFromMemory(const void* data, unsigned int size, const char* hint, R3D_ImportFlags flags); + +/** + * @brief Destroy an importer instance. + * + * Frees all resources associated with the importer. + * Any models or animations extracted from it remain valid. + * + * @param importer Importer instance to destroy. + */ +R3DAPI void R3D_UnloadImporter(R3D_Importer* importer); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Importer + +#endif // R3D_IMPORTER_H + +``` + +`include/r3d/r3d_instance.h`: + +```h +/* r3d_instance.h -- R3D Instance Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_INSTANCE_H +#define R3D_INSTANCE_H + +#include "./r3d_platform.h" +#include +#include + +/** + * @defgroup Instance + * @{ + */ + +// ======================================== +// CONSTANTS +// ======================================== + +#define R3D_INSTANCE_ATTRIBUTE_COUNT 5 + +// ======================================== +// ENUM / FLAGS +// ======================================== + +/** + * @brief Bitmask defining which instance attributes are present. + * + * Each bit enables one per-instance attribute stream. Attribute data is stored + * in separate GPU buffers, one buffer per enabled attribute. + * + * The memory format of each enabled attribute is defined by `R3D_InstanceLayout`. + * In shaders, these attributes are exposed as floating-point vectors, including + * normalized integer formats such as UNORM/SNORM. + */ +typedef uint32_t R3D_InstanceFlags; + +#define R3D_INSTANCE_POSITION (1u << 0) /*< Position attribute: 3 components */ +#define R3D_INSTANCE_ROTATION (1u << 1) /*< Rotation attribute: 4 components (quaternion) */ +#define R3D_INSTANCE_SCALE (1u << 2) /*< Scale attribute: 3 components */ +#define R3D_INSTANCE_COLOR (1u << 3) /*< Color attribute: 4 components */ +#define R3D_INSTANCE_CUSTOM (1u << 4) /*< Custom attribute: 4 components */ + +/** + * @brief Storage format used by an instance attribute. + * + * The selected format controls how the attribute is stored in GPU memory. + * All formats are read by the shader as floating-point values. + * + * UNORM formats are converted to the [0, 1] range. + * SNORM formats are converted to the [-1, 1] range. + */ +typedef enum R3D_InstanceFormat { + R3D_INSTANCE_FORMAT_FLOAT32, ///< 32-bit floating-point component. + R3D_INSTANCE_FORMAT_FLOAT16, ///< 16-bit floating-point component. + R3D_INSTANCE_FORMAT_UNORM16, ///< 16-bit unsigned normalized component. + R3D_INSTANCE_FORMAT_SNORM16, ///< 16-bit signed normalized component. + R3D_INSTANCE_FORMAT_UNORM8, ///< 8-bit unsigned normalized component. + R3D_INSTANCE_FORMAT_SNORM8, ///< 8-bit signed normalized component. + + R3D_INSTANCE_FORMAT_COUNT ///< Number of available instance formats. +} R3D_InstanceFormat; + +// ======================================== +// STRUCT TYPES +// ======================================== + +/** + * @brief Describes the layout of an instance buffer. + * + * `flags` defines which instance attributes are allocated. + * `formats` defines the storage format used by each attribute. + * + * The `formats` array is indexed in the same order as the instance attribute + * flags: + * + * - index 0: `R3D_INSTANCE_POSITION` + * - index 1: `R3D_INSTANCE_ROTATION` + * - index 2: `R3D_INSTANCE_SCALE` + * - index 3: `R3D_INSTANCE_COLOR` + * - index 4: `R3D_INSTANCE_CUSTOM` + * + * Data uploaded or mapped for an attribute must match the format selected for + * that attribute. + */ +typedef struct R3D_InstanceLayout { + R3D_InstanceFormat formats[R3D_INSTANCE_ATTRIBUTE_COUNT]; ///< Storage format for each instance attribute. + R3D_InstanceFlags flags; ///< Enabled instance attribute mask. +} R3D_InstanceLayout; + +/** + * @brief GPU buffers storing instance attribute streams. + * + * Each enabled attribute owns one GPU buffer. The enabled attributes and their + * storage formats are described by `layout`. + */ +typedef struct R3D_InstanceBuffer { + uint32_t buffers[R3D_INSTANCE_ATTRIBUTE_COUNT]; ///< One GPU buffer per attribute, indexed by attribute order. + R3D_InstanceLayout layout; ///< Instance buffer layout. + int capacity; ///< Maximum number of instances. +} R3D_InstanceBuffer; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Creates instance buffers on the GPU using the default layout. + * + * This is the simple entry point. It allocates one GPU buffer for each enabled + * attribute in `flags`. + * + * Default formats: + * + * - position: FLOAT32 + * - rotation: FLOAT32 + * - scale: FLOAT32 + * - color: UNORM8 + * - custom: FLOAT32 + * + * @param capacity Maximum number of instances. + * @param flags Attribute mask to allocate. + * + * @return Initialized instance buffer, or an empty buffer on failure. + */ +R3DAPI R3D_InstanceBuffer R3D_LoadInstanceBuffer(int capacity, R3D_InstanceFlags flags); + +/** + * @brief Creates instance buffers on the GPU using a custom layout. + * + * This is the advanced entry point. It allocates one GPU buffer for each + * enabled attribute in `layout.flags`, using the corresponding storage format + * from `layout.formats`. + * + * Data uploaded or mapped for each attribute must match the format selected in + * the layout. + * + * @param capacity Maximum number of instances. + * @param layout Instance layout describing enabled attributes and formats. + * + * @return Initialized instance buffer, or an empty buffer on failure. + */ +R3DAPI R3D_InstanceBuffer R3D_LoadInstanceBufferEx(int capacity, R3D_InstanceLayout layout); + +/** + * @brief Destroy all GPU buffers owned by this instance buffer. + */ +R3DAPI void R3D_UnloadInstanceBuffer(R3D_InstanceBuffer buffer); + +/** + * @brief Grow the GPU buffers of an instance buffer to a new capacity. + * + * Only expands; if newCapacity <= buffer->capacity the call is a no-op. + * All attribute buffers present in buffer->flags are reallocated and + * if keepData is true, their existing content is copied to the new + * buffers before the old ones are deleted. + * + * @param buffer Instance buffer to resize (updated in place). + * @param newCapacity Desired minimum capacity in number of instances. + * @param keepData If true, preserves existing instance data. + */ +R3DAPI void R3D_ResizeInstanceBuffer(R3D_InstanceBuffer* buffer, int newCapacity, bool keepData); + +/** + * @brief Upload a contiguous range of instance data to a GPU buffer. + * + * @param buffer Instance buffer containing the target GPU buffer. + * @param flag Attribute to update (single bit). + * @param offset First instance index. + * @param count Number of instances to upload. + * @param data Source data pointer. + * @param discard If true, the entire GPU buffer is orphaned before upload, + * avoiding a GPU/CPU sync at the cost of discarding existing data. + * Safe to use when rewriting the full buffer each frame. + */ +R3DAPI void R3D_UploadInstances(R3D_InstanceBuffer buffer, R3D_InstanceFlags flag, + int offset, int count, const void* data, bool discard); + +/** + * @brief Map an entire attribute buffer for CPU write access. + * + * Call R3D_UnmapInstances when done writing. + * + * @param buffer Instance buffer containing the target GPU buffer. + * @param flag Attribute to map (single bit). + * @param discard If true, existing buffer contents are invalidated, + * allowing the driver to return a fresh memory region + * without stalling on in-flight GPU reads. + * @return Writable pointer to the full buffer, or NULL on error. + */ +R3DAPI void* R3D_MapInstances(R3D_InstanceBuffer buffer, R3D_InstanceFlags flag, bool discard); + +/** + * @brief Map a sub-range of an attribute buffer for CPU write access. + * + * Prefer this over R3D_MapInstances for partial updates to avoid + * touching unrelated data. Call R3D_UnmapInstances when done writing. + * + * @param buffer Instance buffer containing the target GPU buffer. + * @param flag Attribute to map (single bit). + * @param offset First instance index of the mapped range. + * @param count Number of instances to map. + * @param discard If true, the mapped range is invalidated, allowing the + * driver to skip syncing that region with in-flight GPU reads. + * @return Writable pointer to the mapped range, or NULL on error. + */ +R3DAPI void* R3D_MapInstancesEx(R3D_InstanceBuffer buffer, R3D_InstanceFlags flag, + int offset, int count, bool discard); + +/** + * @brief Unmap one or more previously mapped attribute buffers. + * @param flags Bitmask of attributes to unmap. + */ +R3DAPI void R3D_UnmapInstances(R3D_InstanceBuffer buffer, R3D_InstanceFlags flags); + +/** + * @brief Sets the storage format of an instance attribute in a layout. + * + * `attribute` must be a single instance attribute flag, such as + * `R3D_INSTANCE_POSITION`, not a combination of multiple flags. + * + * This function only changes the format stored in the layout. It does not + * enable the attribute in `layout.flags`. + * + * @param layout Layout to modify. + * @param attribute Single attribute flag to modify. + * @param format New storage format. + */ +R3DAPI void R3D_SetInstanceFormat(R3D_InstanceLayout* layout, R3D_InstanceFlags attribute, R3D_InstanceFormat format); + +/** + * @brief Gets the storage format of an instance attribute from a layout. + * + * `attribute` must be a single instance attribute flag, such as + * `R3D_INSTANCE_POSITION`, not a combination of multiple flags. + * + * @param layout Layout to read from. + * @param attribute Single attribute flag to query. + * + * @return Storage format of the requested attribute, or FLOAT32 if the + * attribute flag is invalid. + */ +R3DAPI R3D_InstanceFormat R3D_GetInstanceFormat(R3D_InstanceLayout layout, R3D_InstanceFlags attribute); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Instance + +#endif // R3D_INSTANCE_H + +``` + +`include/r3d/r3d_kinematics.h`: + +```h +/* r3d_kinematics.h -- R3D Kinematics Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_KINEMATICS_H +#define R3D_KINEMATICS_H + +#include "./r3d_mesh_data.h" +#include "./r3d_shape.h" + +/** + * @defgroup Kinematics + * @brief Shape state modifiers (movement resolution, depenetration, sliding, sweeping). + * + * Read-only queries on shapes belong to the Shape module. + * @{ + */ + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Collision information from a sweep test + */ +typedef struct R3D_SweepCollision { + bool hit; ///< Whether a collision occurred + float time; ///< Time of impact [0-1], fraction along velocity vector + Vector3 point; ///< World space collision point + Vector3 normal; ///< Surface normal at collision point +} R3D_SweepCollision; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Remove the normal component from a velocity vector (project onto a plane) + * @param velocity Incoming velocity + * @param normal Surface normal (must be normalized) + * @return Velocity with the component along normal removed + */ +R3DAPI Vector3 R3D_ClipVelocity(Vector3 velocity, Vector3 normal); + +/** + * @brief Reflect a velocity vector off a surface + * @param velocity Incoming velocity + * @param normal Surface normal (must be normalized) + * @param bounciness Coefficient of restitution (0=no bounce, 1=perfect bounce) + * @return Reflected velocity scaled by bounciness + */ +R3DAPI Vector3 R3D_ReflectVelocity(Vector3 velocity, Vector3 normal, float bounciness); + +/** + * @brief Resolve a velocity vector against a sweep collision, sliding along the hit surface + * @param velocity Desired movement vector + * @param collision Sweep collision result to resolve against + * @param outNormal Optional: receives the collision normal if a hit occurred + * @return Resolved velocity (safe movement + clipped remainder) + */ +R3DAPI Vector3 R3D_SlideVelocity(Vector3 velocity, R3D_SweepCollision collision, Vector3* outNormal); + +/** + * @brief Slide sphere along bounding box surface, resolving collisions + * @param center Sphere center position + * @param radius Sphere radius + * @param velocity Desired movement vector + * @param box Obstacle bounding box + * @param outNormal Optional: receives collision normal if collision occurred + * @return Actual movement applied (may be reduced/redirected by collision) + */ +R3DAPI Vector3 R3D_SlideSphereBoundingBox(Vector3 center, float radius, Vector3 velocity, R3D_BoundingBox box, Vector3* outNormal); + +/** + * @brief Slide sphere along mesh surface, resolving collisions + * @param center Sphere center position + * @param radius Sphere radius + * @param velocity Desired movement vector + * @param mesh Mesh data to collide against + * @param transform Mesh world transform + * @param outNormal Optional: receives collision normal if collision occurred + * @return Actual movement applied (may be reduced/redirected by collision) + */ +R3DAPI Vector3 R3D_SlideSphereMesh(Vector3 center, float radius, Vector3 velocity, R3D_MeshData mesh, Matrix transform, Vector3* outNormal); + +/** + * @brief Slide capsule along bounding box surface, resolving collisions + * @param capsule Capsule shape + * @param velocity Desired movement vector + * @param box Obstacle bounding box + * @param outNormal Optional: receives collision normal if collision occurred + * @return Actual movement applied (may be reduced/redirected by collision) + */ +R3DAPI Vector3 R3D_SlideCapsuleBoundingBox(R3D_Capsule capsule, Vector3 velocity, R3D_BoundingBox box, Vector3* outNormal); + +/** + * @brief Slide capsule along mesh surface, resolving collisions + * @param capsule Capsule shape + * @param velocity Desired movement vector + * @param mesh Mesh data to collide against + * @param transform Mesh world transform + * @param outNormal Optional: receives collision normal if collision occurred + * @return Actual movement applied (may be reduced/redirected by collision) + */ +R3DAPI Vector3 R3D_SlideCapsuleMesh(R3D_Capsule capsule, Vector3 velocity, R3D_MeshData mesh, Matrix transform, Vector3* outNormal); + +/** + * @brief Push sphere out of bounding box if penetrating + * @param center Sphere center (modified in place if penetrating) + * @param radius Sphere radius + * @param box Obstacle box + * @param outPenetration Optional: receives penetration depth + * @return true if depenetration occurred + */ +R3DAPI bool R3D_DepenetrateSphereBoundingBox(Vector3* center, float radius, R3D_BoundingBox box, float* outPenetration); + +/** + * @brief Push capsule out of bounding box if penetrating + * @param capsule Capsule shape (modified in place if penetrating) + * @param box Obstacle box + * @param outPenetration Optional: receives penetration depth + * @return true if depenetration occurred + */ +R3DAPI bool R3D_DepenetrateCapsuleBoundingBox(R3D_Capsule* capsule, R3D_BoundingBox box, float* outPenetration); + +/** + * @brief Check if a sphere is supported by a bounding box in a given direction + * @param center Sphere center + * @param radius Sphere radius + * @param direction Ray direction to probe (must be normalized) + * @param distance Maximum probe distance beyond the sphere surface + * @param box Bounding box to test against + * @param outHit Optional: receives raycast hit info + * @return true if a surface is within reach in the given direction + */ +R3DAPI bool R3D_CheckSphereSupportBoundingBox(Vector3 center, float radius, Vector3 direction, float distance, R3D_BoundingBox box, RayCollision* outHit); + +/** + * @brief Check if a sphere is supported by mesh geometry in a given direction + * @param center Sphere center + * @param radius Sphere radius + * @param direction Ray direction to probe (must be normalized) + * @param distance Maximum probe distance beyond the sphere surface + * @param mesh Mesh data to test against + * @param transform Mesh world transform + * @param outHit Optional: receives raycast hit info + * @return true if a surface is within reach in the given direction + */ +R3DAPI bool R3D_CheckSphereSupportMesh(Vector3 center, float radius, Vector3 direction, float distance, R3D_MeshData mesh, Matrix transform, RayCollision* outHit); + +/** + * @brief Check if a capsule is supported by a bounding box in a given direction + * @param capsule Capsule shape + * @param direction Ray direction to probe (must be normalized) + * @param distance Maximum probe distance beyond the capsule surface + * @param box Bounding box to test against + * @param outHit Optional: receives raycast hit info + * @return true if a surface is within reach in the given direction + */ +R3DAPI bool R3D_CheckCapsuleSupportBoundingBox(R3D_Capsule capsule, Vector3 direction, float distance, R3D_BoundingBox box, RayCollision* outHit); + +/** + * @brief Check if a capsule is supported by mesh geometry in a given direction + * @param capsule Capsule shape + * @param direction Ray direction to probe (must be normalized) + * @param distance Maximum probe distance beyond the capsule surface + * @param mesh Mesh data to test against + * @param transform Mesh world transform + * @param outHit Optional: receives raycast hit info + * @return true if a surface is within reach in the given direction + */ +R3DAPI bool R3D_CheckCapsuleSupportMesh(R3D_Capsule capsule, Vector3 direction, float distance, R3D_MeshData mesh, Matrix transform, RayCollision* outHit); + +/** + * @brief Sweep sphere against single point + * @param center Sphere center position + * @param radius Sphere radius + * @param velocity Movement vector (direction and magnitude) + * @param point Point to test against + * @return Sweep collision info (hit, time, point, normal) + */ +R3DAPI R3D_SweepCollision R3D_SweepSpherePoint(Vector3 center, float radius, Vector3 velocity, Vector3 point); + +/** + * @brief Sweep sphere against line segment + * @param center Sphere center position + * @param radius Sphere radius + * @param velocity Movement vector (direction and magnitude) + * @param a Segment start point + * @param b Segment end point + * @return Sweep collision info (hit, time, point, normal) + */ +R3DAPI R3D_SweepCollision R3D_SweepSphereSegment(Vector3 center, float radius, Vector3 velocity, Vector3 a, Vector3 b); + +/** + * @brief Sweep sphere against triangle plane (no edge/vertex clipping) + * @param center Sphere center position + * @param radius Sphere radius + * @param velocity Movement vector (direction and magnitude) + * @param a Triangle vertex A + * @param b Triangle vertex B + * @param c Triangle vertex C + * @return Sweep collision info (hit, time, point, normal) + */ +R3DAPI R3D_SweepCollision R3D_SweepSphereTrianglePlane(Vector3 center, float radius, Vector3 velocity, Vector3 a, Vector3 b, Vector3 c); + +/** + * @brief Sweep sphere against triangle with edge/vertex handling + * @param center Sphere center position + * @param radius Sphere radius + * @param velocity Movement vector (direction and magnitude) + * @param a Triangle vertex A + * @param b Triangle vertex B + * @param c Triangle vertex C + * @return Sweep collision info (hit, time, point, normal) + */ +R3DAPI R3D_SweepCollision R3D_SweepSphereTriangle(Vector3 center, float radius, Vector3 velocity, Vector3 a, Vector3 b, Vector3 c); + +/** + * @brief Sweep sphere along velocity vector + * @param center Sphere center position + * @param radius Sphere radius + * @param velocity Movement vector (direction and magnitude) + * @param box Obstacle bounding box + * @return Sweep collision info (hit, distance, point, normal) + */ +R3DAPI R3D_SweepCollision R3D_SweepSphereBoundingBox(Vector3 center, float radius, Vector3 velocity, R3D_BoundingBox box); + +/** + * @brief Sweep sphere along velocity vector against mesh geometry + * @param center Sphere center position + * @param radius Sphere radius + * @param velocity Movement vector (direction and magnitude) + * @param mesh Mesh data to test against + * @param transform Mesh world transform + * @return Sweep collision info (hit, time, point, normal) + */ +R3DAPI R3D_SweepCollision R3D_SweepSphereMesh(Vector3 center, float radius, Vector3 velocity, R3D_MeshData mesh, Matrix transform); + +/** + * @brief Sweep capsule along velocity vector + * @param capsule Capsule shape to sweep + * @param velocity Movement vector (direction and magnitude) + * @param box Obstacle bounding box + * @return Sweep collision info (hit, distance, point, normal) + */ +R3DAPI R3D_SweepCollision R3D_SweepCapsuleBoundingBox(R3D_Capsule capsule, Vector3 velocity, R3D_BoundingBox box); + +/** + * @brief Sweep capsule along velocity vector against mesh geometry + * @param capsule Capsule shape to sweep + * @param velocity Movement vector (direction and magnitude) + * @param mesh Mesh data to test against + * @param transform Mesh world transform + * @return Sweep collision info (hit, time, point, normal) + */ +R3DAPI R3D_SweepCollision R3D_SweepCapsuleMesh(R3D_Capsule capsule, Vector3 velocity, R3D_MeshData mesh, Matrix transform); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Kinematics + +#endif // R3D_KINEMATICS_H + +``` + +`include/r3d/r3d_lighting.h`: + +```h +/* r3d_lighting.h -- R3D Lighting Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_LIGHTING_H +#define R3D_LIGHTING_H + +#include "./r3d_platform.h" +#include "./r3d_camera.h" +#include +#include + +/** + * @defgroup Lighting + * @{ + */ + +// ======================================== +// ENUMS TYPES +// ======================================== + +/** + * @brief Types of lights supported by the rendering engine. + * + * Each light type has different behaviors and use cases. + */ +typedef enum R3D_LightType { + R3D_LIGHT_DIR, ///< Directional light, affects the entire scene with parallel rays. + R3D_LIGHT_SPOT, ///< Spot light, emits light in a cone shape. + R3D_LIGHT_OMNI, ///< Omni light, emits light in all directions from a single point. + R3D_LIGHT_TYPE_COUNT +} R3D_LightType; + +/** + * @brief Modes for updating shadow maps. + * + * Determines how often the shadow maps are refreshed. + */ +typedef enum R3D_ShadowUpdateMode { + R3D_SHADOW_UPDATE_MANUAL, ///< Shadow maps update only when explicitly requested. + R3D_SHADOW_UPDATE_INTERVAL, ///< Shadow maps update at defined time intervals. + R3D_SHADOW_UPDATE_CONTINUOUS ///< Shadow maps update every frame for real-time accuracy. +} R3D_ShadowUpdateMode; + +// ======================================== +// HANDLES TYPES +// ======================================== + +/** + * @brief Unique identifier for an R3D light. + * + * ID type used to reference a light. + * A negative value indicates an invalid light. + */ +typedef int32_t R3D_Light; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +// ---------------------------------------- +// LIGHTING: Lights Config Functions +// ---------------------------------------- + +/** + * @brief Creates a new light of the specified type. + * + * This function creates a light of the given type. The light must be destroyed + * manually when no longer needed by calling `R3D_DestroyLight`. + * + * @param type The type of light to create (directional, spot or omni-directional). + * @return The ID of the created light. + */ +R3DAPI R3D_Light R3D_CreateLight(R3D_LightType type); + +/** + * @brief Destroys the specified light. + * + * This function deallocates the resources associated with the light and makes + * the light ID invalid. It must be called after the light is no longer needed. + * + * @param id The ID of the light to destroy. + */ +R3DAPI void R3D_DestroyLight(R3D_Light id); + +/** + * @brief Checks if a light exists. + * + * This function checks if the specified light ID is valid and if the light exists. + * + * @param id The ID of the light to check. + * @return True if the light exists, false otherwise. + */ +R3DAPI bool R3D_IsLightExist(R3D_Light id); + +/** + * @brief Gets the type of a light. + * + * This function returns the type of the specified light (directional, spot or omni-directional). + * + * @param id The ID of the light. + * @return The type of the light. + */ +R3DAPI R3D_LightType R3D_GetLightType(R3D_Light id); + +/** + * @brief Checks if a light is active. + * + * This function checks whether the specified light is currently active (enabled or disabled). + * + * @param id The ID of the light to check. + * @return True if the light is active, false otherwise. + */ +R3DAPI bool R3D_IsLightActive(R3D_Light id); + +/** + * @brief Toggles the state of a light (active or inactive). + * + * This function toggles the state of the specified light, turning it on if it is off, + * or off if it is on. + * + * @param id The ID of the light to toggle. + */ +R3DAPI void R3D_ToggleLight(R3D_Light id); + +/** + * @brief Sets the active state of a light. + * + * This function allows manually turning a light on or off by specifying its active state. + * + * @param id The ID of the light to set the active state for. + * @param active True to activate the light, false to deactivate it. + */ +R3DAPI void R3D_SetLightActive(R3D_Light id, bool active); + +/** + * @brief Gets the color of a light. + * + * This function retrieves the color of the specified light as a `Color` structure. + * + * @param id The ID of the light. + * @return The color of the light as a `Color` structure. + */ +R3DAPI Color R3D_GetLightColor(R3D_Light id); + +/** + * @brief Gets the color of a light as a `Vector3`. + * + * This function retrieves the color of the specified light as a `Vector3`, where each + * component (x, y, z) represents the RGB values of the light. + * + * @param id The ID of the light. + * @return The color of the light as a `Vector3`. + */ +R3DAPI Vector3 R3D_GetLightColorV(R3D_Light id); + +/** + * @brief Sets the color of a light. + * + * This function sets the color of the specified light using a `Color` structure. + * + * @param id The ID of the light. + * @param color The new color to set for the light. + */ +R3DAPI void R3D_SetLightColor(R3D_Light id, Color color); + +/** + * @brief Sets the color of a light using a `Vector3`. + * + * This function sets the color of the specified light using a `Vector3`, where each + * component (x, y, z) represents the RGB values of the light. + * + * @param id The ID of the light. + * @param color The new color to set for the light as a `Vector3`. + */ +R3DAPI void R3D_SetLightColorV(R3D_Light id, Vector3 color); + +/** + * @brief Gets the position of a light. + * + * This function retrieves the position of the specified light. + * Only applicable to spot lights or omni-lights. + * + * @param id The ID of the light. + * @return The position of the light as a `Vector3`. + */ +R3DAPI Vector3 R3D_GetLightPosition(R3D_Light id); + +/** + * @brief Sets the position of a light. + * + * This function sets the position of the specified light. + * Only applicable to spot lights or omni-lights. + * + * @note Has no effect for directional lights. + * If called on a directional light, + * a warning will be logged. + * + * @param id The ID of the light. + * @param position The new position to set for the light. + */ +R3DAPI void R3D_SetLightPosition(R3D_Light id, Vector3 position); + +/** + * @brief Gets the direction of a light. + * + * This function retrieves the direction of the specified light. + * Only applicable to directional lights or spot lights. + * + * @param id The ID of the light. + * @return The direction of the light as a `Vector3`. + */ +R3DAPI Vector3 R3D_GetLightDirection(R3D_Light id); + +/** + * @brief Sets the direction of a light. + * + * This function sets the direction of the specified light. + * Only applicable to directional lights or spot lights. + * + * @note Has no effect for omni-directional lights. + * If called on an omni-directional light, + * a warning will be logged. + * + * @param id The ID of the light. + * @param direction The new direction to set for the light. + * The vector is automatically normalized. + */ +R3DAPI void R3D_SetLightDirection(R3D_Light id, Vector3 direction); + +/** + * @brief Sets the position and direction of a light to look at a target point. + * + * This function sets both the position and the direction of the specified light, + * causing it to "look at" a given target point. + * + * @note - For directional lights, only the direction is updated (position is ignored). + * - For omni-directional lights, only the position is updated (direction is not calculated). + * - For spot lights, both position and direction are set accordingly. + * - This function does **not** emit any warning or log message. + * + * @param id The ID of the light. + * @param position The position to set for the light. + * @param target The point the light should look at. + */ +R3DAPI void R3D_LightLookAt(R3D_Light id, Vector3 position, Vector3 target); + +/** + * @brief Gets the energy level of a light. + * + * This function retrieves the energy level (intensity) of the specified light. + * Energy typically affects the brightness of the light. + * + * @param id The ID of the light. + * @return The energy level of the light. + */ +R3DAPI float R3D_GetLightEnergy(R3D_Light id); + +/** + * @brief Sets the energy level of a light. + * + * This function sets the energy (intensity) of the specified light. + * A higher energy value will result in a brighter light. + * + * @param id The ID of the light. + * @param energy The new energy value to set for the light. + */ +R3DAPI void R3D_SetLightEnergy(R3D_Light id, float energy); + +/** + * @brief Gets the specular intensity of a light. + * + * This function retrieves the current specular intensity of the specified light. + * Specular intensity affects how shiny surfaces appear when reflecting the light. + * + * @param id The ID of the light. + * @return The current specular intensity of the light. + */ +R3DAPI float R3D_GetLightSpecular(R3D_Light id); + +/** + * @brief Sets the specular intensity of a light. + * + * This function sets the specular intensity of the specified light. + * Higher specular values result in stronger and sharper highlights on reflective surfaces. + * + * @param id The ID of the light. + * @param specular The new specular intensity value to set for the light. + */ +R3DAPI void R3D_SetLightSpecular(R3D_Light id, float specular); + +/** + * @brief Gets the range of a light. + * + * This function retrieves the range of the specified light, which determines how far the light can affect. + * Only applicable to spot lights or omni-lights. + * + * @param id The ID of the light. + * @return The range of the light. + */ +R3DAPI float R3D_GetLightRange(R3D_Light id); + +/** + * @brief Sets the range parameter of a light. + * + * For spot and omni lights, this defines the maximum illumination distance. + * For directional lights, this defines the shadow rendering radius around the camera. + * + * @param id The ID of the light. + * @param range The range value to apply. + */ +R3DAPI void R3D_SetLightRange(R3D_Light id, float range); + +/** + * @brief Gets the attenuation factor of a light. + * + * This function retrieves the attenuation factor of the specified light. + * Attenuation controls how the intensity of a light decreases with distance. + * Only applicable to spot lights or omni-lights. + * + * @param id The ID of the light. + * @return The attenuation factor of the light. + */ +R3DAPI float R3D_GetLightAttenuation(R3D_Light id); + +/** + * @brief Sets the attenuation factor of a light. + * + * This function sets the attenuation factor of the specified light. + * A higher attenuation value causes the light to lose intensity more quickly as the distance increases. + * For a realistic effect, an attenuation factor of 2.0f is typically used. + * Only applicable to spot lights or omni-lights. + * + * @param id The ID of the light. + * @param attenuation The new attenuation factor to set for the light. + */ +R3DAPI void R3D_SetLightAttenuation(R3D_Light id, float attenuation); + +/** + * @brief Gets the inner cutoff angle of a spotlight. + * + * This function retrieves the inner cutoff angle of a spotlight. + * The inner cutoff defines the cone of light where the light is at full intensity. + * + * @param id The ID of the light. + * @return The inner cutoff angle in degrees of the spotlight. + */ +R3DAPI float R3D_GetLightInnerCutOff(R3D_Light id); + +/** + * @brief Sets the inner cutoff angle of a spotlight. + * + * This function sets the inner cutoff angle of a spotlight. + * The inner cutoff angle defines the cone where the light is at full intensity. + * Anything outside this cone starts to fade. + * + * @param id The ID of the light. + * @param degrees The new inner cutoff angle in degrees. + */ +R3DAPI void R3D_SetLightInnerCutOff(R3D_Light id, float degrees); + +/** + * @brief Gets the outer cutoff angle of a spotlight. + * + * This function retrieves the outer cutoff angle of a spotlight. + * The outer cutoff defines the outer boundary of the light's cone, where the light starts to fade. + * + * @param id The ID of the light. + * @return The outer cutoff angle in degrees of the spotlight. + */ +R3DAPI float R3D_GetLightOuterCutOff(R3D_Light id); + +/** + * @brief Sets the outer cutoff angle of a spotlight. + * + * This function sets the outer cutoff angle of a spotlight. + * The outer cutoff defines the boundary of the light's cone where the light intensity starts to gradually decrease. + * + * @param id The ID of the light. + * @param degrees The new outer cutoff angle in degrees. + */ +R3DAPI void R3D_SetLightOuterCutOff(R3D_Light id, float degrees); + +// ---------------------------------------- +// LIGHTING: Shadow Config Functions +// ---------------------------------------- + +/** + * @brief Enables shadow rendering for a light. + * + * Turns on shadow rendering for the light. The engine will allocate a shadow + * map if needed, or reuse one previously allocated for another light. + * + * Shadow map resolutions are fixed: 2048x2048 for spot and point lights, + * and 4096x4096 for directional lights. + * + * @param id The ID of the light. + * + * @note Creating too many shadow-casting lights can exhaust GPU memory and + * potentially crash the graphics driver. Disabling shadows on one light and + * enabling them on another is free, since existing shadow maps are reused. + */ +R3DAPI void R3D_EnableShadow(R3D_Light id); + +/** + * @brief Disables shadow rendering for a light. + * + * Turns off shadow rendering for the light. The associated shadow map is + * kept in memory and may later be reused by another light. + * + * @param id The ID of the light. + */ +R3DAPI void R3D_DisableShadow(R3D_Light id); + +/** + * @brief Checks if shadow casting is enabled for a light. + * + * This function checks if shadow casting is currently enabled for the specified light. + * + * @param id The ID of the light. + * @return True if shadow casting is enabled, false otherwise. + */ +R3DAPI bool R3D_IsShadowEnabled(R3D_Light id); + +/** + * @brief Gets the shadow map update mode of a light. + * + * This function retrieves the current mode for updating the shadow map of a light. The mode can be: + * - Interval: Updates the shadow map at a fixed interval. + * - Continuous: Updates the shadow map continuously. + * - Manual: Updates the shadow map manually (via explicit function calls). + * + * @param id The ID of the light. + * @return The shadow map update mode. + */ +R3DAPI R3D_ShadowUpdateMode R3D_GetShadowUpdateMode(R3D_Light id); + +/** + * @brief Sets the shadow map update mode of a light. + * + * This function sets the mode for updating the shadow map of the specified light. + * The update mode controls when and how often the shadow map is refreshed. + * + * @param id The ID of the light. + * @param mode The update mode to set for the shadow map (Interval, Continuous, or Manual). + */ +R3DAPI void R3D_SetShadowUpdateMode(R3D_Light id, R3D_ShadowUpdateMode mode); + +/** + * @brief Gets the frequency of shadow map updates for the interval update mode. + * + * This function retrieves the frequency (in milliseconds) at which the shadow map should be updated when + * the interval update mode is enabled. This function is only relevant if the shadow map update mode is set + * to "Interval". + * + * @param id The ID of the light. + * @return The frequency in milliseconds at which the shadow map is updated. + */ +R3DAPI int R3D_GetShadowUpdateFrequency(R3D_Light id); + +/** + * @brief Sets the frequency of shadow map updates for the interval update mode. + * + * This function sets the frequency (in milliseconds) at which the shadow map should be updated when + * the interval update mode is enabled. This function is only relevant if the shadow map update mode is set + * to "Interval". + * + * @param id The ID of the light. + * @param msec The frequency in milliseconds at which to update the shadow map. + */ +R3DAPI void R3D_SetShadowUpdateFrequency(R3D_Light id, int msec); + +/** + * @brief Forces an immediate update of the shadow map during the next rendering pass. + * + * This function forces the shadow map of the specified light to be updated during the next call to `R3D_End`. + * This is primarily used for the manual update mode, but may also work for the interval mode. + * + * @param id The ID of the light. + */ +R3DAPI void R3D_UpdateShadowMap(R3D_Light id); + +/** + * @brief Retrieves the softness radius used to simulate penumbra in shadows. + * + * The softness is expressed as a sampling radius in texels within the shadow map. + * + * @param id The ID of the light. + * @return The softness radius in texels currently set for the shadow. + */ +R3DAPI float R3D_GetShadowSoftness(R3D_Light id); + +/** + * @brief Sets the softness radius used to simulate penumbra in shadows. + * + * This function adjusts the softness of the shadow edges for the specified light. + * The softness value corresponds to a number of texels in the shadow map, independent + * of its resolution. Larger values increase the blur radius, resulting in softer, + * more diffuse shadows, while smaller values yield sharper shadows. + * + * @param id The ID of the light. + * @param softness The softness radius in texels to apply (must be >= 0). + * + * @note The softness must be set only after shadows have been enabled for the light, + * since the shadow map resolution must be known before the softness can be applied. + */ +R3DAPI void R3D_SetShadowSoftness(R3D_Light id, float softness); + +/** + * @brief Retrieves the shadow opacity for a light. + * + * @param id The ID of the light. + * @return The current shadow opacity. + */ +R3DAPI float R3D_GetShadowOpacity(R3D_Light id); + +/** + * @brief Sets the shadow opacity for a light. + * + * The opacity controls the visual strength of shadows. A value of 0 makes shadows + * fully transparent, while 1 applies full opacity. Values are not clamped, but the + * usual range is 0 to 1. + * + * When the opacity is exactly 0, the light still owns its shadow map, but shadow + * map rendering and shadow application are entirely skipped. + * + * @param id The ID of the light. + * @param opacity The shadow opacity to apply. + */ +R3DAPI void R3D_SetShadowOpacity(R3D_Light id, float opacity); + +/** + * @brief Gets the shadow depth bias value. + */ +R3DAPI float R3D_GetShadowDepthBias(R3D_Light id); + +/** + * @brief Sets the shadow depth bias value. + * + * A higher bias helps reduce "shadow acne" artifacts + * (shadows flickering or appearing misaligned on surfaces). + * Be careful: too large values may cause shadows to look detached + * or floating away from objects. + */ +R3DAPI void R3D_SetShadowDepthBias(R3D_Light id, float value); + +/** + * @brief Gets the shadow slope bias value. + */ +R3DAPI float R3D_GetShadowSlopeBias(R3D_Light id); + +/** + * @brief Sets the shadow slope bias value. + * + * This bias mainly compensates artifacts on surfaces angled + * relative to the light. It helps prevent shadows from + * incorrectly appearing or disappearing along object edges. + */ +R3DAPI void R3D_SetShadowSlopeBias(R3D_Light id, float value); + +/** + * @brief Gets the shadow caster mask. + */ +R3DAPI R3D_Layer R3D_GetShadowCasterMask(R3D_Light id); + +/** + * @brief Replaces the shadow caster mask. + */ +R3DAPI void R3D_SetShadowCasterMask(R3D_Light id, R3D_Layer cullMask); + +/** + * @brief Enables one or more layers in the shadow caster mask. + */ +R3DAPI void R3D_EnableShadowCasterLayers(R3D_Light id, R3D_Layer layerMask); + +/** + * @brief Disables one or more layers from the shadow caster mask. + */ +R3DAPI void R3D_DisableShadowCasterLayers(R3D_Light id, R3D_Layer layerMask); + +/** + * @brief Toggles one or more layers in the shadow caster mask. + */ +R3DAPI void R3D_ToggleShadowCasterLayers(R3D_Light id, R3D_Layer layerMask); + +/** + * @brief Checks whether at least one object layer is visible to the shadow caster. + */ +R3DAPI bool R3D_IsShadowCasterLayerVisible(R3D_Light id, R3D_Layer layerMask); + +// ---------------------------------------- +// LIGHTING: Light Helper Functions +// ---------------------------------------- + +/** + * @brief Returns the bounding box encompassing the light's area of influence. + * + * This function computes the axis-aligned bounding box (AABB) that encloses the + * volume affected by the specified light, based on its type: + * + * - For spotlights, the bounding box encloses the light cone. + * - For omni-directional lights, it encloses a sphere representing the light's range. + * - For directional lights, it returns an infinite bounding box to represent global influence. + * + * This bounding box is primarily useful for spatial partitioning, culling, or visual debugging. + * + * @param light The light for which to compute the bounding box. + * + * @return A BoundingBox struct that encloses the light's influence volume. + */ +R3DAPI BoundingBox R3D_GetLightBoundingBox(R3D_Light light); + +/** + * @brief Draws the area of influence of the light in 3D space. + * + * This function visualizes the area affected by a light in 3D space. + * It draws the light's influence, such as the cone for spotlights or the volume for omni-lights. + * This function is only relevant for spotlights and omni-lights. + * + * @note This function should be called while using the default 3D rendering mode of raylib, + * not with R3D's rendering mode. It uses raylib's 3D drawing functions to render the light's shape. + * + * @param id The ID of the light. + */ +R3DAPI void R3D_DrawLightShape(R3D_Light id); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Lighting + +#endif // R3D_LIGHTING_H + +``` + +`include/r3d/r3d_material.h`: + +```h +/* r3d_material.h -- R3D Material Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_MATERIAL_H +#define R3D_MATERIAL_H + +#include "./r3d_surface_shader.h" +#include "./r3d_importer.h" +#include "./r3d_platform.h" +#include +#include + +/** + * @defgroup Material + * @{ + */ + +// ======================================== +// CONSTANTS +// ======================================== + +/** + * @brief Default material configuration. + * + * Initializes an R3D_Material structure with sensible default values for all + * rendering parameters. Use this as a starting point for custom configurations. + */ +#define R3D_MATERIAL_BASE \ + R3D_LITERAL(R3D_Material) { \ + .albedo = { \ + .texture = {0}, \ + .color = {255, 255, 255, 255}, \ + }, \ + .emission = { \ + .texture = {0}, \ + .color = {255, 255, 255, 255}, \ + .energy = 0.0f, \ + }, \ + .normal = { \ + .texture = {0}, \ + .scale = 1.0f, \ + }, \ + .orm = { \ + .texture = {0}, \ + .occlusion = 1.0f, \ + .roughness = 1.0f, \ + .metalness = 0.0f, \ + .specular = 0.5f, \ + }, \ + .uvOffset = {0.0f, 0.0f}, \ + .uvScale = {1.0f, 1.0f}, \ + .alphaCutoff = 0.01f, \ + .depth = { \ + .mode = R3D_COMPARE_LESS, \ + .offsetFactor = 0.0f, \ + .offsetUnits = 0.0f, \ + .rangeNear = 0.0f, \ + .rangeFar = 1.0f, \ + }, \ + .stencil = { \ + .mode = R3D_COMPARE_ALWAYS, \ + .ref = 0x00, \ + .mask = 0xFF, \ + .opFail = R3D_STENCIL_KEEP, \ + .opZFail = R3D_STENCIL_KEEP, \ + .opPass = R3D_STENCIL_REPLACE, \ + }, \ + .transparencyMode = R3D_TRANSPARENCY_DISABLED, \ + .billboardMode = R3D_BILLBOARD_DISABLED, \ + .blendMode = R3D_BLEND_MIX, \ + .cullMode = R3D_CULL_BACK, \ + .unlit = false, \ + .priority = 0, \ + .shader = 0, \ + } + +// ======================================== +// ENUMS TYPES +// ======================================== + +/** + * @brief Transparency modes. + * + * This enumeration defines how a material handles transparency during rendering. + * It controls whether transparency is disabled, rendered using a depth pre-pass, + * or rendered with standard alpha blending. + */ +typedef enum R3D_TransparencyMode { + R3D_TRANSPARENCY_DISABLED, ///< No transparency, supports alpha cutoff. + R3D_TRANSPARENCY_PREPASS, ///< Supports transparency with shadows. Writes shadows for alpha > 0.1 and depth for alpha > 0.99. + R3D_TRANSPARENCY_ALPHA, ///< Standard transparency without shadows or depth writes. +} R3D_TransparencyMode; + +/** + * @brief Billboard modes. + * + * This enumeration defines how a 3D object aligns itself relative to the camera. + * It provides options to disable billboarding or to enable specific modes of alignment. + */ +typedef enum R3D_BillboardMode { + R3D_BILLBOARD_DISABLED, ///< Billboarding is disabled; the object retains its original orientation. + R3D_BILLBOARD_FRONT, ///< Full billboarding; the object fully faces the camera, rotating on all axes. + R3D_BILLBOARD_Y_AXIS /**< Y-axis constrained billboarding; the object rotates only around the Y-axis, + keeping its "up" orientation fixed. This is suitable for upright objects like characters or signs. */ +} R3D_BillboardMode; + +/** + * @brief Blend modes. + * + * Defines common blending modes used in 3D rendering to combine source and destination colors. + * @note The blend mode is applied only if you are in forward rendering mode or auto-detect mode. + */ +typedef enum R3D_BlendMode { + R3D_BLEND_MIX, ///< Default mode: the result will be opaque or alpha blended depending on the transparency mode. + R3D_BLEND_ADDITIVE, ///< Additive blending: source color is added to the destination, making bright effects. + R3D_BLEND_MULTIPLY, ///< Multiply blending: source color is multiplied with the destination, darkening the image. + R3D_BLEND_PREMULTIPLIED_ALPHA ///< Premultiplied alpha blending: source color is blended with the destination assuming the source color is already multiplied by its alpha. +} R3D_BlendMode; + +/** + * @brief Comparison modes. + * + * Defines how fragments are tested against the depth/stencil buffer during rendering. + * @note The depth/stencil comparison mode affects both forward and deferred rendering passes. + */ +typedef enum R3D_CompareMode { + R3D_COMPARE_LESS, ///< Passes if 'value' < 'buffer' (default) + R3D_COMPARE_LEQUAL, ///< Passes if 'value' <= 'buffer' + R3D_COMPARE_EQUAL, ///< Passes if 'value' == 'buffer' + R3D_COMPARE_GREATER, ///< Passes if 'value' > 'buffer' + R3D_COMPARE_GEQUAL, ///< Passes if 'value' >= 'buffer' + R3D_COMPARE_NOTEQUAL, ///< Passes if 'value' != 'buffer' + R3D_COMPARE_ALWAYS, ///< Always passes + R3D_COMPARE_NEVER ///< Never passes +} R3D_CompareMode; + +/** + * @brief Stencil buffer operations. + * + * Defines how the stencil buffer value is modified based on test results. + */ +typedef enum R3D_StencilOp { + R3D_STENCIL_KEEP, ///< Keep the current stencil value + R3D_STENCIL_ZERO, ///< Set stencil value to 0 + R3D_STENCIL_REPLACE, ///< Replace with reference value + R3D_STENCIL_INCR, ///< Increment stencil value (clamped) + R3D_STENCIL_DECR ///< Decrement stencil value (clamped) +} R3D_StencilOp; + +/** + * @brief Face culling modes. + * + * Specifies which faces of a geometry are discarded during rendering based on their winding order. + */ +typedef enum R3D_CullMode { + R3D_CULL_NONE, ///< No culling; all faces are rendered. + R3D_CULL_BACK, ///< Cull back-facing polygons (faces with clockwise winding order). + R3D_CULL_FRONT ///< Cull front-facing polygons (faces with counter-clockwise winding order). +} R3D_CullMode; + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Albedo (base color) map. + * + * Provides the base color texture and a color multiplier. + */ +typedef struct R3D_AlbedoMap { + Texture2D texture; ///< Base color texture (default: WHITE) + Color color; ///< Color multiplier (default: WHITE) +} R3D_AlbedoMap; + +/** + * @brief Emission map. + * + * Provides emission texture, color, and energy multiplier. + */ +typedef struct R3D_EmissionMap { + Texture2D texture; ///< Emission texture (default: WHITE) + Color color; ///< Emission color (default: WHITE) + float energy; ///< Emission strength (default: 0.0f) +} R3D_EmissionMap; + +/** + * @brief Normal map. + * + * Provides normal map texture and scale factor. + */ +typedef struct R3D_NormalMap { + Texture2D texture; ///< Normal map texture (default: Front Facing) + float scale; ///< Normal scale (default: 1.0f) +} R3D_NormalMap; + +/** + * @brief Combined Occlusion-Roughness-Metalness (ORM) map. + * + * Provides texture and individual multipliers for occlusion, roughness, and metalness. + */ +typedef struct R3D_OrmMap { + Texture2D texture; ///< ORM texture (default: WHITE) + float occlusion; ///< Occlusion multiplier (default: 1.0f) + float roughness; ///< Roughness multiplier (default: 1.0f) + float metalness; ///< Metalness multiplier (default: 0.0f) + float specular; ///< Controls how reflective non-metal materials appear (default: 0.5f) +} R3D_OrmMap; + +/** + * @brief Depth buffer state configuration. + * + * Controls how fragments interact with the depth buffer during rendering.. + * + * @note This structure does not directly control depth buffer writes for technical reasons. + * To render objects without writing to the depth buffer, use alpha blending mode instead. + */ +typedef struct R3D_DepthState { + R3D_CompareMode mode; ///< Comparison function for depth test (default: LESS) + float offsetFactor; ///< Scales the maximum depth slope for polygon offset (default: 0.0f) + float offsetUnits; ///< Constant depth offset value (default: 0.0f) + float rangeNear; ///< Near clipping plane for depth range mapping (default: 0.0f) + float rangeFar; ///< Far clipping plane for depth range mapping (default: 1.0f) +} R3D_DepthState; + +/** + * @brief Stencil buffer state configuration. + * + * Controls how fragments interact with the stencil buffer during rendering. + * The stencil buffer can be used for effects like x-ray vision, outlines, + * portals, and masking. + */ +typedef struct R3D_StencilState { + R3D_CompareMode mode; ///< Comparison function for stencil test (default: ALWAYS) + uint8_t ref; ///< Reference value (0-255) for comparison and replace operations (default: 0x00) + uint8_t mask; ///< Bit mask applied to both reference and stencil values during comparison (default: 0xFF) + R3D_StencilOp opFail; ///< Operation when stencil test fails (default: KEEP) + R3D_StencilOp opZFail; ///< Operation when stencil test passes but depth test fails (default: KEEP) + R3D_StencilOp opPass; ///< Operation when both stencil and depth tests pass (default: REPLACE) +} R3D_StencilState; + +/** + * @brief Material definition. + * + * Combines multiple texture maps and rendering parameters for shading. + */ +typedef struct R3D_Material { + + R3D_AlbedoMap albedo; ///< Albedo map + R3D_EmissionMap emission; ///< Emission map + R3D_NormalMap normal; ///< Normal map + R3D_OrmMap orm; ///< Occlusion-Roughness-Metalness map + + Vector2 uvOffset; ///< UV offset (default: {0.0f, 0.0f}) + Vector2 uvScale; ///< UV scale (default: {1.0f, 1.0f}) + float alphaCutoff; ///< Alpha cutoff threshold (default: 0.01f) + + R3D_DepthState depth; ///< Depth test configuration (default: standard) + R3D_StencilState stencil; ///< Stencil test configuration (default: disabled) + + R3D_TransparencyMode transparencyMode; ///< Transparency mode (default: DISABLED) + R3D_BillboardMode billboardMode; ///< Billboard mode (default: DISABLED) + R3D_BlendMode blendMode; ///< Blend mode (default: MIX) + R3D_CullMode cullMode; ///< Face culling mode (default: BACK) + bool unlit; ///< If true, material does not participate in lighting (default: false) + + int priority; ///< Render order priority; lower values are drawn first (default: 0) + R3D_SurfaceShader* shader; ///< Custom shader applied to the material (default: NULL) + +} R3D_Material; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get the default material configuration. + * + * Returns `R3D_MATERIAL_BASE` by default, + * or the material defined via `R3D_SetDefaultMaterial()`. + * + * @return Default material structure with standard properties. + */ +R3DAPI R3D_Material R3D_GetDefaultMaterial(void); + +/** + * @brief Set the default material configuration. + * + * Allows you to override the default material. + * The default material will be used as the basis for loading 3D models. + * + * @param material Default material to define. + */ +R3DAPI void R3D_SetDefaultMaterial(R3D_Material material); + +/** + * @brief Load materials from a file. + * + * Parses a 3D model file and loads its associated materials. + * + * @param filePath Path to the 3D model file. + * @param materialCount Pointer to an integer to store the number of loaded materials. + * @return Pointer to an array of loaded R3D_Material, or NULL on failure. + */ +R3DAPI R3D_Material* R3D_LoadMaterials(const char* filePath, int* materialCount); + +/** + * @brief Load materials from memory. + * + * Loads materials directly from a memory buffer containing 3D model data. + * + * @param data Pointer to the memory buffer containing the model data. + * @param size Size of the data buffer in bytes. + * @param hint Hint on the model format (can be NULL). + * @param materialCount Pointer to an integer to store the number of loaded materials. + * @return Pointer to an array of loaded R3D_Material, or NULL on failure. + */ +R3DAPI R3D_Material* R3D_LoadMaterialsFromMemory(const void* data, unsigned int size, const char* hint, int* materialCount); + +/** + * @brief Load materials from an importer. + * + * Loads materials that were previously imported via an R3D_Importer instance. + * + * @param importer Pointer to a valid R3D_Importer. + * @param materialCount Pointer to an integer to store the number of loaded materials. + * @return Pointer to an array of loaded R3D_Material, or NULL on failure. + */ +R3DAPI R3D_Material* R3D_LoadMaterialsFromImporter(const R3D_Importer* importer, int* materialCount); + +/** + * @brief Unload a material and its associated textures. + * + * Frees all memory associated with a material, including its textures. + * This function will unload all textures that are not default textures. + * + * @warning Only call this function if you are certain that the textures + * are not shared with other materials or objects, as this will permanently + * free the texture data. + * + * @param material Pointer to the material structure to be unloaded. + */ +R3DAPI void R3D_UnloadMaterial(R3D_Material material); + +/** + * @brief Load an albedo (base color) map from file. + * + * Loads an image, uploads it as an sRGB texture (if enabled), + * and applies the provided tint color. + * + * @param fileName Path to the texture file. + * @param color Multiplicative tint applied in the shader. + * @return Albedo map structure. Returns an empty map on failure. + */ +R3DAPI R3D_AlbedoMap R3D_LoadAlbedoMap(const char* fileName, Color color); + +/** + * @brief Load an albedo (base color) map from memory. + * + * Same behavior as R3D_LoadAlbedoMap(), but reads from memory instead of disk. + * + * @param fileType Image format hint (e.g. ".png", ".jpg"). + * @param fileData Pointer to image data. + * @param dataSize Size of image data in bytes. + * @param color Multiplicative tint applied in the shader. + * @return Albedo map structure. Returns an empty map on failure. + */ +R3DAPI R3D_AlbedoMap R3D_LoadAlbedoMapFromMemory(const char* fileType, const void* fileData, int dataSize, Color color); + +/** + * @brief Unload an albedo map texture. + * + * Frees the underlying texture unless it is a default texture. + * + * @param map Albedo map to unload. + */ +R3DAPI void R3D_UnloadAlbedoMap(R3D_AlbedoMap map); + +/** + * @brief Load an emission map from file. + * + * Loads an emissive texture (sRGB if enabled) and sets color + energy. + * + * @param fileName Path to the texture file. + * @param color Emission color. + * @param energy Emission intensity multiplier. + * @return Emission map. Returns an empty map on failure. + */ +R3DAPI R3D_EmissionMap R3D_LoadEmissionMap(const char* fileName, Color color, float energy); + +/** + * @brief Load an emission map from memory. + * + * Same behavior as R3D_LoadEmissionMap(), but reads from memory. + * + * @param fileType Image format hint (e.g. ".png", ".jpg"). + * @param fileData Pointer to image data. + * @param dataSize Size of image data in bytes. + * @param color Emission color. + * @param energy Emission intensity multiplier. + * @return Emission map. Returns an empty map on failure. + */ +R3DAPI R3D_EmissionMap R3D_LoadEmissionMapFromMemory(const char* fileType, const void* fileData, int dataSize, Color color, float energy); + +/** + * @brief Unload an emission map texture. + * + * Frees the texture unless it is a default texture. + * + * @param map Emission map to unload. + */ +R3DAPI void R3D_UnloadEmissionMap(R3D_EmissionMap map); + +/** + * @brief Load a normal map from file. + * + * Uploads the texture in linear space and stores the normal scale factor. + * + * @param fileName Path to the texture file. + * @param scale Normal intensity multiplier. + * @return Normal map. Returns an empty map on failure. + */ +R3DAPI R3D_NormalMap R3D_LoadNormalMap(const char* fileName, float scale); + +/** + * @brief Load a normal map from memory. + * + * Same behavior as R3D_LoadNormalMap(), but reads from memory. + * + * @param fileType Image format hint (e.g. ".png", ".jpg"). + * @param fileData Pointer to image data. + * @param dataSize Size of image data in bytes. + * @param scale Normal intensity multiplier. + * @return Normal map. Returns an empty map on failure. + */ +R3DAPI R3D_NormalMap R3D_LoadNormalMapFromMemory(const char* fileType, const void* fileData, int dataSize, float scale); + +/** + * @brief Unload a normal map texture. + * + * Frees the texture unless it is a default texture. + * + * @param map Normal map to unload. + */ +R3DAPI void R3D_UnloadNormalMap(R3D_NormalMap map); + +/** + * @brief Load a combined ORM (Occlusion-Roughness-Metalness) map from file. + * + * Uploads the texture in linear space and applies the provided multipliers. + * + * @param fileName Path to the ORM texture. + * @param occlusion Occlusion multiplier. + * @param roughness Roughness multiplier. + * @param metalness Metalness multiplier. + * @param specular Base reflectivity multiplier for non-metal materials. + * @return ORM map. Returns an empty map on failure. + */ +R3DAPI R3D_OrmMap R3D_LoadOrmMap(const char* fileName, float occlusion, float roughness, float metalness, float specular); + +/** + * @brief Load a combined ORM (Occlusion-Roughness-Metalness) map from memory. + * + * Same behavior as R3D_LoadOrmMap(), but reads from memory. + * + * @param fileType Image format hint (e.g. ".png", ".jpg"). + * @param fileData Pointer to image data. + * @param dataSize Size of image data in bytes. + * @param occlusion Occlusion multiplier. + * @param roughness Roughness multiplier. + * @param metalness Metalness multiplier. + * @param specular Base reflectivity multiplier for non-metal materials. + * @return ORM map. Returns an empty map on failure. + */ +R3DAPI R3D_OrmMap R3D_LoadOrmMapFromMemory(const char* fileType, const void* fileData, int dataSize, + float occlusion, float roughness, float metalness, float specular); + +/** + * @brief Unload an ORM map texture. + * + * Frees the texture unless it is a default texture. + * + * @param map ORM map to unload. + */ +R3DAPI void R3D_UnloadOrmMap(R3D_OrmMap map); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Material + +#endif // R3D_MATERIAL_H + +``` + +`include/r3d/r3d_mesh.h`: + +```h +/* r3d_mesh.h -- R3D Mesh Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_MESH_H +#define R3D_MESH_H + +#include "./r3d_mesh_data.h" +#include "./r3d_platform.h" +#include "./r3d_camera.h" +#include + +/** + * @defgroup Mesh + * @{ + */ + +// ======================================== +// ENUMS TYPES +// ======================================== + +/** + * @brief Shadow casting modes for objects. + * + * Controls how an object interacts with the shadow mapping system. + * These modes determine whether the object contributes to shadows, + * and if so, whether it is also rendered in the main pass. + */ +typedef enum R3D_ShadowCastMode { + R3D_SHADOW_CAST_ON_AUTO, ///< The object casts shadows; the faces used are determined by the material's culling mode. + R3D_SHADOW_CAST_ON_DOUBLE_SIDED, ///< The object casts shadows with both front and back faces, ignoring face culling. + R3D_SHADOW_CAST_ON_FRONT_SIDE, ///< The object casts shadows with only front faces, culling back faces. + R3D_SHADOW_CAST_ON_BACK_SIDE, ///< The object casts shadows with only back faces, culling front faces. + R3D_SHADOW_CAST_ONLY_AUTO, ///< The object only casts shadows; the faces used are determined by the material's culling mode. + R3D_SHADOW_CAST_ONLY_DOUBLE_SIDED, ///< The object only casts shadows with both front and back faces, ignoring face culling. + R3D_SHADOW_CAST_ONLY_FRONT_SIDE, ///< The object only casts shadows with only front faces, culling back faces. + R3D_SHADOW_CAST_ONLY_BACK_SIDE, ///< The object only casts shadows with only back faces, culling front faces. + R3D_SHADOW_CAST_DISABLED ///< The object does not cast shadows at all. +} R3D_ShadowCastMode; + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Represents a 3D mesh. + * + * Stores vertex and index data, shadow casting settings, bounding box, and layer information. + * Can represent a static or skinned mesh. + */ +typedef struct R3D_Mesh { + + int vertexOffset; ///< Offset in the internal VBO. + int vertexCapacity; ///< Number of vertices allocated in the internal VBO. + int vertexCount; ///< Number of vertices currently in use. + + int indexOffset; ///< Offset in the internal EBO. + int indexCapacity; ///< Number of indices allocated in the internal EBO. + int indexCount; ///< Number of indices currently in use. + + R3D_ShadowCastMode shadowCastMode; ///< Shadow casting mode for the mesh. + R3D_PrimitiveType primitiveType; ///< Type of primitive that constitutes the vertices. + R3D_Layer layerMask; ///< Bitfield indicating the rendering layer(s) of this mesh. + BoundingBox aabb; ///< Axis-Aligned Bounding Box in local space. + +} R3D_Mesh; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Creates a 3D mesh from CPU-side mesh data. + * @param type Primitive type used to interpret vertex data. + * @param data R3D_MeshData containing vertices and indices (can be zero initialized). + * @param aabb Optional pointer to a bounding box. If NULL, it will be computed automatically. + * @return Created R3D_Mesh. + * @note The function copies all vertex and index data into GPU buffers. + */ +R3DAPI R3D_Mesh R3D_LoadMesh(R3D_PrimitiveType type, R3D_MeshData data, const BoundingBox* aabb); + +/** + * @brief Destroys a 3D mesh and frees its resources. + * @param mesh R3D_Mesh to destroy. + */ +R3DAPI void R3D_UnloadMesh(R3D_Mesh mesh); + +/** + * @brief Check if a mesh is valid for rendering. + * + * Returns true if the mesh has a valid VAO and VBO. + * + * @param mesh The mesh to check. + * @return true if valid, false otherwise. + */ +R3DAPI bool R3D_IsMeshValid(R3D_Mesh mesh); + +/** + * @brief Generate a quad mesh with orientation. + * @param width Width along local X axis. + * @param length Length along local Z axis. + * @param resX Subdivisions along width. + * @param resZ Subdivisions along length. + * @param frontDir Direction vector for the quad's front face. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataQuad + */ +R3DAPI R3D_Mesh R3D_GenMeshQuad(float width, float length, int resX, int resZ, Vector3 frontDir); + +/** + * @brief Generate a plane mesh. + * @param width Width along X axis. + * @param length Length along Z axis. + * @param resX Subdivisions along X axis. + * @param resZ Subdivisions along Z axis. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataPlane + */ +R3DAPI R3D_Mesh R3D_GenMeshPlane(float width, float length, int resX, int resZ); + +/** + * @brief Generate a polygon mesh. + * @param sides Number of sides (min 3). + * @param radius Radius of the polygon. + * @param frontDir Direction vector for the polygon's front face. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataPoly + */ +R3DAPI R3D_Mesh R3D_GenMeshPoly(int sides, float radius, Vector3 frontDir); + +/** + * @brief Generate a cube mesh. + * @param width Width along X axis. + * @param height Height along Y axis. + * @param length Length along Z axis. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataCube + */ +R3DAPI R3D_Mesh R3D_GenMeshCube(float width, float height, float length); + +/** + * @brief Generate a subdivided cube mesh. + * @param width Width along X axis. + * @param height Height along Y axis. + * @param length Length along Z axis. + * @param resX Subdivisions along X axis. + * @param resY Subdivisions along Y axis. + * @param resZ Subdivisions along Z axis. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataCubeEx + */ +R3DAPI R3D_Mesh R3D_GenMeshCubeEx(float width, float height, float length, int resX, int resY, int resZ); + +/** + * @brief Generate a slope mesh. + * @param width Width along X axis. + * @param height Height along Y axis. + * @param length Length along Z axis. + * @param slopeNormal Direction of the slope. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataSlope + */ +R3DAPI R3D_Mesh R3D_GenMeshSlope(float width, float height, float length, Vector3 slopeNormal); + +/** + * @brief Generate a sphere mesh. + * @param radius Sphere radius. + * @param rings Number of latitude divisions. + * @param slices Number of longitude divisions. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataSphere + */ +R3DAPI R3D_Mesh R3D_GenMeshSphere(float radius, int rings, int slices); + +/** + * @brief Generate a hemisphere mesh. + * @param radius Hemisphere radius. + * @param rings Number of latitude divisions. + * @param slices Number of longitude divisions. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataHemiSphere + */ +R3DAPI R3D_Mesh R3D_GenMeshHemiSphere(float radius, int rings, int slices); + +/** + * @brief Generate a cylinder mesh. + * @param radius Radius of the cylinder. + * @param height Height along Y axis. + * @param slices Radial subdivisions. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataCylinder + */ +R3DAPI R3D_Mesh R3D_GenMeshCylinder(float radius, float height, int slices); + +/** + * @brief Generate a cylinder, cone or truncated cone mesh. + * @param bottomRadius Bottom radius. + * @param topRadius Top radius. + * @param height Height along Y axis. + * @param slices Radial subdivisions. + * @param stacks Vertical subdivisions. + * @param bottomCap Generate bottom cap. + * @param topCap Generate top cap. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataCylinderEx + */ +R3DAPI R3D_Mesh R3D_GenMeshCylinderEx(float bottomRadius, float topRadius, float height, int slices, int stacks, bool bottomCap, bool topCap); + +/** + * @brief Generate a capsule mesh. + * @param radius Capsule radius. + * @param height Height along Y axis. + * @param rings Number of latitude divisions. + * @param slices Number of longitude divisions. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataCapsule + */ +R3DAPI R3D_Mesh R3D_GenMeshCapsule(float radius, float height, int rings, int slices); + +/** + * @brief Generate a torus mesh. + * @param radius Major radius (center to tube). + * @param size Minor radius (tube thickness). + * @param radSeg Segments around major radius. + * @param sides Sides around tube cross-section. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataTorus + */ +R3DAPI R3D_Mesh R3D_GenMeshTorus(float radius, float size, int radSeg, int sides); + +/** + * @brief Generate a trefoil knot mesh. + * @param radius Major radius. + * @param size Minor radius. + * @param radSeg Segments around major radius. + * @param sides Sides around tube cross-section. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataKnot + */ +R3DAPI R3D_Mesh R3D_GenMeshKnot(float radius, float size, int radSeg, int sides); + +/** + * @brief Generate a heightmap terrain mesh. + * @param heightmap Heightmap image. + * @param size 3D dimensions of terrain. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataHeightmap + */ +R3DAPI R3D_Mesh R3D_GenMeshHeightmap(Image heightmap, Vector3 size); + +/** + * @brief Generate a cubicmap voxel mesh. + * @param cubicmap Cubicmap image. + * @param cubeSize Size of each cube. + * @return Mesh ready for rendering. + * @see R3D_GenMeshDataCubicmap + */ +R3DAPI R3D_Mesh R3D_GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); + +/** + * @brief Upload a mesh data on the GPU. + * + * This function uploads a mesh's vertex and optional index data to the GPU. + * + * If `aabb` is provided, it will be used as the mesh's bounding box; if null, + * the bounding box is automatically recalculated from the vertex data. + * + * @param mesh Pointer to the mesh structure to update. + * @param data Mesh data (vertices and indices) to upload. + * @param aabb Optional bounding box; if null, it is recalculated automatically. + * @return Returns true if the update is successful, false otherwise. + */ +R3DAPI bool R3D_UpdateMesh(R3D_Mesh* mesh, R3D_MeshData data, const BoundingBox* aabb); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Mesh + +#endif // R3D_MESH_H + +``` + +`include/r3d/r3d_mesh_data.h`: + +```h +/* r3d_mesh_data.h -- R3D Mesh Data Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_MESH_DATA_H +#define R3D_MESH_DATA_H + +#include "./r3d_platform.h" +#include "./r3d_vertex.h" +#include +#include + +/** + * @defgroup MeshData + * @{ + */ + +// ======================================== +// ENUMS TYPES +// ======================================== + +/** + * @brief Defines the geometric primitive type. + */ +typedef enum R3D_PrimitiveType { + R3D_PRIMITIVE_POINTS, ///< Each vertex represents a single point. + R3D_PRIMITIVE_LINES, ///< Each pair of vertices forms an independent line segment. + R3D_PRIMITIVE_LINE_STRIP, ///< Connected series of line segments sharing vertices. + R3D_PRIMITIVE_LINE_LOOP, ///< Closed loop of connected line segments. + R3D_PRIMITIVE_TRIANGLES, ///< Each set of three vertices forms an independent triangle. + R3D_PRIMITIVE_TRIANGLE_STRIP, ///< Connected strip of triangles sharing vertices. + R3D_PRIMITIVE_TRIANGLE_FAN ///< Fan of triangles sharing the first vertex. +} R3D_PrimitiveType; + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Represents a mesh stored in CPU memory. + * + * R3D_MeshData is the CPU-side container of a mesh. It stores vertex and index data, + * and provides utility functions to generate, transform, and process geometry before + * uploading it to the GPU as an R3D_Mesh. + * + * Think of it as a toolbox for procedural or dynamic mesh generation on the CPU. + */ +typedef struct R3D_MeshData { + R3D_Vertex* vertices; ///< Pointer to vertex data in CPU memory. + uint32_t* indices; ///< Pointer to index data in CPU memory. + int vertexCapacity; ///< Capacity of vertices. + int indexCapacity; ///< Capacity of indices. + int vertexCount; ///< Number of vertices. + int indexCount; ///< Number of indices. +} R3D_MeshData; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Allocates a mesh data container with the given capacity. + * + * This function allocates CPU-side buffers for vertices and indices, but does NOT + * initialize the mesh with any actual data. The returned R3D_MeshData has: + * + * - vertexCapacity and indexCapacity set to the requested sizes + * - vertexCount and indexCount set to 0 + * + * You must manually set vertexCount/indexCount after filling the buffers, + * or use helper functions like R3D_AppendMeshData() to populate the mesh. + * + * All allocated memory is zero-initialized. + * + * @param vertexCount Number of vertices to allocate (capacity). Must be > 0. + * @param indexCount Number of indices to allocate (capacity). May be 0. + * If 0, no index buffer is allocated. + * + * @return A new R3D_MeshData with allocated buffers and zero element counts. + */ +R3DAPI R3D_MeshData R3D_LoadMeshData(int vertexCount, int indexCount); + +/** + * @brief Releases memory used by a mesh data container. + * @param meshData R3D_MeshData to destroy. + */ +R3DAPI void R3D_UnloadMeshData(R3D_MeshData meshData); + +/** + * @brief Check if mesh data is valid. + * + * Returns true if the mesh data contains at least one vertex buffer + * with a positive number of vertices. + * + * @param meshData Mesh data to check. + * @return true if valid, false otherwise. + */ +R3DAPI bool R3D_IsMeshDataValid(R3D_MeshData meshData); + +/** + * @brief Generate a quad mesh with specified dimensions, resolution, and orientation. + * + * Creates a flat rectangular quad mesh with customizable facing direction. + * The mesh can be subdivided for higher resolution or displacement mapping. + * The quad is centered at the origin and oriented according to the frontDir parameter, + * which defines both the face direction and the surface normal. + * + * @param width Width of the quad along its local X axis. + * @param length Length of the quad along its local Z axis. + * @param resX Number of subdivisions along the width. + * @param resZ Number of subdivisions along the length. + * @param frontDir Direction vector defining the quad's front face and normal. + * This vector will be normalized internally. + * + * @return Generated quad mesh structure with proper normals, tangents, and UVs. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataQuad(float width, float length, int resX, int resZ, Vector3 frontDir); + +/** + * @brief Generate a plane mesh with specified dimensions and resolution. + * + * Creates a flat plane mesh in the XZ plane, centered at the origin. + * The mesh can be subdivided for higher resolution or displacement mapping. + * + * @param width Width of the plane along the X axis. + * @param length Length of the plane along the Z axis. + * @param resX Number of subdivisions along the X axis. + * @param resZ Number of subdivisions along the Z axis. + * + * @return Generated plane mesh structure. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataPlane(float width, float length, int resX, int resZ); + +/** + * @brief Generate a polygon mesh with specified number of sides. + * + * Creates a regular polygon mesh centered at the origin in the XY plane. + * The polygon is generated with vertices evenly distributed around a circle. + * + * @param sides Number of sides for the polygon (minimum 3). + * @param radius Radius of the circumscribed circle. + * @param frontDir Direction vector defining the polygon's front face and normal. + * This vector will be normalized internally. + * + * @return Generated polygon mesh structure. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataPoly(int sides, float radius, Vector3 frontDir); + +/** + * @brief Generate a cube mesh with specified dimensions. + * + * Creates a cube mesh centered at the origin with the specified width, height, and length. + * Each face consists of two triangles with proper normals and texture coordinates. + * + * @param width Width of the cube along the X axis. + * @param height Height of the cube along the Y axis. + * @param length Length of the cube along the Z axis. + * + * @return Generated cube mesh structure. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataCube(float width, float height, float length); + +/** + * @brief Generate a subdivided cube mesh with specified dimensions. + * + * Extension of R3D_GenMeshDataCube() allowing per-axis subdivision. + * Each face can be tessellated along the X, Y, and Z axes according + * to the provided resolutions. + * + * @param width Width of the cube along the X axis. + * @param height Height of the cube along the Y axis. + * @param length Length of the cube along the Z axis. + * @param resX Number of subdivisions along the X axis. + * @param resY Number of subdivisions along the Y axis. + * @param resZ Number of subdivisions along the Z axis. + * + * @return Generated cube mesh structure. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataCubeEx(float width, float height, float length, int resX, int resY, int resZ); + +/** + * @brief Generate a slope mesh by cutting a cube with a plane. + * + * Creates a slope mesh by slicing a cube with a plane that passes through the origin. + * The plane is defined by its normal vector, and the portion of the cube on the side + * opposite to the normal direction is kept. This allows creating ramps, wedges, and + * angled surfaces with arbitrary orientations. + * + * @param width Width of the base cube along the X axis. + * @param height Height of the base cube along the Y axis. + * @param length Length of the base cube along the Z axis. + * @param slopeNormal Normal vector of the cutting plane. The mesh keeps the portion + * of the cube in the direction opposite to this normal. + * Example: {-1, 0, 0} creates a ramp rising towards +X. + * {0, 1, 0} creates a wedge with the slope facing up. + * {-1.0, 1.0, 0} creates a 45° diagonal slope. + * + * @return Generated slope mesh structure. + * + * @note The normal vector will be automatically normalized internally. + * @note The cutting plane always passes through the center of the cube (origin). + */ +R3DAPI R3D_MeshData R3D_GenMeshDataSlope(float width, float height, float length, Vector3 slopeNormal); + +/** + * @brief Generate a sphere mesh with specified parameters. + * + * Creates a UV sphere mesh centered at the origin using latitude-longitude subdivision. + * Higher ring and slice counts produce smoother spheres but with more vertices. + * + * @param radius Radius of the sphere. + * @param rings Number of horizontal rings (latitude divisions). + * @param slices Number of vertical slices (longitude divisions). + * + * @return Generated sphere mesh structure. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataSphere(float radius, int rings, int slices); + +/** + * @brief Generate a hemisphere mesh with specified parameters. + * + * Creates a half-sphere mesh (dome) centered at the origin, extending upward in the Y axis. + * Uses the same UV sphere generation technique as R3D_GenMeshSphere but only the upper half. + * + * @param radius Radius of the hemisphere. + * @param rings Number of horizontal rings (latitude divisions). + * @param slices Number of vertical slices (longitude divisions). + * + * @return Generated hemisphere mesh structure. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataHemiSphere(float radius, int rings, int slices); + +/** + * @brief Generates a cylinder mesh centered at the origin along the Y axis. + * + * Both caps are included. For a cone or truncated cone, use R3D_GenMeshDataCylinderEx. + * + * @param radius Radius of the cylinder. Must be > 0. + * @param height Total height along the Y axis. Must be > 0. + * @param slices Radial subdivisions around the circumference. Must be >= 3. + * + * @return The generated mesh data, or an empty mesh on invalid input. + * @see R3D_GenMeshDataCylinderEx + */ +R3DAPI R3D_MeshData R3D_GenMeshDataCylinder(float radius, float height, int slices); + +/** + * @brief Generates a cylinder, cone, or truncated cone mesh centered at the origin along the Y axis. + * + * The bottom cap sits at Y = -height/2 and the top cap at Y = +height/2. + * Setting one radius to 0 produces a cone; caps can be toggled independently. + * + * @param bottomRadius Radius of the bottom end. Must be >= 0. Cannot both be 0. + * @param topRadius Radius of the top end. Must be >= 0. Cannot both be 0. + * @param height Total height along the Y axis. Must be > 0. + * @param slices Radial subdivisions around the circumference. Must be >= 3. + * @param stacks Vertical subdivisions along the height. Must be >= 1. + * Higher values reduce faceting, especially on cones. + * @param bottomCap Whether to generate the bottom cap. + * @param topCap Whether to generate the top cap. + * + * @return The generated mesh data, or an empty mesh on invalid input. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataCylinderEx(float bottomRadius, float topRadius, float height, int slices, int stacks, bool bottomCap, bool topCap); + +/** + * @brief Generate a capsule mesh with specified parameters. + * + * Creates a capsule mesh centered at the origin, extending along the Y axis. + * The capsule consists of a cylindrical body with hemispherical caps on both ends. + * The total height of the capsule is height + 2 * radius. + * + * @param radius Radius of the capsule (both cylindrical body and hemispherical caps). + * @param height Height of the cylindrical portion along the Y axis. + * @param rings Total number of latitudinal subdivisions for both hemispheres combined. + * @param slices Number of radial subdivisions around the shape. + * + * @return Generated mesh structure. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataCapsule(float radius, float height, int rings, int slices); + +/** + * @brief Generate a torus mesh with specified parameters. + * + * Creates a torus (donut shape) mesh centered at the origin in the XZ plane. + * The torus is defined by a major radius (distance from center to tube center) + * and a minor radius (tube thickness). + * + * @param radius Major radius of the torus (distance from center to tube center). + * @param size Minor radius of the torus (tube thickness/radius). + * @param radSeg Number of segments around the major radius. + * @param sides Number of sides around the tube cross-section. + * + * @return Generated torus mesh structure. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataTorus(float radius, float size, int radSeg, int sides); + +/** + * @brief Generate a trefoil knot mesh with specified parameters. + * + * Creates a trefoil knot mesh, which is a mathematical knot shape. + * Similar to a torus but with a twisted, knotted topology. + * + * @param radius Major radius of the knot. + * @param size Minor radius (tube thickness) of the knot. + * @param radSeg Number of segments around the major radius. + * @param sides Number of sides around the tube cross-section. + * + * @return Generated trefoil knot mesh structure. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataKnot(float radius, float size, int radSeg, int sides); + +/** + * @brief Generate a terrain mesh from a heightmap image. + * + * Creates a terrain mesh by interpreting the brightness values of a heightmap image + * as height values. The resulting mesh represents a 3D terrain surface. + * + * @param heightmap Image containing height data (grayscale values represent elevation). + * @param size 3D vector defining the terrain dimensions (width, max height, depth). + * + * @return Generated heightmap terrain mesh structure. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataHeightmap(Image heightmap, Vector3 size); + +/** + * @brief Generate a voxel-style mesh from a cubicmap image. + * + * Creates a mesh composed of cubes based on a cubicmap image, where each pixel + * represents the presence or absence of a cube at that position. Useful for + * creating voxel-based or block-based geometry. + * + * @param cubicmap Image where pixel values determine cube placement. + * @param cubeSize 3D vector defining the size of each individual cube. + * + * @return Generated cubicmap mesh structure. + */ +R3DAPI R3D_MeshData R3D_GenMeshDataCubicmap(Image cubicmap, Vector3 cubeSize); + +/** + * @brief Reserves memory for the specified number of vertices and indices. + * @param meshData Target mesh data container. + * @param vertexCount Number of vertices to reserve space for. + * @param indexCount Number of indices to reserve space for. + */ +R3DAPI void R3D_ReserveMeshData(R3D_MeshData* meshData, int vertexCount, int indexCount); + +/** + * @brief Shrinks allocated memory to fit the current vertex and index counts. + * @param meshData Target mesh data container to shrink. + */ +R3DAPI void R3D_ShrinkMeshData(R3D_MeshData* meshData); + +/** + * @brief Clears all vertices and indices without releasing allocated memory. + * @param meshData Target mesh data container to reset. + */ +R3DAPI void R3D_ResetMeshData(R3D_MeshData* meshData); + +/** + * @brief Creates a deep copy of an existing mesh data container. + * @param meshData Source mesh data to duplicate. + * @return A new R3D_MeshData containing a copy of the source data. + */ +R3DAPI R3D_MeshData R3D_CopyMeshData(R3D_MeshData meshData); + +/** + * @brief Merges two mesh data containers into a single one. + * @param a First mesh data. + * @param b Second mesh data. + * @return A new R3D_MeshData containing the merged geometry. + */ +R3DAPI R3D_MeshData R3D_MergeMeshData(R3D_MeshData a, R3D_MeshData b); + +/** + * @brief Appends vertices and indices to the mesh data container. + * @param meshData Target mesh data container. + * @param vertices Array of vertices to append. + * @param vertexCount Number of vertices to append. + * @param indices Array of indices to append. + * @param indexCount Number of indices to append. + */ +R3DAPI void R3D_AppendMeshData(R3D_MeshData* meshData, R3D_Vertex* vertices, int vertexCount, uint32_t* indices, int indexCount); + +/** + * @brief Applies a transformation matrix to all vertices in the mesh data. + * @param meshData Target mesh data container. + * @param transform Transformation matrix to apply. + */ +R3DAPI void R3D_TransformMeshData(R3D_MeshData* meshData, Matrix transform); + +/** + * @brief Translates all vertices by a given offset. + * @param meshData Mesh data to modify. + * @param translation Offset to apply to all vertex positions. + */ +R3DAPI void R3D_TranslateMeshData(R3D_MeshData* meshData, Vector3 translation); + +/** + * @brief Rotates all vertices using a quaternion. + * @param meshData Mesh data to modify. + * @param rotation Quaternion representing the rotation. + */ +R3DAPI void R3D_RotateMeshData(R3D_MeshData* meshData, Quaternion rotation); + +/** + * @brief Scales all vertices by given factors. + * @param meshData Mesh data to modify. + * @param scale Scaling factors for each axis. + */ +R3DAPI void R3D_ScaleMeshData(R3D_MeshData* meshData, Vector3 scale); + +/** + * @brief Generates planar UV coordinates. + * @param meshData Mesh data to modify. + * @param uvScale Scaling factors for UV coordinates. + * @param axis Axis along which to project the planar mapping. + */ +R3DAPI void R3D_GenMeshDataUVsPlanar(R3D_MeshData* meshData, Vector2 uvScale, Vector3 axis); + +/** + * @brief Generates spherical UV coordinates. + * @param meshData Mesh data to modify. + */ +R3DAPI void R3D_GenMeshDataUVsSpherical(R3D_MeshData* meshData); + +/** + * @brief Generates cylindrical UV coordinates. + * @param meshData Mesh data to modify. + */ +R3DAPI void R3D_GenMeshDataUVsCylindrical(R3D_MeshData* meshData); + +/** + * @brief Computes vertex normals from triangle geometry. + * @param meshData Mesh data to modify. + * @param type Primitive type of the mesh. Points and line primitives are not + * supported and will default to a front-facing normal (0, 0, 1). + */ +R3DAPI void R3D_GenMeshDataNormals(R3D_MeshData* meshData, R3D_PrimitiveType type); + +/** + * @brief Computes vertex tangents based on existing normals and UVs. + * @param meshData Mesh data to modify. + * @param type Primitive type of the mesh. Points and line primitives are not + * supported and will default to a front-facing tangent (1, 0, 0, 1). + */ +R3DAPI void R3D_GenMeshDataTangents(R3D_MeshData* meshData, R3D_PrimitiveType type); + +/** + * @brief Calculates the axis-aligned bounding box of the mesh. + * @param meshData Mesh data to analyze. + * @return The computed bounding box. + */ +R3DAPI BoundingBox R3D_CalculateMeshDataBoundingBox(R3D_MeshData meshData); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of MeshData + +#endif // R3D_MESH_DATA_H + +``` + +`include/r3d/r3d_model.h`: + +```h +/* r3d_model.h -- R3D Model Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_MODEL_H +#define R3D_MODEL_H + +#include "./r3d_material.h" +#include "./r3d_skeleton.h" +#include "./r3d_platform.h" +#include "./r3d_importer.h" +#include "./r3d_mesh.h" + +/** + * @defgroup Model + * @{ + */ + +// ======================================== +// ALIASES TYPES +// ======================================== + +/** + * @brief Fixed-length string type for mesh names. + * + * The size can be freely adjusted before compilation. + */ +typedef char R3D_MeshName[32]; + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Represents a complete 3D model with meshes and materials. + * + * Contains multiple meshes and their associated materials, along with animation and bounding information. + */ +typedef struct R3D_Model { + + R3D_Mesh* meshes; ///< Array of meshes composing the model. + R3D_MeshData* meshData; ///< Array of meshes data in RAM (optional, can be NULL). + R3D_MeshName* meshNames; ///< Array of meshes names (optional, can be NULL). + + R3D_Material* materials; ///< Array of materials used by the model. + int* meshMaterials; ///< Array of material indices, one per mesh. + + int meshCount; ///< Number of meshes. + int materialCount; ///< Number of materials. + + BoundingBox aabb; ///< Axis-Aligned Bounding Box encompassing the whole model. + R3D_Skeleton skeleton; ///< Skeleton hierarchy and bind pose used for skinning (NULL if non-skinned). + +} R3D_Model; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Load a 3D model from a file. + * + * Loads a 3D model from the specified file path. Supports various 3D file formats + * and automatically parses meshes, materials, and texture references. + * + * @param filePath Path to the 3D model file to load. + * + * @return Loaded model structure containing meshes and materials. + */ +R3DAPI R3D_Model R3D_LoadModel(const char* filePath); + +/** + * @brief Load a 3D model from a file with import flags. + * + * Extended version of R3D_LoadModel() allowing control over the import + * process through additional flags. + * + * @param filePath Path to the 3D model file to load. + * @param flags Importer behavior flags. + * + * @return Loaded model structure containing meshes and materials. + */ +R3DAPI R3D_Model R3D_LoadModelEx(const char* filePath, R3D_ImportFlags flags); + +/** + * @brief Load a 3D model from memory buffer. + * + * Loads a 3D model from a memory buffer containing the file data. + * Useful for loading models from embedded resources or network streams. + * + * @param data Pointer to the memory buffer containing the model data. + * @param size Size of the data buffer in bytes. + * @param hint Hint on the model format (can be NULL). + * + * @return Loaded model structure containing meshes and materials. + * + * @note External dependencies (e.g., textures or linked resources) are not supported. + * The model data must be fully self-contained. Use embedded formats like .glb to ensure compatibility. + */ +R3DAPI R3D_Model R3D_LoadModelFromMemory(const void* data, unsigned int size, const char* hint); + +/** + * @brief Load a 3D model from a memory buffer with import flags. + * + * Extended version of R3D_LoadModelFromMemory() allowing control over + * the import process through additional flags. + * + * @param data Pointer to the memory buffer containing the model data. + * @param size Size of the data buffer in bytes. + * @param hint Hint on the model format (can be NULL). + * @param flags Importer behavior flags. + * + * @return Loaded model structure containing meshes and materials. + * + * @note External dependencies (e.g., textures or linked resources) are not supported. + * The model data must be fully self-contained. + */ +R3DAPI R3D_Model R3D_LoadModelFromMemoryEx(const void* data, unsigned int size, const char* hint, R3D_ImportFlags flags); + +/** + * @brief Load a 3D model from an existing importer. + * + * Creates a model from a previously loaded importer instance. + * This avoids re-importing the source file. + * + * @param importer Importer instance to extract the model from. + * + * @return Loaded model structure containing meshes and materials. + */ +R3DAPI R3D_Model R3D_LoadModelFromImporter(const R3D_Importer* importer); + +/** + * @brief Unload a model and optionally its materials. + * + * Frees all memory associated with a model, including its meshes. + * Materials can be optionally unloaded as well. + * + * @param model The model to be unloaded. + * @param unloadMaterials If true, also unloads all materials associated with the model. + * Set to false if textures are still being used elsewhere to avoid freeing shared resources. + */ +R3DAPI void R3D_UnloadModel(R3D_Model model, bool unloadMaterials); + +/** + * @brief Returns the index of the mesh with the given name. + * + * @param model The model to search in. + * @param meshName The name of the mesh to look up. + * @return The mesh index, or -1 if not found or if @c meshNames is NULL. + */ +R3DAPI int R3D_GetModelMeshIndex(R3D_Model model, const char* meshName); + +/** + * @brief Returns a pointer to the mesh with the given name. + * + * @param model The model to search in. + * @param meshName The name of the mesh to look up. + * @return A pointer to the mesh, or NULL if not found or if @c meshNames is NULL. + */ +R3DAPI R3D_Mesh* R3D_GetModelMesh(R3D_Model model, const char* meshName); + +/** + * @brief Returns a pointer to the mesh data with the given name. + * + * @param model The model to search in. + * @param meshName The name of the mesh to look up. + * @return A pointer to the mesh data, or NULL if not found or if @c meshNames or @c meshData is NULL. + */ +R3DAPI R3D_MeshData* R3D_GetModelMeshData(R3D_Model model, const char* meshName); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Model + +#endif // R3D_MODEL_H + +``` + +`include/r3d/r3d_pack.h`: + +```h +/* r3d_pack.h -- R3D Pack Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_PACK_H +#define R3D_PACK_H + +#include "./r3d_platform.h" +#include +#include + +/** + * @defgroup Pack Pack + * @{ + */ + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Packs a 32-bit float into a 16-bit floating-point value. + * + * @param x Value to pack. + * + * @return IEEE 754 half-precision representation. + */ +R3DAPI uint16_t R3D_PackFloat16(float x); + +/** + * @brief Packs a float into a 16-bit unsigned normalized value. + * + * The input value is clamped to the [0, 1] range before packing. + * + * @param x Value to pack. + * + * @return 16-bit UNORM representation. + */ +R3DAPI uint16_t R3D_PackUnorm16(float x); + +/** + * @brief Packs a float into a 16-bit signed normalized value. + * + * The input value is clamped to the [-1, 1] range before packing. + * + * @param x Value to pack. + * + * @return 16-bit SNORM representation. + */ +R3DAPI int16_t R3D_PackSnorm16(float x); + +/** + * @brief Packs a float into an 8-bit unsigned normalized value. + * + * The input value is clamped to the [0, 1] range before packing. + * + * @param x Value to pack. + * + * @return 8-bit UNORM representation. + */ +R3DAPI uint8_t R3D_PackUnorm8(float x); + +/** + * @brief Packs a float into an 8-bit signed normalized value. + * + * The input value is clamped to the [-1, 1] range before packing. + * + * @param x Value to pack. + * + * @return 8-bit SNORM representation. + */ +R3DAPI int8_t R3D_PackSnorm8(float x); + +/** + * @brief Unpacks a 16-bit floating-point value into a 32-bit float. + * + * @param x IEEE 754 half-precision representation. + * + * @return Unpacked float value. + */ +R3DAPI float R3D_UnpackFloat16(uint16_t x); + +/** + * @brief Unpacks a 16-bit unsigned normalized value. + * + * The returned value is in the [0, 1] range. + * + * @param x 16-bit UNORM representation. + * + * @return Unpacked float value. + */ +R3DAPI float R3D_UnpackUnorm16(uint16_t x); + +/** + * @brief Unpacks a 16-bit signed normalized value. + * + * The returned value is clamped to the [-1, 1] range. + * + * @param x 16-bit SNORM representation. + * + * @return Unpacked float value. + */ +R3DAPI float R3D_UnpackSnorm16(int16_t x); + +/** + * @brief Unpacks an 8-bit unsigned normalized value. + * + * The returned value is in the [0, 1] range. + * + * @param x 8-bit UNORM representation. + * + * @return Unpacked float value. + */ +R3DAPI float R3D_UnpackUnorm8(uint8_t x); + +/** + * @brief Unpacks an 8-bit signed normalized value. + * + * The returned value is clamped to the [-1, 1] range. + * + * @param x 8-bit SNORM representation. + * + * @return Unpacked float value. + */ +R3DAPI float R3D_UnpackSnorm8(int8_t x); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Pack + +#endif // R3D_PACK_H + +``` + +`include/r3d/r3d_platform.h`: + +```h +/* r3d_platform.h -- Platform definitions. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_API_H +#define R3D_API_H + +#if defined(_WIN32) +# if defined(__TINYC__) +# define __declspec(x) __attribute__((x)) +# endif +# if defined(R3D_BUILD_SHARED) +# define R3DAPI __declspec(dllexport) +# elif defined(R3D_USE_SHARED) +# define R3DAPI __declspec(dllimport) +# endif +#else +# if defined(R3D_BUILD_SHARED) +# define R3DAPI __attribute__((visibility("default"))) +# endif +#endif + +#ifndef R3DAPI +# define R3DAPI extern +#endif + +#ifndef R3D_LITERAL +# ifdef __cplusplus +# define R3D_LITERAL(type) type +# else +# define R3D_LITERAL(type) (type) +# endif +#endif + +#endif // R3D_API_H + +``` + +`include/r3d/r3d_probe.h`: + +```h +/* r3d_probe.h -- R3D Probe Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_PROBE_H +#define R3D_PROBE_H + +#include "./r3d_platform.h" +#include +#include + +/** + * @defgroup Probe + * @{ + */ + +// ======================================== +// ENUM TYPES +// ======================================== + +/** + * @brief Bit-flags controlling what components are generated. + * + * - R3D_PROBE_ILLUMINATION -> generate diffuse irradiance + * - R3D_PROBE_REFLECTION -> generate specular prefiltered map + */ +typedef uint32_t R3D_ProbeFlags; + +#define R3D_PROBE_ILLUMINATION (1 << 0) +#define R3D_PROBE_REFLECTION (1 << 1) + +/** + * @brief Modes for updating probes. + * + * Controls how often probe captures are refreshed. + */ +typedef enum R3D_ProbeUpdateMode { + R3D_PROBE_UPDATE_ONCE, ///< Updated only when its state or content changes + R3D_PROBE_UPDATE_ALWAYS ///< Updated during every frames +} R3D_ProbeUpdateMode; + +// ======================================== +// HANDLE TYPE +// ======================================== + +/** + * @brief Unique identifier for an R3D probe. + * + * Negative values indicate an invalid probe. + */ +typedef int32_t R3D_Probe; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Creates a new probe of the specified type. + * + * The returned probe must be destroyed using ::R3D_DestroyProbe + * when it is no longer needed. + * + * @param flags IBL components that the probe must support. + * @return A valid probe ID, or a negative value on failure. + */ +R3DAPI R3D_Probe R3D_CreateProbe(R3D_ProbeFlags flags); + +/** + * @brief Destroys a probe and frees its resources. + * + * @param id Probe ID to destroy. + */ +R3DAPI void R3D_DestroyProbe(R3D_Probe id); + +/** + * @brief Returns whether a probe exists. + * + * @param id Probe ID. + * @return true if the probe is valid and allocated, otherwise false. + */ +R3DAPI bool R3D_IsProbeExist(R3D_Probe id); + +/** + * @brief Returns the probe flags. + * + * @param id Probe ID. + * @return The flags assigned to the probe. + */ +R3DAPI R3D_ProbeFlags R3D_GetProbeFlags(R3D_Probe id); + +/** + * @brief Returns whether a probe is currently active. + * + * Inactive probes do not contribute to lighting. + * + * @param id Probe ID. + * @return true if active, otherwise false. + */ +R3DAPI bool R3D_IsProbeActive(R3D_Probe id); + +/** + * @brief Enables or disables a probe. + * + * @param id Probe ID. + * @param active Set to true to enable the probe. + */ +R3DAPI void R3D_SetProbeActive(R3D_Probe id, bool active); + +/** + * @brief Gets the probe update mode. + * + * - R3D_PROBE_UPDATE_ONCE: + * Captured once, then reused unless its state changes. + * + * - R3D_PROBE_UPDATE_ALWAYS: + * Recaptured every frame. + * + * Use "ONCE" for static scenes, "ALWAYS" for highly dynamic scenes. + */ +R3DAPI R3D_ProbeUpdateMode R3D_GetProbeUpdateMode(R3D_Probe id); + +/** + * @brief Sets the probe update mode. + * + * Controls when the probe capture is refreshed. + * + * @param id Probe ID. + * @param mode Update mode to apply. + */ +R3DAPI void R3D_SetProbeUpdateMode(R3D_Probe id, R3D_ProbeUpdateMode mode); + +/** + * @brief Returns whether the probe is considered indoors. + * + * Indoor probes do not sample skybox or environment maps. + * Instead they rely only on ambient and background colors. + * + * Use this for rooms, caves, tunnels, etc... + * where outside lighting should not bleed inside. + */ +R3DAPI bool R3D_GetProbeInterior(R3D_Probe id); + +/** + * @brief Enables or disables indoor mode for the probe. + */ +R3DAPI void R3D_SetProbeInterior(R3D_Probe id, bool active); + +/** + * @brief Returns whether shadows are captured by this probe. + * + * When enabled, shadowing is baked into the captured lighting. + * This improves realism, but increases capture cost. + */ +R3DAPI bool R3D_GetProbeShadows(R3D_Probe id); + +/** + * @brief Enables or disables shadow rendering during probe capture. + */ +R3DAPI void R3D_SetProbeShadows(R3D_Probe id, bool active); + +/** + * @brief Gets the world position of the probe. + */ +R3DAPI Vector3 R3D_GetProbePosition(R3D_Probe id); + +/** + * @brief Sets the world position of the probe. + */ +R3DAPI void R3D_SetProbePosition(R3D_Probe id, Vector3 position); + +/** + * @brief Gets the effective range of the probe. + * + * The range defines the radius (in world units) within which this probe + * contributes to lighting. Objects outside this sphere receive no influence. + */ +R3DAPI float R3D_GetProbeRange(R3D_Probe id); + +/** + * @brief Sets the effective range of the probe. + * + * @param range Radius in world units. Must be > 0. + */ +R3DAPI void R3D_SetProbeRange(R3D_Probe id, float range); + +/** + * @brief Gets the falloff factor applied to probe contributions. + * + * Falloff controls how lighting fades as distance increases. + * + * Internally this uses a power curve: + * attenuation = 1.0 - pow(dist / probe.range, probe.falloff) + * + * Effects: + * - falloff = 1 -> linear fade + * - falloff > 1 -> light stays strong near the probe, drops faster at the edge + * - falloff < 1 -> softer fade across the whole range + */ +R3DAPI float R3D_GetProbeFalloff(R3D_Probe id); + +/** + * @brief Sets the falloff factor used for distance attenuation. + * + * Larger values make the probe feel more localized. + */ +R3DAPI void R3D_SetProbeFalloff(R3D_Probe id, float falloff); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Probe + +#endif // R3D_PROBE_H + +``` + +`include/r3d/r3d_screen_shader.h`: + +```h +/* r3d_screen_shader.h -- R3D Screen Shader Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_SCREEN_SHADER_H +#define R3D_SCREEN_SHADER_H + +#include "./r3d_platform.h" +#include + +/** + * @defgroup ScreenShader + * @{ + */ + +// ======================================== +// OPAQUE TYPES +// ======================================== + +typedef struct R3D_ShaderCustom R3D_ScreenShader; + +// ======================================== +// ENUM TYPES +// ======================================== + +/** + * @brief Screen shader execution stage. + * + * Screen shaders are custom fullscreen post-processing passes inserted at + * specific points of the frame. + * + * The SCENE stage runs before built-in post-processing and receives + * scene-referred HDR linear color. It is an advanced stage: effects + * may affect bloom, auto exposure, and all later passes. + * + * The POST stage runs after built-in HDR post-processing, but before output + * conversion. It still receives scene-referred HDR linear color. + * + * The OUTPUT stage runs after tonemapping/output conversion, but before + * anti-aliasing. It receives display-referred LDR color and is suitable for + * most artistic image effects. + * + * The FINAL stage runs after anti-aliasing, before the final blit. It receives + * the final display-referred image and is suitable for overlays, grain, + * scanlines, sharpening, fades, and debug visualization. + */ +typedef enum R3D_ScreenShaderStage { + R3D_SCREEN_SHADER_STAGE_SCENE, ///< Before built-in post-processing; advanced HDR scene stage. + R3D_SCREEN_SHADER_STAGE_POST, ///< After built-in HDR post-processing, before output conversion. + R3D_SCREEN_SHADER_STAGE_OUTPUT, ///< After output conversion, before anti-aliasing. + R3D_SCREEN_SHADER_STAGE_FINAL, ///< After anti-aliasing, before final blit. + R3D_SCREEN_SHADER_STAGE_COUNT, ///< Number of screen shader stages. +} R3D_ScreenShaderStage; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Loads a screen shader from a file. + * + * The shader must define a single entry point: + * `void fragment()`. Any other entry point, such as `vertex()`, + * or any varyings will be ignored. + * + * @param filePath Path to the shader source file. + * @return Pointer to the loaded screen shader, or NULL on failure. + */ +R3DAPI R3D_ScreenShader* R3D_LoadScreenShader(const char* filePath); + +/** + * @brief Loads a screen shader from a source code string in memory. + * + * The shader must define a single entry point: + * `void fragment()`. Any other entry point, such as `vertex()`, + * or any varyings will be ignored. + * + * @param code Null-terminated shader source code. + * @return Pointer to the loaded screen shader, or NULL on failure. + */ +R3DAPI R3D_ScreenShader* R3D_LoadScreenShaderFromMemory(const char* code); + +/** + * @brief Creates an alias of an existing screen shader. + * + * The alias shares the same compiled program as the original but holds its own + * independent uniform and sampler state. Typical use cases include pre-configuring + * aliases for distinct effects (e.g. different convolution kernels), or running + * the same shader multiple times in a post-process chain with different parameters + * at each pass. + * + * Uniform and sampler state is copied from the original at the moment this + * function is called, not from the shader source defaults. Any values set + * on the original after compilation but before this call will be reflected + * in the alias; values set afterward will not. + * + * @note The alias does not own the program. Always unload all aliases before + * unloading the original, or the alias program references become dangling. + * + * @param shader The original screen shader to alias. + * @return Pointer to the alias, or NULL on failure. + */ +R3DAPI R3D_ScreenShader* R3D_LoadScreenShaderAlias(R3D_ScreenShader* shader); + +/** + * @brief Unloads and destroys a screen shader. + * + * If the shader owns its program shaders (i.e. it was created with @ref R3D_LoadScreenShader + * or @ref R3D_LoadScreenShaderFromMemory), they are deleted. Aliases created from this + * shader via @ref R3D_LoadScreenShaderAlias must be unloaded beforehand, as they + * share the same programs and will be left with dangling references. + * + * @param shader Screen shader to unload. + */ +R3DAPI void R3D_UnloadScreenShader(R3D_ScreenShader* shader); + +/** + * @brief Sets a uniform value for the current frame. + * + * Once a uniform is set, it remains valid for the all frames. + * If an uniform is set multiple times during the same frame, + * the last value defined before R3D_End() is used. + * + * Supported types: + * bool, int, float, + * ivec2, ivec3, ivec4, + * vec2, vec3, vec4, + * mat2, mat3, mat4 + * + * @warning Boolean values are read as 4 bytes. + * + * @param shader Target screen shader. + * May be NULL. In that case, the call is ignored + * and a warning is logged. + * @param name Name of the uniform. Must not be NULL. + * @param value Pointer to the uniform value. Must not be NULL. + */ +R3DAPI void R3D_SetScreenShaderUniform(R3D_ScreenShader* shader, const char* name, const void* value); + +/** + * @brief Sets a texture sampler for the current frame. + * + * Once a sampler is set, it remains valid for all frames. + * If a sampler is set multiple times during the same frame, + * the last value defined before R3D_End() is used. + * + * Supported samplers: + * sampler1D, sampler2D, sampler3D, samplerCube + * + * @param shader Target screen shader. + * May be NULL. In that case, the call is ignored + * and a warning is logged. + * @param name Name of the sampler uniform. Must not be NULL. + * @param texture Texture to bind to the sampler. + */ +R3DAPI void R3D_SetScreenShaderSampler(R3D_ScreenShader* shader, const char* name, Texture texture); + +/** + * @brief Sets the screen shader chain for a given stage. + * + * Screen shaders are executed in the order provided. The maximum number of + * shaders per stage is `R3D_MAX_SCREEN_SHADERS`; extra entries are ignored + * and a warning is emitted. + * + * Shader pointers are copied internally, so the original array may be modified + * or freed after the call. NULL entries are allowed and skipped safely. + * + * Calling this function replaces the previous chain for the selected stage. + * To clear a stage, pass `shaders = NULL` or `count = 0`. + * + * @param stage Screen shader stage to configure. + * @param shaders Array of pointers to R3D_ScreenShader objects. + * @param count Number of shaders in the array. + */ +R3DAPI void R3D_SetScreenShaderChain(R3D_ScreenShaderStage stage, R3D_ScreenShader** shaders, int count); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of ScreenShader + +#endif // R3D_SCREEN_SHADER_H + +``` + +`include/r3d/r3d_shape.h`: + +```h +/* r3d_shape.h -- R3D Shape Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_SHAPE_H +#define R3D_SHAPE_H + +#include "./r3d_mesh_data.h" +#include "./r3d_model.h" + +/** + * @defgroup Shape + * @brief Collision shape types and read-only queries (construction, intersection, penetration, closest points). + * + * Functions that modify a shape's state belong to the Kinematics module. + * @{ + */ + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Alias for raylib's BoundingBox (min/max corner points). + */ +typedef BoundingBox R3D_BoundingBox; + +/** + * @brief Oriented bounding box (OBB). + * + * Defined by a center point, three orthogonal axes, and half-extents along each axis. + */ +typedef struct { + Vector3 center; + Vector3 axisX; + Vector3 axisY; + Vector3 axisZ; + Vector3 halfExtents; +} R3D_OrientedBox; + +/** + * @brief Capsule shape defined by two endpoints and radius + */ +typedef struct R3D_Capsule { + Vector3 start; ///< Start point of capsule axis + Vector3 end; ///< End point of capsule axis + float radius; ///< Capsule radius +} R3D_Capsule; + +/** + * @brief Penetration information from an overlap test + */ +typedef struct R3D_Penetration { + bool collides; ///< Whether shapes are overlapping + float depth; ///< Penetration depth + Vector3 normal; ///< Collision normal (direction to resolve penetration) + Vector3 mtv; ///< Minimum Translation Vector (normal * depth) +} R3D_Penetration; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Compute an axis-aligned bounding box from a center and half-extents. + */ +R3DAPI R3D_BoundingBox R3D_GetBoundingBox(Vector3 center, Vector3 halfExtents); + +/** + * @brief Compute an oriented bounding box from an AABB and transform. + */ +R3DAPI R3D_OrientedBox R3D_GetOrientedBox(R3D_BoundingBox aabb, Matrix transform); + +/** + * @brief Check if two axis-aligned bounding boxes intersect + * @param box1 First bounding box + * @param box2 Second bounding box + * @return true if collision detected + */ +R3DAPI bool R3D_CheckCollisionBoundingBoxes(R3D_BoundingBox box1, R3D_BoundingBox box2); + +/** + * @brief Check if an axis-aligned bounding box intersects a sphere + * @param box Bounding box + * @param center Sphere center + * @param radius Sphere radius + * @return true if collision detected + */ +R3DAPI bool R3D_CheckCollisionBoundingBoxSphere(R3D_BoundingBox box, Vector3 center, float radius); + +/** + * @brief Check if two oriented bounding boxes intersect + * @param box1 First oriented box + * @param box2 Second oriented box + * @return true if collision detected + */ +R3DAPI bool R3D_CheckCollisionOrientedBoxes(R3D_OrientedBox box1, R3D_OrientedBox box2); + +/** + * @brief Check if an oriented bounding box intersects a sphere + * @param box Oriented bounding box + * @param center Sphere center + * @param radius Sphere radius + * @return true if collision detected + */ +R3DAPI bool R3D_CheckCollisionOrientedBoxSphere(R3D_OrientedBox box, Vector3 center, float radius); + +/** + * @brief Check if two spheres intersect + * @param center1 First sphere center + * @param radius1 First sphere radius + * @param center2 Second sphere center + * @param radius2 Second sphere radius + * @return true if collision detected + */ +R3DAPI bool R3D_CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); + +/** + * @brief Check if capsule intersects with bounding box + * @param capsule Capsule shape + * @param box Bounding box + * @return true if collision detected + */ +R3DAPI bool R3D_CheckCollisionCapsuleBoundingBox(R3D_Capsule capsule, R3D_BoundingBox box); + +/** + * @brief Check if capsule intersects with oriented box + * @param capsule Capsule shape + * @param box Oriented box + * @return true if collision detected + */ +R3DAPI bool R3D_CheckCollisionCapsuleOrientedBox(R3D_Capsule capsule, R3D_OrientedBox box); + +/** + * @brief Check if capsule intersects with sphere + * @param capsule Capsule shape + * @param center Sphere center + * @param radius Sphere radius + * @return true if collision detected + */ +R3DAPI bool R3D_CheckCollisionCapsuleSphere(R3D_Capsule capsule, Vector3 center, float radius); + +/** + * @brief Check if two capsules intersect + * @param a First capsule + * @param b Second capsule + * @return true if collision detected + */ +R3DAPI bool R3D_CheckCollisionCapsules(R3D_Capsule a, R3D_Capsule b); + +/** + * @brief Check if capsule intersects with mesh + * @param capsule Capsule shape + * @param mesh Mesh data + * @param transform Mesh transform + * @return true if collision detected + */ +R3DAPI bool R3D_CheckCollisionCapsuleMesh(R3D_Capsule capsule, R3D_MeshData mesh, Matrix transform); + +/** + * @brief Check penetration between two axis-aligned bounding boxes + * @param box1 First bounding box + * @param box2 Second bounding box + * @return Penetration information + */ +R3DAPI R3D_Penetration R3D_CheckPenetrationBoundingBoxes(R3D_BoundingBox box1, R3D_BoundingBox box2); + +/** + * @brief Check penetration between an axis-aligned bounding box and a sphere + * @param box Bounding box + * @param center Sphere center + * @param radius Sphere radius + * @return Penetration information + */ +R3DAPI R3D_Penetration R3D_CheckPenetrationBoundingBoxSphere(R3D_BoundingBox box, Vector3 center, float radius); + +/** + * @brief Check penetration between two oriented bounding boxes + * @param box1 First oriented box + * @param box2 Second oriented box + * @return Penetration information + */ +R3DAPI R3D_Penetration R3D_CheckPenetrationOrientedBoxes(R3D_OrientedBox box1, R3D_OrientedBox box2); + +/** + * @brief Check penetration between an oriented bounding box and a sphere + * @param box Oriented bounding box + * @param center Sphere center + * @param radius Sphere radius + * @return Penetration information + */ +R3DAPI R3D_Penetration R3D_CheckPenetrationOrientedBoxSphere(R3D_OrientedBox box, Vector3 center, float radius); + +/** + * @brief Check penetration between two spheres + * @param center1 First sphere center + * @param radius1 First sphere radius + * @param center2 Second sphere center + * @param radius2 Second sphere radius + * @return Penetration information + */ +R3DAPI R3D_Penetration R3D_CheckPenetrationSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); + +/** + * @brief Check penetration between a capsule and a bounding box + * @param capsule Capsule shape + * @param box Bounding box + * @return Penetration information + */ +R3DAPI R3D_Penetration R3D_CheckPenetrationCapsuleBoundingBox(R3D_Capsule capsule, R3D_BoundingBox box); + +/** + * @brief Check penetration between a capsule and an oriented bounding box + * @param capsule Capsule shape + * @param box Oriented bounding box + * @return Penetration information + */ +R3DAPI R3D_Penetration R3D_CheckPenetrationCapsuleOrientedBox(R3D_Capsule capsule, R3D_OrientedBox box); + +/** + * @brief Check penetration between capsule and sphere + * @param capsule Capsule shape + * @param center Sphere center + * @param radius Sphere radius + * @return Penetration information. + */ +R3DAPI R3D_Penetration R3D_CheckPenetrationCapsuleSphere(R3D_Capsule capsule, Vector3 center, float radius); + +/** + * @brief Check penetration between two capsules + * @param a First capsule + * @param b Second capsule + * @return Penetration information. + */ +R3DAPI R3D_Penetration R3D_CheckPenetrationCapsules(R3D_Capsule a, R3D_Capsule b); + +/** + * @brief Cast a ray against a triangle + * @param ray Ray to cast + * @param p1 First triangle vertex + * @param p2 Second triangle vertex + * @param p3 Third triangle vertex + * @return Ray collision info (hit, distance, point, normal) + */ +R3DAPI RayCollision R3D_RaycastTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); + +/** + * @brief Cast a ray against a quad + * @param ray Ray to cast + * @param p1 First quad vertex + * @param p2 Second quad vertex + * @param p3 Third quad vertex + * @param p4 Fourth quad vertex + * @note The quad must be strictly planar and non-self-intersecting + * @return Ray collision info (hit, distance, point, normal) + */ +R3DAPI RayCollision R3D_RaycastQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); + +/** + * @brief Cast a ray against an axis-aligned bounding box + * @param ray Ray to cast + * @param box Bounding box to test against + * @return Ray collision info (hit, distance, point, normal) + */ +R3DAPI RayCollision R3D_RaycastBoundingBox(Ray ray, R3D_BoundingBox box); + +/** + * @brief Cast a ray against an oriented bounding box + * @param ray Ray to cast + * @param box Oriented bounding box to test against + * @return Ray collision info (hit, distance, point, normal) + */ +R3DAPI RayCollision R3D_RaycastOrientedBox(Ray ray, R3D_OrientedBox box); + +/** + * @brief Cast a ray against a sphere + * @param ray Ray to cast + * @param center Sphere center + * @param radius Sphere radius + * @return Ray collision info (hit, distance, point, normal) + */ +R3DAPI RayCollision R3D_RaycastSphere(Ray ray, Vector3 center, float radius); + +/** + * @brief Cast a ray against a capsule + * @param ray Ray to cast + * @param capsule Capsule shape to test against + * @return Ray collision info (hit, distance, point, normal) + */ +R3DAPI RayCollision R3D_RaycastCapsule(Ray ray, R3D_Capsule capsule); + +/** + * @brief Cast a ray against mesh geometry + * @param ray Ray to cast + * @param mesh Mesh data to test against + * @param transform Mesh world transform + * @return Ray collision info (hit, distance, point, normal) + */ +R3DAPI RayCollision R3D_RaycastMesh(Ray ray, R3D_MeshData mesh, Matrix transform); + +/** + * @brief Cast a ray against a model (tests all meshes) + * @param ray Ray to cast + * @param model Model to test against (must have valid meshData) + * @param transform Model world transform + * @return Ray collision info for closest hit (hit=false if no meshData) + */ +R3DAPI RayCollision R3D_RaycastModel(Ray ray, R3D_Model model, Matrix transform); + +/** + * @brief Find closest point on line segment to given point + * @param point Query point + * @param start Segment start + * @param end Segment end + * @return Closest point on segment [start, end] + */ +R3DAPI Vector3 R3D_ClosestPointOnSegment(Vector3 point, Vector3 start, Vector3 end); + +/** + * @brief Find closest point on triangle to given point + * @param p Query point + * @param a Triangle vertex A + * @param b Triangle vertex B + * @param c Triangle vertex C + * @return Closest point on triangle surface + */ +R3DAPI Vector3 R3D_ClosestPointOnTriangle(Vector3 p, Vector3 a, Vector3 b, Vector3 c); + +/** + * @brief Find closest point on box surface to given point + * @param point Query point + * @param box Bounding box + * @return Closest point on/in box (clamped to box bounds) + */ +R3DAPI Vector3 R3D_ClosestPointOnBox(Vector3 point, R3D_BoundingBox box); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Shape + +#endif // R3D_SHAPE_H + +``` + +`include/r3d/r3d_skeleton.h`: + +```h +/* r3d_skeleton.h -- R3D Skeleton Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_SKELETON_H +#define R3D_SKELETON_H + +#include "./r3d_platform.h" +#include "./r3d_importer.h" +#include +#include + +/** + * @defgroup Skeleton + * @{ + */ + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Stores bone information for skeletal animation. + * + * Contains the bone name and the index of its parent bone. + */ +typedef struct R3D_BoneInfo { + char name[32]; ///< Bone name (max 31 characters + null terminator). + int parent; ///< Index of the parent bone (-1 if root). +} R3D_BoneInfo; + +/** + * @brief Represents a skeletal hierarchy used for skinning. + * + * Defines the bone structure, reference poses, and inverse bind matrices + * required for skeletal animation. The skeleton provides both local and + * global bind poses used during skinning and animation playback. + */ +typedef struct R3D_Skeleton { + + R3D_BoneInfo* bones; ///< Array of bone descriptors defining the hierarchy and names. + int boneCount; ///< Total number of bones in the skeleton. + + Matrix* localBind; ///< Bind pose matrices relative to parent + Matrix* modelBind; ///< Bind pose matrices in model/global space + Matrix* invBind; ///< Inverse bind matrices (model space) for skinning + Matrix rootBind; ///< Root correction if local bind is not identity + + uint32_t skinTexture; ///< Texture ID that contains the bind pose for GPU skinning. This is a 1D Texture RGBA16F 4*boneCount. + +} R3D_Skeleton; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Loads a skeleton hierarchy from a 3D model file. + * + * Skeletons are automatically loaded when importing a model, + * but can be loaded manually for advanced use cases. + * + * @param filePath Path to the model file containing the skeleton data. + * @return Return the loaded R3D_Skeleton. + */ +R3DAPI R3D_Skeleton R3D_LoadSkeleton(const char* filePath); + +/** + * @brief Loads a skeleton hierarchy from memory data. + * + * Allows manual loading of skeletons directly from a memory buffer. + * Typically used for advanced or custom asset loading workflows. + * + * @param data Pointer to the memory buffer containing skeleton data. + * @param size Size of the memory buffer in bytes. + * @param hint Optional format hint (can be NULL). + * @return Return the loaded R3D_Skeleton. + */ +R3DAPI R3D_Skeleton R3D_LoadSkeletonFromMemory(const void* data, unsigned int size, const char* hint); + +/** + * @brief Loads a skeleton hierarchy from an existing importer. + * + * Extracts the skeleton data from a previously loaded importer instance. + * + * @param importer Importer instance to extract the skeleton from. + * @return Return the loaded R3D_Skeleton. + */ +R3DAPI R3D_Skeleton R3D_LoadSkeletonFromImporter(const R3D_Importer* importer); + +/** + * @brief Frees the memory allocated for a skeleton. + * + * @param skeleton R3D_Skeleton to destroy. + */ +R3DAPI void R3D_UnloadSkeleton(R3D_Skeleton skeleton); + +/** + * @brief Check if a skeleton is valid. + * + * Returns true if atleast the texBindPose is greater than zero. + * + * @param skeleton The skeleton to check. + * @return true if valid, false otherwise. + */ +R3DAPI bool R3D_IsSkeletonValid(R3D_Skeleton skeleton); + +/** + * @brief Returns the index of the bone with the given name. + * + * @param skeleton Skeleton to search in. + * @param boneName Name of the bone to find. + * @return Index of the bone, or a negative value if not found. + */ +R3DAPI int R3D_GetSkeletonBoneIndex(R3D_Skeleton skeleton, const char* boneName); + +/** + * @brief Returns a pointer to the bone with the given name. + * + * @param skeleton Skeleton to search in. + * @param boneName Name of the bone to find. + * @return Pointer to the bone, or NULL if not found. + */ +R3DAPI R3D_BoneInfo* R3D_GetSkeletonBone(R3D_Skeleton skeleton, const char* boneName); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Skeleton + +#endif // R3D_SKELETON_H + +``` + +`include/r3d/r3d_sky.h`: + +```h +/* r3d_sky.h -- R3D Sky Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_SKY_H +#define R3D_SKY_H + +#include "./r3d_sky_shader.h" +#include "./r3d_platform.h" +#include "./r3d_cubemap.h" +#include +#include + +/** + * @defgroup Sky + * @{ + */ + +// ======================================== +// CONSTANTS +// ======================================== + +#define R3D_PROCEDURAL_SKY_BASE \ + R3D_LITERAL(R3D_ProceduralSky) { \ + .skyTopColor = (Color) {98, 116, 140, 255}, \ + .skyHorizonColor = (Color) {165, 167, 171, 255}, \ + .skyHorizonCurve = 0.15f, \ + .skyEnergy = 1.0f, \ + .groundBottomColor = (Color) {51, 43, 34, 255}, \ + .groundHorizonColor = (Color) {165, 167, 171, 255}, \ + .groundHorizonCurve = 0.02f, \ + .groundEnergy = 1.0f, \ + .sunDirection = (Vector3) {-1.0f, -1.0f, -1.0f}, \ + .sunColor = (Color) {255, 255, 255, 255}, \ + .sunSize = 1.0f * DEG2RAD, \ + .sunCurve = 0.15f, \ + .sunEnergy = 1.0f, \ + } + +// ======================================== +// STRUCT TYPES +// ======================================== + +/** + * @brief Parameters for procedural sky generation. + * + * Curves control gradient falloff (lower = sharper transition at horizon). + */ +typedef struct R3D_ProceduralSky { + + Color skyTopColor; // Sky color at zenith + Color skyHorizonColor; // Sky color at horizon + float skyHorizonCurve; // Gradient curve exponent (0.01 - 1.0, typical: 0.15) + float skyEnergy; // Sky brightness multiplier + + Color groundBottomColor; // Ground color at nadir + Color groundHorizonColor; // Ground color at horizon + float groundHorizonCurve; // Gradient curve exponent (typical: 0.02) + float groundEnergy; // Ground brightness multiplier + + Vector3 sunDirection; // Direction from which light comes (can take not normalized) + Color sunColor; // Sun disk color + float sunSize; // Sun angular size in radians (real sun: ~0.0087 rad = 0.5°) + float sunCurve; // Sun edge softness exponent (typical: 0.15) + float sunEnergy; // Sun brightness multiplier + +} R3D_ProceduralSky; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generates a procedural sky cubemap. + * + * Creates a GPU cubemap with procedural gradient sky and sun rendering. + * The cubemap is ready for use as environment map or IBL source. + */ +R3DAPI R3D_Cubemap R3D_GenProceduralSky(int size, R3D_ProceduralSky params); + +/** + * @brief Generates a custom sky cubemap. + * + * Creates a GPU cubemap rendered using the provided sky shader. + * The cubemap is ready for use as environment map or IBL source. + */ +R3DAPI R3D_Cubemap R3D_GenCustomSky(int size, R3D_SkyShader* shader); + +/** + * @brief Updates an existing procedural sky cubemap. + * + * Re-renders the cubemap with new parameters. Faster than unload + generate + * when animating sky conditions (time of day, weather, etc.). + */ +R3DAPI void R3D_UpdateProceduralSky(R3D_Cubemap* cubemap, R3D_ProceduralSky params); + +/** + * @brief Updates an existing custom sky cubemap. + * + * Re-renders the cubemap using the provided sky shader. Faster than unload + generate + * when animating sky conditions or updating shader uniforms (time, clouds, stars, etc.). + */ +R3DAPI void R3D_UpdateCustomSky(R3D_Cubemap* cubemap, R3D_SkyShader* shader); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Sky + +#endif // R3D_SKY_H + +``` + +`include/r3d/r3d_sky_shader.h`: + +```h +/* r3d_sky_shader.h -- R3D Sky Shader Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_SKY_SHADER_H +#define R3D_SKY_SHADER_H + +#include "./r3d_platform.h" +#include + +/** + * @defgroup SkyShader + * @{ + */ + +// ======================================== +// OPAQUE TYPES +// ======================================== + +typedef struct R3D_ShaderCustom R3D_SkyShader; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Loads a sky shader from a file. + * + * The shader must define a single entry point: + * `void fragment()`. Any other entry point, such as `vertex()`, + * or any varyings will be ignored. + * + * @param filePath Path to the shader source file. + * @return Pointer to the loaded sky shader, or NULL on failure. + */ +R3DAPI R3D_SkyShader* R3D_LoadSkyShader(const char* filePath); + +/** + * @brief Loads a sky shader from a source code string in memory. + * + * The shader must define a single entry point: + * `void fragment()`. Any other entry point, such as `vertex()`, + * or any varyings will be ignored. + * + * @param code Null-terminated shader source code. + * @return Pointer to the loaded sky shader, or NULL on failure. + */ +R3DAPI R3D_SkyShader* R3D_LoadSkyShaderFromMemory(const char* code); + +/** + * @brief Creates an alias of an existing sky shader. + * + * The alias shares the same compiled program as the original but holds its own + * independent uniform and sampler state. A typical use case is to pre-configure + * several aliases with different uniforms or textures, avoiding the need to + * reconfigure the shader on every skybox switch. + * + * Uniform and sampler state is copied from the original at the moment this + * function is called, not from the shader source defaults. Any values set + * on the original after compilation but before this call will be reflected + * in the alias; values set afterward will not. + * + * @note The alias does not own the program. Always unload all aliases before + * unloading the original, or the alias program references become dangling. + * + * @param shader The original sky shader to alias. + * @return Pointer to the alias, or NULL on failure. + */ +R3DAPI R3D_SkyShader* R3D_LoadSkyShaderAlias(R3D_SkyShader* shader); + +/** + * @brief Unloads and destroys a sky shader. + * + * If the shader owns its program shaders (i.e. it was created with @ref R3D_LoadSkyShader + * or @ref R3D_LoadSkyShaderFromMemory), they are deleted. Aliases created from this + * shader via @ref R3D_LoadSkyShaderAlias must be unloaded beforehand, as they + * share the same programs and will be left with dangling references. + * + * @param shader Sky shader to unload. + */ +R3DAPI void R3D_UnloadSkyShader(R3D_SkyShader* shader); + +/** + * @brief Sets a uniform value for all subsequent sky generations. + * + * Supported types: + * bool, int, float, + * ivec2, ivec3, ivec4, + * vec2, vec3, vec4, + * mat2, mat3, mat4 + * + * @warning Boolean values are read as 4 bytes. + * + * @param shader Target sky shader. + * May be NULL. In that case, the call is ignored + * and a warning is logged. + * @param name Name of the uniform. Must not be NULL. + * @param value Pointer to the uniform value. Must not be NULL. + */ +R3DAPI void R3D_SetSkyShaderUniform(R3D_SkyShader* shader, const char* name, const void* value); + +/** + * @brief Sets a uniform value for all subsequent sky generations. + * + * Supported samplers: + * sampler1D, sampler2D, sampler3D, samplerCube + * + * @param shader Target sky shader. + * May be NULL. In that case, the call is ignored + * and a warning is logged. + * @param name Name of the sampler uniform. Must not be NULL. + * @param texture Texture to bind to the sampler. + */ +R3DAPI void R3D_SetSkyShaderSampler(R3D_SkyShader* shader, const char* name, Texture texture); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of SkyShader + +#endif // R3D_SKY_SHADER_H + +``` + +`include/r3d/r3d_surface_shader.h`: + +```h +/* r3d_surface_shader.h -- R3D Surface Shader Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_SURFACE_SHADER_H +#define R3D_SURFACE_SHADER_H + +#include "./r3d_platform.h" +#include + +/** + * @defgroup SurfaceShader + * @{ + */ + +// ======================================== +// OPAQUE TYPES +// ======================================== + +typedef struct R3D_ShaderCustom R3D_SurfaceShader; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Loads a surface shader from a file. + * + * The shader must define at least one entry point: `void vertex()` or `void fragment()`. + * It can define either one or both. + * + * @param filePath Path to the shader source file. + * @return Pointer to the loaded surface shader, or NULL on failure. + */ +R3DAPI R3D_SurfaceShader* R3D_LoadSurfaceShader(const char* filePath); + +/** + * @brief Loads a surface shader from a source code string in memory. + * + * The shader must define at least one entry point: `void vertex()` or `void fragment()`. + * It can define either one or both. + * + * @param code Null-terminated shader source code. + * @return Pointer to the loaded surface shader, or NULL on failure. + */ +R3DAPI R3D_SurfaceShader* R3D_LoadSurfaceShaderFromMemory(const char* code); + +/** + * @brief Creates an alias of an existing surface shader. + * + * The alias shares the same compiled program as the original but holds its own + * independent uniform and sampler state, allowing the same shader to be used + * multiple times within a single frame with different values. + * + * Uniform and sampler state is copied from the original at the moment this + * function is called, not from the shader source defaults. Any values set + * on the original after compilation but before this call will be reflected + * in the alias; values set afterward will not. + * + * @note The alias does not own the program. Always unload all aliases before + * unloading the original, or the alias program references become dangling. + * + * @param shader The original surface shader to alias. + * @return Pointer to the alias, or NULL on failure. + */ +R3DAPI R3D_SurfaceShader* R3D_LoadSurfaceShaderAlias(R3D_SurfaceShader* shader); + +/** + * @brief Unloads and destroys a surface shader. + * + * If the shader owns its program shaders (i.e. it was created with @ref R3D_LoadSurfaceShader + * or @ref R3D_LoadSurfaceShaderFromMemory), they are deleted. Aliases created from this + * shader via @ref R3D_LoadSurfaceShaderAlias must be unloaded beforehand, as they + * share the same programs and will be left with dangling references. + * + * @param shader Surface shader to unload. + */ +R3DAPI void R3D_UnloadSurfaceShader(R3D_SurfaceShader* shader); + +/** + * @brief Sets a uniform value for the current frame. + * + * Once a uniform is set, it remains valid for the all frames. + * If an uniform is set multiple times during the same frame, + * the last value defined before R3D_End() is used. + * + * Supported types: + * bool, int, float, + * ivec2, ivec3, ivec4, + * vec2, vec3, vec4, + * mat2, mat3, mat4 + * + * @warning Boolean values are read as 4 bytes. + * + * @param shader Target surface shader. + * May be NULL. In that case, the call is ignored + * and a warning is logged. + * @param name Name of the uniform. Must not be NULL. + * @param value Pointer to the uniform value. Must not be NULL. + */ +R3DAPI void R3D_SetSurfaceShaderUniform(R3D_SurfaceShader* shader, const char* name, const void* value); + +/** + * @brief Sets a texture sampler for the current frame. + * + * Once a sampler is set, it remains valid for all frames. + * If a sampler is set multiple times during the same frame, + * the last value defined before R3D_End() is used. + * + * Supported samplers: + * sampler1D, sampler2D, sampler3D, samplerCube + * + * @param shader Target surface shader. + * May be NULL. In that case, the call is ignored + * and a warning is logged. + * @param name Name of the sampler uniform. Must not be NULL. + * @param texture Texture to bind to the sampler. + */ +R3DAPI void R3D_SetSurfaceShaderSampler(R3D_SurfaceShader* shader, const char* name, Texture texture); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of SurfaceShader + +#endif // R3D_SURFACE_SHADER_H + +``` + +`include/r3d/r3d_texture.h`: + +```h +/* r3d_texture.h -- R3D Texture Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_TEXTURE_H +#define R3D_TEXTURE_H + +#include "./r3d_platform.h" +#include + +/** + * @defgroup Texture + * @{ + */ + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Loads a texture from the specified file path. + * + * If 'isColor' is true, the texture is loaded using the currently defined color space (sRGB by default). + * The wrap and filter modes are taken from the current global default state. + * + * @param fileName The path to the texture file. + * @param isColor Whether the texture should be treated as color data. + * @return The loaded texture. + */ +R3DAPI Texture2D R3D_LoadTexture(const char* fileName, bool isColor); + +/** + * @brief Loads a texture from the specified file path. + * + * If 'isColor' is true, the texture is loaded using the currently defined color space (sRGB by default). + * + * @param fileName The path to the texture file. + * @param wrap The texture wrap mode. + * @param filter The texture filter mode. + * @param isColor Whether the texture should be treated as color data. + * @return The loaded texture. + */ +R3DAPI Texture2D R3D_LoadTextureEx(const char* fileName, TextureWrap wrap, TextureFilter filter, bool isColor); + +/** + * @brief Loads a texture from the specified image. + * + * If 'isColor' is true, the texture is loaded using the currently defined color space (sRGB by default). + * The wrap and filter modes are taken from the current global default state. + * + * @param image The source image. + * @param isColor Whether the texture should be treated as color data. + * @return The loaded texture. + */ +R3DAPI Texture2D R3D_LoadTextureFromImage(Image image, bool isColor); + +/** + * @brief Loads a texture from the specified image. + * + * If 'isColor' is true, the texture is loaded using the currently defined color space (sRGB by default). + * + * @param image The source image. + * @param wrap The texture wrap mode. + * @param filter The texture filter mode. + * @param isColor Whether the texture should be treated as color data. + * @return The loaded texture. + */ +R3DAPI Texture2D R3D_LoadTextureFromImageEx(Image image, TextureWrap wrap, TextureFilter filter, bool isColor); + +/** + * @brief Loads a texture directly from memory. + * + * If 'isColor' is true, the texture is loaded using the currently defined color space (sRGB by default). + * The wrap and filter modes are taken from the current global default state. + * + * @param fileType The file type/extension used to interpret the data. + * @param fileData A pointer to the file data in memory. + * @param dataSize The size of the file data in bytes. + * @param isColor Whether the texture should be treated as color data. + * @return The loaded texture. + */ +R3DAPI Texture2D R3D_LoadTextureFromMemory(const char* fileType, const void* fileData, int dataSize, bool isColor); + +/** + * @brief Loads a texture directly from memory. + * + * If 'isColor' is true, the texture is loaded using the currently defined color space (sRGB by default). + * + * @param fileType The file type/extension used to interpret the data. + * @param fileData A pointer to the file data in memory. + * @param dataSize The size of the file data in bytes. + * @param wrap The texture wrap mode. + * @param filter The texture filter mode. + * @param isColor Whether the texture should be treated as color data. + * @return The loaded texture. + */ +R3DAPI Texture2D R3D_LoadTextureFromMemoryEx(const char* fileType, const void* fileData, int dataSize, TextureWrap wrap, TextureFilter filter, bool isColor); + +/** + * @brief Unloads a texture. + * + * This function calls raylib's `UnloadTexture` internally, while ensuring that + * the provided texture is not an internal r3d texture. + * + * @param texture The texture to unload. + */ +R3DAPI void R3D_UnloadTexture(Texture2D texture); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Texture + +#endif // R3D_TEXTURE_H + +``` + +`include/r3d/r3d_utils.h`: + +```h +/* r3d_utils.h -- R3D Utility Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_UTILS_H +#define R3D_UTILS_H + +#include "./r3d_platform.h" +#include + +/** + * @defgroup Utils Utility Functions + * @brief Defines some utility functions, including access to internal data, useful for adding additional effects. + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +// ---------------------------------------- +// UTILS: Default Texture Retrieval Functions +// ---------------------------------------- + +/** + * @brief Retrieves a default white texture. + * + * This texture is fully white (1,1,1,1), useful for default material properties. + * + * @return A white texture. + */ +R3DAPI Texture2D R3D_GetWhiteTexture(void); + +/** + * @brief Retrieves a default black texture. + * + * This texture is fully black (0,0,0,1), useful for masking or default values. + * + * @return A black texture. + */ +R3DAPI Texture2D R3D_GetBlackTexture(void); + +/** + * @brief Retrieves a default normal map texture. + * + * This texture represents a neutral normal map (0.5, 0.5, 1.0), which applies no normal variation. + * + * @return A neutral normal texture. + */ +R3DAPI Texture2D R3D_GetNormalTexture(void); + +// ---------------------------------------- +// UTILS: Render Texture Retrieval Functions +// ---------------------------------------- + +/** + * @brief Retrieves the buffer containing the scene's normal data. + * + * This texture stores octahedral-compressed normals using two 16-bit per-channel RG components. + * + * @note You can find the decoding functions in 'shaders/include/math.glsl'. + * + * @return The normal buffer texture. + */ +R3DAPI Texture2D R3D_GetBufferNormal(void); + +/** + * @brief Retrieves the final depth buffer. + * + * This texture is an R16 texture containing a linear depth value + * normalized between the near and far clipping planes. + * + * The texture is intended for post-processing effects outside of R3D + * that require access to linear depth information. + * + * @return The final depth buffer texture (R16, linear depth). + */ +R3DAPI Texture2D R3D_GetBufferDepth(void); + +// ---------------------------------------- +// UTILS: Camera Matrices Retrieval Functions +// ---------------------------------------- + +/** + * @brief Retrieves the view matrix. + * + * This matrix represents the camera's transformation from world space to view space. + * It is updated at the last call to 'R3D_Begin'. + * + * @return The current view matrix. + */ +R3DAPI Matrix R3D_GetMatrixView(void); + +/** + * @brief Retrieves the inverse view matrix. + * + * This matrix transforms coordinates from view space back to world space. + * It is updated at the last call to 'R3D_Begin'. + * + * @return The current inverse view matrix. + */ +R3DAPI Matrix R3D_GetMatrixInvView(void); + +/** + * @brief Retrieves the projection matrix. + * + * This matrix defines the transformation from view space to clip space. + * It is updated at the last call to 'R3D_Begin'. + * + * @return The current projection matrix. + */ +R3DAPI Matrix R3D_GetMatrixProjection(void); + +/** + * @brief Retrieves the inverse projection matrix. + * + * This matrix transforms coordinates from clip space back to view space. + * It is updated at the last call to 'R3D_Begin'. + * + * @return The current inverse projection matrix. + */ +R3DAPI Matrix R3D_GetMatrixInvProjection(void); + +/** + * @brief Retrieves the view-projection matrix. + * + * This matrix represents the transformation from world space to clip space. + * It is updated at the last call to 'R3D_Begin'. + * + * @return The current view-projection matrix. + */ +R3DAPI Matrix R3D_GetMatrixViewProjection(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Utils + +#endif // R3D_UTILS_H + +``` + +`include/r3d/r3d_vertex.h`: + +```h +/* r3d_vertex.h -- R3D Vertex Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_VERTEX_H +#define R3D_VERTEX_H + +#include "./r3d_platform.h" +#include +#include + +/** + * @defgroup Vertex Vertex + * @{ + */ + +// ======================================== +// STRUCTS TYPES +// ======================================== + +/** + * @brief Compact vertex format used by R3D meshes. + * + * Texture coordinates are stored as float16 values. + * Normals and tangents are stored as signed normalized 8-bit values. + * Bone weights are stored as unsigned 8-bit values and should sum to 255. + */ +typedef struct R3D_Vertex { + Vector3 position; ///< Vertex position in object space. + uint16_t texcoord[2]; ///< Texture coordinates stored as float16. + int8_t normal[4]; ///< Normal vector stored as SNORM8. XYZ are used, W is unused. + int8_t tangent[4]; ///< Tangent vector stored as SNORM8. XYZ are tangent, W stores handedness. + Color color; ///< Vertex color in RGBA8. + uint8_t boneIndices[4]; ///< Indices of up to 4 bones influencing this vertex. + uint8_t boneWeights[4]; ///< Bone weights in UNORM8. Values should sum to 255. +} R3D_Vertex; + +// ======================================== +// PUBLIC API +// ======================================== + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Constructs a packed R3D vertex from unpacked attribute data. + * + * Texture coordinates are packed to float16. + * Normals and tangents are packed to SNORM8. + * + * @param position Vertex position in object space. + * @param texcoord Texture coordinates in float32. Any range is supported. + * @param normal Normal vector. Components are clamped to the [-1, 1] range. + * @param tangent Tangent vector. XYZ components are clamped to [-1, 1], W stores handedness. + * @param color Vertex color in RGBA8. + * + * @return Packed vertex ready for GPU upload. + */ +R3DAPI R3D_Vertex R3D_MakeVertex(Vector3 position, Vector2 texcoord, Vector3 normal, Vector4 tangent, Color color); + +/** + * @brief Packs texture coordinates from float32 to float16. + * + * @param dst Output buffer of 2 uint16_t values. Must not be NULL. + * @param src Texture coordinates in float32. Any range is supported. + */ +R3DAPI void R3D_PackTexCoord(uint16_t* dst, Vector2 src); + +/** + * @brief Unpacks texture coordinates from float16 to float32. + * + * @param src Input buffer of 2 uint16_t values. Must not be NULL. + * + * @return Unpacked texture coordinates in float32. + */ +R3DAPI Vector2 R3D_UnpackTexCoord(const uint16_t* src); + +/** + * @brief Packs a normal vector from float32 to SNORM8. + * + * XYZ components are clamped to the [-1, 1] range before packing. + * The fourth component is set to 0. + * + * @param dst Output buffer of 4 int8_t values. Must not be NULL. + * @param src Normal vector to pack. + */ +R3DAPI void R3D_PackNormal(int8_t* dst, Vector3 src); + +/** + * @brief Unpacks a normal vector from SNORM8 to float32. + * + * Only XYZ components are read. + * + * @param src Input buffer of 4 int8_t values. Must not be NULL. + * + * @return Unpacked normal vector. Not guaranteed to be unit length. + */ +R3DAPI Vector3 R3D_UnpackNormal(const int8_t* src); + +/** + * @brief Packs a tangent vector from float32 to SNORM8. + * + * XYZ components are clamped to the [-1, 1] range before packing. + * W stores tangent handedness and is packed as either +1 or -1. + * + * @param dst Output buffer of 4 int8_t values. Must not be NULL. + * @param src Tangent vector to pack. W is interpreted as handedness. + */ +R3DAPI void R3D_PackTangent(int8_t* dst, Vector4 src); + +/** + * @brief Unpacks a tangent vector from SNORM8 to float32. + * + * XYZ components are unpacked from SNORM8. + * W is returned as exactly +1.0f or -1.0f. + * + * @param src Input buffer of 4 int8_t values. Must not be NULL. + * + * @return Unpacked tangent vector. + */ +R3DAPI Vector4 R3D_UnpackTangent(const int8_t* src); + +#ifdef __cplusplus +} // extern "C" +#endif + +/** @} */ // end of Vertex + +#endif // R3D_VERTEX_H + +``` + +`scripts/bin2c.py`: + +```py +# Copyright (c) 2025-2026 Le Juez Victor +# +# This software is provided "as-is", without any express or implied warranty. In no event +# will the authors be held liable for any damages arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, including commercial +# applications, and to alter it and redistribute it freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not claim that you +# wrote the original software. If you use this software in a product, an acknowledgment +# in the product documentation would be appreciated but is not required. +# +# 2. Altered source versions must be plainly marked as such, and must not be misrepresented +# as being the original software. +# +# 3. This notice may not be removed or altered from any source distribution. + +#!/usr/bin/env python3 + +import sys, os, argparse + +def to_identifier(name): + """Convert filename to valid C identifier""" + return name.upper().replace('.', '_').replace('-', '_') + +def escape_c_string(text): + """Escape special characters for C string literals""" + text = text.replace('\\', '\\\\') # Double backslashes + text = text.replace('"', '\\"') # Escape quotes + text = text.replace('\n', '\\n') # Convert actual newlines to \n + text = text.replace('\r', '\\r') # Convert carriage returns + text = text.replace('\t', '\\t') # Convert tabs + return text + +def write_header_from_file(file_path, out_path, mode='binary', custom_name=None): + """Convert a file into a C header""" + if custom_name: + array_name = to_identifier(custom_name) + guard = array_name + '_H' + else: + name = os.path.basename(file_path) + guard = to_identifier(name) + '_H' + array_name = to_identifier(name) + + if mode == 'text': + with open(file_path, 'r', encoding='utf-8') as f: + text = f.read() + data = text.encode('utf-8') + else: + with open(file_path, 'rb') as f: + data = f.read() + + write_data_to_header(data, out_path, guard, array_name, mode) + +def write_header_from_string(input_string, array_name, out_path, mode='binary'): + """Convert a string into a C header""" + guard = to_identifier(array_name) + '_H' + array_name = to_identifier(array_name) + + if mode == 'text': + try: + interpreted_string = input_string.encode().decode('unicode_escape') + data = interpreted_string.encode('utf-8') + except UnicodeDecodeError: + data = input_string.encode('utf-8') + else: + data = input_string.encode('utf-8') + + write_data_to_header(data, out_path, guard, array_name, mode) + +def write_header_from_stdin(array_name, out_path, mode='binary'): + """Convert stdin input into a C header""" + guard = to_identifier(array_name) + '_H' + array_name = to_identifier(array_name) + + input_string = sys.stdin.read() + + if mode == 'text': + try: + interpreted_string = input_string.encode().decode('unicode_escape') + data = interpreted_string.encode('utf-8') + except UnicodeDecodeError: + data = input_string.encode('utf-8') + else: + data = input_string.encode('utf-8') + + write_data_to_header(data, out_path, guard, array_name, mode) + +def write_data_to_header(data, out_path, guard, array_name, mode='binary'): + """Write data to C header in text or binary format""" + + with open(out_path, 'w') as f: + f.write(f"#ifndef {guard}\n") + f.write(f"#define {guard}\n\n") + + f.write("#ifdef __cplusplus\n") + f.write("extern \"C\" {\n") + f.write("#endif\n\n") + + if mode == 'text': + write_text_array(f, data, array_name) + else: + write_binary_array(f, data, array_name) + + f.write("#ifdef __cplusplus\n") + f.write("}\n") + f.write("#endif\n\n") + + f.write(f"#endif // {guard}\n") + +def write_text_array(f, data, array_name, chunk_size=16000): + """Write data as single-line C string literal""" + try: + text = data.decode('utf-8') + except UnicodeDecodeError: + write_binary_array(f, data, array_name) + return + + text_size = len(data) + + f.write(f'static const char {array_name}[] =\n') + for i in range(0, len(text), chunk_size): + escaped_chunk = escape_c_string(text[i:i + chunk_size]) + f.write(f' "{escaped_chunk}"\n') + f.write(';\n\n') + f.write(f"#define {array_name}_SIZE {text_size}\n\n") + +def write_binary_array(f, data, array_name): + """Write data as binary byte array""" + if not data.endswith(b'\0'): + data = data + b'\0' + + data_size = len(data) - 1 + + f.write(f"static const unsigned char {array_name}[] =\n") + f.write("{\n") + + for i in range(0, len(data), 16): + chunk = data[i:i+16] + f.write(" ") + hex_values = [f"0x{byte:02X}" for byte in chunk] + f.write(", ".join(hex_values)) + if i + 16 < len(data): + f.write(",") + f.write("\n") + + f.write("};\n\n") + f.write(f"#define {array_name}_SIZE {data_size}\n\n") + +def main(): + parser = argparse.ArgumentParser( + description='Convert a file, string, or stdin to a C array header' + ) + parser.add_argument('output', help='Output header file (.h)') + + input_group = parser.add_mutually_exclusive_group(required=True) + input_group.add_argument('-f', '--file', help='Input file to convert') + input_group.add_argument('-s', '--string', help='String to convert') + input_group.add_argument('--stdin', action='store_true', help='Read from stdin') + + parser.add_argument('-n', '--name', help='Array name (overrides filename-based name when using --file)') + parser.add_argument('-m', '--mode', choices=['text', 'binary'], + default='binary', + help='Output mode: text for string literals, binary for byte arrays (default: binary)') + + args = parser.parse_args() + + if (args.string or args.stdin) and not args.name: + parser.error("--name is required when using --string or --stdin") + + if args.file: + write_header_from_file(args.file, args.output, args.mode, args.name) + elif args.string: + write_header_from_string(args.string, args.name, args.output, args.mode) + elif args.stdin: + write_header_from_stdin(args.name, args.output, args.mode) + +if __name__ == "__main__": + main() + +``` + +`scripts/glsl_processor.py`: + +```py +# Copyright (c) 2025-2026 Le Juez Victor +# +# This software is provided "as-is", without any express or implied warranty. In no event +# will the authors be held liable for any damages arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, including commercial +# applications, and to alter it and redistribute it freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not claim that you +# wrote the original software. If you use this software in a product, an acknowledgment +# in the product documentation would be appreciated but is not required. +# +# 2. Altered source versions must be plainly marked as such, and must not be misrepresented +# as being the original software. +# +# 3. This notice may not be removed or altered from any source distribution. + +#!/usr/bin/env python3 + +import sys, re, zlib, struct, argparse +from pathlib import Path + +# === Processing Passes === # + +def process_includes(shader_content, base_path, included_files=None): + """Recursively resolve #include directives in GLSL shader code.""" + if included_files is None: + included_files = set() + + base_path = Path(base_path) + include_pattern = re.compile(r'^\s*#include\s+"([^"]+)"', re.MULTILINE) + + def replacer(match): + file_path = (base_path / match.group(1)).resolve() + + if file_path in included_files: + return "" + + if not file_path.is_file(): + print(f"Include not found: {file_path}", file=sys.stderr) + return "" + + included_files.add(file_path) + content = file_path.read_text(encoding="utf-8") + return process_includes(content, file_path.parent, included_files) + "\n" + + return include_pattern.sub(replacer, shader_content) + +def remove_comments(shader_content): + """Remove C-style comments""" + shader_content = re.sub(r'/\*.*?\*/', '', shader_content, flags=re.DOTALL) + shader_content = re.sub(r'//.*?(?=\n|$)', '', shader_content) + return shader_content + +def remove_newlines(shader_content): + """Remove unnecessary newlines while preserving preprocessor directive spacing""" + lines = [line for line in shader_content.splitlines() if line.strip()] + if not lines: + return "" + + result = [] + for i, line in enumerate(lines): + is_directive = line.lstrip().startswith('#') + prev_directive = i > 0 and lines[i-1].lstrip().startswith('#') + next_directive = i < len(lines)-1 and lines[i+1].lstrip().startswith('#') + if i > 0 and (is_directive or prev_directive or next_directive): + result.append("\n") + result.append(line) + + return "".join(result) + +def normalize_spaces(shader_content): + """Remove redundant spaces around operators and symbols, excluding preprocessor directives""" + lines = shader_content.split('\n') + processed_lines = [] + + symbols = [ + ',', '.', '(', ')', '{', '}', ';', ':', + '+', '-', '*', '/', '=', '<', '>', + '!', '?', '|', '&' + ] + + for line in lines: + if line.lstrip().startswith('#'): + line = re.sub(r'^\s*#', '#', line) # Remove spaces before the '#' + line = re.sub(r'[ \t]+', ' ', line) # Replace consecutive spaces to one + processed_lines.append(line) + else: + # Apply normalization to other lines + processed_line = line + for symbol in symbols: + escaped = re.escape(symbol) + processed_line = re.sub(rf'[ \t]+{escaped}', symbol, processed_line) + processed_line = re.sub(rf'{escaped}[ \t]+', symbol, processed_line) + + processed_line = re.sub(r'[ \t]+', ' ', processed_line) + processed_lines.append(processed_line) + + return '\n'.join(processed_lines) + +def optimize_float_literals(shader_content): + """Optimize float literal notation""" + shader_content = re.sub(r'\b(\d+)\.0+(?!\d)', r'\1.', shader_content) # 1.000 -> 1. + shader_content = re.sub(r'\b0\.([1-9]\d*)\b', r'.\1', shader_content) # 0.5 -> .5 (but no 0.0 -> .0) + return shader_content + +# === Main === # + +def process_shader(filepath): + """Process a shader file through all optimization passes""" + filepath = Path(filepath) + + try: + with open(filepath, 'r', encoding='utf-8') as f: + shader_content = f.read() + except FileNotFoundError: + print(f"Error: File not found: {filepath}", file=sys.stderr) + sys.exit(1) + except Exception as e: + print(f"Error reading file: {e}", file=sys.stderr) + sys.exit(1) + + shader_content = process_includes(shader_content, filepath.parent) + shader_content = remove_comments(shader_content) + shader_content = remove_newlines(shader_content) + shader_content = normalize_spaces(shader_content) + shader_content = optimize_float_literals(shader_content) + + return shader_content + +def compress_shader(shader_content): + """Compresses the content with zlib (DEFLATE) then encodes in base64""" + shader_bytes = shader_content.encode('utf-8') + uncompressed_size = len(shader_bytes) + shader_compressed = zlib.compress(shader_bytes) + header = struct.pack('= 0 && uLight.shadowOpacity != 0.0 && shadow > 1e-4) { + mat2 diskRot = L_ShadowDebandingMatrix(gl_FragCoord.xy); + switch (uLight.type) { + case LIGHT_DIR: shadow *= L_SampleShadowDir(P, depth, NdotL, diskRot); break; + case LIGHT_SPOT: shadow *= L_SampleShadowSpot(P, NdotL, diskRot); break; + case LIGHT_OMNI: shadow *= L_SampleShadowOmni(P, NdotL, diskRot); break; + } + } + + /* Compute final lighting contribution */ + + FragDiff = vec4(diff * shadow, 1.0); + FragSpec = vec4(spec * shadow, 1.0); +} + +``` + +`shaders/external/Fxaa3_11.h`: + +```h +/*============================================================================ + + + NVIDIA FXAA 3.11 by TIMOTHY LOTTES + + +------------------------------------------------------------------------------ +COPYRIGHT (C) 2010, 2011 NVIDIA CORPORATION. ALL RIGHTS RESERVED. +------------------------------------------------------------------------------ +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED +*AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA +OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR +LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, +OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE +THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +------------------------------------------------------------------------------ + INTEGRATION CHECKLIST +------------------------------------------------------------------------------ +(1.) +In the shader source, setup defines for the desired configuration. +When providing multiple shaders (for different presets), +simply setup the defines differently in multiple files. +Example, + + #define FXAA_PC 1 + #define FXAA_HLSL_5 1 + #define FXAA_QUALITY__PRESET 12 + +Or, + + #define FXAA_360 1 + +Or, + + #define FXAA_PS3 1 + +Etc. + +(2.) +Then include this file, + + #include "Fxaa3_11.h" + +(3.) +Then call the FXAA pixel shader from within your desired shader. +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. +As for FXAA 3.11 all inputs for all shaders are the same +to enable easy porting between platforms. + + return FxaaPixelShader(...); + +(4.) +Insure pass prior to FXAA outputs RGBL (see next section). +Or use, + + #define FXAA_GREEN_AS_LUMA 1 + +(5.) +Setup engine to provide the following constants +which are used in the FxaaPixelShader() inputs, + + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. + +(6.) +Have FXAA vertex shader run as a full screen triangle, +and output "pos" and "fxaaConsolePosPos" +such that inputs in the pixel shader provide, + + // {xy} = center of pixel + FxaaFloat2 pos, + + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + +(7.) +Insure the texture sampler(s) used by FXAA are set to bilinear filtering. + + +------------------------------------------------------------------------------ + INTEGRATION - RGBL AND COLORSPACE +------------------------------------------------------------------------------ +FXAA3 requires RGBL as input unless the following is set, + + #define FXAA_GREEN_AS_LUMA 1 + +In which case the engine uses green in place of luma, +and requires RGB input is in a non-linear colorspace. + +RGB should be LDR (low dynamic range). +Specifically do FXAA after tonemapping. + +RGB data as returned by a texture fetch can be non-linear, +or linear when FXAA_GREEN_AS_LUMA is not set. +Note an "sRGB format" texture counts as linear, +because the result of a texture fetch is linear data. +Regular "RGBA8" textures in the sRGB colorspace are non-linear. + +If FXAA_GREEN_AS_LUMA is not set, +luma must be stored in the alpha channel prior to running FXAA. +This luma should be in a perceptual space (could be gamma 2.0). +Example pass before FXAA where output is gamma 2.0 encoded, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + color.a = dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114)); // compute luma + return color; + +Another example where output is linear encoded, +say for instance writing to an sRGB formated render target, +where the render target does the conversion back to sRGB after blending, + + color.rgb = ToneMap(color.rgb); // linear color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.a = sqrt(dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114))); // compute luma + return color; + +Getting luma correct is required for the algorithm to work correctly. + + +------------------------------------------------------------------------------ + BEING LINEARLY CORRECT? +------------------------------------------------------------------------------ +Applying FXAA to a framebuffer with linear RGB color will look worse. +This is very counter intuitive, but happends to be true in this case. +The reason is because dithering artifacts will be more visiable +in a linear colorspace. + + +------------------------------------------------------------------------------ + COMPLEX INTEGRATION +------------------------------------------------------------------------------ +Q. What if the engine is blending into RGB before wanting to run FXAA? + +A. In the last opaque pass prior to FXAA, + have the pass write out luma into alpha. + Then blend into RGB only. + FXAA should be able to run ok + assuming the blending pass did not any add aliasing. + This should be the common case for particles and common blending passes. + +A. Or use FXAA_GREEN_AS_LUMA. + +============================================================================*/ + +/*============================================================================ + + INTEGRATION KNOBS + +============================================================================*/ +// +// FXAA_PS3 and FXAA_360 choose the console algorithm (FXAA3 CONSOLE). +// FXAA_360_OPT is a prototype for the new optimized 360 version. +// +// 1 = Use API. +// 0 = Don't use API. +// +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_PS3 + #define FXAA_PS3 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_360 + #define FXAA_360 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_360_OPT + #define FXAA_360_OPT 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_PC + // + // FXAA Quality + // The high quality PC algorithm. + // + #define FXAA_PC 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_PC_CONSOLE + // + // The console algorithm for PC is included + // for developers targeting really low spec machines. + // Likely better to just run FXAA_PC, and use a really low preset. + // + #define FXAA_PC_CONSOLE 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_120 + #define FXAA_GLSL_120 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_130 + #define FXAA_GLSL_130 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_3 + #define FXAA_HLSL_3 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_4 + #define FXAA_HLSL_4 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_5 + #define FXAA_HLSL_5 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_GREEN_AS_LUMA + // + // For those using non-linear color, + // and either not able to get luma in alpha, or not wanting to, + // this enables FXAA to run using green as a proxy for luma. + // So with this enabled, no need to pack luma in alpha. + // + // This will turn off AA on anything which lacks some amount of green. + // Pure red and blue or combination of only R and B, will get no AA. + // + // Might want to lower the settings for both, + // fxaaConsoleEdgeThresholdMin + // fxaaQualityEdgeThresholdMin + // In order to insure AA does not get turned off on colors + // which contain a minor amount of green. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_GREEN_AS_LUMA 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_EARLY_EXIT + // + // Controls algorithm's early exit path. + // On PS3 turning this ON adds 2 cycles to the shader. + // On 360 turning this OFF adds 10ths of a millisecond to the shader. + // Turning this off on console will result in a more blurry image. + // So this defaults to on. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_EARLY_EXIT 1 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_DISCARD + // + // Only valid for PC OpenGL currently. + // Probably will not work when FXAA_GREEN_AS_LUMA = 1. + // + // 1 = Use discard on pixels which don't need AA. + // For APIs which enable concurrent TEX+ROP from same surface. + // 0 = Return unchanged color on pixels which don't need AA. + // + #define FXAA_DISCARD 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_FAST_PIXEL_OFFSET + // + // Used for GLSL 120 only. + // + // 1 = GL API supports fast pixel offsets + // 0 = do not use fast pixel offsets + // + #ifdef GL_EXT_gpu_shader4 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifndef FXAA_FAST_PIXEL_OFFSET + #define FXAA_FAST_PIXEL_OFFSET 0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GATHER4_ALPHA + // + // 1 = API supports gather4 on alpha channel. + // 0 = API does not support gather4 on alpha channel. + // + #if (FXAA_HLSL_5 == 1) + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifndef FXAA_GATHER4_ALPHA + #define FXAA_GATHER4_ALPHA 0 + #endif +#endif + +/*============================================================================ + FXAA CONSOLE PS3 - TUNING KNOBS +============================================================================*/ +#ifndef FXAA_CONSOLE__PS3_EDGE_SHARPNESS + // + // Consoles the sharpness of edges on PS3 only. + // Non-PS3 tuning is done with shader input. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 8.0 is sharper + // 4.0 is softer + // 2.0 is really soft (good for vector graphics inputs) + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 8.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 4.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 2.0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_CONSOLE__PS3_EDGE_THRESHOLD + // + // Only effects PS3. + // Non-PS3 tuning is done with shader input. + // + // The minimum amount of local contrast required to apply algorithm. + // The console setting has a different mapping than the quality setting. + // + // This only applies when FXAA_EARLY_EXIT is 1. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 0.25 and 0.125. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 0.125 leaves less aliasing, but is softer + // 0.25 leaves more aliasing, and is sharper + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.125 + #else + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.25 + #endif +#endif + +/*============================================================================ + FXAA QUALITY - TUNING KNOBS +------------------------------------------------------------------------------ +NOTE the other tuning knobs are now in the shader function inputs! +============================================================================*/ +#ifndef FXAA_QUALITY__PRESET + // + // Choose the quality preset. + // This needs to be compiled into the shader as it effects code. + // Best option to include multiple presets is to + // in each shader define the preset, then include this file. + // + // OPTIONS + // ----------------------------------------------------------------------- + // 10 to 15 - default medium dither (10=fastest, 15=highest quality) + // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality) + // 39 - no dither, very expensive + // + // NOTES + // ----------------------------------------------------------------------- + // 12 = slightly faster then FXAA 3.9 and higher edge quality (default) + // 13 = about same speed as FXAA 3.9 and better than 12 + // 23 = closest to FXAA 3.9 visually and performance wise + // _ = the lowest digit is directly related to performance + // _ = the highest digit is directly related to style + // + #define FXAA_QUALITY__PRESET 12 +#endif + + +/*============================================================================ + + FXAA QUALITY - PRESETS + +============================================================================*/ + +/*============================================================================ + FXAA QUALITY - MEDIUM DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 10) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 3.0 + #define FXAA_QUALITY__P2 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 11) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 3.0 + #define FXAA_QUALITY__P3 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 12) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 4.0 + #define FXAA_QUALITY__P4 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 13) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 4.0 + #define FXAA_QUALITY__P5 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 14) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 4.0 + #define FXAA_QUALITY__P6 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 15) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 12.0 +#endif + +/*============================================================================ + FXAA QUALITY - LOW DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 20) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 2.0 + #define FXAA_QUALITY__P2 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 21) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 22) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 23) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 24) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 3.0 + #define FXAA_QUALITY__P6 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 25) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 26) + #define FXAA_QUALITY__PS 9 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 4.0 + #define FXAA_QUALITY__P8 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 27) + #define FXAA_QUALITY__PS 10 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 4.0 + #define FXAA_QUALITY__P9 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 28) + #define FXAA_QUALITY__PS 11 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 4.0 + #define FXAA_QUALITY__P10 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 29) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + +/*============================================================================ + FXAA QUALITY - EXTREME QUALITY +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 39) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.0 + #define FXAA_QUALITY__P2 1.0 + #define FXAA_QUALITY__P3 1.0 + #define FXAA_QUALITY__P4 1.0 + #define FXAA_QUALITY__P5 1.5 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + + + +/*============================================================================ + + API PORTING + +============================================================================*/ +#if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) + #define FxaaBool bool + #define FxaaDiscard discard + #define FxaaFloat float + #define FxaaFloat2 vec2 + #define FxaaFloat3 vec3 + #define FxaaFloat4 vec4 + #define FxaaHalf float + #define FxaaHalf2 vec2 + #define FxaaHalf3 vec3 + #define FxaaHalf4 vec4 + #define FxaaInt2 ivec2 + #define FxaaSat(x) clamp(x, 0.0, 1.0) + #define FxaaTex sampler2D +#else + #define FxaaBool bool + #define FxaaDiscard clip(-1) + #define FxaaFloat float + #define FxaaFloat2 float2 + #define FxaaFloat3 float3 + #define FxaaFloat4 float4 + #define FxaaHalf half + #define FxaaHalf2 half2 + #define FxaaHalf3 half3 + #define FxaaHalf4 half4 + #define FxaaSat(x) saturate(x) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_120 == 1) + // Requires, + // #version 120 + // And at least, + // #extension GL_EXT_gpu_shader4 : enable + // (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9) + #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) + #if (FXAA_FAST_PIXEL_OFFSET == 1) + #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) + #else + #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) + #endif + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_130 == 1) + // Requires "#version 130" or better + #define FxaaTexTop(t, p) textureLod(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_3 == 1) || (FXAA_360 == 1) || (FXAA_PS3 == 1) + #define FxaaInt2 float2 + #define FxaaTex sampler2D + #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0)) + #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0)) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_4 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_5 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) + #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) + #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) + #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) + #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) +#endif + + +/*============================================================================ + GREEN AS LUMA OPTION SUPPORT FUNCTION +============================================================================*/ +#if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } +#else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } +#endif + + + + +/*============================================================================ + + FXAA3 QUALITY - PC + +============================================================================*/ +#if (FXAA_PC == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy} = center of pixel + FxaaFloat2 pos, + // + // Used only for FXAA Console, and not used on the 360 version. + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + // + // Input color texture. + // {rgb_} = color in linear or perceptual color space + // if (FXAA_GREEN_AS_LUMA == 0) + // {___a} = luma in perceptual color space (not linear) + FxaaTex tex, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 2nd sampler. + // This sampler needs to have an exponent bias of -1. + FxaaTex fxaaConsole360TexExpBiasNegOne, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 3nd sampler. + // This sampler needs to have an exponent bias of -2. + FxaaTex fxaaConsole360TexExpBiasNegTwo, + // + // Only used on FXAA Quality. + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + FxaaFloat2 fxaaQualityRcpFrame, + // + // Only used on FXAA Console. + // This must be from a constant/uniform. + // This effects sub-pixel AA quality and inversely sharpness. + // Where N ranges between, + // N = 0.50 (default) + // N = 0.33 (sharper) + // {x___} = -N/screenWidthInPixels + // {_y__} = -N/screenHeightInPixels + // {__z_} = N/screenWidthInPixels + // {___w} = N/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt, + // + // Only used on FXAA Console. + // Not used on 360, but used on PS3 and PC. + // This must be from a constant/uniform. + // {x___} = -2.0/screenWidthInPixels + // {_y__} = -2.0/screenHeightInPixels + // {__z_} = 2.0/screenWidthInPixels + // {___w} = 2.0/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + // + // Only used on FXAA Console. + // Only used on 360 in place of fxaaConsoleRcpFrameOpt2. + // This must be from a constant/uniform. + // {x___} = 8.0/screenWidthInPixels + // {_y__} = 8.0/screenHeightInPixels + // {__z_} = -4.0/screenWidthInPixels + // {___w} = -4.0/screenHeightInPixels + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + FxaaFloat fxaaQualitySubpix, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + FxaaFloat fxaaQualityEdgeThreshold, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaQualityEdgeThresholdMin, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + FxaaFloat fxaaConsoleEdgeSharpness, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_THRESHOLD for PS3. + // Due to the PS3 being ALU bound, + // there are only two safe values here: 1/4 and 1/8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // The console setting has a different mapping than the quality setting. + // Other platforms can use other values. + // 0.125 leaves less aliasing, but is softer (default!!!) + // 0.25 leaves more aliasing, and is sharper + FxaaFloat fxaaConsoleEdgeThreshold, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // The console setting has a different mapping than the quality setting. + // This only applies when FXAA_EARLY_EXIT is 1. + // This does not apply to PS3, + // PS3 was simplified to avoid more shader instructions. + // 0.06 - faster but more aliasing in darks + // 0.05 - default + // 0.04 - slower and less aliasing in darks + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaConsoleEdgeThresholdMin, + // + // Extra constants for 360 FXAA Console only. + // Use zeros or anything else for other platforms. + // These must be in physical constant registers and NOT immedates. + // Immedates will result in compiler un-optimizing. + // {xyzw} = float4(1.0, -1.0, 0.25, -0.25) + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; +/*--------------------------------------------------------------------------*/ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif +/*--------------------------------------------------------------------------*/ + #if (FXAA_GATHER4_ALPHA == 0) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; +/*--------------------------------------------------------------------------*/ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; +/*--------------------------------------------------------------------------*/ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; +/*--------------------------------------------------------------------------*/ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY__P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY__P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY__P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY__P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); +/*--------------------------------------------------------------------------*/ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; +/*--------------------------------------------------------------------------*/ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1; +/*--------------------------------------------------------------------------*/ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12; +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } +/*--------------------------------------------------------------------------*/ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; +/*--------------------------------------------------------------------------*/ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; +/*--------------------------------------------------------------------------*/ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; +/*--------------------------------------------------------------------------*/ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + #endif +} +/*==========================================================================*/ +#endif + + + + +/*============================================================================ + + FXAA3 CONSOLE - PC VERSION + +------------------------------------------------------------------------------ +Instead of using this on PC, I'd suggest just using FXAA Quality with + #define FXAA_QUALITY__PRESET 10 +Or + #define FXAA_QUALITY__PRESET 20 +Either are higher qualilty and almost as fast as this on modern PC GPUs. +============================================================================*/ +#if (FXAA_PC_CONSOLE == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xy)); + FxaaFloat lumaSw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xw)); + FxaaFloat lumaNe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zy)); + FxaaFloat lumaSe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zw)); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyM = FxaaTexTop(tex, pos.xy); + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat lumaM = rgbyM.w; + #else + FxaaFloat lumaM = rgbyM.y; + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNwSw = max(lumaNw, lumaSw); + lumaNe += 1.0/384.0; + FxaaFloat lumaMinNwSw = min(lumaNw, lumaSw); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNeSe = max(lumaNe, lumaSe); + FxaaFloat lumaMinNeSe = min(lumaNe, lumaSe); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMax = max(lumaMaxNeSe, lumaMaxNwSw); + FxaaFloat lumaMin = min(lumaMinNeSe, lumaMinNwSw); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxScaled = lumaMax * fxaaConsoleEdgeThreshold; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMinM = min(lumaMin, lumaM); + FxaaFloat lumaMaxScaledClamped = max(fxaaConsoleEdgeThresholdMin, lumaMaxScaled); + FxaaFloat lumaMaxM = max(lumaMax, lumaM); + FxaaFloat dirSwMinusNe = lumaSw - lumaNe; + FxaaFloat lumaMaxSubMinM = lumaMaxM - lumaMinM; + FxaaFloat dirSeMinusNw = lumaSe - lumaNw; + if(lumaMaxSubMinM < lumaMaxScaledClamped) return rgbyM; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 dir; + dir.x = dirSwMinusNe + dirSeMinusNw; + dir.y = dirSwMinusNe - dirSeMinusNw; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 dir1 = normalize(dir.xy); + FxaaFloat4 rgbyN1 = FxaaTexTop(tex, pos.xy - dir1 * fxaaConsoleRcpFrameOpt.zw); + FxaaFloat4 rgbyP1 = FxaaTexTop(tex, pos.xy + dir1 * fxaaConsoleRcpFrameOpt.zw); +/*--------------------------------------------------------------------------*/ + FxaaFloat dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * fxaaConsoleEdgeSharpness; + FxaaFloat2 dir2 = clamp(dir1.xy / dirAbsMinTimesC, -2.0, 2.0); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyN2 = FxaaTexTop(tex, pos.xy - dir2 * fxaaConsoleRcpFrameOpt2.zw); + FxaaFloat4 rgbyP2 = FxaaTexTop(tex, pos.xy + dir2 * fxaaConsoleRcpFrameOpt2.zw); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyA = rgbyN1 + rgbyP1; + FxaaFloat4 rgbyB = ((rgbyN2 + rgbyP2) * 0.25) + (rgbyA * 0.25); +/*--------------------------------------------------------------------------*/ + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaBool twoTap = (rgbyB.w < lumaMin) || (rgbyB.w > lumaMax); + #else + FxaaBool twoTap = (rgbyB.y < lumaMin) || (rgbyB.y > lumaMax); + #endif + if(twoTap) rgbyB.xyz = rgbyA.xyz * 0.5; + return rgbyB; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - 360 PIXEL SHADER + +------------------------------------------------------------------------------ +This optimized version thanks to suggestions from Andy Luedke. +Should be fully tex bound in all cases. +As of the FXAA 3.11 release, I have still not tested this code, +however I fixed a bug which was in both FXAA 3.9 and FXAA 3.10. +And note this is replacing the old unoptimized version. +If it does not work, please let me know so I can fix it. +============================================================================*/ +#if (FXAA_360 == 1) +/*--------------------------------------------------------------------------*/ +[reduceTempRegUsage(4)] +float4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + float4 lumaNwNeSwSe; + #if (FXAA_GREEN_AS_LUMA == 0) + asm { + tfetch2D lumaNwNeSwSe.w___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._w__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__w_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___w, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #else + asm { + tfetch2D lumaNwNeSwSe.y___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._y__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__y_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___y, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #endif +/*--------------------------------------------------------------------------*/ + lumaNwNeSwSe.y += 1.0/384.0; + float2 lumaMinTemp = min(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float2 lumaMaxTemp = max(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float lumaMin = min(lumaMinTemp.x, lumaMinTemp.y); + float lumaMax = max(lumaMaxTemp.x, lumaMaxTemp.y); +/*--------------------------------------------------------------------------*/ + float4 rgbyM = tex2Dlod(tex, float4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + float lumaMinM = min(lumaMin, rgbyM.w); + float lumaMaxM = max(lumaMax, rgbyM.w); + #else + float lumaMinM = min(lumaMin, rgbyM.y); + float lumaMaxM = max(lumaMax, rgbyM.y); + #endif + if((lumaMaxM - lumaMinM) < max(fxaaConsoleEdgeThresholdMin, lumaMax * fxaaConsoleEdgeThreshold)) return rgbyM; +/*--------------------------------------------------------------------------*/ + float2 dir; + dir.x = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.yyxx); + dir.y = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.xyxy); + dir = normalize(dir); +/*--------------------------------------------------------------------------*/ + float4 dir1 = dir.xyxy * fxaaConsoleRcpFrameOpt.xyzw; +/*--------------------------------------------------------------------------*/ + float4 dir2; + float dirAbsMinTimesC = min(abs(dir.x), abs(dir.y)) * fxaaConsoleEdgeSharpness; + dir2 = saturate(fxaaConsole360ConstDir.zzww * dir.xyxy / dirAbsMinTimesC + 0.5); + dir2 = dir2 * fxaaConsole360RcpFrameOpt2.xyxy + fxaaConsole360RcpFrameOpt2.zwzw; +/*--------------------------------------------------------------------------*/ + float4 rgbyN1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.xy, 0.0, 0.0)); + float4 rgbyP1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.zw, 0.0, 0.0)); + float4 rgbyN2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.xy, 0.0, 0.0)); + float4 rgbyP2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.zw, 0.0, 0.0)); +/*--------------------------------------------------------------------------*/ + float4 rgbyA = rgbyN1 + rgbyP1; + float4 rgbyB = rgbyN2 + rgbyP2 + rgbyA * 0.5; +/*--------------------------------------------------------------------------*/ + float4 rgbyR = ((FxaaLuma(rgbyB) - lumaMax) > 0.0) ? rgbyA : rgbyB; + rgbyR = ((FxaaLuma(rgbyB) - lumaMin) > 0.0) ? rgbyR : rgbyA; + return rgbyR; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (NO EARLY EXIT) + +============================================================================== +The code below does not exactly match the assembly. +I have a feeling that 12 cycles is possible, but was not able to get there. +Might have to increase register count to get full performance. +Note this shader does not use perspective interpolation. + +Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + +------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT +------------------------------------------------------------------------------ +For reference and to aid in debug, output of NVShaderPerf should match this, + +Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.z(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h0.w(TRUE), v5.xwxx, #0 + 6: addh h0.z(TRUE), -h2, h0.w + 7: texpkb h1.w(TRUE), v5, #0 + 9: addh h0.x(TRUE), h0.z, -h1.w + 10: addh h3.w(TRUE), h0.z, h1 + 11: texpkb h2.w(TRUE), v5.zwzz, #0 + 13: addh h0.z(TRUE), h3.w, -h2.w + 14: addh h0.x(TRUE), h2.w, h0 + 15: nrmh h1.xz(TRUE), h0_n + 16: minh_m8 h0.x(TRUE), |h1|, |h1.z| + 17: maxh h4.w(TRUE), h0, h1 + 18: divx h2.xy(TRUE), h1_n.xzzw, h0_n + 19: movr r1.zw(TRUE), v4.xxxy + 20: madr r2.xz(TRUE), -h1, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zzww, r1.zzww + 22: minh h5.w(TRUE), h0, h1 + 23: texpkb h0(TRUE), r2.xzxx, #0 + 25: madr r0.zw(TRUE), h1.xzxz, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w), r1 + 27: maxh h4.x(TRUE), h2.z, h2.w + 28: texpkb h1(TRUE), r0.zwzz, #0 + 30: addh_d2 h1(TRUE), h0, h1 + 31: madr r0.xy(TRUE), -h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 33: texpkb h0(TRUE), r0, #0 + 35: minh h4.z(TRUE), h2, h2.w + 36: fenct TRUE + 37: madr r1.xy(TRUE), h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 39: texpkb h2(TRUE), r1, #0 + 41: addh_d2 h0(TRUE), h0, h2 + 42: maxh h2.w(TRUE), h4, h4.x + 43: minh h2.x(TRUE), h5.w, h4.z + 44: addh_d2 h0(TRUE), h0, h1 + 45: slth h2.x(TRUE), h0.w, h2 + 46: sgth h2.w(TRUE), h0, h2 + 47: movh h0(TRUE), h0 + 48: addx.c0 rc(TRUE), h2, h2.w + 49: movh h0(c0.NE.x), h1 + +IPU0 ------ Simplified schedule: -------- +Pass | Unit | uOp | PC: Op +-----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB1 | add | 2: ADDh h2.z, h0.--w-, const.--x-; + | | | + 2 | SCT0/1 | mov | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB1 | add | 6: ADDh h0.z,-h2, h0.--w-; + | | | + 3 | SCT0/1 | mov | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 9: ADDh h0.x, h0.z---,-h1.w---; + | SCB1 | add | 10: ADDh h3.w, h0.---z, h1; + | | | + 4 | SCT0/1 | mov | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h2.w---, h0; + | SCB1 | add | 13: ADDh h0.z, h3.--w-,-h2.--w-; + | | | + 5 | SCT1 | mov | 15: NRMh h1.xz, h0; + | SRB | nrm | 15: NRMh h1.xz, h0; + | SCB0 | min | 16: MINh*8 h0.x, |h1|, |h1.z---|; + | SCB1 | max | 17: MAXh h4.w, h0, h1; + | | | + 6 | SCT0 | div | 18: DIVx h2.xy, h1.xz--, h0; + | SCT1 | mov | 19: MOVr r1.zw, g[TEX0].--xy; + | SCB0 | mad | 20: MADr r2.xz,-h1, const.z-w-, r1.z-w-; + | SCB1 | min | 22: MINh h5.w, h0, h1; + | | | + 7 | SCT0/1 | mov | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | TEX | txl | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | SCB0 | max | 27: MAXh h4.x, h2.z---, h2.w---; + | SCB1 | mad | 25: MADr r0.zw, h1.--xz, const, r1; + | | | + 8 | SCT0/1 | mov | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | TEX | txl | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | SCB0/1 | add | 30: ADDh/2 h1, h0, h1; + | | | + 9 | SCT0 | mad | 31: MADr r0.xy,-h2, const.xy--, r1.zw--; + | SCT1 | mov | 33: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 33: TXLr h0, r0, const.zzzz, TEX0; + | SCB1 | min | 35: MINh h4.z, h2, h2.--w-; + | | | + 10 | SCT0 | mad | 37: MADr r1.xy, h2, const.xy--, r1.zw--; + | SCT1 | mov | 39: TXLr h2, r1, const.zzzz, TEX0; + | TEX | txl | 39: TXLr h2, r1, const.zzzz, TEX0; + | SCB0/1 | add | 41: ADDh/2 h0, h0, h2; + | | | + 11 | SCT0 | min | 43: MINh h2.x, h5.w---, h4.z---; + | SCT1 | max | 42: MAXh h2.w, h4, h4.---x; + | SCB0/1 | add | 44: ADDh/2 h0, h0, h1; + | | | + 12 | SCT0 | set | 45: SLTh h2.x, h0.w---, h2; + | SCT1 | set | 46: SGTh h2.w, h0, h2; + | SCB0/1 | mul | 47: MOVh h0, h0; + | | | + 13 | SCT0 | mad | 48: ADDxc0_s rc, h2, h2.w---; + | SCB0/1 | mul | 49: MOVh h0(NE0.xxxx), h1; + +Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 0% 0% 50% + 6: 100% 0% 75% + 7: 0% 100% 75% + 8: 0% 100% 100% + 9: 0% 100% 25% + 10: 0% 100% 100% + 11: 50% 0% 100% + 12: 50% 0% 100% + 13: 25% 0% 100% + +MEAN: 17% 61% 67% + +Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 0% 100% + 2: 0% 0% 100% 0% 100% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 0% 0% 0% 100% 100% + 6: 100% 100% 0% 100% 100% + 7: 0% 0% 100% 100% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 0% 100% + 10: 0% 0% 100% 100% 100% + 11: 100% 100% 0% 100% 100% + 12: 100% 100% 0% 100% 100% + 13: 100% 0% 0% 100% 100% + +MEAN: 30% 23% 61% 76% 100% +Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 +Results 13 cycles, 3 r regs, 923,076,923 pixels/s +============================================================================*/ +#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 0) +/*--------------------------------------------------------------------------*/ +#pragma regcount 7 +#pragma disablepc all +#pragma option O3 +#pragma option OutColorPrec=fp16 +#pragma texformat default RGBA8 +/*==========================================================================*/ +half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ +// (1) + half4 dir; + half4 lumaNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + lumaNe.w += half(1.0/512.0); + dir.x = -lumaNe.w; + dir.z = -lumaNe.w; + #else + lumaNe.y += half(1.0/512.0); + dir.x = -lumaNe.y; + dir.z = -lumaNe.y; + #endif +/*--------------------------------------------------------------------------*/ +// (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSw.w; + dir.z += lumaSw.w; + #else + dir.x += lumaSw.y; + dir.z += lumaSw.y; + #endif +/*--------------------------------------------------------------------------*/ +// (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x -= lumaNw.w; + dir.z += lumaNw.w; + #else + dir.x -= lumaNw.y; + dir.z += lumaNw.y; + #endif +/*--------------------------------------------------------------------------*/ +// (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSe.w; + dir.z -= lumaSe.w; + #else + dir.x += lumaSe.y; + dir.z -= lumaSe.y; + #endif +/*--------------------------------------------------------------------------*/ +// (5) + half4 dir1_pos; + dir1_pos.xy = normalize(dir.xyz).xz; + half dirAbsMinTimesC = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); +/*--------------------------------------------------------------------------*/ +// (6) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimesC, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; +/*--------------------------------------------------------------------------*/ +// (7) + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; +/*--------------------------------------------------------------------------*/ +// (8) + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (9) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); +/*--------------------------------------------------------------------------*/ +// (10) + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; +/*--------------------------------------------------------------------------*/ +// (11) + // compilier moves these scalar ops up to other cycles + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMin = min(min(lumaNw.w, lumaSw.w), min(lumaNe.w, lumaSe.w)); + half lumaMax = max(max(lumaNw.w, lumaSw.w), max(lumaNe.w, lumaSe.w)); + #else + half lumaMin = min(min(lumaNw.y, lumaSw.y), min(lumaNe.y, lumaSe.y)); + half lumaMax = max(max(lumaNw.y, lumaSw.y), max(lumaNe.y, lumaSe.y)); + #endif + rgby2 = (rgby2 + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (12) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif +/*--------------------------------------------------------------------------*/ +// (13) + if(twoTapLt || twoTapGt) rgby2 = rgby1; +/*--------------------------------------------------------------------------*/ + return rgby2; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (WITH EARLY EXIT) + +============================================================================== +The code mostly matches the assembly. +I have a feeling that 14 cycles is possible, but was not able to get there. +Might have to increase register count to get full performance. +Note this shader does not use perspective interpolation. + +Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + +Use of FXAA_GREEN_AS_LUMA currently adds a cycle (16 clks). +Will look at fixing this for FXAA 3.12. +------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT +------------------------------------------------------------------------------ +For reference and to aid in debug, output of NVShaderPerf should match this, + +Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.y(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h1.w(TRUE), v5.xwxx, #0 + 6: addh h0.x(TRUE), h1.w, -h2.y + 7: texpkb h2.w(TRUE), v5.zwzz, #0 + 9: minh h4.w(TRUE), h2.y, h2 + 10: maxh h5.x(TRUE), h2.y, h2.w + 11: texpkb h0.w(TRUE), v5, #0 + 13: addh h3.w(TRUE), -h0, h0.x + 14: addh h0.x(TRUE), h0.w, h0 + 15: addh h0.z(TRUE), -h2.w, h0.x + 16: addh h0.x(TRUE), h2.w, h3.w + 17: minh h5.y(TRUE), h0.w, h1.w + 18: nrmh h2.xz(TRUE), h0_n + 19: minh_m8 h2.w(TRUE), |h2.x|, |h2.z| + 20: divx h4.xy(TRUE), h2_n.xzzw, h2_n.w + 21: movr r1.zw(TRUE), v4.xxxy + 22: maxh h2.w(TRUE), h0, h1 + 23: fenct TRUE + 24: madr r0.xy(TRUE), -h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 26: texpkb h0(TRUE), r0, #0 + 28: maxh h5.x(TRUE), h2.w, h5 + 29: minh h5.w(TRUE), h5.y, h4 + 30: madr r1.xy(TRUE), h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 32: texpkb h2(TRUE), r1, #0 + 34: addh_d2 h2(TRUE), h0, h2 + 35: texpkb h1(TRUE), v4, #0 + 37: maxh h5.y(TRUE), h5.x, h1.w + 38: minh h4.w(TRUE), h1, h5 + 39: madr r0.xy(TRUE), -h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 41: texpkb h0(TRUE), r0, #0 + 43: addh_m8 h5.z(TRUE), h5.y, -h4.w + 44: madr r2.xy(TRUE), h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 46: texpkb h3(TRUE), r2, #0 + 48: addh_d2 h0(TRUE), h0, h3 + 49: addh_d2 h3(TRUE), h0, h2 + 50: movh h0(TRUE), h3 + 51: slth h3.x(TRUE), h3.w, h5.w + 52: sgth h3.w(TRUE), h3, h5.x + 53: addx.c0 rc(TRUE), h3.x, h3 + 54: slth.c0 rc(TRUE), h5.z, h5 + 55: movh h0(c0.NE.w), h2 + 56: movh h0(c0.NE.x), h1 + +IPU0 ------ Simplified schedule: -------- +Pass | Unit | uOp | PC: Op +-----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB0 | add | 2: ADDh h2.y, h0.-w--, const.-x--; + | | | + 2 | SCT0/1 | mov | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB0 | add | 6: ADDh h0.x, h1.w---,-h2.y---; + | | | + 3 | SCT0/1 | mov | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | max | 10: MAXh h5.x, h2.y---, h2.w---; + | SCB1 | min | 9: MINh h4.w, h2.---y, h2; + | | | + 4 | SCT0/1 | mov | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h0.w---, h0; + | SCB1 | add | 13: ADDh h3.w,-h0, h0.---x; + | | | + 5 | SCT0 | mad | 16: ADDh h0.x, h2.w---, h3.w---; + | SCT1 | mad | 15: ADDh h0.z,-h2.--w-, h0.--x-; + | SCB0 | min | 17: MINh h5.y, h0.-w--, h1.-w--; + | | | + 6 | SCT1 | mov | 18: NRMh h2.xz, h0; + | SRB | nrm | 18: NRMh h2.xz, h0; + | SCB1 | min | 19: MINh*8 h2.w, |h2.---x|, |h2.---z|; + | | | + 7 | SCT0 | div | 20: DIVx h4.xy, h2.xz--, h2.ww--; + | SCT1 | mov | 21: MOVr r1.zw, g[TEX0].--xy; + | SCB1 | max | 22: MAXh h2.w, h0, h1; + | | | + 8 | SCT0 | mad | 24: MADr r0.xy,-h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 26: TXLr h0, r0, const.xxxx, TEX0; + | TEX | txl | 26: TXLr h0, r0, const.xxxx, TEX0; + | SCB0 | max | 28: MAXh h5.x, h2.w---, h5; + | SCB1 | min | 29: MINh h5.w, h5.---y, h4; + | | | + 9 | SCT0 | mad | 30: MADr r1.xy, h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 32: TXLr h2, r1, const.xxxx, TEX0; + | TEX | txl | 32: TXLr h2, r1, const.xxxx, TEX0; + | SCB0/1 | add | 34: ADDh/2 h2, h0, h2; + | | | + 10 | SCT0/1 | mov | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | TEX | txl | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | SCB0 | max | 37: MAXh h5.y, h5.-x--, h1.-w--; + | SCB1 | min | 38: MINh h4.w, h1, h5; + | | | + 11 | SCT0 | mad | 39: MADr r0.xy,-h4, const.xy--, r1.zw--; + | SCT1 | mov | 41: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 41: TXLr h0, r0, const.zzzz, TEX0; + | SCB0 | mad | 44: MADr r2.xy, h4, const.xy--, r1.zw--; + | SCB1 | add | 43: ADDh*8 h5.z, h5.--y-,-h4.--w-; + | | | + 12 | SCT0/1 | mov | 46: TXLr h3, r2, const.xxxx, TEX0; + | TEX | txl | 46: TXLr h3, r2, const.xxxx, TEX0; + | SCB0/1 | add | 48: ADDh/2 h0, h0, h3; + | | | + 13 | SCT0/1 | mad | 49: ADDh/2 h3, h0, h2; + | SCB0/1 | mul | 50: MOVh h0, h3; + | | | + 14 | SCT0 | set | 51: SLTh h3.x, h3.w---, h5.w---; + | SCT1 | set | 52: SGTh h3.w, h3, h5.---x; + | SCB0 | set | 54: SLThc0 rc, h5.z---, h5; + | SCB1 | add | 53: ADDxc0_s rc, h3.---x, h3; + | | | + 15 | SCT0/1 | mul | 55: MOVh h0(NE0.wwww), h2; + | SCB0/1 | mul | 56: MOVh h0(NE0.xxxx), h1; + +Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 50% 0% 25% + 6: 0% 0% 25% + 7: 100% 0% 25% + 8: 0% 100% 50% + 9: 0% 100% 100% + 10: 0% 100% 50% + 11: 0% 100% 75% + 12: 0% 100% 100% + 13: 100% 0% 100% + 14: 50% 0% 50% + 15: 100% 0% 100% + +MEAN: 26% 60% 56% + +Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 100% 0% + 2: 0% 0% 100% 100% 0% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 100% 100% 0% 100% 0% + 6: 0% 0% 0% 0% 100% + 7: 100% 100% 0% 0% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 100% 100% + 10: 0% 0% 100% 100% 100% + 11: 0% 0% 100% 100% 100% + 12: 0% 0% 100% 100% 100% + 13: 100% 100% 0% 100% 100% + 14: 100% 100% 0% 100% 100% + 15: 100% 100% 0% 100% 100% + +MEAN: 33% 33% 60% 86% 80% +Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 +Results 15 cycles, 3 r regs, 800,000,000 pixels/s +============================================================================*/ +#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 1) +/*--------------------------------------------------------------------------*/ +#pragma regcount 7 +#pragma disablepc all +#pragma option O2 +#pragma option OutColorPrec=fp16 +#pragma texformat default RGBA8 +/*==========================================================================*/ +half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ +// (1) + half4 rgbyNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaNe = rgbyNe.w + half(1.0/512.0); + #else + half lumaNe = rgbyNe.y + half(1.0/512.0); + #endif +/*--------------------------------------------------------------------------*/ +// (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaSwNegNe = lumaSw.w - lumaNe; + #else + half lumaSwNegNe = lumaSw.y - lumaNe; + #endif +/*--------------------------------------------------------------------------*/ +// (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNwSw = max(lumaNw.w, lumaSw.w); + half lumaMinNwSw = min(lumaNw.w, lumaSw.w); + #else + half lumaMaxNwSw = max(lumaNw.y, lumaSw.y); + half lumaMinNwSw = min(lumaNw.y, lumaSw.y); + #endif +/*--------------------------------------------------------------------------*/ +// (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half dirZ = lumaNw.w + lumaSwNegNe; + half dirX = -lumaNw.w + lumaSwNegNe; + #else + half dirZ = lumaNw.y + lumaSwNegNe; + half dirX = -lumaNw.y + lumaSwNegNe; + #endif +/*--------------------------------------------------------------------------*/ +// (5) + half3 dir; + dir.y = 0.0; + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x = lumaSe.w + dirX; + dir.z = -lumaSe.w + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.w); + #else + dir.x = lumaSe.y + dirX; + dir.z = -lumaSe.y + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.y); + #endif +/*--------------------------------------------------------------------------*/ +// (6) + half4 dir1_pos; + dir1_pos.xy = normalize(dir).xz; + half dirAbsMinTimes8 = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); +/*--------------------------------------------------------------------------*/ +// (7) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimes8, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNeSe = max(lumaNe, lumaSe.w); + #else + half lumaMaxNeSe = max(lumaNe, lumaSe.y); + #endif +/*--------------------------------------------------------------------------*/ +// (8) + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half lumaMax = max(lumaMaxNwSw, lumaMaxNeSe); + half lumaMin = min(lumaMinNwSw, lumaMinNeSe); +/*--------------------------------------------------------------------------*/ +// (9) + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (10) + half4 rgbyM = h4tex2Dlod(tex, half4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxM = max(lumaMax, rgbyM.w); + half lumaMinM = min(lumaMin, rgbyM.w); + #else + half lumaMaxM = max(lumaMax, rgbyM.y); + half lumaMinM = min(lumaMin, rgbyM.y); + #endif +/*--------------------------------------------------------------------------*/ +// (11) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + half lumaRangeM = (lumaMaxM - lumaMinM) / FXAA_CONSOLE__PS3_EDGE_THRESHOLD; +/*--------------------------------------------------------------------------*/ +// (12) + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; +/*--------------------------------------------------------------------------*/ +// (13) + rgby2 = (rgby2 + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (14) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif + bool earlyExit = lumaRangeM < lumaMax; + bool twoTap = twoTapLt || twoTapGt; +/*--------------------------------------------------------------------------*/ +// (15) + if(twoTap) rgby2 = rgby1; + if(earlyExit) rgby2 = rgbyM; +/*--------------------------------------------------------------------------*/ + return rgby2; } +/*==========================================================================*/ +#endif + +``` + +`shaders/external/SMAA.hlsl`: + +```hlsl +/** + * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) + * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) + * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) + * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) + * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * 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. As clarification, there + * is no requirement that the copyright notice and permission be included in + * binary distributions 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. + */ + + +/** + * _______ ___ ___ ___ ___ + * / || \/ | / \ / \ + * | (---- | \ / | / ^ \ / ^ \ + * \ \ | |\/| | / /_\ \ / /_\ \ + * ----) | | | | | / _____ \ / _____ \ + * |_______/ |__| |__| /__/ \__\ /__/ \__\ + * + * E N H A N C E D + * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G + * + * http://www.iryoku.com/smaa/ + * + * Hi, welcome aboard! + * + * Here you'll find instructions to get the shader up and running as fast as + * possible. + * + * IMPORTANTE NOTICE: when updating, remember to update both this file and the + * precomputed textures! They may change from version to version. + * + * The shader has three passes, chained together as follows: + * + * |input|------------------+ + * v | + * [ SMAA*EdgeDetection ] | + * v | + * |edgesTex| | + * v | + * [ SMAABlendingWeightCalculation ] | + * v | + * |blendTex| | + * v | + * [ SMAANeighborhoodBlending ] <------+ + * v + * |output| + * + * Note that each [pass] has its own vertex and pixel shader. Remember to use + * oversized triangles instead of quads to avoid overshading along the + * diagonal. + * + * You've three edge detection methods to choose from: luma, color or depth. + * They represent different quality/performance and anti-aliasing/sharpness + * tradeoffs, so our recommendation is for you to choose the one that best + * suits your particular scenario: + * + * - Depth edge detection is usually the fastest but it may miss some edges. + * + * - Luma edge detection is usually more expensive than depth edge detection, + * but catches visible edges that depth edge detection can miss. + * + * - Color edge detection is usually the most expensive one but catches + * chroma-only edges. + * + * For quickstarters: just use luma edge detection. + * + * The general advice is to not rush the integration process and ensure each + * step is done correctly (don't try to integrate SMAA T2x with predicated edge + * detection from the start!). Ok then, let's go! + * + * 1. The first step is to create two RGBA temporal render targets for holding + * |edgesTex| and |blendTex|. + * + * In DX10 or DX11, you can use a RG render target for the edges texture. + * In the case of NVIDIA GPUs, using RG render targets seems to actually be + * slower. + * + * On the Xbox 360, you can use the same render target for resolving both + * |edgesTex| and |blendTex|, as they aren't needed simultaneously. + * + * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared + * each frame. Do not forget to clear the alpha channel! + * + * 3. The next step is loading the two supporting precalculated textures, + * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as + * C++ headers, and also as regular DDS files. They'll be needed for the + * 'SMAABlendingWeightCalculation' pass. + * + * If you use the C++ headers, be sure to load them in the format specified + * inside of them. + * + * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 + * respectively, if you have that option in your content processor pipeline. + * When compressing then, you get a non-perceptible quality decrease, and a + * marginal performance increase. + * + * 4. All samplers must be set to linear filtering and clamp. + * + * After you get the technique working, remember that 64-bit inputs have + * half-rate linear filtering on GCN. + * + * If SMAA is applied to 64-bit color buffers, switching to point filtering + * when accesing them will increase the performance. Search for + * 'SMAASamplePoint' to see which textures may benefit from point + * filtering, and where (which is basically the color input in the edge + * detection and resolve passes). + * + * 5. All texture reads and buffer writes must be non-sRGB, with the exception + * of the input read and the output write in + * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in + * this last pass are not possible, the technique will work anyway, but + * will perform antialiasing in gamma space. + * + * IMPORTANT: for best results the input read for the color/luma edge + * detection should *NOT* be sRGB. + * + * 6. Before including SMAA.h you'll have to setup the render target metrics, + * the target and any optional configuration defines. Optionally you can + * use a preset. + * + * You have the following targets available: + * SMAA_HLSL_3 + * SMAA_HLSL_4 + * SMAA_HLSL_4_1 + * SMAA_GLSL_3 * + * SMAA_GLSL_4 * + * + * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). + * + * And four presets: + * SMAA_PRESET_LOW (%60 of the quality) + * SMAA_PRESET_MEDIUM (%80 of the quality) + * SMAA_PRESET_HIGH (%95 of the quality) + * SMAA_PRESET_ULTRA (%99 of the quality) + * + * For example: + * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) + * #define SMAA_HLSL_4 + * #define SMAA_PRESET_HIGH + * #include "SMAA.h" + * + * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a + * uniform variable. The code is designed to minimize the impact of not + * using a constant value, but it is still better to hardcode it. + * + * Depending on how you encoded 'areaTex' and 'searchTex', you may have to + * add (and customize) the following defines before including SMAA.h: + * #define SMAA_AREATEX_SELECT(sample) sample.rg + * #define SMAA_SEARCHTEX_SELECT(sample) sample.r + * + * If your engine is already using porting macros, you can define + * SMAA_CUSTOM_SL, and define the porting functions by yourself. + * + * 7. Then, you'll have to setup the passes as indicated in the scheme above. + * You can take a look into SMAA.fx, to see how we did it for our demo. + * Checkout the function wrappers, you may want to copy-paste them! + * + * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. + * You can use a screenshot from your engine to compare the |edgesTex| + * and |blendTex| produced inside of the engine with the results obtained + * with the reference demo. + * + * 9. After you get the last pass to work, it's time to optimize. You'll have + * to initialize a stencil buffer in the first pass (discard is already in + * the code), then mask execution by using it the second pass. The last + * pass should be executed in all pixels. + * + * + * After this point you can choose to enable predicated thresholding, + * temporal supersampling and motion blur integration: + * + * a) If you want to use predicated thresholding, take a look into + * SMAA_PREDICATION; you'll need to pass an extra texture in the edge + * detection pass. + * + * b) If you want to enable temporal supersampling (SMAA T2x): + * + * 1. The first step is to render using subpixel jitters. I won't go into + * detail, but it's as simple as moving each vertex position in the + * vertex shader, you can check how we do it in our DX10 demo. + * + * 2. Then, you must setup the temporal resolve. You may want to take a look + * into SMAAResolve for resolving 2x modes. After you get it working, you'll + * probably see ghosting everywhere. But fear not, you can enable the + * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. + * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. + * + * 3. The next step is to apply SMAA to each subpixel jittered frame, just as + * done for 1x. + * + * 4. At this point you should already have something usable, but for best + * results the proper area textures must be set depending on current jitter. + * For this, the parameter 'subsampleIndices' of + * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x + * mode: + * + * @SUBSAMPLE_INDICES + * + * | S# | Camera Jitter | subsampleIndices | + * +----+------------------+---------------------+ + * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | + * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | + * + * These jitter positions assume a bottom-to-top y axis. S# stands for the + * sample number. + * + * More information about temporal supersampling here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * c) If you want to enable spatial multisampling (SMAA S2x): + * + * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be + * created with: + * - DX10: see below (*) + * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or + * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN + * + * This allows to ensure that the subsample order matches the table in + * @SUBSAMPLE_INDICES. + * + * (*) In the case of DX10, we refer the reader to: + * - SMAA::detectMSAAOrder and + * - SMAA::msaaReorder + * + * These functions allow to match the standard multisample patterns by + * detecting the subsample order for a specific GPU, and reordering + * them appropriately. + * + * 2. A shader must be run to output each subsample into a separate buffer + * (DX10 is required). You can use SMAASeparate for this purpose, or just do + * it in an existing pass (for example, in the tone mapping pass, which has + * the advantage of feeding tone mapped subsamples to SMAA, which will yield + * better results). + * + * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing + * the results in the final buffer. The second run should alpha blend with + * the existing final buffer using a blending factor of 0.5. + * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point + * b). + * + * d) If you want to enable temporal supersampling on top of SMAA S2x + * (which actually is SMAA 4x): + * + * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is + * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' + * must be set as follows: + * + * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | + * +----+----+--------------------+-------------------+----------------------+ + * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | + * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | + * +----+----+--------------------+-------------------+----------------------+ + * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | + * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | + * + * These jitter positions assume a bottom-to-top y axis. F# stands for the + * frame number. S# stands for the sample number. + * + * 2. After calculating SMAA S2x for current frame (with the new subsample + * indices), previous frame must be reprojected as in SMAA T2x mode (see + * point b). + * + * e) If motion blur is used, you may want to do the edge detection pass + * together with motion blur. This has two advantages: + * + * 1. Pixels under heavy motion can be omitted from the edge detection process. + * For these pixels we can just store "no edge", as motion blur will take + * care of them. + * 2. The center pixel tap is reused. + * + * Note that in this case depth testing should be used instead of stenciling, + * as we have to write all the pixels in the motion blur pass. + * + * That's it! + */ + +//----------------------------------------------------------------------------- +// SMAA Presets + +/** + * Note that if you use one of these presets, the following configuration + * macros will be ignored if set in the "Configurable Defines" section. + */ + +#if defined(SMAA_PRESET_LOW) +#define SMAA_THRESHOLD 0.15 +#define SMAA_MAX_SEARCH_STEPS 4 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_MEDIUM) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 8 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_HIGH) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 16 +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#define SMAA_CORNER_ROUNDING 25 +#elif defined(SMAA_PRESET_ULTRA) +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 +#endif + +//----------------------------------------------------------------------------- +// Configurable Defines + +/** + * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. + * Lowering this value you will be able to detect more edges at the expense of + * performance. + * + * Range: [0, 0.5] + * 0.1 is a reasonable value, and allows to catch most visible edges. + * 0.05 is a rather overkill value, that allows to catch 'em all. + * + * If temporal supersampling is used, 0.2 could be a reasonable value, as low + * contrast edges are properly filtered by just 2x. + */ +#ifndef SMAA_THRESHOLD +#define SMAA_THRESHOLD 0.1 +#endif + +/** + * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. + * + * Range: depends on the depth range of the scene. + */ +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + +/** + * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the + * horizontal/vertical pattern searches, at each side of the pixel. + * + * In number of pixels, it's actually the double. So the maximum line length + * perfectly handled by, for example 16, is 64 (by perfectly, we meant that + * longer lines won't look as good, but still antialiased). + * + * Range: [0, 112] + */ +#ifndef SMAA_MAX_SEARCH_STEPS +#define SMAA_MAX_SEARCH_STEPS 16 +#endif + +/** + * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the + * diagonal pattern searches, at each side of the pixel. In this case we jump + * one pixel at time, instead of two. + * + * Range: [0, 20] + * + * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 + * steps), but it can have a significant impact on older machines. + * + * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. + */ +#ifndef SMAA_MAX_SEARCH_STEPS_DIAG +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#endif + +/** + * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. + * + * Range: [0, 100] + * + * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. + */ +#ifndef SMAA_CORNER_ROUNDING +#define SMAA_CORNER_ROUNDING 25 +#endif + +/** + * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times + * bigger contrast than current edge, current edge will be discarded. + * + * This allows to eliminate spurious crossing edges, and is based on the fact + * that, if there is too much contrast in a direction, that will hide + * perceptually contrast in the other neighbors. + */ +#ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR +#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 +#endif + +/** + * Predicated thresholding allows to better preserve texture details and to + * improve performance, by decreasing the number of detected edges using an + * additional buffer like the light accumulation buffer, object ids or even the + * depth buffer (the depth buffer usage may be limited to indoor or short range + * scenes). + * + * It locally decreases the luma or color threshold if an edge is found in an + * additional buffer (so the global threshold can be higher). + * + * This method was developed by Playstation EDGE MLAA team, and used in + * Killzone 3, by using the light accumulation buffer. More information here: + * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx + */ +#ifndef SMAA_PREDICATION +#define SMAA_PREDICATION 0 +#endif + +/** + * Threshold to be used in the additional predication buffer. + * + * Range: depends on the input, so you'll have to find the magic number that + * works for you. + */ +#ifndef SMAA_PREDICATION_THRESHOLD +#define SMAA_PREDICATION_THRESHOLD 0.01 +#endif + +/** + * How much to scale the global threshold used for luma or color edge + * detection when using predication. + * + * Range: [1, 5] + */ +#ifndef SMAA_PREDICATION_SCALE +#define SMAA_PREDICATION_SCALE 2.0 +#endif + +/** + * How much to locally decrease the threshold. + * + * Range: [0, 1] + */ +#ifndef SMAA_PREDICATION_STRENGTH +#define SMAA_PREDICATION_STRENGTH 0.4 +#endif + +/** + * Temporal reprojection allows to remove ghosting artifacts when using + * temporal supersampling. We use the CryEngine 3 method which also introduces + * velocity weighting. This feature is of extreme importance for totally + * removing ghosting. More information here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * Note that you'll need to setup a velocity buffer for enabling reprojection. + * For static geometry, saving the previous depth buffer is a viable + * alternative. + */ +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +/** + * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to + * remove ghosting trails behind the moving object, which are not removed by + * just using reprojection. Using low values will exhibit ghosting, while using + * high values will disable temporal supersampling under motion. + * + * Behind the scenes, velocity weighting removes temporal supersampling when + * the velocity of the subsamples differs (meaning they are different objects). + * + * Range: [0, 80] + */ +#ifndef SMAA_REPROJECTION_WEIGHT_SCALE +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 +#endif + +/** + * On some compilers, discard cannot be used in vertex shaders. Thus, they need + * to be compiled separately. + */ +#ifndef SMAA_INCLUDE_VS +#define SMAA_INCLUDE_VS 1 +#endif +#ifndef SMAA_INCLUDE_PS +#define SMAA_INCLUDE_PS 1 +#endif + +//----------------------------------------------------------------------------- +// Texture Access Defines + +#ifndef SMAA_AREATEX_SELECT +#if defined(SMAA_HLSL_3) +#define SMAA_AREATEX_SELECT(sample) sample.ra +#else +#define SMAA_AREATEX_SELECT(sample) sample.rg +#endif +#endif + +#ifndef SMAA_SEARCHTEX_SELECT +#define SMAA_SEARCHTEX_SELECT(sample) sample.r +#endif + +#ifndef SMAA_DECODE_VELOCITY +#define SMAA_DECODE_VELOCITY(sample) sample.rg +#endif + +//----------------------------------------------------------------------------- +// Non-Configurable Defines + +#define SMAA_AREATEX_MAX_DISTANCE 16 +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / float2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) +#define SMAA_SEARCHTEX_SIZE float2(66.0, 33.0) +#define SMAA_SEARCHTEX_PACKED_SIZE float2(64.0, 16.0) +#define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0) + +//----------------------------------------------------------------------------- +// Porting Functions + +#if defined(SMAA_HLSL_3) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) +#define SMAASample(tex, coord) tex2D(tex, coord) +#define SMAASamplePoint(tex, coord) tex2D(tex, coord) +#define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#endif +#if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) +SamplerState LinearSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +SamplerState PointSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +#define SMAATexture2D(tex) Texture2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex.SampleLevel(LinearSampler, coord, 0) +#define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(PointSampler, coord, 0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(LinearSampler, coord, 0, offset) +#define SMAASample(tex, coord) tex.Sample(LinearSampler, coord) +#define SMAASamplePoint(tex, coord) tex.Sample(PointSampler, coord) +#define SMAASampleOffset(tex, coord, offset) tex.Sample(LinearSampler, coord, offset) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#define SMAATexture2DMS2(tex) Texture2DMS tex +#define SMAALoad(tex, pos, sample) tex.Load(pos, sample) +#if defined(SMAA_HLSL_4_1) +#define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0) +#endif +#endif +#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroPoint(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) textureLodOffset(tex, coord, 0.0, offset) +#define SMAASample(tex, coord) texture(tex, coord) +#define SMAASamplePoint(tex, coord) texture(tex, coord) +#define SMAASampleOffset(tex, coord, offset) texture(tex, coord, offset) +#define SMAA_FLATTEN +#define SMAA_BRANCH +#define lerp(a, b, t) mix(a, b, t) +#define saturate(a) clamp(a, 0.0, 1.0) +#if defined(SMAA_GLSL_4) +#define mad(a, b, c) fma(a, b, c) +#define SMAAGather(tex, coord) textureGather(tex, coord) +#else +#define mad(a, b, c) (a * b + c) +#endif +#define float2 vec2 +#define float3 vec3 +#define float4 vec4 +#define int2 ivec2 +#define int3 ivec3 +#define int4 ivec4 +#define bool2 bvec2 +#define bool3 bvec3 +#define bool4 bvec4 +#endif + +#if !defined(SMAA_HLSL_3) && !defined(SMAA_HLSL_4) && !defined(SMAA_HLSL_4_1) && !defined(SMAA_GLSL_3) && !defined(SMAA_GLSL_4) && !defined(SMAA_CUSTOM_SL) +#error you must define the shading language: SMAA_HLSL_*, SMAA_GLSL_* or SMAA_CUSTOM_SL +#endif + +//----------------------------------------------------------------------------- +// Misc functions + +/** + * Gathers current pixel, and the top-left neighbors. + */ +float3 SMAAGatherNeighbours(float2 texcoord, + float4 offset[3], + SMAATexture2D(tex)) { + #ifdef SMAAGather + return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, -0.5)).grb; + #else + float P = SMAASamplePoint(tex, texcoord).r; + float Pleft = SMAASamplePoint(tex, offset[0].xy).r; + float Ptop = SMAASamplePoint(tex, offset[0].zw).r; + return float3(P, Pleft, Ptop); + #endif +} + +/** + * Adjusts the threshold by means of predication. + */ +float2 SMAACalculatePredicatedThreshold(float2 texcoord, + float4 offset[3], + SMAATexture2D(predicationTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(predicationTex)); + float2 delta = abs(neighbours.xx - neighbours.yz); + float2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); + return SMAA_PREDICATION_SCALE * SMAA_THRESHOLD * (1.0 - SMAA_PREDICATION_STRENGTH * edges); +} + +/** + * Conditional move: + */ +void SMAAMovc(bool2 cond, inout float2 variable, float2 value) { + SMAA_FLATTEN if (cond.x) variable.x = value.x; + SMAA_FLATTEN if (cond.y) variable.y = value.y; +} + +void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { + SMAAMovc(cond.xy, variable.xy, value.xy); + SMAAMovc(cond.zw, variable.zw, value.zw); +} + + +#if SMAA_INCLUDE_VS +//----------------------------------------------------------------------------- +// Vertex Shaders + +/** + * Edge Detection Vertex Shader + */ +void SMAAEdgeDetectionVS(float2 texcoord, + out float4 offset[3]) { + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); + offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); +} + +/** + * Blend Weight Calculation Vertex Shader + */ +void SMAABlendingWeightCalculationVS(float2 texcoord, + out float2 pixcoord, + out float4 offset[3]) { + pixcoord = texcoord * SMAA_RT_METRICS.zw; + + // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); + + // And these for the searches, they indicate the ends of the loops: + offset[2] = mad(SMAA_RT_METRICS.xxyy, + float4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), + float4(offset[0].xz, offset[1].yw)); +} + +/** + * Neighborhood Blending Vertex Shader + */ +void SMAANeighborhoodBlendingVS(float2 texcoord, + out float4 offset) { + offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); +} +#endif // SMAA_INCLUDE_VS + +#if SMAA_INCLUDE_PS +//----------------------------------------------------------------------------- +// Edge Detection Pixel Shaders (First Pass) + +/** + * Luma Edge Detection + * + * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAALumaEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate lumas: + float3 weights = float3(0.2126, 0.7152, 0.0722); + float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); + + float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); + float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); + + // We do the usual threshold: + float4 delta; + delta.xy = abs(L - float2(Lleft, Ltop)); + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); + float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); + delta.zw = abs(L - float2(Lright, Lbottom)); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); + float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); + delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Color Edge Detection + * + * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAAColorEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate color deltas: + float4 delta; + float3 C = SMAASamplePoint(colorTex, texcoord).rgb; + + float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; + float3 t = abs(C - Cleft); + delta.x = max(max(t.r, t.g), t.b); + + float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; + t = abs(C - Ctop); + delta.y = max(max(t.r, t.g), t.b); + + // We do the usual threshold: + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; + t = abs(C - Cright); + delta.z = max(max(t.r, t.g), t.b); + + float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; + t = abs(C - Cbottom); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; + t = abs(C - Cleftleft); + delta.z = max(max(t.r, t.g), t.b); + + float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; + t = abs(C - Ctoptop); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Depth Edge Detection + */ +float2 SMAADepthEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(depthTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); + float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); + float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); + + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + return edges; +} + +//----------------------------------------------------------------------------- +// Diagonal Search Functions + +#if !defined(SMAA_DISABLE_DIAG_DETECTION) + +/** + * Allows to decode two binary values from a bilinear-filtered access. + */ +float2 SMAADecodeDiagBilinearAccess(float2 e) { + // Bilinear access for fetching 'e' have a 0.25 offset, and we are + // interested in the R and G edges: + // + // +---G---+-------+ + // | x o R x | + // +-------+-------+ + // + // Then, if one of these edge is enabled: + // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 + // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 + // + // This function will unpack the values (mad + mul + round): + // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 + e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); + return round(e); +} + +float4 SMAADecodeDiagBilinearAccess(float4 e) { + e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); + return round(e); +} + +/** + * These functions allows to perform diagonal pattern searches. + */ +float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + + // @SearchDiag2Optimization + // Fetch both edges at once using bilinear filtering: + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + e = SMAADecodeDiagBilinearAccess(e); + + // Non-optimized version: + // e.g = SMAASampleLevelZero(edgesTex, coord.xy).g; + // e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0)).r; + + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +/** + * Similar to SMAAArea, this calculates the area corresponding to a certain + * diagonal distance and crossing edges 'e'. + */ +float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Diagonal areas are on the second half of the texture: + texcoord.x += 0.5; + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +/** + * This searches for diagonal patterns and returns the corresponding weights. + */ +float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { + float2 weights = float2(0.0, 0.0); + + // Search for the line ends: + float4 d; + float2 end; + if (e.r > 0.0) { + d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); + d.x += float(end.y > 0.9); + } else + d.xz = float2(0.0, 0.0); + d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; + c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); + + // Non-optimized version: + // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + // float4 c; + // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; + // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; + // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; + + // Merge crossing edges at each side into a single value: + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); + } + + // Search for the line ends: + d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); + if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { + d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); + d.y += float(end.y > 0.9); + } else + d.yw = float2(0.0, 0.0); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, -1)).r; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; + } + + return weights; +} +#endif + +//----------------------------------------------------------------------------- +// Horizontal/Vertical Search Functions + +/** + * This allows to determine how much length should we add in the last step + * of the searches. It takes the bilinearly interpolated edge (see + * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and + * crossing edges are active. + */ +float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { + // The texture is flipped vertically, with left and right cases taking half + // of the space horizontally: + float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); + float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); + + // Scale and bias to access texel centers: + scale += float2(-1.0, 1.0); + bias += float2( 0.5, -0.5); + + // Convert from pixel coordinates to texcoords: + // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) + scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + + // Lookup the search texture: + return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias))); +} + +/** + * Horizontal/vertical search functions for the 2nd pass. + */ +float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + /** + * @PSEUDO_GATHER4 + * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to + * sample between edge, thus fetching four edges in a row. + * Sampling with different offsets in each direction allows to disambiguate + * which edges are active from the four fetched ones. + */ + float2 e = float2(0.0, 1.0); + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0), 3.25); + return mad(SMAA_RT_METRICS.x, offset, texcoord.x); + + // Non-optimized version: + // We correct the previous (-0.25, -0.125) offset we applied: + // texcoord.x += 0.25 * SMAA_RT_METRICS.x; + + // The searches are bias by 1, so adjust the coords accordingly: + // texcoord.x += SMAA_RT_METRICS.x; + + // Disambiguate the length added by the last step: + // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step + // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); + // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(0.0, 1.0); + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y > end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.0), 3.25); + return mad(SMAA_RT_METRICS.y, offset, texcoord.y); +} + +float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y < end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.y, offset, texcoord.y); +} + +/** + * Ok, we have the distance and both crossing edges. So, what are the areas + * at each side of current edge? + */ +float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { + // Rounding prevents precision errors of bilinear filtering: + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Move to proper place, according to the subpixel offset: + texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +//----------------------------------------------------------------------------- +// Corner Detection Functions + +void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, -2)).r; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, -2)).r; + + weights *= saturate(factor); + #endif +} + +void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, 1)).g; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, 1)).g; + + weights *= saturate(factor); + #endif +} + +//----------------------------------------------------------------------------- +// Blending Weight Calculation Pixel Shader (Second Pass) + +float4 SMAABlendingWeightCalculationPS(float2 texcoord, + float2 pixcoord, + float4 offset[3], + SMAATexture2D(edgesTex), + SMAATexture2D(areaTex), + SMAATexture2D(searchTex), + float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + float4 weights = float4(0.0, 0.0, 0.0, 0.0); + + float2 e = SMAASample(edgesTex, texcoord).rg; + + SMAA_BRANCH + if (e.g > 0.0) { // Edge at north + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + // Diagonals have both north and west edges, so searching for them in + // one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + SMAA_BRANCH + if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 + #endif + + float2 d; + + // Find the distance to the left: + float3 coords; + coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; + + // Find the distance to the right: + coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); + d.y = coords.z; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the right crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + + // Fix corners: + coords.y = texcoord.y; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + } else + e.r = 0.0; // Skip vertical processing. + #endif + } + + SMAA_BRANCH + if (e.r > 0.0) { // Edge at west + float2 d; + + // Find the distance to the top: + float3 coords; + coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; + d.x = coords.y; + + // Fetch the top crossing edges: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; + + // Find the distance to the bottom: + coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); + d.y = coords.z; + + // We want the distances to be in pixel units: + d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the bottom crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; + + // Get the area for this direction: + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + + // Fix corners: + coords.x = texcoord.x; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + } + + return weights; +} + +//----------------------------------------------------------------------------- +// Neighborhood Blending Pixel Shader (Third Pass) + +float4 SMAANeighborhoodBlendingPS(float2 texcoord, + float4 offset, + SMAATexture2D(colorTex), + SMAATexture2D(blendTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + // Fetch the blending weights for current pixel: + float4 a; + a.x = SMAASample(blendTex, offset.xy).a; // Right + a.y = SMAASample(blendTex, offset.zw).g; // Top + a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left + + // Is there any blending weight with a value greater than 0.0? + SMAA_BRANCH + if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { + float4 color = SMAASampleLevelZero(colorTex, texcoord); + + #if SMAA_REPROJECTION + float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } else { + bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) + + // Calculate the blending offsets: + float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); + float2 blendingWeight = a.yw; + SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); + SMAAMovc(bool2(h, h), blendingWeight, a.xz); + blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); + + // Calculate the texture coordinates: + float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); + + // We exploit bilinear filtering to mix current pixel with the chosen + // neighbor: + float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); + color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); + + #if SMAA_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); + velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } +} + +//----------------------------------------------------------------------------- +// Temporal Resolve Pixel Shader (Optional Pass) + +float4 SMAAResolvePS(float2 texcoord, + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + #if SMAA_REPROJECTION + // Velocity is assumed to be calculated for motion blur, so we need to + // inverse it for reprojection: + float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); + + // Fetch current pixel: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + + // Reproject current coordinates and fetch previous pixel: + float4 previous = SMAASamplePoint(previousColorTex, texcoord + velocity); + + // Attenuate the previous pixel if the velocity is different: + float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; + float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); + + // Blend the pixels according to the calculated weight: + return lerp(current, previous, weight); + #else + // Just blend the pixels: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + float4 previous = SMAASamplePoint(previousColorTex, texcoord); + return lerp(current, previous, 0.5); + #endif +} + +//----------------------------------------------------------------------------- +// Separate Multisamples Pixel Shader (Optional Pass) + +#ifdef SMAALoad +void SMAASeparatePS(float4 position, + float2 texcoord, + out float4 target0, + out float4 target1, + SMAATexture2DMS2(colorTexMS)) { + int2 pos = int2(position.xy); + target0 = SMAALoad(colorTexMS, pos, 0); + target1 = SMAALoad(colorTexMS, pos, 1); +} +#endif + +//----------------------------------------------------------------------------- +#endif // SMAA_INCLUDE_PS + +``` + +`shaders/generic/color.frag`: + +```frag +/* color.frag -- Generic fragment shader used only to draw a color (e.g. background) + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +uniform vec4 uColor; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = uColor; +} + +``` + +`shaders/generic/cubemap.vert`: + +```vert +/* cube.vert -- Generic vertex shader for rendering a cube + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +layout(location = 0) in vec3 aPosition; +layout(location = 1) in vec2 aTexCoord; + +uniform mat4 uMatProj; +uniform mat4 uMatView; + +out vec3 vPosition; +out vec2 vTexCoord; + +void main() +{ + vPosition = aPosition; + vTexCoord = aTexCoord; + + gl_Position = uMatProj * uMatView * vec4(vPosition, 1.0); +} + +``` + +`shaders/generic/screen.vert`: + +```vert +/* screen.vert -- Generic vertex shader for rendering a full-screen quad + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +const vec2 positions[3] = vec2[] +( + vec2(-1.0, -1.0), + vec2( 3.0, -1.0), + vec2(-1.0, 3.0) +); + +noperspective out vec2 vTexCoord; + +void main() +{ + // Here Z is fixed at 1.0. + // For fullscreen passes that match the depth buffer resolution: + // - GL_EQUAL allows rendering only the background + // - GL_GREATER allows invoking the shader only on geometry + + gl_Position = vec4(positions[gl_VertexID], 1.0, 1.0); + vTexCoord = (gl_Position.xy * 0.5) + 0.5; +} + +``` + +`shaders/include/blocks/env.glsl`: + +```glsl +/* env.glsl -- Contains everything you need to manage environment + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +/* === Includes === */ + +#include "../math.glsl" + +/* === Structures === */ + +struct EnvAmbient { + vec4 rotation; + vec4 color; + float energy; + int irradiance; + int prefilter; +}; + +struct EnvProbe { + vec3 position; + float falloff; + float range; + int irradiance; + int prefilter; +}; + +/* === Uniform Block === */ + +layout(std140) uniform EnvBlock { + EnvProbe uProbes[NUM_PROBES]; + EnvAmbient uAmbient; + int uNumPrefilterLevels; + int uNumProbes; +}; + +/* === IBL Functions === */ + +float IBL_GetSpecularOcclusion(float NdotV, float ao, float roughness) +{ + // Lagarde method: https://seblagarde.wordpress.com/wp-content/uploads/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf + return clamp(pow(NdotV + ao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ao, 0.0, 1.0); +} + +vec3 IBL_SampleIrradiance(samplerCubeArray irradiance, int index, vec3 N) +{ + return texture(irradiance, vec4(N, float(index))).rgb; +} + +vec3 IBL_SampleIrradiance(samplerCubeArray irradiance, int index, vec3 N, vec4 rotation) +{ + return texture(irradiance, vec4(M_Rotate3D(N, rotation), float(index))).rgb; +} + +vec3 IBL_SamplePrefilter(samplerCubeArray prefilter, int index, vec3 V, vec3 N, float roughness) +{ + float mipLevel = roughness * float(uNumPrefilterLevels - 1); + return textureLod(prefilter, vec4(reflect(-V, N), float(index)), mipLevel).rgb; +} + +vec3 IBL_SamplePrefilter(samplerCubeArray prefilter, int index, vec3 V, vec3 N, vec4 rotation, float roughness) +{ + float mipLevel = roughness * float(uNumPrefilterLevels - 1); + return textureLod(prefilter, vec4(M_Rotate3D(reflect(-V, N), rotation), float(index)), mipLevel).rgb; +} + +void IBL_MultiScattering(inout vec3 irradiance, inout vec3 radiance, vec3 diffuse, vec3 F0, vec2 brdf, float NdotV, float roughness) +{ + // Adapted from Fdez-Aguera method without the roughness-dependent Fresnel + // See: https://jcgt.org/published/0008/01/03/paper.pdf + + // Single scattering with standard Fresnel + vec3 FssEss = F0 * brdf.x + brdf.y; + + // Multiple scattering, from Fdez-Aguera + float Ess = brdf.x + brdf.y; + float Ems = 1.0 - Ess; + vec3 Favg = F0 + (1.0 - F0) / 21.0; + vec3 FmsD = max(1.0 - (1.0 - Ess) * Favg, vec3(1e-6)); //< avoids division by zero in extreme F0/low roughness cases + vec3 Fms = FssEss * Favg / FmsD; + vec3 kD = diffuse * (1.0 - FssEss); + + // Compute final irradiance / radiance + irradiance *= (Fms * Ems + kD); + radiance *= FssEss; +} + +void IBL_SampleProbe(inout vec3 irr, inout vec3 rad, inout float wIrr, inout float wRad, int probeIndex, float roughness, vec3 P, vec3 N, vec3 V) +{ + EnvProbe probe = uProbes[probeIndex]; + float dist = length(P - probe.position); + float weight = pow(clamp(1.0 - dist / probe.range, 0.0, 1.0), probe.falloff); + + if (weight < 1e-6) return; + + if (probe.irradiance >= 0) { + vec3 probeIrr = IBL_SampleIrradiance(uIrradianceTex, probe.irradiance, N); + irr += probeIrr.rgb * weight; + wIrr += weight; + } + + if (probe.prefilter >= 0) { + vec3 probeRad = IBL_SamplePrefilter(uPrefilterTex, probe.prefilter, V, N, roughness); + rad += probeRad.rgb * weight; + wRad += weight; + } +} + +/* === Environment Functions === */ + +void E_ComputeAmbientAndProbes(inout vec3 diffuse, inout vec3 specular, vec3 kD, vec3 orm, vec3 F0, vec3 P, vec3 N, vec3 V, float NdotV) +{ + float occlusion = orm.x; + float roughness = orm.y; + float metalness = orm.z; + + vec3 irradiance = vec3(0.0); + float wIrradiance = 0.0; + + vec3 radiance = vec3(0.0); + float wRadiance = 0.0; + + for (int i = 0; i < uNumProbes; ++i) { + IBL_SampleProbe(irradiance, radiance, wIrradiance, wRadiance, i, roughness, P, N, V); + } + + if (wIrradiance > 1.0) { + float invTotalWeight = 1.0 / wIrradiance; + irradiance *= invTotalWeight; + wIrradiance = 1.0; + } + + if (wRadiance > 1.0) { + float invTotalWeight = 1.0 / wRadiance; + radiance *= invTotalWeight; + wRadiance = 1.0; + } + + if (wIrradiance < 1.0) { + vec3 ambientIrr = vec3(0.0); + if (uAmbient.irradiance < 0) ambientIrr = uAmbient.color.rgb; + else ambientIrr = IBL_SampleIrradiance(uIrradianceTex, uAmbient.irradiance, N, uAmbient.rotation); + irradiance += ambientIrr * (1.0 - wIrradiance); + } + + if (wRadiance < 1.0 && uAmbient.prefilter >= 0) { + vec3 ambientRad = IBL_SamplePrefilter(uPrefilterTex, uAmbient.prefilter, V, N, uAmbient.rotation, roughness); + radiance += ambientRad * (1.0 - wRadiance); + } + + irradiance *= occlusion * uAmbient.energy; + radiance *= IBL_GetSpecularOcclusion(NdotV, occlusion, roughness); + + vec2 brdf = texture(uBrdfLutTex, vec2(NdotV, roughness)).xy; + IBL_MultiScattering(irradiance, radiance, kD, F0, brdf, NdotV, roughness); + + diffuse += irradiance; + specular += radiance; +} + +void E_ComputeAmbientOnly(inout vec3 diffuse, inout vec3 specular, vec3 kD, vec3 orm, vec3 F0, vec3 P, vec3 N, vec3 V, float NdotV) +{ + float occlusion = orm.x; + float roughness = orm.y; + float metalness = orm.z; + + vec3 irradiance = (uAmbient.irradiance >= 0) + ? IBL_SampleIrradiance(uIrradianceTex, uAmbient.irradiance, N, uAmbient.rotation).rgb + : uAmbient.color.rgb; + irradiance *= occlusion * uAmbient.energy; + + vec3 radiance = vec3(0.0); + if (uAmbient.prefilter >= 0) { + radiance = IBL_SamplePrefilter(uPrefilterTex, uAmbient.prefilter, V, N, uAmbient.rotation, roughness).rgb; + radiance *= IBL_GetSpecularOcclusion(NdotV, occlusion, roughness); + } + + vec2 brdf = texture(uBrdfLutTex, vec2(NdotV, roughness)).xy; + IBL_MultiScattering(irradiance, radiance, kD, F0, brdf, NdotV, roughness); + + diffuse += irradiance; + specular += radiance; +} + +void E_ComputeAmbientColor(inout vec3 diffuse, vec3 kD, float occlusion) +{ + diffuse += kD * uAmbient.color.rgb * uAmbient.energy * occlusion; +} + +``` + +`shaders/include/blocks/fog.glsl`: + +```glsl +/* fog.glsl -- Contains everything you need for the fog + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +/* === Definitions === */ + +#define FOG_DISABLED 0 +#define FOG_LINEAR 1 +#define FOG_EXP2 2 +#define FOG_EXP 3 + +/* === Uniform Block === */ + +struct Fog { + vec3 color; + float start; + float end; + float density; + float skyAffect; + int mode; +}; + +layout(std140) uniform FogBlock { + Fog uFog; +}; + +/* === Functions === */ + +float FogFactorLinear(float dist, float start, float end) +{ + return 1.0 - clamp((end - dist) / (end - start), 0.0, 1.0); +} + +float FogFactorExp2(float dist, float density) +{ + const float LOG2 = -1.442695; + float d = density * dist; + return 1.0 - clamp(exp2(d * d * LOG2), 0.0, 1.0); +} + +float FogFactorExp(float dist, float density) +{ + return 1.0 - clamp(exp(-density * dist), 0.0, 1.0); +} + +float FogFactor(float dist) +{ + if (uFog.mode == FOG_LINEAR) return FogFactorLinear(dist, uFog.start, uFog.end); + if (uFog.mode == FOG_EXP2) return FogFactorExp2(dist, uFog.density); + if (uFog.mode == FOG_EXP) return FogFactorExp(dist, uFog.density); + return 0.0; // FOG_DISABLED +} + +vec4 FogColorAlpha(float dist) +{ + return vec4(uFog.color, FogFactor(dist)); +} + +vec3 FogColorMix(vec3 color, float dist) +{ + return mix(color, uFog.color, FogFactor(dist)); +} + +vec4 FogColorMix(vec4 color, float dist) +{ + return vec4(mix(color.rgb, uFog.color, FogFactor(dist)), color.a); +} + +vec3 FogSkyMix(vec3 sky) +{ + if (uFog.mode == FOG_DISABLED) return sky; + return mix(sky, uFog.color, uFog.skyAffect); +} + +``` + +`shaders/include/blocks/frame.glsl`: + +```glsl +/* frame.glsl -- Contains all the information about the current frame. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +struct Frame { + vec2 screenSize; + vec2 texelSize; + float time; + int index; +}; + +layout(std140) uniform FrameBlock { + Frame uFrame; +}; + +``` + +`shaders/include/blocks/light.glsl`: + +```glsl +/* light.glsl -- Contains everything you need to manage lights + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +/* === Includes === */ + +#include "../math.glsl" +#include "../pbr.glsl" + +/* === Macros === */ + +#if defined(NUM_FORWARD_LIGHTS) +# define DECL_SHADOW_DIR(name) float name(int lightIndex, vec4 Pls, float Zvs, float NdotL, mat2 diskRot) +# define DECL_SHADOW_SPOT(name) float name(int lightIndex, vec4 Pls, float NdotL, mat2 diskRot) +# define DECL_SHADOW_OMNI(name) float name(int lightIndex, vec3 Pws, float NdotL, mat2 diskRot) +# define LIGHT uLights[lightIndex] +#else +# define DECL_SHADOW_DIR(name) float name(vec3 Pws, float Zvs, float NdotL, mat2 diskRot) +# define DECL_SHADOW_SPOT(name) float name(vec3 Pws, float NdotL, mat2 diskRot) +# define DECL_SHADOW_OMNI(name) float name(vec3 Pws, float NdotL, mat2 diskRot) +# define LIGHT uLight +#endif + +/* === Constants === */ + +// Should be defined in client side +// #define NUM_FORWARD_LIGHTS 8 + +#define LIGHT_DIR 0 +#define LIGHT_SPOT 1 +#define LIGHT_OMNI 2 + +#define SHADOW_SAMPLES 8 + +const vec2 VOGEL_DISK[8] = vec2[8]( + vec2(0.250000, 0.000000), + vec2(-0.319290, 0.292496), + vec2(0.048872, -0.556877), + vec2(0.402444, 0.524918), + vec2(-0.738535, -0.130636), + vec2(0.699605, -0.445031), + vec2(-0.234004, 0.870484), + vec2(-0.446271, -0.859268) +); + +/* === Structures === */ + +struct Light { + mat4 viewProj; + vec3 color; + vec3 position; + vec3 direction; + float specular; + float energy; + float range; + float near; + float far; + float attenuation; + float innerCutOff; + float outerCutOff; + float shadowSoftness; + float shadowOpacity; + float shadowDepthBias; + float shadowSlopeBias; + int shadowLayer; //< less than zero if no shadows + int type; +}; + +/* === Uniform Block === */ + +#ifdef NUM_FORWARD_LIGHTS +layout(std140) uniform LightArrayBlock { + Light uLights[NUM_FORWARD_LIGHTS]; + int uNumLights; +}; +#else +layout(std140) uniform LightBlock { + Light uLight; +}; +#endif + +/* === Functions === */ + +vec3 L_Diffuse(float LdotH, float NdotV, float NdotL, float roughness) +{ + float FD90_minus_1 = 2.0 * LdotH * LdotH * roughness - 0.5; + float FdV = 1.0 + FD90_minus_1 * PBR_SchlickFresnel(NdotV); + float FdL = 1.0 + FD90_minus_1 * PBR_SchlickFresnel(NdotL); + + return vec3(M_INV_PI * (FdV * FdL * NdotL)); // Diffuse BRDF (Burley) +} + +vec3 L_Specular(vec3 F0, float LdotH, float cNdotH, float NdotV, float NdotL, float roughness) +{ + roughness = max(roughness, 0.02); // >0.01 to avoid FP16 overflow after GGX distribution + + float alphaGGX = roughness * roughness; + float D = PBR_DistributionGGX(cNdotH, alphaGGX); + float G = PBR_GeometryGGX(NdotL, NdotV, alphaGGX); + + float cLdotH5 = PBR_SchlickFresnel(LdotH); + float F90 = clamp(50.0 * F0.g, 0.0, 1.0); + vec3 F = F0 + (F90 - F0) * cLdotH5; + + return NdotL * D * F * G; // Specular BRDF (Schlick GGX) +} + +#ifdef L_SHADOW_IMPL + +mat2 L_ShadowDebandingMatrix(vec2 fragCoord) +{ + float r = M_TAU * M_HashIGN(fragCoord); + float sr = sin(r), cr = cos(r); + return mat2(vec2(cr, -sr), vec2(sr, cr)); +} + +DECL_SHADOW_DIR(L_SampleShadowDir) +{ +#if !defined(NUM_FORWARD_LIGHTS) + vec4 Pls = LIGHT.viewProj * vec4(Pws, 1.0); +#endif + + vec3 projCoords = Pls.xyz / Pls.w * 0.5 + 0.5; + float bias = LIGHT.shadowDepthBias + LIGHT.shadowSlopeBias * (1.0 - NdotL); + float compareDepth = projCoords.z - bias; + + float shadow = 0.0; + for (int i = 0; i < SHADOW_SAMPLES; ++i) { + vec2 offset = diskRot * VOGEL_DISK[i] * LIGHT.shadowSoftness; + shadow += texture(uShadowDirTex, vec4(projCoords.xy + offset, LIGHT.shadowLayer, compareDepth)); + } + shadow /= float(SHADOW_SAMPLES); + + vec3 distToBorder = min(projCoords, 1.0 - projCoords); + float edgeFade = smoothstep(0.0, 0.05, min(distToBorder.x, min(distToBorder.y, distToBorder.z))); + float distFade = smoothstep(LIGHT.range, LIGHT.range * 0.75, Zvs); + + return mix(1.0, shadow, edgeFade * distFade * LIGHT.shadowOpacity); +} + +DECL_SHADOW_SPOT(L_SampleShadowSpot) +{ +#if !defined(NUM_FORWARD_LIGHTS) + vec4 Pls = LIGHT.viewProj * vec4(Pws, 1.0); +#endif + + vec3 projCoords = Pls.xyz / Pls.w * 0.5 + 0.5; + float bias = LIGHT.shadowDepthBias + LIGHT.shadowSlopeBias * (1.0 - NdotL); + float compareDepth = projCoords.z - bias; + + float shadow = 0.0; + for (int i = 0; i < SHADOW_SAMPLES; ++i) { + vec2 offset = diskRot * VOGEL_DISK[i] * LIGHT.shadowSoftness; + shadow += texture(uShadowSpotTex, vec4(projCoords.xy + offset, LIGHT.shadowLayer, compareDepth)); + } + shadow /= float(SHADOW_SAMPLES); + + return mix(1.0, shadow, LIGHT.shadowOpacity); +} + +DECL_SHADOW_OMNI(L_SampleShadowOmni) +{ + vec3 lightToFrag = Pws - LIGHT.position; + float currentDepth = length(lightToFrag); + + float bias = LIGHT.shadowDepthBias + LIGHT.shadowSlopeBias * (1.0 - NdotL); + float compareDepth = (currentDepth - bias) / LIGHT.far; + + mat3 OBN = M_OrthonormalBasis(lightToFrag / currentDepth); + + float shadow = 0.0; + for (int i = 0; i < SHADOW_SAMPLES; ++i) { + vec2 diskOffset = diskRot * VOGEL_DISK[i] * LIGHT.shadowSoftness; + shadow += texture(uShadowOmniTex, vec4(OBN * vec3(diskOffset.xy, 1.0), LIGHT.shadowLayer), compareDepth); + } + shadow /= float(SHADOW_SAMPLES); + + return mix(1.0, shadow, LIGHT.shadowOpacity); +} + +#endif // L_SHADOW_IMPL + +/* === Undefs === */ + +#undef SAMPLE_SHADOW_PROJ +#undef SAMPLE_SHADOW_OMNI +#undef LIGHT + +``` + +`shaders/include/blocks/view.glsl`: + +```glsl +/* view.glsl -- Contains everything you need to manage transformations + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "../math.glsl" + +#define V_PROJ_PERSP 0 +#define V_PROJ_ORTHO 1 + +struct View { + vec3 position; + mat4 view; + mat4 invView; + mat4 proj; + mat4 invProj; + mat4 viewProj; + int projMode; + float aspect; + float near; + float far; +}; + +layout(std140) uniform ViewBlock { + View uView; +}; + +vec3 V_GetViewPosition(vec2 texCoord, float linearDepth) +{ + vec2 ndc = texCoord * 2.0 - 1.0; + + if (uView.projMode == V_PROJ_ORTHO) { + vec2 xyScale = vec2(1.0 / uView.proj[0][0], 1.0 / uView.proj[1][1]); + return vec3(ndc * xyScale, -linearDepth); + } + + float tanHalfFov = 1.0 / uView.proj[1][1]; + vec3 viewRay = vec3(ndc.x * tanHalfFov * uView.aspect, ndc.y * tanHalfFov, -1.0); + + return viewRay * linearDepth; +} + +vec3 V_GetViewPosition(sampler2D texLinearDepth, vec2 texCoord) +{ + float linearDepth = texture(texLinearDepth, texCoord).r; + return V_GetViewPosition(texCoord, linearDepth); +} + +vec3 V_GetViewPosition(sampler2D texLinearDepth, ivec2 pixCoord) +{ + vec2 texCoord = (vec2(pixCoord) + 0.5) / vec2(textureSize(texLinearDepth, 0)); + float linearDepth = texelFetch(texLinearDepth, pixCoord, 0).r; + return V_GetViewPosition(texCoord, linearDepth); +} + +vec3 V_GetWorldPosition(vec3 viewPosition) +{ + return (uView.invView * vec4(viewPosition, 1.0)).xyz; +} + +vec3 V_GetWorldPosition(vec2 texCoord, float linearDepth) +{ + vec3 viewPosition = V_GetViewPosition(texCoord, linearDepth); + return V_GetWorldPosition(viewPosition); +} + +vec3 V_GetWorldPosition(sampler2D texLinearDepth, vec2 texCoord) +{ + float linearDepth = texture(texLinearDepth, texCoord).r; + return V_GetWorldPosition(texCoord, linearDepth); +} + +vec3 V_GetWorldPosition(sampler2D texLinearDepth, ivec2 pixCoord) +{ + vec2 texCoord = (vec2(pixCoord) + 0.5) / vec2(textureSize(texLinearDepth, 0)); + float linearDepth = texelFetch(texLinearDepth, pixCoord, 0).r; + return V_GetWorldPosition(texCoord, linearDepth); +} + +vec3 V_GetViewNormal(vec3 worldNormal) +{ + return normalize(mat3(uView.view) * worldNormal); +} + +vec3 V_GetViewNormal(vec2 encWorldNormal) +{ + vec3 worldNormal = M_DecodeOctahedral(encWorldNormal); + return V_GetViewNormal(worldNormal); +} + +vec3 V_GetViewNormal(sampler2D texNormal, vec2 texCoord) +{ + vec2 encWorldNormal = texture(texNormal, texCoord).rg; + return V_GetViewNormal(encWorldNormal); +} + +vec3 V_GetViewNormal(sampler2D texNormal, ivec2 pixCoord) +{ + vec2 encWorldNormal = texelFetch(texNormal, pixCoord, 0).rg; + return V_GetViewNormal(encWorldNormal); +} + +vec3 V_GetWorldNormal(sampler2D texNormal, vec2 texCoord) +{ + vec2 encWorldNormal = texture(texNormal, texCoord).rg; + return M_DecodeOctahedral(encWorldNormal); +} + +vec3 V_GetWorldNormal(sampler2D texNormal, ivec2 pixCoord) +{ + vec2 encWorldNormal = texelFetch(texNormal, pixCoord, 0).rg; + return M_DecodeOctahedral(encWorldNormal); +} + +vec2 V_ViewToScreen(vec3 viewPosition) +{ + vec4 clipPos = uView.proj * vec4(viewPosition, 1.0); + return (clipPos.xy / clipPos.w) * 0.5 + 0.5; +} + +vec2 V_WorldToScreen(vec3 worldPosition) +{ + vec4 projPos = uView.viewProj * vec4(worldPosition, 1.0); + return (projPos.xy / projPos.w) * 0.5 + 0.5; +} + +bool V_OffScreen(vec2 texCoord) +{ + return any(lessThan(texCoord, vec2(0.0))) || + any(greaterThan(texCoord, vec2(1.0))); +} + +float V_LinearizeDepth(float depth) +{ + if (uView.projMode == V_PROJ_ORTHO) { + return uView.near + depth * (uView.far - uView.near); + } + + // Perspective + float near = uView.near, far = uView.far; + return (2.0 * near * far) / (far + near - (depth * 2.0 - 1.0) * (far - near)); +} + +float V_LinearizeDepth01(float depth) +{ + if (uView.projMode == V_PROJ_ORTHO) { + return depth; + } + + // Perspective + float near = uView.near, far = uView.far; + float z = (2.0 * near * far) / (far + near - (depth * 2.0 - 1.0) * (far - near)); + + return (z - near) / (far - near); +} + +``` + +`shaders/include/math.glsl`: + +```glsl +/* math.glsl -- Contains everything you need for maths + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +/* === Constants === */ + +#define M_PI 3.1415926535897931 +#define M_HPI 1.5707963267948966 +#define M_TAU 6.2831853071795862 +#define M_INV_PI 0.3183098861837907 +#define M_PHI 1.6180339887498949 +#define M_PHI_FRAC 0.6180339887498949 +#define M_GOLDEN_ANGLE 2.3999632297286535 + +/* === Functions === */ + +vec3 M_Rotate3D(vec3 v, vec4 q) +{ + vec3 t = 2.0 * cross(q.xyz, v); + return v + q.w * t + cross(q.xyz, t); +} + +mat3 M_OrthonormalBasis(vec3 n) +{ + // Previously we used Frisvad's method to generate a stable orthonormal basis + // SEE: https://backend.orbit.dtu.dk/ws/portalfiles/portal/126824972/onb_frisvad_jgt2012_v2.pdf + + // However, it can cause visible artifacts (eg. bright pixels on the -Z face of irradiance cubemaps) + // So now we use the revised method by Duff et al., it's more accurate, though slightly slower + // SEE: https://graphics.pixar.com/library/OrthonormalB/paper.pdf + + float sgn = n.z >= 0.0 ? 1.0 : -1.0; + float a = -1.0 / (sgn + n.z); + float b = n.x * n.y * a; + + vec3 t = vec3(1.0 + sgn * n.x * n.x * a, sgn * b, -sgn * n.x); + vec3 bt = vec3(b, sgn + n.y * n.y * a, -n.y); + + return mat3(t, bt, n); +} + +vec2 M_OctahedronWrap(vec2 val) +{ + // Reference(s): + // - Octahedron normal vector encoding + // https://web.archive.org/web/20191027010600/https://knarkowicz.wordpress.com/2014/04/16/octahedron-normal-vector-encoding/comment-page-1/ + return (1.0 - abs(val.yx)) * mix(vec2(-1.0), vec2(1.0), vec2(greaterThanEqual(val.xy, vec2(0.0)))); +} + +vec3 M_DecodeOctahedral(vec2 encoded) +{ + encoded = encoded * 2.0 - 1.0; + + vec3 normal; + normal.z = 1.0 - abs(encoded.x) - abs(encoded.y); + normal.xy = normal.z >= 0.0 ? encoded.xy : M_OctahedronWrap(encoded.xy); + return normalize(normal); +} + +vec2 M_EncodeOctahedral(vec3 normal) +{ + normal /= abs(normal.x) + abs(normal.y) + abs(normal.z); + normal.xy = normal.z >= 0.0 ? normal.xy : M_OctahedronWrap(normal.xy); + normal.xy = normal.xy * 0.5 + 0.5; + return normal.xy; +} + +vec3 M_NormalScale(vec3 normal, float scale) +{ + normal.xy *= scale; + normal.z = sqrt(1.0 - clamp(dot(normal.xy, normal.xy), 0.0, 1.0)); + return normal; +} + +float M_HashIGN(vec2 pos) +{ + // http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare + const vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); + return fract(magic.z * fract(dot(pos, magic.xy))); +} + +float M_HashIGN(vec2 pos, float frame) +{ + vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); + return fract(magic.z * fract(dot(vec3(pos, frame), magic))); +} + +float M_HashR2(vec2 p) +{ + const vec2 k = vec2(0.75487766624669276, 0.56984029099805327); + return fract(dot(p, k)); +} + +/* +float M_Bayer8(vec2 p) +{ + uvec2 r = uvec2(p) & 7u; + r.x ^= r.y; + + uint v = (r.x & 1u) << 5u + | (r.y & 1u) << 4u + | (r.x & 2u) << 2u + | (r.y & 2u) << 1u + | (r.x & 4u) >> 1u + | (r.y & 4u) >> 2u; + + return float(v) / 64.0; +} +*/ + +``` + +`shaders/include/pbr.glsl`: + +```glsl +/* pbr.glsl -- Contains everything you need for PBR + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./math.glsl" + +/* === Functions === */ + +float PBR_DistributionGGX(float cosTheta, float alpha) +{ + // Standard GGX/Trowbridge-Reitz distribution - optimized form + + float a = cosTheta * alpha; + float k = alpha / (1.0 - cosTheta * cosTheta + a * a); + return k * k * (1.0 / M_PI); +} + +float PBR_GeometryGGX(float NdotL, float NdotV, float roughness) +{ + // Hammon's optimized approximation for GGX Smith geometry term + // This version is an efficient approximation that: + // 1. Avoids expensive square root calculations + // 2. Combines both G1 terms into a single expression + // 3. Provides very close results to the exact version at a much lower cost + // SEE: https://www.gdcvault.com/play/1024478/PBR-Diffuse-Lighting-for-GGX + + return 0.5 / mix(2.0 * NdotL * NdotV, NdotL + NdotV, roughness); +} + +float PBR_SchlickFresnel(float u) +{ + float m = 1.0 - u; + float m2 = m * m; + return m2 * m2 * m; // pow(m,5) +} + +vec3 PBR_ComputeF0(float metallic, float specular, vec3 albedo) +{ + // use (albedo * metallic) as colored specular reflectance at 0 angle for metallic materials + // SEE: https://google.github.io/filament/Filament.md.html + + float dielectric = 0.16 * specular * specular; + return mix(vec3(dielectric), albedo, vec3(metallic)); +} + +``` + +`shaders/include/smaa.glsl`: + +```glsl +/* smaa.glsl -- Includes SMAA.hlsl with the correct configuration + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "../include/blocks/frame.glsl" + +#define SMAA_GLSL_3 +#define SMAA_RT_METRICS vec4(uFrame.texelSize, uFrame.screenSize) + +#if QUALITY_PRESET == 0 +# define SMAA_PRESET_LOW +#elif QUALITY_PRESET == 1 +# define SMAA_PRESET_MEDIUM +#elif QUALITY_PRESET == 2 +# define SMAA_PRESET_HIGH +#elif QUALITY_PRESET == 3 +# define SMAA_PRESET_ULTRA +#endif + +#include "../external/SMAA.hlsl" + +``` + +`shaders/include/user/scene.frag`: + +```frag +/* scene.fs -- Contains everything for custom user scene fragment shader + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +/* === Built-In Constants === */ + +#define POSITION vPosition + +/* === Built-In Input Variables === */ + +vec2 TEXCOORD = vec2(0.0); +vec3 TANGENT = vec3(0.0); +vec3 BITANGENT = vec3(0.0); +vec3 NORMAL = vec3(0.0); + +/* === Built-In Output Variables === */ + +vec3 ALBEDO = vec3(0.0); +float ALPHA = 0.0; +vec3 EMISSION = vec3(0.0); +vec3 NORMAL_MAP = vec3(0.0); +float OCCLUSION = 0.0; +float ROUGHNESS = 0.0; +float METALNESS = 0.0; +float SPECULAR = 0.0; + +/* === Built-In Globals === */ + +int FRAME_INDEX = 0; +float TIME = 0.0; + +/* === User Callable === */ + +vec4 SampleAlbedo(vec2 texCoord) +{ + return vColor * texture(uAlbedoMap, texCoord); +} + +vec3 SampleEmission(vec2 texCoord) +{ + vec3 emission = vec3(0.0); +#if !defined(UNLIT) && !defined(PROBE_UNLIT) && !defined(DEPTH) && !defined(DEPTH_CUBE) + emission = vEmission * texture(uEmissionMap, texCoord).rgb; +#endif + return emission; +} + +vec3 SampleNormal(vec2 texCoord) +{ + vec3 normal = vec3(0.0); +#if !defined(UNLIT) && !defined(PROBE_UNLIT) && !defined(DEPTH) && !defined(DEPTH_CUBE) + normal = texture(uNormalMap, texCoord).rgb; +#endif + return normal; +} + +vec4 SampleOrm(vec2 texCoord) +{ + vec4 ORM = vec4(0.0); +#if !defined(UNLIT) && !defined(PROBE_UNLIT) && !defined(DEPTH) && !defined(DEPTH_CUBE) + ORM = texture(uOrmMap, texCoord); + ORM.x *= uOcclusion; + ORM.y *= uRoughness; + ORM.z *= uMetalness; + ORM.w = uSpecular; +#endif + return ORM; +} + +void FetchMaterial(vec2 texCoord) +{ + vec4 color = vColor * texture(uAlbedoMap, texCoord); + ALBEDO = color.rgb; + ALPHA = color.a; + +#if !defined(UNLIT) && !defined(PROBE_UNLIT) && !defined(DEPTH) && !defined(DEPTH_CUBE) + EMISSION = vEmission * texture(uEmissionMap, texCoord).rgb; + NORMAL_MAP = texture(uNormalMap, texCoord).rgb; + vec3 ORM = texture(uOrmMap, texCoord).rgb; + OCCLUSION = uOcclusion * ORM.x; + ROUGHNESS = uRoughness * ORM.y; + METALNESS = uMetalness * ORM.z; + SPECULAR = uSpecular; +#endif +} + +/* === Internal Fragment Stage === */ + +#define fragment() + +void SceneFragment(vec2 texCoord, mat3 tbn, float alphaCutoff) +{ + /* --- Fill input variables --- */ + + TEXCOORD = texCoord; + TANGENT = tbn[0]; + BITANGENT = tbn[1]; + NORMAL = tbn[2]; + + /* --- Fetch output variables --- */ + +#if !defined(R3D_NO_AUTO_FETCH) + vec4 color = vColor * texture(uAlbedoMap, texCoord); + if (color.a < alphaCutoff) discard; + ALBEDO = color.rgb; + ALPHA = color.a; + +#if !defined(UNLIT) && !defined(PROBE_UNLIT) && !defined(DEPTH) && !defined(DEPTH_CUBE) + EMISSION = vEmission * texture(uEmissionMap, texCoord).rgb; + NORMAL_MAP = texture(uNormalMap, texCoord).rgb; + vec3 ORM = texture(uOrmMap, texCoord).rgb; + OCCLUSION = uOcclusion * ORM.x; + ROUGHNESS = uRoughness * ORM.y; + METALNESS = uMetalness * ORM.z; + SPECULAR = uSpecular; + +#endif // !R3D_NO_AUTO_FETCH && !UNLIT && !PROBE_UNLIT && !DEPTH && !DEPTH_CUBE +#endif // !R3D_NO_AUTO_FETCH + + /* --- Fill constants --- */ + + FRAME_INDEX = uFrame.index; + TIME = uFrame.time; + + /* --- Execute user code --- */ + + fragment(); + + // Alpha cutoff again after user code + if (ALPHA < alphaCutoff) discard; +} + +``` + +`shaders/include/user/scene.vert`: + +```vert +/* scene.vs -- Contains everything for custom user scene vertex shader + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +/* === Built-In Constants === */ + +#define MATRIX_MODEL uMatModel +#define MATRIX_NORMAL mat3(uMatNormal) + +#if defined(DEPTH) || defined(DEPTH_CUBE) || defined(PROBE) +# define MATRIX_INV_VIEW uMatInvView +# define MATRIX_VIEW_PROJECTION uMatViewProj +#else +# define MATRIX_INV_VIEW uView.invView +# define MATRIX_VIEW_PROJECTION uView.viewProj +#endif + +/* === Built-In Output Variables === */ + +vec3 POSITION = vec3(0.0); +vec2 TEXCOORD = vec2(0.0); +vec3 EMISSION = vec3(0.0); +vec4 COLOR = vec4(0.0); +vec4 TANGENT = vec4(0.0); +vec3 NORMAL = vec3(0.0); + +vec3 INSTANCE_POSITION = vec3(0.0); +vec4 INSTANCE_ROTATION = vec4(0.0); +vec3 INSTANCE_SCALE = vec3(0.0); +vec4 INSTANCE_COLOR = vec4(0.0); +vec4 INSTANCE_CUSTOM = vec4(0.0); + +/* === Built-In Globals === */ + +int FRAME_INDEX = 0; +float TIME = 0.0; + +/* === Internal Vertex Stage === */ + +#define vertex() + +void SceneVertex() +{ + INSTANCE_POSITION = iPosition; + INSTANCE_ROTATION = iRotation; + INSTANCE_SCALE = iScale; + INSTANCE_COLOR = iColor; + INSTANCE_CUSTOM = iCustom; + + POSITION = aPosition; + TEXCOORD = uTexCoordOffset + aTexCoord * uTexCoordScale; + EMISSION = uEmissionColor * uEmissionEnergy; + COLOR = aColor * uAlbedoColor; + TANGENT = aTangent; + NORMAL = aNormal; + + FRAME_INDEX = uFrame.index; + TIME = uFrame.time; + + vertex(); +} + +``` + +`shaders/post/auto_exposure.frag`: + +```frag +#version 330 core + +uniform sampler2D uSceneTex; +uniform sampler2D uExposureTex; + +out vec4 FragColor; + +void main() +{ + vec4 color = texelFetch(uSceneTex, ivec2(gl_FragCoord.xy), 0); + float exposure = texelFetch(uExposureTex, ivec2(0), 0).r; + FragColor = vec4(color.rgb * exposure, color.a); +} + +``` + +`shaders/post/bloom.frag`: + +```frag +/* bloom.frag -- Fragment shader for applying bloom to the scene + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Definitions === */ + +#define BLOOM_MIX 1 +#define BLOOM_ADDITIVE 2 +#define BLOOM_SCREEN 3 + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uSceneTex; +uniform sampler2D uBloomTex; + +uniform lowp int uBloomMode; +uniform float uBloomIntensity; + +/* === Fragments === */ + +out vec3 FragColor; + +/* === Main program === */ + +void main() +{ + vec3 color = texture(uSceneTex, vTexCoord).rgb; + vec3 bloom = texture(uBloomTex, vTexCoord).rgb; + + if (uBloomMode == BLOOM_MIX) { + color = mix(color, bloom, uBloomIntensity); + } + else if (uBloomMode == BLOOM_ADDITIVE) { + color += bloom * uBloomIntensity; + } + else if (uBloomMode == BLOOM_SCREEN) { + bloom = clamp(bloom * uBloomIntensity, vec3(0.0), vec3(1.0)); + color = max((color + bloom) - (color * bloom), vec3(0.0)); + } + + FragColor = vec3(color); +} + +``` + +`shaders/post/dof.frag`: + +```frag +/* dof.frag -- Depth of Field composition shader + * + * Copyright (c) 2025 Victor Le Juez + * + * This software is distributed under the terms of the accompanying LICENSE file. + * It is provided "as-is", without any express or implied warranty. + */ + +#version 330 core + +noperspective in vec2 vTexCoord; + +uniform sampler2D uSceneTex; +uniform sampler2D uBlurTex; + +out vec4 FragColor; + +void main() +{ + // There can be a tiny 1-pixel "bleeding" around sharp edges. + // This comes from the half-res linear filtering of sharp pixels during upsampling, not from the blur itself. + // One way to completely eliminate this bleeding is to use texelFetch but this creates a blocky pixelated look. + + vec4 sharp = texelFetch(uSceneTex, ivec2(gl_FragCoord), 0); + vec4 blur = texture(uBlurTex, vTexCoord); + + FragColor = vec4(mix(blur.rgb, sharp.rgb, blur.a), 1.0); +} + +``` + +`shaders/post/fxaa.frag`: + +```frag +/* fxaa.frag -- Fragment shader for applying FXAA to the scene + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === FXAA Setup === */ + +#define FXAA_PC 1 +#define FXAA_GLSL_130 1 + +#define FXAA_GREEN_AS_LUMA 1 + +#ifndef FXAA_FAST_PIXEL_OFFSET +# define FXAA_FAST_PIXEL_OFFSET 0 +#endif + +#ifndef FXAA_GATHER4_ALPHA +# define FXAA_GATHER4_ALPHA 0 +#endif + +#if QUALITY_PRESET == 0 +# define FXAA_QUALITY__PRESET 12 +# define FXAA_EDGE_THRESHOLD (1.0/4.0) +# define FXAA_EDGE_THRESHOLD_MIN (1.0/12.0) +# define FXAA_SUBPIX 0.50 +#elif QUALITY_PRESET == 1 +# define FXAA_QUALITY__PRESET 15 +# define FXAA_EDGE_THRESHOLD (1.0/8.0) +# define FXAA_EDGE_THRESHOLD_MIN (1.0/16.0) +# define FXAA_SUBPIX 0.75 +#elif QUALITY_PRESET == 2 +# define FXAA_QUALITY__PRESET 23 +# define FXAA_EDGE_THRESHOLD (1.0/8.0) +# define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0) +# define FXAA_SUBPIX 0.90 +#elif QUALITY_PRESET == 3 +# define FXAA_QUALITY__PRESET 29 +# define FXAA_EDGE_THRESHOLD (1.0/12.0) +# define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0) +# define FXAA_SUBPIX 1.00 +#endif + +#include "../external/Fxaa3_11.h" + +/* === Includes === */ + +#include "../include/blocks/frame.glsl" + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uSceneTex; + +/* === Fragments === */ + +out vec4 FragColor; + +/* === Main Function === */ + +void main() +{ + vec4 fxaaConsolePosPos = FxaaFloat4( + vTexCoord - (0.5 * uFrame.texelSize), + vTexCoord + (0.5 * uFrame.texelSize) + ); + + vec4 dummy = vec4(0.0); + + FragColor = FxaaPixelShader( + vTexCoord, // pos + fxaaConsolePosPos, // fxaaConsolePosPos + uSceneTex, // tex + uSceneTex, // fxaaConsole360TexExpBiasNegOne + uSceneTex, // fxaaConsole360TexExpBiasNegTwo + uFrame.texelSize, // fxaaQualityRcpFrame + vec4(0.0), // fxaaConsoleRcpFrameOpt + vec4(0.0), // fxaaConsoleRcpFrameOpt2 + vec4(0.0), // fxaaConsole360RcpFrameOpt2 + FXAA_SUBPIX, // fxaaQualitySubpix + FXAA_EDGE_THRESHOLD, // fxaaQualityEdgeThreshold + FXAA_EDGE_THRESHOLD_MIN, // fxaaQualityEdgeThresholdMin + 0.0, // fxaaConsoleEdgeSharpness + 0.0, // fxaaConsoleEdgeThreshold + 0.0, // fxaaConsoleEdgeThresholdMin + vec4(0.0) // fxaaConsole360ConstDir + ); +} + +``` + +`shaders/post/output.frag`: + +```frag +/* output.frag -- Scene output fragment shader + * + * Performs tone mapping, debanding, color adjustments, and + * converts from linear color space to sRGB. + * + * Copyright (c) 2025 Victor Le Juez + * + * This software is distributed under the terms of the accompanying LICENSE file. + * It is provided "as-is", without any express or implied warranty. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/math.glsl" + +/* === Definitions === */ + +#define TONEMAP_LINEAR 0 +#define TONEMAP_REINHARD 1 +#define TONEMAP_FILMIC 2 +#define TONEMAP_ACES 3 +#define TONEMAP_AGX 4 + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uSceneTex; //< Scene color texture +uniform float uTonemapExposure; //< Tonemap exposure +uniform float uTonemapWhite; //< Tonemap white point, not used with AGX +uniform int uTonemapMode; //< Tonemap mode used (e.g. TONEMAP_LINEAR) +uniform float uBrightness; //< Brightness adjustment +uniform float uContrast; //< Contrast adjustment +uniform float uSaturation; //< Saturation adjustment + +/* === Fragments === */ + +out vec4 FragColor; + +/* === Tonemap Functions === */ + +// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt +vec3 TonemapReinhard(vec3 color, float pWhite) +{ + float whiteSquared = pWhite * pWhite; + vec3 whiteSquaredColor = whiteSquared * color; + // Equivalent to color * (1 + color / whiteSquared) / (1 + color) + return (whiteSquaredColor + color * color) / (whiteSquaredColor + whiteSquared); +} + +vec3 TonemapFilmic(vec3 color, float pWhite) +{ + // exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers + // also useful to scale the input to the range that the tonemapper is designed for (some require very high input values) + // has no effect on the curve's general shape or visual properties + const float exposureBias = 2.0; + const float A = 0.22 * exposureBias * exposureBias; // bias baked into constants for performance + const float B = 0.30 * exposureBias; + const float C = 0.10; + const float D = 0.20; + const float E = 0.01; + const float F = 0.30; + + vec3 colorTonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; + float pWhiteTonemapped = ((pWhite * (A * pWhite + C * B) + D * E) / (pWhite * (A * pWhite + B) + D * F)) - E / F; + + return colorTonemapped / pWhiteTonemapped; +} + +// Adapted from https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl +// (MIT License). +vec3 TonemapACES(vec3 color, float pWhite) +{ + const float exposureBias = 1.8; + const float A = 0.0245786; + const float B = 0.000090537; + const float C = 0.983729; + const float D = 0.432951; + const float E = 0.238081; + + // Exposure bias baked into transform to save shader instructions. Equivalent to `color *= exposureBias` + const mat3 rgb_to_rrt = mat3( + vec3(0.59719 * exposureBias, 0.35458 * exposureBias, 0.04823 * exposureBias), + vec3(0.07600 * exposureBias, 0.90834 * exposureBias, 0.01566 * exposureBias), + vec3(0.02840 * exposureBias, 0.13383 * exposureBias, 0.83777 * exposureBias) + ); + + const mat3 odt_to_rgb = mat3( + vec3(1.60475, -0.53108, -0.07367), + vec3(-0.10208, 1.10813, -0.00605), + vec3(-0.00327, -0.07276, 1.07602) + ); + + color *= rgb_to_rrt; + vec3 colorTonemapped = (color * (color + A) - B) / (color * (C * color + D) + E); + colorTonemapped *= odt_to_rgb; + + pWhite *= exposureBias; + float pWhiteTonemapped = (pWhite * (pWhite + A) - B) / (pWhite * (C * pWhite + D) + E); + + return colorTonemapped / pWhiteTonemapped; +} + +// Polynomial approximation of EaryChow's AgX sigmoid curve. +// x must be within range [0.0, 1.0] +vec3 AgXContrastApprox(vec3 x) +{ + // 6th order polynomial generated from sigmoid curve with 57 sample points + // Intercept set to 0.0 for performance and correct intersection + vec3 x2 = x * x; + vec3 x4 = x2 * x2; + return 0.021 * x + 4.0111 * x2 - 25.682 * x2 * x + 70.359 * x4 - 74.778 * x4 * x + 27.069 * x4 * x2; +} + +// AgX tonemap implementation based on EaryChow's algorithm used by Blender +// Source: https://github.com/EaryChow/AgX_LUT_Gen/blob/main/AgXBasesRGB.py +vec3 TonemapAgX(vec3 color) +{ + // Combined sRGB to Rec2020 + AgX inset transform + const mat3 srgbToRec2020AgxInsetMat = mat3( + 0.54490813676363087053, 0.14044005884001287035, 0.088827411851915368603, + 0.37377945959812267119, 0.75410959864013760045, 0.17887712465043811023, + 0.081384976686407536266, 0.10543358536857773485, 0.73224999956948382528 + ); + + // Combined inverse AgX outset + Rec2020 to sRGB transform + const mat3 agxOutsetRec2020ToSrgbMatrix = mat3( + 1.9645509602733325934, -0.29932243390911083839, -0.16436833806080403409, + -0.85585845117807513559, 1.3264510741502356555, -0.23822464068860595117, + -0.10886710826831608324, -0.027084020983874825605, 1.402665347143271889 + ); + + // EV range constants (LOG2_MIN = -10.0, LOG2_MAX = +6.5, MIDDLE_GRAY = 0.18) + const float minEV = -12.4739311883324; // log2(pow(2, LOG2_MIN) * MIDDLE_GRAY) + const float maxEV = 4.02606881166759; // log2(pow(2, LOG2_MAX) * MIDDLE_GRAY) + + // Prevent negative values to avoid darker/oversaturated results after matrix transform + // Small epsilon (2e-10) prevents log2(0.0) while maintaining minimal error + color = max(color, 2e-10); + + // Transform to Rec2020 and apply inset matrix + color = srgbToRec2020AgxInsetMat * color; + + // Log2 encoding and normalization to [0,1] range + color = clamp(log2(color), minEV, maxEV); + color = (color - minEV) / (maxEV - minEV); + + // Apply sigmoid contrast curve + color = AgXContrastApprox(color); + + // Convert back to linear (gamma 2.4) + color = pow(color, vec3(2.4)); + + // Apply outset matrix and return to sRGB + color = agxOutsetRec2020ToSrgbMatrix * color; + + // Return color (may contain negative components useful for further adjustments) + return color; +} + +/* === Main Functions === */ + +vec3 Tonemapping(vec3 color, float exposure, float pWhite) // inputs are LINEAR +{ + // Ensure color values passed to tonemappers are positive. + // They can be negative in the case of negative lights, which leads to undesired behavior. + + color *= exposure; + + switch (uTonemapMode) { + case TONEMAP_REINHARD: + color = TonemapReinhard(max(vec3(0.0), color), pWhite); + break; + case TONEMAP_FILMIC: + color = TonemapFilmic(max(vec3(0.0), color), pWhite); + break; + case TONEMAP_ACES: + color = TonemapACES(max(vec3(0.0), color), pWhite); + break; + case TONEMAP_AGX: + color = TonemapAgX(color); + break; + default: + break; + } + + return color; +} + +vec3 Adjustments(vec3 color, float brightness, float contrast, float saturation) +{ + color = mix(vec3(0.0), color, brightness); + color = mix(vec3(0.5), color, contrast); + color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, saturation); + + return color; +} + +vec3 Debanding(vec3 color) +{ + const float ditherStrength = 1.0 / 255.0; + float n = M_HashIGN(gl_FragCoord.xy); + float d = (n - 0.5) * ditherStrength; + return color + d; +} + +vec3 LinearToSRGB(vec3 color) +{ + // color = clamp(color, vec3(0.0), vec3(1.0)); + // const vec3 a = vec3(0.055f); + // return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); + // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html + return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0)); +} + +/* === Main program === */ + +void main() +{ + vec3 color = texelFetch(uSceneTex, ivec2(gl_FragCoord.xy), 0).rgb; + + color = Tonemapping(color, uTonemapExposure, uTonemapWhite); + color = Adjustments(color, uBrightness, uContrast, uSaturation); + color = LinearToSRGB(color); + + FragColor = vec4(Debanding(color), 1.0); +} + +``` + +`shaders/post/screen.frag`: + +```frag +/* screen.frag -- Base of custom screen fragment shader + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/frame.glsl" +#include "../include/blocks/view.glsl" + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uSceneTex; +uniform sampler2D uNormalTex; +uniform sampler2D uDepthTex; + +/* === Fragments === */ + +out vec4 FragColor; + +/* === Built-In Constants === */ + +#define CAMERA_POSITION uView.position +#define MATRIX_VIEW uView.view +#define MATRIX_INV_VIEW uView.invView +#define MATRIX_PROJECTION uView.proj +#define MATRIX_INV_PROJECTION uView.invProj +#define MATRIX_VIEW_PROJECTION uView.viewProj +#define PROJECTION_MODE uView.projMode +#define NEAR_PLANE uView.near +#define FAR_PLANE uView.far +#define RESOLUTION uFrame.screenSize +#define TEXEL_SIZE uFrame.texelSize +#define ASPECT uFrame.aspect + +/* === Built-In Input Variables === */ + +vec2 TEXCOORD = vec2(0.0); +ivec2 PIXCOORD = ivec2(0); +int FRAME_INDEX = 0; +float TIME = 0.0; + +/* === Built-In Output Variables === */ + +vec3 COLOR = vec3(0.0); + +/* === User Callable === */ + +vec3 FetchColor(ivec2 pixCoord) +{ + return texelFetch(uSceneTex, pixCoord, 0).rgb; +} + +vec3 SampleColor(vec2 texCoord) +{ + return textureLod(uSceneTex, texCoord, 0.0).rgb; +} + +float FetchDepth(ivec2 pixCoord) +{ + return texelFetch(uDepthTex, pixCoord, 0).r; +} + +float SampleDepth(vec2 texCoord) +{ + return textureLod(uDepthTex, texCoord, 0.0).r; +} + +float FetchDepth01(ivec2 pixCoord) +{ + float z = texelFetch(uDepthTex, pixCoord, 0).r; + return clamp((z - uView.near) / (uView.far - uView.near), 0.0, 1.0); +} + +float SampleDepth01(vec2 texCoord) +{ + float z = textureLod(uDepthTex, texCoord, 0.0).r; + return clamp((z - uView.near) / (uView.far - uView.near), 0.0, 1.0); +} + +vec3 FetchPosition(ivec2 pixCoord) +{ + return V_GetViewPosition(uDepthTex, pixCoord); +} + +vec3 SamplePosition(vec2 texCoord) +{ + return V_GetViewPosition(uDepthTex, texCoord); +} + +vec3 FetchNormal(ivec2 pixCoord) +{ + return V_GetViewNormal(uNormalTex, pixCoord); +} + +vec3 SampleNormal(vec2 texCoord) +{ + return V_GetViewNormal(uNormalTex, texCoord); +} + +/* === Main function === */ + +#define fragment() + +void main() +{ + TEXCOORD = vTexCoord; + PIXCOORD = ivec2(gl_FragCoord.xy); + FRAME_INDEX = uFrame.index; + TIME = uFrame.time; + + fragment(); + + FragColor = vec4(COLOR, 1.0); +} + +``` + +`shaders/post/smaa.frag`: + +```frag +/* smaa.frag -- Fragment shader of SMAA blend pass + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +#define SMAA_INCLUDE_VS 0 +#define SMAA_INCLUDE_PS 1 + +#include "../include/smaa.glsl" + +uniform sampler2D uSceneTex; +uniform sampler2D uBlendTex; + +noperspective in vec2 vTexCoord; +noperspective in vec4 vOffset; + +out vec4 FragColor; + +void main() +{ + FragColor = SMAANeighborhoodBlendingPS(vTexCoord, vOffset, uSceneTex, uBlendTex); +} + +``` + +`shaders/post/smaa.vert`: + +```vert +/* smaa.vert -- Vertex shader of SMAA blend pass + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +#define SMAA_INCLUDE_VS 1 +#define SMAA_INCLUDE_PS 0 + +#include "../include/smaa.glsl" + +const vec2 positions[3] = vec2[] +( + vec2(-1.0, -1.0), + vec2( 3.0, -1.0), + vec2(-1.0, 3.0) +); + +noperspective out vec2 vTexCoord; +noperspective out vec4 vOffset; + +void main() +{ + gl_Position = vec4(positions[gl_VertexID], 1.0, 1.0); + vTexCoord = (gl_Position.xy * 0.5) + 0.5; + + SMAANeighborhoodBlendingVS(vTexCoord, vOffset); +} + +``` + +`shaders/post/visualizer.frag`: + +```frag +/* visualizer.frag -- Buffer visualizer fragment shader + * + * Copyright (c) 2025 Victor Le Juez + * + * This software is distributed under the terms of the accompanying LICENSE file. + * It is provided "as-is", without any express or implied warranty. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/math.glsl" + +/* === Constants === */ + +#define OUTPUT_ALBEDO 1 +#define OUTPUT_NORMAL 2 +#define OUTPUT_ORM 3 +#define OUTPUT_DIFFUSE 4 +#define OUTPUT_SPECULAR 5 +#define OUTPUT_SSAO 6 +#define OUTPUT_SSIL 7 +#define OUTPUT_SSGI 8 +#define OUTPUT_SSR 9 +#define OUTPUT_BLOOM 10 +#define OUTPUT_DOF 11 + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uSourceTex; +uniform int uOutputMode; + +/* === Fragments === */ + +out vec4 FragColor; + +/* === Main Program === */ + +void main() +{ + FragColor = texture(uSourceTex, vTexCoord); + + switch (uOutputMode) { + case OUTPUT_ALBEDO: + FragColor = vec4(FragColor.rgb, 1.0); + break; + case OUTPUT_NORMAL: + FragColor = vec4(0.5 * M_DecodeOctahedral(FragColor.rg) + 0.5, 1.0); + break; + case OUTPUT_ORM: + FragColor = vec4(FragColor.rgb, 1.0); + break; + case OUTPUT_DIFFUSE: + FragColor = vec4(FragColor.rgb, 1.0); + break; + case OUTPUT_SPECULAR: + FragColor = vec4(FragColor.rgb, 1.0); + break; + case OUTPUT_SSAO: + FragColor = vec4(FragColor.xxx, 1.0); + break; + case OUTPUT_SSIL: + FragColor = vec4(mix(FragColor.rgb, vec3(FragColor.a), 0.2), 1.0); + break; + case OUTPUT_SSGI: + FragColor = vec4(FragColor.rgb, 1.0); + break; + case OUTPUT_SSR: + FragColor = vec4(FragColor.rgb * FragColor.a, 1.0); + break; + case OUTPUT_BLOOM: + FragColor = vec4(FragColor.rgb, 1.0); + break; + case OUTPUT_DOF: + float front = clamp(-FragColor.r, 0.0, 1.0); // in front of focus plane (near) + float back = clamp(FragColor.r, 0.0, 1.0); // behind the focus plane (far) + vec3 tint = vec3(0.0, front, back); // green front, blue back, black at focus + FragColor = vec4(tint, 1.0); + break; + default: + break; + } +} + +``` + +`shaders/prepare/bloom_down.frag`: + +```frag +/* bloom_down.frag -- Custom 36-tap bilinear downsampling shader for bloom generation + * + * Original implementation by Jorge Jiménez, presented at SIGGRAPH 2014 + * (used in Call of Duty: Advanced Warfare) + * + * Copyright (c) 2014 Jorge Jiménez + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +// This shader performs downsampling on a texture, +// as taken from Call Of Duty method, presented at ACM Siggraph 2014. +// This particular method was customly designed to eliminate +// "pulsating artifacts and temporal stability issues". + +#version 330 core + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uTexture; +uniform vec2 uTexelSize; //< Reciprocal of the resolution of the source being sampled +uniform vec4 uPrefilter; +uniform int uDstLevel; //< Which mip we are writing to, used for Karis average + +/* === Fragments === */ + +layout (location = 0) out vec3 FragColor; + +/* === Helper Functions === */ + +vec3 LinearToSRGB(vec3 color) +{ + // color = clamp(color, vec3(0.0), vec3(1.0)); + // const vec3 a = vec3(0.055f); + // return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); + // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html + return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0)); +} + +float sRGBToLuma(vec3 col) +{ + //return dot(col, vec3(0.2126, 0.7152, 0.0722)); + return dot(col, vec3(0.299, 0.587, 0.114)); +} + +float KarisAverage(vec3 col) +{ + // Formula is 1 / (1 + luma) + float luma = sRGBToLuma(LinearToSRGB(col)) * 0.25f; + return 1.0f / (1.0f + luma); +} + +vec3 Prefilter (vec3 col) +{ + float brightness = max(col.r, max(col.g, col.b)); + float soft = brightness - uPrefilter.y; + soft = clamp(soft, 0, uPrefilter.z); + soft = soft * soft * uPrefilter.w; + float contribution = max(soft, brightness - uPrefilter.x); + contribution /= max(brightness, 0.00001); + return col * contribution; +} + +/* === Main Function === */ + +void main() +{ + // NOTE: This is the readable version of this shader. It will be optimized! + + float srcLevel = float(max(uDstLevel - 1, 0)); + float x = uTexelSize.x; + float y = uTexelSize.y; + + // Take 13 samples around current texel: + // a - b - c + // - j - k - + // d - e - f + // - l - m - + // g - h - i + // === ('e' is the current texel) === + vec3 a = textureLod(uTexture, vec2(vTexCoord.x - 2*x, vTexCoord.y + 2*y), srcLevel).rgb; + vec3 b = textureLod(uTexture, vec2(vTexCoord.x, vTexCoord.y + 2*y), srcLevel).rgb; + vec3 c = textureLod(uTexture, vec2(vTexCoord.x + 2*x, vTexCoord.y + 2*y), srcLevel).rgb; + + vec3 d = textureLod(uTexture, vec2(vTexCoord.x - 2*x, vTexCoord.y), srcLevel).rgb; + vec3 e = textureLod(uTexture, vec2(vTexCoord.x, vTexCoord.y), srcLevel).rgb; + vec3 f = textureLod(uTexture, vec2(vTexCoord.x + 2*x, vTexCoord.y), srcLevel).rgb; + + vec3 g = textureLod(uTexture, vec2(vTexCoord.x - 2*x, vTexCoord.y - 2*y), srcLevel).rgb; + vec3 h = textureLod(uTexture, vec2(vTexCoord.x, vTexCoord.y - 2*y), srcLevel).rgb; + vec3 i = textureLod(uTexture, vec2(vTexCoord.x + 2*x, vTexCoord.y - 2*y), srcLevel).rgb; + + vec3 j = textureLod(uTexture, vec2(vTexCoord.x - x, vTexCoord.y + y), srcLevel).rgb; + vec3 k = textureLod(uTexture, vec2(vTexCoord.x + x, vTexCoord.y + y), srcLevel).rgb; + vec3 l = textureLod(uTexture, vec2(vTexCoord.x - x, vTexCoord.y - y), srcLevel).rgb; + vec3 m = textureLod(uTexture, vec2(vTexCoord.x + x, vTexCoord.y - y), srcLevel).rgb; + + // Apply weighted distribution: + // 0.5 + 0.125 + 0.125 + 0.125 + 0.125 = 1 + // a,b,d,e * 0.125 + // b,c,e,f * 0.125 + // d,e,g,h * 0.125 + // e,f,h,i * 0.125 + // j,k,l,m * 0.5 + // This shows 5 square areas that are being sampled. But some of them overlap, + // so to have an energy preserving downsample we need to make some adjustments. + // The weights are the distributed, so that the sum of j,k,l,m (e.g.) + // contribute 0.5 to the final color output. The code below is written + // to effectively yield this sum. We get: + // 0.125*5 + 0.03125*4 + 0.0625*4 = 1 + + // Check if we need to perform Karis average on each block of 4 samples + vec3 groups[5]; + if (uDstLevel == 0) + { + // We are writing to mip 0, so we need to apply Karis average to each block + // of 4 samples to prevent fireflies (very bright subpixels, leads to pulsating artifacts). + groups[0] = (a+b+d+e) * (0.125/4.0); + groups[1] = (b+c+e+f) * (0.125/4.0); + groups[2] = (d+e+g+h) * (0.125/4.0); + groups[3] = (e+f+h+i) * (0.125/4.0); + groups[4] = (j+k+l+m) * (0.5/4.0); + groups[0] *= KarisAverage(groups[0]); + groups[1] *= KarisAverage(groups[1]); + groups[2] *= KarisAverage(groups[2]); + groups[3] *= KarisAverage(groups[3]); + groups[4] *= KarisAverage(groups[4]); + FragColor = groups[0]+groups[1]+groups[2]+groups[3]+groups[4]; + FragColor = max(FragColor, 0.0001); + FragColor = Prefilter(FragColor); + } + else + { + FragColor = e*0.125; // ok + FragColor += (a+c+g+i)*0.03125; // ok + FragColor += (b+d+f+h)*0.0625; // ok + FragColor += (j+k+l+m)*0.125; // ok + } +} + +``` + +`shaders/prepare/bloom_up.frag`: + +```frag +/* bloom_up.frag -- Custom upsampling fragment shader used for bloom generation + * + * Original implementation by Jorge Jiménez, presented at SIGGRAPH 2014 + * (used in Call of Duty: Advanced Warfare) + * + * Copyright (c) 2014 Jorge Jiménez + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +// This shader performs upsampling on a texture, +// as taken from Call Of Duty method, presented at ACM Siggraph 2014. + +#version 330 core + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uTexture; +uniform vec2 uFilterRadius; +uniform float uSrcLevel; //< Which mip we are sampling from + +layout (location = 0) out vec3 FragColor; + +/* === Main Function === */ + +void main() +{ + // The filter kernel is applied with a radius, specified in texture + // coordinates, so that the radius will vary across mip resolutions. + float x = uFilterRadius.x; + float y = uFilterRadius.y; + + // Take 9 samples around current texel: + // a - b - c + // d - e - f + // g - h - i + // === ('e' is the current texel) === + vec3 a = textureLod(uTexture, vec2(vTexCoord.x - x, vTexCoord.y + y), uSrcLevel).rgb; + vec3 b = textureLod(uTexture, vec2(vTexCoord.x, vTexCoord.y + y), uSrcLevel).rgb; + vec3 c = textureLod(uTexture, vec2(vTexCoord.x + x, vTexCoord.y + y), uSrcLevel).rgb; + + vec3 d = textureLod(uTexture, vec2(vTexCoord.x - x, vTexCoord.y), uSrcLevel).rgb; + vec3 e = textureLod(uTexture, vec2(vTexCoord.x, vTexCoord.y), uSrcLevel).rgb; + vec3 f = textureLod(uTexture, vec2(vTexCoord.x + x, vTexCoord.y), uSrcLevel).rgb; + + vec3 g = textureLod(uTexture, vec2(vTexCoord.x - x, vTexCoord.y - y), uSrcLevel).rgb; + vec3 h = textureLod(uTexture, vec2(vTexCoord.x, vTexCoord.y - y), uSrcLevel).rgb; + vec3 i = textureLod(uTexture, vec2(vTexCoord.x + x, vTexCoord.y - y), uSrcLevel).rgb; + + // Apply weighted distribution, by using a 3x3 tent filter: + // 1 | 1 2 1 | + // -- * | 2 4 2 | + // 16 | 1 2 1 | + FragColor = e*4.0; + FragColor += (b+d+f+h)*2.0; + FragColor += (a+c+g+i); + FragColor *= 1.0 / 16.0; +} + +``` + +`shaders/prepare/blur_down.frag`: + +```frag +/* blur_down.frag - Downsampling part of ARM dual filtering + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +// Adapted from the ARM dual filtering method. +// See: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf + +#version 330 core + +noperspective in vec2 vTexCoord; +uniform sampler2D uSourceTex; +uniform int uSourceLod; +out vec4 FragColor; + +void main() +{ + vec2 halfPixel = 0.5 / vec2(textureSize(uSourceTex, uSourceLod)); + float lod = float(uSourceLod); + + vec4 sum = textureLod(uSourceTex, vTexCoord, lod) * 4.0; + sum += textureLod(uSourceTex, vTexCoord - halfPixel.xy, lod); + sum += textureLod(uSourceTex, vTexCoord + halfPixel.xy, lod); + sum += textureLod(uSourceTex, vTexCoord + vec2(halfPixel.x, -halfPixel.y), lod); + sum += textureLod(uSourceTex, vTexCoord - vec2(halfPixel.x, -halfPixel.y), lod); + + FragColor = sum / 8.0; +} + +``` + +`shaders/prepare/blur_up.frag`: + +```frag +/* blur_up.frag - Upsampling part of ARM dual filtering + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +// Adapted from the ARM dual filtering method. +// See: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf + +#version 330 core + +noperspective in vec2 vTexCoord; +uniform sampler2D uSourceTex; +uniform int uSourceLod; +out vec4 FragColor; + +void main() +{ + vec2 halfPixel = 0.5 / vec2(textureSize(uSourceTex, uSourceLod)); + float lod = float(uSourceLod); + + vec4 sum = textureLod(uSourceTex, vTexCoord + vec2(-halfPixel.x * 2.0, 0.0), lod); + sum += textureLod(uSourceTex, vTexCoord + vec2(-halfPixel.x, halfPixel.y), lod) * 2.0; + sum += textureLod(uSourceTex, vTexCoord + vec2(0.0, halfPixel.y * 2.0), lod); + sum += textureLod(uSourceTex, vTexCoord + vec2(halfPixel.x, halfPixel.y), lod) * 2.0; + sum += textureLod(uSourceTex, vTexCoord + vec2(halfPixel.x * 2.0, 0.0), lod); + sum += textureLod(uSourceTex, vTexCoord + vec2(halfPixel.x, -halfPixel.y), lod) * 2.0; + sum += textureLod(uSourceTex, vTexCoord + vec2(0.0, -halfPixel.y * 2.0), lod); + sum += textureLod(uSourceTex, vTexCoord + vec2(-halfPixel.x, -halfPixel.y), lod) * 2.0; + + FragColor = sum / 12.0; +} + +``` + +`shaders/prepare/cubemap_custom_sky.frag`: + +```frag +/* cubemap_custom_sky.frag -- Base of custom custom skybox fragment shader + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/frame.glsl" +#include "../include/blocks/view.glsl" + +/* === Varyings === */ + +in vec3 vPosition; +in vec2 vTexCoord; + +/* === Fragments === */ + +out vec4 FragColor; + +/* === Built-In Input Variables === */ + +vec3 POSITION = vec3(0.0); +vec2 TEXCOORD = vec2(0.0); +vec3 EYEDIR = vec3(0.0); +int FRAME_INDEX = 0; +float TIME = 0.0; + +/* === Built-In Output Variables === */ + +vec3 COLOR = vec3(0.0); + +/* === Main function === */ + +vec2 GetSphericalCoord(vec3 v) +{ + vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); + uv *= vec2(0.1591, -0.3183); // negative Y, to flip axis + uv += 0.5; + return uv; +} + +/* === Main function === */ + +#define fragment() + +void main() +{ + POSITION = vPosition; + TEXCOORD = vTexCoord; + EYEDIR = normalize(vPosition); + FRAME_INDEX = uFrame.index; + TIME = uFrame.time; + + fragment(); + + FragColor = vec4(COLOR, 1.0); +} + +``` + +`shaders/prepare/cubemap_from_equirectangular.frag`: + +```frag +/* cubemap_from_equirectangular.frag -- Fragment shader for converting equirectangular panorama to cubemap + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +in vec3 vPosition; + +uniform sampler2D uPanoramaTex; + +out vec4 FragColor; + +vec2 SampleSphericalMap(vec3 v) +{ + vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); + uv *= vec2(0.1591, -0.3183); // negative Y, to flip axis + uv += 0.5; + return uv; +} + +void main() +{ + vec2 uv = SampleSphericalMap(normalize(vPosition)); + vec3 color = texture(uPanoramaTex, uv).rgb; + FragColor = vec4(color, 1.0); +} + +``` + +`shaders/prepare/cubemap_irradiance.frag`: + +```frag +/* cubemap_irradiance.frag -- Irradiance cubemap generation fragment shader + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Inlcudes === */ + +#include "../include/math.glsl" + +/* === Varyings === */ + +in vec3 vPosition; + +/* === Uniforms === */ + +uniform samplerCube uSourceTex; + +/* === Fragments === */ + +out vec4 FragColor; + +/* === Program === */ + +void main() +{ + vec3 N = normalize(vPosition); + mat3 OBN = M_OrthonormalBasis(N); + + vec3 irradiance = vec3(0.0); + float sampleDelta = 0.025; + float nrSamples = 0.0; + + for (float phi = 0.0; phi < 2.0 * M_PI; phi += sampleDelta) { + for (float theta = 0.0; theta < 0.5 * M_PI; theta += sampleDelta) { + vec3 sampleVec = OBN * vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); + irradiance += texture(uSourceTex, sampleVec).rgb * cos(theta) * sin(theta); + nrSamples++; + } + } + + irradiance = M_PI * irradiance * (1.0 / float(nrSamples)); + FragColor = vec4(irradiance, 1.0); +} + +``` + +`shaders/prepare/cubemap_prefilter.frag`: + +```frag +/* cubemap_prefilter.frag -- Prefiltered cubemap generation fragment shader + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/math.glsl" + +/* === Constants === */ + +#define EPSILON 1e-6 +#define SAMPLE_COUNT 64u + +/* === Varyings === */ + +in vec3 vPosition; + +/* === Uniforms === */ + +uniform samplerCube uSourceTex; //< Source cubemap +uniform float uSourceNumLevels; //< Level count of the source cubemap +uniform float uSourceFaceSize; //< Resolution of the source cubemap +uniform float uRoughness; //< Roughness (relative to mip level) + +/* === Fragments === */ + +out vec4 FragColor; + +/* === Helper Functions === */ + +float DistributionGGX(vec3 N, vec3 H, float a2) +{ + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH * NdotH; + + float nom = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = M_PI * denom * denom; + + return nom / max(denom, EPSILON); +} + +float RadicalInverse_VdC(uint bits) +{ + // Efficient VanDerCorpus calculation + // See: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} + +vec2 Hammersley(uint i, uint N) +{ + return vec2(float(i) / float(N), RadicalInverse_VdC(i)); +} + +vec3 ImportanceSampleGGX(vec2 Xi, float a, mat3 OBN) +{ + float phi = M_TAU * Xi.x; + float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y)); + float sinTheta = sqrt(max(1.0 - cosTheta * cosTheta, 0.0)); + + // Spherical coordinates -> Cartesian (halfway vector) + vec3 H; + H.x = cos(phi) * sinTheta; + H.y = sin(phi) * sinTheta; + H.z = cosTheta; + + // Transformation to world space + return normalize(OBN * H); +} + +float ComputeMipLevel(float pdf, float cubeResolution) +{ + float saTexel = 4.0 * M_PI / (6.0 * cubeResolution * cubeResolution); + float saSample = 1.0 / (float(SAMPLE_COUNT) * max(pdf, EPSILON)); + return max(0.0, 0.5 * log2(saSample / saTexel)); +} + +/* === Program === */ + +void main() +{ + /* --- Get the world space direction for this texel --- */ + + vec3 N = normalize(vPosition); + vec3 V = N; + + /* --- Handle the case where roughness = 0 (perfect mirror) --- */ + + if (uRoughness <= EPSILON) { + FragColor = textureLod(uSourceTex, N, 0.0); + return; + } + + /* --- Pre calculate invariants --- */ + + float a = uRoughness * uRoughness; + float a2 = a * a; + + mat3 OBN = M_OrthonormalBasis(N); + + /* --- Convolve environment map --- */ + + vec3 prefilteredColor = vec3(0.0); + float totalWeight = 0.0; + + for (uint i = 0u; i < SAMPLE_COUNT; ++i) + { + vec2 Xi = Hammersley(i, SAMPLE_COUNT); + vec3 H = ImportanceSampleGGX(Xi, a, OBN); + vec3 L = normalize(reflect(-V, H)); + + float NdotL = max(dot(N, L), 0.0); + + if (NdotL > EPSILON) + { + float D = DistributionGGX(N, H, a2); + float NdotH = max(dot(N, H), 0.0); + float HdotV = max(dot(H, V), 0.0); + + float pdf = (D * NdotH) / max(4.0 * HdotV, EPSILON); + float mipLevel = ComputeMipLevel(pdf, uSourceFaceSize); + mipLevel = clamp(mipLevel, 0.0, uSourceNumLevels - 1.0); + + vec3 sampleColor = textureLod(uSourceTex, L, mipLevel).rgb; + prefilteredColor += sampleColor * NdotL; + totalWeight += NdotL; + } + } + + /* --- Normalize and store --- */ + + prefilteredColor = prefilteredColor / max(totalWeight, EPSILON); + FragColor = vec4(prefilteredColor, 1.0); +} + +``` + +`shaders/prepare/cubemap_procedural_sky.frag`: + +```frag +/* cubemap_procedural_sky.frag -- Procedural sky generation fragment shader + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/math.glsl" + +/* === Varyings === */ + +in vec3 vPosition; + +/* === Uniforms === */ + +uniform vec3 uSkyTopColor; +uniform vec3 uSkyHorizonColor; +uniform float uSkyHorizonCurve; +uniform float uSkyEnergy; + +uniform vec3 uGroundBottomColor; +uniform vec3 uGroundHorizonColor; +uniform float uGroundHorizonCurve; +uniform float uGroundEnergy; + +uniform vec3 uSunDirection; +uniform vec3 uSunColor; +uniform float uSunSize; +uniform float uSunCurve; +uniform float uSunEnergy; + +/* === Fragments === */ + +out vec4 FragColor; + +/* === Program === */ + +void main() +{ + /* --- Normalization of ray direction --- */ + + vec3 eyeDir = normalize(vPosition); + vec3 sunDir = normalize(uSunDirection); + + /* --- Vertical angle calculation --- */ + + float verticalAngle = acos(clamp(eyeDir.y, -1.0, 1.0)); + + /* --- Sky gradient (above the horizon) --- */ + + vec3 color; + + if (eyeDir.y >= 0.0) { + float c = (1.0 - verticalAngle / (M_PI * 0.5)); + float skyGradient = clamp(1.0 - pow(1.0 - c, 1.0 / max(uSkyHorizonCurve, 0.001)), 0.0, 1.0); + color = mix(uSkyHorizonColor, uSkyTopColor, skyGradient) * uSkyEnergy; + } + else { + float c = (verticalAngle - (M_PI * 0.5)) / (M_PI * 0.5); + float groundGradient = clamp(1.0 - pow(1.0 - c, 1.0 / max(uGroundHorizonCurve, 0.001)), 0.0, 1.0); + color = mix(uGroundHorizonColor, uGroundBottomColor, groundGradient) * uGroundEnergy; + } + + /* --- Sun contribution --- */ + + float sunAngle = acos(dot(sunDir, eyeDir)); + + if (sunAngle < uSunSize) { + color = uSunColor * uSunEnergy; + } + else if (sunAngle < uSunSize * 10.0) { + float c2 = (sunAngle - uSunSize) / (uSunSize * 10.0 - uSunSize); + float sunFade = clamp(1.0 - pow(1.0 - c2, 1.0 / max(uSunCurve, 0.001)), 0.0, 1.0); + color = mix(uSunColor * uSunEnergy, color, sunFade); + } + + /* --- Output --- */ + + FragColor = vec4(color, 1.0); +} + +``` + +`shaders/prepare/denoiser_atrous.frag`: + +```frag +/* denoiser_trous.frag -- À-Trous denoiser + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/view.glsl" +#include "../include/math.glsl" + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uSourceTex; +uniform sampler2D uNormalTex; +uniform sampler2D uDepthTex; + +uniform float uNormalSharpness; +uniform float uDepthSharpness; +uniform float uInvStepWidth2; // 1.0 / (uStepWidth*uStepWidth) +uniform int uStepWidth; // Powers of 2: 1, 2, 4, 8... for each pass + +/* === Fragments === */ + +out vec4 FragColor; + +/* === Kernel === */ + +const int KERNEL_SIZE = 8; + +const ivec2 OFFSETS[8] = ivec2[8]( + ivec2(-1,-1), ivec2(0,-1), ivec2(1,-1), + ivec2(-1, 0), ivec2(1, 0), + ivec2(-1, 1), ivec2(0, 1), ivec2(1, 1) +); + +const float WEIGHTS[8] = float[8]( + 0.0625, 0.125, 0.0625, + 0.125, 0.125, + 0.0625, 0.125, 0.0625 +); + +/* === Helpers === */ + +#define OFFSCREEN(sc, res) (any(lessThan(sc, ivec2(0))) || any(greaterThanEqual(sc, res))) + +/* === Main === */ + +void main() +{ + ivec2 resolution = textureSize(uSourceTex, 0); + ivec2 pixCoord = ivec2(gl_FragCoord.xy); + + vec3 cp = V_GetViewPosition(uDepthTex, pixCoord); + vec3 cn = V_GetViewNormal(uNormalTex, pixCoord); + vec4 cc = texelFetch(uSourceTex, pixCoord, 0); + + vec4 result = cc * 0.25; + float weightSum = 0.25; + + for (int i = 0; i < KERNEL_SIZE; i++) + { + ivec2 sampleCoord = pixCoord + OFFSETS[i] * uStepWidth; + if (OFFSCREEN(sampleCoord, resolution)) continue; + + vec3 sp = V_GetViewPosition(uDepthTex, sampleCoord); + vec3 sn = V_GetViewNormal(uNormalTex, sampleCoord); + vec4 sc = texelFetch(uSourceTex, sampleCoord, 0); + + float planeDist = dot(sp - cp, cn); + float nDiff = dot(cn - sn, cn - sn) * uInvStepWidth2; + + float w = WEIGHTS[i] * exp( + -nDiff * uNormalSharpness + -planeDist * planeDist * uDepthSharpness + ); + + result += sc * w; + weightSum += w; + } + + FragColor = result / max(weightSum, 1e-4); +} + +``` + +`shaders/prepare/denoiser_sparse.frag`: + +```frag +/* denoiser_sparse.frag -- Sparse denoiser + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/view.glsl" +#include "../include/math.glsl" + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uSourceTex; +uniform sampler2D uNormalTex; +uniform sampler2D uDepthTex; + +uniform float uNormalSharpness; +uniform float uDepthSharpness; +uniform float uInvBlurRadius2; // 1.0 / (uBlurRadius*uBlurRadius) +uniform float uBlurRadius; + +/* === Fragments === */ + +out vec4 FragColor; + +/* === Kernel === */ + +const int KERNEL_SIZE = 8; + +const vec2 DISK[8] = vec2[8]( + vec2( 0.8376, 0.1478), + vec2( 0.3502, 0.2715), + vec2(-0.1253, 0.8960), + vec2(-0.3420, 0.2562), + vec2(-0.8573, -0.1832), + vec2(-0.3472, -0.3570), + vec2( 0.1599, -0.9071), + vec2( 0.3951, -0.2541) +); + +/* === Helpers === */ + +#define OFFSCREEN(sc, res) (any(lessThan(sc, ivec2(0))) || any(greaterThanEqual(sc, res))) + +/* === Main === */ + +void main() +{ + ivec2 resolution = textureSize(uSourceTex, 0); + ivec2 pixCoord = ivec2(gl_FragCoord.xy); + vec2 invRes = 1.0 / vec2(resolution); + + vec3 cp = V_GetViewPosition(uDepthTex, pixCoord); + vec3 cn = V_GetViewNormal(uNormalTex, pixCoord); + vec4 cc = texelFetch(uSourceTex, pixCoord, 0); + + vec4 result = cc; + float weightSum = 1.0; + + for (int i = 0; i < KERNEL_SIZE; i++) + { + vec2 offset = DISK[i] * uBlurRadius; + ivec2 iSampleCoord = ivec2(gl_FragCoord.xy + offset); + + if (OFFSCREEN(iSampleCoord, resolution)) continue; + vec2 fSampleCoord = (gl_FragCoord.xy + offset) * invRes; + + vec3 sp = V_GetViewPosition(uDepthTex, iSampleCoord); + vec3 sn = V_GetViewNormal(uNormalTex, iSampleCoord); + vec4 sc = textureLod(uSourceTex, fSampleCoord, 0); + + float planeDist = dot(sp - cp, cn); + float normalDiff = 1.0 - dot(cn, sn); + + float w = exp( + -dot(offset, offset) * uInvBlurRadius2 + -planeDist * planeDist * uDepthSharpness + -normalDiff * normalDiff * uNormalSharpness + ); + + result += sc * w; + weightSum += w; + } + + FragColor = result / weightSum; +} + +``` + +`shaders/prepare/depth_pyramid.frag`: + +```frag +/* depth_pyramid.frag - Depth buffer downsampling shader + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +uniform sampler2D uDepthTex; + +layout(location = 0) out float FragDepth; +layout(location = 1) out uint FragSelect; + +void main() +{ + ivec2 pixCoord = 2 * ivec2(gl_FragCoord.xy); + + ivec2 p0 = pixCoord + ivec2(0, 0); + ivec2 p1 = pixCoord + ivec2(1, 0); + ivec2 p2 = pixCoord + ivec2(0, 1); + ivec2 p3 = pixCoord + ivec2(1, 1); + + float d0 = texelFetch(uDepthTex, p0, 0).r; + float d1 = texelFetch(uDepthTex, p1, 0).r; + float d2 = texelFetch(uDepthTex, p2, 0).r; + float d3 = texelFetch(uDepthTex, p3, 0).r; + + FragDepth = d0, FragSelect = 0u; + + if (d1 < FragDepth) { FragDepth = d1; FragSelect = 1u; } + if (d2 < FragDepth) { FragDepth = d2; FragSelect = 2u; } + if (d3 < FragDepth) { FragDepth = d3; FragSelect = 3u; } +} + +``` + +`shaders/prepare/dof_blur.frag`: + +```frag +/* dof_blur.frag -- DoF in single pass + * + * Depth of Field done in a single pass. + * Originally written by Dennis Gustafsson. + * + * Copyright (c) 2025 Victor Le Juez, Jens Roth + * + * This software is distributed under the terms of the accompanying LICENSE file. + * It is provided "as-is", without any express or implied warranty. + */ + +// reference impl. from SmashHit iOS game dev blog +// see: https://blog.voxagon.se/2018/05/04/bokeh-depth-of-field-in-single-pass.html + +#version 330 core + +#include "../include/math.glsl" + +noperspective in vec2 vTexCoord; + +uniform sampler2D uSceneTex; //< RGB: Color | A: CoC +uniform sampler2D uDepthTex; +uniform float uMaxBlurSize; + +const float RAD_SCALE = 0.5; + +out vec4 FragColor; + +void main() +{ + vec2 texelSize = 1.0 / vec2(textureSize(uSceneTex, 0)); + + vec4 centerScene = texture(uSceneTex, vTexCoord); + float centerDepth = texture(uDepthTex, vTexCoord).r; + float centerSize = abs(centerScene.a) * uMaxBlurSize; + + vec3 color = centerScene.rgb; + float radius = RAD_SCALE; + float tot = 1.0; + + for (float ang = 0.0; radius < uMaxBlurSize; ang += M_GOLDEN_ANGLE) + { + vec2 uv = vTexCoord + vec2(cos(ang), sin(ang)) * texelSize * radius; + + vec4 sampleScene = texture(uSceneTex, uv); + float sampleDepth = texture(uDepthTex, uv).r; + float sampleSize = abs(sampleScene.a) * uMaxBlurSize; + + if (sampleDepth > centerDepth) { + sampleSize = clamp(sampleSize, 0.0, centerSize * 2.0); + } + + float w = smoothstep(radius - 0.5, radius + 0.5, sampleSize); + color += mix(color / tot, sampleScene.rgb, w); + + radius += RAD_SCALE / radius; + tot += 1.0; + } + + // Compute a Gaussian-like weight from the central CoC to produce a blur probability for this pixel. + // After extensive testing, this gives the most physically plausible blending while remaining simple. + + float w = exp(-0.5 * centerSize * centerSize); + FragColor = vec4(color / tot, clamp(w, 0.0, 1.0)); +} + +``` + +`shaders/prepare/dof_coc.frag`: + +```frag +/* dof_coc.frag -- Circle of Confusion calculation shader + * + * Copyright (c) 2025 Victor Le Juez + * + * This software is distributed under the terms of the accompanying LICENSE file. + * It is provided "as-is", without any express or implied warranty. + */ + +#version 330 core + +noperspective in vec2 vTexCoord; + +uniform sampler2D uDepthTex; +uniform float uFocusPoint; +uniform float uFocusScale; +uniform float uNearScale; + +out float FragCoC; + +void main() +{ + float depth = texelFetch(uDepthTex, ivec2(gl_FragCoord.xy), 0).r; + float coc = (1.0 / uFocusPoint - 1.0 / depth) * uFocusScale; + coc *= mix(uNearScale, 1.0, step(0.0, coc)); + FragCoC = clamp(coc, -1.0, 1.0); +} + +``` + +`shaders/prepare/dof_down.frag`: + +```frag +/* dof_down.frag -- Input buffers downsampling for DoF + * + * Copyright (c) 2025 Victor Le Juez + * + * This software is distributed under the terms of the accompanying LICENSE file. + * It is provided "as-is", without any express or implied warranty. + */ + +#version 330 core + +uniform sampler2D uSceneTex; +uniform sampler2D uCoCTex; + +out vec4 FragCoC; + +void main() +{ + ivec2 pixCoord = 2 * ivec2(gl_FragCoord.xy); + + vec4 color0 = texelFetch(uSceneTex, pixCoord + ivec2(0, 0), 0); + vec4 color1 = texelFetch(uSceneTex, pixCoord + ivec2(1, 0), 0); + vec4 color2 = texelFetch(uSceneTex, pixCoord + ivec2(0, 1), 0); + vec4 color3 = texelFetch(uSceneTex, pixCoord + ivec2(1, 1), 0); + + float coc0 = texelFetch(uCoCTex, pixCoord + ivec2(0, 0), 0).r; + float coc1 = texelFetch(uCoCTex, pixCoord + ivec2(1, 0), 0).r; + float coc2 = texelFetch(uCoCTex, pixCoord + ivec2(0, 1), 0).r; + float coc3 = texelFetch(uCoCTex, pixCoord + ivec2(1, 1), 0).r; + + vec4 color = (color0 + color1 + color2 + color3) * 0.25; + float coc = min(min(min(coc0, coc1), coc2), coc3); + + FragCoC = vec4(color.rgb, coc); +} + +``` + +`shaders/prepare/exposure_adapt.frag`: + +```frag +#version 330 core + +uniform sampler2D uMeasuredLogLumTex; +uniform sampler2D uPrevAutoExposureTex; + +uniform float uDeltaTime; +uniform float uMinLogLum; +uniform float uMaxLogLum; +uniform float uSpeedUp; +uniform float uSpeedDown; +uniform float uExposureCompLog; + +out vec4 FragColor; + +void main() +{ + const float LOG_MIDDLE_GRAY = -1.7147984280919266; // log(0.18) + + float measuredLogLum = texelFetch(uMeasuredLogLumTex, ivec2(0, 0), 0).r; + float prevLogLum = texelFetch(uPrevAutoExposureTex, ivec2(0, 0), 0).g; + + measuredLogLum = clamp(measuredLogLum, uMinLogLum, uMaxLogLum); + + float speed = measuredLogLum > prevLogLum ? uSpeedUp : uSpeedDown; + float blend = 1.0 - exp(-uDeltaTime * speed); + + float adaptedLogLum = mix(prevLogLum, measuredLogLum, blend); + float exposure = exp(LOG_MIDDLE_GRAY - adaptedLogLum + uExposureCompLog); + + FragColor = vec4(exposure, adaptedLogLum, 0.0, 1.0); +} + +``` + +`shaders/prepare/luminance.frag`: + +```frag +#version 330 core + +uniform sampler2D uSourceTex; + +out float FragColor; + +void main() +{ + const vec3 LUMA = vec3(0.2126, 0.7152, 0.0722); + const float EPSILON = 1e-4; + + ivec2 srcSize = textureSize(uSourceTex, 0); + ivec2 base = 2 * ivec2(gl_FragCoord.xy); + + ivec2 p0 = min(base + ivec2(0, 0), srcSize - 1); + ivec2 p1 = min(base + ivec2(1, 0), srcSize - 1); + ivec2 p2 = min(base + ivec2(0, 1), srcSize - 1); + ivec2 p3 = min(base + ivec2(1, 1), srcSize - 1); + + vec3 c0 = texelFetch(uSourceTex, p0, 0).rgb; + vec3 c1 = texelFetch(uSourceTex, p1, 0).rgb; + vec3 c2 = texelFetch(uSourceTex, p2, 0).rgb; + vec3 c3 = texelFetch(uSourceTex, p3, 0).rgb; + + float l0 = max(dot(c0, LUMA), EPSILON); + float l1 = max(dot(c1, LUMA), EPSILON); + float l2 = max(dot(c2, LUMA), EPSILON); + float l3 = max(dot(c3, LUMA), EPSILON); + + FragColor = 0.25 * (log(l0) + log(l1) + log(l2) + log(l3)); +} + +``` + +`shaders/prepare/smaa_blending_weigths.frag`: + +```frag +/* smaa_edge_detection.frag -- SMAA blending weigths calculation pass + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +#define SMAA_INCLUDE_VS 0 +#define SMAA_INCLUDE_PS 1 + +#include "../include/smaa.glsl" + +uniform sampler2D uEdgesTex; +uniform sampler2D uAreaTex; +uniform sampler2D uSearchTex; + +noperspective in vec2 vTexCoord; +noperspective in vec2 vPixCoord; +noperspective in vec4 vOffset[3]; + +out vec4 FragColor; + +void main() +{ + FragColor = SMAABlendingWeightCalculationPS( + vTexCoord, vPixCoord, vOffset, + uEdgesTex, uAreaTex, uSearchTex, + ivec4(0) // subsample indices, 0 for SMAA 1x + ); +} + +``` + +`shaders/prepare/smaa_blending_weigths.vert`: + +```vert +/* smaa_edge_detection.vert -- SMAA blending weigths calculation pass + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +#define SMAA_INCLUDE_VS 1 +#define SMAA_INCLUDE_PS 0 + +#include "../include/smaa.glsl" + +const vec2 positions[3] = vec2[] +( + vec2(-1.0, -1.0), + vec2( 3.0, -1.0), + vec2(-1.0, 3.0) +); + +noperspective out vec2 vTexCoord; +noperspective out vec2 vPixCoord; +noperspective out vec4 vOffset[3]; + +void main() +{ + gl_Position = vec4(positions[gl_VertexID], 1.0, 1.0); + vTexCoord = (gl_Position.xy * 0.5) + 0.5; + + SMAABlendingWeightCalculationVS(vTexCoord, vPixCoord, vOffset); +} + +``` + +`shaders/prepare/smaa_edge_detection.frag`: + +```frag +/* smaa_edge_detection.frag -- SMAA edge detection pass + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +#define SMAA_INCLUDE_VS 0 +#define SMAA_INCLUDE_PS 1 + +#include "../include/smaa.glsl" + +uniform sampler2D uSceneTex; + +noperspective in vec2 vTexCoord; +noperspective in vec4 vOffset[3]; + +out vec2 FragColor; + +void main() +{ +#if QUALITY_PRESET < 2 + FragColor = SMAALumaEdgeDetectionPS(vTexCoord, vOffset, uSceneTex); +#else + FragColor = SMAAColorEdgeDetectionPS(vTexCoord, vOffset, uSceneTex); +#endif +} + +``` + +`shaders/prepare/smaa_edge_detection.vert`: + +```vert +/* smaa_edge_detection.vert -- SMAA edge detection pass + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +#define SMAA_INCLUDE_VS 1 +#define SMAA_INCLUDE_PS 0 + +#include "../include/smaa.glsl" + +const vec2 positions[3] = vec2[] +( + vec2(-1.0, -1.0), + vec2( 3.0, -1.0), + vec2(-1.0, 3.0) +); + +noperspective out vec2 vTexCoord; +noperspective out vec4 vOffset[3]; + +void main() +{ + gl_Position = vec4(positions[gl_VertexID], 1.0, 1.0); + vTexCoord = (gl_Position.xy * 0.5) + 0.5; + + SMAAEdgeDetectionVS(vTexCoord, vOffset); +} + +``` + +`shaders/prepare/ssao.frag`: + +```frag +/* ssao.frag -- Scalable Ambient Occlusion fragment shader + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +// Adapted from the method proposed by Morgan McGuire et al. in "Scalable Ambient Obscurance" +// SEE: https://research.nvidia.com/publication/2012-06_scalable-ambient-obscurance + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/view.glsl" +#include "../include/math.glsl" + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uNormalTex; +uniform sampler2D uDepthTex; + +uniform int uSampleCount; +uniform float uRadius; +uniform float uBias; +uniform float uIntensity; +uniform float uMaxSSRadius; + +/* === Constants === */ + +// Number of spiral turns for each sample count, ensuring coprime relationship. +// Each entry ROTATIONS[i] is chosen such that GCD(i+1, ROTATIONS[i]) = 1, +// preventing sample alignment artifacts in the spiral pattern. +// Indexed by (sampleCount - 1). Supports 1 to 98 samples. +const int ROTATIONS[98] = int[98]( + 1, 1, 2, 3, 2, 5, 2, 3, 2, + 3, 3, 5, 5, 3, 4, 7, 5, 5, 7, + 9, 8, 5, 5, 7, 7, 7, 8, 5, 8, + 11, 12, 7, 10, 13, 8, 11, 8, 7, 14, + 11, 11, 13, 12, 13, 19, 17, 13, 11, 18, + 19, 11, 11, 14, 17, 21, 15, 16, 17, 18, + 13, 17, 11, 17, 19, 18, 25, 18, 19, 19, + 29, 21, 19, 27, 31, 29, 21, 18, 17, 29, + 31, 31, 23, 18, 25, 26, 25, 23, 19, 34, + 19, 27, 21, 25, 39, 29, 17, 21, 27 +); + +/* === Fragments === */ + +out float FragOcclusion; + +/* === Helper functions === */ + +vec2 TapLocation(int i, float numSpiralTurns, float spin, out float rNorm) +{ + float alpha = (float(i) + 0.5) / float(uSampleCount); + float angle = alpha * (numSpiralTurns * M_TAU) + spin; + + rNorm = alpha; + return vec2(cos(angle), sin(angle)); +} + +/* === Main program === */ + +void main() +{ + FragOcclusion = 1.0; + + ivec2 pixelCoord = ivec2(gl_FragCoord.xy); + float depth = texture(uDepthTex, vTexCoord).r; + if (depth >= uView.far) return; + + vec3 position = V_GetViewPosition(vTexCoord, depth); + vec3 normal = V_GetViewNormal(uNormalTex, pixelCoord); + + float projScale = abs(uView.proj[1][1]) * textureSize(uDepthTex, 0).y * 0.5; + float ssRadiusRaw = projScale * uRadius / max(depth, 0.1); + float ssRadius = min(ssRadiusRaw, uMaxSSRadius); + + float radiusScale = ssRadius / max(ssRadiusRaw, 1e-4); + float radiusSq = uRadius * uRadius; + + // Here we use an IGN instead of the hash from the HPG12 AlchemyAO paper. + // The result is much more pleasing and blurs much better. + + float spin = M_TAU * M_HashR2(gl_FragCoord.xy); + int numSpiralTurns = ROTATIONS[clamp(uSampleCount - 1, 0, 97)]; + + float aoSum = 0.0; + for (int i = 0; i < uSampleCount; ++i) + { + float rNorm; + vec2 unitDir = TapLocation(i, float(numSpiralTurns), spin, rNorm); + ivec2 pixelOffset = pixelCoord + ivec2(unitDir * ssRadius * rNorm); + + vec3 samplePos = V_GetViewPosition(uDepthTex, pixelOffset); + vec3 v = samplePos - position; + + float vv = dot(v, v); + float vn = dot(v, normal); + + const float epsilon = 0.02; + float f = max(radiusSq - vv, 0.0); + aoSum += f * f * f * max((vn - uBias) / (epsilon + vv), 0.0); + } + + float temp = radiusSq * uRadius; + aoSum /= (temp * temp); + + // Attenuate intensity proportionally when ssRadius was clamped, preventing over-darkening at close range + float ao = max(0.0, 1.0 - aoSum * uIntensity * (2.0 / float(uSampleCount)) * radiusScale); + + // 1-pixel bilateral filter using derivatives (almost free) + if (abs(dFdx(depth)) < 0.2) ao -= dFdx(ao) * (float(pixelCoord.x & 1) - 0.5); + if (abs(dFdy(depth)) < 0.2) ao -= dFdy(ao) * (float(pixelCoord.y & 1) - 0.5); + + FragOcclusion = ao; +} + +``` + +`shaders/prepare/ssao_in_down.frag`: + +```frag +/* ssao_in_down.frag - G-Buffer downsampling for SSAO + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +uniform usampler2D uSelectorTex; +uniform sampler2D uNormalTex; + +layout(location = 0) out vec2 FragNormal; + +const ivec2 OFFSETS[4] = ivec2[4]( + ivec2(0, 0), ivec2(1, 0), + ivec2(0, 1), ivec2(1, 1) +); + +void main() +{ + ivec2 upCoord = 2 * ivec2(gl_FragCoord.xy); + ivec2 pxCoord = ivec2(gl_FragCoord.xy); + + uint index = texelFetch(uSelectorTex, pxCoord, 0).r; + FragNormal = texelFetch(uNormalTex, upCoord + OFFSETS[index], 0).rg; +} + +``` + +`shaders/prepare/ssgi.frag`: + +```frag +/* ssgi.frag -- Screen Space Global Illumination fragment shader + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +// Adapted from SSVGI by Alexander Sannikov, available in his LegitEngine repository: +// SEE: https://github.com/Raikiri/LegitEngine/tree/master/bin/data/Shaders/glsl/SSVGI + +/* === Includes === */ + +#include "../include/blocks/view.glsl" +#include "../include/math.glsl" + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uDiffuseTex; +uniform sampler2D uNormalTex; +uniform sampler2D uDepthTex; + +uniform int uSliceCount; +uniform float uEdgeFade; +uniform float uDistanceFalloff; +uniform float uNormalRejection; + +out vec4 FragColor; + +/* === Helper Functions === */ + +// Analytically integrates dot(N,w)*sin(th) over the horizon +// arc [h0, h1] in the slice plane defined by eyeDir and tangent +float HorizonContribution(float NdotE, float NdotT, float h0, float h1) +{ + return 0.25 * NdotE * (cos(2.0 * h0) - cos(2.0 * h1)) + + 0.25 * NdotT * (2.0 * (h1 - h0) - sin(2.0 * h1) + sin(2.0 * h0)); +} + +/* === Main Function === */ + +void main() +{ + vec2 viewport = vec2(textureSize(uDiffuseTex, 0)); + vec2 invViewport = 1.0 / viewport; + + // Receiver geometry + float depth = texelFetch(uDepthTex, ivec2(gl_FragCoord.xy), 0).r; + vec3 viewPos = V_GetViewPosition(vTexCoord, depth); + vec3 viewNorm = V_GetViewNormal(uNormalTex, vTexCoord); + vec3 eyeDir = normalize(-viewPos); + float NdotE = dot(viewNorm, eyeDir); + + // Per-pixel jitter + float jitter = M_HashIGN(gl_FragCoord.xy); + float angOffset = M_TAU * jitter; + float linOffset = jitter; + + // Exponential step size parameters + float startStep = viewport.x / 1000.0; + float invStartStep = 1.0 / startStep; + float stepGrowth = M_TAU / float(uSliceCount) + 1.0; + float invLogStepGrowth = 1.0 / log(stepGrowth); + float pixelDistBase = startStep * pow(stepGrowth, linOffset); + float pixelDistOffset = 1.0 - startStep; + + // Slice constants + float sliceStep = M_TAU / float(uSliceCount); + float sliceWeight = 2.0 / float(uSliceCount); + + vec3 gi = vec3(0.0); + + for (int slice = 0; slice < uSliceCount; slice++) + { + // Slice direction in screen space + float sliceAngle = angOffset + sliceStep * float(slice); + vec2 sliceDir = vec2(cos(sliceAngle), sin(sliceAngle)); + + // Tangent in view space derived from a small screen-space offset at the same depth + vec2 offsetUV = (gl_FragCoord.xy + sliceDir * 0.1) * invViewport; + vec3 offsetPos = V_GetViewPosition(offsetUV, depth); + vec3 tangent = normalize(normalize(offsetPos) + eyeDir); + float NdotT = dot(viewNorm, tangent); + + // Initial horizon angle: projection of the surface normal into the slice plane + float horizonAngle = atan(NdotE, -NdotT); + + // Distance in pixels to the nearest screen edge along sliceDir + vec2 t = mix((viewport - gl_FragCoord.xy) / sliceDir, -gl_FragCoord.xy / sliceDir, lessThan(sliceDir, vec2(0.0))); + int stepCount = int(log(max(min(t.x, t.y), 1.0) * invStartStep) * invLogStepGrowth) + 1; + + vec3 giSlice = vec3(0.0); + float pixelDistMult = 1.0; + + for (int step = 0; step < stepCount; step++) + { + // Exponentially growing pixel offset + linear jitter + float pixelDist = pixelDistBase * pixelDistMult + pixelDistOffset; + pixelDistMult *= stepGrowth; + + vec2 sampleUV = (gl_FragCoord.xy + sliceDir * pixelDist) * invViewport; + vec3 sampleViewPos = V_GetViewPosition(uDepthTex, sampleUV); + vec3 sampleNorm = V_GetViewNormal(uNormalTex, sampleUV); + vec3 delta = sampleViewPos - viewPos; + + // Skip samples on the same continuous surface (coplanar + similar normal) + if (abs(dot(delta, viewNorm)) < 0.03 && dot(sampleNorm, viewNorm) > 0.95) continue; + + float sampleAngle = atan(dot(tangent, delta), dot(eyeDir, delta)); + + // Only samples above the current horizon contribute + if (sampleAngle < horizonAngle) + { + vec3 light = textureLod(uDiffuseTex, sampleUV, 0.0).rgb; + float contrib = max(0.0, HorizonContribution(NdotE, NdotT, sampleAngle, horizonAngle)); + + vec2 edge = min(sampleUV, 1.0 - sampleUV); + float edgeFade = smoothstep(0.0, uEdgeFade, min(edge.x, edge.y)); + + float dist2 = dot(delta, delta); + float distFade = 1.0 / (1.0 + dist2 * uDistanceFalloff); + + // Reject light from back-facing emitters + float facing = -dot(sampleNorm, delta * inversesqrt(max(dist2, 1e-8))); + float normalFade = mix(1.0, smoothstep(0.0, 0.1, facing), uNormalRejection); + + giSlice += light * contrib * edgeFade * distFade * normalFade; + horizonAngle = sampleAngle; // tighten horizon + } + } + + gi += giSlice * sliceWeight; + } + + FragColor = vec4(gi, 1.0); +} + +``` + +`shaders/prepare/ssgi_in_down.frag`: + +```frag +/* ssgi_in_down.frag - G-Buffer downsampling for SSGI + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +uniform usampler2D uSelectorTex; +uniform sampler2D uDiffuseTex; +uniform sampler2D uNormalTex; + +layout(location = 0) out vec3 FragDiffuse; +layout(location = 1) out vec2 FragNormal; + +const ivec2 OFFSETS[4] = ivec2[4]( + ivec2(0, 0), ivec2(1, 0), + ivec2(0, 1), ivec2(1, 1) +); + +void main() +{ + ivec2 upCoord = 2 * ivec2(gl_FragCoord.xy); + ivec2 pxCoord = ivec2(gl_FragCoord.xy); + + uint index = texelFetch(uSelectorTex, pxCoord, 0).r; + + FragDiffuse = texelFetch(uDiffuseTex, upCoord + OFFSETS[index], 0).rgb; + FragNormal = texelFetch(uNormalTex, upCoord + OFFSETS[index], 0).rg; +} + +``` + +`shaders/prepare/ssil.frag`: + +```frag +/* ssil.frag -- Screen Space Directional Occlusion (SSDO) + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +// Based on Screen Space Directional Occlusion by Tobias Ritschel et al. +// SEE: https://web.archive.org/web/20140725130649/https://people.mpi-inf.mpg.de/~ritschel/Papers/SSDO.pdf + +// And on Scalable Ambient Obscurance method by Morgan McGuire et al. +// SEE: https://research.nvidia.com/publication/2012-06_scalable-ambient-obscurance + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/view.glsl" +#include "../include/math.glsl" + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uDiffuseTex; +uniform sampler2D uNormalTex; +uniform sampler2D uDepthTex; + +uniform int uSampleCount; +uniform float uRadius; +uniform float uBias; +uniform float uAoIntensity; +uniform float uMaxSSRadius; + +/* === Constants === */ + +// Number of spiral turns for each sample count, ensuring coprime relationship. +// Each entry ROTATIONS[i] is chosen such that GCD(i+1, ROTATIONS[i]) = 1, +// preventing sample alignment artifacts in the spiral pattern. +// Indexed by (sampleCount - 1). Supports 1 to 98 samples. +const int ROTATIONS[98] = int[98]( + 1, 1, 2, 3, 2, 5, 2, 3, 2, + 3, 3, 5, 5, 3, 4, 7, 5, 5, 7, + 9, 8, 5, 5, 7, 7, 7, 8, 5, 8, + 11, 12, 7, 10, 13, 8, 11, 8, 7, 14, + 11, 11, 13, 12, 13, 19, 17, 13, 11, 18, + 19, 11, 11, 14, 17, 21, 15, 16, 17, 18, + 13, 17, 11, 17, 19, 18, 25, 18, 19, 19, + 29, 21, 19, 27, 31, 29, 21, 18, 17, 29, + 31, 31, 23, 18, 25, 26, 25, 23, 19, 34, + 19, 27, 21, 25, 39, 29, 17, 21, 27 +); + +/* === Fragments === */ + +out vec4 FragColor; + +/* === Helper functions === */ + +vec2 TapLocation(int i, float numSpiralTurns, float spin, out float rNorm) +{ + float alpha = (float(i) + 0.5) / float(uSampleCount); + float angle = alpha * (numSpiralTurns * M_TAU) + spin; + + rNorm = alpha; + return vec2(cos(angle), sin(angle)); +} + +/* === Main program === */ + +void main() +{ + FragColor = vec4(vec3(0.0), 1.0); + + ivec2 pixelCoord = ivec2(gl_FragCoord.xy); + float depth = texture(uDepthTex, vTexCoord).r; + if (depth >= uView.far) return; + + vec3 position = V_GetViewPosition(vTexCoord, depth); + vec3 normal = V_GetViewNormal(uNormalTex, pixelCoord); + + float projScale = abs(uView.proj[1][1]) * textureSize(uDepthTex, 0).y * 0.5; + float ssRadiusRaw = projScale * uRadius / max(depth, 0.1); + float ssRadius = min(ssRadiusRaw, uMaxSSRadius); + + float radiusScale = ssRadius / max(ssRadiusRaw, 1e-4); + float radiusSq = uRadius * uRadius; + + // Here we use an IGN instead of the hash from the HPG12 AlchemyAO paper. + // The result is much more pleasing and blurs much better. + + float spin = M_TAU * M_HashR2(gl_FragCoord.xy); + int numSpiralTurns = ROTATIONS[clamp(uSampleCount - 1, 0, 97)]; + + float aoSum = 0.0; + vec3 giSum = vec3(0.0); + + for (int i = 0; i < uSampleCount; ++i) + { + float rNorm; + vec2 unitDir = TapLocation(i, float(numSpiralTurns), spin, rNorm); + ivec2 pixelOffset = pixelCoord + ivec2(unitDir * ssRadius * rNorm); + + vec3 samplePos = V_GetViewPosition(uDepthTex, pixelOffset); + vec3 v = samplePos - position; + + float vv = dot(v, v); + float vn = dot(v, normal); + + const float epsilon = 0.02; + float f = max(radiusSq - vv, 0.0); + float w = f * f * f * max((vn - uBias) / (epsilon + vv), 0.0); + + aoSum += w; + giSum += texelFetch(uDiffuseTex, pixelOffset, 0).rgb * w; + } + + float temp = radiusSq * uRadius; + float normFactor = 1.0 / (temp * temp); + + aoSum *= normFactor; + giSum *= normFactor; + + // Attenuate intensity proportionally when ssRadius was clamped, preventing over-darkening at close range + float ao = max(0.0, 1.0 - aoSum * uAoIntensity * (2.0 / float(uSampleCount)) * radiusScale); + vec3 gi = giSum * (4.0 / float(uSampleCount)) * radiusScale; + + // 1-pixel bilateral filter using derivatives (almost free) + if (abs(dFdx(depth)) < 0.2) { + float dx = float(pixelCoord.x & 1) - 0.5; + ao -= dFdx(ao) * dx; + gi -= dFdx(gi) * dx; + } + if (abs(dFdy(depth)) < 0.2) { + float dy = float(pixelCoord.y & 1) - 0.5; + ao -= dFdy(ao) * dy; + gi -= dFdy(gi) * dy; + } + + FragColor = vec4(gi, ao); +} + +``` + +`shaders/prepare/ssil_in_down.frag`: + +```frag +/* ssil_in_down.frag - G-Buffer downsampling for SSIL + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +uniform usampler2D uSelectorTex; +uniform sampler2D uDiffuseTex; +uniform sampler2D uNormalTex; + +layout(location = 0) out vec3 FragDiffuse; +layout(location = 1) out vec2 FragNormal; + +const ivec2 OFFSETS[4] = ivec2[4]( + ivec2(0, 0), ivec2(1, 0), + ivec2(0, 1), ivec2(1, 1) +); + +void main() +{ + ivec2 upCoord = 2 * ivec2(gl_FragCoord.xy); + ivec2 pxCoord = ivec2(gl_FragCoord.xy); + + uint index = texelFetch(uSelectorTex, pxCoord, 0).r; + + FragDiffuse = texelFetch(uDiffuseTex, upCoord + OFFSETS[index], 0).rgb; + FragNormal = texelFetch(uNormalTex, upCoord + OFFSETS[index], 0).rg; +} + +``` + +`shaders/prepare/ssr.frag`: + +```frag +/* ssr.frag -- Screen Space Reflections fragment shader + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/view.glsl" +#include "../include/math.glsl" + +/* === Varyings === */ + +noperspective in vec2 vTexCoord; + +/* === Uniforms === */ + +uniform sampler2D uDiffuseTex; +uniform sampler2D uSpecularTex; +uniform sampler2D uNormalTex; +uniform sampler2D uDepthTex; + +uniform int uMaxRaySteps; +uniform int uBinarySteps; +uniform float uStepSize; +uniform float uThickness; +uniform float uMaxDistance; +uniform float uEdgeFade; + +/* === Output === */ + +out vec4 FragColor; + +/* === Raymarching === */ + +vec4 TraceRay(vec3 startViewPos, vec3 reflectionDir) +{ + vec3 dirStep = reflectionDir * uStepSize; + float stepDistanceSq = dot(dirStep, dirStep); + float maxDistanceSq = uMaxDistance * uMaxDistance; + + vec3 currentPos = startViewPos + dirStep; + vec3 prevPos = startViewPos; + float rayDistanceSq = stepDistanceSq; + + vec2 hitUV = vec2(0.0); + bool hit = false; + + for (int i = 1; i < uMaxRaySteps; i++) + { + if (rayDistanceSq > maxDistanceSq) break; + vec2 uv = V_ViewToScreen(currentPos); + if (V_OffScreen(uv)) break; + + float sampleZ = -textureLod(uDepthTex, uv, 0).r; + float depthDiff = sampleZ - currentPos.z; + + if (depthDiff > 0.0 && depthDiff < uThickness) { + hitUV = uv; + hit = true; + break; + } + + prevPos = currentPos; + currentPos += dirStep; + rayDistanceSq += stepDistanceSq; + } + + if (!hit) return vec4(0.0); + + vec3 start = prevPos; + vec3 end = currentPos; + + for (int i = 0; i < uBinarySteps; i++) + { + vec3 mid = mix(start, end, 0.5); + vec2 uv = V_ViewToScreen(mid); + if (V_OffScreen(uv)) break; + + float sampleZ = -textureLod(uDepthTex, uv, 0).r; + float depthDiff = sampleZ - mid.z; + + if (depthDiff > 0.0 && depthDiff < uThickness) { + hitUV = uv; + end = mid; + } + else { + start = mid; + } + } + + vec3 hitNormal = V_GetViewNormal(uNormalTex, hitUV); + float d = dot(reflectionDir, hitNormal); + if (d > 0.0) return vec4(0.0); + + vec3 hitDiff = textureLod(uDiffuseTex, hitUV, 0).rgb; + vec3 hitSpec = textureLod(uSpecularTex, hitUV, 0).rgb; + + vec2 distToBorder = min(hitUV, 1.0 - hitUV); + float edgeFade = smoothstep(0.0, uEdgeFade, min(distToBorder.x, distToBorder.y)); + float distFade = 1.0 - smoothstep(0.0, uMaxDistance, sqrt(rayDistanceSq)); + + return vec4(hitDiff + hitSpec, edgeFade * distFade); +} + +/* === Main Program === */ + +void main() +{ + // Early depth rejection + float linearDepth = texelFetch(uDepthTex, ivec2(gl_FragCoord.xy), 0).r; + if (linearDepth >= uView.far) { + FragColor = vec4(0.0); + return; + } + + // Fetch view-space geometry + vec3 viewNormal = V_GetViewNormal(uNormalTex, ivec2(gl_FragCoord.xy)); + vec3 viewPos = V_GetViewPosition(vTexCoord, linearDepth); + vec3 viewDir = normalize(viewPos); + vec3 reflectionDir = reflect(viewDir, viewNormal); + + // Trace reflection ray + FragColor = TraceRay(viewPos, reflectionDir); +} + +``` + +`shaders/prepare/ssr_in_down.frag`: + +```frag +/* ssr_in_down.frag - G-Buffer downsampling for SSR + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +uniform usampler2D uSelectorTex; +uniform sampler2D uDiffuseTex; +uniform sampler2D uSpecularTex; +uniform sampler2D uNormalTex; + +layout(location = 0) out vec3 FragDiffuse; +layout(location = 1) out vec3 FragSpecular; +layout(location = 2) out vec2 FragNormal; + +const ivec2 OFFSETS[4] = ivec2[4]( + ivec2(0, 0), ivec2(1, 0), + ivec2(0, 1), ivec2(1, 1) +); + +void main() +{ + ivec2 upCoord = 2 * ivec2(gl_FragCoord.xy); + ivec2 pxCoord = ivec2(gl_FragCoord.xy); + + uint index = texelFetch(uSelectorTex, pxCoord, 0).r; + + FragDiffuse = texelFetch(uDiffuseTex, upCoord + OFFSETS[index], 0).rgb; + FragSpecular = texelFetch(uSpecularTex, upCoord + OFFSETS[index], 0).rgb; + FragNormal = texelFetch(uNormalTex, upCoord + OFFSETS[index], 0).rg; +} + +``` + +`shaders/scene/decal.frag`: + +```frag +/* decal.frag -- Fragment shader used for rendering decals into G-buffers + * + * Copyright (c) 2025 Michael Blaine + * This file is derived from the work of Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/frame.glsl" +#include "../include/blocks/view.glsl" +#include "../include/math.glsl" + +/* === Varyings === */ + +smooth in vec3 vPosition; //< For custom shaders +smooth in mat4 vDecalProjection; +smooth in mat3 vDecalAxes; +flat in vec3 vEmission; +smooth in vec4 vColor; + +/* === Uniforms === */ + +uniform sampler2D uAlbedoMap; +uniform sampler2D uNormalMap; +uniform sampler2D uEmissionMap; +uniform sampler2D uOrmMap; + +uniform sampler2D uDepthTex; +uniform sampler2D uGeomNormalTex; + +uniform float uAlphaCutoff; +uniform float uNormalScale; +uniform float uOcclusion; +uniform float uRoughness; +uniform float uMetalness; +uniform float uSpecular; + +uniform vec2 uTexCoordOffset; +uniform vec2 uTexCoordScale; + +uniform float uNormalThreshold; +uniform float uFadeWidth; +uniform bool uApplyColor; + +/* === Fragments === */ + +layout(location = 0) out vec4 FragAlbedo; +layout(location = 1) out vec4 FragEmission; +layout(location = 2) out vec4 FragORM; +layout(location = 3) out vec4 FragNormal; + +/* === Helper functions === */ + +mat3 BuildDecalTBN(vec3 worldNormal) +{ + vec3 decalX = normalize(vDecalAxes[0]); + vec3 decalZ = normalize(vDecalAxes[2]); + + vec3 T = normalize(decalX - dot(decalX, worldNormal) * worldNormal); + vec3 B = normalize(decalZ - dot(decalZ, worldNormal) * worldNormal); + + B *= sign(dot(cross(T, B), worldNormal)); + + return mat3(T, B, worldNormal); +} + +/* === User override === */ + +#include "../include/user/scene.frag" + +/* === Main function === */ + +void main() +{ + /* Transform view-space position to decal local space */ + vec3 positionViewSpace = V_GetViewPosition(uDepthTex, ivec2(gl_FragCoord.xy)); + vec4 positionObjectSpace = vDecalProjection * vec4(positionViewSpace, 1.0); + + /* Discard fragments outside projector bounds */ + if (any(greaterThan(abs(positionObjectSpace.xyz), vec3(0.5)))) discard; + + /* Compute decal UVs in [0, 1] range */ + vec2 decalTexCoord = uTexCoordOffset + (positionObjectSpace.xz + 0.5) * uTexCoordScale; + + /* Fetch surface normal and build TBN */ + vec2 encGeomNormal = texelFetch(uGeomNormalTex, ivec2(gl_FragCoord.xy), 0).rg; + vec3 worldNormal = M_DecodeOctahedral(encGeomNormal); + mat3 TBN = BuildDecalTBN(worldNormal); + + /* Normal threshold culling */ + float angle = acos(clamp(dot(vDecalAxes[1], worldNormal), -1.0, 1.0)); + float difference = uNormalThreshold - angle; + if (difference < 0.0) discard; + + /* Sample material maps with alpha cutoff */ + SceneFragment(decalTexCoord, TBN, uAlphaCutoff); + + /* Compute fade factor */ + float fadeAlpha = clamp(difference / uFadeWidth, 0.0, 1.0) * ALPHA; + + /* Transform and scale decal normal */ + TBN = mat3(TANGENT, BITANGENT, NORMAL); + vec3 N = normalize(TBN * M_NormalScale(NORMAL_MAP * 2.0 - 1.0, uNormalScale * fadeAlpha)); + + /* Output */ + FragAlbedo = vec4(ALBEDO, fadeAlpha * float(uApplyColor)); + FragNormal = vec4(M_EncodeOctahedral(N), 0.0, 1.0); + FragEmission = vec4(EMISSION, fadeAlpha); + FragORM = vec4(vec3(OCCLUSION, ROUGHNESS, METALNESS), fadeAlpha); + + /* FIXME: uSpecular/SPECULAR is available but not written to FragORM.a, which is used + * here as a blend factor. Alpha writes are currently masked in pass_scene_decals + * to preserve the underlying surface specular (F0) until a proper solution is found. */ +} + +``` + +`shaders/scene/depth.frag`: + +```frag +/* depth.frag -- Fragment shader used for dir/spot-lights shadow mapping + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/frame.glsl" + +/* === Varyings === */ + +smooth in vec3 vPosition; //< For custom shaders +smooth in vec2 vTexCoord; +smooth in vec4 vColor; + +/* === Uniforms === */ + +uniform sampler2D uAlbedoMap; +uniform float uAlphaCutoff; + +/* === User override === */ + +#include "../include/user/scene.frag" + +/* === Main function === */ + +void main() +{ + // NOTE: The depth is automatically written + SceneFragment(vTexCoord, mat3(1.0), uAlphaCutoff); +} + +``` + +`shaders/scene/depth_cube.frag`: + +```frag +/* depth_cube.frag -- Fragment shader used for omni-lights shadow mapping + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/frame.glsl" + +/* === Varyings === */ + +smooth in vec3 vPosition; +smooth in vec2 vTexCoord; +smooth in vec4 vColor; + +/* === Uniforms === */ + +uniform sampler2D uAlbedoMap; +uniform float uAlphaCutoff; +uniform vec3 uViewPosition; +uniform float uFar; + +/* === User override === */ + +#include "../include/user/scene.frag" + +/* === Main function === */ + +void main() +{ + SceneFragment(vTexCoord, mat3(1.0), uAlphaCutoff); + gl_FragDepth = length(vPosition - uViewPosition) / uFar; +} + +``` + +`shaders/scene/forward.frag`: + +```frag +/* forward.frag -- Fragment shader used for forward shading + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Extensions === */ + +#extension GL_ARB_texture_cube_map_array : enable + +/* === Includes === */ + +#include "../include/math.glsl" +#include "../include/pbr.glsl" + +/* === Varyings === */ + +smooth in vec3 vPosition; +smooth in vec2 vTexCoord; +flat in vec3 vEmission; +smooth in vec4 vColor; +smooth in mat3 vTBN; + +smooth in float vLinearDepth; +smooth in vec4 vPosLightSpace[NUM_FORWARD_LIGHTS]; + +/* === Uniforms === */ + +uniform sampler2D uAlbedoMap; +uniform sampler2D uEmissionMap; +uniform sampler2D uNormalMap; +uniform sampler2D uOrmMap; + +uniform sampler2DArrayShadow uShadowDirTex; +uniform sampler2DArrayShadow uShadowSpotTex; +uniform samplerCubeArrayShadow uShadowOmniTex; + +uniform samplerCubeArray uIrradianceTex; +uniform samplerCubeArray uPrefilterTex; +uniform sampler2D uBrdfLutTex; + +uniform float uNormalScale; +uniform float uOcclusion; +uniform float uRoughness; +uniform float uMetalness; +uniform float uSpecular; + +uniform vec3 uViewPosition; + +#if defined(PROBE) +uniform bool uProbeInterior; +#endif // PROBE + +/* === Blocks === */ + +#define L_SHADOW_IMPL //< Shadow functions in blocks/light.glsl + +#include "../include/blocks/frame.glsl" +#include "../include/blocks/light.glsl" +#include "../include/blocks/env.glsl" +#include "../include/blocks/fog.glsl" + +/* === Fragments === */ + +layout(location = 0) out vec4 FragColor; + +/* === User override === */ + +#include "../include/user/scene.frag" + +/* === Main function === */ + +void main() +{ + /* Sample material maps */ + + SceneFragment(vTexCoord, vTBN, 0.0); + + vec3 ORM = vec3(OCCLUSION, ROUGHNESS, METALNESS); + mat3 TBN = mat3(TANGENT, BITANGENT, NORMAL); + float dielectric = (1.0 - METALNESS); + + /* Compute F0 (reflectance at normal incidence) and diffuse coefficient */ + + vec3 F0 = PBR_ComputeF0(METALNESS, SPECULAR, ALBEDO); + vec3 kD = dielectric * ALBEDO; + + /* Sample normal map and compute final normal */ + + vec3 N = normalize(TBN * M_NormalScale(texture(uNormalMap, vTexCoord).rgb * 2.0 - 1.0, uNormalScale)); + if (!gl_FrontFacing) N = -N; // Flip for back facing triangles with double sided meshes + + /* Compute view direction and the dot product of the normal and view direction */ + + vec3 V = normalize(uViewPosition - vPosition); + float NdotV = max(dot(N, V), 1e-4); + + /* Loop through all light sources accumulating diffuse and specular light */ + + mat2 diskRot = L_ShadowDebandingMatrix(gl_FragCoord.xy); + + vec3 diff = vec3(0.0); + vec3 spec = vec3(0.0); + + for (int i = 0; i < uNumLights; i++) + { + Light light = uLights[i]; + + /* Compute light direction and the dot product of the normal and light direction */ + + vec3 Ldelta = light.position - vPosition; + float Ldist = length(Ldelta); + + vec3 L = (light.type == LIGHT_DIR) ? -light.direction : Ldelta / max(Ldist, 1e-4); + float NdotL = dot(N, L); + + if (NdotL <= 0.0) continue; + + /* Compute the halfway vector between the view and light directions */ + + vec3 H = normalize(V + L); + + float LdotH = max(dot(L, H), 0.0); + float NdotH = max(dot(N, H), 0.0); + + /* Compute light color energy */ + + vec3 lightColE = light.color * light.energy; + + /* Compute diffuse lighting */ + + vec3 diffLight = L_Diffuse(LdotH, NdotV, NdotL, ROUGHNESS); + diffLight *= lightColE * dielectric; + + /* Compute specular lighting */ + + vec3 specLight = L_Specular(F0, LdotH, NdotH, NdotV, NdotL, ROUGHNESS); + specLight *= lightColE * light.specular; + + /* Compute shadow factor */ + + float shadow = 1.0; + + if (light.type != LIGHT_DIR) { + float atten = 1.0 - clamp(Ldist / light.range, 0.0, 1.0); + shadow *= atten * light.attenuation; + } + + if (light.type == LIGHT_SPOT) { + float theta = dot(L, -light.direction); + float epsilon = (light.innerCutOff - light.outerCutOff); + shadow *= smoothstep(0.0, 1.0, (theta - light.outerCutOff) / epsilon); + } + + if (light.shadowLayer >= 0 && light.shadowOpacity != 0.0 && shadow > 1e-4) { + switch (light.type) { + case LIGHT_DIR: shadow *= L_SampleShadowDir(i, vPosLightSpace[i], vLinearDepth, NdotL, diskRot); break; + case LIGHT_SPOT: shadow *= L_SampleShadowSpot(i, vPosLightSpace[i], NdotL, diskRot); break; + case LIGHT_OMNI: shadow *= L_SampleShadowOmni(i, vPosition, NdotL, diskRot); break; + } + } + + /* Accumulate the diffuse and specular lighting contributions */ + + diff += diffLight * shadow; + spec += specLight * shadow; + } + + /* Compute ambient */ + +#if defined(PROBE) + if (uProbeInterior) E_ComputeAmbientColor(diff, kD, OCCLUSION); + else E_ComputeAmbientOnly(diff, spec, kD, ORM.rgb, F0, vPosition, N, V, NdotV); +#else + E_ComputeAmbientAndProbes(diff, spec, kD, ORM.rgb, F0, vPosition, N, V, NdotV); +#endif + + /* Compute the final fragment color */ + + FragColor = vec4(ALBEDO * diff + spec + EMISSION, ALPHA); + FragColor = FogColorMix(FragColor, vLinearDepth); +} + +``` + +`shaders/scene/geometry.frag`: + +```frag +/* geometry.frag -- Fragment shader used for rendering in G-buffers + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/frame.glsl" +#include "../include/blocks/view.glsl" +#include "../include/math.glsl" + +/* === Varyings === */ + +smooth in vec3 vPosition; //< For custom shaders +smooth in vec2 vTexCoord; +flat in vec3 vEmission; +smooth in vec4 vColor; +smooth in mat3 vTBN; + +smooth in float vLinearDepth; + +/* === Uniforms === */ + +uniform sampler2D uAlbedoMap; +uniform sampler2D uNormalMap; +uniform sampler2D uEmissionMap; +uniform sampler2D uOrmMap; + +uniform float uAlphaCutoff; +uniform float uNormalScale; +uniform float uOcclusion; +uniform float uRoughness; +uniform float uMetalness; +uniform float uSpecular; + +/* === Fragments === */ + +layout(location = 0) out vec3 FragAlbedo; +layout(location = 1) out vec3 FragEmission; +layout(location = 2) out vec2 FragNormal; +layout(location = 3) out vec4 FragORM; +layout(location = 4) out vec2 FragGeomNormal; +layout(location = 5) out float FragDepth; + +/* === User override === */ + +#include "../include/user/scene.frag" + +/* === Main function === */ + +void main() +{ + SceneFragment(vTexCoord, vTBN, uAlphaCutoff); + + mat3 TBN = mat3(TANGENT, BITANGENT, NORMAL); + vec3 N = normalize(TBN * M_NormalScale(NORMAL_MAP * 2.0 - 1.0, uNormalScale)); + vec3 gN = NORMAL; + + // Flip for back facing triangles with double sided meshes + if (!gl_FrontFacing) N = -N, gN = -gN; + + FragAlbedo = ALBEDO; + FragEmission = EMISSION; + FragNormal = M_EncodeOctahedral(N); + FragGeomNormal = M_EncodeOctahedral(gN); + FragORM = vec4(OCCLUSION, ROUGHNESS, METALNESS, SPECULAR); + FragDepth = vLinearDepth; +} + +``` + +`shaders/scene/scene.vert`: + +```vert +/* scene.vert -- Common vertex shader for all scene render paths. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Constants === */ + +#define BILLBOARD_NONE 0 +#define BILLBOARD_FRONT 1 +#define BILLBOARD_Y_AXIS 2 + +/* === Includes === */ + +#include "../include/blocks/frame.glsl" +#include "../include/blocks/light.glsl" +#include "../include/blocks/view.glsl" +#include "../include/math.glsl" + +/* === Attributes === */ + +layout(location = 0) in vec3 aPosition; +layout(location = 1) in vec2 aTexCoord; +layout(location = 2) in vec3 aNormal; +layout(location = 3) in vec4 aTangent; +layout(location = 4) in vec4 aColor; +layout(location = 5) in ivec4 aBoneIndices; +layout(location = 6) in vec4 aBoneWeights; + +layout(location = 10) in vec3 iPosition; +layout(location = 11) in vec4 iRotation; +layout(location = 12) in vec3 iScale; +layout(location = 13) in vec4 iColor; +layout(location = 14) in vec4 iCustom; + +/* === Uniforms === */ + +uniform sampler1D uBoneMatricesTex; + +uniform mat4 uMatModel; +uniform mat4 uMatNormal; + +uniform vec4 uAlbedoColor; +uniform vec3 uEmissionColor; +uniform float uEmissionEnergy; + +uniform vec2 uTexCoordOffset; +uniform vec2 uTexCoordScale; +uniform bool uInstancing; + +#if !defined(DECAL) +uniform bool uSkinning; +uniform int uBillboard; +#endif // !DECAL + +#if defined(PROBE) +uniform mat4 uMatView; +#endif // PROBE + +#if defined(DEPTH) || defined(DEPTH_CUBE) || defined(PROBE) +uniform mat4 uMatInvView; // inv view only for billboard modes +uniform mat4 uMatViewProj; +#endif // DEPTH || DEPTH_CUBE || PROBE + +/* === Varyings === */ + +smooth out vec3 vPosition; +smooth out vec2 vTexCoord; +flat out vec3 vEmission; +smooth out vec4 vColor; +smooth out mat3 vTBN; + +#if defined(GEOMETRY) || defined(FORWARD) || defined(UNLIT) || defined(PROBE) +smooth out float vLinearDepth; +#endif // GEOMETRY || FORWARD || UNLIT || PROBE + +#if defined(FORWARD) || defined(PROBE_FORWARD) +smooth out vec4 vPosLightSpace[NUM_FORWARD_LIGHTS]; +#endif // FORWARD || PROBE_FORWARD + +#if defined(DECAL) +smooth out mat4 vDecalProjection; +smooth out mat3 vDecalAxes; +#endif // DECAL + +/* === Helper Functions === */ + +mat4 BoneMatrix(int boneID) +{ + int baseIndex = 4 * boneID; + + vec4 row0 = texelFetch(uBoneMatricesTex, baseIndex + 0, 0); + vec4 row1 = texelFetch(uBoneMatricesTex, baseIndex + 1, 0); + vec4 row2 = texelFetch(uBoneMatricesTex, baseIndex + 2, 0); + vec4 row3 = texelFetch(uBoneMatricesTex, baseIndex + 3, 0); + + return transpose(mat4(row0, row1, row2, row3)); +} + +mat4 SkinMatrix(ivec4 boneIDs, vec4 weights) +{ + return weights.x * BoneMatrix(boneIDs.x) + + weights.y * BoneMatrix(boneIDs.y) + + weights.z * BoneMatrix(boneIDs.z) + + weights.w * BoneMatrix(boneIDs.w); +} + +#if defined(DECAL) +mat4 MatrixTransform(vec3 translation, vec4 quat, vec3 scale) +{ + float xx = quat.x * quat.x; + float yy = quat.y * quat.y; + float zz = quat.z * quat.z; + float xy = quat.x * quat.y; + float xz = quat.x * quat.z; + float yz = quat.y * quat.z; + float wx = quat.w * quat.x; + float wy = quat.w * quat.y; + float wz = quat.w * quat.z; + + return mat4( + scale.x * (1.0 - 2.0 * (yy + zz)), scale.x * 2.0 * (xy + wz), scale.x * 2.0 * (xz - wy), 0.0, + scale.y * 2.0 * (xy - wz), scale.y * (1.0 - 2.0 * (xx + zz)), scale.y * 2.0 * (yz + wx), 0.0, + scale.z * 2.0 * (xz + wy), scale.z * 2.0 * (yz - wx), scale.z * (1.0 - 2.0 * (xx + yy)), 0.0, + translation.x, translation.y, translation.z, 1.0 + ); +} +#endif + +void BillboardFront(inout vec3 position, inout vec3 normal, inout vec3 tangent, vec3 center, mat4 invView) +{ + vec3 right = invView[0].xyz; + vec3 up = invView[1].xyz; + vec3 forward = invView[2].xyz; + + vec3 localPos = position - center; + vec3 localNormal = normal; + vec3 localTangent = tangent; + + position = center + localPos.x*right + localPos.y*up + localPos.z*forward; + normal = localNormal.x*right + localNormal.y*up + localNormal.z*forward; + tangent = localTangent.x*right + localTangent.y*up + localTangent.z*forward; +} + +void BillboardYAxis(inout vec3 position, inout vec3 normal, inout vec3 tangent, vec3 center, mat4 invView) +{ + vec3 cameraPos = vec3(invView[3]); + vec3 upVector = vec3(0, 1, 0); + + vec3 look = normalize(cameraPos - center); + vec3 right = normalize(cross(upVector, look)); + vec3 front = normalize(cross(right, upVector)); + + vec3 localPos = position - center; + vec3 localNormal = normal; + vec3 localTangent = tangent; + + position = center + localPos.x*right + localPos.y*upVector + localPos.z*front; + normal = localNormal.x*right + localNormal.y*upVector + localNormal.z*front; + tangent = localTangent.x*right + localTangent.y*upVector + localTangent.z*front; +} + +/* === User override === */ + +#include "../include/user/scene.vert" + +/* === Main program === */ + +void main() +{ + SceneVertex(); + + vec3 billboardCenter = vec3(MATRIX_MODEL[3]); + vec3 localPosition = POSITION; + vec3 localNormal = NORMAL; + vec3 localTangent = TANGENT.xyz; + +#if !defined(DECAL) + if (uSkinning) { + mat4 sMatModel = SkinMatrix(aBoneIndices, aBoneWeights); + mat3 sMatNormal = mat3(transpose(inverse(sMatModel))); + localPosition = vec3(sMatModel * vec4(localPosition, 1.0)); + localNormal = sMatNormal * localNormal; + localTangent = sMatNormal * localTangent; + } +#endif // !DECAL + + vec3 finalPosition = vec3(MATRIX_MODEL * vec4(localPosition, 1.0)); + vec3 finalNormal = MATRIX_NORMAL * localNormal; + vec3 finalTangent = MATRIX_NORMAL * localTangent; + vec4 finalColor = COLOR; + +#if defined(DECAL) + mat4 decalMatModel = MATRIX_MODEL; +#endif // DECAL + + if (uInstancing) { + billboardCenter += INSTANCE_POSITION; + finalPosition = finalPosition * INSTANCE_SCALE; + finalPosition = M_Rotate3D(finalPosition, INSTANCE_ROTATION); + finalPosition = finalPosition + INSTANCE_POSITION; + finalNormal = M_Rotate3D(finalNormal, INSTANCE_ROTATION); + finalTangent = M_Rotate3D(finalTangent, INSTANCE_ROTATION); + finalColor *= INSTANCE_COLOR; + + #if defined(DECAL) + mat4 iMatModel = MatrixTransform(INSTANCE_POSITION, INSTANCE_ROTATION, INSTANCE_SCALE); + decalMatModel = iMatModel * decalMatModel; + #endif // DECAL + } + +#if !defined(DECAL) + if (uBillboard == BILLBOARD_FRONT) { + BillboardFront(finalPosition, finalNormal, finalTangent, billboardCenter, MATRIX_INV_VIEW); + } + else if (uBillboard == BILLBOARD_Y_AXIS) { + BillboardYAxis(finalPosition, finalNormal, finalTangent, billboardCenter, MATRIX_INV_VIEW); + } +#endif // !DECAL + + vec3 T = normalize(finalTangent); + vec3 N = normalize(finalNormal); + vec3 B = normalize(cross(N, T) * TANGENT.w); + + vPosition = finalPosition; + vTexCoord = TEXCOORD; + vEmission = EMISSION; + vColor = finalColor; + vTBN = mat3(T, B, N); + +#if defined(GEOMETRY) || defined(FORWARD) || defined(UNLIT) + vLinearDepth = -(uView.view * vec4(vPosition, 1.0)).z; +#elif defined(PROBE) + vLinearDepth = -(uMatView * vec4(vPosition, 1.0)).z; +#endif // GEOMETRY || FORWARD || UNLIT || PROBE + +#if defined(FORWARD) || defined(PROBE_FORWARD) + for (int i = 0; i < uNumLights; i++) { + vPosLightSpace[i] = uLights[i].viewProj * vec4(vPosition, 1.0); + } +#endif // FORWARD || PROBE_FORWARD + + gl_Position = MATRIX_VIEW_PROJECTION * vec4(vPosition, 1.0); + +#if defined(DECAL) + vDecalProjection = inverse(decalMatModel) * MATRIX_INV_VIEW; + vDecalAxes[0] = normalize(decalMatModel[0].xyz); + vDecalAxes[1] = normalize(decalMatModel[1].xyz); + vDecalAxes[2] = normalize(decalMatModel[2].xyz); +#endif // DECAL +} + +``` + +`shaders/scene/skybox.frag`: + +```frag +/* skybox.frag -- Fragment shader used to render skyboxes + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/fog.glsl" +#include "../include/math.glsl" + +/* === Varyings === */ + +in vec3 vViewRay; + +/* === Uniforms === */ + +uniform samplerCube uSkyMap; +uniform vec4 uRotation; +uniform float uEnergy; +uniform float uLod; + +/* === Fragments === */ + +layout(location = 0) out vec3 FragColor; + +/* === Program === */ + +void main() +{ + vec3 direction = normalize(vViewRay); + direction = M_Rotate3D(direction, uRotation); + FragColor = textureLod(uSkyMap, direction, uLod).rgb * uEnergy; + FragColor = FogSkyMix(FragColor); +} + +``` + +`shaders/scene/skybox.vert`: + +```vert +/* skybox.vert -- Vertex shader used to render skyboxes + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Constants === */ + +const vec2 positions[3] = vec2[] +( + vec2(-1.0, -1.0), + vec2( 3.0, -1.0), + vec2(-1.0, 3.0) +); + +/* === Uniforms === */ + +uniform mat4 uMatInvProj; +uniform mat4 uMatInvView; + +/* === Varyings === */ + +out vec3 vViewRay; + +/* === Program === */ + +void main() +{ + vec2 pos = positions[gl_VertexID]; + gl_Position = vec4(pos, 1.0, 1.0); + + vec4 unprojected = uMatInvProj * vec4(pos, 1.0, 1.0); + vViewRay = mat3(uMatInvView) * unprojected.xyz; +} + +``` + +`shaders/scene/unlit.frag`: + +```frag +/* unlit.frag -- Fragment shader used for unlit objects + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#version 330 core + +/* === Includes === */ + +#include "../include/blocks/frame.glsl" +#include "../include/blocks/fog.glsl" + +/* === Varyings === */ + +smooth in vec3 vPosition; //< For custom shaders +smooth in vec2 vTexCoord; +smooth in vec4 vColor; + +smooth in float vLinearDepth; + +/* === Uniforms === */ + +uniform sampler2D uAlbedoMap; +uniform float uAlphaCutoff; + +/* === Fragments === */ + +layout(location = 0) out vec4 FragColor; + +/* === User override === */ + +#include "../include/user/scene.frag" + +/* === Main function === */ + +void main() +{ + SceneFragment(vTexCoord, mat3(1.0), uAlphaCutoff); + + FragColor = vec4(ALBEDO, ALPHA); + FragColor = FogColorMix(FragColor, vLinearDepth); +} + +``` + +`src/common/r3d_anim.c`: + +```c +/* r3d_anim.c -- Common R3D Animation Helper Functions + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_anim.h" + +// ======================================== +// INTERNAL FUNCTION +// ======================================== + +static void find_key_frames(const float* times, uint32_t count, float time, + uint32_t* outIdx0, uint32_t* outIdx1, + float* outT) +{ + // No keys + if (count == 0) { + *outIdx0 = *outIdx1 = 0; + *outT = 0.0f; + return; + } + + // Single key or before first + if (count == 1 || time <= times[0]) { + *outIdx0 = *outIdx1 = 0; + *outT = 0.0f; + return; + } + + // After last + if (time >= times[count - 1]) { + *outIdx0 = *outIdx1 = count - 1; + *outT = 0.0f; + return; + } + + // Binary search + uint32_t left = 0; + uint32_t right = count - 1; + while(right - left > 1) { + uint32_t mid = (left + right) >> 1; + if (times[mid] <= time) left = mid; + else right = mid; + } + + *outIdx0 = left; + *outIdx1 = right; + + float t0 = times[left]; + float t1 = times[right]; + float dt = t1 - t0; + + *outT = (dt > 0.0f) ? (time - t0) / dt : 0.0f; +} + +// ======================================== +// TRANSFORM/MATRIX FUNCTIONS +// ======================================== + +Transform r3d_anim_transform_lerp(Transform a, Transform b, float value) +{ + Transform result; + result.translation = Vector3Lerp(a.translation, b.translation, value); + result.rotation = QuaternionSlerp(a.rotation, b.rotation, value); + result.scale = Vector3Lerp(a.scale, b.scale, value); + return result; +} + +Transform r3d_anim_transform_add(Transform a, Transform b) +{ + Transform result; + result.translation = Vector3Add(a.translation, b.translation); + result.rotation = QuaternionAdd(a.rotation, b.rotation); + result.scale = Vector3Add(a.scale, b.scale); + return result; +} + +Transform r3d_anim_transform_add_v(Transform a, Transform b, float value) +{ + Transform result; + result.translation = Vector3Add(a.translation, Vector3Scale(b.translation, value)); + result.rotation = QuaternionAdd(a.rotation, QuaternionScale(b.rotation, value)); + result.scale = Vector3Add(a.scale, Vector3Scale(b.scale, value)); + return result; +} + +Transform r3d_anim_transform_addx_v(Transform a, Transform b, float value) +{ + Transform result; + result.translation = Vector3Add(a.translation, Vector3Scale(b.translation, value)); + result.rotation = QuaternionSlerp(a.rotation, b.rotation, value); + result.scale = Vector3Add(a.scale, Vector3Scale(b.scale, value)); + return result; +} + +Transform r3d_anim_transform_subtr(Transform a, Transform b) +{ + Transform result; + result.translation = Vector3Subtract(a.translation, b.translation); + result.rotation = QuaternionSubtract(a.rotation, b.rotation); + result.scale = Vector3Subtract(a.scale, b.scale); + return result; +} + +Transform r3d_anim_transform_scale(Transform tf, float val) +{ + Transform result; + result.translation = Vector3Scale(tf.translation, val); + result.rotation = QuaternionScale(tf.rotation, val); + result.scale = Vector3Scale(tf.scale, val); + return result; +} + +void r3d_anim_matrices_compute(R3D_AnimationPlayer* player) +{ + const R3D_BoneInfo* bones = player->skeleton.bones; + const Matrix rootBind = player->skeleton.rootBind; + const Matrix* localPose = player->localPose; + const int boneCount = player->skeleton.boneCount; + + Matrix* pose = player->modelPose; + for (int boneIdx = 0; boneIdx < boneCount; boneIdx++) { + int parentIdx = bones[boneIdx].parent; + Matrix parentPose = parentIdx >= 0 ? pose[parentIdx] : rootBind; + pose[boneIdx] = MatrixMultiply(localPose[boneIdx], parentPose); + } +} + +// ======================================== +// ANIMATION CHANNEL FUNCTIONS +// ======================================== + +const R3D_AnimationChannel* r3d_anim_channel_find(const R3D_Animation* anim, int boneIdx) +{ + for (int i = 0; i < anim->channelCount; i++) { + if (anim->channels[i].boneIndex == boneIdx) { + return &anim->channels[i]; + } + } + return NULL; +} + +Transform r3d_anim_channel_lerp(const R3D_AnimationChannel* channel, float time, Transform* rest0, Transform* restN) +{ + Transform result = { + .translation = {0.0f, 0.0f, 0.0f}, + .rotation = {0.0f, 0.0f, 0.0f, 1.0f}, + .scale = {1.0f, 1.0f, 1.0f} + }; + + if (channel->translation.count > 0) { + const Vector3* values = (const Vector3*)channel->translation.values; + uint32_t i0, i1; + float t; + find_key_frames( + channel->translation.times, + channel->translation.count, + time, &i0, &i1, &t + ); + result.translation = Vector3Lerp(values[i0], values[i1], t); + if (rest0) rest0->translation = values[0]; + if (restN) restN->translation = values[channel->translation.count-1]; + } + + if (channel->rotation.count > 0) { + const Quaternion* values = (const Quaternion*)channel->rotation.values; + uint32_t i0, i1; + float t; + find_key_frames( + channel->rotation.times, + channel->rotation.count, + time, &i0, &i1, &t + ); + result.rotation = QuaternionSlerp(values[i0], values[i1], t); + if (rest0) rest0->rotation = values[0]; + if (restN) restN->rotation = values[channel->rotation.count-1]; + } + + if (channel->scale.count > 0) { + const Vector3* values = (const Vector3*)channel->scale.values; + uint32_t i0, i1; + float t; + find_key_frames( + channel->scale.times, + channel->scale.count, + time, &i0, &i1, &t + ); + result.scale = Vector3Lerp(values[i0], values[i1], t); + if (rest0) rest0->scale = values[0]; + if (restN) restN->scale = values[channel->scale.count-1]; + } + + return result; +} + +``` + +`src/common/r3d_anim.h`: + +```h +/* r3d_anim.h -- Common R3D Animation Helper Functions + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_COMMON_ANIM_H +#define R3D_COMMON_ANIM_H + +#include + +#include "./r3d_math.h" + +// ======================================== +// TRANSFORM/MATRIX FUNCTIONS +// ======================================== + +Transform r3d_anim_transform_lerp(Transform a, Transform b, float value); +Transform r3d_anim_transform_add(Transform a, Transform b); +Transform r3d_anim_transform_add_v(Transform a, Transform b, float value); +Transform r3d_anim_transform_addx_v(Transform a, Transform b, float value); +Transform r3d_anim_transform_subtr(Transform a, Transform b); +Transform r3d_anim_transform_scale(Transform tf, float val); +void r3d_anim_matrices_compute(R3D_AnimationPlayer* player); + +// ======================================== +// ANIMATION CHANNEL FUNCTIONS +// ======================================== + +const R3D_AnimationChannel* r3d_anim_channel_find(const R3D_Animation* anim, int boneIdx); +Transform r3d_anim_channel_lerp(const R3D_AnimationChannel* channel, float time, Transform* rest0, Transform* restN); + +#endif // R3D_COMMON_ANIM_H + +``` + +`src/common/r3d_half.h`: + +```h +/* r3d_half.h -- Common R3D Half Floating Point + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_COMMON_HALF_H +#define R3D_COMMON_HALF_H + +#include + +/* === Types definitions === */ + +typedef uint16_t r3d_half_t; + +/* === Float type 16 bits === */ + +static inline uint16_t r3d_half_from_float_ui(uint32_t ui) +{ + int32_t s = (ui >> 16) & 0x8000; + int32_t em = ui & 0x7fffffff; + + // bias exponent and round to nearest; 112 is relative exponent bias (127-15) + int32_t h = (em - (112 << 23) + (1 << 12)) >> 13; + + // underflow: flush to zero; 113 encodes exponent -14 + h = (em < (113 << 23)) ? 0 : h; + + // overflow: infinity; 143 encodes exponent 16 + h = (em >= (143 << 23)) ? 0x7c00 : h; + + // NaN; note that we convert all types of NaN to qNaN + h = (em > (255 << 23)) ? 0x7e00 : h; + + return (uint16_t)(s | h); +} + +static inline uint32_t r3d_half_to_float_ui(uint16_t h) +{ + uint32_t s = (unsigned)(h & 0x8000) << 16; + int32_t em = h & 0x7fff; + + // bias exponent and pad mantissa with 0; 112 is relative exponent bias (127-15) + int32_t r = (em + (112 << 10)) << 13; + + // denormal: flush to zero + r = (em < (1 << 10)) ? 0 : r; + + // infinity/NaN; note that we preserve NaN payload as a byproduct of unifying inf/nan cases + // 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255 + r += (em >= (31 << 10)) ? (112 << 23) : 0; + + return s | r; +} + +static inline r3d_half_t r3d_half_from_float(float i) +{ + union { float f; uint32_t i; } v; + v.f = i; + return r3d_half_from_float_ui(v.i); +} + +static inline float r3d_half_to_float(r3d_half_t y) +{ + union { float f; uint32_t i; } v; + v.i = r3d_half_to_float_ui(y); + return v.f; +} + +#endif // R3D_COMMON_HALF_H + +``` + +`src/common/r3d_hash.h`: + +```h +/* r3d_hash.h -- Common R3D Hash Functions + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include + +// ======================================== +// CONSTANTS +// ======================================== + +#define R3D_HASH_FNV_OFFSET_BASIS_32 2166136261U +#define R3D_HASH_FNV_PRIME_32 16777619U + +#define R3D_HASH_FNV_OFFSET_BASIS_64 14695981039346656037ULL +#define R3D_HASH_FNV_PRIME_64 1099511628211ULL + +// ======================================== +// INLINED FUNCTIONS +// ======================================== + +static inline uint32_t r3d_hash_fnv1a_32(const void *data, size_t len) +{ + uint32_t hash = R3D_HASH_FNV_OFFSET_BASIS_32; + const uint8_t *ptr = (const uint8_t *)data; + for (size_t i = 0; i < len; i++) { + hash ^= ptr[i]; + hash *= R3D_HASH_FNV_PRIME_32; + } + return hash; +} + +static inline uint64_t r3d_hash_fnv1a_64(const void *data, size_t len) +{ + uint64_t hash = R3D_HASH_FNV_OFFSET_BASIS_64; + const uint8_t *ptr = (const uint8_t *)data; + for (size_t i = 0; i < len; i++) { + hash ^= ptr[i]; + hash *= R3D_HASH_FNV_PRIME_64; + } + return hash; +} + +static inline uint32_t r3d_hash_fnv1a_32_str(const char *str) +{ + uint32_t hash = R3D_HASH_FNV_OFFSET_BASIS_32; + while (*str) { + hash ^= (uint8_t)(*str++); + hash *= R3D_HASH_FNV_PRIME_32; + } + return hash; +} + +static inline uint64_t r3d_hash_fnv1a_64_str(const char *str) +{ + uint64_t hash = R3D_HASH_FNV_OFFSET_BASIS_64; + while (*str) { + hash ^= (uint8_t)(*str++); + hash *= R3D_HASH_FNV_PRIME_64; + } + return hash; +} + +``` + +`src/common/r3d_helper.c`: + +```c +/* r3d_helper.c -- Common R3D Helpers + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_helper.h" +#include +#include + +#ifdef _WIN32 +# define NOGDI +# define NOUSER +# define WIN32_LEAN_AND_MEAN +# include +# undef near +# undef far +#elif defined(__linux__) || defined(__APPLE__) +# include +#else +# error "Oops, platform not supported by R3D" +#endif + +// ======================================== +// HELPER FUNCTIONS +// ======================================== + +int r3d_get_cpu_count(void) +{ + static int numCPUs = 0; + + if (numCPUs > 0) { + return numCPUs; + } + + #ifdef _WIN32 + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + numCPUs = sysinfo.dwNumberOfProcessors; + #elif defined(__linux__) || defined(__APPLE__) + numCPUs = sysconf(_SC_NPROCESSORS_ONLN); + #endif + + if (numCPUs < 1) { + R3D_TRACELOG(LOG_WARNING, "Failed to detect CPU count, defaulting to 1 thread"); + numCPUs = 1; + } + + return numCPUs; +} + +``` + +`src/common/r3d_helper.h`: + +```h +/* r3d_helper.h -- Common R3D Helpers + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_COMMON_HELPER_H +#define R3D_COMMON_HELPER_H + +#include +#include +#include +#include + +#if defined(_MSC_VER) +# include +#endif + +// ======================================== +// HELPER MACROS +// ======================================== + +#ifndef MIN +# define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif + +#ifndef MAX +# define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +#ifndef CLAMP +# define CLAMP(v, min, max) ((v) < (min) ? (min) : ((v) > (max) ? (max) : (v))) +#endif + +#ifndef SATURATE +# define SATURATE(x) (CLAMP(x, 0.0f, 1.0f)) +#endif + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(arr) (sizeof((arr)) / sizeof((arr)[0])) +#endif + +#ifndef SWAP +# define SWAP(type, a, b) do { type _tmp = (a); (a) = (b); (b) = _tmp; } while (0) +#endif + +#ifndef BIT_SET +# define BIT_SET(v, m) ((v) |= (m)) +#endif + +#ifndef BIT_CLEAR +# define BIT_CLEAR(v, m) ((v) &= ~(m)) +#endif + +#ifndef BIT_TOGGLE +# define BIT_TOGGLE(v, m) ((v) ^= (m)) +#endif + +#ifndef BIT_TEST_ALL +# define BIT_TEST_ALL(v, m) (((v) & (m)) == (m)) +#endif + +#ifndef BIT_TEST_ANY +# define BIT_TEST_ANY(v, m) (((v) & (m)) != 0) +#endif + +#ifndef BIT_TEST +# define BIT_TEST(v, b) BIT_TEST_ANY(v, b) +#endif + +#ifndef MAX_OF +# define MAX_OF(x) _Generic((x), \ + char: CHAR_MAX, \ + signed char: SCHAR_MAX, \ + unsigned char: UCHAR_MAX, \ + short: SHRT_MAX, \ + unsigned short: USHRT_MAX, \ + int: INT_MAX, \ + unsigned int: UINT_MAX, \ + long: LONG_MAX, \ + unsigned long: ULONG_MAX, \ + long long: LLONG_MAX, \ + unsigned long long: ULLONG_MAX \ + ) +#endif + +// ======================================== +// HELPER FUNCTIONS +// ======================================== + +/* + * Returns the number of logical CPUs available to the system. + * The value is detected once and cached. + */ +int r3d_get_cpu_count(void); + +// ======================================== +// INLINED FUNCTIONS +// ======================================== + +static inline int r3d_string_format(char* dst, size_t dstSize, const char* fmt, ...) +{ + if (!dst || dstSize == 0) return -1; + + va_list args; + va_start(args, fmt); + int written = vsnprintf(dst, dstSize, fmt, args); + va_end(args); + + dst[dstSize - 1] = '\0'; + + return written; +} + +static inline bool r3d_is_absolute_path(const char* path) +{ +#ifdef _WIN32 + return (path[1] == ':') || (path[0] == '\\' && path[1] == '\\'); +#else + return path[0] == '/'; +#endif +} + +static inline int32_t r3d_get_mip_levels_1d(int32_t s) +{ + if (s <= 0) return 0; + +#if defined(__GNUC__) || defined(__clang__) + return 32 - __builtin_clz((unsigned)s); +#elif defined(_MSC_VER) + unsigned long index; + _BitScanReverse(&index, (unsigned long)s); + return (int32_t)index + 1; +#else + int32_t levels = 0; + while (s > 0) { + levels++; + s >>= 1; + } + return levels; +#endif +} + +static inline int32_t r3d_get_mip_levels_2d(int32_t w, int32_t h) +{ + return r3d_get_mip_levels_1d((w > h) ? w : h); +} + +static inline int32_t r3d_lsb_index(uint32_t value) +{ + if (value == 0) return -1; + +#if defined(__GNUC__) || defined(__clang__) + return __builtin_ctz(value); +#elif defined(_MSC_VER) + unsigned long index; + _BitScanForward(&index, (unsigned long)value); + return (int32_t)index; +#else + int32_t index = 0; + while ((value & 1) == 0) { + value >>= 1; + index++; + } + return index; +#endif +} + +static inline int r3d_align_offset(int offset, int align) +{ + return (offset + align - 1) & ~(align - 1); +} + +#endif // R3D_COMMON_HELPER_H + +``` + +`src/common/r3d_image.c`: + +```c +/* r3d_image.c -- Common R3D Image Functions + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_image.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "../modules/r3d_driver.h" + +// ======================================== +// IMAGE FUNCTIONS +// ======================================== + +Image r3d_image_compose_rgb(const Image* sources[3], Color defaultColor) +{ + Image image = { 0 }; + + /* --- Determine dimensions --- */ + + int w = 0, h = 0; + for (int i = 0; i < 3; i++) { + if (sources[i]) { + w = (w < sources[i]->width) ? sources[i]->width : w; + h = (h < sources[i]->height) ? sources[i]->height : h; + } + } + + if (w == 0 || h == 0) { + return image; + } + + /* --- Allocation --- */ + + uint8_t* pixels = RL_MALLOC(3 * w * h); + if (pixels == NULL) { + return image; + } + + /* --- Calculate scales --- */ + + int scaleX[3] = { 0 }; + int scaleY[3] = { 0 }; + for (int i = 0; i < 3; i++) { + if (sources[i] && sources[i]->width > 0 && sources[i]->height > 0) { + scaleX[i] = (sources[i]->width << 16) / w; + scaleY[i] = (sources[i]->height << 16) / h; + } + } + + /* --- Calculate bytes per pixels --- */ + + int bpp[3] = { + sources[0] ? GetPixelDataSize(1, 1, sources[0]->format) : 0, + sources[1] ? GetPixelDataSize(1, 1, sources[1]->format) : 0, + sources[2] ? GetPixelDataSize(1, 1, sources[2]->format) : 0, + }; + + /* --- Generate optimized loop based on source combination --- */ + + int mask = (sources[0] ? 1 : 0) | (sources[1] ? 2 : 0) | (sources[2] ? 4 : 0); + + #define SAMPLE_TEMPLATE(R_CODE, G_CODE, B_CODE) \ + for (int y = 0; y < h; y++) { \ + for (int x = 0; x < w; x++) { \ + Color color = defaultColor; \ + R_CODE; G_CODE; B_CODE; \ + int offset = 3 * (y * w + x); \ + pixels[offset + 0] = color.r; \ + pixels[offset + 1] = color.g; \ + pixels[offset + 2] = color.b; \ + } \ + } + + #define SAMPLE_R do { \ + int srcX = (x * scaleX[0]) >> 16; \ + int srcY = (y * scaleY[0]) >> 16; \ + if (srcX >= sources[0]->width) srcX = sources[0]->width - 1; \ + if (srcY >= sources[0]->height) srcY = sources[0]->height - 1; \ + Color src = GetPixelColor(((uint8_t*)sources[0]->data) + bpp[0] * (srcY * sources[0]->width + srcX), sources[0]->format); \ + color.r = src.r; \ + } while(0) + + #define SAMPLE_G do { \ + int srcX = (x * scaleX[1]) >> 16; \ + int srcY = (y * scaleY[1]) >> 16; \ + if (srcX >= sources[1]->width) srcX = sources[1]->width - 1; \ + if (srcY >= sources[1]->height) srcY = sources[1]->height - 1; \ + Color src = GetPixelColor(((uint8_t*)sources[1]->data) + bpp[1] * (srcY * sources[1]->width + srcX), sources[1]->format); \ + color.g = src.g; \ + } while(0) + + #define SAMPLE_B do { \ + int srcX = (x * scaleX[2]) >> 16; \ + int srcY = (y * scaleY[2]) >> 16; \ + if (srcX >= sources[2]->width) srcX = sources[2]->width - 1; \ + if (srcY >= sources[2]->height) srcY = sources[2]->height - 1; \ + Color src = GetPixelColor(((uint8_t*)sources[2]->data) + bpp[2] * (srcY * sources[2]->width + srcX), sources[2]->format); \ + color.b = src.b; \ + } while(0) + + #define NOOP do {} while(0) + + switch (mask) { + case 0: break; // No sources + case 1: SAMPLE_TEMPLATE(SAMPLE_R, NOOP, NOOP); break; + case 2: SAMPLE_TEMPLATE(NOOP, SAMPLE_G, NOOP); break; + case 3: SAMPLE_TEMPLATE(SAMPLE_R, SAMPLE_G, NOOP); break; + case 4: SAMPLE_TEMPLATE(NOOP, NOOP, SAMPLE_B); break; + case 5: SAMPLE_TEMPLATE(SAMPLE_R, NOOP, SAMPLE_B); break; + case 6: SAMPLE_TEMPLATE(NOOP, SAMPLE_G, SAMPLE_B); break; + case 7: SAMPLE_TEMPLATE(SAMPLE_R, SAMPLE_G, SAMPLE_B); break; + } + + #undef SAMPLE_TEMPLATE + #undef SAMPLE_R + #undef SAMPLE_G + #undef SAMPLE_B + #undef NOOP + + image.data = pixels; + image.width = w; + image.height = h; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; + image.mipmaps = 1; + + return image; +} + +// ======================================== +// TEXTURE FUNCTIONS +// ======================================== + +static void get_texture_format(int format, bool srgb, GLenum* glInternalFormat, GLenum* glFormat, GLenum* glType) +{ + // TODO: Add checks for support of compressed formats (consider WebGL 2 for later) + + *glInternalFormat = 0; + *glFormat = 0; + *glType = 0; + + switch (format) { + case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32: *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16: *glInternalFormat = GL_R16F; *glFormat = GL_RED; *glType = GL_HALF_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *glInternalFormat = GL_RGB16F; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: *glInternalFormat = GL_RGBA16F; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT; break; + case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; + case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA: *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + default: R3D_TRACELOG(RL_LOG_WARNING, "Current format not supported (%i)", format); break; + } + + if (srgb) { + switch (*glInternalFormat) { + case GL_RGBA8: *glInternalFormat = GL_SRGB8_ALPHA8; break; + case GL_RGB8: *glInternalFormat = GL_SRGB8; break; + // NOT SUPPORTED FOR NOW + case GL_COMPRESSED_RGBA_BPTC_UNORM: *glInternalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; break; + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR; break; + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR; break; + case GL_COMPRESSED_RGB8_ETC2: *glInternalFormat = GL_COMPRESSED_SRGB8_ETC2; break; + case GL_COMPRESSED_RGBA8_ETC2_EAC: *glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; break; + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: *glInternalFormat = GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2; break; + } + } +} + +static void upload_texture_mipmap(const uint8_t *data, int width, int height, int level, int format, bool srgb) +{ + GLenum glInternalFormat, glFormat, glType; + get_texture_format(format, srgb, &glInternalFormat, &glFormat, &glType); + + if (glInternalFormat == 0) return; + + if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) { + glTexImage2D( + GL_TEXTURE_2D, level, glInternalFormat, width, height, + 0, glFormat, glType, data + ); + } + else { + int size = GetPixelDataSize(width, height, format); + glCompressedTexImage2D( + GL_TEXTURE_2D, level, glInternalFormat, + width, height, 0, size, data + ); + } +} + +static void set_texture_swizzle(int format) +{ + static const GLint grayscale[] = {GL_RED, GL_RED, GL_RED, GL_ONE}; + static const GLint gray_alpha[] = {GL_RED, GL_RED, GL_RED, GL_GREEN}; + + const GLint *mask = NULL; + if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) mask = grayscale; + else if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) mask = gray_alpha; + + if (mask) { + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask); + } +} + +static void set_texture_wrap(TextureWrap wrap) +{ + static const GLenum wrap_modes[] = { + [TEXTURE_WRAP_REPEAT] = GL_REPEAT, + [TEXTURE_WRAP_CLAMP] = GL_CLAMP_TO_EDGE, + [TEXTURE_WRAP_MIRROR_REPEAT] = GL_MIRRORED_REPEAT, + [TEXTURE_WRAP_MIRROR_CLAMP] = GL_MIRROR_CLAMP_TO_EDGE + }; + + if (wrap < sizeof(wrap_modes) / sizeof(wrap_modes[0]) && wrap_modes[wrap]) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_modes[wrap]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_modes[wrap]); + } +} + +static void set_texture_filter(TextureFilter filter) +{ + typedef struct { + GLenum mag, min; + float aniso; + } FilterMode; + + static const FilterMode modes[] = { + [TEXTURE_FILTER_POINT] = {GL_NEAREST, GL_NEAREST, 0}, + [TEXTURE_FILTER_BILINEAR] = {GL_LINEAR, GL_LINEAR, 0}, + [TEXTURE_FILTER_TRILINEAR] = {GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, 0}, + [TEXTURE_FILTER_ANISOTROPIC_4X] = {GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, 4.0f}, + [TEXTURE_FILTER_ANISOTROPIC_8X] = {GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, 8.0f}, + [TEXTURE_FILTER_ANISOTROPIC_16X] = {GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, 16.0f} + }; + + if (filter < sizeof(modes) / sizeof(modes[0]) && modes[filter].mag) { + const FilterMode *m = &modes[filter]; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m->mag); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m->min); + if (m->aniso > 0) { + float maxAniso = 1.0f; + if (r3d_driver_has_anisotropy(&maxAniso)) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fminf(m->aniso, maxAniso)); + } + } + } +} + +Texture2D r3d_image_upload(const Image* image, TextureWrap wrap, TextureFilter filter, bool srgb) +{ + GLuint id = 0; + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + const uint8_t *dataPtr = image->data; + int mipW = image->width, mipH = image->height; + + for (int i = 0; i < image->mipmaps; i++) { + upload_texture_mipmap(dataPtr, mipW, mipH, i, image->format, srgb); + if (i == 0) set_texture_swizzle(image->format); + + int mipSize = GetPixelDataSize(mipW, mipH, image->format); + if (dataPtr) dataPtr += mipSize; + + mipW = (mipW > 1) ? mipW / 2 : 1; + mipH = (mipH > 1) ? mipH / 2 : 1; + } + + if (image->mipmaps == 1 && filter >= TEXTURE_FILTER_TRILINEAR) { + glGenerateMipmap(GL_TEXTURE_2D); + } + + set_texture_wrap(wrap); + set_texture_filter(filter); + glBindTexture(GL_TEXTURE_2D, 0); + + return (Texture2D) { + .id = id, + .width = image->width, + .height = image->height, + .mipmaps = image->mipmaps, + .format = image->format + }; +} + +``` + +`src/common/r3d_image.h`: + +```h +/* r3d_image.h -- Common R3D Image Functions + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_COMMON_IMAGE_H +#define R3D_COMMON_IMAGE_H + +#include + +// ======================================== +// IMAGE FUNCTIONS +// ======================================== + +/** + * Build an RGB image from up to 3 grayscale sources (R,G,B). + * Missing channels use defaultColor. + * Output size = max width/height of non-NULL inputs. + * Channels are resampled with nearest-neighbor (16.16 fixed-point). + */ +Image r3d_image_compose_rgb(const Image* sources[3], Color defaultColor); + +// ======================================== +// TEXTURE FUNCTIONS +// ======================================== + +/** + * Uploads the given image to a 2D texture, setting wrap and filter. + * The srgb flag tries to load the texture in an sRGB format when supported. + */ +Texture2D r3d_image_upload(const Image* image, TextureWrap wrap, TextureFilter filter, bool srgb); + +#endif // R3D_COMMON_IMAGE_H + +``` + +`src/common/r3d_math.h`: + +```h +/* r3d_math.h -- Common R3D Math + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_COMMON_MATH_H +#define R3D_COMMON_MATH_H + +#include +#include +#include +#include + +#include "./r3d_helper.h" + +// ======================================== +// DEFINITIONS AND CONSTANTS +// ======================================== + +#ifndef R3D_RESTRICT +# if defined(_MSC_VER) +# define R3D_RESTRICT __restrict +# else +# define R3D_RESTRICT restrict +# endif +#endif + +#define R3D_LOG2 0.6931471805599453f +#define R3D_LOG018 -1.7147984280919266f + +#define R3D_SRGB_ALPHA (0.055f) +#define R3D_SRGB_INV_ALPHA (1.0f / 1.055f) +#define R3D_SRGB_GAMMA (2.4f) +#define R3D_SRGB_INV_GAMMA (1.0f / 2.4f) +#define R3D_SRGB_LINEAR_THRESHOLD (0.04045f) +#define R3D_SRGB_NONLINEAR_THRESHOLD (0.0031308f) +#define R3D_SRGB_LINEAR_FACTOR (1.0f / 12.92f) + +#define R3D_MATRIX_IDENTITY \ + (Matrix) { \ + 1.0f, 0.0f, 0.0f, 0.0f, \ + 0.0f, 1.0f, 0.0f, 0.0f, \ + 0.0f, 0.0f, 1.0f, 0.0f, \ + 0.0f, 0.0f, 0.0f, 1.0f, \ + } + +#define R3D_AABB_UNIT \ + (BoundingBox) { \ + .min = {-0.5f, -0.5f, -0.5f}, \ + .max = {+0.5f, +0.5f, +0.5f}, \ + } + +// ======================================== +// HELPER TYPES +// ======================================== + +typedef struct { + int x, y; + int w, h; +} r3d_rect_t; + +// ======================================== +// COLOR FUNCTIONS +// ======================================== + +static inline Vector3 r3d_color_to_vec3(Color color) +{ + return (Vector3) { + color.r * (1.0f / 255.0f), + color.g * (1.0f / 255.0f), + color.b * (1.0f / 255.0f) + }; +} + +static inline Vector4 r3d_color_to_vec4(Color color) +{ + return (Vector4) { + color.r * (1.0f / 255.0f), + color.g * (1.0f / 255.0f), + color.b * (1.0f / 255.0f), + color.a * (1.0f / 255.0f) + }; +} + +static inline float r3d_srgb8_to_linear(uint8_t srgb8) +{ + float srgb = srgb8 * (1.0f / 255.0f); + + return (srgb <= R3D_SRGB_LINEAR_THRESHOLD) + ? srgb * R3D_SRGB_LINEAR_FACTOR + : powf((srgb + R3D_SRGB_ALPHA) * R3D_SRGB_INV_ALPHA, R3D_SRGB_GAMMA); +} + +static inline uint8_t r3d_linear_to_srgb8(float linear) +{ + float srgb = (linear <= R3D_SRGB_NONLINEAR_THRESHOLD) + ? 12.92f * linear + : (1.0f + R3D_SRGB_ALPHA) * powf(linear, R3D_SRGB_INV_GAMMA) - R3D_SRGB_ALPHA; + + return (uint8_t)(SATURATE(srgb) * 255.0f + 0.5f); +} + +static inline Vector3 r3d_color_srgb_to_linear_vec3(Color color) +{ + return (Vector3) { + r3d_srgb8_to_linear(color.r), + r3d_srgb8_to_linear(color.g), + r3d_srgb8_to_linear(color.b) + }; +} + +static inline Vector4 r3d_color_srgb_to_linear_vec4(Color color) +{ + return (Vector4) { + r3d_srgb8_to_linear(color.r), + r3d_srgb8_to_linear(color.g), + r3d_srgb8_to_linear(color.b), + color.a * (1.0f / 255.0f) + }; +} + +static inline Color r3d_color_linear_to_srgb_vec3(Vector3 linear) +{ + return (Color) { + r3d_linear_to_srgb8(linear.x), + r3d_linear_to_srgb8(linear.y), + r3d_linear_to_srgb8(linear.z), + 255 + }; +} + +static inline Color r3d_color_linear_to_srgb_vec4(Vector4 linear) +{ + return (Color) { + r3d_linear_to_srgb8(linear.x), + r3d_linear_to_srgb8(linear.y), + r3d_linear_to_srgb8(linear.z), + (uint8_t)(SATURATE(linear.w) * 255.0f + 0.5f) + }; +} + +static inline Vector3 r3d_color_to_linear_vec3(Color color, R3D_ColorSpace space) +{ + switch (space) { + case R3D_COLORSPACE_SRGB: return r3d_color_srgb_to_linear_vec3(color); + default: break; + } + + return r3d_color_to_vec3(color); +} + +static inline Vector4 r3d_color_to_linear_vec4(Color color, R3D_ColorSpace space) +{ + switch (space) { + case R3D_COLORSPACE_SRGB: return r3d_color_srgb_to_linear_vec4(color); + default: break; + } + + return r3d_color_to_vec4(color); +} + +static inline Vector3 r3d_color_to_linear_scaled_vec3(Color color, R3D_ColorSpace space, float scale) +{ + Vector3 result = r3d_color_to_linear_vec3(color, space); + result.x *= scale; + result.y *= scale; + result.z *= scale; + return result; +} + +static inline Vector4 r3d_color_to_linear_scaled_vec4(Color color, R3D_ColorSpace space, float scale) +{ + Vector4 result = r3d_color_to_linear_vec4(color, space); + result.x *= scale; + result.y *= scale; + result.z *= scale; + return result; +} + +// ======================================== +// VECTOR FUNCTIONS +// ======================================== + +static inline float r3d_vector3_len_sq(Vector3 v) +{ + return v.x*v.x + v.y*v.y + v.z*v.z; +} + +static inline Vector3 r3d_vector3_normalize_or(Vector3 v, Vector3 fallback) +{ + float len_sqr = v.x*v.x + v.y*v.y + v.z*v.z; + + if (len_sqr <= 1e-12f) { + return fallback; + } + + float inv_len = 1.0f / sqrtf(len_sqr); + + return (Vector3) { + v.x * inv_len, + v.y * inv_len, + v.z * inv_len, + }; +} + +static inline Vector3 r3d_vector3_transform(Vector3 v, const Matrix* m) +{ + float x = v.x, y = v.y, z = v.z; + return (Vector3){ + m->m0 * x + m->m4 * y + m->m8 * z + m->m12, + m->m1 * x + m->m5 * y + m->m9 * z + m->m13, + m->m2 * x + m->m6 * y + m->m10 * z + m->m14 + }; +} + +static inline Vector3 r3d_vector3_transform_normal(Vector3 v, const Matrix* m) +{ + float x = v.x, y = v.y, z = v.z; + return (Vector3){ + m->m0 * x + m->m4 * y + m->m8 * z, + m->m1 * x + m->m5 * y + m->m9 * z, + m->m2 * x + m->m6 * y + m->m10 * z + }; +} + +static inline Vector3 r3d_vector3_transform_linear(Vector3 v, const Matrix* m) +{ + float x = v.x, y = v.y, z = v.z; + return (Vector3){ + m->m0 * x + m->m4 * y + m->m8 * z, + m->m1 * x + m->m5 * y + m->m9 * z, + m->m2 * x + m->m6 * y + m->m10 * z + }; +} + +static inline Vector4 r3d_vector4_transform(Vector4 v, const Matrix* m) +{ + float x = v.x, y = v.y, z = v.z, w = v.w; + return (Vector4){ + m->m0 * x + m->m4 * y + m->m8 * z + m->m12 * w, + m->m1 * x + m->m5 * y + m->m9 * z + m->m13 * w, + m->m2 * x + m->m6 * y + m->m10 * z + m->m14 * w, + m->m3 * x + m->m7 * y + m->m11 * z + m->m15 * w + }; +} + +static inline Quaternion r3d_quaternion_normalize_or_id(Quaternion q) +{ + float len_sqr = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; + + if (len_sqr <= 1e-12f) { + return (Quaternion) {0, 0, 0, 1}; + } + + float inv_len = 1.0f / sqrtf(len_sqr); + + return (Quaternion) { + q.x * inv_len, + q.y * inv_len, + q.z * inv_len, + q.w * inv_len, + }; +} + +static inline Quaternion r3d_quaternion_from_axes(Vector3 right, Vector3 up, Vector3 back) +{ + // Matrix columns are the world-space directions of local +X, +Y, +Z. + Matrix m = { + right.x, right.y, right.z, 0.0f, + up.x, up.y, up.z, 0.0f, + back.x, back.y, back.z, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + return r3d_quaternion_normalize_or_id(QuaternionFromMatrix(m)); +} + +// ======================================== +// MATRIX FUNCTIONS +// ======================================== + +static inline bool r3d_matrix_is_identity(Matrix matrix) +{ + return (0 == memcmp(&matrix, &R3D_MATRIX_IDENTITY, sizeof(Matrix))); +} + +static inline Matrix r3d_matrix_st(Vector3 scale, Vector3 translate) +{ + return (Matrix) { + scale.x, 0.0f, 0.0f, translate.x, + 0.0f, scale.y, 0.0f, translate.y, + 0.0f, 0.0f, scale.z, translate.z, + 0.0f, 0.0f, 0.0f, 1.0f + }; +} + +static inline Matrix r3d_matrix_srt_axis(Vector3 scale, Vector4 axis, Vector3 translate) +{ + float axisLen = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); + if (axisLen < 1e-6f) { + return r3d_matrix_st(scale, translate); + } + + float invLen = 1.0f / axisLen; + float x = axis.x * invLen; + float y = axis.y * invLen; + float z = axis.z * invLen; + float angle = axis.w; + + float c = cosf(angle); + float s = sinf(angle); + float oneMinusC = 1.0f - c; + + float xx = x * x, yy = y * y, zz = z * z; + float xy = x * y, xz = x * z, yz = y * z; + float xs = x * s, ys = y * s, zs = z * s; + + return (Matrix) { + scale.x * (c + xx * oneMinusC), scale.x * (xy * oneMinusC - zs), scale.x * (xz * oneMinusC + ys), translate.x, + scale.y * (xy * oneMinusC + zs), scale.y * (c + yy * oneMinusC), scale.y * (yz * oneMinusC - xs), translate.y, + scale.z * (xz * oneMinusC - ys), scale.z * (yz * oneMinusC + xs), scale.z * (c + zz * oneMinusC), translate.z, + 0,0,0,1 + }; +} + +static inline Matrix r3d_matrix_srt_euler(Vector3 scale, Vector3 euler, Vector3 translate) +{ + float cx = cosf(euler.x), sx = sinf(euler.x); + float cy = cosf(euler.y), sy = sinf(euler.y); + float cz = cosf(euler.z), sz = sinf(euler.z); + + float czcx = cz * cx, czsx = cz * sx; + float szcx = sz * cx, szsx = sz * sx; + + return (Matrix) { + scale.x * (cy*cz), scale.x * (-cy*sz), scale.x * sy, translate.x, + scale.y * (sx*sy*cz + cx*sz), scale.y * (-sx*sy*sz + cx*cz), scale.y * (-sx*cy), translate.y, + scale.z * (-cx*sy*cz + sx*sz), scale.z * (cx*sy*sz + sx*cz), scale.z * (cx*cy), translate.z, + 0,0,0,1 + }; +} + +static inline Matrix r3d_matrix_srt_quat(Vector3 scale, Quaternion quat, Vector3 translate) +{ + float qlen = sqrtf(quat.x*quat.x + quat.y*quat.y + quat.z*quat.z + quat.w*quat.w); + if (qlen < 1e-6f) { + return r3d_matrix_st(scale, translate); + } + + float invLen = 1.0f / qlen; + float qx = quat.x * invLen; + float qy = quat.y * invLen; + float qz = quat.z * invLen; + float qw = quat.w * invLen; + + float qx2 = qx * qx, qy2 = qy * qy, qz2 = qz * qz; + float qxqy = qx * qy, qxqz = qx * qz, qxqw = qx * qw; + float qyqz = qy * qz, qyqw = qy * qw, qzqw = qz * qw; + + float r00 = 1.0f - 2.0f * (qy2 + qz2); + float r01 = 2.0f * (qxqy - qzqw); + float r02 = 2.0f * (qxqz + qyqw); + + float r10 = 2.0f * (qxqy + qzqw); + float r11 = 1.0f - 2.0f * (qx2 + qz2); + float r12 = 2.0f * (qyqz - qxqw); + + float r20 = 2.0f * (qxqz - qyqw); + float r21 = 2.0f * (qyqz + qxqw); + float r22 = 1.0f - 2.0f * (qx2 + qy2); + + return (Matrix) { + r00 * scale.x, r01 * scale.y, r02 * scale.z, translate.x, + r10 * scale.x, r11 * scale.y, r12 * scale.z, translate.y, + r20 * scale.x, r21 * scale.y, r22 * scale.z, translate.z, + 0, 0, 0, 1 + }; +} + +static inline Matrix r3d_matrix_normal(const Matrix* transform) +{ + Matrix result = {0}; + + float a00 = transform->m0, a01 = transform->m1, a02 = transform->m2, a03 = transform->m3; + float a10 = transform->m4, a11 = transform->m5, a12 = transform->m6, a13 = transform->m7; + float a20 = transform->m8, a21 = transform->m9, a22 = transform->m10, a23 = transform->m11; + float a30 = transform->m12, a31 = transform->m13, a32 = transform->m14, a33 = transform->m15; + + float b00 = a00*a11 - a01*a10; + float b01 = a00*a12 - a02*a10; + float b02 = a00*a13 - a03*a10; + float b03 = a01*a12 - a02*a11; + float b04 = a01*a13 - a03*a11; + float b05 = a02*a13 - a03*a12; + float b06 = a20*a31 - a21*a30; + float b07 = a20*a32 - a22*a30; + float b08 = a20*a33 - a23*a30; + float b09 = a21*a32 - a22*a31; + float b10 = a21*a33 - a23*a31; + float b11 = a22*a33 - a23*a32; + + float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); + + result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; + result.m1 = (-a10*b11 + a12*b08 - a13*b07)*invDet; + result.m2 = (a10*b10 - a11*b08 + a13*b06)*invDet; + + result.m4 = (-a01*b11 + a02*b10 - a03*b09)*invDet; + result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; + result.m6 = (-a00*b10 + a01*b08 - a03*b06)*invDet; + + result.m8 = (a31*b05 - a32*b04 + a33*b03)*invDet; + result.m9 = (-a30*b05 + a32*b02 - a33*b01)*invDet; + result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; + + result.m15 = 1.0f; + + return result; +} + +#endif // R3D_COMMON_MATH_H + +``` + +`src/common/r3d_pass.c`: + +```c +/* r3d_pass.c -- Common R3D Passes + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_pass.h" +#include +#include +#include + +#include "../r3d_core_state.h" + +#include "../common/r3d_helper.h" + +#include "../modules/r3d_driver.h" +#include "../modules/r3d_shader.h" +#include "../modules/r3d_render.h" +#include "../modules/r3d_env.h" + +// ======================================== +// COMMON ENVIRONMENT GENERATION +// ======================================== + +void r3d_pass_prepare_irradiance(int layerMap, GLuint srcCubemap, int srcSize) +{ + Matrix matProj = MatrixPerspective(90.0 * DEG2RAD, 1.0, 0.1, 10.0); + + R3D_SHADER_USE(prepare.cubemapIrradiance); + r3d_driver_disable(GL_DEPTH_TEST); + r3d_driver_disable(GL_CULL_FACE); + + R3D_SHADER_BIND_SAMPLER(prepare.cubemapIrradiance, uSourceTex, srcCubemap); + R3D_SHADER_SET_MAT4(prepare.cubemapIrradiance, uMatProj, matProj); + + for (int i = 0; i < 6; i++) { + r3d_env_irradiance_bind_fbo(layerMap, i); + R3D_SHADER_SET_MAT4(prepare.cubemapIrradiance, uMatView, R3D.matCubeViews[i]); + R3D_RENDER_CUBE(); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + r3d_driver_enable(GL_CULL_FACE); +} + +void r3d_pass_prepare_prefilter(int layerMap, GLuint srcCubemap, int srcSize) +{ + Matrix matProj = MatrixPerspective(90.0 * DEG2RAD, 1.0, 0.1, 10.0); + int srcNumLevels = r3d_get_mip_levels_1d(srcSize); + + R3D_SHADER_USE(prepare.cubemapPrefilter); + r3d_driver_disable(GL_DEPTH_TEST); + r3d_driver_disable(GL_CULL_FACE); + + R3D_SHADER_BIND_SAMPLER(prepare.cubemapPrefilter, uSourceTex, srcCubemap); + + R3D_SHADER_SET_FLOAT(prepare.cubemapPrefilter, uSourceNumLevels, (float)srcNumLevels); + R3D_SHADER_SET_FLOAT(prepare.cubemapPrefilter, uSourceFaceSize, (float)srcSize); + R3D_SHADER_SET_MAT4(prepare.cubemapPrefilter, uMatProj, matProj); + + int numLevels = r3d_get_mip_levels_1d(R3D_CUBEMAP_PREFILTER_SIZE); + + for (int mip = 0; mip < numLevels; mip++) { + float roughness = (float)mip / (float)(numLevels - 1); + R3D_SHADER_SET_FLOAT(prepare.cubemapPrefilter, uRoughness, roughness); + + for (int i = 0; i < 6; i++) { + r3d_env_prefilter_bind_fbo(layerMap, i, mip); + R3D_SHADER_SET_MAT4(prepare.cubemapPrefilter, uMatView, R3D.matCubeViews[i]); + R3D_RENDER_CUBE(); + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + r3d_driver_enable(GL_CULL_FACE); +} + +``` + +`src/common/r3d_pass.h`: + +```h +/* r3d_pass.h -- Common R3D Passes + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_COMMON_PASS_H +#define R3D_COMMON_PASS_H + +#include +#include + +// ======================================== +// COMMON ENVIRONMENT GENERATION +// ======================================== + +void r3d_pass_prepare_irradiance(int layerMap, GLuint srcCubemap, int srcSize); +void r3d_pass_prepare_prefilter(int layerMap, GLuint srcCubemap, int srcSize); + +#endif // R3D_COMMON_PASS_H + +``` + +`src/common/r3d_rshade.h`: + +```h +/* r3d_rshade.h -- Common Ray-Shade functions + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_RSHADE_H +#define R3D_RSHADE_H + +#include +#include +#include +#include +#include +#include + +#include "./r3d_helper.h" + +// ======================================== +// CONSTANTS +// ======================================== + +#define R3D_RSHADE_MAX_VAR_TYPE_LENGTH 32 +#define R3D_RSHADE_MAX_VAR_NAME_LENGTH 64 + +// ======================================== +// HELPER MACROS +// ======================================== + +// NOTE: All macros are undef at the end + +#define IS_SPACE(c) isspace((unsigned char)(c)) + +// ======================================== +// STRUCT TYPES +// ======================================== + +typedef struct { + char qualifier[16]; + char type[R3D_RSHADE_MAX_VAR_TYPE_LENGTH]; + char name[R3D_RSHADE_MAX_VAR_NAME_LENGTH]; +} r3d_rshade_varying_t; + +typedef struct { + char name[R3D_RSHADE_MAX_VAR_NAME_LENGTH]; + GLenum target; + GLuint texture; +} r3d_rshade_sampler_t; + +typedef struct { + char type[R3D_RSHADE_MAX_VAR_TYPE_LENGTH]; + char name[R3D_RSHADE_MAX_VAR_NAME_LENGTH]; + int offset; + int size; +} r3d_rshade_uniform_t; + +typedef struct { + r3d_rshade_uniform_t entries[R3D_MAX_SHADER_UNIFORMS]; + uint8_t buffer[R3D_MAX_SHADER_UNIFORMS * 64]; + GLuint bufferId; + int bufferSize; + bool dirty; +} r3d_rshade_uniform_buffer_t; + +typedef struct { + const char* bodyStart; + const char* bodyEnd; +} r3d_rshade_parsed_function_t; + +typedef struct { + char* ptr; + size_t remaining; + bool overflow; +} r3d_rshade_writer_t; + +// ======================================== +// INLINE FUNCTIONS +// ======================================== + +/* Returns the size in bytes of a GLSL type (std140 layout) */ +static inline int r3d_rshade_get_type_size(const char* type) +{ + // Scalar types + if (strcmp(type, "bool") == 0) return 4; + if (strcmp(type, "int") == 0) return 4; + if (strcmp(type, "float") == 0) return 4; + + // Integer vectors + if (strcmp(type, "ivec2") == 0) return 8; + if (strcmp(type, "ivec3") == 0) return 12; + if (strcmp(type, "ivec4") == 0) return 16; + + // Float vectors + if (strcmp(type, "vec2") == 0) return 8; + if (strcmp(type, "vec3") == 0) return 12; + if (strcmp(type, "vec4") == 0) return 16; + + // Matrices + if (strcmp(type, "mat2") == 0) return 32; + if (strcmp(type, "mat3") == 0) return 48; + if (strcmp(type, "mat4") == 0) return 64; + + return 0; +} + +/* Returns the OpenGL texture target for a GLSL sampler type */ +static inline GLenum r3d_rshade_get_sampler_target(const char* type) +{ + if (strcmp(type, "sampler1D") == 0) return GL_TEXTURE_1D; + if (strcmp(type, "sampler2D") == 0) return GL_TEXTURE_2D; + if (strcmp(type, "sampler3D") == 0) return GL_TEXTURE_3D; + if (strcmp(type, "samplerCube") == 0) return GL_TEXTURE_CUBE_MAP; + + return 0; +} + +/* Returns the std140 alignment requirement for a given type size + * Only supports 4-byte scalars (scalar_sz=4; max_align=scalar_sz << 2) */ +static inline int r3d_rshade_get_std140_alignment(int size) +{ + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + return size > 16 ? 16 : size; +} + +/* Skip to next semicolon and advance past it */ +static inline void r3d_rshade_skip_to_semicolon(const char** ptr) +{ + while (**ptr && **ptr != ';') (*ptr)++; + if (**ptr == ';') (*ptr)++; +} + +/* Skip to end of line */ +static inline void r3d_rshade_skip_to_end_of_line(const char** ptr) +{ + while (**ptr && **ptr != '\n' && **ptr != '\r') (*ptr)++; + if (**ptr == '\r') (*ptr)++; + if (**ptr == '\n') (*ptr)++; +} + +/* Skip to opening brace */ +static inline void r3d_rshade_skip_to_brace(const char** ptr) +{ + while (**ptr && **ptr != '{') (*ptr)++; +} + +/* Skip to the matching closing brace */ +static inline void r3d_rshade_skip_to_matching_brace(const char** ptr) +{ + int depth = 0; + while (**ptr) { + if (**ptr == '{') depth++; + else if (**ptr == '}') { + if (--depth == 0) { + (*ptr)++; + return; + } + } + (*ptr)++; + } +} + +/* Skip whitespace characters */ +static inline void r3d_rshade_skip_whitespace(const char** ptr) +{ + while (**ptr && IS_SPACE(**ptr)) (*ptr)++; +} + +/* Skip whitespace and all comment types (single-line and multi-line) */ +static inline void r3d_rshade_skip_whitespace_and_comments(const char** ptr) +{ + while (**ptr) + { + if (IS_SPACE(**ptr)) { + (*ptr)++; + continue; + } + + if (strncmp(*ptr, "//", 2) == 0) { + r3d_rshade_skip_to_end_of_line(ptr); + continue; + } + + if (strncmp(*ptr, "/*", 2) == 0) { + *ptr += 2; + while (**ptr && strncmp(*ptr, "*/", 2) != 0) (*ptr)++; + if (**ptr) *ptr += 2; + continue; + } + + break; + } +} + +/* Check if current position matches a keyword followed by whitespace */ +static inline bool r3d_rshade_match_keyword(const char* ptr, const char* keyword, size_t len) +{ + return strncmp(ptr, keyword, len) == 0 && IS_SPACE(ptr[len]); +} + +/* Check if current position is a varying keyword (varying, flat, smooth, noperspective) */ +static inline bool r3d_rshade_match_varying_keyword(const char* ptr) +{ + return r3d_rshade_match_keyword(ptr, "varying", 7) || + r3d_rshade_match_keyword(ptr, "flat", 4) || + r3d_rshade_match_keyword(ptr, "smooth", 6) || + r3d_rshade_match_keyword(ptr, "noperspective", 13); +} + +/* Parse an identifier (stops at whitespace, semicolon, or bracket) */ +static inline bool r3d_rshade_parse_identifier(const char** ptr, char* output, size_t maxLen) +{ + r3d_rshade_skip_whitespace(ptr); + + size_t i = 0; + while (**ptr && !IS_SPACE(**ptr) && **ptr != ';' && **ptr != '[' && i < maxLen - 1) { + output[i++] = *(*ptr)++; + } + output[i] = '\0'; + + return i > 0; +} + +/* Parse a GLSL declaration (type + name) and advance past semicolon */ +static inline bool r3d_rshade_parse_declaration(const char** ptr, char* type, char* name) +{ + if (!r3d_rshade_parse_identifier(ptr, type, R3D_RSHADE_MAX_VAR_TYPE_LENGTH)) { + r3d_rshade_skip_to_semicolon(ptr); + return false; + } + + if (!r3d_rshade_parse_identifier(ptr, name, R3D_RSHADE_MAX_VAR_NAME_LENGTH)) { + r3d_rshade_skip_to_semicolon(ptr); + return false; + } + + r3d_rshade_skip_to_semicolon(ptr); + return true; +} + +/* Count comma-separated arguments at depth 1 inside a constructor. + * ptr must point to the character just after the opening '('. */ +static inline int r3d_rshade_count_constructor_args(const char* ptr) +{ + int depth = 1; + int count = 0; + bool hasContent = false; + + while (*ptr && depth > 0) + { + if (*ptr == '(') { + depth++; + hasContent = true; + } + else if (*ptr == ')') { + if (depth == 1 && hasContent) count++; + depth--; + } + else if (*ptr == ',' && depth == 1) { + count++; + hasContent = false; + } + else if (!IS_SPACE(*ptr)) { + hasContent = true; + } + ptr++; + } + + return count; +} + +/* Parse a single scalar token (float or int) from *ptr without advancing it. */ +static inline void r3d_rshade_read_scalar(const char* ptr, bool isFloat, float* fOut, int32_t* iOut) +{ + if (isFloat) { + float v = 0.0f; + sscanf(ptr, "%f", &v); + *fOut = v; + } + else { + int32_t v = 0; + sscanf(ptr, "%d", &v); + *iOut = v; + } +} + +/* Parse a GLSL literal value and write it into the uniform buffer at given offset. + * Returns true if a default value was found and parsed. */ +static inline bool r3d_rshade_parse_default_value(const char** ptr, + const char* type, uint8_t* buffer, int offset) +{ + r3d_rshade_skip_whitespace(ptr); + if (**ptr != '=') return false; + + (*ptr)++; + r3d_rshade_skip_whitespace(ptr); + + uint8_t* dst = buffer + offset; + + // Scalars + if (strcmp(type, "float") == 0) { + float v = 0.0f; sscanf(*ptr, "%f", &v); + memcpy(dst, &v, 4); + return true; + } + if (strcmp(type, "int") == 0) { + int v = 0; sscanf(*ptr, "%d", &v); + memcpy(dst, &v, 4); + return true; + } + if (strcmp(type, "bool") == 0) { + // Accept both true/false keywords and 1/0 literals + int v = (strncmp(*ptr, "true", 4) == 0) ? 1 + : (strncmp(*ptr, "false", 5) == 0) ? 0 + : ((*ptr)[0] != '0'); + memcpy(dst, &v, 4); + return true; + } + + // Vectors and matrices + // std140: each column occupies a 16-byte slot (vec4 stride), + // so the byte offset of component [col][row] is: col * 16 + row * 4. + // For vectors (cols=1) this degenerates to simple contiguous layout. + + int cols = 0, rows = 0; + bool isFloat = true; + + if (strcmp(type, "vec2") == 0) { cols = 1; rows = 2; } + else if (strcmp(type, "vec3") == 0) { cols = 1; rows = 3; } + else if (strcmp(type, "vec4") == 0) { cols = 1; rows = 4; } + else if (strcmp(type, "ivec2") == 0) { cols = 1; rows = 2; isFloat = false; } + else if (strcmp(type, "ivec3") == 0) { cols = 1; rows = 3; isFloat = false; } + else if (strcmp(type, "ivec4") == 0) { cols = 1; rows = 4; isFloat = false; } + else if (strcmp(type, "mat2") == 0) { cols = 2; rows = 2; } + else if (strcmp(type, "mat3") == 0) { cols = 3; rows = 3; } + else if (strcmp(type, "mat4") == 0) { cols = 4; rows = 4; } + + if (cols == 0) return false; + + // Advance past the constructor name to '(' + while (**ptr && **ptr != '(') (*ptr)++; + if (**ptr != '(') return false; + (*ptr)++; + + int argCount = r3d_rshade_count_constructor_args(*ptr); + + if (argCount == 1) { + // Single-scalar constructor + r3d_rshade_skip_whitespace(ptr); + + float fScalar = 0.0f; + int32_t iScalar = 0; + r3d_rshade_read_scalar(*ptr, isFloat, &fScalar, &iScalar); + + for (int c = 0; c < cols; c++) { + for (int r = 0; r < rows; r++) { + int byteOffset = c * 16 + r * 4; + + /* For matrices, only the diagonal gets the scalar value. + * For vectors (cols == 1), every component gets it. */ + bool active = (cols == 1) || (r == c); + + if (isFloat) { + float v = active ? fScalar : 0.0f; + memcpy(dst + byteOffset, &v, 4); + } + else { + int32_t v = active ? iScalar : 0; + memcpy(dst + byteOffset, &v, 4); + } + } + } + + // Skip to closing ')' + while (**ptr && **ptr != ')') (*ptr)++; + if (**ptr == ')') (*ptr)++; + } + else { + // Full component list + for (int c = 0; c < cols; c++) { + for (int r = 0; r < rows; r++) { + r3d_rshade_skip_whitespace(ptr); + + int byteOffset = c * 16 + r * 4; + if (isFloat) { + float v = 0.0f; sscanf(*ptr, "%f", &v); + memcpy(dst + byteOffset, &v, 4); + } + else { + int32_t v = 0; sscanf(*ptr, "%d", &v); + memcpy(dst + byteOffset, &v, 4); + } + + while (**ptr && **ptr != ',' && **ptr != ')') (*ptr)++; + if (**ptr == ',') (*ptr)++; + } + } + } + + return true; +} + +/* Parse varying declaration with optional interpolation qualifier */ +static inline bool r3d_rshade_parse_varying(const char** ptr, r3d_rshade_varying_t* varying) +{ + varying->qualifier[0] = '\0'; + + // Check for interpolation qualifier before "varying" + size_t qualLen = 0; + if (strncmp(*ptr, "flat", 4) == 0 && IS_SPACE((*ptr)[4])) qualLen = 4; + else if (strncmp(*ptr, "noperspective", 13) == 0 && IS_SPACE((*ptr)[13])) qualLen = 13; + else if (strncmp(*ptr, "smooth", 6) == 0 && IS_SPACE((*ptr)[6])) qualLen = 6; + + if (qualLen > 0) { + memcpy(varying->qualifier, *ptr, qualLen); + varying->qualifier[qualLen] = '\0'; + *ptr += qualLen; + r3d_rshade_skip_whitespace(ptr); + + // Must be followed by "varying" + if (!r3d_rshade_match_keyword(*ptr, "varying", 7)) { + r3d_rshade_skip_to_semicolon(ptr); + return false; + } + } + + // Skip "varying" keyword + *ptr += 7; + + return r3d_rshade_parse_declaration(ptr, varying->type, varying->name); +} + +/* Parse a 'uniform' declaration and register it as a sampler or UBO entry. + * The pointer must be positioned just after the 'uniform' keyword. */ +static inline bool r3d_rshade_parse_uniform(const char** ptr, + r3d_rshade_sampler_t* samplers, r3d_rshade_uniform_buffer_t* uniforms, + int* samplerCount, int* uniformCount, int* currentOffset, + int maxSamplers, int maxUniforms) +{ + char type[R3D_RSHADE_MAX_VAR_TYPE_LENGTH]; + char name[R3D_RSHADE_MAX_VAR_NAME_LENGTH]; + + // Stop before '=' or ';' so default value parsing can inspect what follows + if (!r3d_rshade_parse_identifier(ptr, type, R3D_RSHADE_MAX_VAR_TYPE_LENGTH) || + !r3d_rshade_parse_identifier(ptr, name, R3D_RSHADE_MAX_VAR_NAME_LENGTH)) { + r3d_rshade_skip_to_semicolon(ptr); + return false; + } + + // Samplers are bound by texture unit, not stored in the UBO + GLenum samplerTarget = r3d_rshade_get_sampler_target(type); + if (samplerTarget != 0) { + if (*samplerCount < maxSamplers) { + r3d_rshade_sampler_t* s = &samplers[(*samplerCount)++]; + strncpy(s->name, name, R3D_RSHADE_MAX_VAR_NAME_LENGTH - 1); + s->name[R3D_RSHADE_MAX_VAR_NAME_LENGTH - 1] = '\0'; + s->target = samplerTarget; + s->texture = 0; + } + r3d_rshade_skip_to_semicolon(ptr); + return true; + } + + int size = r3d_rshade_get_type_size(type); + if (size > 0 && *uniformCount < maxUniforms) { + // Align offset per std140 before committing the slot + *currentOffset = r3d_align_offset(*currentOffset, r3d_rshade_get_std140_alignment(size)); + + r3d_rshade_uniform_t* u = &uniforms->entries[(*uniformCount)++]; + strncpy(u->name, name, R3D_RSHADE_MAX_VAR_NAME_LENGTH - 1); + u->name[R3D_RSHADE_MAX_VAR_NAME_LENGTH - 1] = '\0'; + strncpy(u->type, type, R3D_RSHADE_MAX_VAR_TYPE_LENGTH - 1); + u->type[R3D_RSHADE_MAX_VAR_TYPE_LENGTH - 1] = '\0'; + u->offset = *currentOffset; + u->size = size; + *currentOffset += size; + + if (r3d_rshade_parse_default_value(ptr, type, uniforms->buffer, u->offset)) { + uniforms->dirty = true; // Just for security, but the data is necessarily uploaded when the buffer is created afterwards + } + } + + r3d_rshade_skip_to_semicolon(ptr); + return true; +} + +/* Check if current position is vertex() or fragment() function entry point */ +static inline r3d_rshade_parsed_function_t* r3d_rshade_check_shader_entry(const char* ptr, + r3d_rshade_parsed_function_t* vertexFunc, r3d_rshade_parsed_function_t* fragmentFunc) +{ + if (!r3d_rshade_match_keyword(ptr, "void", 4)) { + return NULL; + } + + const char* ahead = ptr + 4; + while (*ahead && IS_SPACE(*ahead)) ahead++; + + if (strncmp(ahead, "vertex", 6) == 0) return vertexFunc; + if (strncmp(ahead, "fragment", 8) == 0) return fragmentFunc; + + return NULL; +} + +/* Check if line should be skipped during global code copy */ +static inline bool r3d_rshade_should_skip_line(const char* ptr, bool hasVaryings, + r3d_rshade_parsed_function_t* vertexFunc, r3d_rshade_parsed_function_t* fragmentFunc) +{ + return strncmp(ptr, "#pragma", 7) == 0 || + r3d_rshade_match_keyword(ptr, "uniform", 7) || + (hasVaryings && r3d_rshade_match_varying_keyword(ptr)) || + (r3d_rshade_check_shader_entry(ptr, vertexFunc, fragmentFunc) != NULL); +} + +/* Write a single character to the buffer */ +static inline void r3d_rshade_writer_putc(r3d_rshade_writer_t* w, char c) +{ + if (w->overflow || w->remaining < 2) { + w->overflow = true; + return; + } + *w->ptr++ = c; + w->remaining--; +} + +/* Write raw data to the buffer */ +static inline void r3d_rshade_writer_write(r3d_rshade_writer_t* w, const char* src, size_t len) +{ + if (w->overflow) return; + if (len + 1 > w->remaining) { + w->overflow = true; + return; + } + memcpy(w->ptr, src, len); + w->ptr += len; + w->remaining -= len; +} + +/* Write formatted text to the buffer */ +static inline void r3d_rshade_writer_printf(r3d_rshade_writer_t* w, const char* fmt, ...) +{ + if (w->overflow) return; + va_list args; + va_start(args, fmt); + int n = vsnprintf(w->ptr, w->remaining, fmt, args); + va_end(args); + if (n < 0 || (size_t)n >= w->remaining) { + w->overflow = true; + return; + } + w->ptr += n; + w->remaining -= n; +} + +/* Write varyings with in/out qualifier */ +static inline void r3d_rshade_write_varyings(r3d_rshade_writer_t* w, + const char* inout, r3d_rshade_varying_t* varyings, int count) +{ + for (int i = 0; i < count; i++) { + if (varyings[i].qualifier[0] != '\0') { + r3d_rshade_writer_printf(w, "%s %s %s %s;\n", + varyings[i].qualifier, inout, varyings[i].type, varyings[i].name); + } + else { + r3d_rshade_writer_printf(w, "%s %s %s;\n", + inout, varyings[i].type, varyings[i].name); + } + } + if (count > 0) r3d_rshade_writer_putc(w, '\n'); +} + +/* Write sampler uniform declarations */ +static inline void r3d_rshade_write_samplers(r3d_rshade_writer_t* w, + r3d_rshade_sampler_t* samplers, int count) +{ + for (int i = 0; i < count; i++) { + const char* typeStr; + switch (samplers[i].target) { + case GL_TEXTURE_1D: typeStr = "sampler1D"; break; + case GL_TEXTURE_2D: typeStr = "sampler2D"; break; + case GL_TEXTURE_3D: typeStr = "sampler3D"; break; + case GL_TEXTURE_CUBE_MAP: typeStr = "samplerCube"; break; + default: typeStr = "sampler2D"; break; + } + r3d_rshade_writer_printf(w, "uniform %s %s;\n", typeStr, samplers[i].name); + } + if (count > 0) r3d_rshade_writer_putc(w, '\n'); +} + +/* Write uniform block (UBO) declaration */ +static inline void r3d_rshade_write_uniform_block(r3d_rshade_writer_t* w, + r3d_rshade_uniform_t* entries, int count) +{ + if (count <= 0) return; + + r3d_rshade_writer_printf(w, "layout(std140) uniform UserBlock {\n"); + for (int i = 0; i < count; i++) { + r3d_rshade_writer_printf(w, " %s %s;\n", entries[i].type, entries[i].name); + } + r3d_rshade_writer_printf(w, "};\n\n"); +} + +/* Write shader function body (vertex or fragment) */ +static inline void r3d_rshade_write_shader_function(r3d_rshade_writer_t* w, + const char* name, r3d_rshade_parsed_function_t* func) +{ + if (!func->bodyStart) return; + + r3d_rshade_writer_printf(w, "void %s() ", name); + r3d_rshade_writer_write(w, func->bodyStart, (size_t)(func->bodyEnd - func->bodyStart)); + r3d_rshade_writer_putc(w, '\n'); +} + +/* Copy global code while skipping uniforms, varyings, and entry points */ +static inline void r3d_rshade_copy_global_code(r3d_rshade_writer_t* w, + const char* code, bool hasVaryings, + r3d_rshade_parsed_function_t* vertexFunc, + r3d_rshade_parsed_function_t* fragmentFunc) +{ + const char* ptr = code; + + while (*ptr && !w->overflow) + { + const char* lineStart = ptr; + r3d_rshade_skip_whitespace_and_comments(&ptr); + + if (!*ptr) break; + + if (r3d_rshade_should_skip_line(ptr, hasVaryings, vertexFunc, fragmentFunc)) { + if (r3d_rshade_check_shader_entry(ptr, vertexFunc, fragmentFunc)) { + r3d_rshade_skip_to_matching_brace(&ptr); + } + else if (strncmp(ptr, "#pragma", 7) == 0) { + r3d_rshade_skip_to_end_of_line(&ptr); + } + else { + r3d_rshade_skip_to_semicolon(&ptr); + } + continue; + } + + // Copy whitespace/comments preceding this token, then the token character + r3d_rshade_writer_write(w, lineStart, (size_t)(ptr - lineStart)); + if (*ptr) r3d_rshade_writer_putc(w, *ptr++); + } +} + +// ======================================== +// UNDEF HELPER MACROS +// ======================================== + +#undef IS_SPACE + +#endif // R3D_RSHADE_H + +``` + +`src/importer/r3d_importer_animation.c`: + +```c +/* r3d_importer_animation.c -- Module to import animations from assimp scene. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_importer_internal.h" + +#include +#include +#include +#include + +// ======================================== +// CHANNEL LOADING (INTERNAL) +// ======================================== + +static bool load_vector3_track(R3D_AnimationTrack* track, unsigned int count, const struct aiVectorKey* keys) +{ + track->count = (int)count; + track->times = NULL; + track->values = NULL; + + if (track->count == 0) + return true; + + float* times = RL_MALLOC(sizeof(float) * track->count); + Vector3* values = RL_MALLOC(sizeof(Vector3) * track->count); + + if (!times || !values) { + RL_FREE(times); + RL_FREE(values); + return false; + } + + for (int i = 0; i < track->count; ++i) { + times[i] = (float)keys[i].mTime; + values[i] = r3d_importer_cast(keys[i].mValue); + } + + track->times = times; + track->values = values; + return true; +} + +static bool load_quaternion_track(R3D_AnimationTrack* track, unsigned int count, const struct aiQuatKey* keys) +{ + track->count = (int)count; + track->times = NULL; + track->values = NULL; + + if (track->count == 0) + return true; + + float* times = RL_MALLOC(sizeof(float) * track->count); + Quaternion* values = RL_MALLOC(sizeof(Quaternion) * track->count); + + if (!times || !values) { + RL_FREE(times); + RL_FREE(values); + return false; + } + + for (int i = 0; i < track->count; ++i) { + times[i] = (float)keys[i].mTime; + values[i] = r3d_importer_cast(keys[i].mValue); + } + + track->times = times; + track->values = values; + return true; +} + +static bool load_channel(R3D_AnimationChannel* channel, const R3D_Importer* importer, const struct aiNodeAnim* aiChannel) +{ + if (!aiChannel) { + R3D_TRACELOG(LOG_ERROR, "Invalid animation channel"); + return false; + } + + const char* boneName = aiChannel->mNodeName.data; + channel->boneIndex = r3d_importer_get_bone_index(importer, boneName); + + if (channel->boneIndex < 0) { + R3D_TRACELOG(LOG_WARNING, "Bone '%s' from animation not found in skeleton", boneName); + return false; + } + + if (!load_vector3_track(&channel->translation, aiChannel->mNumPositionKeys, aiChannel->mPositionKeys)) { + goto fail; + } + + if (!load_quaternion_track(&channel->rotation, aiChannel->mNumRotationKeys, aiChannel->mRotationKeys)) { + goto fail; + } + + if (!load_vector3_track(&channel->scale, aiChannel->mNumScalingKeys, aiChannel->mScalingKeys)) { + goto fail; + } + + return true; + +fail: + RL_FREE((void*)channel->translation.times); + RL_FREE((void*)channel->translation.values); + RL_FREE((void*)channel->rotation.times); + RL_FREE((void*)channel->rotation.values); + RL_FREE((void*)channel->scale.times); + RL_FREE((void*)channel->scale.values); + return false; +} + + +// ======================================== +// ANIMATION LOADING (INTERNAL) +// ======================================== + +static bool load_animation(R3D_Animation* animation, const R3D_Importer* importer, const struct aiAnimation* aiAnim) +{ + // Basic validation + if (!aiAnim || aiAnim->mNumChannels == 0) { + R3D_TRACELOG(LOG_ERROR, "Invalid animation or no channels"); + return false; + } + + // Check that we have bones in the skeleton + const int boneCount = r3d_importer_get_bone_count(importer); + if (boneCount == 0) { + R3D_TRACELOG(LOG_ERROR, "No bones in skeleton"); + return false; + } + + // Initialize animation structure + animation->boneCount = boneCount; + animation->duration = (float)aiAnim->mDuration; + animation->ticksPerSecond = (aiAnim->mTicksPerSecond != 0.0) + ? (float)aiAnim->mTicksPerSecond + : 24.0f; + + // Copy animation name + size_t nameLen = strlen(aiAnim->mName.data); + if (nameLen >= sizeof(animation->name)) { + nameLen = sizeof(animation->name) - 1; + } + memcpy(animation->name, aiAnim->mName.data, nameLen); + animation->name[nameLen] = '\0'; + + // Allocate channels + animation->channelCount = aiAnim->mNumChannels; + animation->channels = RL_CALLOC(animation->channelCount, sizeof(R3D_AnimationChannel)); + if (!animation->channels) { + R3D_TRACELOG(LOG_ERROR, "Failed to allocate animation channels"); + return false; + } + + // Load each channel + int successChannels = 0; + for (unsigned int i = 0; i < aiAnim->mNumChannels; i++) { + if (load_channel(&animation->channels[successChannels], importer, aiAnim->mChannels[i])) { + successChannels++; + } else { + R3D_TRACELOG(LOG_WARNING, "Failed to load channel %u", i); + } + } + + if (successChannels == 0) { + R3D_TRACELOG(LOG_ERROR, "No channels were successfully loaded"); + RL_FREE(animation->channels); + return false; + } + + // Adjust channel count if some failed + if (successChannels < animation->channelCount) { + animation->channelCount = successChannels; + R3D_AnimationChannel* resized = RL_REALLOC( + animation->channels, + successChannels * sizeof(R3D_AnimationChannel) + ); + if (resized) { + animation->channels = resized; + } + } + + R3D_TRACELOG(LOG_INFO, "Animation '%s' loaded: %.2f duration, %.2f ticks/sec, %d channels", + animation->name, animation->duration, animation->ticksPerSecond, animation->channelCount); + + return true; +} + +// ======================================== +// PUBLIC FUNCTIONS +// ======================================== + +bool r3d_importer_load_animations(const R3D_Importer* importer, R3D_AnimationLib* animLib) +{ + if (!importer || !r3d_importer_is_valid(importer)) { + R3D_TRACELOG(LOG_ERROR, "Invalid importer for animation loading"); + return false; + } + + int animCount = r3d_importer_get_animation_count(importer); + if (animCount == 0) { + R3D_TRACELOG(LOG_WARNING, "No animations found in the imported scene"); + return false; + } + + // Allocate temporary animations array + R3D_Animation* animations = RL_CALLOC(animCount, sizeof(R3D_Animation)); + if (!animations) { + R3D_TRACELOG(LOG_ERROR, "Unable to allocate memory for animations"); + return false; + } + + // Load each animation + int successCount = 0; + for (int i = 0; i < animCount; i++) { + const struct aiAnimation* aiAnim = r3d_importer_get_animation(importer, i); + if (load_animation(&animations[successCount], importer, aiAnim)) { + successCount++; + } else { + R3D_TRACELOG(LOG_ERROR, "Failed to process animation %d", i); + } + } + + if (successCount == 0) { + R3D_TRACELOG(LOG_ERROR, "No animations were successfully loaded"); + RL_FREE(animations); + return false; + } + + // Resize if some animations failed + if (successCount < animCount) { + R3D_TRACELOG(LOG_WARNING, "Only %d out of %d animations were successfully loaded", successCount, animCount); + R3D_Animation* resized = RL_REALLOC(animations, successCount * sizeof(R3D_Animation)); + if (resized) { + animations = resized; + } + } + + animLib->animations = animations; + animLib->count = successCount; + + return true; +} + +``` + +`src/importer/r3d_importer_internal.h`: + +```h +/* r3d_importer.h -- Module to manage model imports via assimp. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_IMPORTER_INTERNAL_H +#define R3D_IMPORTER_INTERNAL_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// ======================================== +// MACROS +// ======================================== + +#define r3d_importer_cast(src) _Generic((src), \ + struct aiVector2D: r3d_importer_cast_aivector2d_to_vector2, \ + struct aiVector3D: r3d_importer_cast_aivector3d_to_vector3, \ + struct aiQuaternion: r3d_importer_cast_aiquaternion_to_quaternion, \ + struct aiColor4D: r3d_importer_cast_aicolor4d_to_color, \ + struct aiMatrix4x4: r3d_importer_cast_aimatrix4x4_to_matrix \ +)(src) + +#define r3d_importer_cast_to_vector2(src) _Generic((src), \ + struct aiVector2D: r3d_importer_cast_aivector2d_to_vector2, \ + struct aiVector3D: r3d_importer_cast_aivector3d_to_vector2 \ +)(src) + +// ======================================== +// SCENE IMPORTER +// ======================================== + +typedef struct { + char name[128]; // Key + int index; // Value + UT_hash_handle hh; // Uthash handle +} r3d_importer_bone_entry_t; + +typedef struct { + r3d_importer_bone_entry_t* array; + r3d_importer_bone_entry_t* head; + int count; +} r3d_importer_bone_map_t; + +struct R3D_Importer { + const struct aiScene* scene; + r3d_importer_bone_map_t bones; + R3D_ImportFlags flags; + char name[256]; +}; + +// ======================================== +// TEXTURE CACHE +// ======================================== + +typedef enum { + R3D_MAP_ALBEDO = 0, + R3D_MAP_EMISSION = 1, + R3D_MAP_ORM = 2, + R3D_MAP_NORMAL = 3, + R3D_MAP_COUNT +} r3d_importer_texture_map_t; + +typedef struct r3d_importer_texture_cache r3d_importer_texture_cache_t; + +// ======================================== +// PUBLIC FUNCTIONS +// ======================================== + +/** + * Create a texture cache that loads all textures for all materials + * This will spawn worker threads to load images in parallel, then + * progressively upload them to GPU as they become ready + */ +r3d_importer_texture_cache_t* r3d_importer_load_texture_cache(const R3D_Importer* importer, R3D_ColorSpace colorSpace, TextureFilter filter); + +/** + * Frees the memory space allocated to store textures. + * The 'unloadTextures' parameter should only be set to true in case of an error, in order to free the loaded textures. + * If everything goes well, all textures loaded into the cache should be used. + */ +void r3d_importer_unload_texture_cache(r3d_importer_texture_cache_t* cache, bool unloadTextures); + +/** + * Get a texture for a specific material and map type + * Returns NULL if the texture doesn't exist + */ +Texture2D* r3d_importer_get_loaded_texture(r3d_importer_texture_cache_t* cache, int materialIndex, r3d_importer_texture_map_t map); + +/** + * Load all meshes from the importer into the model + * Returns true on success, false on failure + */ +bool r3d_importer_load_meshes(const R3D_Importer* importer, R3D_Model* model); + +/** + * Process and create a skeleton from the imported scene + * Returns NULL if the scene has no bones or on allocation failure + * The returned skeleton must be freed by the caller + */ +bool r3d_importer_load_skeleton(const R3D_Importer* importer, R3D_Skeleton* skeleton); + +/** + * Load all materials from the importer into the material array + * The textureCache can be NULL, detault textures will be used + * Returns true on success, false on failure + */ +bool r3d_importer_load_materials(const R3D_Importer* importer, R3D_Material** materials, int* materialCount, r3d_importer_texture_cache_t* textureCache); + +/** + * Load all animations from the imported scene + * Returns NULL if no animations are found or on error + * The returned animation library must be freed by the caller + */ +bool r3d_importer_load_animations(const R3D_Importer* importer, R3D_AnimationLib* animationLib); + +// ======================================== +// INLINE FUNCTIONS +// ======================================== + +static inline const struct aiAnimation* r3d_importer_get_animation(const R3D_Importer* importer, int index) +{ + return importer->scene->mAnimations[index]; +} + +static inline const struct aiMaterial* r3d_importer_get_material(const R3D_Importer* importer, int index) +{ + return importer->scene->mMaterials[index]; +} + +static inline const struct aiTexture* r3d_importer_get_texture(const R3D_Importer* importer, int index) +{ + return importer->scene->mTextures[index]; +} + +static inline const struct aiMesh* r3d_importer_get_mesh(const R3D_Importer* importer, int index) +{ + return importer->scene->mMeshes[index]; +} + +static inline const struct aiNode* r3d_importer_get_root(const R3D_Importer* importer) +{ + return importer->scene->mRootNode; +} + +static inline const struct aiScene* r3d_importer_get_scene(const R3D_Importer* importer) +{ + return importer->scene; +} + +static inline int r3d_importer_get_animation_count(const R3D_Importer* importer) +{ + return importer->scene->mNumAnimations; +} + +static inline int r3d_importer_get_material_count(const R3D_Importer* importer) +{ + return importer->scene->mNumMaterials; +} + +static inline int r3d_importer_get_mesh_count(const R3D_Importer* importer) +{ + return importer->scene->mNumMeshes; +} + +static inline int r3d_importer_get_bone_count(const R3D_Importer* importer) +{ + return importer->bones.count; +} + +static inline int r3d_importer_get_bone_index(const R3D_Importer* importer, const char* name) +{ + if (!importer || !name) return -1; + + r3d_importer_bone_entry_t* entry = NULL; + HASH_FIND_STR(importer->bones.head, name, entry); + + return entry ? entry->index : -1; +} + +static inline bool r3d_importer_is_valid(const R3D_Importer* importer) +{ + return importer && importer->scene; +} + +// ======================================== +// ASSIMP CAST HELPERS +// ======================================== + +static inline Vector2 r3d_importer_cast_aivector2d_to_vector2(struct aiVector2D src) +{ + return (Vector2) { src.x, src.y }; +} + +static inline Vector2 r3d_importer_cast_aivector3d_to_vector2(struct aiVector3D src) +{ + return (Vector2) { src.x, src.y }; +} + +static inline Vector3 r3d_importer_cast_aivector3d_to_vector3(struct aiVector3D src) +{ + return (Vector3) { src.x, src.y, src.z }; +} + +static inline Quaternion r3d_importer_cast_aiquaternion_to_quaternion(struct aiQuaternion src) +{ + return (Quaternion) { src.x, src.y, src.z, src.w }; +} + +static inline Color r3d_importer_cast_aicolor4d_to_color(struct aiColor4D src) +{ + return (Color) { + (uint8_t)(fminf(1.0f, fmaxf(0.0f, src.r)) * 255), + (uint8_t)(fminf(1.0f, fmaxf(0.0f, src.g)) * 255), + (uint8_t)(fminf(1.0f, fmaxf(0.0f, src.b)) * 255), + (uint8_t)(fminf(1.0f, fmaxf(0.0f, src.a)) * 255) + }; +} + +static inline Matrix r3d_importer_cast_aimatrix4x4_to_matrix(struct aiMatrix4x4 src) +{ + return (Matrix) { + src.a1, src.a2, src.a3, src.a4, + src.b1, src.b2, src.b3, src.b4, + src.c1, src.c2, src.c3, src.c4, + src.d1, src.d2, src.d3, src.d4, + }; +} + +#endif // R3D_IMPORTER_INTERNAL_H + +``` + +`src/importer/r3d_importer_material.c`: + +```c +/* r3d_importer_material.c -- Module to import materials from assimp scene. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_importer_internal.h" +#include "r3d/r3d_material.h" + +#include +#include +#include +#include +#include +#include + +// ======================================== +// MATERIAL MAP LOADERS +// ======================================== + +static void load_map_albedo(R3D_Material* material, const struct aiMaterial* aiMat, r3d_importer_texture_cache_t* cache, int index) +{ + Texture2D* tex = r3d_importer_get_loaded_texture(cache, index, R3D_MAP_ALBEDO); + if (tex) { + material->albedo.texture = *tex; + } + + struct aiColor4D color; + if (aiGetMaterialColor(aiMat, AI_MATKEY_BASE_COLOR, &color) == AI_SUCCESS) { + material->albedo.color = r3d_importer_cast(color); + } + else if (aiGetMaterialColor(aiMat, AI_MATKEY_COLOR_DIFFUSE, &color) == AI_SUCCESS) { + material->albedo.color = r3d_importer_cast(color); + } + + // Opacity: try each source in order, stop as soon as alpha is modified + // AI_MATKEY_OPACITY: direct opacity factor (1.0 = fully opaque) + float opacityFactor; + if (material->albedo.color.a == 255 + && aiGetMaterialFloat(aiMat, AI_MATKEY_OPACITY, &opacityFactor) == AI_SUCCESS) { + material->albedo.color.a = (unsigned char)(opacityFactor * 255.0f); + } + + // AI_MATKEY_TRANSPARENCYFACTOR: inverse of opacity (1.0 = fully transparent) + if (material->albedo.color.a == 255 + && aiGetMaterialFloat(aiMat, AI_MATKEY_TRANSPARENCYFACTOR, &opacityFactor) == AI_SUCCESS) { + material->albedo.color.a = (unsigned char)((1.0f - opacityFactor) * 255.0f); + } + + // AI_MATKEY_TRANSMISSION_FACTOR: glTF transmission (1.0 = fully transparent) + if (material->albedo.color.a == 255 + && aiGetMaterialFloat(aiMat, AI_MATKEY_TRANSMISSION_FACTOR, &opacityFactor) == AI_SUCCESS) { + material->albedo.color.a = (unsigned char)((1.0f - opacityFactor) * 255.0f); + } + + // AI_MATKEY_COLOR_TRANSPARENT: OBJ/MTL 'Tf' transmission filter + // Tf 1 1 1 = fully transparent, Tf 0 0 0 = fully opaque (opposite of opacity) + // Tf 1 1 1 is also the default for opaque materials, so we ignore it + struct aiColor4D tf; + if (material->albedo.color.a == 255 + && aiGetMaterialColor(aiMat, AI_MATKEY_COLOR_TRANSPARENT, &tf) == AI_SUCCESS) { + float transmission = tf.r * 0.2126f + tf.g * 0.7152f + tf.b * 0.0722f; + if (transmission > 0.0f && transmission < 1.0f) { + material->albedo.color.a = (unsigned char)((1.0f - transmission) * 255.0f); + } + } +} + +static void load_map_emission(R3D_Material* material, const struct aiMaterial* aiMat, r3d_importer_texture_cache_t* cache, int index) +{ + Texture2D* tex = r3d_importer_get_loaded_texture(cache, index, R3D_MAP_EMISSION); + if (tex) { + material->emission.texture = *tex; + } + + struct aiColor4D color; + if (aiGetMaterialColor(aiMat, AI_MATKEY_COLOR_EMISSIVE, &color) == AI_SUCCESS) { + material->emission.color = r3d_importer_cast(color); + } + + float intensity; + if (aiGetMaterialFloat(aiMat, AI_MATKEY_EMISSIVE_INTENSITY, &intensity) == AI_SUCCESS) { + material->emission.energy = intensity; + } + else if (tex || material->emission.color.r || material->emission.color.g || material->emission.color.b) { + // No explicit intensity but emission is present: default to 1.0 + material->emission.energy = 1.0f; + } +} + +static void load_map_orm(R3D_Material* material, const struct aiMaterial* aiMat, r3d_importer_texture_cache_t* cache, int index) +{ + Texture2D* tex = r3d_importer_get_loaded_texture(cache, index, R3D_MAP_ORM); + if (tex) { + material->orm.texture = *tex; + } + + // Roughness: prefer PBR value, fallback to Phong shininess for OBJ/MTL + float roughness; + if (aiGetMaterialFloat(aiMat, AI_MATKEY_ROUGHNESS_FACTOR, &roughness) == AI_SUCCESS) { + material->orm.roughness = roughness; + } + else { + // OBJ/MTL fallback: derive roughness from Ns (specular exponent, range 0-1000) + // Ns 1000 = very shiny = low roughness, Ns 0 = matte = high roughness + float shininess = 0.0f; + if (aiGetMaterialFloat(aiMat, AI_MATKEY_SHININESS, &shininess) == AI_SUCCESS && shininess > 0.0f) { + float normalized = fminf(shininess / 1000.0f, 1.0f); + float derived = 1.0f - sqrtf(normalized); + + // Modulate by specular strength if available: low strength = weaker specular = more rough + float strength = 1.0f; + if (aiGetMaterialFloat(aiMat, AI_MATKEY_SHININESS_STRENGTH, &strength) == AI_SUCCESS) { + strength = fminf(fmaxf(strength, 0.0f), 1.0f); + derived = derived + (1.0f - derived) * (1.0f - strength); + } + + material->orm.roughness = fminf(fmaxf(derived, 0.0f), 1.0f); + } + } + + // Metalness: no equivalent in Phong, stays at default if not found + float metalness; + if (aiGetMaterialFloat(aiMat, AI_MATKEY_METALLIC_FACTOR, &metalness) == AI_SUCCESS) { + material->orm.metalness = metalness; + } +} + +static void load_map_normal(R3D_Material* material, const struct aiMaterial* aiMat, r3d_importer_texture_cache_t* cache, int index) +{ + Texture2D* tex = r3d_importer_get_loaded_texture(cache, index, R3D_MAP_NORMAL); + if (!tex) return; + + material->normal.texture = *tex; + + float scale; + if (aiGetMaterialFloat(aiMat, AI_MATKEY_BUMPSCALING, &scale) == AI_SUCCESS) { + material->normal.scale = scale; + } +} + +// ======================================== +// MATERIAL PARAMETER LOADERS +// ======================================== + +static void load_param_blend_mode(R3D_Material* material, const struct aiMaterial* aiMat) +{ + // glTF alpha mode takes priority when present + struct aiString alphaMode; + if (aiGetMaterialString(aiMat, AI_MATKEY_GLTF_ALPHAMODE, &alphaMode) == AI_SUCCESS) { + if (strcmp(alphaMode.data, "MASK") == 0) { + float alphaCutoff; + if (aiGetMaterialFloat(aiMat, AI_MATKEY_GLTF_ALPHACUTOFF, &alphaCutoff) == AI_SUCCESS) { + material->alphaCutoff = alphaCutoff; + } + return; + } + if (strcmp(alphaMode.data, "BLEND") == 0) { + material->transparencyMode = R3D_TRANSPARENCY_PREPASS; + material->blendMode = R3D_BLEND_MIX; + return; + } + } + + // Generic blend function + int blendFunc; + if (aiGetMaterialInteger(aiMat, AI_MATKEY_BLEND_FUNC, &blendFunc) == AI_SUCCESS) { + switch (blendFunc) { + case aiBlendMode_Default: + // sColor*sAlpha + dColor*(1-sAlpha) + material->transparencyMode = R3D_TRANSPARENCY_PREPASS; + material->blendMode = R3D_BLEND_MIX; + return; + case aiBlendMode_Additive: + // sColor*1 + dColor*1 + material->transparencyMode = R3D_TRANSPARENCY_DISABLED; + material->blendMode = R3D_BLEND_ADDITIVE; + return; + default: + break; + } + } + + // Fallback: infer blend mode from albedo alpha uniform + // alpha == 0 is likely a degenerate material, ignore it + if (material->albedo.color.a > 0 && material->albedo.color.a < 255) { + material->transparencyMode = R3D_TRANSPARENCY_ALPHA; + material->blendMode = R3D_BLEND_MIX; + } +} + +static void load_param_cull_mode(R3D_Material* material, const struct aiMaterial* aiMat) +{ + int twoSided; + if (aiGetMaterialInteger(aiMat, AI_MATKEY_TWOSIDED, &twoSided) == AI_SUCCESS && twoSided) { + material->cullMode = R3D_CULL_NONE; + } +} + +static void load_param_shading_mode(R3D_Material* material, const struct aiMaterial* aiMat) +{ + int shadingMode; + if (aiGetMaterialInteger(aiMat, AI_MATKEY_SHADING_MODEL, &shadingMode) == AI_SUCCESS) { + material->unlit = (shadingMode == aiShadingMode_Unlit); + } +} + +// ======================================== +// MATERIAL LOADING (INTERNAL) +// ======================================== + +static void load_material(R3D_Material* material, const R3D_Importer* importer, r3d_importer_texture_cache_t* cache, int index) +{ + const struct aiMaterial* aiMat = r3d_importer_get_material(importer, index); + + *material = R3D_GetDefaultMaterial(); + + load_map_albedo(material, aiMat, cache, index); + load_map_emission(material, aiMat, cache, index); + load_map_orm(material, aiMat, cache, index); + load_map_normal(material, aiMat, cache, index); + + load_param_blend_mode(material, aiMat); + load_param_cull_mode(material, aiMat); + load_param_shading_mode(material, aiMat); +} + +// ======================================== +// PUBLIC FUNCTIONS +// ======================================== + +bool r3d_importer_load_materials(const R3D_Importer* importer, R3D_Material** materials, int* materialCount, r3d_importer_texture_cache_t* textureCache) +{ + if (!materials || !materialCount || !importer || !r3d_importer_is_valid(importer)) { + R3D_TRACELOG(LOG_ERROR, "Invalid parameters for material loading"); + return false; + } + + *materialCount = r3d_importer_get_material_count(importer); + *materials = RL_CALLOC(*materialCount, sizeof(R3D_Material)); + + if (*materials == NULL) { + R3D_TRACELOG(LOG_ERROR, "Unable to allocate memory for materials"); + return false; + } + + for (int i = 0; i < *materialCount; i++) { + load_material(&(*materials)[i], importer, textureCache, i); + } + + return true; +} + +``` + +`src/importer/r3d_importer_mesh.c`: + +```c +/* r3d_importer_mesh.c -- Module to import meshes from assimp scene. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_importer_internal.h" + +#include +#include +#include +#include + +#include +#include + +#include "../common/r3d_helper.h" +#include "../common/r3d_math.h" + +// ======================================== +// CONSTANTS +// ======================================== + +#define MAX_BONE_WEIGHTS 4 + +// ======================================== +// VERTEX PROCESSING (INTERNAL) +// ======================================== + +static inline void process_vertex_position(Vector3* position, const struct aiVector3D* aiPos, const Matrix* transform, bool hasBones, BoundingBox* aabb) +{ + Vector3 lPosition = r3d_importer_cast(*aiPos); + Vector3 gPosition = r3d_vector3_transform(lPosition, transform); + + *position = hasBones ? lPosition : gPosition; + aabb->min = Vector3Min(aabb->min, gPosition); + aabb->max = Vector3Max(aabb->max, gPosition); +} + +static inline void process_vertex_texcoord(uint16_t* texcoord, const struct aiMesh* aiMesh, int index) +{ + if (aiMesh->mTextureCoords[0] && aiMesh->mNumUVComponents[0] >= 2) { + R3D_PackTexCoord(texcoord, r3d_importer_cast_to_vector2(aiMesh->mTextureCoords[0][index])); + } + // NOTE: Vertices are zero-initialized + //else { + // *texcoord = (Vector2) {0.0f, 0.0f}; + //} +} + +static inline void process_vertex_normal(int8_t* normal, const struct aiMesh* aiMesh, int index, const Matrix* normalMatrix, bool hasBones) +{ + if (aiMesh->mNormals) { + Vector3 vec = r3d_importer_cast(aiMesh->mNormals[index]); + if (!hasBones) { + vec = r3d_vector3_transform_linear(vec, normalMatrix); + } + R3D_PackNormal(normal, Vector3Normalize(vec)); + } + else { + R3D_PackNormal(normal, (Vector3){0.0f, 0.0f, 1.0f}); + } +} + +static inline void process_vertex_tangent(R3D_Vertex* vertex, const struct aiMesh* aiMesh, int index, const Matrix* normalMatrix, bool hasBones) +{ + if (aiMesh->mNormals && aiMesh->mTangents && aiMesh->mBitangents) { + Vector3 normal = r3d_importer_cast(aiMesh->mNormals[index]); + Vector3 tangent = r3d_importer_cast(aiMesh->mTangents[index]); + Vector3 bitangent = r3d_importer_cast(aiMesh->mBitangents[index]); + + if (!hasBones) { + normal = r3d_vector3_transform_linear(normal, normalMatrix); + tangent = r3d_vector3_transform_linear(tangent, normalMatrix); + bitangent = r3d_vector3_transform_linear(bitangent, normalMatrix); + } + + normal = Vector3Normalize(tangent); + tangent = Vector3Normalize(tangent); + bitangent = Vector3Normalize(bitangent); + + Vector3 reconstructedBitangent = Vector3CrossProduct(normal, tangent); + float handedness = Vector3DotProduct(reconstructedBitangent, bitangent); + + R3D_PackTangent(vertex->tangent, (Vector4){ + tangent.x, tangent.y, tangent.z, + copysignf(1.0f, handedness) + }); + } + else { + R3D_PackTangent(vertex->tangent, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}); + } +} + +static inline void process_vertex_color(Color* color, const struct aiMesh* aiMesh, int index) +{ + if (aiMesh->mColors[0]) { + *color = r3d_importer_cast(aiMesh->mColors[0][index]); + } + else { + *color = WHITE; + } +} + +// ======================================== +// FACE/INDEX PROCESSING (INTERNAL) +// ======================================== + +static void process_indices(const struct aiMesh* aiMesh, R3D_MeshData* data) +{ + uint32_t* indexPtr = data->indices; + for (unsigned int i = 0; i < aiMesh->mNumFaces; i++) { + const struct aiFace* face = &aiMesh->mFaces[i]; + *indexPtr++ = face->mIndices[0]; + *indexPtr++ = face->mIndices[1]; + *indexPtr++ = face->mIndices[2]; + } +} + +// ======================================== +// BONE PROCESSING (INTERNAL) +// ======================================== + +static inline bool assign_bone_weight(R3D_Vertex* vertex, uint32_t boneIndex, uint8_t weightValue) +{ + int emptySlot = -1, minWeightSlot = 0; + uint8_t minWeight = vertex->boneWeights[0]; + + // Pass to find both empty slot and minimum weight + for (int slot = 1; slot < MAX_BONE_WEIGHTS; slot++) { + uint8_t w = vertex->boneWeights[slot]; + if (w == 0 && emptySlot == -1) { + emptySlot = slot; + } + if (w < minWeight) { + minWeight = w; + minWeightSlot = slot; + } + } + + // Use empty slot if available + if (emptySlot != -1) { + vertex->boneIndices[emptySlot] = boneIndex; + vertex->boneWeights[emptySlot] = weightValue; + return true; + } + + // All slots occupied - replace if new weight is larger + if (weightValue > minWeight) { + vertex->boneIndices[minWeightSlot] = boneIndex; + vertex->boneWeights[minWeightSlot] = weightValue; + return true; + } + + return false; +} + +static void normalize_bone_weights(R3D_Vertex* vertex) +{ + uint32_t sum = (uint32_t)vertex->boneWeights[0] + (uint32_t)vertex->boneWeights[1] + + (uint32_t)vertex->boneWeights[2] + (uint32_t)vertex->boneWeights[3]; + + if (sum == 255) return; + + if (sum > 0) { + uint32_t half = sum >> 1; // nearest rounding + vertex->boneWeights[0] = (uint8_t)((vertex->boneWeights[0] * 255 + half) / sum); + vertex->boneWeights[1] = (uint8_t)((vertex->boneWeights[1] * 255 + half) / sum); + vertex->boneWeights[2] = (uint8_t)((vertex->boneWeights[2] * 255 + half) / sum); + vertex->boneWeights[3] = (uint8_t)((vertex->boneWeights[3] * 255 + half) / sum); + } + else { + vertex->boneWeights[0] = 255; + } +} + +static bool process_bones(const struct aiMesh* aiMesh, R3D_MeshData* data, int vertexCount) +{ + if (aiMesh->mNumBones == 0) { + // No bones - initialize default weights + for (int i = 0; i < vertexCount; i++) { + data->vertices[i].boneWeights[0] = 255; + } + return true; + } + + // Check if the mesh has too many bones + if (aiMesh->mNumBones > MAX_OF(*data->vertices->boneIndices) + 1) { + R3D_TRACELOG(LOG_WARNING, "Mesh has %u bones, max %d supported", + aiMesh->mNumBones, MAX_OF(*data->vertices->boneIndices) + 1); + return false; + } + + // Process each bone + for (unsigned int boneIndex = 0; boneIndex < aiMesh->mNumBones; boneIndex++) + { + const struct aiBone* bone = aiMesh->mBones[boneIndex]; + + // Process all vertex weights for this bone + for (unsigned int weightIndex = 0; weightIndex < bone->mNumWeights; weightIndex++) + { + const struct aiVertexWeight* weight = &bone->mWeights[weightIndex]; + + // Validate vertex ID + uint32_t vertexId = weight->mVertexId; + if (vertexId >= (uint32_t)vertexCount) { + R3D_TRACELOG(LOG_ERROR, "Invalid vertex ID %u in bone weights (max: %d)", vertexId, vertexCount); + continue; + } + + uint8_t weightValue = (uint8_t)(weight->mWeight * 255.0f + 0.5f); + if (weightValue == 0) continue; + + assign_bone_weight(&data->vertices[vertexId], boneIndex, weightValue); + } + } + + // Normalize all vertex weights + for (int i = 0; i < vertexCount; i++) { + normalize_bone_weights(&data->vertices[i]); + } + + return true; +} + +// ======================================== +// MESH LOADING (INTERNAL) +// ======================================== + +static R3D_PrimitiveType get_primitive_type(unsigned int aiPrimitiveTypes) +{ + // A single mesh may theoretically contain multiple primitive types, + // but we use `aiProcess_SortByPType` during import, which resolves this issue, + // so we can assume there is only one primitive type per mesh. + + if (BIT_TEST(aiPrimitiveTypes, aiPrimitiveType_POINT)) { + return R3D_PRIMITIVE_POINTS; + } + + if (BIT_TEST(aiPrimitiveTypes, aiPrimitiveType_LINE)) { + return R3D_PRIMITIVE_LINES; + } + + if (BIT_TEST(aiPrimitiveTypes, aiPrimitiveType_TRIANGLE)) { + return R3D_PRIMITIVE_TRIANGLES; + } + + // NOTE: This should never happen if the mesh has been triangulated. + //if (BIT_TEST(aiPrimitiveTypes, aiPrimitiveType_POLYGON)) { + // return 0; + //} + + if (BIT_TEST(aiPrimitiveTypes, aiPrimitiveType_NGONEncodingFlag)) { + R3D_TRACELOG(LOG_WARNING, "NGON primitive encoding not supported"); + return R3D_PRIMITIVE_TRIANGLE_FAN; + } + + return R3D_PRIMITIVE_TRIANGLES; +} + +static bool load_mesh_internal( + R3D_Mesh* outMesh, + R3D_MeshData* outMeshData, + R3D_MeshName* outMeshName, + const struct aiMesh* aiMesh, + Matrix transform, + bool hasBones) +{ + // Validate input + if (!aiMesh) { + R3D_TRACELOG(LOG_ERROR, "Invalid parameters during assimp mesh processing"); + return false; + } + + if (aiMesh->mNumVertices == 0 || aiMesh->mNumFaces == 0) { + R3D_TRACELOG(LOG_ERROR, "Empty mesh detected during assimp mesh processing"); + return false; + } + + // Allocate mesh data + int vertexCount = aiMesh->mNumVertices; + int indexCount = 3 * aiMesh->mNumFaces; + R3D_MeshData data = R3D_LoadMeshData(vertexCount, indexCount); + if (!data.vertices || !data.indices) { + R3D_TRACELOG(LOG_ERROR, "Failed to load mesh; Unable to allocate mesh data"); + return false; + } + data.vertexCount = vertexCount; + data.indexCount = indexCount; + + // Initialize bounding box + BoundingBox aabb; + aabb.min = (Vector3) {+FLT_MAX, +FLT_MAX, +FLT_MAX}; + aabb.max = (Vector3) {-FLT_MAX, -FLT_MAX, -FLT_MAX}; + + // Pre-compute normal matrix for non-bone meshes + Matrix normalMatrix = {0}; + if (!hasBones) { + normalMatrix = r3d_matrix_normal(&transform); + } + + // Process all vertex attributes + for (int i = 0; i < vertexCount; i++) { + R3D_Vertex* vertex = &data.vertices[i]; + process_vertex_position(&vertex->position, &aiMesh->mVertices[i], &transform, hasBones, &aabb); + process_vertex_texcoord(vertex->texcoord, aiMesh, i); + process_vertex_normal(vertex->normal, aiMesh, i, &normalMatrix, hasBones); + process_vertex_tangent(vertex, aiMesh, i, &normalMatrix, hasBones); + process_vertex_color(&vertex->color, aiMesh, i); + } + + // Process indices + process_indices(aiMesh, &data); + + // Process bone data + if (!process_bones(aiMesh, &data, vertexCount)) { + R3D_UnloadMeshData(data); + return false; + } + + // Upload the mesh + R3D_PrimitiveType ptype = get_primitive_type(aiMesh->mPrimitiveTypes); + *outMesh = R3D_LoadMesh(ptype, data, &aabb); + + if (outMeshData != NULL) *outMeshData = data; + else R3D_UnloadMeshData(data); + + if (outMeshName != NULL && aiMesh->mName.length > 0) { + strncpy(*outMeshName, aiMesh->mName.data, sizeof(R3D_MeshName) - 1); + *outMeshName[sizeof(R3D_MeshName) - 1] = '\0'; + } + + return true; +} + +// ======================================== +// RECURSIVE LOADING +// ======================================== + +static bool load_recursive(const R3D_Importer* importer, R3D_Model* model, const struct aiNode* node, const Matrix* parentTransform) +{ + Matrix localTransform = r3d_importer_cast(node->mTransformation); + Matrix globalTransform = MatrixMultiply(localTransform, *parentTransform); + + // Process all meshes in this node + for (unsigned int i = 0; i < node->mNumMeshes; i++) { + uint32_t meshIndex = node->mMeshes[i]; + const struct aiMesh* mesh = r3d_importer_get_mesh(importer, meshIndex); + + R3D_MeshData* meshData = model->meshData ? &model->meshData[meshIndex] : NULL; + R3D_MeshName* meshName = model->meshNames ? &model->meshNames[meshIndex] : NULL; + + if (!load_mesh_internal(&model->meshes[meshIndex], meshData, meshName, mesh, globalTransform, mesh->mNumBones > 0)) { + R3D_TRACELOG(LOG_ERROR, "Unable to load mesh [%u]; The model will be invalid", meshIndex); + return false; + } + + model->meshMaterials[meshIndex] = mesh->mMaterialIndex; + } + + // Process all children recursively + for (unsigned int i = 0; i < node->mNumChildren; i++) { + if (!load_recursive(importer, model, node->mChildren[i], &globalTransform)) { + return false; + } + } + + return true; +} + +// ======================================== +// PUBLIC FUNCTIONS +// ======================================== + +bool r3d_importer_load_meshes(const R3D_Importer* importer, R3D_Model* model) +{ + if (!model || !importer || !r3d_importer_is_valid(importer)) { + R3D_TRACELOG(LOG_ERROR, "Invalid parameters for mesh loading"); + return false; + } + + bool keepMeshData = BIT_TEST(importer->flags, R3D_IMPORT_MESH_DATA); + bool keepMeshNames = BIT_TEST(importer->flags, R3D_IMPORT_MESH_NAMES); + + const struct aiScene* scene = r3d_importer_get_scene(importer); + + // Allocate space for meshes + model->meshCount = scene->mNumMeshes; + model->meshes = RL_CALLOC(model->meshCount, sizeof(*model->meshes)); + model->meshMaterials = RL_CALLOC(model->meshCount, sizeof(*model->meshMaterials)); + if (keepMeshData) model->meshData = RL_CALLOC(model->meshCount, sizeof(*model->meshData)); + if (keepMeshNames) model->meshNames = RL_CALLOC(model->meshCount, sizeof(*model->meshNames)); + if (!model->meshes || !model->meshMaterials || (keepMeshData && !model->meshData) || (keepMeshNames && !model->meshNames)) { + R3D_TRACELOG(LOG_ERROR, "Unable to allocate memory for meshes"); + goto cleanup_and_fail; + } + + // Load all meshes recursively + if (!load_recursive(importer, model, r3d_importer_get_root(importer), &R3D_MATRIX_IDENTITY)) { + goto cleanup_and_fail; + } + + // Calculate model bounding box + model->aabb.min = (Vector3) {+FLT_MAX, +FLT_MAX, +FLT_MAX}; + model->aabb.max = (Vector3) {-FLT_MAX, -FLT_MAX, -FLT_MAX}; + for (int i = 0; i < model->meshCount; i++) { + model->aabb.min = Vector3Min(model->aabb.min, model->meshes[i].aabb.min); + model->aabb.max = Vector3Max(model->aabb.max, model->meshes[i].aabb.max); + } + + // Slightly expands the bounding box of skinned models + if (scene->mNumSkeletons > 0) { + Vector3 center = Vector3Scale(Vector3Add(model->aabb.min, model->aabb.max), 0.5f); + Vector3 halfSz = Vector3Scale(Vector3Subtract(model->aabb.max, model->aabb.min), 0.5f); + halfSz = Vector3Multiply(halfSz, (Vector3) {1.4f, 1.2f, 1.4f}); + model->aabb.min = Vector3Subtract(center, halfSz); + model->aabb.max = Vector3Add(center, halfSz); + } + + return true; + +cleanup_and_fail: + if (model->meshes) { + for (int i = 0; i < model->meshCount; i++) { + R3D_UnloadMesh(model->meshes[i]); + } + } + + if (model->meshData) { + for (int i = 0; i < model->meshCount; i++) { + R3D_UnloadMeshData(model->meshData[i]); + } + } + + RL_FREE(model->meshMaterials); + RL_FREE(model->meshData); + RL_FREE(model->meshes); + + model->meshMaterials = NULL; + model->meshData = NULL; + model->meshes = NULL; + model->meshCount = 0; + + return false; +} + +``` + +`src/importer/r3d_importer_skeleton.c`: + +```c +/* r3d_importer_skeleton.c -- Module to import skeleton from assimp scene. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_importer_internal.h" + +#include +#include +#include +#include +#include + +#include "../common/r3d_math.h" + +// ======================================== +// INTERNAL CONTEXT +// ======================================== + +typedef struct { + const R3D_Importer* importer; + R3D_BoneInfo* bones; + Matrix* invBind; + Matrix* localBind; + Matrix* modelBind; + Matrix* rootBind; + int boneCount; +} skeleton_build_context_t; + +// ======================================== +// RECURSIVE HIERARCHY BUILD +// ======================================== + +static void build_skeleton_recursive( + skeleton_build_context_t* ctx, + const struct aiNode* node, + Matrix parentTransform, + int parentIndex) +{ + if (!node) return; + + Matrix localTransform = r3d_importer_cast(node->mTransformation); + Matrix modelTransform = MatrixMultiply(localTransform, parentTransform); + + // Check if this node is a bone + int currentIndex = r3d_importer_get_bone_index(ctx->importer, node->mName.data); + + if (currentIndex >= 0) { + // Store bone matrices + ctx->localBind[currentIndex] = localTransform; + ctx->modelBind[currentIndex] = modelTransform; + + // Store bind root matrix + if (parentIndex == -1) { + Matrix invLocalTransform = MatrixInvert(localTransform); + *ctx->rootBind = MatrixMultiply(invLocalTransform, modelTransform); + } + + // Store bone infos + strncpy(ctx->bones[currentIndex].name, node->mName.data, sizeof(ctx->bones[currentIndex].name) - 1); + ctx->bones[currentIndex].name[sizeof(ctx->bones[currentIndex].name) - 1] = '\0'; + ctx->bones[currentIndex].parent = parentIndex; + + // This bone becomes the parent for its children + parentIndex = currentIndex; + } + + // Recursively process children + for (unsigned int i = 0; i < node->mNumChildren; i++) { + build_skeleton_recursive(ctx, node->mChildren[i], modelTransform, parentIndex); + } +} + +// ======================================== +// BIND POSE TEXTURE UPLOAD +// ======================================== + +static void upload_skeleton_bind_pose(R3D_Skeleton* skeleton) +{ + Matrix* skinBuffer = RL_MALLOC(skeleton->boneCount * sizeof(Matrix)); + for (int i = 0; i < skeleton->boneCount; i++) { + skinBuffer[i] = MatrixMultiply(skeleton->invBind[i], skeleton->modelBind[i]); + } + + glGenTextures(1, &skeleton->skinTexture); + glBindTexture(GL_TEXTURE_1D, skeleton->skinTexture); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16F, 4 * skeleton->boneCount, 0, GL_RGBA, GL_FLOAT, skinBuffer); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_1D, 0); + + RL_FREE(skinBuffer); +} + +// ======================================== +// PUBLIC FUNCTIONS +// ======================================== + +bool r3d_importer_load_skeleton(const R3D_Importer* importer, R3D_Skeleton* skeleton) +{ + if (!importer || !r3d_importer_is_valid(importer)) { + R3D_TRACELOG(LOG_ERROR, "Invalid importer for skeleton processing"); + return false; + } + + int boneCount = r3d_importer_get_bone_count(importer); + if (boneCount == 0) { + return true; // No skeleton in this model + } + + // Allocate bone arrays + skeleton->bones = RL_MALLOC(boneCount * sizeof(R3D_BoneInfo)); + skeleton->invBind = RL_MALLOC(boneCount * sizeof(Matrix)); + skeleton->localBind = RL_MALLOC(boneCount * sizeof(Matrix)); + skeleton->modelBind = RL_MALLOC(boneCount * sizeof(Matrix)); + skeleton->boneCount = boneCount; + + if (!skeleton->bones || !skeleton->invBind || !skeleton->localBind || !skeleton->modelBind) { + R3D_TRACELOG(LOG_ERROR, "Failed to allocate memory for skeleton bones"); + RL_FREE(skeleton->bones); + RL_FREE(skeleton->invBind); + RL_FREE(skeleton->localBind); + RL_FREE(skeleton->modelBind); + RL_FREE(skeleton); + return false; + } + + // Initialize parent indices to -1 (no parent) + for (int i = 0; i < boneCount; i++) { + skeleton->bones[i].parent = -1; + memset(skeleton->bones[i].name, 0, sizeof(skeleton->bones[i].name)); + } + + // Fill bone offsets from meshes + for (int m = 0; m < r3d_importer_get_mesh_count(importer); m++) { + const struct aiMesh* mesh = r3d_importer_get_mesh(importer, m); + + for (unsigned int b = 0; b < mesh->mNumBones; b++) { + const struct aiBone* bone = mesh->mBones[b]; + int boneIdx = r3d_importer_get_bone_index(importer, bone->mName.data); + + if (boneIdx >= 0) { + skeleton->invBind[boneIdx] = r3d_importer_cast(bone->mOffsetMatrix); + } + } + } + + // Build hierarchy and bind poses in single traversal + skeleton_build_context_t ctx = { + .importer = importer, + .bones = skeleton->bones, + .invBind = skeleton->invBind, + .localBind = skeleton->localBind, + .modelBind = skeleton->modelBind, + .rootBind = &skeleton->rootBind, + .boneCount = boneCount + }; + + build_skeleton_recursive(&ctx, r3d_importer_get_root(importer), R3D_MATRIX_IDENTITY, -1); + upload_skeleton_bind_pose(skeleton); + + return true; +} + +``` + +`src/importer/r3d_importer_texture.c`: + +```c +/* r3d_importer_texture_cache.c -- Module to load textures from assimp materials. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_importer_internal.h" +#include + +#include +#include +#include + +#ifdef _WIN32 +# define NOGDI +# define NOUSER +# define WIN32_LEAN_AND_MEAN +# include +# undef near +# undef far +#endif + +#if defined(R3D_NO_C11_THREADS) +# include +#else +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "../common/r3d_helper.h" +#include "../common/r3d_image.h" +#include "../common/r3d_hash.h" + +// ======================================== +// CONSTANTS +// ======================================== + +#define MAX_PATH_LENGTH 256 + +// ======================================== +// INTERNAL ALIASES +// ======================================== + +typedef uint64_t texture_key_t; + +// ======================================== +// INTERNAL STRUCTS +// ======================================== + +struct r3d_importer_texture_cache { + Texture2D* textures; // [materialCount][R3D_MAP_COUNT] + int materialCount; +}; + +typedef struct { + char paths[3][MAX_PATH_LENGTH]; + enum aiTextureMapMode wrap[2]; + bool hasRoughMetalORM; + bool isShininessORM; + bool isORM; +} texture_job_t; + +typedef struct { + Image image; + Texture2D texture; + TextureWrap wrapMode; + r3d_importer_texture_map_t map; + bool ownsImageData; +} texture_slot_t; + +typedef struct { + texture_key_t key; + int slotIndex; + UT_hash_handle hh; +} texture_entry_t; + +typedef struct { + const R3D_Importer* importer; + texture_job_t* jobs; + texture_slot_t* slots; + int slotCount; + atomic_int nextJob; + atomic_int* readySlots; + atomic_int writePos; + atomic_int readPos; +} loader_context_t; + +// ======================================== +// MEMORY ARENA ALLOCATOR +// ======================================== + +typedef struct { + void* base; + size_t used; + size_t capacity; +} memory_arena_t; + +static inline void* arena_alloc(memory_arena_t* arena, size_t size, size_t align) +{ + size_t padding = (align - (arena->used % align)) % align; + size_t required = arena->used + padding + size; + if (required > arena->capacity) return NULL; + + void* ptr = (char*)arena->base + arena->used + padding; + arena->used = required; + return ptr; +} + +#define ARENA_ALLOC(arena, type, count) \ + arena_alloc(arena, sizeof(type) * (count), _Alignof(type)) + +// ======================================== +// HELPER FUNCTIONS +// ======================================== + +static inline bool is_srgb(r3d_importer_texture_map_t map, R3D_ColorSpace space) +{ + return (space == R3D_COLORSPACE_SRGB && (map == R3D_MAP_ALBEDO || map == R3D_MAP_EMISSION)); +} + +static inline TextureWrap get_wrap_mode(enum aiTextureMapMode wrap) +{ + switch (wrap) { + case aiTextureMapMode_Wrap: return TEXTURE_WRAP_REPEAT; + case aiTextureMapMode_Mirror: return TEXTURE_WRAP_MIRROR_REPEAT; + case aiTextureMapMode_Clamp: + case aiTextureMapMode_Decal: + default: return TEXTURE_WRAP_CLAMP; + } +} + +static texture_key_t make_key_texture_job(const texture_job_t* job) +{ + uint64_t hash = R3D_HASH_FNV_OFFSET_BASIS_64; + + for (int i = 0; i < ARRAY_SIZE(job->paths); i++) { + if (job->paths[i][0] != '\0') { + hash ^= r3d_hash_fnv1a_64_str(job->paths[i]); + hash *= R3D_HASH_FNV_PRIME_64; + hash ^= (uint64_t)i; + } + else { + hash ^= (0xB16B00B500000000ULL | i); // :3 + hash *= R3D_HASH_FNV_PRIME_64; + } + } + + hash ^= ((uint64_t)job->wrap[0] << 32) | job->wrap[1]; + hash *= R3D_HASH_FNV_PRIME_64; + + return hash; +} + +// ======================================== +// DESCRIPTOR EXTRACTION +// ======================================== + +static bool texture_job_extract_data( + char* outPath, enum aiTextureMapMode* outWrap, + const struct aiMaterial* material, + enum aiTextureType type, + unsigned int index) +{ + struct aiString path = {0}; + if (aiGetMaterialTexture(material, type, index, &path, NULL, NULL, NULL, NULL, outWrap, NULL) != AI_SUCCESS) { + outPath[0] = '\0'; + return false; + } + strncpy(outPath, path.data, MAX_PATH_LENGTH - 1); + outPath[MAX_PATH_LENGTH - 1] = '\0'; + return true; +} + +static bool texture_job_extract_albedo(texture_job_t* job, const struct aiMaterial* material) +{ + if (texture_job_extract_data(job->paths[0], job->wrap, material, aiTextureType_BASE_COLOR, 0)) { + return true; + } + return texture_job_extract_data(job->paths[0], job->wrap, material, aiTextureType_DIFFUSE, 0); +} + +static bool texture_job_extract_emission(texture_job_t* job, const struct aiMaterial* material) +{ + return texture_job_extract_data(job->paths[0], job->wrap, material, aiTextureType_EMISSIVE, 0); +} + +static bool texture_job_extract_orm(texture_job_t* job, const struct aiMaterial* material) +{ + job->isORM = true; + + // Try to extract occlusion + bool hasOcclusion = texture_job_extract_data(job->paths[0], job->wrap, material, aiTextureType_AMBIENT_OCCLUSION, 0); + if (!hasOcclusion) { + hasOcclusion = texture_job_extract_data(job->paths[0], job->wrap, material, aiTextureType_LIGHTMAP, 0); + } + + // Try to extract PBR Metallic Roughness (glTF) + // If we have it, we can finish the extraction sooner + bool hasRoughness = texture_job_extract_data(job->paths[1], job->wrap, material, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); + if (hasRoughness) { + job->hasRoughMetalORM = true; + return true; + } + + // Try to extract roughness or shininess + hasRoughness = texture_job_extract_data(job->paths[1], job->wrap, material, aiTextureType_DIFFUSE_ROUGHNESS, 0); + if (!hasRoughness) { + hasRoughness = texture_job_extract_data(job->paths[1], job->wrap, material, aiTextureType_SHININESS, 0); + if (hasRoughness) job->isShininessORM = true; + } + + // Try to extract metalness + bool hasMetalness = texture_job_extract_data(job->paths[2], job->wrap, material, aiTextureType_METALNESS, 0); + + // Success if we have at least one texture + return (hasOcclusion || hasRoughness || hasMetalness); +} + +static bool texture_job_extract_normal(texture_job_t* job, const struct aiMaterial* material) +{ + return texture_job_extract_data(job->paths[0], job->wrap, material, aiTextureType_NORMALS, 0); +} + +static bool texture_job_init(texture_job_t* job, const struct aiMaterial* material, r3d_importer_texture_map_t mapIdx) +{ + switch (mapIdx) { + case R3D_MAP_ALBEDO: return texture_job_extract_albedo(job, material); + case R3D_MAP_EMISSION: return texture_job_extract_emission(job, material); + case R3D_MAP_ORM: return texture_job_extract_orm(job, material); + case R3D_MAP_NORMAL: return texture_job_extract_normal(job, material); + default: assert(false); break; + } + return false; +} + +// ======================================== +// IMAGE LOADING +// ======================================== + +static bool load_image_base(Image* outImage, bool* outOwned, const R3D_Importer* importer, const char* path) +{ + if (path[0] == '*') { + int textureIndex = atoi(&path[1]); + const struct aiTexture* aiTex = r3d_importer_get_texture(importer, textureIndex); + + if (aiTex->mHeight == 0) { + char formatHint[sizeof(aiTex->achFormatHint) + 2] = {0}; + r3d_string_format(formatHint, sizeof(formatHint), ".%.*s", (int)sizeof(aiTex->achFormatHint), aiTex->achFormatHint); + + *outImage = LoadImageFromMemory( + formatHint, + (const unsigned char*)aiTex->pcData, + aiTex->mWidth + ); + *outOwned = (outImage->data != NULL); + } + else { + outImage->width = aiTex->mWidth; + outImage->height = aiTex->mHeight; + outImage->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + outImage->mipmaps = 1; + outImage->data = aiTex->pcData; + *outOwned = false; + } + } + else { + char fullPath[MAX_PATH_LENGTH]; + + // Standardize the separators (MTL can have Windows backslashes) + strncpy(fullPath, path, sizeof(fullPath) - 1); + fullPath[sizeof(fullPath) - 1] = '\0'; + for (char* p = fullPath; *p; p++) { + if (*p == '\\') *p = '/'; + } + + // Prepend base directory if path is relative + if (!r3d_is_absolute_path(fullPath)) { + const char* baseDir = GetDirectoryPath(importer->name); + // Shift the relative path to the right to make room for baseDir + size_t baseDirLen = strlen(baseDir); + size_t pathLen = strlen(fullPath); + memmove(fullPath + baseDirLen + 1, fullPath, pathLen + 1); + memcpy(fullPath, baseDir, baseDirLen); + fullPath[baseDirLen] = '/'; + } + + *outImage = LoadImage(fullPath); + *outOwned = (outImage->data != NULL); + } + + return outImage->data != NULL; +} + +static bool load_image_simple(texture_slot_t* slot, const R3D_Importer* importer, const texture_job_t* job) +{ + slot->wrapMode = get_wrap_mode(job->wrap[0]); + bool success = load_image_base(&slot->image, &slot->ownsImageData, importer, job->paths[0]); + if (!success) { + R3D_TRACELOG(LOG_WARNING, "Failed to load texture: %s", job->paths[0]); + } + return success; +} + +static bool load_image_orm(texture_slot_t* slot, const R3D_Importer* importer, const texture_job_t* job) +{ +# define ROUGHNESS_IDX 1 + + Image sources[3] = {0}; + bool owned[3] = {0}; + + // Load individual components + for (int i = 0; i < 3; i++) { + if (job->paths[i][0] != '\0') { + if (!load_image_base(&sources[i], &owned[i], importer, job->paths[i])) { + R3D_TRACELOG(LOG_WARNING, "Failed to load ORM component %d: %s", i, job->paths[i]); + } + if (i == ROUGHNESS_IDX && job->isShininessORM && sources[i].data) { + ImageColorInvert(&sources[i]); + } + } + } + + // Compose ORM + const Image* srcPtrs[3] = { + sources[0].data ? &sources[0] : NULL, + sources[1].data ? &sources[1] : NULL, + job->hasRoughMetalORM ? + (sources[1].data ? &sources[1] : NULL) : + (sources[2].data ? &sources[2] : NULL) + }; + + slot->image = r3d_image_compose_rgb(srcPtrs, WHITE); + slot->wrapMode = get_wrap_mode(job->wrap[0]); + slot->ownsImageData = true; + + // Free sources + for (int i = 0; i < 3; i++) { + if (owned[i] && sources[i].data) { + UnloadImage(sources[i]); + } + } + + bool success = (slot->image.data != NULL); + if (!success) { + R3D_TRACELOG(LOG_WARNING, "Failed to compose ORM texture"); + } + + return success; +} + +// ======================================== +// WORKER THREAD +// ======================================== + +static int worker_thread(void* arg) +{ + loader_context_t* ctx = (loader_context_t*)arg; + + while (true) + { + int jobIdx = atomic_fetch_add_explicit(&ctx->nextJob, 1, memory_order_relaxed); + if (jobIdx >= ctx->slotCount) break; + + texture_slot_t* slot = &ctx->slots[jobIdx]; + const texture_job_t* job = &ctx->jobs[jobIdx]; + + // Load image + if (job->isORM) load_image_orm(slot, ctx->importer, job); + else load_image_simple(slot, ctx->importer, job); + + // Add to upload queue (atomic reserve + store) + int pos = atomic_fetch_add_explicit(&ctx->writePos, 1, memory_order_relaxed); + atomic_store_explicit(&ctx->readySlots[pos], jobIdx, memory_order_release); + } + + return 0; +} + +// ======================================== +// PUBLIC FUNCTIONS +// ======================================== + +r3d_importer_texture_cache_t* r3d_importer_load_texture_cache( + const R3D_Importer* importer, + R3D_ColorSpace colorSpace, + TextureFilter filter) +{ + if (!importer || !r3d_importer_is_valid(importer)) { + R3D_TRACELOG(LOG_WARNING, "Invalid importer for texture loading"); + return NULL; + } + + /* --- Early exit: check if any material has textures before allocating --- */ + + int materialCount = r3d_importer_get_material_count(importer); + + bool hasAnyTexture = false; + for (int matIdx = 0; matIdx < materialCount && !hasAnyTexture; matIdx++) { + const struct aiMaterial* mat = r3d_importer_get_material(importer, matIdx); + for (int mapIdx = 0; mapIdx < R3D_MAP_COUNT && !hasAnyTexture; mapIdx++) { + texture_job_t job = {0}; + if (texture_job_init(&job, mat, mapIdx)) { + hasAnyTexture = true; + } + } + } + if (!hasAnyTexture) return NULL; + + /* --- Phase 0: Allocate a large block of memory for work --- */ + + int maxSlots = materialCount * R3D_MAP_COUNT; + + size_t arenaSize = + sizeof(texture_job_t) * maxSlots + // Jobs + sizeof(texture_slot_t) * maxSlots + // Slots + sizeof(texture_entry_t) * maxSlots + // Entries + sizeof(int) * maxSlots + // Material mapping + sizeof(thrd_t) * r3d_get_cpu_count() + // Thread handles + sizeof(atomic_int) * maxSlots + // Ready slots queue + 1024; // Padding for alignment + + void* arenaMemory = RL_CALLOC(arenaSize, 1); + memory_arena_t arena = {.base = arenaMemory, .used = 0, .capacity = arenaSize}; + + /* --- Phase 1: Collect unique textures --- */ + + texture_job_t* jobs = ARENA_ALLOC(&arena, texture_job_t, maxSlots); + texture_slot_t* slots = ARENA_ALLOC(&arena, texture_slot_t, maxSlots); + texture_entry_t* entries = ARENA_ALLOC(&arena, texture_entry_t, maxSlots); + + int* materialToSlot = ARENA_ALLOC(&arena, int, maxSlots); + for (int i = 0; i < maxSlots; i++) materialToSlot[i] = -1; + + texture_entry_t* hashTable = NULL; + int entryPoolUsed = 0; + int uniqueCount = 0; + + for (int matIdx = 0; matIdx < materialCount; matIdx++) + { + const struct aiMaterial* material = r3d_importer_get_material(importer, matIdx); + + for (int mapIdx = 0; mapIdx < R3D_MAP_COUNT; mapIdx++) + { + texture_job_t job = {0}; + if (!texture_job_init(&job, material, mapIdx)) continue; + + texture_entry_t* entry = NULL; + texture_key_t key = make_key_texture_job(&job); + HASH_FIND(hh, hashTable, &key, sizeof(key), entry); + + if (entry) { + // Reuse existing slot + materialToSlot[matIdx * R3D_MAP_COUNT + mapIdx] = entry->slotIndex; + continue; + } + + // New unique texture + entry = &entries[entryPoolUsed++]; + entry->key = key; + entry->slotIndex = uniqueCount; + HASH_ADD(hh, hashTable, key, sizeof(key), entry); + + jobs[uniqueCount] = job; + slots[uniqueCount].map = mapIdx; + + materialToSlot[matIdx * R3D_MAP_COUNT + mapIdx] = uniqueCount; + uniqueCount++; + } + } + + /* --- Phase 2: Parallel loading to RAM --- */ + + loader_context_t ctx = { + .importer = importer, + .jobs = jobs, + .slots = slots, + .slotCount = uniqueCount, + .readySlots = ARENA_ALLOC(&arena, atomic_int, uniqueCount) + }; + + atomic_init(&ctx.nextJob, 0); + atomic_init(&ctx.writePos, 0); + atomic_init(&ctx.readPos, 0); + + int numThreads = r3d_get_cpu_count(); + if (numThreads > uniqueCount) numThreads = uniqueCount; + + thrd_t* threads = ARENA_ALLOC(&arena, thrd_t, numThreads); + for (int i = 0; i < numThreads; i++) { + thrd_create(&threads[i], worker_thread, &ctx); + } + + /* --- Phase 3: Progressive upload to VRAM --- */ + + int processedCount = 0; + int uploadedCount = 0; + + while (processedCount < uniqueCount) + { + int readPos = atomic_load_explicit(&ctx.readPos, memory_order_acquire); + int writePos = atomic_load_explicit(&ctx.writePos, memory_order_acquire); + + if (writePos == readPos) { + struct timespec ts = {.tv_sec = 0, .tv_nsec = 1000000}; // 1ms + thrd_sleep(&ts, NULL); + continue; + } + + atomic_thread_fence(memory_order_acquire); + + for (int i = readPos; i < writePos; i++) { + int slotIdx = ctx.readySlots[i]; + texture_slot_t* slot = &slots[slotIdx]; + if (slot->image.data) { + slot->texture = r3d_image_upload(&slot->image, slot->wrapMode, filter, is_srgb(slot->map, colorSpace)); + if (slot->ownsImageData) { + UnloadImage(slot->image); + slot->image.data = NULL; + } + uploadedCount++; + } + processedCount++; + } + + atomic_store_explicit(&ctx.readPos, writePos, memory_order_release); + } + + for (int i = 0; i < numThreads; i++) { + thrd_join(threads[i], NULL); + } + + /* --- Phase 4: Build final cache --- */ + + Texture2D* finalTextures = RL_CALLOC(materialCount * R3D_MAP_COUNT, sizeof(Texture2D)); + for (int i = 0; i < maxSlots; i++) { + int slotIdx = materialToSlot[i]; + if (slotIdx >= 0) finalTextures[i] = slots[slotIdx].texture; + } + + r3d_importer_texture_cache_t* cache = RL_MALLOC(sizeof(*cache)); + cache->materialCount = materialCount; + cache->textures = finalTextures; + + /* --- Cleanup --- */ + + texture_entry_t* entry, *tmp; + HASH_ITER(hh, hashTable, entry, tmp) { + HASH_DEL(hashTable, entry); + } + RL_FREE(arenaMemory); + + if (uploadedCount == processedCount) { + R3D_TRACELOG(LOG_INFO, "Model textures cached: %d/%d textures loaded successfully", + uploadedCount, processedCount + ); + } + else { + R3D_TRACELOG(LOG_WARNING, "Model textures cached: %d/%d textures loaded (%d failed)", + uploadedCount, processedCount, processedCount - uploadedCount + ); + } + + return cache; +} + +void r3d_importer_unload_texture_cache(r3d_importer_texture_cache_t* cache, bool unloadTextures) +{ + if (!cache) return; + + if (unloadTextures) { + int textureCount = cache->materialCount * R3D_MAP_COUNT; + for (int i = 0; i < textureCount; i++) { + if (cache->textures[i].id != 0) { + UnloadTexture(cache->textures[i]); + } + } + } + + RL_FREE(cache->textures); + RL_FREE(cache); +} + +Texture2D* r3d_importer_get_loaded_texture(r3d_importer_texture_cache_t* cache, int materialIndex, r3d_importer_texture_map_t map) +{ + if (!cache || materialIndex < 0 || materialIndex >= cache->materialCount) return NULL; + Texture2D* tex = &cache->textures[materialIndex * R3D_MAP_COUNT + map]; + return (tex->id != 0) ? tex : NULL; +} + +``` + +`src/modules/r3d_driver.c`: + +```c +/* r3d_driver.c -- Internal R3D OpenGL cache module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_driver.h" +#include +#include +#include +#include +#include +#include + +// ======================================== +// CONFIGURATION +// ======================================== + +#define R3D_OPENGL_EXT_NAME_MAX 64 // Maximum length of a domain name extension +#define R3D_OPENGL_EXT_CACHE_MAX 32 // Maximum number of extensions to cache + +// ======================================== +// INTERNAL MACROS +// ======================================== + +/* + * Returns true if the pipeline entry needs to be updated. + */ +#define CHECK_PIPE(entry, type, value) \ + ((!R3D_MOD_DRIVER.pipeCache.entry.known) || (R3D_MOD_DRIVER.pipeCache.entry.v##type != (value))) + +/* + * Updates the specified pipeline entry. + */ +#define SET_PIPE(entry, type, value) do { \ + R3D_MOD_DRIVER.pipeCache.entry.v##type = (value); \ + R3D_MOD_DRIVER.pipeCache.entry.known = true; \ +} while(0) + +// ======================================== +// INTERNAL ENUMS +// ======================================== + +typedef enum { + CAP_STATE_UNKNOWN = 0, + CAP_STATE_ENABLED = 1, + CAP_STATE_DISABLED = 2, +} cap_state_t; + +typedef enum { + CAP_INDEX_BLEND = 0, + CAP_INDEX_CULL_FACE, + CAP_INDEX_DEPTH_TEST, + CAP_INDEX_SCISSOR_TEST, + CAP_INDEX_STENCIL_TEST, + CAP_INDEX_POLYGON_OFFSET_FILL, + CAP_INDEX_POLYGON_OFFSET_LINE, + CAP_INDEX_POLYGON_OFFSET_POINT, + CAP_INDEX_COUNT +} cap_index_t; + +// ======================================== +// INTERNAL STRUCTS +// ======================================== + +typedef struct { + char name[R3D_OPENGL_EXT_NAME_MAX]; // key (extension name, inline) + bool supported; // value (supported extension?) + UT_hash_handle hh; // uthash handle +} extension_entry_t; + +typedef struct { + extension_entry_t array[R3D_OPENGL_EXT_CACHE_MAX]; // extension cache buffer + extension_entry_t* head; // uthash pointer + size_t count; // number of entries used +} extension_cache_t; + +typedef struct { + union { + float vfloat; + GLenum venum; + uint8_t vbyte; + GLboolean vbool; + }; + bool known; +} pipeline_entry_t; + +typedef struct { + cap_state_t capStates[CAP_INDEX_COUNT]; + pipeline_entry_t cullFace; + pipeline_entry_t depthFunc; + pipeline_entry_t depthMask; + pipeline_entry_t depthNear; + pipeline_entry_t depthFar; + pipeline_entry_t depthUnits; + pipeline_entry_t depthFactor; + pipeline_entry_t stencilFunc; + pipeline_entry_t stencilRef; + pipeline_entry_t stencilMask; + pipeline_entry_t stencilOpFail; + pipeline_entry_t stencilOpZFail; + pipeline_entry_t stencilOpZPass; + pipeline_entry_t blendEquation; + pipeline_entry_t blendSrcFactor; + pipeline_entry_t blendDstFactor; + GLint viewport[4]; +} pipeline_cache_t; + +typedef struct { + GLuint queryID; + const char* label; +} driver_timer_t; + +// ======================================== +// MODULE STATE +// ======================================== + +static struct r3d_driver { + extension_cache_t extCache; + pipeline_cache_t pipeCache; + driver_timer_t timer; +} R3D_MOD_DRIVER; + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +/* Returns -1 if capability is not tracked */ +static int get_capability_index(GLenum cap) +{ + switch (cap) { + case GL_BLEND: return CAP_INDEX_BLEND; + case GL_CULL_FACE: return CAP_INDEX_CULL_FACE; + case GL_DEPTH_TEST: return CAP_INDEX_DEPTH_TEST; + case GL_SCISSOR_TEST: return CAP_INDEX_SCISSOR_TEST; + case GL_STENCIL_TEST: return CAP_INDEX_STENCIL_TEST; + case GL_POLYGON_OFFSET_FILL: return CAP_INDEX_POLYGON_OFFSET_FILL; + case GL_POLYGON_OFFSET_LINE: return CAP_INDEX_POLYGON_OFFSET_LINE; + case GL_POLYGON_OFFSET_POINT: return CAP_INDEX_POLYGON_OFFSET_POINT; + default: break; + } + return -1; +} + +/* Returns true if the given extension is supported */ +static bool query_ext(const char* name) +{ + GLint numExtensions = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); + + for (GLint i = 0; i < numExtensions; i++) { + const char* ext = (const char*)glGetStringi(GL_EXTENSIONS, i); + if (ext && strcmp(ext, name) == 0) return true; + } + + return false; +} + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +bool r3d_driver_init(void) +{ + memset(&R3D_MOD_DRIVER, 0, sizeof(R3D_MOD_DRIVER)); + return true; +} + +void r3d_driver_quit(void) +{ + extension_entry_t* current, * tmp; + HASH_ITER(hh, R3D_MOD_DRIVER.extCache.head, current, tmp) { + HASH_DEL(R3D_MOD_DRIVER.extCache.head, current); + } +} + +bool r3d_driver_check_ext(const char* name) +{ + if (!name) return false; + + // Name too long: skip cache, query directly + if (strlen(name) >= R3D_OPENGL_EXT_NAME_MAX) { + return query_ext(name); + } + + // Search the cache + extension_entry_t* cached = NULL; + HASH_FIND_STR(R3D_MOD_DRIVER.extCache.head, name, cached); + if (cached) return cached->supported; + + // Query OpenGL and cache the result if space available + bool supported = query_ext(name); + + if (R3D_MOD_DRIVER.extCache.count < R3D_OPENGL_EXT_CACHE_MAX) { + extension_entry_t* entry = &R3D_MOD_DRIVER.extCache.array[R3D_MOD_DRIVER.extCache.count++]; + strncpy(entry->name, name, R3D_OPENGL_EXT_NAME_MAX - 1); + entry->name[R3D_OPENGL_EXT_NAME_MAX - 1] = '\0'; + entry->supported = supported; + HASH_ADD_STR(R3D_MOD_DRIVER.extCache.head, name, entry); + } + + return supported; +} + +bool r3d_driver_has_anisotropy(float* max) +{ + static bool checked = false; + static bool hasAniso = false; + static float maxAniso = 1.0f; + + if (max) *max = 1.0f; + + if (!checked) { + hasAniso = r3d_driver_check_ext("GL_EXT_texture_filter_anisotropic"); + if (hasAniso) { + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAniso); + } + checked = true; + } + + if (max && hasAniso) { + *max = maxAniso; + } + + return hasAniso; +} + +void r3d_driver_clear_errors(void) +{ + while (glGetError() != GL_NO_ERROR); +} + +bool r3d_driver_check_error(const char* msg) +{ + int err = glGetError(); + if (err != GL_NO_ERROR) { + R3D_TRACELOG(LOG_ERROR, "OpenGL Error (%s): 0x%04x", msg, err); + return true; + } + return false; +} + +void r3d_driver_enable(GLenum cap) +{ + int index = get_capability_index(cap); + if (index < 0) { glEnable(cap); return; } + + if (R3D_MOD_DRIVER.pipeCache.capStates[index] != CAP_STATE_ENABLED) { + R3D_MOD_DRIVER.pipeCache.capStates[index] = CAP_STATE_ENABLED; + glEnable(cap); + } +} + +void r3d_driver_disable(GLenum cap) +{ + int index = get_capability_index(cap); + if (index < 0) { glDisable(cap); return; } + + if (R3D_MOD_DRIVER.pipeCache.capStates[index] != CAP_STATE_DISABLED) { + R3D_MOD_DRIVER.pipeCache.capStates[index] = CAP_STATE_DISABLED; + glDisable(cap); + } +} + +void r3d_driver_set_cull_face(GLenum face) +{ + if (CHECK_PIPE(cullFace, enum, face)) { + SET_PIPE(cullFace, enum, face); + glCullFace(face); + } +} + +void r3d_driver_set_depth_func(GLenum func) +{ + if (CHECK_PIPE(depthFunc, enum, func)) { + SET_PIPE(depthFunc, enum, func); + glDepthFunc(func); + } +} + +void r3d_driver_set_depth_mask(GLboolean mask) +{ + if (CHECK_PIPE(depthMask, bool, mask)) { + SET_PIPE(depthMask, bool, mask); + glDepthMask(mask); + } +} + +void r3d_driver_set_depth_range(float dNear, float dFar) +{ + if (CHECK_PIPE(depthNear, float, dNear) || CHECK_PIPE(depthFar, float, dFar)) { + SET_PIPE(depthNear, float, dNear); + SET_PIPE(depthFar, float, dFar); + glDepthRange(dNear, dFar); + } +} + +void r3d_driver_set_depth_offset(float units, float factor) +{ + if (CHECK_PIPE(depthUnits, float, units) || CHECK_PIPE(depthFactor, float, factor)) + { + SET_PIPE(depthUnits, float, units); + SET_PIPE(depthFactor, float, factor); + + if (units != 0.0f || factor != 0.0f) { + r3d_driver_enable(GL_POLYGON_OFFSET_POINT); + r3d_driver_enable(GL_POLYGON_OFFSET_LINE); + r3d_driver_enable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(factor, units); + } + else { + r3d_driver_disable(GL_POLYGON_OFFSET_POINT); + r3d_driver_disable(GL_POLYGON_OFFSET_LINE); + r3d_driver_disable(GL_POLYGON_OFFSET_FILL); + } + } +} + +void r3d_driver_set_stencil_func(GLenum func, uint8_t ref, uint8_t mask) +{ + if (CHECK_PIPE(stencilFunc, enum, func) || CHECK_PIPE(stencilRef, byte, ref) || CHECK_PIPE(stencilMask, byte, mask)) { + SET_PIPE(stencilFunc, enum, func); + SET_PIPE(stencilMask, byte, mask); + SET_PIPE(stencilRef, byte, ref); + glStencilFunc(func, ref, mask); + } +} + +void r3d_driver_set_stencil_op(GLenum fail, GLenum zFail, GLenum zPass) +{ + if (CHECK_PIPE(stencilOpFail, enum, fail) || CHECK_PIPE(stencilOpZFail, enum, zFail) || CHECK_PIPE(stencilOpZPass, enum, zPass)) { + SET_PIPE(stencilOpFail, enum, fail); + SET_PIPE(stencilOpZFail, enum, zFail); + SET_PIPE(stencilOpZPass, enum, zPass); + glStencilOp(fail, zFail, zPass); + } +} + +void r3d_driver_set_stencil_mask(uint8_t mask) +{ + if (CHECK_PIPE(stencilMask, byte, mask)) { + SET_PIPE(stencilMask, byte, mask); + glStencilMask(mask); + } +} + +void r3d_driver_set_blend_func(GLenum equation, GLenum srcFactor, GLenum dstFactor) +{ + if (CHECK_PIPE(blendEquation, enum, equation)) { + SET_PIPE(blendEquation, enum, equation); + glBlendEquation(equation); + } + + if (CHECK_PIPE(blendSrcFactor, enum, srcFactor) || CHECK_PIPE(blendDstFactor, enum, dstFactor)) { + SET_PIPE(blendSrcFactor, enum, srcFactor); + SET_PIPE(blendDstFactor, enum, dstFactor); + glBlendFunc(srcFactor, dstFactor); + } +} + +void r3d_driver_set_depth_state(R3D_DepthState state) +{ + GLenum glFunc; + switch (state.mode) { + case R3D_COMPARE_LESS: glFunc = GL_LESS; break; + case R3D_COMPARE_LEQUAL: glFunc = GL_LEQUAL; break; + case R3D_COMPARE_EQUAL: glFunc = GL_EQUAL; break; + case R3D_COMPARE_GREATER: glFunc = GL_GREATER; break; + case R3D_COMPARE_GEQUAL: glFunc = GL_GEQUAL; break; + case R3D_COMPARE_NOTEQUAL: glFunc = GL_NOTEQUAL; break; + case R3D_COMPARE_ALWAYS: glFunc = GL_ALWAYS; break; + case R3D_COMPARE_NEVER: glFunc = GL_NEVER; break; + default: glFunc = GL_ALWAYS; break; + } + + r3d_driver_set_depth_func(glFunc); + r3d_driver_set_depth_range(state.rangeNear, state.rangeFar); + r3d_driver_set_depth_offset(state.offsetUnits, state.offsetFactor); +} + +void r3d_driver_set_stencil_state(R3D_StencilState state) +{ + GLenum glFunc; + switch (state.mode) { + case R3D_COMPARE_LESS: glFunc = GL_LESS; break; + case R3D_COMPARE_LEQUAL: glFunc = GL_LEQUAL; break; + case R3D_COMPARE_EQUAL: glFunc = GL_EQUAL; break; + case R3D_COMPARE_GREATER: glFunc = GL_GREATER; break; + case R3D_COMPARE_GEQUAL: glFunc = GL_GEQUAL; break; + case R3D_COMPARE_NOTEQUAL: glFunc = GL_NOTEQUAL; break; + case R3D_COMPARE_ALWAYS: glFunc = GL_ALWAYS; break; + case R3D_COMPARE_NEVER: glFunc = GL_NEVER; break; + default: glFunc = GL_ALWAYS; break; + } + + GLenum glOpFail, glOpZFail, glOpPass; + + switch (state.opFail) { + case R3D_STENCIL_KEEP: glOpFail = GL_KEEP; break; + case R3D_STENCIL_ZERO: glOpFail = GL_ZERO; break; + case R3D_STENCIL_REPLACE: glOpFail = GL_REPLACE; break; + case R3D_STENCIL_INCR: glOpFail = GL_INCR; break; + case R3D_STENCIL_DECR: glOpFail = GL_DECR; break; + default: glOpFail = GL_KEEP; break; + } + + switch (state.opZFail) { + case R3D_STENCIL_KEEP: glOpZFail = GL_KEEP; break; + case R3D_STENCIL_ZERO: glOpZFail = GL_ZERO; break; + case R3D_STENCIL_REPLACE: glOpZFail = GL_REPLACE; break; + case R3D_STENCIL_INCR: glOpZFail = GL_INCR; break; + case R3D_STENCIL_DECR: glOpZFail = GL_DECR; break; + default: glOpZFail = GL_KEEP; break; + } + + switch (state.opPass) { + case R3D_STENCIL_KEEP: glOpPass = GL_KEEP; break; + case R3D_STENCIL_ZERO: glOpPass = GL_ZERO; break; + case R3D_STENCIL_REPLACE: glOpPass = GL_REPLACE; break; + case R3D_STENCIL_INCR: glOpPass = GL_INCR; break; + case R3D_STENCIL_DECR: glOpPass = GL_DECR; break; + default: glOpPass = GL_KEEP; break; + } + + r3d_driver_set_stencil_func(glFunc, state.ref, state.mask); + r3d_driver_set_stencil_op(glOpFail, glOpZFail, glOpPass); +} + +void r3d_driver_set_blend_mode(R3D_BlendMode blend, R3D_TransparencyMode transparency) +{ + switch (blend) { + case R3D_BLEND_MIX: + if (transparency == R3D_TRANSPARENCY_DISABLED) { + r3d_driver_set_blend_func(GL_FUNC_ADD, GL_ONE, GL_ZERO); + } + else { + r3d_driver_set_blend_func(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + break; + case R3D_BLEND_ADDITIVE: + if (transparency == R3D_TRANSPARENCY_DISABLED) { + r3d_driver_set_blend_func(GL_FUNC_ADD, GL_ONE, GL_ONE); + } + else { + r3d_driver_set_blend_func(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE); + } + break; + case R3D_BLEND_MULTIPLY: + r3d_driver_set_blend_func(GL_FUNC_ADD, GL_DST_COLOR, GL_ZERO); + break; + case R3D_BLEND_PREMULTIPLIED_ALPHA: + r3d_driver_set_blend_func(GL_FUNC_ADD, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + break; + default: + break; + } +} + +void r3d_driver_set_cull_mode(R3D_CullMode mode) +{ + switch (mode) { + case R3D_CULL_NONE: + r3d_driver_disable(GL_CULL_FACE); + break; + case R3D_CULL_BACK: + r3d_driver_enable(GL_CULL_FACE); + r3d_driver_set_cull_face(GL_BACK); + break; + case R3D_CULL_FRONT: + r3d_driver_enable(GL_CULL_FACE); + r3d_driver_set_cull_face(GL_FRONT); + break; + default: + break; + } +} + +void r3d_driver_set_shadow_cast_mode(R3D_ShadowCastMode castMode, R3D_CullMode cullMode) +{ + switch (castMode) { + case R3D_SHADOW_CAST_ON_AUTO: + case R3D_SHADOW_CAST_ONLY_AUTO: + r3d_driver_set_cull_mode(cullMode); + break; + + case R3D_SHADOW_CAST_ON_DOUBLE_SIDED: + case R3D_SHADOW_CAST_ONLY_DOUBLE_SIDED: + r3d_driver_set_cull_mode(R3D_CULL_NONE); + break; + + case R3D_SHADOW_CAST_ON_FRONT_SIDE: + case R3D_SHADOW_CAST_ONLY_FRONT_SIDE: + r3d_driver_set_cull_mode(R3D_CULL_BACK); + break; + + case R3D_SHADOW_CAST_ON_BACK_SIDE: + case R3D_SHADOW_CAST_ONLY_BACK_SIDE: + r3d_driver_set_cull_mode(R3D_CULL_FRONT); + break; + + case R3D_SHADOW_CAST_DISABLED: + default: + assert(false && "This shouldn't happen"); + break; + } +} + +void r3d_driver_timer_start(const char* label) +{ + driver_timer_t* t = &R3D_MOD_DRIVER.timer; + t->label = label; + + if (t->queryID == 0) { + glGenQueries(1, &t->queryID); + } + + glBeginQuery(GL_TIME_ELAPSED, t->queryID); +} + +double r3d_driver_timer_stop(void) +{ + driver_timer_t* t = &R3D_MOD_DRIVER.timer; + glEndQuery(GL_TIME_ELAPSED); + + GLuint64 timeElapsed = 0; + glGetQueryObjectui64v(t->queryID, GL_QUERY_RESULT, &timeElapsed); + + double ms = (double)timeElapsed / 1e6; + + if (t->label) { + R3D_TRACELOG(LOG_INFO, "[TIMER] %s: %.3f ms\n", t->label, ms); + } + + return ms; +} + +void r3d_driver_store_viewport(void) +{ + glGetIntegerv(GL_VIEWPORT, R3D_MOD_DRIVER.pipeCache.viewport); +} + +void r3d_driver_restore_viewport(void) +{ + const GLint* vp = R3D_MOD_DRIVER.pipeCache.viewport; + glViewport(vp[0], vp[1], vp[2], vp[3]); +} + +void r3d_driver_invalidate_cache(void) +{ + memset(&R3D_MOD_DRIVER.pipeCache, 0, sizeof(R3D_MOD_DRIVER.pipeCache)); +} + +``` + +`src/modules/r3d_driver.h`: + +```h +/* r3d_driver.h -- Internal R3D OpenGL cache module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_MODULE_OPENGL_H +#define R3D_MODULE_OPENGL_H + +#include +#include +#include +#include +#include + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + + +/* Initialize module (called once during R3D_Init) */ +bool r3d_driver_init(void); + +/* Deinitialize module (called once during R3D_Close) */ +void r3d_driver_quit(void); + +/* + * Checks if an OpenGL extension is supported. Results are cached. + */ +bool r3d_driver_check_ext(const char* name); + +/* + * Checks if anisotropic filtering is supported. + * Optionally returns the max level. + */ +bool r3d_driver_has_anisotropy(float* max); + +/* + * Clears all pending OpenGL errors. + */ +void r3d_driver_clear_errors(void); + +/* + * Checks for OpenGL errors and logs them if present. + * Returns true if an error occurred. + */ +bool r3d_driver_check_error(const char* msg); + +/* + * Enables an OpenGL capability. + * Cached to avoid redundant state changes. + */ +void r3d_driver_enable(GLenum state); + +/* + * Disables an OpenGL capability. + * Cached to avoid redundant state changes. + */ +void r3d_driver_disable(GLenum state); + +/* + * Sets which faces to cull (GL_FRONT, GL_BACK, GL_FRONT_AND_BACK). + */ +void r3d_driver_set_cull_face(GLenum face); + +/* + * Sets the depth comparison function. + */ +void r3d_driver_set_depth_func(GLenum func); + +/* + * Enables or disables writing to the depth buffer. + */ +void r3d_driver_set_depth_mask(GLboolean mask); + +/* + * Sets the depth range mapping. + */ +void r3d_driver_set_depth_range(float dNear, float dFar); + +/* + * Sets the depth polygon offset. + * Automatically enables or disables GL_POLYGON_OFFSET_XXX. + */ +void r3d_driver_set_depth_offset(float units, float factor); + +/* + * Sets the stencil test function, reference value, and mask. + */ +void r3d_driver_set_stencil_func(GLenum func, uint8_t ref, uint8_t mask); + +/* + * Sets stencil operations for different test outcomes. + */ +void r3d_driver_set_stencil_op(GLenum fail, GLenum zFail, GLenum zPass); + +/* + * Sets the stencil mask. + */ +void r3d_driver_set_stencil_mask(uint8_t mask); + +/* + * Sets the blend equation and blend factors. + */ +void r3d_driver_set_blend_func(GLenum equation, GLenum srcFactor, GLenum dstFactor); + +/* + * Applies the given depth state. + * Assumes that GL_DEPTH_TEST is already enabled. + * Automatically enables or disables GL_POLYGON_OFFSET_XXX. + */ +void r3d_driver_set_depth_state(R3D_DepthState state); + +/* + * Applies the given stencil state. + * Assumes that GL_STENCIL_TEST is already enabled. + */ +void r3d_driver_set_stencil_state(R3D_StencilState state); + +/* + * Applies the given blend mode. + * Assumes that GL_BLEND is already enabled. + * Some modes like MIX or ADD behave differently depending on the transparency mode. + */ +void r3d_driver_set_blend_mode(R3D_BlendMode blend, R3D_TransparencyMode transparency); + +/* + * Applies the given cull mode. + * Automatically enables or disables GL_CULL_FACE. + */ +void r3d_driver_set_cull_mode(R3D_CullMode mode); + +/* + * Applies the given cull mode depending on shadow casting mode. + * Automatically enables or disables GL_CULL_FACE. + */ +void r3d_driver_set_shadow_cast_mode(R3D_ShadowCastMode castMode, R3D_CullMode cullMode); + +/* + * Starts a GPU timer to measure the elapsed time of a code section. + * Uses a GL_TIME_ELAPSED query and stores the label for optional logging. + * DEBUG ONLY: may stall the CPU if the GPU has not finished. + */ +void r3d_driver_timer_start(const char* label); + +/* + * Stops the GPU timer started with r3d_driver_timer_start. + * Retrieves the GPU time in milliseconds and logs it if a label was provided. + * DEBUG ONLY: may stall the CPU if the GPU has not finished. + */ +double r3d_driver_timer_stop(void); + +/* + * Stores the current viewport state into the cache. + * The cached state persists until the next call to r3d_driver_invalidate_cache. + */ +void r3d_driver_store_viewport(void); + +/* + * Restores the viewport state from the cache. + * Should be called after r3d_driver_store_viewport to reset the viewport + * to the state it was in before any internal rendering operations. + */ +void r3d_driver_restore_viewport(void); + +/* + * Invalidates the entire pipeline cache. + */ +void r3d_driver_invalidate_cache(void); + +#endif // R3D_MODULE_OPENGL_H + +``` + +`src/modules/r3d_env.c`: + +```c +/* r3d_env.c -- Internal R3D environment module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_env.h" + +#include +#include +#include +#include +#include +#include + +#include "../common/r3d_helper.h" +#include "../common/r3d_math.h" + +// ======================================== +// CONSTANTS +// ======================================== + +#define PROBE_INITIAL_CAPACITY 16 +#define LAYER_GROWTH 4 + +// ======================================== +// MODULE STATE +// ======================================== + +struct r3d_env R3D_MOD_ENV; + +// ======================================== +// LAYER POOL FUNCTIONS +// ======================================== + +static bool layer_pool_init(r3d_env_layer_pool_t* pool, int initialCapacity) +{ + pool->freeCapacity = initialCapacity * 2; + pool->freeLayers = RL_MALLOC(pool->freeCapacity * sizeof(int)); + pool->freeCount = 0; + pool->totalLayers = 0; + return (pool->freeLayers != NULL); +} + +static void layer_pool_quit(r3d_env_layer_pool_t* pool) +{ + RL_FREE(pool->freeLayers); + memset(pool, 0, sizeof(*pool)); +} + +static int layer_pool_reserve(r3d_env_layer_pool_t* pool) +{ + if (pool->freeCount > 0) { + return pool->freeLayers[--pool->freeCount]; + } + return -1; // Needs expansion +} + +static void layer_pool_release(r3d_env_layer_pool_t* pool, int layer) +{ + if (layer < 0 || layer >= pool->totalLayers) return; + if (pool->freeCount < pool->freeCapacity) { + pool->freeLayers[pool->freeCount++] = layer; + } +} + +static bool layer_pool_expand(r3d_env_layer_pool_t* pool, int addCount) +{ + int oldTotal = pool->totalLayers; + int newTotal = oldTotal + addCount; + + // Reallocate free layers array if needed + if (pool->freeCount + addCount > pool->freeCapacity) { + pool->freeCapacity = newTotal; + int* newFree = RL_REALLOC(pool->freeLayers, pool->freeCapacity * sizeof(int)); + if (!newFree) return false; + pool->freeLayers = newFree; + } + + // Add new layers to free list + for (int i = oldTotal; i < newTotal; i++) { + pool->freeLayers[pool->freeCount++] = i; + } + + pool->totalLayers = newTotal; + return true; +} + +// ======================================== +// TEXTURE FUNCTIONS +// ======================================== + +static bool alloc_depth_stencil_renderbuffer(GLuint renderbuffer, int size) +{ + glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, size, size); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + return true; +} + +// ======================================== +// CUBEMAP FUNCTIONS +// ======================================== + +typedef struct { + int size; + int layers; + int mipLevels; + GLenum target; +} cubemap_spec_t; + +static inline cubemap_spec_t cubemap_spec(int size, int layers, bool mipmapped) +{ + return (cubemap_spec_t) { + .size = size, + .layers = layers, + .mipLevels = mipmapped ? r3d_get_mip_levels_1d(size) : 1, + .target = (layers > 0) ? GL_TEXTURE_CUBE_MAP_ARRAY : GL_TEXTURE_CUBE_MAP + }; +} + +static bool allocate_cubemap(GLuint texture, cubemap_spec_t spec) +{ + glBindTexture(spec.target, texture); + + for (int level = 0; level < spec.mipLevels; level++) { + int mipSize = spec.size >> level; + if (mipSize < 1) mipSize = 1; + + if (spec.target == GL_TEXTURE_CUBE_MAP_ARRAY) { + glTexImage3D( + spec.target, level, GL_RGB16F, + mipSize, mipSize, spec.layers * 6, + 0, GL_RGB, GL_FLOAT, NULL + ); + } + else { + for (int face = 0; face < 6; face++) { + glTexImage2D( + GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, GL_RGB16F, + mipSize, mipSize, 0, GL_RGB, GL_FLOAT, NULL + ); + } + } + } + + GLenum minFilter = (spec.mipLevels > 1) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR; + glTexParameteri(spec.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(spec.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(spec.target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(spec.target, GL_TEXTURE_MIN_FILTER, minFilter); + glTexParameteri(spec.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(spec.target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(spec.target, GL_TEXTURE_MAX_LEVEL, spec.mipLevels - 1); + + glBindTexture(spec.target, 0); + return true; +} + +static bool resize_cubemap_array(GLuint* texture, cubemap_spec_t oldSpec, cubemap_spec_t newSpec) +{ + GLuint newTexture; + glGenTextures(1, &newTexture); + + if (!allocate_cubemap(newTexture, newSpec)) { + glDeleteTextures(1, &newTexture); + return false; + } + + if (oldSpec.layers > 0 && *texture != 0) { + glBindFramebuffer(GL_FRAMEBUFFER, R3D_MOD_ENV.workFramebuffer); + for (int level = 0; level < oldSpec.mipLevels; level++) { + int mipSize = oldSpec.size >> level; + if (mipSize < 1) mipSize = 1; + for (int layer = 0; layer < oldSpec.layers; layer++) { + for (int face = 0; face < 6; face++) { + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, *texture, level, layer * 6 + face); + glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, newTexture); + glCopyTexSubImage3D( + GL_TEXTURE_CUBE_MAP_ARRAY, level, + 0, 0, layer * 6 + face, + 0, 0, mipSize, mipSize + ); + } + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, 0); + } + + if (*texture != 0) { + glDeleteTextures(1, texture); + } + *texture = newTexture; + + return true; +} + +static bool expand_cubemap_capacity(GLuint* texture, r3d_env_layer_pool_t* pool, int size, bool mipmapped) +{ + cubemap_spec_t oldSpec = cubemap_spec(size, pool->totalLayers, mipmapped); + cubemap_spec_t newSpec = cubemap_spec(size, pool->totalLayers + LAYER_GROWTH, mipmapped); + + if (!resize_cubemap_array(texture, oldSpec, newSpec)) { + return false; + } + + return layer_pool_expand(pool, LAYER_GROWTH); +} + +// ======================================== +// PROBE FUNCTIONS +// ======================================== + +static bool growth_probe_arrays(void) +{ + int newCapacity = 2 * R3D_MOD_ENV.capacityProbes; + + r3d_env_probe_t* newProbes = RL_REALLOC(R3D_MOD_ENV.probes, newCapacity * sizeof(*R3D_MOD_ENV.probes)); + if (!newProbes) return false; + + R3D_MOD_ENV.capacityProbes = newCapacity; + R3D_MOD_ENV.probes = newProbes; + + for (int i = 0; i < R3D_ENV_PROBE_ARRAY_COUNT; i++) { + R3D_Probe* newPtr = RL_REALLOC(R3D_MOD_ENV.arrays[i].probes, newCapacity * sizeof(R3D_Probe)); + if (!newPtr) return false; + R3D_MOD_ENV.arrays[i].probes = newPtr; + } + + return true; +} + +static bool init_probe(r3d_env_probe_t* probe, R3D_ProbeFlags flags) +{ + probe->flags = flags; + probe->irradiance = -1; + probe->prefilter = -1; + + if (BIT_TEST(flags, R3D_PROBE_ILLUMINATION)) { + probe->irradiance = r3d_env_irradiance_reserve_layer(); + if (probe->irradiance == -1) return false; + } + + if (BIT_TEST(flags, R3D_PROBE_REFLECTION)) { + probe->prefilter = r3d_env_prefilter_reserve_layer(); + if (probe->prefilter == -1) { + if (probe->irradiance >= 0) { + r3d_env_irradiance_release_layer(probe->irradiance); + } + return false; + } + } + + probe->state = (r3d_env_probe_state_t) { + .updateMode = R3D_PROBE_UPDATE_ONCE, + .matrixShouldBeUpdated = true, + .sceneShouldBeUpdated = true + }; + + probe->position = (Vector3) {0}; + probe->falloff = 1.0f; + probe->range = 16.0f; + probe->interior = false; + probe->shadows = false; + probe->enabled = false; + + return true; +} + +static void deinit_probe(r3d_env_probe_t* probe) +{ + if (probe->irradiance >= 0) { + r3d_env_irradiance_release_layer(probe->irradiance); + } + if (probe->prefilter >= 0) { + r3d_env_prefilter_release_layer(probe->prefilter); + } +} + +static void update_probe_matrix_frustum(r3d_env_probe_t* probe) +{ + static const Vector3 dirs[6] = { + { 1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}, // +X, -X + { 0.0, 1.0, 0.0}, { 0.0, -1.0, 0.0}, // +Y, -Y + { 0.0, 0.0, 1.0}, { 0.0, 0.0, -1.0} // +Z, -Z + }; + + static const Vector3 ups[6] = { + { 0.0, -1.0, 0.0 }, { 0.0, -1.0, 0.0}, // +X, -X + { 0.0, 0.0, 1.0 }, { 0.0, 0.0, -1.0}, // +Y, -Y + { 0.0, -1.0, 0.0 }, { 0.0, -1.0, 0.0} // +Z, -Z + }; + + Matrix proj = MatrixPerspective(90 * DEG2RAD, 1.0, 0.05f, probe->range); + probe->invProj = MatrixInvert(proj); + + for (int face = 0; face < 6; face++) { + Vector3 target = Vector3Add(probe->position, dirs[face]); + probe->view[face] = MatrixLookAt(probe->position, target, ups[face]); + probe->viewProj[face] = MatrixMultiply(probe->view[face], proj); + probe->frustum[face] = R3D_ComputeFrustum(probe->viewProj[face]); + probe->invView[face] = MatrixInvert(probe->view[face]); + } +} + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +bool r3d_env_init(void) +{ + memset(&R3D_MOD_ENV, 0, sizeof(R3D_MOD_ENV)); + + glGenFramebuffers(1, &R3D_MOD_ENV.workFramebuffer); + glGenFramebuffers(1, &R3D_MOD_ENV.captureFramebuffer); + + glGenTextures(1, &R3D_MOD_ENV.irradianceArray); + glGenTextures(1, &R3D_MOD_ENV.prefilterArray); + + glGenRenderbuffers(1, &R3D_MOD_ENV.captureDepth); + glGenTextures(1, &R3D_MOD_ENV.captureCube); + + // Initialize layer pools + if (!layer_pool_init(&R3D_MOD_ENV.irradiancePool, LAYER_GROWTH)) { + R3D_TRACELOG(LOG_FATAL, "Failed to init irradiance layer pool"); + r3d_env_quit(); + return false; + } + + if (!layer_pool_init(&R3D_MOD_ENV.prefilterPool, LAYER_GROWTH)) { + R3D_TRACELOG(LOG_FATAL, "Failed to init prefilter layer pool"); + r3d_env_quit(); + return false; + } + + // Allocate probe arrays + R3D_MOD_ENV.probes = RL_MALLOC(PROBE_INITIAL_CAPACITY * sizeof(*R3D_MOD_ENV.probes)); + R3D_MOD_ENV.capacityProbes = PROBE_INITIAL_CAPACITY; + + if (!R3D_MOD_ENV.probes) { + R3D_TRACELOG(LOG_FATAL, "Failed to allocate probe array"); + r3d_env_quit(); + return false; + } + + for (int i = 0; i < R3D_ENV_PROBE_ARRAY_COUNT; i++) { + R3D_MOD_ENV.arrays[i].probes = RL_MALLOC(PROBE_INITIAL_CAPACITY * sizeof(R3D_Probe)); + if (!R3D_MOD_ENV.arrays[i].probes) { + R3D_TRACELOG(LOG_FATAL, "Failed to allocate probe list array %i", i); + r3d_env_quit(); + return false; + } + } + + return true; +} + +void r3d_env_quit(void) +{ + if (R3D_MOD_ENV.irradianceArray) glDeleteTextures(1, &R3D_MOD_ENV.irradianceArray); + if (R3D_MOD_ENV.prefilterArray) glDeleteTextures(1, &R3D_MOD_ENV.prefilterArray); + + if (R3D_MOD_ENV.captureDepth) glDeleteRenderbuffers(1, &R3D_MOD_ENV.captureDepth); + if (R3D_MOD_ENV.captureCube) glDeleteTextures(1, &R3D_MOD_ENV.captureCube); + + if (R3D_MOD_ENV.workFramebuffer) glDeleteFramebuffers(1, &R3D_MOD_ENV.workFramebuffer); + if (R3D_MOD_ENV.captureFramebuffer) glDeleteFramebuffers(1, &R3D_MOD_ENV.captureFramebuffer); + + layer_pool_quit(&R3D_MOD_ENV.irradiancePool); + layer_pool_quit(&R3D_MOD_ENV.prefilterPool); + + for (int i = 0; i < R3D_ENV_PROBE_ARRAY_COUNT; i++) { + RL_FREE(R3D_MOD_ENV.arrays[i].probes); + } + + RL_FREE(R3D_MOD_ENV.probes); +} + +R3D_Probe r3d_env_probe_new(R3D_ProbeFlags flags) +{ + if (!BIT_TEST_ANY(flags, R3D_PROBE_ILLUMINATION | R3D_PROBE_REFLECTION)) { + R3D_TRACELOG(LOG_FATAL, "Failed to create probe; Invalid flags"); + return -1; + } + + r3d_env_probe_array_t* validProbes = &R3D_MOD_ENV.arrays[R3D_ENV_PROBE_ARRAY_VALID]; + r3d_env_probe_array_t* freeProbes = &R3D_MOD_ENV.arrays[R3D_ENV_PROBE_ARRAY_FREE]; + + R3D_Probe index; + if (freeProbes->count == 0) index = validProbes->count; + else index = freeProbes->probes[--freeProbes->count]; + + if (index >= R3D_MOD_ENV.capacityProbes) { + if (!growth_probe_arrays()) { + R3D_TRACELOG(LOG_FATAL, "Failed to grow probe arrays"); + return -1; + } + } + + if (!init_probe(&R3D_MOD_ENV.probes[index], flags)) { + R3D_TRACELOG(LOG_FATAL, "Failed to initialize probe"); + return -1; + } + + validProbes->probes[validProbes->count++] = index; + + return index; +} + +void r3d_env_probe_delete(R3D_Probe index) +{ + if (index < 0) return; + + r3d_env_probe_array_t* validProbes = &R3D_MOD_ENV.arrays[R3D_ENV_PROBE_ARRAY_VALID]; + + // Find and remove from valid list + for (int i = 0; i < validProbes->count; i++) { + if (index == validProbes->probes[i]) { + int numToMove = validProbes->count - i - 1; + if (numToMove > 0) { + memmove(&validProbes->probes[i], &validProbes->probes[i + 1], + numToMove * sizeof(validProbes->probes[0])); + } + validProbes->count--; + + // Add to free list and cleanup + r3d_env_probe_array_t* freeProbes = &R3D_MOD_ENV.arrays[R3D_ENV_PROBE_ARRAY_FREE]; + freeProbes->probes[freeProbes->count++] = index; + deinit_probe(&R3D_MOD_ENV.probes[index]); + return; + } + } +} + +bool r3d_env_probe_is_valid(R3D_Probe index) +{ + if (index < 0) return false; + + const r3d_env_probe_array_t* validProbes = &R3D_MOD_ENV.arrays[R3D_ENV_PROBE_ARRAY_VALID]; + for (int i = 0; i < validProbes->count; i++) { + if (index == validProbes->probes[i]) return true; + } + return false; +} + +r3d_env_probe_t* r3d_env_probe_get(R3D_Probe index) +{ + return r3d_env_probe_is_valid(index) ? &R3D_MOD_ENV.probes[index] : NULL; +} + +bool r3d_env_probe_has(r3d_env_probe_array_enum_t array) +{ + return (R3D_MOD_ENV.arrays[array].count > 0); +} + +bool r3d_env_probe_iter(r3d_env_probe_t** probe, r3d_env_probe_array_enum_t array) +{ + static int index = 0; + index = (*probe == NULL) ? 0 : index + 1; + + if (index >= R3D_MOD_ENV.arrays[array].count) return false; + + *probe = &R3D_MOD_ENV.probes[R3D_MOD_ENV.arrays[array].probes[index]]; + return true; +} + +void r3d_env_probe_update_and_cull(const R3D_Frustum* viewFrustum, bool* hasVisibleProbes) +{ + r3d_env_probe_array_t* visibleProbes = &R3D_MOD_ENV.arrays[R3D_ENV_PROBE_ARRAY_VISIBLE]; + r3d_env_probe_array_t* validProbes = &R3D_MOD_ENV.arrays[R3D_ENV_PROBE_ARRAY_VALID]; + + visibleProbes->count = 0; + + for (int i = 0; i < validProbes->count; i++) { + R3D_Probe index = validProbes->probes[i]; + r3d_env_probe_t* probe = &R3D_MOD_ENV.probes[index]; + + if (probe->state.matrixShouldBeUpdated) { + probe->state.matrixShouldBeUpdated = false; + update_probe_matrix_frustum(probe); + } + + if (!probe->enabled) continue; + + BoundingBox aabb = { + .min = { + probe->position.x - probe->range, + probe->position.y - probe->range, + probe->position.z - probe->range + }, + .max = { + probe->position.x + probe->range, + probe->position.y + probe->range, + probe->position.z + probe->range + } + }; + + if (R3D_FrustumIntersectsBoundingBox(viewFrustum, aabb)) { + visibleProbes->probes[visibleProbes->count++] = index; + if (hasVisibleProbes) *hasVisibleProbes = true; + } + } +} + +bool r3d_env_probe_should_be_updated(r3d_env_probe_t* probe, bool willBeUpdated) +{ + bool shouldUpdate = probe->state.sceneShouldBeUpdated; + + if (willBeUpdated && probe->state.updateMode == R3D_PROBE_UPDATE_ONCE) { + probe->state.sceneShouldBeUpdated = false; + } + + return shouldUpdate; +} + +int r3d_env_irradiance_reserve_layer(void) +{ + int layer = layer_pool_reserve(&R3D_MOD_ENV.irradiancePool); + + if (layer < 0) { + if (!expand_cubemap_capacity(&R3D_MOD_ENV.irradianceArray, &R3D_MOD_ENV.irradiancePool, R3D_CUBEMAP_IRRADIANCE_SIZE, false)) { + return -1; + } + layer = layer_pool_reserve(&R3D_MOD_ENV.irradiancePool); + } + + return layer; +} + +void r3d_env_irradiance_release_layer(int layer) +{ + layer_pool_release(&R3D_MOD_ENV.irradiancePool, layer); +} + +void r3d_env_irradiance_bind_fbo(int layer, int face) +{ + glBindFramebuffer(GL_FRAMEBUFFER, R3D_MOD_ENV.workFramebuffer); + glFramebufferTextureLayer( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + R3D_MOD_ENV.irradianceArray, 0, layer * 6 + face + ); + + glViewport(0, 0, R3D_CUBEMAP_IRRADIANCE_SIZE, R3D_CUBEMAP_IRRADIANCE_SIZE); +} + +GLuint r3d_env_irradiance_get(void) +{ + return R3D_MOD_ENV.irradianceArray; +} + +int r3d_env_prefilter_reserve_layer(void) +{ + int layer = layer_pool_reserve(&R3D_MOD_ENV.prefilterPool); + + if (layer < 0) { + if (!expand_cubemap_capacity(&R3D_MOD_ENV.prefilterArray, &R3D_MOD_ENV.prefilterPool, R3D_CUBEMAP_PREFILTER_SIZE, true)) { + return -1; + } + layer = layer_pool_reserve(&R3D_MOD_ENV.prefilterPool); + } + + return layer; +} + +void r3d_env_prefilter_release_layer(int layer) +{ + layer_pool_release(&R3D_MOD_ENV.prefilterPool, layer); +} + +void r3d_env_prefilter_bind_fbo(int layer, int face, int mipLevel) +{ + assert(mipLevel < r3d_get_mip_levels_1d(R3D_CUBEMAP_PREFILTER_SIZE)); + + glBindFramebuffer(GL_FRAMEBUFFER, R3D_MOD_ENV.workFramebuffer); + glFramebufferTextureLayer( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + R3D_MOD_ENV.prefilterArray, mipLevel, layer * 6 + face + ); + + int mipSize = R3D_CUBEMAP_PREFILTER_SIZE >> mipLevel; + glViewport(0, 0, mipSize, mipSize); +} + +GLuint r3d_env_prefilter_get(void) +{ + return R3D_MOD_ENV.prefilterArray; +} + +void r3d_env_capture_bind_fbo(int face, int mipLevel) +{ + assert(mipLevel < r3d_get_mip_levels_1d(R3D_PROBE_CAPTURE_SIZE)); + + glBindFramebuffer(GL_FRAMEBUFFER, R3D_MOD_ENV.captureFramebuffer); + + if (!R3D_MOD_ENV.captureCubeAllocated) { + alloc_depth_stencil_renderbuffer(R3D_MOD_ENV.captureDepth, R3D_PROBE_CAPTURE_SIZE); + cubemap_spec_t spec = cubemap_spec(R3D_PROBE_CAPTURE_SIZE, 0, true); + allocate_cubemap(R3D_MOD_ENV.captureCube, spec); + R3D_MOD_ENV.captureCubeAllocated = true; + + glFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, R3D_MOD_ENV.captureDepth + ); + } + + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, + R3D_MOD_ENV.captureCube, mipLevel + ); + + int mipSize = R3D_PROBE_CAPTURE_SIZE >> mipLevel; + glViewport(0, 0, mipSize, mipSize); +} + +void r3d_env_capture_gen_mipmaps(void) +{ + assert(R3D_MOD_ENV.captureCubeAllocated); + + glBindTexture(GL_TEXTURE_CUBE_MAP, R3D_MOD_ENV.captureCube); + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +} + +GLuint r3d_env_capture_get(void) +{ + return R3D_MOD_ENV.captureCube; +} + +``` + +`src/modules/r3d_env.h`: + +```h +/* r3d_env.h -- Internal R3D environment module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_MODULE_ENV_H +#define R3D_MODULE_ENV_H + +#include +#include +#include +#include + +// ======================================== +// HELPER MACROS +// ======================================== + +#define R3D_ENV_PROBE_FOR_EACH_VALID(probe) \ + for (r3d_env_probe_t* probe = NULL; \ + r3d_env_probe_iter(&probe, R3D_ENV_PROBE_ARRAY_VALID); ) + +#define R3D_ENV_PROBE_FOR_EACH_VISIBLE(probe) \ + for (r3d_env_probe_t* probe = NULL; \ + r3d_env_probe_iter(&probe, R3D_ENV_PROBE_ARRAY_VISIBLE); ) + +// ======================================== +// TYPES +// ======================================== + +typedef struct { + R3D_ProbeUpdateMode updateMode; + bool matrixShouldBeUpdated; + bool sceneShouldBeUpdated; +} r3d_env_probe_state_t; + +typedef struct { + r3d_env_probe_state_t state; + R3D_Frustum frustum[6]; + Matrix view[6]; + Matrix viewProj[6]; + Matrix invView[6]; + Matrix invProj; + + R3D_ProbeFlags flags; + int irradiance; // Layer index, -1 if unused + int prefilter; // Layer index, -1 if unused + + Vector3 position; + float falloff; + float range; + + bool interior; + bool shadows; + bool enabled; +} r3d_env_probe_t; + +typedef enum { + R3D_ENV_PROBE_ARRAY_VISIBLE, + R3D_ENV_PROBE_ARRAY_VALID, + R3D_ENV_PROBE_ARRAY_FREE, + R3D_ENV_PROBE_ARRAY_COUNT +} r3d_env_probe_array_enum_t; + +typedef struct { + R3D_Probe* probes; + int count; +} r3d_env_probe_array_t; + +// Cubemap layer pool (manages reusable texture layers) +typedef struct { + int* freeLayers; // Stack of available layer indices + int freeCount; // Number of free layers + int freeCapacity; // Capacity of freeLayers array + int totalLayers; // Total allocated layers +} r3d_env_layer_pool_t; + +// ======================================== +// MODULE STATE +// ======================================== + +extern struct r3d_env { + GLuint workFramebuffer; + GLuint irradianceArray; + GLuint prefilterArray; + + GLuint captureFramebuffer; + GLuint captureDepth; + GLuint captureCube; + + r3d_env_layer_pool_t irradiancePool; + r3d_env_layer_pool_t prefilterPool; + + bool captureCubeAllocated; + + r3d_env_probe_array_t arrays[R3D_ENV_PROBE_ARRAY_COUNT]; + r3d_env_probe_t* probes; + int capacityProbes; +} R3D_MOD_ENV; + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +/* Initialize module (called once during R3D_Init) */ +bool r3d_env_init(void); + +/* Deinitialize module (called once during R3D_Close) */ +void r3d_env_quit(void); + +/* Create a new probe of the given type */ +R3D_Probe r3d_env_probe_new(R3D_ProbeFlags type); + +/* Delete a probe and return it to the free list */ +void r3d_env_probe_delete(R3D_Probe index); + +/* Check whether a probe handle is valid */ +bool r3d_env_probe_is_valid(R3D_Probe index); + +/* Get internal probe structure (returns NULL if invalid) */ +r3d_env_probe_t* r3d_env_probe_get(R3D_Probe index); + +/* Check if the specified probe array is not empty */ +bool r3d_env_probe_has(r3d_env_probe_array_enum_t array); + +/* Iterator for probes by category (stateful, not thread-safe) */ +bool r3d_env_probe_iter(r3d_env_probe_t** probe, r3d_env_probe_array_enum_t array); + +/* Update probe states and collect visible ones (can indicate if probes influcences are visible) */ +void r3d_env_probe_update_and_cull(const R3D_Frustum* viewFrustum, bool* hasVisibleProbes); + +/* Check if probe should be rendered (updates state if willBeUpdated is true) */ +bool r3d_env_probe_should_be_updated(r3d_env_probe_t* probe, bool willBeUpdated); + +/* Reserve a new irradiance map layer (returns -1 on failure) */ +int r3d_env_irradiance_reserve_layer(void); + +/* Release an irradiance map layer */ +void r3d_env_irradiance_release_layer(int layer); + +/* Bind irradiance framebuffer for the given layer and face */ +void r3d_env_irradiance_bind_fbo(int layer, int face); + +/* Get irradiance cubemap array texture ID */ +GLuint r3d_env_irradiance_get(void); + +/* Reserve a new prefilter map layer (returns -1 on failure) */ +int r3d_env_prefilter_reserve_layer(void); + +/* Release a prefilter map layer */ +void r3d_env_prefilter_release_layer(int layer); + +/* Bind prefilter framebuffer for the given layer, face and mip level */ +void r3d_env_prefilter_bind_fbo(int layer, int face, int mipLevel); + +/* Get prefiltered cubemap array texture ID */ +GLuint r3d_env_prefilter_get(void); + +/* Bind capture framebuffer for the given face and mip level */ +void r3d_env_capture_bind_fbo(int face, int mipLevel); + +/* Generate mipmaps for the capture target */ +void r3d_env_capture_gen_mipmaps(void); + +/* Get capture cubemap texture ID */ +GLuint r3d_env_capture_get(void); + +#endif // R3D_MODULE_ENV_H + +``` + +`src/modules/r3d_light.c`: + +```c +/* r3d_light.c -- Internal R3D light module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_light.h" +#include +#include +#include +#include +#include +#include + +// ======================================== +// CONSTANTS +// ======================================== + +#define LIGHT_INITIAL_CAPACITY 32 +#define SHADOW_DIR_LAYER_GROWTH 2 +#define SHADOW_SPOT_LAYER_GROWTH 4 +#define SHADOW_OMNI_LAYER_GROWTH 4 + +// ======================================== +// MODULE STATE +// ======================================== + +struct r3d_light R3D_MOD_LIGHT; + +// ======================================== +// CONSTANTS +// ======================================== + +static const GLenum SHADOW_TEXTURE_TARGET[] = { + [R3D_LIGHT_DIR] = GL_TEXTURE_2D_ARRAY, + [R3D_LIGHT_SPOT] = GL_TEXTURE_2D_ARRAY, + [R3D_LIGHT_OMNI] = GL_TEXTURE_CUBE_MAP_ARRAY, +}; + +static const int SHADOW_LAYER_GROWTH[] = { + [R3D_LIGHT_DIR] = SHADOW_DIR_LAYER_GROWTH, + [R3D_LIGHT_SPOT] = SHADOW_SPOT_LAYER_GROWTH, + [R3D_LIGHT_OMNI] = SHADOW_OMNI_LAYER_GROWTH, +}; + +// ======================================== +// SHADOW LAYER POOL FUNCTIONS +// ======================================== + +static bool shadow_pool_init(r3d_light_shadow_pool_t* pool, int initialCapacity) +{ + pool->freeLayers = RL_MALLOC(initialCapacity * sizeof(int)); + if (!pool->freeLayers) return false; + + pool->freeCapacity = initialCapacity; + pool->freeCount = 0; + pool->totalLayers = 0; + + return true; +} + +static void shadow_pool_quit(r3d_light_shadow_pool_t* pool) +{ + RL_FREE(pool->freeLayers); +} + +static int shadow_pool_reserve(r3d_light_shadow_pool_t* pool) +{ + if (pool->freeCount > 0) { + return pool->freeLayers[--pool->freeCount]; + } + return -1; // Needs expansion +} + +static void shadow_pool_release(r3d_light_shadow_pool_t* pool, int layer) +{ + if (layer < 0 || layer >= pool->totalLayers) return; + if (pool->freeCount < pool->freeCapacity) { + pool->freeLayers[pool->freeCount++] = layer; + } +} + +static bool shadow_pool_expand(r3d_light_shadow_pool_t* pool, int addCount) +{ + int oldTotal = pool->totalLayers; + int newTotal = oldTotal + addCount; + + // Reallocate free layers array if needed + if (pool->freeCount + addCount > pool->freeCapacity) { + pool->freeCapacity = newTotal; + int* newFree = RL_REALLOC(pool->freeLayers, pool->freeCapacity * sizeof(int)); + if (!newFree) return false; + pool->freeLayers = newFree; + } + + // Add new layers to free list + for (int i = oldTotal; i < newTotal; i++) { + pool->freeLayers[pool->freeCount++] = i; + } + + pool->totalLayers = newTotal; + return true; +} + +// ======================================== +// SHADOW MAP TEXTURE FUNCTIONS +// ======================================== + +static bool allocate_shadow_array(GLuint texture, GLenum target, int size, int layers) +{ + int actualLayers = (target == GL_TEXTURE_CUBE_MAP_ARRAY) ? layers * 6 : layers; + + glBindTexture(target, texture); + glTexImage3D( + target, 0, GL_DEPTH_COMPONENT16, size, size, actualLayers, + 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL + ); + + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (target == GL_TEXTURE_CUBE_MAP_ARRAY) { + glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + } + + glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + + glBindTexture(target, 0); + return true; +} + +static bool resize_shadow_array(GLuint* texture, GLenum target, int size, int oldLayers, int newLayers) +{ + GLuint newTexture; + glGenTextures(1, &newTexture); + + if (!allocate_shadow_array(newTexture, target, size, newLayers)) { + glDeleteTextures(1, &newTexture); + return false; + } + + // Copy existing data + if (oldLayers > 0) { + glBindFramebuffer(GL_FRAMEBUFFER, R3D_MOD_LIGHT.workFramebuffer); + int facesPerLayer = (target == GL_TEXTURE_CUBE_MAP_ARRAY) ? 6 : 1; + for (int layer = 0; layer < oldLayers; layer++) { + for (int face = 0; face < facesPerLayer; face++) { + int layerIndex = layer * facesPerLayer + face; + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, *texture, 0, layerIndex); + glBindTexture(target, newTexture); + glCopyTexSubImage3D(target, 0, 0, 0, layerIndex, 0, 0, size, size); + } + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + glDeleteTextures(1, texture); + *texture = newTexture; + return true; +} + +static bool expand_shadow_array_capacity(R3D_LightType type) +{ + r3d_light_shadow_pool_t* pool = &R3D_MOD_LIGHT.shadowPools[type]; + GLuint* shadowArray = &R3D_MOD_LIGHT.shadowArrays[type]; + GLenum shadowTarget = SHADOW_TEXTURE_TARGET[type]; + int shadowSize = R3D_LIGHT_SHADOW_SIZE[type]; + int growth = SHADOW_LAYER_GROWTH[type]; + + if (!resize_shadow_array(shadowArray, shadowTarget, shadowSize, pool->totalLayers, pool->totalLayers + growth)) { + return false; + } + + return shadow_pool_expand(pool, growth); +} + +// ======================================== +// LIGHT FUNCTIONS +// ======================================== + +static bool growth_light_arrays(void) +{ + int newCapacity = 2 * R3D_MOD_LIGHT.capacityLights; + + r3d_light_t* newLights = RL_REALLOC(R3D_MOD_LIGHT.lights, newCapacity * sizeof(*R3D_MOD_LIGHT.lights)); + if (!newLights) return false; + + R3D_MOD_LIGHT.capacityLights = newCapacity; + R3D_MOD_LIGHT.lights = newLights; + + for (int i = 0; i < R3D_LIGHT_ARRAY_COUNT; i++) { + R3D_Light* newPtr = RL_REALLOC(R3D_MOD_LIGHT.arrays[i].lights, newCapacity * sizeof(R3D_Light)); + if (!newPtr) return false; + R3D_MOD_LIGHT.arrays[i].lights = newPtr; + } + + return true; +} + +static bool init_light(r3d_light_t* light, R3D_LightType type) +{ + if (type < 0 || type >= R3D_LIGHT_TYPE_COUNT) { + return false; + } + + memset(light, 0, sizeof(*light)); + + light->state = (r3d_light_state_t) { + .shadowUpdate = R3D_SHADOW_UPDATE_INTERVAL, + .shadowShouldBeUpdated = true, + .matrixShouldBeUpdated = true, + .shadowFrequencySec = 0.016f, + .shadowTimerSec = 0.0f + }; + + light->shadowLayer = -1; + + light->aabb.min = (Vector3) {-FLT_MAX, -FLT_MAX, -FLT_MAX}; + light->aabb.max = (Vector3) {+FLT_MAX, +FLT_MAX, +FLT_MAX}; + + light->color = (Vector3) {1, 1, 1}; + light->position = (Vector3) {0}; + light->direction = (Vector3) {0, 0, -1}; + + light->specular = 0.5f; + light->energy = 1.0f; + light->range = 50.0f; + light->attenuation = 1.0f; + light->innerCutOff = cosf(22.5f * DEG2RAD); + light->outerCutOff = cosf(45.0f * DEG2RAD); + + light->shadowSoftness = 4.0f / R3D_LIGHT_SHADOW_SIZE[light->type]; + light->shadowOpacity = 1.0f; + switch (type) { + case R3D_LIGHT_DIR: + light->shadowDepthBias = 4.0f / R3D_LIGHT_SHADOW_SIZE[light->type]; + light->shadowSlopeBias = 6.0f / R3D_LIGHT_SHADOW_SIZE[light->type]; + break; + case R3D_LIGHT_SPOT: + light->shadowDepthBias = 0.25f / R3D_LIGHT_SHADOW_SIZE[light->type]; + light->shadowSlopeBias = 1.0f / R3D_LIGHT_SHADOW_SIZE[light->type]; + break; + case R3D_LIGHT_OMNI: + light->shadowDepthBias = 0.025f; + light->shadowSlopeBias = 0.1f; + break; + default: + break; + } + light->casterMask = R3D_LAYER_ALL; + + light->type = type; + light->enabled = false; + + return true; +} + +static void update_light_shadow_state(r3d_light_t* light) +{ + switch (light->state.shadowUpdate) { + case R3D_SHADOW_UPDATE_MANUAL: + break; + case R3D_SHADOW_UPDATE_INTERVAL: + if (!light->state.shadowShouldBeUpdated) { + light->state.shadowTimerSec += GetFrameTime(); + if (light->state.shadowTimerSec >= light->state.shadowFrequencySec) { + light->state.shadowTimerSec -= light->state.shadowFrequencySec; + light->state.shadowShouldBeUpdated = true; + } + } + break; + case R3D_SHADOW_UPDATE_CONTINUOUS: + light->state.shadowShouldBeUpdated = true; + break; + } +} + +static void update_light_dir_matrix(r3d_light_t* light, R3D_Camera camera, double aspect) +{ + assert(light->type == R3D_LIGHT_DIR); + + float camNear = light->range / 1000.0f; + float camFar = light->range; + float camFovy = (float)camera.fovy; + float camAspect = (float)aspect; + + float farH = camFar * tanf(camFovy * (DEG2RAD * 0.5f)); + float halfDepth = (camFar - camNear) * 0.5f; + float radius = sqrtf(farH * farH * (1.0f + camAspect * camAspect) + halfDepth * halfDepth); + + Vector3 forward = R3D_GetCameraForward(camera); + Vector3 frustumCenter = Vector3Add(camera.position, Vector3Scale(forward, (camNear + camFar) * 0.5f)); + + Vector3 lightDir = Vector3Normalize(light->direction); + float ax = fabsf(lightDir.x), ay = fabsf(lightDir.y), az = fabsf(lightDir.z); + Vector3 up = (ax <= ay && ax <= az) ? (Vector3){1,0,0} : (ay <= az) ? (Vector3){0,1,0} : (Vector3){0,0,1}; + Vector3 lightRight = Vector3Normalize(Vector3CrossProduct(up, lightDir)); + Vector3 lightUp = Vector3CrossProduct(lightDir, lightRight); + + float texelSize = (radius * 2.0f) / R3D_SHADOW_MAP_DIRECTIONAL_SIZE; + float cx = floorf(Vector3DotProduct(frustumCenter, lightRight) / texelSize) * texelSize; + float cy = floorf(Vector3DotProduct(frustumCenter, lightUp) / texelSize) * texelSize; + float cz = Vector3DotProduct(frustumCenter, lightDir); + + Vector3 snappedCenter = Vector3Add( + Vector3Add( + Vector3Scale(lightRight, cx), + Vector3Scale(lightUp, cy) + ), + Vector3Scale(lightDir, cz) + ); + + const float zExtension = 100.0f; // Extent to capture objects behind the camera + Vector3 eye = Vector3Subtract(snappedCenter, Vector3Scale(lightDir, radius + zExtension)); + Matrix view = MatrixLookAt(eye, snappedCenter, lightUp); + + light->near = 0.0f; + light->far = zExtension + radius * 2.0f; + light->viewProj[0] = MatrixMultiply(view, MatrixOrtho(-radius, radius, -radius, radius, light->near, light->far)); +} + +static void update_light_spot_matrix(r3d_light_t* light) +{ + assert(light->type == R3D_LIGHT_SPOT); + + light->near = 0.05f; + light->far = light->range; + + Vector3 up = {0, 1, 0}; + float upDot = fabsf(Vector3DotProduct(light->direction, up)); + if (upDot > 0.99f) up = (Vector3){1, 0, 0}; + + Matrix view = MatrixLookAt(light->position, Vector3Add(light->position, light->direction), up); + Matrix proj = MatrixPerspective(90 * DEG2RAD, 1.0, light->near, light->far); + light->viewProj[0] = MatrixMultiply(view, proj); +} + +static void update_light_omni_matrix(r3d_light_t* light) +{ + assert(light->type == R3D_LIGHT_OMNI); + + static const Vector3 dirs[6] = { + { 1.0, 0.0, 0.0 }, { -1.0, 0.0, 0.0 }, + { 0.0, 1.0, 0.0 }, { 0.0, -1.0, 0.0 }, + { 0.0, 0.0, 1.0 }, { 0.0, 0.0, -1.0 } + }; + + static const Vector3 ups[6] = { + { 0.0, -1.0, 0.0 }, { 0.0, -1.0, 0.0 }, + { 0.0, 0.0, 1.0 }, { 0.0, 0.0, -1.0 }, + { 0.0, -1.0, 0.0 }, { 0.0, -1.0, 0.0 } + }; + + light->near = 0.05f; + light->far = light->range; + + Matrix proj = MatrixPerspective(90 * DEG2RAD, 1.0, light->near, light->far); + + for (int face = 0; face < 6; face++) { + Vector3 target = Vector3Add(light->position, dirs[face]); + Matrix view = MatrixLookAt(light->position, target, ups[face]); + light->viewProj[face] = MatrixMultiply(view, proj); + } +} + +static void update_light_matrix(r3d_light_t* light, R3D_Camera camera, double aspect) +{ + switch (light->type) { + case R3D_LIGHT_DIR: + update_light_dir_matrix(light, camera, aspect); + break; + case R3D_LIGHT_SPOT: + update_light_spot_matrix(light); + break; + case R3D_LIGHT_OMNI: + update_light_omni_matrix(light); + break; + default: + break; + } +} + +static void update_light_frustum(r3d_light_t* light) +{ + int faceCount = (light->type == R3D_LIGHT_OMNI) ? 6 : 1; + for (int i = 0; i < faceCount; i++) { + light->frustum[i] = R3D_ComputeFrustum(light->viewProj[i]); + } +} + +static void update_light_bounding_box(r3d_light_t* light) +{ + switch (light->type) { + case R3D_LIGHT_OMNI: + light->aabb.min = Vector3AddValue(light->position, -light->range); + light->aabb.max = Vector3AddValue(light->position, +light->range); + break; + case R3D_LIGHT_SPOT: + light->aabb = R3D_ComputeFrustumBoundingBox(MatrixInvert(light->viewProj[0])); + break; + case R3D_LIGHT_DIR: + light->aabb.min = (Vector3) {-FLT_MAX, -FLT_MAX, -FLT_MAX}; + light->aabb.max = (Vector3) {+FLT_MAX, +FLT_MAX, +FLT_MAX}; + break; + default: + break; + } +} + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +bool r3d_light_init(void) +{ + memset(&R3D_MOD_LIGHT, 0, sizeof(R3D_MOD_LIGHT)); + + glGenFramebuffers(1, &R3D_MOD_LIGHT.workFramebuffer); + glGenTextures(R3D_LIGHT_TYPE_COUNT, R3D_MOD_LIGHT.shadowArrays); + + // Configure the framebuffer to only consider the depth + glBindFramebuffer(GL_FRAMEBUFFER, R3D_MOD_LIGHT.workFramebuffer); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Initialize shadow pools + for (int i = 0; i < R3D_LIGHT_TYPE_COUNT; i++) { + if (!shadow_pool_init(&R3D_MOD_LIGHT.shadowPools[i], SHADOW_LAYER_GROWTH[i])) { + R3D_TRACELOG(LOG_FATAL, "Failed to init shadow pool number %i", i); + r3d_light_quit(); + return false; + } + } + + // Allocate light arrays + R3D_MOD_LIGHT.lights = RL_MALLOC(LIGHT_INITIAL_CAPACITY * sizeof(*R3D_MOD_LIGHT.lights)); + R3D_MOD_LIGHT.capacityLights = LIGHT_INITIAL_CAPACITY; + + if (!R3D_MOD_LIGHT.lights) { + R3D_TRACELOG(LOG_FATAL, "Failed to allocate light array"); + r3d_light_quit(); + return false; + } + + for (int i = 0; i < R3D_LIGHT_ARRAY_COUNT; i++) { + R3D_MOD_LIGHT.arrays[i].lights = RL_MALLOC(LIGHT_INITIAL_CAPACITY * sizeof(R3D_Light)); + if (!R3D_MOD_LIGHT.arrays[i].lights) { + R3D_TRACELOG(LOG_FATAL, "Failed to allocate light list array %i", i); + r3d_light_quit(); + return false; + } + } + + return true; +} + +void r3d_light_quit(void) +{ + if (R3D_MOD_LIGHT.workFramebuffer) { + glDeleteFramebuffers(1, &R3D_MOD_LIGHT.workFramebuffer); + } + + for (int i = 0; i < R3D_LIGHT_TYPE_COUNT; i++) { + if (R3D_MOD_LIGHT.shadowArrays[i]) { + glDeleteTextures(1, &R3D_MOD_LIGHT.shadowArrays[i]); + } + shadow_pool_quit(&R3D_MOD_LIGHT.shadowPools[i]); + } + + for (int i = 0; i < R3D_LIGHT_ARRAY_COUNT; i++) { + RL_FREE(R3D_MOD_LIGHT.arrays[i].lights); + } + + RL_FREE(R3D_MOD_LIGHT.lights); +} + +R3D_Light r3d_light_new(R3D_LightType type) +{ + r3d_light_array_t* validLights = &R3D_MOD_LIGHT.arrays[R3D_LIGHT_ARRAY_VALID]; + r3d_light_array_t* freeLights = &R3D_MOD_LIGHT.arrays[R3D_LIGHT_ARRAY_FREE]; + + // Get index (from free list or new) + R3D_Light index = (freeLights->count > 0) + ? freeLights->lights[--freeLights->count] + : validLights->count; + + // Grow if needed + if (index >= R3D_MOD_LIGHT.capacityLights && !growth_light_arrays()) { + R3D_TRACELOG(LOG_ERROR, "Failed to grow light arrays"); + goto error_restore_free; + } + + // Initialize light + if (!init_light(&R3D_MOD_LIGHT.lights[index], type)) { + R3D_TRACELOG(LOG_ERROR, "Failed to initialize light (type: %d)", type); + goto error_restore_free; + } + + // Add to valid array + validLights->lights[validLights->count++] = index; + return index; + +error_restore_free: + // Restore free list if we took from it + if (index < validLights->count) { + freeLights->lights[freeLights->count++] = index; + } + return -1; +} + +void r3d_light_delete(R3D_Light index) +{ + if (index < 0) return; + + r3d_light_array_t* validLights = &R3D_MOD_LIGHT.arrays[R3D_LIGHT_ARRAY_VALID]; + + for (int i = 0; i < validLights->count; i++) { + if (index == validLights->lights[i]) { + int numToMove = validLights->count - i - 1; + if (numToMove > 0) { + memmove( + &validLights->lights[i], &validLights->lights[i + 1], + numToMove * sizeof(validLights->lights[0]) + ); + } + validLights->count--; + + // Release shadow layer and add to free list + r3d_light_t* light = &R3D_MOD_LIGHT.lights[index]; + if (light->shadowLayer >= 0) { + shadow_pool_release(&R3D_MOD_LIGHT.shadowPools[light->type], light->shadowLayer); + light->shadowLayer = -1; + } + + r3d_light_array_t* freeLights = &R3D_MOD_LIGHT.arrays[R3D_LIGHT_ARRAY_FREE]; + freeLights->lights[freeLights->count++] = index; + return; + } + } +} + +bool r3d_light_is_valid(R3D_Light index) +{ + if (index < 0) return false; + + const r3d_light_array_t* validLights = &R3D_MOD_LIGHT.arrays[R3D_LIGHT_ARRAY_VALID]; + for (int i = 0; i < validLights->count; i++) { + if (index == validLights->lights[i]) return true; + } + return false; +} + +r3d_light_t* r3d_light_get(R3D_Light index) +{ + return r3d_light_is_valid(index) ? &R3D_MOD_LIGHT.lights[index] : NULL; +} + +r3d_rect_t r3d_light_get_screen_rect(const r3d_light_t* light, const Matrix* viewProj, int w, int h) +{ + assert(light->type != R3D_LIGHT_DIR); + + Vector3 min = light->aabb.min; + Vector3 max = light->aabb.max; + + Vector2 minNDC = {+FLT_MAX, +FLT_MAX}; + Vector2 maxNDC = {-FLT_MAX, -FLT_MAX}; + + for (int i = 0; i < 8; i++) { + Vector4 corner = { + (i & 1) ? max.x : min.x, + (i & 2) ? max.y : min.y, + (i & 4) ? max.z : min.z, + 1.0f + }; + Vector4 clip = r3d_vector4_transform(corner, viewProj); + + // If the AABB crosses the near plane: fullscreen + if (clip.w <= 0.0f) { + return (r3d_rect_t){0, 0, w, h}; + } + + Vector2 ndc = Vector2Scale((Vector2){clip.x, clip.y}, 1.0f / clip.w); + minNDC = Vector2Min(minNDC, ndc); + maxNDC = Vector2Max(maxNDC, ndc); + } + + // NDC to screen + int x = (int)fmaxf((minNDC.x * 0.5f + 0.5f) * w, 0.0f); + int y = (int)fmaxf((minNDC.y * 0.5f + 0.5f) * h, 0.0f); + int rectW = (int)fminf((maxNDC.x * 0.5f + 0.5f) * w, (float)w) - x; + int rectH = (int)fminf((maxNDC.y * 0.5f + 0.5f) * h, (float)h) - y; + + // Security: Invalid dimensions = skip + if (rectW <= 0 || rectH <= 0) { + return (r3d_rect_t){0, 0, 0, 0}; + } + + return (r3d_rect_t){x, y, rectW, rectH}; +} + +bool r3d_light_iter(r3d_light_t** light, r3d_light_array_enum_t array) +{ + static int index = 0; + index = (*light == NULL) ? 0 : index + 1; + + if (index >= R3D_MOD_LIGHT.arrays[array].count) return false; + + *light = &R3D_MOD_LIGHT.lights[R3D_MOD_LIGHT.arrays[array].lights[index]]; + return true; +} + +bool r3d_light_enable_shadows(r3d_light_t* light) +{ + if (light->shadowLayer >= 0) return true; + + int layer = shadow_pool_reserve(&R3D_MOD_LIGHT.shadowPools[light->type]); + if (layer < 0) { + if (!expand_shadow_array_capacity(light->type)) { + return false; + } + layer = shadow_pool_reserve(&R3D_MOD_LIGHT.shadowPools[light->type]); + } + + light->state.shadowShouldBeUpdated = true; + light->shadowLayer = layer; + + return true; +} + +void r3d_light_disable_shadows(r3d_light_t* light) +{ + if (light->shadowLayer >= 0) { + shadow_pool_release(&R3D_MOD_LIGHT.shadowPools[light->type], light->shadowLayer); + light->shadowLayer = -1; + } +} + +void r3d_light_update_and_cull(const R3D_Frustum* viewFrustum, R3D_Camera camera, double aspect, bool* hasVisibleShadows) +{ + r3d_light_array_t* visibleLights = &R3D_MOD_LIGHT.arrays[R3D_LIGHT_ARRAY_VISIBLE]; + r3d_light_array_t* validLights = &R3D_MOD_LIGHT.arrays[R3D_LIGHT_ARRAY_VALID]; + + visibleLights->count = 0; + + for (int i = 0; i < validLights->count; i++) { + R3D_Light index = validLights->lights[i]; + r3d_light_t* light = &R3D_MOD_LIGHT.lights[index]; + + if (!light->enabled) continue; + + if (light->shadowLayer >= 0) { + update_light_shadow_state(light); + } + + bool isDirectional = (light->type == R3D_LIGHT_DIR); + bool shouldUpdateMatrix = isDirectional + ? light->state.shadowShouldBeUpdated + : light->state.matrixShouldBeUpdated; + + if (shouldUpdateMatrix) { + update_light_matrix(light, camera, aspect); + update_light_frustum(light); + if (!isDirectional) { + update_light_bounding_box(light); + } + light->state.matrixShouldBeUpdated = false; + } + + if (R3D_FrustumIntersectsBoundingBox(viewFrustum, light->aabb)) { + if (hasVisibleShadows) *hasVisibleShadows |= (light->shadowLayer >= 0); + visibleLights->lights[visibleLights->count++] = index; + } + } +} + +bool r3d_light_shadow_should_be_updated(r3d_light_t* light, bool willBeUpdated) +{ + // If the light does not have a shadow map assigned, we return + if (light->shadowLayer < 0) return false; + + // If the shadow opacity is set to zero, the update is ignored + if (light->shadowOpacity == 0.0f) return false; + + bool shouldUpdate = light->state.shadowShouldBeUpdated; + + if (willBeUpdated) { + switch (light->state.shadowUpdate) { + case R3D_SHADOW_UPDATE_MANUAL: + case R3D_SHADOW_UPDATE_INTERVAL: + light->state.shadowShouldBeUpdated = false; + break; + default: + break; + } + } + + return shouldUpdate; +} + +void r3d_light_shadow_bind_fbo(R3D_LightType type, int layer, int face) +{ + assert((type == R3D_LIGHT_OMNI && face >= 0 && face < 6) || (type != R3D_LIGHT_OMNI && face == 0)); + + GLuint shadowArray = R3D_MOD_LIGHT.shadowArrays[type]; + int shadowSize = R3D_LIGHT_SHADOW_SIZE[type]; + int stride = (type == R3D_LIGHT_OMNI) ? 6 : 1; + + glBindFramebuffer(GL_FRAMEBUFFER, R3D_MOD_LIGHT.workFramebuffer); + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowArray, 0, layer * stride + face); + glViewport(0, 0, shadowSize, shadowSize); +} + +GLuint r3d_light_shadow_get(R3D_LightType type) +{ + return R3D_MOD_LIGHT.shadowArrays[type]; +} + +``` + +`src/modules/r3d_light.h`: + +```h +/* r3d_light.h -- Internal R3D light module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_MODULE_LIGHT_H +#define R3D_MODULE_LIGHT_H + +#include +#include +#include +#include +#include +#include + +#include "../common/r3d_math.h" + +// ======================================== +// MODULE CONSTANTS +// ======================================== + +static const int R3D_LIGHT_SHADOW_SIZE[] = { + [R3D_LIGHT_DIR] = R3D_SHADOW_MAP_DIRECTIONAL_SIZE, + [R3D_LIGHT_SPOT] = R3D_SHADOW_MAP_SPOT_SIZE, + [R3D_LIGHT_OMNI] = R3D_SHADOW_MAP_OMNI_SIZE, +}; + +// ======================================== +// HELPER MACROS +// ======================================== + +#define R3D_LIGHT_FOR_EACH_VISIBLE(light) \ + for (r3d_light_t* light = NULL; \ + r3d_light_iter(&light, R3D_LIGHT_ARRAY_VISIBLE); ) + +// ======================================== +// TYPES +// ======================================== + +typedef struct { + R3D_ShadowUpdateMode shadowUpdate; + float shadowFrequencySec; + float shadowTimerSec; + bool shadowShouldBeUpdated; + bool matrixShouldBeUpdated; +} r3d_light_state_t; + +typedef struct { + + R3D_Frustum frustum[6]; // Frustum (only [0] for dir/spot, 6 for omni) + Matrix viewProj[6]; // View/projection matrix (only [0] for dir/spot, 6 for omni) + BoundingBox aabb; // AABB in world space of the light volume + + r3d_light_state_t state; // Contains the current state useful for the update + int shadowLayer; // Shadow map layer index, -1 if no shadow + + Vector3 color; + Vector3 position; // Light position (spot/omni) + Vector3 direction; // Light direction (spot/dir) + + float specular; + float energy; + float range; // Maximum distance (spot/omni) + float near; // Near plane for shadow projection + float far; // Far plane for shadow projection + float attenuation; // Additional attenuation (spot/omni) + float innerCutOff; // Spot light inner cutoff angle + float outerCutOff; // Spot light outer cutoff angle + float shadowSoftness; // Softness factor for penumbra + float shadowOpacity; // Shadow opacity factor + float shadowDepthBias; // Constant depth bias + float shadowSlopeBias; // Slope-scaled depth bias + R3D_Layer casterMask; // Shadow caster mask + + R3D_LightType type; + bool enabled; + +} r3d_light_t; + +typedef enum { + R3D_LIGHT_ARRAY_VISIBLE, + R3D_LIGHT_ARRAY_VALID, + R3D_LIGHT_ARRAY_FREE, + R3D_LIGHT_ARRAY_COUNT +} r3d_light_array_enum_t; + +typedef struct { + R3D_Light* lights; + int count; +} r3d_light_array_t; + +// Shadow layer pool +typedef struct { + int* freeLayers; + int freeCount; + int freeCapacity; + int totalLayers; +} r3d_light_shadow_pool_t; + +// ======================================== +// MODULE STATE +// ======================================== + +extern struct r3d_light { + + // Common framebuffer for rendering or copy + GLuint workFramebuffer; + + // Shadow map arrays and layer pools + GLuint shadowArrays[R3D_LIGHT_TYPE_COUNT]; + r3d_light_shadow_pool_t shadowPools[R3D_LIGHT_TYPE_COUNT]; + + // Light management + r3d_light_array_t arrays[R3D_LIGHT_ARRAY_COUNT]; + r3d_light_t* lights; + int capacityLights; + +} R3D_MOD_LIGHT; + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +/* Initialize module (called once during R3D_Init) */ +bool r3d_light_init(void); + +/* Deinitialize module (called once during R3D_Close) */ +void r3d_light_quit(void); + +/* Create a new light of the given type */ +R3D_Light r3d_light_new(R3D_LightType type); + +/* Delete a light and return it to the free list */ +void r3d_light_delete(R3D_Light index); + +/* Check whether a light handle is valid */ +bool r3d_light_is_valid(R3D_Light index); + +/* Get internal light structure (returns NULL if invalid) */ +r3d_light_t* r3d_light_get(R3D_Light index); + +/* Returns the screen-space rectangle covered by the light's influence */ +r3d_rect_t r3d_light_get_screen_rect(const r3d_light_t* light, const Matrix* viewProj, int w, int h); + +/* Iterator for lights by category (stateful, not thread-safe) */ +bool r3d_light_iter(r3d_light_t** light, r3d_light_array_enum_t array); + +/* Enable shadows for a light */ +bool r3d_light_enable_shadows(r3d_light_t* light); + +/* Disable shadows for a light */ +void r3d_light_disable_shadows(r3d_light_t* light); + +/* Update light states and collect visible ones (can indicate if shadows are visible) */ +void r3d_light_update_and_cull(const R3D_Frustum* viewFrustum, R3D_Camera camera, double aspect, bool* hasVisibleShadows); + +/* Check if shadow map should be rendered (updates state if willBeUpdated is true) */ +bool r3d_light_shadow_should_be_updated(r3d_light_t* light, bool willBeUpdated); + +/* Bind shadow framebuffer for a light type */ +void r3d_light_shadow_bind_fbo(R3D_LightType type, int layer, int face); + +/* Get a shadow map array texture ID */ +GLuint r3d_light_shadow_get(R3D_LightType type); + +// ======================================== +// INLINE QUERIES +// ======================================== + +static inline bool r3d_light_has_visible(void) +{ + return R3D_MOD_LIGHT.arrays[R3D_LIGHT_ARRAY_VISIBLE].count > 0; +} + +#endif // R3D_MODULE_LIGHT_H + +``` + +`src/modules/r3d_render.c`: + +```c +/* r3d_render.c -- Internal R3D render module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_render.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common/r3d_math.h" +#include "../common/r3d_hash.h" + +// ======================================== +// MODULE CONSTANTS +// ======================================== + +static const GLenum INSTANCE_FORMAT_GL_TYPE[R3D_INSTANCE_FORMAT_COUNT] = { + [R3D_INSTANCE_FORMAT_FLOAT32] = GL_FLOAT, + [R3D_INSTANCE_FORMAT_FLOAT16] = GL_HALF_FLOAT, + [R3D_INSTANCE_FORMAT_UNORM16] = GL_UNSIGNED_SHORT, + [R3D_INSTANCE_FORMAT_SNORM16] = GL_SHORT, + [R3D_INSTANCE_FORMAT_UNORM8] = GL_UNSIGNED_BYTE, + [R3D_INSTANCE_FORMAT_SNORM8] = GL_BYTE, +}; + +static const GLboolean INSTANCE_FORMAT_NORMALIZED[R3D_INSTANCE_FORMAT_COUNT] = { + [R3D_INSTANCE_FORMAT_FLOAT32] = GL_FALSE, + [R3D_INSTANCE_FORMAT_FLOAT16] = GL_FALSE, + [R3D_INSTANCE_FORMAT_UNORM16] = GL_TRUE, + [R3D_INSTANCE_FORMAT_SNORM16] = GL_TRUE, + [R3D_INSTANCE_FORMAT_UNORM8] = GL_TRUE, + [R3D_INSTANCE_FORMAT_SNORM8] = GL_TRUE, +}; + +static const int INSTANCE_ATTRIBUTE_COMPONENTS[R3D_INSTANCE_ATTRIBUTE_COUNT] = { + /* POSITION */ 3, + /* ROTATION */ 4, + /* SCALE */ 3, + /* COLOR */ 4, + /* CUSTOM */ 4, +}; + +static const int INSTANCE_FORMAT_SIZE[R3D_INSTANCE_FORMAT_COUNT] = { + [R3D_INSTANCE_FORMAT_FLOAT32] = 4, + [R3D_INSTANCE_FORMAT_FLOAT16] = 2, + [R3D_INSTANCE_FORMAT_UNORM16] = 2, + [R3D_INSTANCE_FORMAT_SNORM16] = 2, + [R3D_INSTANCE_FORMAT_UNORM8] = 1, + [R3D_INSTANCE_FORMAT_SNORM8] = 1, +}; + +// ======================================== +// MODULE STATE +// ======================================== + +struct r3d_mod_render R3D_MOD_RENDER; + +// ======================================== +// INTERNAL BUFFER RESIZE FUNCTIONS +// ======================================== + +/* + * Sets up vertex attribute pointers on the global VAO (already bound). + * Pass rebindVbo=true after a VBO resize to update the stored buffer ID. + * Pass configInstances=true only at init to set divisors and default values. + */ +static void configure_global_vao_attributes(bool rebindVbo, bool configInstances) +{ + if (rebindVbo) { + glBindBuffer(GL_ARRAY_BUFFER, R3D_MOD_RENDER.globalVbo); + } + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(R3D_Vertex), (void*)offsetof(R3D_Vertex, position)); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_HALF_FLOAT, GL_FALSE, sizeof(R3D_Vertex), (void*)offsetof(R3D_Vertex, texcoord)); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 3, GL_BYTE, GL_TRUE, sizeof(R3D_Vertex), (void*)offsetof(R3D_Vertex, normal)); + + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 4, GL_BYTE, GL_TRUE, sizeof(R3D_Vertex), (void*)offsetof(R3D_Vertex, tangent)); + + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(R3D_Vertex), (void*)offsetof(R3D_Vertex, color)); + + glEnableVertexAttribArray(5); + glVertexAttribIPointer(5, 4, GL_UNSIGNED_BYTE, sizeof(R3D_Vertex), (void*)offsetof(R3D_Vertex, boneIndices)); + + glEnableVertexAttribArray(6); + glVertexAttribPointer(6, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(R3D_Vertex), (void*)offsetof(R3D_Vertex, boneWeights)); + + if (configInstances) { + glVertexAttribDivisor(10, 1); + glVertexAttribDivisor(11, 1); + glVertexAttribDivisor(12, 1); + glVertexAttribDivisor(13, 1); + glVertexAttribDivisor(14, 1); + + glVertexAttrib3f(10, 0.0f, 0.0f, 0.0f); + glVertexAttrib4f(11, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib3f(12, 1.0f, 1.0f, 1.0f); + glVertexAttrib4f(13, 1.0f, 1.0f, 1.0f, 1.0f); + glVertexAttrib4f(14, 0.0f, 0.0f, 0.0f, 0.0f); + } +} + +/* + * Grows the global VBO to at least 'minCapacity' vertices. + * Creates a new buffer, copies the old content via glCopyBufferSubData, + * deletes the old buffer, and reconfigures the VAO attrib pointers. + */ +static bool grow_global_vbo(int minCapacity) +{ + int newCapacity = R3D_MOD_RENDER.globalVertexCapacity * 2; + while (newCapacity < minCapacity) newCapacity *= 2; + + GLuint newVbo; + glGenBuffers(1, &newVbo); + + // Allocate the new buffer without data + glBindBuffer(GL_COPY_WRITE_BUFFER, newVbo); + glBufferData(GL_COPY_WRITE_BUFFER, newCapacity * sizeof(R3D_Vertex), NULL, GL_DYNAMIC_DRAW); + + // Copy the old content + glBindBuffer(GL_COPY_READ_BUFFER, R3D_MOD_RENDER.globalVbo); + glCopyBufferSubData( + GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, + 0, 0, + R3D_MOD_RENDER.globalVertexCount * sizeof(R3D_Vertex) + ); + + // Replaces the old buffer + glDeleteBuffers(1, &R3D_MOD_RENDER.globalVbo); + R3D_MOD_RENDER.globalVbo = newVbo; + R3D_MOD_RENDER.globalVertexCapacity = newCapacity; + + // Reconfigure the assignments on the new VBO + glBindVertexArray(R3D_MOD_RENDER.globalVao); + configure_global_vao_attributes(true, false); + glBindVertexArray(0); + + return true; +} + +/* + * Grows the global EBO to at least 'minCapacity' indices. + * Same strategy as grow_global_vbo: new buffer + copy + delete + rebind. + */ +static bool grow_global_ebo(int minCapacity) +{ + int newCapacity = R3D_MOD_RENDER.globalElementCapacity * 2; + while (newCapacity < minCapacity) newCapacity *= 2; + + GLuint newEbo; + glGenBuffers(1, &newEbo); + + glBindBuffer(GL_COPY_WRITE_BUFFER, newEbo); + glBufferData(GL_COPY_WRITE_BUFFER, newCapacity * sizeof(GLuint), NULL, GL_DYNAMIC_DRAW); + + glBindBuffer(GL_COPY_READ_BUFFER, R3D_MOD_RENDER.globalEbo); + glCopyBufferSubData( + GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, + 0, 0, + R3D_MOD_RENDER.globalElementCount * sizeof(GLuint) + ); + + glDeleteBuffers(1, &R3D_MOD_RENDER.globalEbo); + R3D_MOD_RENDER.globalEbo = newEbo; + R3D_MOD_RENDER.globalElementCapacity = newCapacity; + + // Rebind the EBO into the global VAO + glBindVertexArray(R3D_MOD_RENDER.globalVao); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, R3D_MOD_RENDER.globalEbo); + glBindVertexArray(0); + + return true; +} + +/* + * Copies 'count' vertex slots from 'srcOffset' to 'dstOffset' within + * the global VBO. Ranges must not overlap. + */ +static void copy_global_vertices(int dstOffset, int srcOffset, int count) +{ + GLsizeiptr size = count * sizeof(R3D_Vertex); + + glBindBuffer(GL_COPY_READ_BUFFER, R3D_MOD_RENDER.globalVbo); + glBindBuffer(GL_COPY_WRITE_BUFFER, R3D_MOD_RENDER.globalVbo); + glCopyBufferSubData( + GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, + srcOffset * sizeof(R3D_Vertex), + dstOffset * sizeof(R3D_Vertex), + size + ); +} + +/* + * Copies 'count' index slots from 'srcOffset' to 'dstOffset' within + * the global EBO. Ranges must not overlap. + */ +static void copy_global_elements(int dstOffset, int srcOffset, int count) +{ + GLsizeiptr size = count * sizeof(GLuint); + + glBindBuffer(GL_COPY_READ_BUFFER, R3D_MOD_RENDER.globalEbo); + glBindBuffer(GL_COPY_WRITE_BUFFER, R3D_MOD_RENDER.globalEbo); + glCopyBufferSubData( + GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, + srcOffset * sizeof(GLuint), + dstOffset * sizeof(GLuint), + size + ); +} + +// ======================================== +// INTERNAL FREE LIST FUNCTIONS +// ======================================== + +/* + * After inserting a range into a free list, sort by offset and merge + * adjacent/overlapping blocks. Keeps the list compact and prevents + * fragmentation from accumulating across many alloc/free cycles. + */ +static void coalesce_free_list(r3d_render_range_t* list, int* count) +{ + // Offset sorting (insertion sort, the list is almost sorted in practice) + for (int i = 1; i < *count; i++) { + r3d_render_range_t key = list[i]; + int j = i - 1; + while (j >= 0 && list[j].offset > key.offset) { + list[j + 1] = list[j]; + j--; + } + list[j + 1] = key; + } + + // Merging of adjacent blocks + int write = 0; + for (int i = 1; i < *count; i++) { + if (list[write].offset + list[write].count >= list[i].offset) { + // Contiguous or overlapping blocks: we extend + int end = list[write].offset + list[write].count; + int end_i = list[i].offset + list[i].count; + if (end_i > end) list[write].count = end_i - list[write].offset; + } + else { + list[++write] = list[i]; + } + } + *count = write + 1; +} + +/* + * Pushes a range onto a free list, growing the list's backing array if needed. + * Returns false on allocation failure. + */ +static bool push_free_range(r3d_render_range_t** list, int* count, int* capacity, + int offset, int rangeCount) +{ + if (*count >= *capacity) { + int newCapacity = (*capacity) * 2; + r3d_render_range_t* p = RL_REALLOC(*list, newCapacity * sizeof(r3d_render_range_t)); + if (!p) return false; + *list = p; + *capacity = newCapacity; + } + + (*list)[(*count)++] = (r3d_render_range_t) { + .offset = offset, .count = rangeCount + }; + + return true; +} + +/* + * First-fit search in a free list. + * If a block large enough is found: + * - it is split if strictly larger than needed (remainder stays in the list) + * - it is removed entirely if it matches exactly + * Returns the offset on success, -1 if nothing fits. + */ +static int pop_free_range(r3d_render_range_t* list, int* count, int needed) +{ + for (int i = 0; i < *count; i++) { + if (list[i].count >= needed) { + int offset = list[i].offset; + if (list[i].count > needed) { + // Cut: we keep the rest in the list + list[i].offset += needed; + list[i].count -= needed; + } + else { + // Exact match: remove the entry + list[i] = list[--(*count)]; + } + return offset; + } + } + return -1; +} + +/* + * Searches the free list for a block starting exactly at 'afterOffset' + * with at least 'needed' slots. If found, consumes 'needed' slots from it + * (splitting the remainder back) and returns true. + */ +static bool try_extend_in_place(r3d_render_range_t* list, int* count, + int afterOffset, int needed) +{ + for (int i = 0; i < *count; i++) { + if (list[i].offset != afterOffset) continue; + + if (list[i].count < needed) { + // Adjacent block but too small + return false; + } + + if (list[i].count == needed) { + // Exact match: remove the entry + list[i] = list[--(*count)]; + } + else { + // We consume 'needed' since the beginning of the block + list[i].offset += needed; + list[i].count -= needed; + } + + return true; + } + + return false; +} + +// ======================================== +// INTERNAL SHAPE FUNCTIONS +// ======================================== + +typedef void (*shape_loader_func)(r3d_render_shape_t*); + +static void load_shape_dummy(r3d_render_shape_t* dummy); +static void load_shape_quad(r3d_render_shape_t* quad); +static void load_shape_cube(r3d_render_shape_t* cube); + +static const shape_loader_func SHAPE_LOADERS[] = { + [R3D_RENDER_SHAPE_DUMMY] = load_shape_dummy, + [R3D_RENDER_SHAPE_QUAD] = load_shape_quad, + [R3D_RENDER_SHAPE_CUBE] = load_shape_cube, +}; + +void load_shape_dummy(r3d_render_shape_t* shape) +{ + shape->vertices.count = 3; + shape->elements.count = 0; +} + +void load_shape_quad(r3d_render_shape_t* shape) +{ + const R3D_Vertex vertices[] = { + R3D_MakeVertex((Vector3){-0.5f, 0.5f, 0}, (Vector2){0, 1}, (Vector3){0, 0, 1}, (Vector4){1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){-0.5f, -0.5f, 0}, (Vector2){0, 0}, (Vector3){0, 0, 1}, (Vector4){1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, 0.5f, 0}, (Vector2){1, 1}, (Vector3){0, 0, 1}, (Vector4){1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, -0.5f, 0}, (Vector2){1, 0}, (Vector3){0, 0, 1}, (Vector4){1, 0, 0, 1}, WHITE), + }; + + static const uint32_t INDICES[] = { + 0, 1, 2, + 1, 3, 2 + }; + + r3d_render_alloc_vertices(ARRAY_SIZE(vertices), &shape->vertices.offset); + r3d_render_alloc_elements(ARRAY_SIZE(INDICES), &shape->elements.offset); + + r3d_render_upload_vertices(shape->vertices.offset, vertices, ARRAY_SIZE(vertices)); + r3d_render_upload_elements(shape->elements.offset, INDICES, ARRAY_SIZE(INDICES)); + + shape->vertices.count = ARRAY_SIZE(vertices); + shape->elements.count = ARRAY_SIZE(INDICES); +} + +void load_shape_cube(r3d_render_shape_t* shape) +{ + const R3D_Vertex vertices[] = { + // Front (Z+) + R3D_MakeVertex((Vector3){-0.5f, 0.5f, 0.5f}, (Vector2){0, 1}, (Vector3){ 0, 0, 1}, (Vector4){ 1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){-0.5f, -0.5f, 0.5f}, (Vector2){0, 0}, (Vector3){ 0, 0, 1}, (Vector4){ 1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, 0.5f, 0.5f}, (Vector2){1, 1}, (Vector3){ 0, 0, 1}, (Vector4){ 1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, -0.5f, 0.5f}, (Vector2){1, 0}, (Vector3){ 0, 0, 1}, (Vector4){ 1, 0, 0, 1}, WHITE), + // Back (Z-) + R3D_MakeVertex((Vector3){-0.5f, 0.5f, -0.5f}, (Vector2){1, 1}, (Vector3){ 0, 0,-1}, (Vector4){-1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){-0.5f, -0.5f, -0.5f}, (Vector2){1, 0}, (Vector3){ 0, 0,-1}, (Vector4){-1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, 0.5f, -0.5f}, (Vector2){0, 1}, (Vector3){ 0, 0,-1}, (Vector4){-1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, -0.5f, -0.5f}, (Vector2){0, 0}, (Vector3){ 0, 0,-1}, (Vector4){-1, 0, 0, 1}, WHITE), + // Left (X-) + R3D_MakeVertex((Vector3){-0.5f, 0.5f, -0.5f}, (Vector2){0, 1}, (Vector3){-1, 0, 0}, (Vector4){ 0, 0,-1, 1}, WHITE), + R3D_MakeVertex((Vector3){-0.5f, -0.5f, -0.5f}, (Vector2){0, 0}, (Vector3){-1, 0, 0}, (Vector4){ 0, 0,-1, 1}, WHITE), + R3D_MakeVertex((Vector3){-0.5f, 0.5f, 0.5f}, (Vector2){1, 1}, (Vector3){-1, 0, 0}, (Vector4){ 0, 0,-1, 1}, WHITE), + R3D_MakeVertex((Vector3){-0.5f, -0.5f, 0.5f}, (Vector2){1, 0}, (Vector3){-1, 0, 0}, (Vector4){ 0, 0,-1, 1}, WHITE), + // Right (X+) + R3D_MakeVertex((Vector3){ 0.5f, 0.5f, 0.5f}, (Vector2){0, 1}, (Vector3){ 1, 0, 0}, (Vector4){ 0, 0, 1, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, -0.5f, 0.5f}, (Vector2){0, 0}, (Vector3){ 1, 0, 0}, (Vector4){ 0, 0, 1, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, 0.5f, -0.5f}, (Vector2){1, 1}, (Vector3){ 1, 0, 0}, (Vector4){ 0, 0, 1, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, -0.5f, -0.5f}, (Vector2){1, 0}, (Vector3){ 1, 0, 0}, (Vector4){ 0, 0, 1, 1}, WHITE), + // Top (Y+) + R3D_MakeVertex((Vector3){-0.5f, 0.5f, -0.5f}, (Vector2){0, 0}, (Vector3){ 0, 1, 0}, (Vector4){ 1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){-0.5f, 0.5f, 0.5f}, (Vector2){0, 1}, (Vector3){ 0, 1, 0}, (Vector4){ 1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, 0.5f, -0.5f}, (Vector2){1, 0}, (Vector3){ 0, 1, 0}, (Vector4){ 1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, 0.5f, 0.5f}, (Vector2){1, 1}, (Vector3){ 0, 1, 0}, (Vector4){ 1, 0, 0, 1}, WHITE), + // Bottom (Y-) + R3D_MakeVertex((Vector3){-0.5f, -0.5f, 0.5f}, (Vector2){0, 0}, (Vector3){ 0,-1, 0}, (Vector4){ 1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){-0.5f, -0.5f, -0.5f}, (Vector2){0, 1}, (Vector3){ 0,-1, 0}, (Vector4){ 1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, -0.5f, 0.5f}, (Vector2){1, 0}, (Vector3){ 0,-1, 0}, (Vector4){ 1, 0, 0, 1}, WHITE), + R3D_MakeVertex((Vector3){ 0.5f, -0.5f, -0.5f}, (Vector2){1, 1}, (Vector3){ 0,-1, 0}, (Vector4){ 1, 0, 0, 1}, WHITE), + }; + + static const uint32_t INDICES[] = { + 0,1,2, 2,1,3, + 6,5,4, 7,5,6, + 8,9,10, 10,9,11, + 12,13,14, 14,13,15, + 16,17,18, 18,17,19, + 20,21,22, 22,21,23 + }; + + r3d_render_alloc_vertices(ARRAY_SIZE(vertices), &shape->vertices.offset); + r3d_render_alloc_elements(ARRAY_SIZE(INDICES), &shape->elements.offset); + + r3d_render_upload_vertices(shape->vertices.offset, vertices, ARRAY_SIZE(vertices)); + r3d_render_upload_elements(shape->elements.offset, INDICES, ARRAY_SIZE(INDICES)); + + shape->vertices.count = ARRAY_SIZE(vertices); + shape->elements.count = ARRAY_SIZE(INDICES); +} + +// ======================================== +// INTERNAL INSTANCES FUNCTIONS +// ======================================== + +static void enable_instances(const GLuint buffers[R3D_INSTANCE_ATTRIBUTE_COUNT], R3D_InstanceLayout layout, int offset) +{ + r3d_render_instance_state_t* state = &R3D_MOD_RENDER.instanceState; + + if (offset == state->offset && + memcmp(buffers, state->buffers, R3D_INSTANCE_ATTRIBUTE_COUNT * sizeof(GLuint)) == 0 && + memcmp(&layout, &state->layout, sizeof(R3D_InstanceLayout)) == 0) { + return; + } + + memcpy(state->buffers, buffers, R3D_INSTANCE_ATTRIBUTE_COUNT * sizeof(GLuint)); + state->layout = layout; + state->offset = offset; + + for (int i = 0; i < R3D_INSTANCE_ATTRIBUTE_COUNT; i++) { + GLuint attrib = 10 + i; + R3D_InstanceFlags flag = 1u << i; + + if (!BIT_TEST(layout.flags, flag)) { + glDisableVertexAttribArray(attrib); + continue; + } + + R3D_InstanceFormat format = layout.formats[i]; + + if (format < 0 || format >= R3D_INSTANCE_FORMAT_COUNT) { + glDisableVertexAttribArray(attrib); + R3D_TRACELOG(LOG_WARNING, "enable_instances -> invalid instance format (attribute=%d, format=%d)", i, format); + continue; + } + + int components = INSTANCE_ATTRIBUTE_COMPONENTS[i]; + int stride = components * INSTANCE_FORMAT_SIZE[format]; + + glBindBuffer(GL_ARRAY_BUFFER, buffers[i]); + glEnableVertexAttribArray(attrib); + glVertexAttribPointer( + attrib, + components, + INSTANCE_FORMAT_GL_TYPE[format], + INSTANCE_FORMAT_NORMALIZED[format], + stride, + (void*)((size_t)offset * stride) + ); + } +} + +static void disable_instances(R3D_InstanceFlags flags) +{ + memset(&R3D_MOD_RENDER.instanceState, 0, sizeof(R3D_MOD_RENDER.instanceState)); + + if (BIT_TEST(flags, R3D_INSTANCE_POSITION)) { + glDisableVertexAttribArray(10); + } + + if (BIT_TEST(flags, R3D_INSTANCE_ROTATION)) { + glDisableVertexAttribArray(11); + } + + if (BIT_TEST(flags, R3D_INSTANCE_SCALE)) { + glDisableVertexAttribArray(12); + } + + if (BIT_TEST(flags, R3D_INSTANCE_COLOR)) { + glDisableVertexAttribArray(13); + } + + if (BIT_TEST(flags, R3D_INSTANCE_CUSTOM)) { + glDisableVertexAttribArray(14); + } +} + +// ======================================== +// INTERNAL ARRAY FUNCTIONS +// ======================================== + +static inline int get_draw_call_index(const r3d_render_call_t* call) +{ + assert(call >= R3D_MOD_RENDER.calls); + return (int)(call - R3D_MOD_RENDER.calls); +} + +static inline int get_last_group_index(void) +{ + int groupIndex = R3D_MOD_RENDER.numGroups - 1; + assert(groupIndex >= 0); + return groupIndex; +} + +static inline r3d_render_group_t* get_last_group(void) +{ + int groupIndex = get_last_group_index(); + return &R3D_MOD_RENDER.groups[groupIndex]; +} + +static bool growth_arrays(void) +{ + #define GROW_AND_ASSIGN(field) do { \ + void* _p = RL_REALLOC(R3D_MOD_RENDER.field, newCapacity * sizeof(*R3D_MOD_RENDER.field)); \ + if (_p == NULL) return false; \ + R3D_MOD_RENDER.field = _p; \ + } while (0) + + int newCapacity = 2 * R3D_MOD_RENDER.capacity; + + GROW_AND_ASSIGN(clusters); + GROW_AND_ASSIGN(groupVisibility); + GROW_AND_ASSIGN(callIndices); + GROW_AND_ASSIGN(groups); + + for (int i = 0; i < R3D_RENDER_LIST_COUNT; ++i) { + GROW_AND_ASSIGN(list[i].calls); + } + + GROW_AND_ASSIGN(calls); + GROW_AND_ASSIGN(groupIndices); + GROW_AND_ASSIGN(sortCache); + + #undef GROW_AND_ASSIGN + + R3D_MOD_RENDER.capacity = newCapacity; + + return true; +} + +// ======================================== +// INTERNAL BINDING FUNCTIONS +// ======================================== + +static inline GLenum get_opengl_primitive(R3D_PrimitiveType primitive) +{ + switch (primitive) { + case R3D_PRIMITIVE_POINTS: return GL_POINTS; + case R3D_PRIMITIVE_LINES: return GL_LINES; + case R3D_PRIMITIVE_LINE_STRIP: return GL_LINE_STRIP; + case R3D_PRIMITIVE_LINE_LOOP: return GL_LINE_LOOP; + case R3D_PRIMITIVE_TRIANGLES: return GL_TRIANGLES; + case R3D_PRIMITIVE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; + case R3D_PRIMITIVE_TRIANGLE_FAN: return GL_TRIANGLE_FAN; + default: break; + } + + return GL_TRIANGLES; // consider an error... +} + +static void get_draw_call_info(const r3d_render_call_t* call, GLenum* primitive, r3d_render_range_t* vertexRange, r3d_render_range_t* indexRange) +{ + assert(primitive && vertexRange && indexRange); + + *primitive = GL_NONE; + *vertexRange = (r3d_render_range_t) {0}; + *indexRange = (r3d_render_range_t) {0}; + + switch (call->type) { + case R3D_RENDER_CALL_MESH: + { + const R3D_Mesh* mesh = &call->mesh.instance; + + *primitive = get_opengl_primitive(mesh->primitiveType); + vertexRange->offset = mesh->vertexOffset; + vertexRange->count = mesh->vertexCount; + indexRange->offset = mesh->indexOffset; + indexRange->count = mesh->indexCount; + } + break; + case R3D_RENDER_CALL_DECAL: + { + r3d_render_shape_t* shape = &R3D_MOD_RENDER.shapes[R3D_RENDER_SHAPE_CUBE]; + if (shape->vertices.count == 0) { + SHAPE_LOADERS[R3D_RENDER_SHAPE_CUBE](shape); + glBindVertexArray(R3D_MOD_RENDER.globalVao); + } + + *primitive = GL_TRIANGLES; + vertexRange->offset = shape->vertices.offset; + vertexRange->count = shape->vertices.count; + indexRange->offset = shape->elements.offset; + indexRange->count = shape->elements.count; + } + break; + default: + assert(false); + break; + } +} + +// ======================================== +// INTERNAL CULLING FUNCTIONS +// ======================================== + +static inline bool is_aabb_visible(const R3D_Frustum* frustum, BoundingBox aabb) +{ + if (memcmp(&aabb, &(BoundingBox){0}, sizeof(BoundingBox)) == 0) { + return true; + } + + return R3D_FrustumIntersectsBoundingBox(frustum, aabb); +} + +static inline bool is_obb_visible(const R3D_Frustum* frustum, R3D_OrientedBox obb) +{ + if (memcmp(&obb, &(R3D_OrientedBox){0}, sizeof(R3D_OrientedBox)) == 0) { + return true; + } + + return R3D_FrustumIntersectsOrientedBox(frustum, obb); +} + +static inline bool is_transformed_aabb_visible(const R3D_Frustum* frustum, BoundingBox aabb, Matrix transform) +{ + if (memcmp(&aabb, &(BoundingBox){0}, sizeof(BoundingBox)) == 0) { + return true; + } + + if (r3d_matrix_is_identity(transform)) { + return R3D_FrustumIntersectsBoundingBox(frustum, aabb); + } + + return R3D_FrustumIntersectsOrientedBox(frustum, R3D_GetOrientedBox(aabb, transform)); +} + +static inline bool is_draw_call_visible(const R3D_Frustum* frustum, const r3d_render_call_t* call, Matrix transform) +{ + switch (call->type) { + case R3D_RENDER_CALL_MESH: + return is_transformed_aabb_visible(frustum, call->mesh.instance.aabb, transform); + case R3D_RENDER_CALL_DECAL: + return is_transformed_aabb_visible(frustum, (BoundingBox) { + .min.x = -0.5f, .min.y = -0.5f, .min.z = -0.5f, + .max.x = +0.5f, .max.y = +0.5f, .max.z = +0.5f + }, transform); + default: + assert(false); + break; + } + + return false; +} + +// ======================================== +// INTERNAL SORTING FUNCTIONS +// ======================================== + +static Vector3 G_sortViewPosition = {0}; + +static inline float calculate_center_distance_to_camera(const BoundingBox* aabb, const Matrix* transform) +{ + Vector3 center = { + (aabb->min.x + aabb->max.x) * 0.5f, + (aabb->min.y + aabb->max.y) * 0.5f, + (aabb->min.z + aabb->max.z) * 0.5f + }; + center = Vector3Transform(center, *transform); + + return Vector3DistanceSqr(G_sortViewPosition, center); +} + +static inline float calculate_max_distance_to_camera(const BoundingBox* aabb, const Matrix* transform) +{ + float maxDistSq = 0.0f; + + for (int i = 0; i < 8; ++i) { + Vector3 corner = { + (i & 1) ? aabb->max.x : aabb->min.x, + (i & 2) ? aabb->max.y : aabb->min.y, + (i & 4) ? aabb->max.z : aabb->min.z + }; + + corner = Vector3Transform(corner, *transform); + float distSq = Vector3DistanceSqr(G_sortViewPosition, corner); + maxDistSq = (distSq > maxDistSq) ? distSq : maxDistSq; + } + + return maxDistSq; +} + +static inline void sort_fill_state_data(r3d_render_sort_state_t* state, const r3d_render_call_t* call) +{ + memset(state, 0, sizeof(*state)); + + switch (call->type) { + case R3D_RENDER_CALL_MESH: + state->priority = call->mesh.material.priority; + state->shader = (uintptr_t)call->mesh.material.shader; + state->shading = call->mesh.material.unlit; + state->albedo = call->mesh.material.albedo.texture.id; + state->normal = call->mesh.material.normal.texture.id; + state->orm = call->mesh.material.orm.texture.id; + state->emission = call->mesh.material.emission.texture.id; + state->stencil = r3d_hash_fnv1a_32(&call->mesh.material.stencil, sizeof(call->mesh.material.stencil)); + state->depth = r3d_hash_fnv1a_32(&call->mesh.material.depth, sizeof(call->mesh.material.depth)); + state->blend = call->mesh.material.blendMode; + state->cull = call->mesh.material.cullMode; + state->transparency = call->mesh.material.transparencyMode; + state->billboard = call->mesh.material.billboardMode; + break; + + case R3D_RENDER_CALL_DECAL: + state->shader = (uintptr_t)call->decal.instance.shader; + state->albedo = call->decal.instance.albedo.texture.id; + state->normal = call->decal.instance.normal.texture.id; + state->orm = call->decal.instance.orm.texture.id; + state->emission = call->decal.instance.emission.texture.id; + break; + } +} + +static void sort_fill_cache_front_to_back(r3d_render_list_enum_t list) +{ + assert(list < R3D_RENDER_LIST_NON_INST_COUNT && "Instantiated render lists should not be sorted by distance"); + assert(list != R3D_RENDER_LIST_DECAL && "Decal render list should not be sorted by distance"); + + r3d_render_list_t* drawList = &R3D_MOD_RENDER.list[list]; + + for (int i = 0; i < drawList->numCalls; i++) + { + int callIndex = drawList->calls[i]; + const r3d_render_call_t* call = &R3D_MOD_RENDER.calls[callIndex]; + const r3d_render_group_t* group = r3d_render_get_call_group(call); + r3d_render_sort_t* sortData = &R3D_MOD_RENDER.sortCache[callIndex]; + + sortData->distance = calculate_center_distance_to_camera( + &call->mesh.instance.aabb, &group->transform + ); + + sort_fill_state_data(&sortData->state, call); + } +} + +static void sort_fill_cache_back_to_front(r3d_render_list_enum_t list) +{ + assert(list < R3D_RENDER_LIST_NON_INST_COUNT && "Instantiated render lists should not be sorted by distance"); + assert(list != R3D_RENDER_LIST_DECAL && "Decal render list should not be sorted by distance"); + + r3d_render_list_t* drawList = &R3D_MOD_RENDER.list[list]; + + for (int i = 0; i < drawList->numCalls; i++) + { + int callIndex = drawList->calls[i]; + const r3d_render_call_t* call = &R3D_MOD_RENDER.calls[callIndex]; + const r3d_render_group_t* group = r3d_render_get_call_group(call); + r3d_render_sort_t* sortData = &R3D_MOD_RENDER.sortCache[callIndex]; + + sortData->distance = calculate_max_distance_to_camera( + &call->mesh.instance.aabb, &group->transform + ); + + // For back-to-front sorting we just need the priority for the meshes + memset(&sortData->state, 0, sizeof(sortData->state)); + if (call->type == R3D_RENDER_CALL_MESH) { + sortData->state.priority = call->mesh.material.priority; + } + } +} + +static void sort_fill_cache_by_material(r3d_render_list_enum_t list) +{ + r3d_render_list_t* drawList = &R3D_MOD_RENDER.list[list]; + + for (int i = 0; i < drawList->numCalls; i++) + { + int callIndex = drawList->calls[i]; + const r3d_render_call_t* call = &R3D_MOD_RENDER.calls[callIndex]; + r3d_render_sort_t* sortData = &R3D_MOD_RENDER.sortCache[callIndex]; + + sortData->distance = 0.0f; + sort_fill_state_data(&sortData->state, call); + } +} + +static inline int compare_i32(int32_t a, int32_t b) +{ + return (a > b) - (a < b); +} + +static inline int compare_f32(float a, float b) +{ + return (a > b) - (a < b); +} + +static inline int compare_material(const r3d_render_sort_t* a, const r3d_render_sort_t* b) +{ + // User priority first (signed) + if (a->state.priority != b->state.priority) { + return compare_i32(a->state.priority, b->state.priority); + } + + // Remaining fields via memcmp (must be all unsigned, zero-padded) + size_t n = sizeof(a->state) - offsetof(r3d_render_sort_state_t, shader); + return memcmp(&a->state.shader, &b->state.shader, n); +} + +static int compare_front_to_back(const void* a, const void* b) +{ + const r3d_render_sort_t* aEntry = &R3D_MOD_RENDER.sortCache[*(const int*)(a)]; + const r3d_render_sort_t* bEntry = &R3D_MOD_RENDER.sortCache[*(const int*)(b)]; + + int cmp = compare_material(aEntry, bEntry); + if (cmp != 0) return cmp; + + return compare_f32(aEntry->distance, bEntry->distance); +} + +static int compare_back_to_front(const void* a, const void* b) +{ + const r3d_render_sort_t* aEntry = &R3D_MOD_RENDER.sortCache[*(const int*)(a)]; + const r3d_render_sort_t* bEntry = &R3D_MOD_RENDER.sortCache[*(const int*)(b)]; + + int cmp = compare_i32(aEntry->state.priority, bEntry->state.priority); + if (cmp != 0) return cmp; + + return compare_f32(bEntry->distance, aEntry->distance); +} + +static int compare_materials_only(const void* a, const void* b) +{ + const r3d_render_sort_t* aEntry = &R3D_MOD_RENDER.sortCache[*(const int*)(a)]; + const r3d_render_sort_t* bEntry = &R3D_MOD_RENDER.sortCache[*(const int*)(b)]; + + return compare_material(aEntry, bEntry); +} + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +bool r3d_render_init(void) +{ + memset(&R3D_MOD_RENDER, 0, sizeof(R3D_MOD_RENDER)); + + /* --- CPU array allocation (draw calls, groups, etc) --- */ + + #define ALLOC_AND_ASSIGN(field, logfmt, ...) do { \ + void* _p = RL_CALLOC(R3D_RENDER_INITIAL_DRAW_CALL_RESERVE, sizeof(*R3D_MOD_RENDER.field)); \ + if (_p == NULL) { \ + R3D_TRACELOG(LOG_FATAL, "Failed to init render module; " logfmt, ##__VA_ARGS__); \ + goto fail; \ + } \ + R3D_MOD_RENDER.field = _p; \ + } while (0) + + ALLOC_AND_ASSIGN(clusters, "Render cluster array allocation failed"); + ALLOC_AND_ASSIGN(groupVisibility, "Render group visibility array allocation failed"); + ALLOC_AND_ASSIGN(callIndices, "Draw call indices array allocation failed"); + ALLOC_AND_ASSIGN(groups, "Render group array allocation failed"); + + for (int i = 0; i < R3D_RENDER_LIST_COUNT; i++) { + ALLOC_AND_ASSIGN(list[i].calls, "Draw call list[%i] allocation failed", i); + } + + ALLOC_AND_ASSIGN(calls, "Draw call array allocation failed"); + ALLOC_AND_ASSIGN(groupIndices, "Render group indices array allocation failed"); + ALLOC_AND_ASSIGN(sortCache, "Sorting cache array allocation failed"); + + #undef ALLOC_AND_ASSIGN + + R3D_MOD_RENDER.capacity = R3D_RENDER_INITIAL_DRAW_CALL_RESERVE; + R3D_MOD_RENDER.activeCluster = -1; + + /* --- CPU free list allocation --- */ + + #define ALLOC_FREELIST(field, cap_field, logmsg) do { \ + R3D_MOD_RENDER.field = RL_MALLOC(R3D_RENDER_INITIAL_FREE_LIST_RESERVE * sizeof(*R3D_MOD_RENDER.field)); \ + if (!R3D_MOD_RENDER.field) { \ + R3D_TRACELOG(LOG_FATAL, "Failed to init render module; " logmsg); \ + goto fail; \ + } \ + R3D_MOD_RENDER.cap_field = R3D_RENDER_INITIAL_FREE_LIST_RESERVE; \ + } while (0) + + ALLOC_FREELIST(freeVertices, freeVertexCapacity, "Free vertex list allocation failed"); + ALLOC_FREELIST(freeElements, freeElementCapacity, "Free element list allocation failed"); + + #undef ALLOC_FREELIST + + /* --- Creation of the global VAO/VBO/EBO --- */ + + glGenVertexArrays(1, &R3D_MOD_RENDER.globalVao); + glBindVertexArray(R3D_MOD_RENDER.globalVao); + + glGenBuffers(1, &R3D_MOD_RENDER.globalVbo); + glBindBuffer(GL_ARRAY_BUFFER, R3D_MOD_RENDER.globalVbo); + glBufferData(GL_ARRAY_BUFFER, + R3D_RENDER_INITIAL_VERTICES_RESERVE * sizeof(R3D_Vertex), + NULL, GL_DYNAMIC_DRAW); + + glGenBuffers(1, &R3D_MOD_RENDER.globalEbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, R3D_MOD_RENDER.globalEbo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + R3D_RENDER_INITIAL_ELEMENTS_RESERVE * sizeof(GLuint), + NULL, GL_DYNAMIC_DRAW); + + R3D_MOD_RENDER.globalVertexCapacity = R3D_RENDER_INITIAL_VERTICES_RESERVE; + R3D_MOD_RENDER.globalElementCapacity = R3D_RENDER_INITIAL_ELEMENTS_RESERVE; + + /* --- Configuring vertex attributes --- */ + + configure_global_vao_attributes(false, true); + glBindVertexArray(0); + + return true; + +fail: + r3d_render_quit(); + return false; +} + +void r3d_render_quit(void) +{ + /* --- Delete global GL buffers --- */ + + if (R3D_MOD_RENDER.globalVao) glDeleteVertexArrays(1, &R3D_MOD_RENDER.globalVao); + if (R3D_MOD_RENDER.globalVbo) glDeleteBuffers(1, &R3D_MOD_RENDER.globalVbo); + if (R3D_MOD_RENDER.globalEbo) glDeleteBuffers(1, &R3D_MOD_RENDER.globalEbo); + + /* --- Release CPU arrays --- */ + + for (int i = 0; i < R3D_RENDER_LIST_COUNT; i++) { + RL_FREE(R3D_MOD_RENDER.list[i].calls); + } + + RL_FREE(R3D_MOD_RENDER.groupVisibility); + RL_FREE(R3D_MOD_RENDER.groupIndices); + RL_FREE(R3D_MOD_RENDER.callIndices); + RL_FREE(R3D_MOD_RENDER.sortCache); + RL_FREE(R3D_MOD_RENDER.clusters); + RL_FREE(R3D_MOD_RENDER.groups); + RL_FREE(R3D_MOD_RENDER.calls); + + /* --- Realease free lists --- */ + + RL_FREE(R3D_MOD_RENDER.freeVertices); + RL_FREE(R3D_MOD_RENDER.freeElements); +} + +bool r3d_render_alloc_vertices(int count, int* outOffset) +{ + assert(outOffset != NULL); + assert(count > 0); + + // First search the free list + int offset = pop_free_range( + R3D_MOD_RENDER.freeVertices, + &R3D_MOD_RENDER.numFreeVertices, + count + ); + + if (offset >= 0) { + *outOffset = offset; + return true; + } + + // No free block, we extend from the end of the buffer + int needed = R3D_MOD_RENDER.globalVertexCount + count; + if (needed > R3D_MOD_RENDER.globalVertexCapacity) { + if (!grow_global_vbo(needed)) { + R3D_TRACELOG(LOG_FATAL, "r3d_render_alloc_vertices: VBO resize failed"); + return false; + } + } + + *outOffset = R3D_MOD_RENDER.globalVertexCount; + R3D_MOD_RENDER.globalVertexCount += count; + return true; +} + +bool r3d_render_alloc_elements(int count, int* outOffset) +{ + assert(outOffset != NULL); + assert(count > 0); + + int offset = pop_free_range( + R3D_MOD_RENDER.freeElements, + &R3D_MOD_RENDER.numFreeElements, + count + ); + + if (offset >= 0) { + *outOffset = offset; + return true; + } + + int needed = R3D_MOD_RENDER.globalElementCount + count; + if (needed > R3D_MOD_RENDER.globalElementCapacity) { + if (!grow_global_ebo(needed)) { + R3D_TRACELOG(LOG_FATAL, "r3d_render_alloc_elements: EBO resize failed"); + return false; + } + } + + *outOffset = R3D_MOD_RENDER.globalElementCount; + R3D_MOD_RENDER.globalElementCount += count; + return true; +} + +bool r3d_render_realloc_vertices(int* offset, int* count, int newCount, bool keepData) +{ + assert(offset != NULL && count != NULL); + assert(*offset >= 0 && *count >= 0 && newCount > 0); + + if (newCount == *count) { + return true; + } + + // Reduction + // The queue is released: the (count - newCount) end slots + // are returned to the free list. No GPU copy is necessary + if (newCount < *count) { + r3d_render_free_vertices(*offset + newCount, *count - newCount); + *count = newCount; + return true; + } + + // Enlargement + int extra = newCount - *count; + + // Case 1: Extension in place if the free list + // has a contiguous block immediately after + if (try_extend_in_place( + R3D_MOD_RENDER.freeVertices, + &R3D_MOD_RENDER.numFreeVertices, + *offset + *count, extra)) + { + *count = newCount; + return true; + } + + // Case 2: Extension in place if we are at the end of + // the buffer and there is still uncommitted capacity + if (*offset + *count == R3D_MOD_RENDER.globalVertexCount) { + int needed = R3D_MOD_RENDER.globalVertexCount + extra; + if (needed > R3D_MOD_RENDER.globalVertexCapacity) { + if (!grow_global_vbo(needed)) { + R3D_TRACELOG(LOG_FATAL, "r3d_render_realloc_vertices: VBO resize failed"); + return false; + } + } + R3D_MOD_RENDER.globalVertexCount += extra; + *count = newCount; + return true; + } + + // Case 3: no contiguous space, we look for a new larger block, + // copy the existing data into it, then free the old one + int newOffset; + if (!r3d_render_alloc_vertices(newCount, &newOffset)) { + return false; + } + + if (keepData) { + copy_global_vertices(newOffset, *offset, *count); + } + + r3d_render_free_vertices(*offset, *count); + + *offset = newOffset; + *count = newCount; + return true; +} + +bool r3d_render_realloc_elements(int* offset, int* count, int newCount, bool keepData) +{ + assert(offset != NULL && count != NULL); + assert(*offset >= 0 && *count >= 0 && newCount > 0); + + if (newCount == *count) { + return true; + } + + // Reduction + // The queue is released: the (count - newCount) end slots + // are returned to the free list. No GPU copy is necessary + if (newCount < *count) { + r3d_render_free_elements(*offset + newCount, *count - newCount); + *count = newCount; + return true; + } + + // Enlargement + int extra = newCount - *count; + + // Case 1: Extension in place if the free list + // has a contiguous block immediately after + if (try_extend_in_place( + R3D_MOD_RENDER.freeElements, + &R3D_MOD_RENDER.numFreeElements, + *offset + *count, extra)) + { + *count = newCount; + return true; + } + + // Case 2: Extension in place if we are at the end of + // the buffer and there is still uncommitted capacity + if (*offset + *count == R3D_MOD_RENDER.globalElementCount) { + int needed = R3D_MOD_RENDER.globalElementCount + extra; + if (needed > R3D_MOD_RENDER.globalElementCapacity) { + if (!grow_global_ebo(needed)) { + R3D_TRACELOG(LOG_FATAL, "r3d_render_realloc_elements: EBO resize failed"); + return false; + } + } + R3D_MOD_RENDER.globalElementCount += extra; + *count = newCount; + return true; + } + + // Case 3: no contiguous space, we look for a new larger block, + // copy the existing data into it, then free the old one + int newOffset; + if (!r3d_render_alloc_elements(newCount, &newOffset)) { + return false; + } + + if (keepData) { + copy_global_elements(newOffset, *offset, *count); + } + + r3d_render_free_elements(*offset, *count); + + *offset = newOffset; + *count = newCount; + return true; +} + +void r3d_render_free_vertices(int offset, int count) +{ + assert(offset >= 0 && count > 0); + + if (!push_free_range( + &R3D_MOD_RENDER.freeVertices, + &R3D_MOD_RENDER.numFreeVertices, + &R3D_MOD_RENDER.freeVertexCapacity, + offset, count)) + { + R3D_TRACELOG(LOG_WARNING, "r3d_render_free_vertices: free list push failed (leak)"); + return; + } + + coalesce_free_list(R3D_MOD_RENDER.freeVertices, &R3D_MOD_RENDER.numFreeVertices); +} + +void r3d_render_free_elements(int offset, int count) +{ + assert(offset >= 0 && count > 0); + + if (!push_free_range( + &R3D_MOD_RENDER.freeElements, + &R3D_MOD_RENDER.numFreeElements, + &R3D_MOD_RENDER.freeElementCapacity, + offset, count)) + { + R3D_TRACELOG(LOG_WARNING, "r3d_render_free_elements: free list push failed (leak)"); + return; + } + + coalesce_free_list(R3D_MOD_RENDER.freeElements, &R3D_MOD_RENDER.numFreeElements); +} + +void r3d_render_upload_vertices(int offset, const R3D_Vertex* verts, int count) +{ + assert(offset >= 0 && verts != NULL && count > 0); + assert(offset + count <= R3D_MOD_RENDER.globalVertexCapacity); + + glBindBuffer(GL_ARRAY_BUFFER, R3D_MOD_RENDER.globalVbo); + glBufferSubData( + GL_ARRAY_BUFFER, + offset * sizeof(R3D_Vertex), + count * sizeof(R3D_Vertex), + verts + ); +} + +void r3d_render_upload_elements(int offset, const GLuint* indices, int count) +{ + assert(offset >= 0 && indices != NULL && count > 0); + assert(offset + count <= R3D_MOD_RENDER.globalElementCapacity); + + glBindVertexArray(0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, R3D_MOD_RENDER.globalEbo); + glBufferSubData( + GL_ELEMENT_ARRAY_BUFFER, + offset * sizeof(GLuint), + count * sizeof(GLuint), + indices + ); +} + +void r3d_render_clear(void) +{ + for (int i = 0; i < R3D_RENDER_LIST_COUNT; i++) { + R3D_MOD_RENDER.list[i].numCalls = 0; + } + + R3D_MOD_RENDER.numClusters = 0; + R3D_MOD_RENDER.numGroups = 0; + R3D_MOD_RENDER.numCalls = 0; + + R3D_MOD_RENDER.groupCulled = false; + R3D_MOD_RENDER.hasDeferred = false; + R3D_MOD_RENDER.hasPrepass = false; + R3D_MOD_RENDER.hasForward = false; +} + +bool r3d_render_cluster_begin(BoundingBox aabb) +{ + if (R3D_MOD_RENDER.activeCluster >= 0) { + return false; + } + + if (R3D_MOD_RENDER.numClusters >= R3D_MOD_RENDER.capacity) { + if (!growth_arrays()) { + R3D_TRACELOG(LOG_FATAL, "Bad alloc on render cluster begin"); + return false; + } + } + + R3D_MOD_RENDER.activeCluster = R3D_MOD_RENDER.numClusters++; + + r3d_render_cluster_t* cluster = &R3D_MOD_RENDER.clusters[R3D_MOD_RENDER.activeCluster]; + cluster->visible = R3D_RENDER_VISBILITY_UNKNOWN; + cluster->aabb = aabb; + + return true; +} + +bool r3d_render_cluster_end(void) +{ + if (R3D_MOD_RENDER.activeCluster < 0) return false; + R3D_MOD_RENDER.activeCluster = -1; + return true; +} + +void r3d_render_group_push(const r3d_render_group_t* group) +{ + if (R3D_MOD_RENDER.numGroups >= R3D_MOD_RENDER.capacity) { + if (!growth_arrays()) { + R3D_TRACELOG(LOG_FATAL, "Bad alloc on render group push"); + return; + } + } + + int groupIndex = R3D_MOD_RENDER.numGroups++; + + R3D_MOD_RENDER.groupVisibility[groupIndex] = (r3d_render_group_visibility_t) { + .clusterIndex = R3D_MOD_RENDER.activeCluster, + .visible = R3D_RENDER_VISBILITY_UNKNOWN + }; + + R3D_MOD_RENDER.callIndices[groupIndex] = (r3d_render_indices_t) {0}; + R3D_MOD_RENDER.groups[groupIndex] = *group; +} + +void r3d_render_call_push(const r3d_render_call_t* call) +{ + if (R3D_MOD_RENDER.numCalls >= R3D_MOD_RENDER.capacity) { + if (!growth_arrays()) { + R3D_TRACELOG(LOG_FATAL, "Bad alloc on draw call push"); + return; + } + } + + // Get group and their call indices + int groupIndex = get_last_group_index(); + r3d_render_group_t* group = &R3D_MOD_RENDER.groups[groupIndex]; + r3d_render_indices_t* indices = &R3D_MOD_RENDER.callIndices[groupIndex]; + + // Get call index and set call group indices + int callIndex = R3D_MOD_RENDER.numCalls++; + if (indices->numCall == 0) { + indices->firstCall = callIndex; + } + ++indices->numCall; + + // Set group index for this draw call + R3D_MOD_RENDER.groupIndices[callIndex] = groupIndex; + + // Determine the draw call list + r3d_render_list_enum_t list = R3D_RENDER_LIST_OPAQUE; + if (r3d_render_is_decal(call)) list = R3D_RENDER_LIST_DECAL; + else if (!r3d_render_is_opaque(call)) list = R3D_RENDER_LIST_TRANSPARENT; + if (r3d_render_has_instances(group)) list += R3D_RENDER_LIST_NON_INST_COUNT; + + // Update internal flags + if (r3d_render_is_deferred(call)) R3D_MOD_RENDER.hasDeferred = true; + else if (r3d_render_is_prepass(call)) R3D_MOD_RENDER.hasPrepass = true; + else if (r3d_render_is_forward(call)) R3D_MOD_RENDER.hasForward = true; + + // Push the draw call and its index to the list + R3D_MOD_RENDER.calls[callIndex] = *call; + int listIndex = R3D_MOD_RENDER.list[list].numCalls++; + R3D_MOD_RENDER.list[list].calls[listIndex] = callIndex; +} + +r3d_render_group_t* r3d_render_get_call_group(const r3d_render_call_t* call) +{ + int callIndex = get_draw_call_index(call); + int groupIndex = R3D_MOD_RENDER.groupIndices[callIndex]; + r3d_render_group_t* group = &R3D_MOD_RENDER.groups[groupIndex]; + + return group; +} + +void r3d_render_cull_groups(const R3D_Frustum* frustum) +{ + // Reset visibility states if groups were already culled in a previous pass + if (R3D_MOD_RENDER.groupCulled) { + for (int i = 0; i < R3D_MOD_RENDER.numGroups; i++) { + R3D_MOD_RENDER.groupVisibility[i].visible = R3D_RENDER_VISBILITY_UNKNOWN; + } + for (int i = 0; i < R3D_MOD_RENDER.numClusters; i++) { + R3D_MOD_RENDER.clusters[i].visible = R3D_RENDER_VISBILITY_UNKNOWN; + } + } + R3D_MOD_RENDER.groupCulled = true; + + // Perform frustum culling for each group + for (int i = 0; i < R3D_MOD_RENDER.numGroups; i++) + { + r3d_render_group_visibility_t* visibility = &R3D_MOD_RENDER.groupVisibility[i]; + const r3d_render_group_t* group = &R3D_MOD_RENDER.groups[i]; + + // Branch 1: Group belongs to a cluster + if (visibility->clusterIndex >= 0) { + r3d_render_cluster_t* cluster = &R3D_MOD_RENDER.clusters[visibility->clusterIndex]; + + // Test cluster once (shared by multiple groups) + if (cluster->visible == R3D_RENDER_VISBILITY_UNKNOWN) { + cluster->visible = is_aabb_visible(frustum, cluster->aabb); + } + + // If cluster is visible, test the group + if (cluster->visible == R3D_RENDER_VISBILITY_TRUE) { + // For instanced: trust cluster visibility + // For others: test group AABB individually + if (r3d_render_has_instances(group)) visibility->visible = R3D_RENDER_VISBILITY_TRUE; + else visibility->visible = is_obb_visible(frustum, group->obb); + } + else { + visibility->visible = R3D_RENDER_VISBILITY_FALSE; + } + } + // Branch 2: Group without cluster + else { + // For instanced: always visible + // For others: test group AABB + if (r3d_render_has_instances(group)) visibility->visible = R3D_RENDER_VISBILITY_TRUE; + else visibility->visible = is_obb_visible(frustum, group->obb); + } + } +} + +bool r3d_render_call_is_visible(const r3d_render_call_t* call, const R3D_Frustum* frustum) +{ + // Get the draw call's parent group and its visibility state + int callIndex = get_draw_call_index(call); + int groupIndex = R3D_MOD_RENDER.groupIndices[callIndex]; + const r3d_render_group_t* group = &R3D_MOD_RENDER.groups[groupIndex]; + r3d_render_visibility_enum_t groupVisibility = R3D_MOD_RENDER.groupVisibility[groupIndex].visible; + + // If the group was already culled, reject immediately + if (groupVisibility == R3D_RENDER_VISBILITY_FALSE) { + return false; + } + + // If the group passed culling, check if we can skip per-call testing + if (groupVisibility == R3D_RENDER_VISBILITY_TRUE) { + // Single-call groups were already tested at the group level + if (R3D_MOD_RENDER.callIndices[groupIndex].numCall == 1) { + return true; + } + // Instanced/skinned groups: trust the group-level test + if (r3d_render_has_instances(group) || group->skinTexture > 0) { + return true; + } + // Multi-call group: fall through to individual call testing + } + // If the group hasn't been tested yet, check instanced/skinned groups now + else if (groupVisibility == R3D_RENDER_VISBILITY_UNKNOWN) { + if (r3d_render_has_instances(group) || group->skinTexture > 0) { + return is_obb_visible(frustum, group->obb); + } + // Regular multi-call group: fall through to individual call testing + } + + // Test this specific draw call against the frustum + return is_draw_call_visible(frustum, call, group->transform); +} + +void r3d_render_sort_list(r3d_render_list_enum_t list, Vector3 viewPosition, r3d_render_sort_enum_t mode) +{ + G_sortViewPosition = viewPosition; + + int (*compareFunc)(const void *a, const void *b) = NULL; + r3d_render_list_t* drawList = &R3D_MOD_RENDER.list[list]; + + switch (mode) { + case R3D_RENDER_SORT_FRONT_TO_BACK: + compareFunc = compare_front_to_back; + sort_fill_cache_front_to_back(list); + break; + case R3D_RENDER_SORT_BACK_TO_FRONT: + compareFunc = compare_back_to_front; + sort_fill_cache_back_to_front(list); + break; + case R3D_RENDER_SORT_MATERIAL_ONLY: + compareFunc = compare_materials_only; + sort_fill_cache_by_material(list); + break; + } + + qsort( + drawList->calls, + drawList->numCalls, + sizeof(*drawList->calls), + compareFunc + ); +} + +void r3d_render_prepare_drawing(void) +{ + glBindVertexArray(R3D_MOD_RENDER.globalVao); +} + +void r3d_render_draw(const r3d_render_call_t* call) +{ + GLenum primitive; + r3d_render_range_t vertexRange; + r3d_render_range_t indexRange; + + get_draw_call_info(call, &primitive, &vertexRange, &indexRange); + + if (indexRange.count == 0) { + glDrawArrays(primitive, vertexRange.offset, vertexRange.count); + } + else { + glDrawElementsBaseVertex( + primitive, + indexRange.count, + GL_UNSIGNED_INT, + (void*)(indexRange.offset * sizeof(GLuint)), + vertexRange.offset + ); + } +} + +void r3d_render_draw_instanced(const r3d_render_call_t* call) +{ + GLenum primitive; + r3d_render_range_t vertexRange; + r3d_render_range_t indexRange; + + get_draw_call_info(call, &primitive, &vertexRange, &indexRange); + + const r3d_render_group_t* group = r3d_render_get_call_group(call); + + enable_instances(group->instances.buffers, group->instances.layout, group->instanceOffset); + + if (indexRange.count == 0) { + glDrawArraysInstanced(primitive, vertexRange.offset, vertexRange.count, group->instanceCount); + } + else { + glDrawElementsInstancedBaseVertex( + primitive, + indexRange.count, + GL_UNSIGNED_INT, + (void*)(indexRange.offset * sizeof(GLuint)), + group->instanceCount, + vertexRange.offset + ); + } + + // Instance attributes (locations 10-14) are intentionally left enabled after + // the draw. This is safe as long as non-instanced vertex shaders + // never read those locations. + + //disable_instances(group->instances.layout.flags); +} + +void r3d_render_draw_shape(r3d_render_shape_enum_t shape) +{ + r3d_render_shape_t* s = &R3D_MOD_RENDER.shapes[shape]; + if (s->vertices.count == 0) { + SHAPE_LOADERS[shape](s); + glBindVertexArray(R3D_MOD_RENDER.globalVao); + } + + if (s->elements.count == 0) { + glDrawArrays(GL_TRIANGLES, s->vertices.offset, s->vertices.count); + } + else { + glDrawElementsBaseVertex( + GL_TRIANGLES, + s->elements.count, + GL_UNSIGNED_INT, + (void*)(s->elements.offset * sizeof(GLuint)), + s->vertices.offset + ); + } +} + +``` + +`src/modules/r3d_render.h`: + +```h +/* r3d_render.h -- Internal R3D render module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_MODULE_RENDER_H +#define R3D_MODULE_RENDER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +// ======================================== +// HELPER MACROS +// ======================================== + +/* + * A set of all lists that can be rendered in probe captures. + * May require check per draw call depending on the context. + */ +#define R3D_RENDER_PACKLIST_PROBE \ + R3D_RENDER_LIST_OPAQUE_INST, \ + R3D_RENDER_LIST_OPAQUE, \ + R3D_RENDER_LIST_TRANSPARENT_INST, \ + R3D_RENDER_LIST_TRANSPARENT + +/* + * A set of all lists that can be rendered in shadow maps. + * May require check per draw call depending on the context. + */ +#define R3D_RENDER_PACKLIST_SHADOW \ + R3D_RENDER_LIST_OPAQUE_INST, \ + R3D_RENDER_LIST_TRANSPARENT_INST, \ + R3D_RENDER_LIST_OPAQUE, \ + R3D_RENDER_LIST_TRANSPARENT + +/* + * Iterate over multiple render lists in the order specified by the variadic arguments, + * yielding a pointer to each r3d_render_call_t. + * + * The optional 'cond' expression filters calls before culling (use `true` if unused). + * The optional 'frustum' pointer enables frustum culling; pass NULL to disable it. + * + * Intended for internal rendering passes only. + */ +#define R3D_RENDER_FOR_EACH(call, cond, frustum, ...) \ + for (int _lists[] = {__VA_ARGS__}, _list_idx = 0, _i = 0, _keep = 1; \ + _list_idx < (int)(sizeof(_lists)/sizeof(_lists[0])); \ + (_i >= R3D_MOD_RENDER.list[_lists[_list_idx]].numCalls ? \ + (_list_idx++, _i = 0) : 0)) \ + for (; _list_idx < (int)(sizeof(_lists)/sizeof(_lists[0])) && \ + _i < R3D_MOD_RENDER.list[_lists[_list_idx]].numCalls; \ + _i++, _keep = 1) \ + for (const r3d_render_call_t* call = \ + &R3D_MOD_RENDER.calls[R3D_MOD_RENDER.list[_lists[_list_idx]].calls[_i]]; \ + _keep && (cond) && (!(frustum) || r3d_render_call_is_visible(call, (frustum))); \ + _keep = 0) + +/* + * glDrawArrays call on the 3-vertcies dummy VAO for screen-space rendering. + */ +#define R3D_RENDER_SCREEN() do { \ + r3d_render_draw_shape(R3D_RENDER_SHAPE_DUMMY); \ +} while(0) + +/* + * Draw a centered quad of dimensions 1.0 + */ +#define R3D_RENDER_QUAD() do { \ + r3d_render_draw_shape(R3D_RENDER_SHAPE_QUAD); \ +} while(0) + +/* + * Draw a centered cube with dimensions 1.0 + */ +#define R3D_RENDER_CUBE() do { \ + r3d_render_draw_shape(R3D_RENDER_SHAPE_CUBE); \ +} while(0) + +// ======================================== +// DRAW MODULE ENUMS +// ======================================== + +/* + * Enum listing all built-in shapes that can be rendered. + */ +typedef enum { + R3D_RENDER_SHAPE_DUMMY, //< Calls glDrawArrays with 3 vertices without an attached VBO/EBO + R3D_RENDER_SHAPE_QUAD, //< Draws a quad with dimensions 1.0 (-0.5 .. +0.5) + R3D_RENDER_SHAPE_CUBE, //< Draws a cube with dimensions 1.0 (-0.5 .. +0.5) + R3D_RENDER_SHAPE_COUNT +} r3d_render_shape_enum_t; + +/* + * Defines the type of a draw call, used for tagged-union. + */ +typedef enum { + R3D_RENDER_CALL_MESH, + R3D_RENDER_CALL_DECAL +} r3d_render_call_enum_t; + +/* + * Visibility state for a group or cluster. + * Used by culling passes to indicate whether drawing is required. + */ +typedef enum { + R3D_RENDER_VISBILITY_UNKNOWN = -1, //< Visibility has not been evaluated yet + R3D_RENDER_VISBILITY_FALSE = 0, //< Determined to be not visible (culled) + R3D_RENDER_VISBILITY_TRUE = 1, //< Determined to be visible +} r3d_render_visibility_enum_t; + +/* + * Sorting modes applied to render lists. + * Used to control draw order for depth testing efficiency or visual correctness. + */ +typedef enum { + R3D_RENDER_SORT_FRONT_TO_BACK, //< Sort first by materials, then front to back, used for simple opaque geometry + R3D_RENDER_SORT_BACK_TO_FRONT, //< Sort back to front only, used for simple transparent geometry + R3D_RENDER_SORT_MATERIAL_ONLY //< Sort only by materials, used for instanced and decals +} r3d_render_sort_enum_t; + +/* + * Enumeration of internal render lists. + * Lists are separated by rendering path and instancing mode. + */ +typedef enum { + + R3D_RENDER_LIST_OPAQUE, + R3D_RENDER_LIST_TRANSPARENT, + R3D_RENDER_LIST_DECAL, + + R3D_RENDER_LIST_NON_INST_COUNT, + + R3D_RENDER_LIST_OPAQUE_INST = R3D_RENDER_LIST_NON_INST_COUNT, + R3D_RENDER_LIST_TRANSPARENT_INST, + R3D_RENDER_LIST_DECAL_INST, + + R3D_RENDER_LIST_COUNT + +} r3d_render_list_enum_t; + +// ======================================== +// DRAW MODULE STRUCTS +// ======================================== + +/* + * Cached state of the per-instance vertex attribute bindings. + * Compared before each instanced draw call to avoid redundant + * GL attribute reconfiguration when consecutive draw calls share + * the same instance group. + */ +typedef struct r3d_render_instance_state_t { + GLuint buffers[R3D_INSTANCE_ATTRIBUTE_COUNT]; + R3D_InstanceLayout layout; + int offset; +} r3d_render_instance_state_t; + +/* + * Interval [offset, offset+count) into the global VBO or EBO. + */ +typedef struct { + int offset; + int count; +} r3d_render_range_t; + +/* + * Internal structure for storing built-in shapes. + */ +typedef struct { + r3d_render_range_t vertices; + r3d_render_range_t elements; +} r3d_render_shape_t; + +/* + * Cluster that may contain multiple render groups. + * Stores bounds and the evaluated visibility state. + */ +typedef struct { + BoundingBox aabb; + r3d_render_visibility_enum_t visible; +} r3d_render_cluster_t; + +/* + * Visibility metadata for a render group. + * Holds its cluster index (if assigned) and its own visibility state. + * Note: a group is effectively visible only when its cluster is visible. + */ +typedef struct { + int clusterIndex; + r3d_render_visibility_enum_t visible; +} r3d_render_group_visibility_t; + +/* + * Mapping structure linking render groups to their associated draw calls. + * Stores the range of draw calls belonging to a specific group. + * One entry is stored per render group. + */ +typedef struct { + int firstCall; //< Index of the first draw call in this group + int numCall; //< Number of draw calls in this group +} r3d_render_indices_t; + +/* + * Draw group containing shared state for multiple draw calls. + * All draw calls pushed after a group inherit its transform, skeleton, and instancing data. + */ +typedef struct { + Matrix transform; //< Model transformation matrix + R3D_OrientedBox obb; //< Oriented bounding box of all drawables contained in the group + GLuint skinTexture; //< Texture that contains the bone matrices (can be 0 for non-skinned) + R3D_InstanceBuffer instances; //< Instance buffer to use + int instanceOffset; //< Offset to the first instance + int instanceCount; //< Number of instances +} r3d_render_group_t; + +/* + * Represents a draw call for a mesh in `r3d_render_call_t` + */ +typedef struct { + R3D_Material material; + R3D_Mesh instance; +} r3d_render_call_mesh_t; + +/* + * Represents a draw call for a decal in `r3d_render_call_t` + */ +typedef struct { + R3D_Decal instance; +} r3d_render_call_decal_t; + +/* + * Internal representation of a single draw call. + * Contains all data required to issue a draw, like a mesh or decal. + * Transform and animation data are stored in the parent render group. + */ +typedef struct { + r3d_render_call_enum_t type; + union { + r3d_render_call_mesh_t mesh; + r3d_render_call_decal_t decal; + }; +} r3d_render_call_t; + +/* + * A render list stores indices into the global draw call array. + */ +typedef struct { + int* calls; //< Indices of draw calls + int numCalls; //< Number of active entries +} r3d_render_list_t; + +/* + * Sort key derived from a rendering state. + * Fields are declared in priority order: the first differing field + * encountered during comparison determines the sort result. + */ +typedef struct { + int32_t priority; ///< User-defined render order (signed, lower = first) + uintptr_t shader; ///< Shader program pointer + uint32_t shading; ///< Shading mode (lit/unlit) + uint32_t albedo; ///< Albedo texture ID + uint32_t normal; ///< Normal map texture ID + uint32_t orm; ///< ORM texture ID + uint32_t emission; ///< Emission texture ID + uint32_t stencil; ///< Hashed stencil state + uint32_t depth; ///< Hashed depth state + uint8_t blend; ///< Blend mode + uint8_t cull; ///< Cull mode + uint8_t transparency; ///< Transparency mode + uint8_t billboard; ///< Billboard mode +} r3d_render_sort_state_t; + +/* + * Data stored per draw call in the sort cache. + */ +typedef struct { + r3d_render_sort_state_t state; + float distance; +} r3d_render_sort_t; + +// ======================================== +// MODULE STATE +// ======================================== + +/* + * Global internal state of the render module. + * Owns the draw call storage and per-pass render lists. + */ +extern struct r3d_mod_render { + + GLuint globalVao; //< Single VAO shared by all mesh and shape draw calls + GLuint globalVbo; //< Global vertex buffer holding all mesh and shape vertices + GLuint globalEbo; //< Global index buffer holding all mesh and shape indices + int globalVertexCapacity; //< Number of vertex slots allocated in the VBO + int globalElementCapacity; //< Number of index slots allocated in the EBO + int globalVertexCount; //< High-water mark: first never-allocated vertex offset + int globalElementCount; //< High-water mark: first never-allocated index offset + + r3d_render_range_t* freeVertices; //< Free list of released vertex ranges available for reuse + r3d_render_range_t* freeElements; //< Free list of released index ranges available for reuse + int numFreeVertices; //< Number of entries in the vertex free list + int numFreeElements; //< Number of entries in the element free list + int freeVertexCapacity; //< Allocated capacity of the vertex free list array + int freeElementCapacity; //< Allocated capacity of the element free list array + + r3d_render_instance_state_t instanceState; //< Cached instance binding configuration + r3d_render_shape_t shapes[R3D_RENDER_SHAPE_COUNT]; //< Array of built-in shapes buffers + + r3d_render_cluster_t* clusters; //< Array of render clusters + int activeCluster; //< Index of the active cluster for new render groups (-1 if no active clusters) + + r3d_render_group_visibility_t* groupVisibility; //< Array containing visibility info for each render group (generated during group culling) + r3d_render_indices_t* callIndices; //< Array of draw call index ranges for each render group (automatically managed) + r3d_render_group_t* groups; //< Array of render groups (shared data across draw calls) + + r3d_render_list_t list[R3D_RENDER_LIST_COUNT]; //< Lists of draw call indices organized by rendering category + r3d_render_sort_t* sortCache; //< Draw call sorting data cache array + r3d_render_call_t* calls; //< Array of draw calls + int* groupIndices; //< Array of group indices for each draw call (automatically managed) + + int numClusters; //< Number of active render clusters + int numGroups; //< Number of active render groups + int numCalls; //< Number of active draw calls + int capacity; //< Allocated capacity for all arrays (in number of elements) + + bool groupCulled; //< Indicates if groups already culled (controls visibility reset) + bool hasDeferred; //< If there are any deferred calls (lit opaque) + bool hasPrepass; //< If there are any prepass calls (lit opaque / lit transparent) + bool hasForward; //< If there are any forward calls (unlit opaque / transparent) + +} R3D_MOD_RENDER; + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +/* + * Module initialization function. + * Called once during `R3D_Init()` + */ +bool r3d_render_init(void); + +/* + * Module deinitialization function. + * Called once during `R3D_Close()` + */ +void r3d_render_quit(void); + +/* + * Allocates 'count' vertex slots from the global VBO; returns their offset via 'outOffset'. + * Reuses a free block when possible, otherwise extends the buffer (growing it if needed). + */ +bool r3d_render_alloc_vertices(int count, int* outOffset); + +/* + * Allocates 'count' index slots from the global EBO; returns their offset via 'outOffset'. + * Reuses a free block when possible, otherwise extends the buffer (growing it if needed). + */ +bool r3d_render_alloc_elements(int count, int* outOffset); + +/* + * Resizes an existing vertex allocation to 'newCount' slots, updating '*offset' and '*count'. + * Shrinking always succeeds in-place. Growing tries to extend in-place before relocating; + * if relocated and 'keepData' is true, existing GPU data is copied to the new location. + */ +bool r3d_render_realloc_vertices(int* offset, int* count, int newCount, bool keepData); + +/* + * Resizes an existing element allocation to 'newCount' slots, updating '*offset' and '*count'. + * Shrinking always succeeds in-place. Growing tries to extend in-place before relocating; + * if relocated and 'keepData' is true, existing GPU data is copied to the new location. + */ +bool r3d_render_realloc_elements(int* offset, int* count, int newCount, bool keepData); + +/* + * Returns 'count' vertex slots starting at 'offset' to the free list. + * Adjacent free blocks are merged immediately to limit fragmentation. + */ +void r3d_render_free_vertices(int offset, int count); + +/* + * Returns 'count' index slots starting at 'offset' to the free list. + * Adjacent free blocks are merged immediately to limit fragmentation. + */ +void r3d_render_free_elements(int offset, int count); + +/* + * Uploads 'count' vertices to the global VBO at 'offset'. Does not allocate; caller must + * hold a valid allocation covering [offset, offset+count). + */ +void r3d_render_upload_vertices(int offset, const R3D_Vertex* verts, int count); + +/* + * Uploads 'count' indices to the global EBO at 'offset'. Does not allocate; caller must + * hold a valid allocation covering [offset, offset+count). + */ +void r3d_render_upload_elements(int offset, const GLuint* indices, int count); + +/* + * Clear all render lists and reset the draw call buffer for the next frame. + */ +void r3d_render_clear(void); + +/* + * Begins a new render cluster with the given bounds. + * All subsequent render group pushes will belong to this cluster. + * Returns false if a cluster is already active or allocation fails. + */ +bool r3d_render_cluster_begin(BoundingBox aabb); + +/* + * Ends the currently active render cluster. + * Returns false if no cluster is currently active. + */ +bool r3d_render_cluster_end(void); + +/* + * Push a new render group. All subsequent draw calls will belong to this group + * until a new group is pushed. + */ +void r3d_render_group_push(const r3d_render_group_t* group); + +/* + * Push a new draw call to the appropriate render list. + * The draw call data is copied internally. + * Inherits the group previously pushed. + */ +void r3d_render_call_push(const r3d_render_call_t* call); + +/* + * Retrieve the render group associated with a given draw call. + * Returns a pointer to the parent group containing shared transform and instancing data. + */ +r3d_render_group_t* r3d_render_get_call_group(const r3d_render_call_t* call); + +/* + * Builds the list of groups that are visible inside the given frustum. + * Must be called before issuing visibility tests with the same frustum. + */ +void r3d_render_cull_groups(const R3D_Frustum* frustum); + +/* + * Returns true if the draw call is visible within the given frustum. + * Uses both per-call culling and the results produced by `r3d_render_cull_groups()` + * Make sure to compute visible groups with the same frustum before calling this function. + */ +bool r3d_render_call_is_visible(const r3d_render_call_t* call, const R3D_Frustum* frustum); + +/* + * Sort a render list according to the given mode and camera position. + */ +void r3d_render_sort_list(r3d_render_list_enum_t list, Vector3 viewPosition, r3d_render_sort_enum_t mode); + +/* + * Binds the global VAO, making it active for all subsequent draw calls. + * Must be called once before any r3d_render_draw* calls in a rendering pass. + */ +void r3d_render_prepare_drawing(void); + +/* + * Issue a non-instanced draw call. + */ +void r3d_render_draw(const r3d_render_call_t* call); + +/* + * Issue an instanced draw call. + * Instance data is bound internally. + */ +void r3d_render_draw_instanced(const r3d_render_call_t* call); + +/* + * Bind, draws the shape, and unbind the VAO of the shape. + */ +void r3d_render_draw_shape(r3d_render_shape_enum_t shape); + +// ---------------------------------------- +// INLINE QUERIES +// ---------------------------------------- + +/* + * Check whether a render group has valid instancing data. + * Returns true if the draw call contains a non-null instance transform array + * and a positive instance count. + */ +static inline bool r3d_render_has_instances(const r3d_render_group_t* group) +{ + return (group->instances.capacity > 0) && (group->instanceCount > 0); +} + +/* + * Check whether there are any deferred draw calls queued for the current frame. + * Includes both instanced and non-instanced variants. + */ +static inline bool r3d_render_has_deferred(void) +{ + return R3D_MOD_RENDER.hasDeferred; +} + +/* + * Check whether there are any prepass draw calls queued for the current frame. + * Includes both instanced and non-instanced variants. + */ +static inline bool r3d_render_has_prepass(void) +{ + return R3D_MOD_RENDER.hasPrepass; +} + +/* + * Check whether there are any forward draw calls queued for the current frame. + * Includes both instanced and non-instanced variants. + */ +static inline bool r3d_render_has_forward(void) +{ + return R3D_MOD_RENDER.hasForward; +} + +/* + * Check whether there are any decal draw calls queued for the current frame. + * Includes both instanced and non-instanced variants. + */ +static inline bool r3d_render_has_decal(void) +{ + return + (R3D_MOD_RENDER.list[R3D_RENDER_LIST_DECAL].numCalls > 0) || + (R3D_MOD_RENDER.list[R3D_RENDER_LIST_DECAL_INST].numCalls > 0); +} + +/* + * Indicates whether a draw call corresponds to a decal. + */ +static inline bool r3d_render_is_decal(const r3d_render_call_t* call) +{ + return call->type == R3D_RENDER_CALL_DECAL; +} + +/* + * Indicates whether a draw call corresponds to an object that is only rendered in deferred. + * Always check if an object is prepassed before checking if it is deferred. + */ +static inline bool r3d_render_is_deferred(const r3d_render_call_t* call) +{ + if (call->type != R3D_RENDER_CALL_MESH) return false; + if (call->mesh.material.unlit) return false; + + if (call->mesh.material.blendMode != R3D_BLEND_MIX) { + return false; + } + + return call->mesh.material.transparencyMode == R3D_TRANSPARENCY_DISABLED; +} + +/* + * Indicates whether a draw call corresponds to an opaque object (lit or unlit) + */ +static inline bool r3d_render_is_opaque(const r3d_render_call_t* call) +{ + if (call->type != R3D_RENDER_CALL_MESH) return false; + + if (call->mesh.material.blendMode != R3D_BLEND_MIX) { + return false; + } + + return call->mesh.material.transparencyMode == R3D_TRANSPARENCY_DISABLED; +} + +/* + * Indicates whether a draw call corresponds to an illuminated object rendered in multiple passes (deferred / forward) + */ +static inline bool r3d_render_is_prepass(const r3d_render_call_t* call) +{ + if (call->type != R3D_RENDER_CALL_MESH) return false; + if (call->mesh.material.unlit) return false; + + return call->mesh.material.transparencyMode == R3D_TRANSPARENCY_PREPASS; +} + +/* + * Indicates whether a draw call corresponds to an object rendered only in forward (unlit opaque / transparent) + * Always check if an object is prepassed before checking if it is forwarded. + */ +static inline bool r3d_render_is_forward(const r3d_render_call_t* call) +{ + if (call->type != R3D_RENDER_CALL_MESH) return false; + if (call->mesh.material.unlit) return true; + + if (call->mesh.material.blendMode != R3D_BLEND_MIX) { + return true; + } + + return call->mesh.material.transparencyMode == R3D_TRANSPARENCY_ALPHA; +} + +/* + * Indicates whether a draw call corresponds to an object that should be rendered in a shadow map. + */ +static inline bool r3d_render_should_cast_shadow(const r3d_render_call_t* call) +{ + return (call->mesh.material.transparencyMode == R3D_TRANSPARENCY_DISABLED) || + (call->mesh.material.transparencyMode == R3D_TRANSPARENCY_PREPASS); +} + +#endif // R3D_MODULE_RENDER_H + +``` + +`src/modules/r3d_shader.c`: + +```c +/* r3d_shader.c -- Internal R3D shader module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_shader.h" +#include +#include +#include +#include +#include +#include + +#include "../common/r3d_helper.h" + +// ======================================== +// SHADER CODE INCLUDES +// ======================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ======================================== +// MODULE STATE +// ======================================== + +struct r3d_mod_shader R3D_MOD_SHADER; + +// ======================================== +// INTERNAL MACROS +// ======================================== + +#define DECL_SHADER(type, category, shader_name) \ + type* shader_name = &R3D_MOD_SHADER.category.shader_name + +#define DECL_SHADER_INDEXED(type, category, shader_name, index) \ + type* shader_name = &R3D_MOD_SHADER.category.shader_name[index] + +#define DECL_SHADER_SELECT(type, category, shader_name, custom) \ + type* shader_name = ((custom) == NULL) \ + ? &R3D_MOD_SHADER.category.shader_name \ + : &(custom)->program->category.shader_name + +#define LOAD_SHADER(shader_name, vsCode, fsCode) do { \ + shader_name->id = load_shader(vsCode, fsCode); \ + if (shader_name->id == 0) { \ + R3D_TRACELOG(LOG_ERROR, "Failed to load shader '" #shader_name "'"); \ + return false; \ + } \ +} while(0) + +#define USE_SHADER(shader_name) do { \ + glUseProgram(shader_name->id); \ +} while(0) \ + +#define GET_LOCATION(shader_name, uniform) do { \ + shader_name->uniform.loc = glGetUniformLocation( \ + shader_name->id, #uniform \ + ); \ +} while(0) + +#define SET_SAMPLER(shader_name, uniform, value) do { \ + GLint loc = glGetUniformLocation(shader_name->id, #uniform); \ + glUniform1i(loc, (int)(value)); \ + shader_name->uniform.slot = (int)(value); \ +} while(0) + +#define SET_UNIFORM_BUFFER(shader_name, uniform, slot) do { \ + GLuint idx = glGetUniformBlockIndex(shader_name->id, #uniform); \ + glUniformBlockBinding(shader_name->id, idx, slot); \ +} while(0) \ + +#define UNLOAD_SHADER(shader_name) do { \ + if (R3D_MOD_SHADER.shader_name.id != 0) { \ + glDeleteProgram(R3D_MOD_SHADER.shader_name.id); \ + } \ +} while(0) + +#define UNLOAD_SHADERS(shader_name) do { \ + for (int i = 0; i < ARRAY_SIZE(R3D_MOD_SHADER.shader_name); i++) { \ + if (R3D_MOD_SHADER.shader_name[i].id != 0) { \ + glDeleteProgram(R3D_MOD_SHADER.shader_name[i].id); \ + } \ + } \ +} while(0) + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +/** + * Inject content into a string at a marker position + * Returns newly allocated string with injected content + * Or NULL on failure (caller must free the returned string) + * source: Source string to modify + * content: Content to inject + * marker: String marker to find in source + * mode: Injection mode: <0 = before marker, 0 = replace marker, >0 = after marker + */ +static char* inject_content(const char* source, const char* content, const char* marker, int mode); + +/** + * Inject the provided list of definitions into the code + * Returns newly allocated string with injected content + * Or NULL on failure (caller must free the returned string) + */ +static char* inject_defines(const char* code, const char* defines[], int count); + +/* + * Modifies each input stage by injecting user code if it contains the corresponding stages. + */ +static void inject_user_code(char** vsCode, char** fsCode, const char* userCode); + +/** + * Initializes the sampler locations for the given shader program. + */ +static void set_custom_samplers(GLuint id, r3d_shader_custom_t* custom); + +// ======================================== +// SHADER COMPLING / LINKING FUNCTIONS +// ======================================== + +static GLuint compile_shader(const char* source, GLenum shaderType) +{ + GLuint shader = glCreateShader(shaderType); + if (shader == 0) { + R3D_TRACELOG(LOG_ERROR, "Failed to create shader object"); + return 0; + } + + glShaderSource(shader, 1, &source, NULL); + glCompileShader(shader); + + int success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + char infoLog[512]; + glGetShaderInfoLog(shader, 512, NULL, infoLog); + const char* type_str = (shaderType == GL_VERTEX_SHADER) ? "vertex" : "fragment"; + R3D_TRACELOG(LOG_ERROR, "%s shader compilation failed: %s", type_str, infoLog); + glDeleteShader(shader); + return 0; + } + + return shader; +} + +static GLuint link_shader(GLuint vertShader, GLuint fragShader) +{ + GLuint program = glCreateProgram(); + if (program == 0) { + R3D_TRACELOG(LOG_ERROR, "Failed to create shader program"); + return 0; + } + + glAttachShader(program, vertShader); + glAttachShader(program, fragShader); + glLinkProgram(program); + + int success; + glGetProgramiv(program, GL_LINK_STATUS, &success); + if (!success) { + char infoLog[512]; + glGetProgramInfoLog(program, 512, NULL, infoLog); + R3D_TRACELOG(LOG_ERROR, "Shader program linking failed: %s", infoLog); + glDeleteProgram(program); + return 0; + } + + glDetachShader(program, vertShader); + glDetachShader(program, fragShader); + + return program; +} + +static GLuint load_shader(const char* vsCode, const char* fsCode) +{ + GLuint vs = compile_shader(vsCode, GL_VERTEX_SHADER); + if (vs == 0) return 0; + + GLuint fs = compile_shader(fsCode, GL_FRAGMENT_SHADER); + if (fs == 0) { + glDeleteShader(vs); + return 0; + } + + GLuint program = link_shader(vs, fs); + + glDeleteShader(vs); + glDeleteShader(fs); + + return program; +} + +// ======================================== +// SHADER LOADING FUNCTIONS +// ======================================== + +bool r3d_shader_load_prepare_denoiser_atrous(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_denoiser_atrous_t, prepare, denoiserAtrous); + LOAD_SHADER(denoiserAtrous, SCREEN_VERT, DENOISER_ATROUS_FRAG); + + SET_UNIFORM_BUFFER(denoiserAtrous, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + + GET_LOCATION(denoiserAtrous, uNormalSharpness); + GET_LOCATION(denoiserAtrous, uDepthSharpness); + GET_LOCATION(denoiserAtrous, uInvStepWidth2); + GET_LOCATION(denoiserAtrous, uStepWidth); + + USE_SHADER(denoiserAtrous); + SET_SAMPLER(denoiserAtrous, uSourceTex, R3D_SHADER_SAMPLER_SOURCE_2D_0); + SET_SAMPLER(denoiserAtrous, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + SET_SAMPLER(denoiserAtrous, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + + return true; +} + +bool r3d_shader_load_prepare_denoiser_sparse(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_denoiser_sparse_t, prepare, denoiserSparse); + LOAD_SHADER(denoiserSparse, SCREEN_VERT, DENOISER_SPARSE_FRAG); + + SET_UNIFORM_BUFFER(denoiserSparse, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + + GET_LOCATION(denoiserSparse, uNormalSharpness); + GET_LOCATION(denoiserSparse, uDepthSharpness); + GET_LOCATION(denoiserSparse, uInvBlurRadius2); + GET_LOCATION(denoiserSparse, uBlurRadius); + + USE_SHADER(denoiserSparse); + SET_SAMPLER(denoiserSparse, uSourceTex, R3D_SHADER_SAMPLER_SOURCE_2D_0); + SET_SAMPLER(denoiserSparse, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + SET_SAMPLER(denoiserSparse, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + + return true; +} + +bool r3d_shader_load_prepare_blur_down(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_blur_down_t, prepare, blurDown); + LOAD_SHADER(blurDown, SCREEN_VERT, BLUR_DOWN_FRAG); + + GET_LOCATION(blurDown, uSourceLod); + + USE_SHADER(blurDown); + SET_SAMPLER(blurDown, uSourceTex, R3D_SHADER_SAMPLER_SOURCE_2D_0); + + return true; +} + +bool r3d_shader_load_prepare_blur_up(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_blur_up_t, prepare, blurUp); + LOAD_SHADER(blurUp, SCREEN_VERT, BLUR_UP_FRAG); + + GET_LOCATION(blurUp, uSourceLod); + + USE_SHADER(blurUp); + SET_SAMPLER(blurUp, uSourceTex, R3D_SHADER_SAMPLER_SOURCE_2D_0); + + return true; +} + +bool r3d_shader_load_prepare_depth_pyramid(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_depth_pyramid_t, prepare, depthPyramid); + LOAD_SHADER(depthPyramid, SCREEN_VERT, DEPTH_PYRAMID_FRAG); + + USE_SHADER(depthPyramid); + SET_SAMPLER(depthPyramid, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + + return true; +} + +bool r3d_shader_load_prepare_ssao_in_down(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_ssao_in_down_t, prepare, ssaoInDown); + LOAD_SHADER(ssaoInDown, SCREEN_VERT, SSAO_IN_DOWN_FRAG); + + USE_SHADER(ssaoInDown); + SET_SAMPLER(ssaoInDown, uSelectorTex, R3D_SHADER_SAMPLER_BUFFER_SELECTOR); + SET_SAMPLER(ssaoInDown, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + + return true; +} + +bool r3d_shader_load_prepare_ssao(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_ssao_t, prepare, ssao); + LOAD_SHADER(ssao, SCREEN_VERT, SSAO_FRAG); + + SET_UNIFORM_BUFFER(ssao, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + + GET_LOCATION(ssao, uSampleCount); + GET_LOCATION(ssao, uRadius); + GET_LOCATION(ssao, uBias); + GET_LOCATION(ssao, uIntensity); + GET_LOCATION(ssao, uMaxSSRadius); + + USE_SHADER(ssao); + + SET_SAMPLER(ssao, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + SET_SAMPLER(ssao, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + + return true; +} + +bool r3d_shader_load_prepare_ssil_in_down(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_ssil_in_down_t, prepare, ssilInDown); + LOAD_SHADER(ssilInDown, SCREEN_VERT, SSIL_IN_DOWN_FRAG); + + USE_SHADER(ssilInDown); + SET_SAMPLER(ssilInDown, uSelectorTex, R3D_SHADER_SAMPLER_BUFFER_SELECTOR); + SET_SAMPLER(ssilInDown, uDiffuseTex, R3D_SHADER_SAMPLER_BUFFER_DIFFUSE); + SET_SAMPLER(ssilInDown, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + + return true; +} + +bool r3d_shader_load_prepare_ssil(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_ssil_t, prepare, ssil); + LOAD_SHADER(ssil, SCREEN_VERT, SSIL_FRAG); + + SET_UNIFORM_BUFFER(ssil, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + + GET_LOCATION(ssil, uSampleCount); + GET_LOCATION(ssil, uRadius); + GET_LOCATION(ssil, uBias); + GET_LOCATION(ssil, uAoIntensity); + GET_LOCATION(ssil, uMaxSSRadius); + + USE_SHADER(ssil); + + SET_SAMPLER(ssil, uDiffuseTex, R3D_SHADER_SAMPLER_BUFFER_DIFFUSE); + SET_SAMPLER(ssil, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + SET_SAMPLER(ssil, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + + return true; +} + +bool r3d_shader_load_prepare_ssgi_in_down(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_ssgi_in_down_t, prepare, ssgiInDown); + LOAD_SHADER(ssgiInDown, SCREEN_VERT, SSGI_IN_DOWN_FRAG); + + USE_SHADER(ssgiInDown); + SET_SAMPLER(ssgiInDown, uSelectorTex, R3D_SHADER_SAMPLER_BUFFER_SELECTOR); + SET_SAMPLER(ssgiInDown, uDiffuseTex, R3D_SHADER_SAMPLER_BUFFER_DIFFUSE); + SET_SAMPLER(ssgiInDown, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + + return true; +} + +bool r3d_shader_load_prepare_ssgi(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_ssgi_t, prepare, ssgi); + LOAD_SHADER(ssgi, SCREEN_VERT, SSGI_FRAG); + + SET_UNIFORM_BUFFER(ssgi, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + + GET_LOCATION(ssgi, uSliceCount); + GET_LOCATION(ssgi, uEdgeFade); + GET_LOCATION(ssgi, uDistanceFalloff); + GET_LOCATION(ssgi, uNormalRejection); + + USE_SHADER(ssgi); + + SET_SAMPLER(ssgi, uDiffuseTex, R3D_SHADER_SAMPLER_BUFFER_DIFFUSE); + SET_SAMPLER(ssgi, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + SET_SAMPLER(ssgi, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + + return true; +} + +bool r3d_shader_load_prepare_ssr_in_down(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_ssr_in_down_t, prepare, ssrInDown); + LOAD_SHADER(ssrInDown, SCREEN_VERT, SSR_IN_DOWN_FRAG); + + USE_SHADER(ssrInDown); + SET_SAMPLER(ssrInDown, uSelectorTex, R3D_SHADER_SAMPLER_BUFFER_SELECTOR); + SET_SAMPLER(ssrInDown, uDiffuseTex, R3D_SHADER_SAMPLER_BUFFER_DIFFUSE); + SET_SAMPLER(ssrInDown, uSpecularTex, R3D_SHADER_SAMPLER_BUFFER_SPECULAR); + SET_SAMPLER(ssrInDown, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + + return true; +} + +bool r3d_shader_load_prepare_ssr(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_ssr_t, prepare, ssr); + LOAD_SHADER(ssr, SCREEN_VERT, SSR_FRAG); + + SET_UNIFORM_BUFFER(ssr, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + + GET_LOCATION(ssr, uMaxRaySteps); + GET_LOCATION(ssr, uBinarySteps); + GET_LOCATION(ssr, uStepSize); + GET_LOCATION(ssr, uThickness); + GET_LOCATION(ssr, uMaxDistance); + GET_LOCATION(ssr, uEdgeFade); + + USE_SHADER(ssr); + + SET_SAMPLER(ssr, uDiffuseTex, R3D_SHADER_SAMPLER_BUFFER_DIFFUSE); + SET_SAMPLER(ssr, uSpecularTex, R3D_SHADER_SAMPLER_BUFFER_SPECULAR); + SET_SAMPLER(ssr, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + SET_SAMPLER(ssr, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + + return true; +} + +bool r3d_shader_load_prepare_dof_coc(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_dof_coc_t, prepare, dofCoc); + LOAD_SHADER(dofCoc, SCREEN_VERT, DOF_COC_FRAG); + + GET_LOCATION(dofCoc, uFocusPoint); + GET_LOCATION(dofCoc, uFocusScale); + GET_LOCATION(dofCoc, uNearScale); + + USE_SHADER(dofCoc); + SET_SAMPLER(dofCoc, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + + return true; +} + +bool r3d_shader_load_prepare_dof_down(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_dof_down_t, prepare, dofDown); + LOAD_SHADER(dofDown, SCREEN_VERT, DOF_DOWN_FRAG); + + USE_SHADER(dofDown); + + SET_SAMPLER(dofDown, uSceneTex, R3D_SHADER_SAMPLER_BUFFER_SCENE); + SET_SAMPLER(dofDown, uCoCTex, R3D_SHADER_SAMPLER_BUFFER_DOF_COC); + + return true; +} + +bool r3d_shader_load_prepare_dof_blur(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_dof_blur_t, prepare, dofBlur); + LOAD_SHADER(dofBlur, SCREEN_VERT, DOF_BLUR_FRAG); + + GET_LOCATION(dofBlur, uMaxBlurSize); + + USE_SHADER(dofBlur); + SET_SAMPLER(dofBlur, uSceneTex, R3D_SHADER_SAMPLER_BUFFER_DOF); //< RGB: Color | A: CoC + SET_SAMPLER(dofBlur, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + + return true; +} + +bool r3d_shader_load_prepare_bloom_down(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_bloom_down_t, prepare, bloomDown); + LOAD_SHADER(bloomDown, SCREEN_VERT, BLOOM_DOWN_FRAG); + + GET_LOCATION(bloomDown, uTexelSize); + GET_LOCATION(bloomDown, uPrefilter); + GET_LOCATION(bloomDown, uDstLevel); + + USE_SHADER(bloomDown); + SET_SAMPLER(bloomDown, uTexture, R3D_SHADER_SAMPLER_BUFFER_BLOOM); + + return true; +} + +bool r3d_shader_load_prepare_bloom_up(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_bloom_up_t, prepare, bloomUp); + LOAD_SHADER(bloomUp, SCREEN_VERT, BLOOM_UP_FRAG); + + GET_LOCATION(bloomUp, uFilterRadius); + GET_LOCATION(bloomUp, uSrcLevel); + + USE_SHADER(bloomUp); + SET_SAMPLER(bloomUp, uTexture, R3D_SHADER_SAMPLER_BUFFER_BLOOM); + + return true; +} + +bool r3d_shader_load_prepare_luminance(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_luminance_t, prepare, luminance); + LOAD_SHADER(luminance, SCREEN_VERT, LUMINANCE_FRAG); + + USE_SHADER(luminance); + SET_SAMPLER(luminance, uSourceTex, R3D_SHADER_SAMPLER_BUFFER_LUMINANCE); + + return true; +} + +bool r3d_shader_load_prepare_exposure_adapt(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_exposure_adapt_t, prepare, exposureAdapt); + + LOAD_SHADER(exposureAdapt, SCREEN_VERT, EXPOSURE_ADAPT_FRAG); + + GET_LOCATION(exposureAdapt, uDeltaTime); + GET_LOCATION(exposureAdapt, uMinLogLum); + GET_LOCATION(exposureAdapt, uMaxLogLum); + GET_LOCATION(exposureAdapt, uSpeedUp); + GET_LOCATION(exposureAdapt, uSpeedDown); + GET_LOCATION(exposureAdapt, uExposureCompLog); + + USE_SHADER(exposureAdapt); + SET_SAMPLER(exposureAdapt, uMeasuredLogLumTex, R3D_SHADER_SAMPLER_BUFFER_LUMINANCE); + SET_SAMPLER(exposureAdapt, uPrevAutoExposureTex, R3D_SHADER_SAMPLER_BUFFER_EXPOSURE); + + return true; +} + +static bool load_prepare_smaa_edge_detection(r3d_shader_custom_t* custom, int index) +{ + char defQualityPreset[32] = {0}; + r3d_string_format(defQualityPreset, sizeof(defQualityPreset), "QUALITY_PRESET %i", index); + + const char* VS_DEFINES[] = {defQualityPreset}; + const char* FS_DEFINES[] = {defQualityPreset}; + + char* vsCode = inject_defines(SMAA_EDGE_DETECTION_VERT, VS_DEFINES, ARRAY_SIZE(VS_DEFINES)); + char* fsCode = inject_defines(SMAA_EDGE_DETECTION_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + DECL_SHADER_INDEXED(r3d_shader_prepare_smaa_edge_detection_t, prepare, smaaEdgeDetection, index); + LOAD_SHADER(smaaEdgeDetection, vsCode, fsCode); + + RL_FREE(vsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(smaaEdgeDetection, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + + USE_SHADER(smaaEdgeDetection); + SET_SAMPLER(smaaEdgeDetection, uSceneTex, R3D_SHADER_SAMPLER_BUFFER_SCENE); + + return true; +} + +bool r3d_shader_load_prepare_smaa_edge_detection_low(r3d_shader_custom_t* custom) +{ + return load_prepare_smaa_edge_detection(custom, R3D_ANTI_ALIASING_PRESET_LOW); +} + +bool r3d_shader_load_prepare_smaa_edge_detection_medium(r3d_shader_custom_t* custom) +{ + return load_prepare_smaa_edge_detection(custom, R3D_ANTI_ALIASING_PRESET_MEDIUM); +} + +bool r3d_shader_load_prepare_smaa_edge_detection_high(r3d_shader_custom_t* custom) +{ + return load_prepare_smaa_edge_detection(custom, R3D_ANTI_ALIASING_PRESET_HIGH); +} + +bool r3d_shader_load_prepare_smaa_edge_detection_ultra(r3d_shader_custom_t* custom) +{ + return load_prepare_smaa_edge_detection(custom, R3D_ANTI_ALIASING_PRESET_ULTRA); +} + +static bool load_prepare_smaa_blending_weights(r3d_shader_custom_t* custom, int index) +{ + char defQualityPreset[32] = {0}; + r3d_string_format(defQualityPreset, sizeof(defQualityPreset), "QUALITY_PRESET %i", index); + + const char* VS_DEFINES[] = {defQualityPreset}; + const char* FS_DEFINES[] = {defQualityPreset}; + + char* vsCode = inject_defines(SMAA_BLENDING_WEIGTHS_VERT, VS_DEFINES, ARRAY_SIZE(VS_DEFINES)); + char* fsCode = inject_defines(SMAA_BLENDING_WEIGTHS_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + DECL_SHADER_INDEXED(r3d_shader_prepare_smaa_blending_weights_t, prepare, smaaBlendingWeights, index); + LOAD_SHADER(smaaBlendingWeights, vsCode, fsCode); + + RL_FREE(vsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(smaaBlendingWeights, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + + USE_SHADER(smaaBlendingWeights); + SET_SAMPLER(smaaBlendingWeights, uEdgesTex, R3D_SHADER_SAMPLER_BUFFER_SMAA_EDGES); + SET_SAMPLER(smaaBlendingWeights, uAreaTex, R3D_SHADER_SAMPLER_SOURCE_2D_0); + SET_SAMPLER(smaaBlendingWeights, uSearchTex, R3D_SHADER_SAMPLER_SOURCE_2D_1); + + return true; +} + +bool r3d_shader_load_prepare_smaa_blending_weights_low(r3d_shader_custom_t* custom) +{ + return load_prepare_smaa_blending_weights(custom, R3D_ANTI_ALIASING_PRESET_LOW); +} + +bool r3d_shader_load_prepare_smaa_blending_weights_medium(r3d_shader_custom_t* custom) +{ + return load_prepare_smaa_blending_weights(custom, R3D_ANTI_ALIASING_PRESET_MEDIUM); +} + +bool r3d_shader_load_prepare_smaa_blending_weights_high(r3d_shader_custom_t* custom) +{ + return load_prepare_smaa_blending_weights(custom, R3D_ANTI_ALIASING_PRESET_HIGH); +} + +bool r3d_shader_load_prepare_smaa_blending_weights_ultra(r3d_shader_custom_t* custom) +{ + return load_prepare_smaa_blending_weights(custom, R3D_ANTI_ALIASING_PRESET_ULTRA); +} + +bool r3d_shader_load_prepare_cubemap_from_equirectangular(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_cubemap_from_equirectangular_t, prepare, cubemapFromEquirectangular); + LOAD_SHADER(cubemapFromEquirectangular, CUBEMAP_VERT, CUBEMAP_FROM_EQUIRECTANGULAR_FRAG); + + GET_LOCATION(cubemapFromEquirectangular, uMatProj); + GET_LOCATION(cubemapFromEquirectangular, uMatView); + + USE_SHADER(cubemapFromEquirectangular); + SET_SAMPLER(cubemapFromEquirectangular, uPanoramaTex, R3D_SHADER_SAMPLER_SOURCE_2D_0); + + return true; +} + +bool r3d_shader_load_prepare_cubemap_irradiance(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_cubemap_irradiance_t, prepare, cubemapIrradiance); + LOAD_SHADER(cubemapIrradiance, CUBEMAP_VERT, CUBEMAP_IRRADIANCE_FRAG); + + GET_LOCATION(cubemapIrradiance, uMatProj); + GET_LOCATION(cubemapIrradiance, uMatView); + + USE_SHADER(cubemapIrradiance); + SET_SAMPLER(cubemapIrradiance, uSourceTex, R3D_SHADER_SAMPLER_SOURCE_CUBE_0); + + return true; +} + +bool r3d_shader_load_prepare_cubemap_prefilter(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_cubemap_prefilter_t, prepare, cubemapPrefilter); + LOAD_SHADER(cubemapPrefilter, CUBEMAP_VERT, CUBEMAP_PREFILTER_FRAG); + + GET_LOCATION(cubemapPrefilter, uMatProj); + GET_LOCATION(cubemapPrefilter, uMatView); + GET_LOCATION(cubemapPrefilter, uSourceNumLevels); + GET_LOCATION(cubemapPrefilter, uSourceFaceSize); + GET_LOCATION(cubemapPrefilter, uRoughness); + + USE_SHADER(cubemapPrefilter); + SET_SAMPLER(cubemapPrefilter, uSourceTex, R3D_SHADER_SAMPLER_SOURCE_CUBE_0); + + return true; +} + +bool r3d_shader_load_prepare_cubemap_procedural_sky(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_prepare_cubemap_procedural_sky_t, prepare, cubemapProceduralSky); + LOAD_SHADER(cubemapProceduralSky, CUBEMAP_VERT, CUBEMAP_PROCEDURAL_SKY_FRAG); + + GET_LOCATION(cubemapProceduralSky, uMatProj); + GET_LOCATION(cubemapProceduralSky, uMatView); + GET_LOCATION(cubemapProceduralSky, uSkyTopColor); + GET_LOCATION(cubemapProceduralSky, uSkyHorizonColor); + GET_LOCATION(cubemapProceduralSky, uSkyHorizonCurve); + GET_LOCATION(cubemapProceduralSky, uSkyEnergy); + GET_LOCATION(cubemapProceduralSky, uGroundBottomColor); + GET_LOCATION(cubemapProceduralSky, uGroundHorizonColor); + GET_LOCATION(cubemapProceduralSky, uGroundHorizonCurve); + GET_LOCATION(cubemapProceduralSky, uGroundEnergy); + GET_LOCATION(cubemapProceduralSky, uSunDirection); + GET_LOCATION(cubemapProceduralSky, uSunColor); + GET_LOCATION(cubemapProceduralSky, uSunSize); + GET_LOCATION(cubemapProceduralSky, uSunCurve); + GET_LOCATION(cubemapProceduralSky, uSunEnergy); + + USE_SHADER(cubemapProceduralSky); + + return true; +} + +bool r3d_shader_load_prepare_cubemap_custom_sky(r3d_shader_custom_t* custom) +{ + assert(custom != NULL); + + r3d_shader_prepare_cubemap_custom_sky_t* cubemapCustomSky = &custom->program->prepare.cubemapCustomSky; + char* fragCode = inject_content(CUBEMAP_CUSTOM_SKY_FRAG, custom->program->userCode, "#define fragment()", 0); + LOAD_SHADER(cubemapCustomSky, CUBEMAP_VERT, fragCode); + RL_FREE(fragCode); + + SET_UNIFORM_BUFFER(cubemapCustomSky, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + + if (strstr(custom->program->userCode, "UserBlock") != NULL) { + SET_UNIFORM_BUFFER(cubemapCustomSky, UserBlock, R3D_SHADER_BLOCK_SLOT_USER); + } + + GET_LOCATION(cubemapCustomSky, uMatProj); + GET_LOCATION(cubemapCustomSky, uMatView); + + USE_SHADER(cubemapCustomSky); + set_custom_samplers(cubemapCustomSky->id, custom); + + return true; +} + +bool r3d_shader_load_scene_geometry(r3d_shader_custom_t* custom) +{ + DECL_SHADER_SELECT(r3d_shader_scene_geometry_t, scene, geometry, custom); + + const char* VS_DEFINES[] = {"STAGE_VERT", "GEOMETRY"}; + const char* FS_DEFINES[] = {"STAGE_FRAG", "GEOMETRY"}; + + char* vsCode = inject_defines(SCENE_VERT, VS_DEFINES, ARRAY_SIZE(VS_DEFINES)); + char* fsCode = inject_defines(GEOMETRY_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + const char* userCode = custom ? custom->program->userCode : NULL; + + if (userCode != NULL) { + inject_user_code(&vsCode, &fsCode, userCode); + } + + LOAD_SHADER(geometry, vsCode, fsCode); + + RL_FREE(vsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(geometry, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + SET_UNIFORM_BUFFER(geometry, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + + if (userCode && strstr(userCode, "UserBlock") != NULL) { + SET_UNIFORM_BUFFER(geometry, UserBlock, R3D_SHADER_BLOCK_SLOT_USER); + } + + GET_LOCATION(geometry, uMatNormal); + GET_LOCATION(geometry, uMatModel); + GET_LOCATION(geometry, uAlbedoColor); + GET_LOCATION(geometry, uEmissionEnergy); + GET_LOCATION(geometry, uEmissionColor); + GET_LOCATION(geometry, uTexCoordOffset); + GET_LOCATION(geometry, uTexCoordScale); + GET_LOCATION(geometry, uInstancing); + GET_LOCATION(geometry, uSkinning); + GET_LOCATION(geometry, uBillboard); + GET_LOCATION(geometry, uAlphaCutoff); + GET_LOCATION(geometry, uNormalScale); + GET_LOCATION(geometry, uOcclusion); + GET_LOCATION(geometry, uRoughness); + GET_LOCATION(geometry, uMetalness); + GET_LOCATION(geometry, uSpecular); + + USE_SHADER(geometry); + + SET_SAMPLER(geometry, uBoneMatricesTex, R3D_SHADER_SAMPLER_BONE_MATRICES); + SET_SAMPLER(geometry, uAlbedoMap, R3D_SHADER_SAMPLER_MAP_ALBEDO); + SET_SAMPLER(geometry, uNormalMap, R3D_SHADER_SAMPLER_MAP_NORMAL); + SET_SAMPLER(geometry, uEmissionMap, R3D_SHADER_SAMPLER_MAP_EMISSION); + SET_SAMPLER(geometry, uOrmMap, R3D_SHADER_SAMPLER_MAP_ORM); + + if (custom != NULL) { + set_custom_samplers(geometry->id, custom); + } + + return true; +} + +bool r3d_shader_load_scene_forward(r3d_shader_custom_t* custom) +{ + DECL_SHADER_SELECT(r3d_shader_scene_forward_t, scene, forward, custom); + + char defNumForwardLights[32] = {0}; + char defNumProbes[32] = {0}; + + r3d_string_format(defNumForwardLights, sizeof(defNumForwardLights), "NUM_FORWARD_LIGHTS %i", R3D_MAX_LIGHT_FORWARD_PER_MESH); + r3d_string_format(defNumProbes, sizeof(defNumProbes), "NUM_PROBES %i", R3D_MAX_PROBE_ON_SCREEN); + + const char* VS_DEFINES[] = {"STAGE_VERT", "FORWARD", defNumForwardLights}; + const char* FS_DEFINES[] = {"STAGE_FRAG", "FORWARD", defNumForwardLights, defNumProbes}; + + char* vsCode = inject_defines(SCENE_VERT, VS_DEFINES, ARRAY_SIZE(VS_DEFINES)); + char* fsCode = inject_defines(FORWARD_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + const char* userCode = custom ? custom->program->userCode : NULL; + + if (userCode != NULL) { + inject_user_code(&vsCode, &fsCode, userCode); + } + + LOAD_SHADER(forward, vsCode, fsCode); + + RL_FREE(vsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(forward, LightArrayBlock, R3D_SHADER_BLOCK_SLOT_LIGHT_ARRAY); + SET_UNIFORM_BUFFER(forward, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + SET_UNIFORM_BUFFER(forward, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + SET_UNIFORM_BUFFER(forward, EnvBlock, R3D_SHADER_BLOCK_SLOT_ENV); + SET_UNIFORM_BUFFER(forward, FogBlock, R3D_SHADER_BLOCK_SLOT_FOG); + + if (userCode && strstr(userCode, "UserBlock") != NULL) { + SET_UNIFORM_BUFFER(forward, UserBlock, R3D_SHADER_BLOCK_SLOT_USER); + } + + GET_LOCATION(forward, uMatNormal); + GET_LOCATION(forward, uMatModel); + GET_LOCATION(forward, uAlbedoColor); + GET_LOCATION(forward, uEmissionColor); + GET_LOCATION(forward, uEmissionEnergy); + GET_LOCATION(forward, uTexCoordOffset); + GET_LOCATION(forward, uTexCoordScale); + GET_LOCATION(forward, uInstancing); + GET_LOCATION(forward, uSkinning); + GET_LOCATION(forward, uBillboard); + GET_LOCATION(forward, uNormalScale); + GET_LOCATION(forward, uOcclusion); + GET_LOCATION(forward, uRoughness); + GET_LOCATION(forward, uMetalness); + GET_LOCATION(forward, uSpecular); + GET_LOCATION(forward, uViewPosition); + + USE_SHADER(forward); + + SET_SAMPLER(forward, uBoneMatricesTex, R3D_SHADER_SAMPLER_BONE_MATRICES); + SET_SAMPLER(forward, uAlbedoMap, R3D_SHADER_SAMPLER_MAP_ALBEDO); + SET_SAMPLER(forward, uEmissionMap, R3D_SHADER_SAMPLER_MAP_EMISSION); + SET_SAMPLER(forward, uNormalMap, R3D_SHADER_SAMPLER_MAP_NORMAL); + SET_SAMPLER(forward, uOrmMap, R3D_SHADER_SAMPLER_MAP_ORM); + SET_SAMPLER(forward, uShadowDirTex, R3D_SHADER_SAMPLER_SHADOW_DIR); + SET_SAMPLER(forward, uShadowSpotTex, R3D_SHADER_SAMPLER_SHADOW_SPOT); + SET_SAMPLER(forward, uShadowOmniTex, R3D_SHADER_SAMPLER_SHADOW_OMNI); + SET_SAMPLER(forward, uIrradianceTex, R3D_SHADER_SAMPLER_IBL_IRRADIANCE); + SET_SAMPLER(forward, uPrefilterTex, R3D_SHADER_SAMPLER_IBL_PREFILTER); + SET_SAMPLER(forward, uBrdfLutTex, R3D_SHADER_SAMPLER_IBL_BRDF_LUT); + + if (custom != NULL) { + set_custom_samplers(forward->id, custom); + } + + return true; +} + +bool r3d_shader_load_scene_unlit(r3d_shader_custom_t *custom) +{ + DECL_SHADER_SELECT(r3d_shader_scene_unlit_t, scene, unlit, custom); + + const char* VS_DEFINES[] = {"STAGE_VERT", "UNLIT"}; + const char* FS_DEFINES[] = {"STAGE_FRAG", "UNLIT"}; + + char* vsCode = inject_defines(SCENE_VERT, VS_DEFINES, ARRAY_SIZE(VS_DEFINES)); + char* fsCode = inject_defines(UNLIT_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + const char* userCode = custom ? custom->program->userCode : NULL; + + if (userCode != NULL) { + inject_user_code(&vsCode, &fsCode, userCode); + } + + LOAD_SHADER(unlit, vsCode, fsCode); + + RL_FREE(vsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(unlit, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + SET_UNIFORM_BUFFER(unlit, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + SET_UNIFORM_BUFFER(unlit, FogBlock, R3D_SHADER_BLOCK_SLOT_FOG); + + if (userCode && strstr(userCode, "UserBlock") != NULL) { + SET_UNIFORM_BUFFER(unlit, UserBlock, R3D_SHADER_BLOCK_SLOT_USER); + } + + GET_LOCATION(unlit, uMatNormal); + GET_LOCATION(unlit, uMatModel); + GET_LOCATION(unlit, uAlbedoColor); + GET_LOCATION(unlit, uTexCoordOffset); + GET_LOCATION(unlit, uTexCoordScale); + GET_LOCATION(unlit, uInstancing); + GET_LOCATION(unlit, uSkinning); + GET_LOCATION(unlit, uBillboard); + GET_LOCATION(unlit, uAlphaCutoff); + + USE_SHADER(unlit); + + SET_SAMPLER(unlit, uBoneMatricesTex, R3D_SHADER_SAMPLER_BONE_MATRICES); + SET_SAMPLER(unlit, uAlbedoMap, R3D_SHADER_SAMPLER_MAP_ALBEDO); + + if (custom != NULL) { + set_custom_samplers(unlit->id, custom); + } + + return true; +} + +bool r3d_shader_load_scene_background(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_scene_background_t, scene, background); + LOAD_SHADER(background, SCREEN_VERT, COLOR_FRAG); + GET_LOCATION(background, uColor); + + return true; +} + +bool r3d_shader_load_scene_skybox(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_scene_skybox_t, scene, skybox); + LOAD_SHADER(skybox, SKYBOX_VERT, SKYBOX_FRAG); + + SET_UNIFORM_BUFFER(skybox, FogBlock, R3D_SHADER_BLOCK_SLOT_FOG); + + GET_LOCATION(skybox, uMatInvView); + GET_LOCATION(skybox, uMatInvProj); + GET_LOCATION(skybox, uRotation); + GET_LOCATION(skybox, uEnergy); + GET_LOCATION(skybox, uLod); + + USE_SHADER(skybox); + + SET_SAMPLER(skybox, uSkyMap, R3D_SHADER_SAMPLER_SOURCE_CUBE_0); + + return true; +} + +bool r3d_shader_load_scene_depth(r3d_shader_custom_t* custom) +{ + DECL_SHADER_SELECT(r3d_shader_scene_depth_t, scene, depth, custom); + + const char* VS_DEFINES[] = {"STAGE_VERT", "DEPTH"}; + const char* FS_DEFINES[] = {"STAGE_FRAG", "DEPTH"}; + + char* vsCode = inject_defines(SCENE_VERT, VS_DEFINES, ARRAY_SIZE(VS_DEFINES)); + char* fsCode = inject_defines(DEPTH_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + const char* userCode = custom ? custom->program->userCode : NULL; + + if (userCode != NULL) { + inject_user_code(&vsCode, &fsCode, userCode); + } + + LOAD_SHADER(depth, vsCode, fsCode); + + RL_FREE(vsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(depth, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + + if (userCode && strstr(userCode, "UserBlock") != NULL) { + SET_UNIFORM_BUFFER(depth, UserBlock, R3D_SHADER_BLOCK_SLOT_USER); + } + + GET_LOCATION(depth, uMatModel); + GET_LOCATION(depth, uMatInvView); + GET_LOCATION(depth, uMatViewProj); + GET_LOCATION(depth, uAlbedoColor); + GET_LOCATION(depth, uTexCoordOffset); + GET_LOCATION(depth, uTexCoordScale); + GET_LOCATION(depth, uInstancing); + GET_LOCATION(depth, uSkinning); + GET_LOCATION(depth, uBillboard); + GET_LOCATION(depth, uAlphaCutoff); + + USE_SHADER(depth); + + SET_SAMPLER(depth, uBoneMatricesTex, R3D_SHADER_SAMPLER_BONE_MATRICES); + SET_SAMPLER(depth, uAlbedoMap, R3D_SHADER_SAMPLER_MAP_ALBEDO); + + if (custom != NULL) { + set_custom_samplers(depth->id, custom); + } + + return true; +} + +bool r3d_shader_load_scene_depth_cube(r3d_shader_custom_t* custom) +{ + DECL_SHADER_SELECT(r3d_shader_scene_depth_cube_t, scene, depthCube, custom); + + const char* VS_DEFINES[] = {"STAGE_VERT", "DEPTH_CUBE"}; + const char* FS_DEFINES[] = {"STAGE_FRAG", "DEPTH_CUBE"}; + + char* vsCode = inject_defines(SCENE_VERT, VS_DEFINES, ARRAY_SIZE(VS_DEFINES)); + char* fsCode = inject_defines(DEPTH_CUBE_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + const char* userCode = custom ? custom->program->userCode : NULL; + + if (userCode != NULL) { + inject_user_code(&vsCode, &fsCode, userCode); + } + + LOAD_SHADER(depthCube, vsCode, fsCode); + + RL_FREE(vsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(depthCube, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + + if (userCode && strstr(userCode, "UserBlock") != NULL) { + SET_UNIFORM_BUFFER(depthCube, UserBlock, R3D_SHADER_BLOCK_SLOT_USER); + } + + GET_LOCATION(depthCube, uMatModel); + GET_LOCATION(depthCube, uMatInvView); + GET_LOCATION(depthCube, uMatViewProj); + GET_LOCATION(depthCube, uAlbedoColor); + GET_LOCATION(depthCube, uTexCoordOffset); + GET_LOCATION(depthCube, uTexCoordScale); + GET_LOCATION(depthCube, uInstancing); + GET_LOCATION(depthCube, uSkinning); + GET_LOCATION(depthCube, uBillboard); + GET_LOCATION(depthCube, uAlphaCutoff); + GET_LOCATION(depthCube, uViewPosition); + GET_LOCATION(depthCube, uFar); + + USE_SHADER(depthCube); + + SET_SAMPLER(depthCube, uBoneMatricesTex, R3D_SHADER_SAMPLER_BONE_MATRICES); + SET_SAMPLER(depthCube, uAlbedoMap, R3D_SHADER_SAMPLER_MAP_ALBEDO); + + if (custom != NULL) { + set_custom_samplers(depthCube->id, custom); + } + + return true; +} + +bool r3d_shader_load_scene_probe_forward(r3d_shader_custom_t* custom) +{ + DECL_SHADER_SELECT(r3d_shader_scene_probe_forward_t, scene, probeForward, custom); + + char defNumForwardLights[32] = {0}; + char defNumProbes[32] = {0}; + + r3d_string_format(defNumForwardLights, sizeof(defNumForwardLights), "NUM_FORWARD_LIGHTS %i", R3D_MAX_LIGHT_FORWARD_PER_MESH); + r3d_string_format(defNumProbes, sizeof(defNumProbes), "NUM_PROBES %i", R3D_MAX_PROBE_ON_SCREEN); + + const char* VS_DEFINES[] = {"STAGE_VERT", "PROBE", "PROBE_FORWARD", defNumForwardLights}; + const char* FS_DEFINES[] = {"STAGE_FRAG", "PROBE", "PROBE_FORWARD", defNumForwardLights, defNumProbes}; + + char* vsCode = inject_defines(SCENE_VERT, VS_DEFINES, ARRAY_SIZE(VS_DEFINES)); + char* fsCode = inject_defines(FORWARD_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + const char* userCode = custom ? custom->program->userCode : NULL; + + if (userCode != NULL) { + inject_user_code(&vsCode, &fsCode, userCode); + } + + LOAD_SHADER(probeForward, vsCode, fsCode); + + RL_FREE(vsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(probeForward, LightArrayBlock, R3D_SHADER_BLOCK_SLOT_LIGHT_ARRAY); + SET_UNIFORM_BUFFER(probeForward, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + SET_UNIFORM_BUFFER(probeForward, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + SET_UNIFORM_BUFFER(probeForward, EnvBlock, R3D_SHADER_BLOCK_SLOT_ENV); + SET_UNIFORM_BUFFER(probeForward, FogBlock, R3D_SHADER_BLOCK_SLOT_FOG); + + if (userCode && strstr(userCode, "UserBlock") != NULL) { + SET_UNIFORM_BUFFER(probeForward, UserBlock, R3D_SHADER_BLOCK_SLOT_USER); + } + + GET_LOCATION(probeForward, uMatNormal); + GET_LOCATION(probeForward, uMatModel); + GET_LOCATION(probeForward, uMatView); + GET_LOCATION(probeForward, uMatInvView); + GET_LOCATION(probeForward, uMatViewProj); + GET_LOCATION(probeForward, uAlbedoColor); + GET_LOCATION(probeForward, uEmissionColor); + GET_LOCATION(probeForward, uEmissionEnergy); + GET_LOCATION(probeForward, uTexCoordOffset); + GET_LOCATION(probeForward, uTexCoordScale); + GET_LOCATION(probeForward, uInstancing); + GET_LOCATION(probeForward, uSkinning); + GET_LOCATION(probeForward, uBillboard); + GET_LOCATION(probeForward, uNormalScale); + GET_LOCATION(probeForward, uOcclusion); + GET_LOCATION(probeForward, uRoughness); + GET_LOCATION(probeForward, uMetalness); + GET_LOCATION(probeForward, uSpecular); + GET_LOCATION(probeForward, uViewPosition); + GET_LOCATION(probeForward, uProbeInterior); + + USE_SHADER(probeForward); + + SET_SAMPLER(probeForward, uBoneMatricesTex, R3D_SHADER_SAMPLER_BONE_MATRICES); + SET_SAMPLER(probeForward, uAlbedoMap, R3D_SHADER_SAMPLER_MAP_ALBEDO); + SET_SAMPLER(probeForward, uEmissionMap, R3D_SHADER_SAMPLER_MAP_EMISSION); + SET_SAMPLER(probeForward, uNormalMap, R3D_SHADER_SAMPLER_MAP_NORMAL); + SET_SAMPLER(probeForward, uOrmMap, R3D_SHADER_SAMPLER_MAP_ORM); + SET_SAMPLER(probeForward, uShadowDirTex, R3D_SHADER_SAMPLER_SHADOW_DIR); + SET_SAMPLER(probeForward, uShadowSpotTex, R3D_SHADER_SAMPLER_SHADOW_SPOT); + SET_SAMPLER(probeForward, uShadowOmniTex, R3D_SHADER_SAMPLER_SHADOW_OMNI); + SET_SAMPLER(probeForward, uIrradianceTex, R3D_SHADER_SAMPLER_IBL_IRRADIANCE); + SET_SAMPLER(probeForward, uPrefilterTex, R3D_SHADER_SAMPLER_IBL_PREFILTER); + SET_SAMPLER(probeForward, uBrdfLutTex, R3D_SHADER_SAMPLER_IBL_BRDF_LUT); + + if (custom != NULL) { + set_custom_samplers(probeForward->id, custom); + } + + return true; +} + +bool r3d_shader_load_scene_probe_unlit(r3d_shader_custom_t *custom) +{ + DECL_SHADER_SELECT(r3d_shader_scene_probe_unlit_t, scene, probeUnlit, custom); + + const char* VS_DEFINES[] = {"STAGE_VERT", "PROBE", "PROBE_UNLIT"}; + const char* FS_DEFINES[] = {"STAGE_FRAG", "PROBE", "PROBE_UNLIT"}; + + char* vsCode = inject_defines(SCENE_VERT, VS_DEFINES, ARRAY_SIZE(VS_DEFINES)); + char* fsCode = inject_defines(UNLIT_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + const char* userCode = custom ? custom->program->userCode : NULL; + + if (userCode != NULL) { + inject_user_code(&vsCode, &fsCode, userCode); + } + + LOAD_SHADER(probeUnlit, vsCode, fsCode); + + RL_FREE(vsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(probeUnlit, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + SET_UNIFORM_BUFFER(probeUnlit, FogBlock, R3D_SHADER_BLOCK_SLOT_FOG); + + if (userCode && strstr(userCode, "UserBlock") != NULL) { + SET_UNIFORM_BUFFER(probeUnlit, UserBlock, R3D_SHADER_BLOCK_SLOT_USER); + } + + GET_LOCATION(probeUnlit, uMatNormal); + GET_LOCATION(probeUnlit, uMatModel); + GET_LOCATION(probeUnlit, uMatView); + GET_LOCATION(probeUnlit, uMatInvView); + GET_LOCATION(probeUnlit, uMatViewProj); + GET_LOCATION(probeUnlit, uAlbedoColor); + GET_LOCATION(probeUnlit, uTexCoordOffset); + GET_LOCATION(probeUnlit, uTexCoordScale); + GET_LOCATION(probeUnlit, uInstancing); + GET_LOCATION(probeUnlit, uSkinning); + GET_LOCATION(probeUnlit, uBillboard); + GET_LOCATION(probeUnlit, uAlphaCutoff); + + USE_SHADER(probeUnlit); + + SET_SAMPLER(probeUnlit, uBoneMatricesTex, R3D_SHADER_SAMPLER_BONE_MATRICES); + SET_SAMPLER(probeUnlit, uAlbedoMap, R3D_SHADER_SAMPLER_MAP_ALBEDO); + + if (custom != NULL) { + set_custom_samplers(probeUnlit->id, custom); + } + + return true; +} + +bool r3d_shader_load_scene_decal(r3d_shader_custom_t* custom) +{ + DECL_SHADER_SELECT(r3d_shader_scene_decal_t, scene, decal, custom); + + const char* VS_DEFINES[] = {"STAGE_VERT", "DECAL"}; + const char* FS_DEFINES[] = {"STAGE_FRAG", "DECAL"}; + + char* vsCode = inject_defines(SCENE_VERT, VS_DEFINES, ARRAY_SIZE(VS_DEFINES)); + char* fsCode = inject_defines(DECAL_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + const char* userCode = custom ? custom->program->userCode : NULL; + + if (userCode != NULL) { + inject_user_code(&vsCode, &fsCode, userCode); + } + + LOAD_SHADER(decal, vsCode, fsCode); + + RL_FREE(vsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(decal, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + SET_UNIFORM_BUFFER(decal, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + + if (userCode && strstr(userCode, "UserBlock") != NULL) { + SET_UNIFORM_BUFFER(decal, UserBlock, R3D_SHADER_BLOCK_SLOT_USER); + } + + GET_LOCATION(decal, uMatNormal); + GET_LOCATION(decal, uMatModel); + GET_LOCATION(decal, uAlbedoColor); + GET_LOCATION(decal, uEmissionEnergy); + GET_LOCATION(decal, uEmissionColor); + GET_LOCATION(decal, uTexCoordOffset); + GET_LOCATION(decal, uTexCoordScale); + GET_LOCATION(decal, uInstancing); + GET_LOCATION(decal, uAlphaCutoff); + GET_LOCATION(decal, uNormalScale); + GET_LOCATION(decal, uOcclusion); + GET_LOCATION(decal, uRoughness); + GET_LOCATION(decal, uMetalness); + GET_LOCATION(decal, uSpecular); + GET_LOCATION(decal, uNormalThreshold); + GET_LOCATION(decal, uFadeWidth); + GET_LOCATION(decal, uApplyColor); + + USE_SHADER(decal); + + SET_SAMPLER(decal, uAlbedoMap, R3D_SHADER_SAMPLER_MAP_ALBEDO); + SET_SAMPLER(decal, uNormalMap, R3D_SHADER_SAMPLER_MAP_NORMAL); + SET_SAMPLER(decal, uEmissionMap, R3D_SHADER_SAMPLER_MAP_EMISSION); + SET_SAMPLER(decal, uOrmMap, R3D_SHADER_SAMPLER_MAP_ORM); + SET_SAMPLER(decal, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + SET_SAMPLER(decal, uGeomNormalTex, R3D_SHADER_SAMPLER_BUFFER_GEOM_NORMAL); + + if (custom != NULL) { + set_custom_samplers(decal->id, custom); + } + + return true; +} + +bool r3d_shader_load_deferred_ambient(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_deferred_ambient_t, deferred, ambient); + + char defNumProbes[32] = {0}; + r3d_string_format(defNumProbes, sizeof(defNumProbes), "NUM_PROBES %i", R3D_MAX_PROBE_ON_SCREEN); + + const char* FS_DEFINES[] = {defNumProbes}; + char* fsCode = inject_defines(AMBIENT_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + LOAD_SHADER(ambient, SCREEN_VERT, fsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(ambient, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + SET_UNIFORM_BUFFER(ambient, EnvBlock, R3D_SHADER_BLOCK_SLOT_ENV); + + GET_LOCATION(ambient, uSsaoPower); + GET_LOCATION(ambient, uSsilAoPower); + GET_LOCATION(ambient, uSsilIntensity); + GET_LOCATION(ambient, uSsgiIntensity); + + USE_SHADER(ambient); + + SET_SAMPLER(ambient, uAlbedoTex, R3D_SHADER_SAMPLER_BUFFER_ALBEDO); + SET_SAMPLER(ambient, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + SET_SAMPLER(ambient, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + SET_SAMPLER(ambient, uSsaoTex, R3D_SHADER_SAMPLER_BUFFER_SSAO); + SET_SAMPLER(ambient, uSsilTex, R3D_SHADER_SAMPLER_BUFFER_SSIL); + SET_SAMPLER(ambient, uSsgiTex, R3D_SHADER_SAMPLER_BUFFER_SSGI); + SET_SAMPLER(ambient, uOrmTex, R3D_SHADER_SAMPLER_BUFFER_ORM); + + SET_SAMPLER(ambient, uIrradianceTex, R3D_SHADER_SAMPLER_IBL_IRRADIANCE); + SET_SAMPLER(ambient, uPrefilterTex, R3D_SHADER_SAMPLER_IBL_PREFILTER); + SET_SAMPLER(ambient, uBrdfLutTex, R3D_SHADER_SAMPLER_IBL_BRDF_LUT); + + return true; +} + +bool r3d_shader_load_deferred_lighting(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_deferred_lighting_t, deferred, lighting); + LOAD_SHADER(lighting, SCREEN_VERT, LIGHTING_FRAG); + + SET_UNIFORM_BUFFER(lighting, LightBlock, R3D_SHADER_BLOCK_SLOT_LIGHT); + SET_UNIFORM_BUFFER(lighting, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + + USE_SHADER(lighting); + + SET_SAMPLER(lighting, uAlbedoTex, R3D_SHADER_SAMPLER_BUFFER_ALBEDO); + SET_SAMPLER(lighting, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + SET_SAMPLER(lighting, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + SET_SAMPLER(lighting, uOrmTex, R3D_SHADER_SAMPLER_BUFFER_ORM); + + SET_SAMPLER(lighting, uShadowDirTex, R3D_SHADER_SAMPLER_SHADOW_DIR); + SET_SAMPLER(lighting, uShadowSpotTex, R3D_SHADER_SAMPLER_SHADOW_SPOT); + SET_SAMPLER(lighting, uShadowOmniTex, R3D_SHADER_SAMPLER_SHADOW_OMNI); + + return true; +} + +bool r3d_shader_load_deferred_compose(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_deferred_compose_t, deferred, compose); + LOAD_SHADER(compose, SCREEN_VERT, COMPOSE_FRAG); + + GET_LOCATION(compose, uSsrNumLevels); + + USE_SHADER(compose); + + SET_SAMPLER(compose, uAlbedoTex, R3D_SHADER_SAMPLER_BUFFER_ALBEDO); + SET_SAMPLER(compose, uDiffuseTex, R3D_SHADER_SAMPLER_BUFFER_DIFFUSE); + SET_SAMPLER(compose, uSpecularTex, R3D_SHADER_SAMPLER_BUFFER_SPECULAR); + SET_SAMPLER(compose, uOrmTex, R3D_SHADER_SAMPLER_BUFFER_ORM); + SET_SAMPLER(compose, uSsrTex, R3D_SHADER_SAMPLER_BUFFER_SSR); + + return true; +} + +bool r3d_shader_load_deferred_fog(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_deferred_fog_t, deferred, fog); + LOAD_SHADER(fog, SCREEN_VERT, FOG_FRAG); + + SET_UNIFORM_BUFFER(fog, FogBlock, R3D_SHADER_BLOCK_SLOT_FOG); + + USE_SHADER(fog); + SET_SAMPLER(fog, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + + return true; +} + +bool r3d_shader_load_post_dof(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_post_dof_t, post, dof); + LOAD_SHADER(dof, SCREEN_VERT, DOF_FRAG); + + USE_SHADER(dof); + SET_SAMPLER(dof, uSceneTex, R3D_SHADER_SAMPLER_BUFFER_SCENE); + SET_SAMPLER(dof, uBlurTex, R3D_SHADER_SAMPLER_BUFFER_DOF); + + return true; +} + +bool r3d_shader_load_post_bloom(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_post_bloom_t, post, bloom); + LOAD_SHADER(bloom, SCREEN_VERT, BLOOM_FRAG); + + GET_LOCATION(bloom, uBloomMode); + GET_LOCATION(bloom, uBloomIntensity); + + USE_SHADER(bloom); + + SET_SAMPLER(bloom, uSceneTex, R3D_SHADER_SAMPLER_BUFFER_SCENE); + SET_SAMPLER(bloom, uBloomTex, R3D_SHADER_SAMPLER_BUFFER_BLOOM); + + return true; +} + +bool r3d_shader_load_post_auto_exposure(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_post_auto_exposure_t, post, autoExposure); + LOAD_SHADER(autoExposure, SCREEN_VERT, AUTO_EXPOSURE_FRAG); + + USE_SHADER(autoExposure); + SET_SAMPLER(autoExposure, uSceneTex, R3D_SHADER_SAMPLER_BUFFER_SCENE); + SET_SAMPLER(autoExposure, uExposureTex, R3D_SHADER_SAMPLER_BUFFER_EXPOSURE); + + return true; +} + +bool r3d_shader_load_post_screen(r3d_shader_custom_t* custom) +{ + assert(custom != NULL); + + r3d_shader_post_screen_t* screen = &custom->program->post.screen; + char* fragCode = inject_content(SCREEN_FRAG, custom->program->userCode, "#define fragment()", 0); + LOAD_SHADER(screen, SCREEN_VERT, fragCode); + RL_FREE(fragCode); + + SET_UNIFORM_BUFFER(screen, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + SET_UNIFORM_BUFFER(screen, ViewBlock, R3D_SHADER_BLOCK_SLOT_VIEW); + + if (strstr(custom->program->userCode, "UserBlock") != NULL) { + SET_UNIFORM_BUFFER(screen, UserBlock, R3D_SHADER_BLOCK_SLOT_USER); + } + + USE_SHADER(screen); + SET_SAMPLER(screen, uSceneTex, R3D_SHADER_SAMPLER_BUFFER_SCENE); + SET_SAMPLER(screen, uNormalTex, R3D_SHADER_SAMPLER_BUFFER_NORMAL); + SET_SAMPLER(screen, uDepthTex, R3D_SHADER_SAMPLER_BUFFER_DEPTH); + + set_custom_samplers(screen->id, custom); + + return true; +} + +bool r3d_shader_load_post_output(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_post_output_t, post, output); + LOAD_SHADER(output, SCREEN_VERT, OUTPUT_FRAG); + + GET_LOCATION(output, uTonemapExposure); + GET_LOCATION(output, uTonemapWhite); + GET_LOCATION(output, uTonemapMode); + GET_LOCATION(output, uBrightness); + GET_LOCATION(output, uContrast); + GET_LOCATION(output, uSaturation); + + USE_SHADER(output); + SET_SAMPLER(output, uSceneTex, R3D_SHADER_SAMPLER_BUFFER_SCENE); + + return true; +} + +static bool load_post_fxaa(r3d_shader_custom_t* custom, int index) +{ + char defQualityPreset[32] = {0}; + r3d_string_format(defQualityPreset, sizeof(defQualityPreset), "QUALITY_PRESET %i", index); + + const char* FS_DEFINES[] = {defQualityPreset}; + char* fsCode = inject_defines(FXAA_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + DECL_SHADER_INDEXED(r3d_shader_post_fxaa_t, post, fxaa, index); + LOAD_SHADER(fxaa, SCREEN_VERT, fsCode); + + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(fxaa, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + + USE_SHADER(fxaa); + SET_SAMPLER(fxaa, uSceneTex, R3D_SHADER_SAMPLER_BUFFER_SCENE); + + return true; +} + +bool r3d_shader_load_post_fxaa_low(r3d_shader_custom_t* custom) +{ + return load_post_fxaa(custom, R3D_ANTI_ALIASING_PRESET_LOW); +} + +bool r3d_shader_load_post_fxaa_medium(r3d_shader_custom_t* custom) +{ + return load_post_fxaa(custom, R3D_ANTI_ALIASING_PRESET_MEDIUM); +} + +bool r3d_shader_load_post_fxaa_high(r3d_shader_custom_t* custom) +{ + return load_post_fxaa(custom, R3D_ANTI_ALIASING_PRESET_HIGH); +} + +bool r3d_shader_load_post_fxaa_ultra(r3d_shader_custom_t* custom) +{ + return load_post_fxaa(custom, R3D_ANTI_ALIASING_PRESET_ULTRA); +} + +static bool load_post_smaa(r3d_shader_custom_t* custom, int index) +{ + char defQualityPreset[32] = {0}; + r3d_string_format(defQualityPreset, sizeof(defQualityPreset), "QUALITY_PRESET %i", index); + + const char* VS_DEFINES[] = {defQualityPreset}; + const char* FS_DEFINES[] = {defQualityPreset}; + + char* vsCode = inject_defines(SMAA_VERT, VS_DEFINES, ARRAY_SIZE(VS_DEFINES)); + char* fsCode = inject_defines(SMAA_FRAG, FS_DEFINES, ARRAY_SIZE(FS_DEFINES)); + + DECL_SHADER_INDEXED(r3d_shader_post_smaa_t, post, smaa, index); + LOAD_SHADER(smaa, vsCode, fsCode); + + RL_FREE(vsCode); + RL_FREE(fsCode); + + SET_UNIFORM_BUFFER(smaa, FrameBlock, R3D_SHADER_BLOCK_SLOT_FRAME); + + USE_SHADER(smaa); + SET_SAMPLER(smaa, uSceneTex, R3D_SHADER_SAMPLER_BUFFER_SCENE); + SET_SAMPLER(smaa, uBlendTex, R3D_SHADER_SAMPLER_BUFFER_SMAA_BLEND); + + return true; +} + +bool r3d_shader_load_post_smaa_low(r3d_shader_custom_t* custom) +{ + return load_post_smaa(custom, R3D_ANTI_ALIASING_PRESET_LOW); +} + +bool r3d_shader_load_post_smaa_medium(r3d_shader_custom_t* custom) +{ + return load_post_smaa(custom, R3D_ANTI_ALIASING_PRESET_MEDIUM); +} + +bool r3d_shader_load_post_smaa_high(r3d_shader_custom_t* custom) +{ + return load_post_smaa(custom, R3D_ANTI_ALIASING_PRESET_HIGH); +} + +bool r3d_shader_load_post_smaa_ultra(r3d_shader_custom_t* custom) +{ + return load_post_smaa(custom, R3D_ANTI_ALIASING_PRESET_ULTRA); +} + +bool r3d_shader_load_post_visualizer(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_post_visualizer_t, post, visualizer); + LOAD_SHADER(visualizer, SCREEN_VERT, VISUALIZER_FRAG); + + GET_LOCATION(visualizer, uOutputMode); + + USE_SHADER(visualizer); + SET_SAMPLER(visualizer, uSourceTex, R3D_SHADER_SAMPLER_BUFFER_SCENE); + + return true; +} + +bool r3d_shader_load_blit_up_bicubic(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_blit_up_bicubic_t, blit, upBicubic); + LOAD_SHADER(upBicubic, SCREEN_VERT, UP_BICUBIC_FRAG); + + GET_LOCATION(upBicubic, uSourceTexel); + + USE_SHADER(upBicubic); + SET_SAMPLER(upBicubic, uSourceTex, R3D_SHADER_SAMPLER_SOURCE_2D_0); + + return true; +} + +bool r3d_shader_load_blit_up_lanczos(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_blit_up_lanczos_t, blit, upLanczos); + LOAD_SHADER(upLanczos, SCREEN_VERT, UP_LANCZOS_FRAG); + + GET_LOCATION(upLanczos, uSourceTexel); + + USE_SHADER(upLanczos); + SET_SAMPLER(upLanczos, uSourceTex, R3D_SHADER_SAMPLER_SOURCE_2D_0); + + return true; +} + +bool r3d_shader_load_blit_down_rgss(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_blit_down_rgss_t, blit, downRgss); + LOAD_SHADER(downRgss, SCREEN_VERT, DOWN_RGSS_FRAG); + + GET_LOCATION(downRgss, uDestTexel); + + USE_SHADER(downRgss); + SET_SAMPLER(downRgss, uSourceTex, R3D_SHADER_SAMPLER_SOURCE_2D_0); + + return true; +} + +bool r3d_shader_load_blit_down_pdss(r3d_shader_custom_t* custom) +{ + DECL_SHADER(r3d_shader_blit_down_pdss_t, blit, downPdss); + LOAD_SHADER(downPdss, SCREEN_VERT, DOWN_PDSS_FRAG); + + GET_LOCATION(downPdss, uDestTexel); + + USE_SHADER(downPdss); + SET_SAMPLER(downPdss, uSourceTex, R3D_SHADER_SAMPLER_SOURCE_2D_0); + + return true; +} + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +bool r3d_shader_init() +{ + memset(&R3D_MOD_SHADER, 0, sizeof(R3D_MOD_SHADER)); + + glGenBuffers(R3D_SHADER_BLOCK_COUNT, R3D_MOD_SHADER.uniformBuffers); + for (int i = 0; i < R3D_SHADER_BLOCK_COUNT; i++) { + GLuint buffer = R3D_MOD_SHADER.uniformBuffers[i]; + glBindBuffer(GL_UNIFORM_BUFFER, R3D_MOD_SHADER.uniformBuffers[i]); + glBufferData(GL_UNIFORM_BUFFER, R3D_SHADER_BLOCK_SIZES[i], NULL, GL_DYNAMIC_DRAW); + } + + memcpy(R3D_MOD_SHADER.samplerTargets, R3D_MOD_SHADER_SAMPLER_TYPES, sizeof(R3D_MOD_SHADER_SAMPLER_TYPES)); + for (int i = 0; i < R3D_MAX_SHADER_SAMPLERS; ++i) { + R3D_MOD_SHADER.samplerTargets[R3D_SHADER_SAMPLER_CUSTOM_1D + i] = GL_TEXTURE_1D; + R3D_MOD_SHADER.samplerTargets[R3D_SHADER_SAMPLER_CUSTOM_2D + i] = GL_TEXTURE_2D; + R3D_MOD_SHADER.samplerTargets[R3D_SHADER_SAMPLER_CUSTOM_3D + i] = GL_TEXTURE_3D; + R3D_MOD_SHADER.samplerTargets[R3D_SHADER_SAMPLER_CUSTOM_CUBE + i] = GL_TEXTURE_CUBE_MAP; + } + + return true; +} + +void r3d_shader_quit() +{ + glDeleteBuffers(R3D_SHADER_BLOCK_COUNT, R3D_MOD_SHADER.uniformBuffers); + + UNLOAD_SHADER(prepare.denoiserAtrous); + UNLOAD_SHADER(prepare.denoiserSparse); + UNLOAD_SHADER(prepare.blurDown); + UNLOAD_SHADER(prepare.blurUp); + UNLOAD_SHADER(prepare.depthPyramid); + UNLOAD_SHADER(prepare.ssaoInDown); + UNLOAD_SHADER(prepare.ssao); + UNLOAD_SHADER(prepare.ssilInDown); + UNLOAD_SHADER(prepare.ssil); + UNLOAD_SHADER(prepare.ssgiInDown); + UNLOAD_SHADER(prepare.ssgi); + UNLOAD_SHADER(prepare.ssrInDown); + UNLOAD_SHADER(prepare.ssr); + UNLOAD_SHADER(prepare.dofCoc); + UNLOAD_SHADER(prepare.dofDown); + UNLOAD_SHADER(prepare.dofBlur); + UNLOAD_SHADER(prepare.bloomDown); + UNLOAD_SHADER(prepare.bloomUp); + UNLOAD_SHADERS(prepare.smaaEdgeDetection); + UNLOAD_SHADERS(prepare.smaaBlendingWeights); + UNLOAD_SHADER(prepare.cubemapFromEquirectangular); + UNLOAD_SHADER(prepare.cubemapIrradiance); + UNLOAD_SHADER(prepare.cubemapPrefilter); + UNLOAD_SHADER(prepare.cubemapProceduralSky); + + UNLOAD_SHADER(scene.geometry); + UNLOAD_SHADER(scene.forward); + UNLOAD_SHADER(scene.unlit); + UNLOAD_SHADER(scene.background); + UNLOAD_SHADER(scene.skybox); + UNLOAD_SHADER(scene.depth); + UNLOAD_SHADER(scene.depthCube); + UNLOAD_SHADER(scene.probeForward); + UNLOAD_SHADER(scene.probeUnlit); + UNLOAD_SHADER(scene.decal); + + UNLOAD_SHADER(deferred.ambient); + UNLOAD_SHADER(deferred.lighting); + UNLOAD_SHADER(deferred.compose); + UNLOAD_SHADER(deferred.fog); + + UNLOAD_SHADER(post.dof); + UNLOAD_SHADER(post.bloom); + UNLOAD_SHADER(post.output); + UNLOAD_SHADERS(post.fxaa); + UNLOAD_SHADERS(post.smaa); + UNLOAD_SHADER(post.visualizer); + + UNLOAD_SHADER(blit.upBicubic); + UNLOAD_SHADER(blit.upLanczos); + UNLOAD_SHADER(blit.downRgss); + UNLOAD_SHADER(blit.downPdss); +} + +void r3d_shader_bind_sampler(r3d_shader_sampler_t sampler, GLuint texture) +{ + assert(R3D_MOD_SHADER.samplerTargets[sampler] != GL_NONE); + + if (texture != R3D_MOD_SHADER.samplerBindings[sampler]) { + glActiveTexture(GL_TEXTURE0 + sampler); + glBindTexture(R3D_MOD_SHADER.samplerTargets[sampler], texture); + R3D_MOD_SHADER.samplerBindings[sampler] = texture; + glActiveTexture(GL_TEXTURE0); + } +} + +void r3d_shader_set_uniform_block(r3d_shader_block_t block, const void* data) +{ + assert(block < R3D_SHADER_BLOCK_COUNT); + + GLuint ubo = R3D_MOD_SHADER.uniformBuffers[block]; + int blockSlot = R3D_SHADER_BLOCK_SLOTS[block]; + int blockSize = R3D_SHADER_BLOCK_SIZES[block]; + + glBindBuffer(GL_UNIFORM_BUFFER, ubo); + glBufferSubData(GL_UNIFORM_BUFFER, 0, blockSize, data); + + if (R3D_MOD_SHADER.uniformBindings[block] != ubo) { + glBindBufferBase(GL_UNIFORM_BUFFER, blockSlot, ubo); + R3D_MOD_SHADER.uniformBindings[block] = ubo; + } +} + +void r3d_shader_bind_uniform_block(r3d_shader_block_t block) +{ + assert(block < R3D_SHADER_BLOCK_COUNT); + + GLuint ubo = R3D_MOD_SHADER.uniformBuffers[block]; + int blockSlot = R3D_SHADER_BLOCK_SLOTS[block]; + + if (R3D_MOD_SHADER.uniformBindings[block] != ubo) { + glBindBufferBase(GL_UNIFORM_BUFFER, blockSlot, ubo); + R3D_MOD_SHADER.uniformBindings[block] = ubo; + } +} + +r3d_shader_custom_t* r3d_shader_custom_alloc(void) +{ + size_t programOffset = (sizeof(r3d_shader_custom_t) + alignof(r3d_shader_custom_program_t) - 1) & ~(alignof(r3d_shader_custom_program_t) - 1); + size_t size = programOffset + sizeof(r3d_shader_custom_program_t); + + r3d_shader_custom_t* shader = RL_CALLOC(1, size); + if (shader == NULL) return NULL; + + shader->program = (r3d_shader_custom_program_t*)((uint8_t*)shader + programOffset); + shader->programOwner = true; + + return shader; +} + +r3d_shader_custom_t* r3d_shader_custom_clone(r3d_shader_custom_t* custom) +{ + r3d_shader_custom_t* clone = RL_CALLOC(1, sizeof(r3d_shader_custom_t)); + if (clone == NULL) return NULL; + + clone->program = custom->program; + clone->programOwner = false; + + memcpy(clone->data.samplers, custom->data.samplers, sizeof(custom->data.samplers)); + + if (custom->data.uniforms.bufferSize > 0) + { + memcpy(&clone->data.uniforms, &custom->data.uniforms, sizeof(r3d_rshade_uniform_buffer_t)); + + clone->data.uniforms.dirty = false; + clone->data.uniforms.bufferId = 0; + + glGenBuffers(1, &clone->data.uniforms.bufferId); + glBindBuffer(GL_UNIFORM_BUFFER, clone->data.uniforms.bufferId); + glBufferData(GL_UNIFORM_BUFFER, clone->data.uniforms.bufferSize, clone->data.uniforms.buffer, GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + + return clone; +} + +void r3d_shader_custom_free(r3d_shader_custom_t* custom) +{ +#define DELETE_PROGRAM(id) \ + do { if ((id) != 0) glDeleteProgram((id)); } while(0) + + if (custom == NULL) return; + + if (custom->data.uniforms.bufferId != 0) { + glDeleteBuffers(1, &custom->data.uniforms.bufferId); + } + + if (custom->programOwner) { + DELETE_PROGRAM(custom->program->prepare.cubemapCustomSky.id); + DELETE_PROGRAM(custom->program->scene.geometry.id); + DELETE_PROGRAM(custom->program->scene.forward.id); + DELETE_PROGRAM(custom->program->scene.unlit.id); + DELETE_PROGRAM(custom->program->scene.depth.id); + DELETE_PROGRAM(custom->program->scene.depthCube.id); + DELETE_PROGRAM(custom->program->scene.probeForward.id); + DELETE_PROGRAM(custom->program->scene.probeUnlit.id); + DELETE_PROGRAM(custom->program->scene.decal.id); + DELETE_PROGRAM(custom->program->post.screen.id); + } + + RL_FREE(custom); + +#undef DELETE_PROGRAM +} + +void r3d_shader_custom_init_uniforms(r3d_shader_custom_t* custom, int currentOffset) +{ + r3d_rshade_uniform_buffer_t* uniforms = &custom->data.uniforms; + if (uniforms->entries[0].name[0] == '\0') return; + + int uboSize = r3d_align_offset(currentOffset, 16); + if (uboSize < 16) uboSize = 16; + + uniforms->bufferSize = uboSize; + uniforms->dirty = false; + + glGenBuffers(1, &uniforms->bufferId); + glBindBuffer(GL_UNIFORM_BUFFER, uniforms->bufferId); + glBufferData(GL_UNIFORM_BUFFER, uboSize, uniforms->buffer, GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +bool r3d_shader_custom_set_uniform(r3d_shader_custom_t* shader, const char* name, const void* value) +{ + assert(shader != NULL); + + for (int i = 0; i < R3D_MAX_SHADER_UNIFORMS && shader->data.uniforms.entries[i].name[0] != '\0'; i++) { + if (strcmp(shader->data.uniforms.entries[i].name, name) == 0) { + int offset = shader->data.uniforms.entries[i].offset; + int size = shader->data.uniforms.entries[i].size; + memcpy(shader->data.uniforms.buffer + offset, value, size); + shader->data.uniforms.dirty = true; + return true; + } + } + return false; +} + +bool r3d_shader_custom_set_sampler(r3d_shader_custom_t* shader, const char* name, Texture texture) +{ + assert(shader != NULL); + + for (int i = 0; i < R3D_MAX_SHADER_SAMPLERS && shader->data.samplers[i].name[0] != '\0'; i++) { + if (strcmp(shader->data.samplers[i].name, name) == 0) { + shader->data.samplers[i].texture = texture.id; + return true; + } + } + return false; +} + +void r3d_shader_custom_bind_uniforms(r3d_shader_custom_t* shader) +{ + assert(shader != NULL); + + if (shader->data.uniforms.bufferId == 0) return; + + if (shader->data.uniforms.dirty) { + glBindBuffer(GL_UNIFORM_BUFFER, shader->data.uniforms.bufferId); + glBufferSubData(GL_UNIFORM_BUFFER, 0, shader->data.uniforms.bufferSize, shader->data.uniforms.buffer); + shader->data.uniforms.dirty = false; + } + + if (R3D_MOD_SHADER.uniformBindings[R3D_SHADER_BLOCK_USER] != shader->data.uniforms.bufferId) { + glBindBufferBase(GL_UNIFORM_BUFFER, R3D_SHADER_BLOCK_SLOT_USER, shader->data.uniforms.bufferId); + R3D_MOD_SHADER.uniformBindings[R3D_SHADER_BLOCK_USER] = shader->data.uniforms.bufferId; + } +} + +void r3d_shader_custom_bind_samplers(r3d_shader_custom_t* shader) +{ + assert(shader != NULL); + + for (int i = 0; i < R3D_MAX_SHADER_SAMPLERS && shader->data.samplers[i].name[0] != '\0'; i++) + { + r3d_shader_sampler_t sampler = R3D_SHADER_SAMPLER_CUSTOM_2D; + + switch (shader->data.samplers[i].target) { + case GL_TEXTURE_1D: + sampler = R3D_SHADER_SAMPLER_CUSTOM_1D; + break; + case GL_TEXTURE_2D: + sampler = R3D_SHADER_SAMPLER_CUSTOM_2D; + break; + case GL_TEXTURE_3D: + sampler = R3D_SHADER_SAMPLER_CUSTOM_3D; + break; + case GL_TEXTURE_CUBE_MAP: + sampler = R3D_SHADER_SAMPLER_CUSTOM_CUBE; + break; + default: + assert(false); + break; + } + + r3d_shader_bind_sampler(sampler + i, shader->data.samplers[i].texture); + } +} + +void r3d_shader_invalidate_cache(void) +{ + // Disable current program shader + R3D_MOD_SHADER.currentProgram = 0; + glUseProgram(0); + + // Unbind all textures + for (int iSampler = 0; iSampler < R3D_SHADER_SAMPLER_COUNT; iSampler++) { + if (R3D_MOD_SHADER.samplerBindings[iSampler] != 0) { + glActiveTexture(GL_TEXTURE0 + iSampler); + glBindTexture(R3D_MOD_SHADER.samplerTargets[iSampler], 0); + R3D_MOD_SHADER.samplerBindings[iSampler] = 0; + } + } + glActiveTexture(GL_TEXTURE0); + + // Only reset current UBO binding state + memset(&R3D_MOD_SHADER.uniformBindings, 0, sizeof(R3D_MOD_SHADER.uniformBindings)); +} + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +char* inject_content(const char* source, const char* content, const char* marker, int mode) +{ + if (!source || !content || !marker) return NULL; + + // Find marker position + const char* markerPos = strstr(source, marker); + if (!markerPos) return NULL; + + size_t markerLen = strlen(marker); + size_t contentLen = strlen(content); + size_t sourceLen = strlen(source); + + // Calculate new string length + size_t prefixLen = markerPos - source; + size_t newLen; + + if (mode == 0) { + // Replace mode: remove marker + newLen = sourceLen - markerLen + contentLen; + } else { + // Before/after mode: keep marker + newLen = sourceLen + contentLen; + } + + // Allocate new string + char* result = RL_MALLOC(newLen + 1); + if (!result) return NULL; + + char* ptr = result; + + if (mode < 0) { + // Insert BEFORE marker: [prefix][content][marker][suffix] + memcpy(ptr, source, prefixLen); + ptr += prefixLen; + + memcpy(ptr, content, contentLen); + ptr += contentLen; + + memcpy(ptr, markerPos, sourceLen - prefixLen); + ptr += sourceLen - prefixLen; + } + else if (mode == 0) { + // REPLACE marker: [prefix][content][suffix] + memcpy(ptr, source, prefixLen); + ptr += prefixLen; + + memcpy(ptr, content, contentLen); + ptr += contentLen; + + size_t suffixLen = sourceLen - prefixLen - markerLen; + memcpy(ptr, markerPos + markerLen, suffixLen); + ptr += suffixLen; + } + else { + // Insert AFTER marker: [prefix][marker][content][suffix] + size_t upToMarkerEnd = prefixLen + markerLen; + memcpy(ptr, source, upToMarkerEnd); + ptr += upToMarkerEnd; + + memcpy(ptr, content, contentLen); + ptr += contentLen; + + size_t suffixLen = sourceLen - upToMarkerEnd; + memcpy(ptr, markerPos + markerLen, suffixLen); + ptr += suffixLen; + } + + *ptr = '\0'; + return result; +} + +char* inject_defines(const char* code, const char* defines[], int count) +{ + if (!code || count < 0) return NULL; + + // Find the end of the #version line + const char* versionStart = strstr(code, "#version"); + assert(versionStart && "Shader must have version"); + + const char* versionEnd = strchr(versionStart, '\n'); + if (!versionEnd) versionEnd = versionStart + strlen(versionStart); + else versionEnd++; // Include the \n + + // Calculate sizes + static const char DEFINE_PREFIX[] = "#define "; + static const size_t DEFINE_PREFIX_LEN = sizeof(DEFINE_PREFIX) - 1; // -1 to exclude '\0' + + size_t prefixLen = versionEnd - code; + size_t definesLen = 0; + for (int i = 0; i < count; i++) { + if (defines[i]) { + definesLen += DEFINE_PREFIX_LEN + strlen(defines[i]) + 1; // +1 for \n + } + } + size_t suffixLen = strlen(versionEnd); + + // Allocate and build the new shader + char* newShader = (char*)RL_MALLOC(prefixLen + definesLen + suffixLen + 1); + if (!newShader) return NULL; + + char* dest = newShader; + + // Copy the part before defines (up to after #version) + memcpy(dest, code, prefixLen); + dest += prefixLen; + + // Add the defines + for (int i = 0; i < count; i++) { + if (defines[i]) { + memcpy(dest, DEFINE_PREFIX, DEFINE_PREFIX_LEN); + dest += DEFINE_PREFIX_LEN; + + size_t defineLen = strlen(defines[i]); + memcpy(dest, defines[i], defineLen); + dest += defineLen; + + *dest++ = '\n'; + } + } + + // Copy the rest of the shader + memcpy(dest, versionEnd, suffixLen); + dest[suffixLen] = '\0'; + + return newShader; +} + +void inject_user_code(char** vsCode, char** fsCode, const char* userCode) +{ + if (strstr(userCode, "void vertex()") != NULL) { + char* vsUser = inject_content(*vsCode, userCode, "#define vertex()", 0); + RL_FREE(*vsCode); + *vsCode = vsUser; + } + + if (strstr(userCode, "void fragment()")) { + char* fsUser = inject_content(*fsCode, userCode, "#define fragment()", 0); + RL_FREE(*fsCode); + *fsCode = fsUser; + } +} + +void set_custom_samplers(GLuint id, r3d_shader_custom_t* custom) +{ + for (int i = 0; i < R3D_MAX_SHADER_SAMPLERS && custom->data.samplers[i].name[0] != '\0'; i++) + { + r3d_shader_sampler_t sampler = R3D_SHADER_SAMPLER_CUSTOM_2D; + + switch (custom->data.samplers[i].target) { + case GL_TEXTURE_1D: + sampler = R3D_SHADER_SAMPLER_CUSTOM_1D; + break; + case GL_TEXTURE_2D: + sampler = R3D_SHADER_SAMPLER_CUSTOM_2D; + break; + case GL_TEXTURE_3D: + sampler = R3D_SHADER_SAMPLER_CUSTOM_3D; + break; + case GL_TEXTURE_CUBE_MAP: + sampler = R3D_SHADER_SAMPLER_CUSTOM_CUBE; + break; + default: + assert(false); + break; + } + + GLint loc = glGetUniformLocation(id, custom->data.samplers[i].name); + glUniform1i(loc, sampler + i); + } +} + +``` + +`src/modules/r3d_shader.h`: + +```h +/* r3d_shader.h -- Internal R3D shader module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_MODULE_SHADER_H +#define R3D_MODULE_SHADER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "../common/r3d_rshade.h" +#include "../common/r3d_math.h" + +// ======================================== +// SHADER MANAGEMENT MACROS +// ======================================== + +#define R3D_SHADER_GET(shader_name, custom) \ + (((custom) != NULL) \ + ? &(custom)->program->shader_name \ + : &R3D_MOD_SHADER.shader_name) + +#define R3D_SHADER_USE(shader_name) do { \ + if (R3D_MOD_SHADER.shader_name.id == 0) { \ + bool ok = R3D_MOD_SHADER_LOADER.shader_name(NULL); \ + assert(ok); \ + } \ + if (R3D_MOD_SHADER.currentProgram != R3D_MOD_SHADER.shader_name.id) { \ + R3D_MOD_SHADER.currentProgram = R3D_MOD_SHADER.shader_name.id; \ + glUseProgram(R3D_MOD_SHADER.shader_name.id); \ + } \ +} while(0) + +#define R3D_SHADER_USE_CUSTOM(custom, shader_name) do { \ + assert((custom) != NULL); \ + if ((custom)->program->shader_name.id == 0) { \ + bool ok = R3D_MOD_SHADER_LOADER.shader_name(custom); \ + assert(ok); \ + } \ + if (R3D_MOD_SHADER.currentProgram != (custom)->program->shader_name.id) { \ + R3D_MOD_SHADER.currentProgram = (custom)->program->shader_name.id; \ + glUseProgram((custom)->program->shader_name.id); \ + } \ + r3d_shader_custom_bind_samplers(custom); \ + r3d_shader_custom_bind_uniforms(custom); \ +} while(0) + +#define R3D_SHADER_USE_SELECT(shader_name, custom) do { \ + if ((custom) == NULL) R3D_SHADER_USE(shader_name); \ + else R3D_SHADER_USE_CUSTOM(custom, shader_name); \ +} while(0) + +#define R3D_SHADER_BIND_SAMPLER(shader_name, uniform, texId) do { \ + r3d_shader_bind_sampler(R3D_MOD_SHADER.shader_name.uniform.slot, (texId)); \ +} while(0) + +#define R3D_SHADER_BIND_SAMPLER_CUSTOM(custom, shader_name, uniform, texId) do {\ + r3d_shader_bind_sampler((custom)->program->shader_name.uniform.slot, (texId));\ +} while(0) + +#define R3D_SHADER_BIND_SAMPLER_SELECT(shader_name, custom, uniform, texId) do {\ + r3d_shader_bind_sampler(((custom) != NULL) \ + ? (custom)->program->shader_name.uniform.slot \ + : R3D_MOD_SHADER.shader_name.uniform.slot, \ + (texId)); \ +} while(0) + +#define R3D_SHADER_SET_INT(shader_name, uniform, value) do { \ + if (R3D_MOD_SHADER.shader_name.uniform.val != (value)) { \ + R3D_MOD_SHADER.shader_name.uniform.val = (value); \ + glUniform1i( \ + R3D_MOD_SHADER.shader_name.uniform.loc, \ + (value) \ + ); \ + } \ +} while(0) + +#define R3D_SHADER_SET_INT_CUSTOM(custom, shader_name, uniform, value) do { \ + if ((custom)->program->shader_name.uniform.val != (value)) { \ + (custom)->program->shader_name.uniform.val = (value); \ + glUniform1i((custom)->program->shader_name.uniform.loc, (value)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_INT_SELECT(shader_name, custom, uniform, value) do { \ + if (R3D_SHADER_GET(shader_name, custom)->uniform.val != (value)) { \ + R3D_SHADER_GET(shader_name, custom)->uniform.val = (value); \ + glUniform1i(R3D_SHADER_GET(shader_name, custom)->uniform.loc, (value)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_FLOAT(shader_name, uniform, value) do { \ + if (R3D_MOD_SHADER.shader_name.uniform.val != (value)) { \ + R3D_MOD_SHADER.shader_name.uniform.val = (value); \ + glUniform1f( \ + R3D_MOD_SHADER.shader_name.uniform.loc, \ + (value) \ + ); \ + } \ +} while(0) + +#define R3D_SHADER_SET_FLOAT_CUSTOM(custom, shader_name, uniform, value) do { \ + if ((custom)->program->shader_name.uniform.val != (value)) { \ + (custom)->program->shader_name.uniform.val = (value); \ + glUniform1f((custom)->program->shader_name.uniform.loc, (value)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_FLOAT_SELECT(shader_name, custom, uniform, value) do { \ + if (R3D_SHADER_GET(shader_name, custom)->uniform.val != (value)) { \ + R3D_SHADER_GET(shader_name, custom)->uniform.val = (value); \ + glUniform1f(R3D_SHADER_GET(shader_name, custom)->uniform.loc, (value)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_VEC2(shader_name, uniform, ...) do { \ + const Vector2 tmp = (__VA_ARGS__); \ + if (!Vector2Equals(R3D_MOD_SHADER.shader_name.uniform.val, tmp)) { \ + R3D_MOD_SHADER.shader_name.uniform.val = tmp; \ + glUniform2fv( \ + R3D_MOD_SHADER.shader_name.uniform.loc, \ + 1, \ + (float*)(&tmp) \ + ); \ + } \ +} while(0) + +#define R3D_SHADER_SET_VEC2_CUSTOM(custom, shader_name, uniform, ...) do { \ + const Vector2 tmp = (__VA_ARGS__); \ + if (!Vector2Equals((custom)->program->shader_name.uniform.val, tmp)) { \ + (custom)->program->shader_name.uniform.val = tmp; \ + glUniform2fv((custom)->program->shader_name.uniform.loc, 1, (float*)(&tmp)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_VEC2_SELECT(shader_name, custom, uniform, ...) do { \ + const Vector2 tmp = (__VA_ARGS__); \ + if (!Vector2Equals(R3D_SHADER_GET(shader_name, custom)->uniform.val, tmp)) {\ + R3D_SHADER_GET(shader_name, custom)->uniform.val = tmp; \ + glUniform2fv(R3D_SHADER_GET(shader_name, custom)->uniform.loc, 1, (float*)(&tmp)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_VEC3(shader_name, uniform, ...) do { \ + const Vector3 tmp = (__VA_ARGS__); \ + if (!Vector3Equals(R3D_MOD_SHADER.shader_name.uniform.val, tmp)) { \ + R3D_MOD_SHADER.shader_name.uniform.val = tmp; \ + glUniform3fv( \ + R3D_MOD_SHADER.shader_name.uniform.loc, \ + 1, \ + (float*)(&tmp) \ + ); \ + } \ +} while(0) + +#define R3D_SHADER_SET_VEC3_CUSTOM(custom, shader_name, uniform, ...) do { \ + const Vector3 tmp = (__VA_ARGS__); \ + if (!Vector3Equals((custom)->program->shader_name.uniform.val, tmp)) { \ + (custom)->program->shader_name.uniform.val = tmp; \ + glUniform3fv((custom)->program->shader_name.uniform.loc, 1, (float*)(&tmp)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_VEC3_SELECT(shader_name, custom, uniform, ...) do { \ + const Vector3 tmp = (__VA_ARGS__); \ + if (!Vector3Equals(R3D_SHADER_GET(shader_name, custom)->uniform.val, tmp)) {\ + R3D_SHADER_GET(shader_name, custom)->uniform.val = tmp; \ + glUniform3fv(R3D_SHADER_GET(shader_name, custom)->uniform.loc, 1, (float*)(&tmp)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_VEC4(shader_name, uniform, ...) do { \ + const Vector4 tmp = (__VA_ARGS__); \ + if (!Vector4Equals(R3D_MOD_SHADER.shader_name.uniform.val, tmp)) { \ + R3D_MOD_SHADER.shader_name.uniform.val = tmp; \ + glUniform4fv( \ + R3D_MOD_SHADER.shader_name.uniform.loc, \ + 1, \ + (float*)(&tmp) \ + ); \ + } \ +} while(0) + +#define R3D_SHADER_SET_VEC4_CUSTOM(custom, shader_name, uniform, ...) do { \ + const Vector4 tmp = (__VA_ARGS__); \ + if (!Vector4Equals((custom)->program->shader_name.uniform.val, tmp)) { \ + (custom)->program->shader_name.uniform.val = tmp; \ + glUniform4fv((custom)->program->shader_name.uniform.loc, 1, (float*)(&tmp)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_VEC4_SELECT(shader_name, custom, uniform, ...) do { \ + const Vector4 tmp = (__VA_ARGS__); \ + if (!Vector4Equals(R3D_SHADER_GET(shader_name, custom)->uniform.val, tmp)) {\ + R3D_SHADER_GET(shader_name, custom)->uniform.val = tmp; \ + glUniform4fv(R3D_SHADER_GET(shader_name, custom)->uniform.loc, 1, (float*)(&tmp)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_COL3(shader_name, uniform, space, ...) do { \ + const Color tmp = (__VA_ARGS__); \ + if (R3D_MOD_SHADER.shader_name.uniform.colorSpace != (space) || \ + memcmp(&R3D_MOD_SHADER.shader_name.uniform.val, &tmp, \ + sizeof(Color)) != 0) { \ + Vector3 v = r3d_color_to_linear_vec3(tmp, (space)); \ + R3D_MOD_SHADER.shader_name.uniform.val = tmp; \ + R3D_MOD_SHADER.shader_name.uniform.colorSpace = (space); \ + glUniform3fv( \ + R3D_MOD_SHADER.shader_name.uniform.loc, \ + 1, \ + (float*)(&v) \ + ); \ + } \ +} while(0) + +#define R3D_SHADER_SET_COL3_CUSTOM(custom, shader_name, uniform, space, ...) do { \ + const Color tmp = (__VA_ARGS__); \ + if ((custom)->program->shader_name.uniform.colorSpace != (space) || \ + memcmp(&(custom)->program->shader_name.uniform.val, &tmp, sizeof(Color)) != 0) { \ + Vector3 v = r3d_color_to_linear_vec3(tmp, (space)); \ + (custom)->program->shader_name.uniform.val = tmp; \ + (custom)->program->shader_name.uniform.colorSpace = (space); \ + glUniform3fv((custom)->program->shader_name.uniform.loc, 1, (float*)(&v)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_COL3_SELECT(shader_name, custom, uniform, space, ...) do { \ + const Color tmp = (__VA_ARGS__); \ + if (R3D_SHADER_GET(shader_name, custom)->uniform.colorSpace != (space) || \ + memcmp(&R3D_SHADER_GET(shader_name, custom)->uniform.val, &tmp, sizeof(Color)) != 0) { \ + Vector3 v = r3d_color_to_linear_vec3(tmp, (space)); \ + R3D_SHADER_GET(shader_name, custom)->uniform.val = tmp; \ + R3D_SHADER_GET(shader_name, custom)->uniform.colorSpace = (space); \ + glUniform3fv(R3D_SHADER_GET(shader_name, custom)->uniform.loc, 1, (float*)(&v)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_COL4(shader_name, uniform, space, ...) do { \ + const Color tmp = (__VA_ARGS__); \ + if (R3D_MOD_SHADER.shader_name.uniform.colorSpace != (space) || \ + memcmp(&R3D_MOD_SHADER.shader_name.uniform.val, &tmp, \ + sizeof(Color)) != 0) { \ + Vector4 v = r3d_color_to_linear_vec4(tmp, (space)); \ + R3D_MOD_SHADER.shader_name.uniform.val = tmp; \ + R3D_MOD_SHADER.shader_name.uniform.colorSpace = (space); \ + glUniform4fv( \ + R3D_MOD_SHADER.shader_name.uniform.loc, \ + 1, \ + (float*)(&v) \ + ); \ + } \ +} while(0) + +#define R3D_SHADER_SET_COL4_CUSTOM(custom, shader_name, uniform, space, ...) do { \ + const Color tmp = (__VA_ARGS__); \ + if ((custom)->program->shader_name.uniform.colorSpace != (space) || \ + memcmp(&(custom)->program->shader_name.uniform.val, &tmp, sizeof(Color)) != 0) { \ + Vector4 v = r3d_color_to_linear_vec4(tmp, (space)); \ + (custom)->program->shader_name.uniform.val = tmp; \ + (custom)->program->shader_name.uniform.colorSpace = (space); \ + glUniform4fv((custom)->program->shader_name.uniform.loc, 1, (float*)(&v)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_COL4_SELECT(shader_name, custom, uniform, space, ...) do { \ + const Color tmp = (__VA_ARGS__); \ + if (R3D_SHADER_GET(shader_name, custom)->uniform.colorSpace != (space) || \ + memcmp(&R3D_SHADER_GET(shader_name, custom)->uniform.val, &tmp, sizeof(Color)) != 0) { \ + Vector4 v = r3d_color_to_linear_vec4(tmp, (space)); \ + R3D_SHADER_GET(shader_name, custom)->uniform.val = tmp; \ + R3D_SHADER_GET(shader_name, custom)->uniform.colorSpace = (space); \ + glUniform4fv(R3D_SHADER_GET(shader_name, custom)->uniform.loc, 1, (float*)(&v)); \ + } \ +} while(0) + +#define R3D_SHADER_SET_MAT4(shader_name, uniform, value) do { \ + glUniformMatrix4fv( \ + R3D_MOD_SHADER.shader_name.uniform.loc, \ + 1, \ + GL_TRUE, \ + (float*)(&(value)) \ + ); \ +} while(0) + +#define R3D_SHADER_SET_MAT4_CUSTOM(custom, shader_name, uniform, value) do { \ + glUniformMatrix4fv( \ + (custom)->program->shader_name.uniform.loc, \ + 1, GL_TRUE, (float*)(&(value)) \ + ); \ +} while(0) + +#define R3D_SHADER_SET_MAT4_SELECT(shader_name, custom, uniform, value) do { \ + glUniformMatrix4fv( \ + R3D_SHADER_GET(shader_name, custom)->uniform.loc, \ + 1, \ + GL_TRUE, \ + (float*)(&(value)) \ + ); \ +} while(0) + +// ======================================== +// SAMPLER ENUMS +// ======================================== + +/* + * Slot '0' is reserved for texture operations exclusively on the client side. + * Note that the specification guarantees at least 80 binding slots for textures. + * See: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glActiveTexture.xhtml + */ +typedef enum { + + // Material maps + R3D_SHADER_SAMPLER_MAP_ALBEDO = 1, + R3D_SHADER_SAMPLER_MAP_EMISSION = 2, + R3D_SHADER_SAMPLER_MAP_NORMAL = 3, + R3D_SHADER_SAMPLER_MAP_ORM = 4, + + // Shadow maps + R3D_SHADER_SAMPLER_SHADOW_DIR = 10, + R3D_SHADER_SAMPLER_SHADOW_SPOT = 11, + R3D_SHADER_SAMPLER_SHADOW_OMNI = 12, + + // IBL maps + R3D_SHADER_SAMPLER_IBL_IRRADIANCE = 13, + R3D_SHADER_SAMPLER_IBL_PREFILTER = 14, + R3D_SHADER_SAMPLER_IBL_BRDF_LUT = 15, + + // Scene miscs + R3D_SHADER_SAMPLER_BONE_MATRICES = 16, + + // Buffers + R3D_SHADER_SAMPLER_BUFFER_DEPTH = 20, + R3D_SHADER_SAMPLER_BUFFER_GEOM_NORMAL = 21, + R3D_SHADER_SAMPLER_BUFFER_ALBEDO = 22, + R3D_SHADER_SAMPLER_BUFFER_NORMAL = 23, + R3D_SHADER_SAMPLER_BUFFER_ORM = 24, + R3D_SHADER_SAMPLER_BUFFER_DIFFUSE = 25, + R3D_SHADER_SAMPLER_BUFFER_SPECULAR = 26, + R3D_SHADER_SAMPLER_BUFFER_SELECTOR = 27, + R3D_SHADER_SAMPLER_BUFFER_SSAO = 28, + R3D_SHADER_SAMPLER_BUFFER_SSIL = 29, + R3D_SHADER_SAMPLER_BUFFER_SSGI = 30, + R3D_SHADER_SAMPLER_BUFFER_SSR = 31, + R3D_SHADER_SAMPLER_BUFFER_DOF_COC = 32, + R3D_SHADER_SAMPLER_BUFFER_DOF = 33, + R3D_SHADER_SAMPLER_BUFFER_BLOOM = 34, + R3D_SHADER_SAMPLER_BUFFER_LUMINANCE = 35, + R3D_SHADER_SAMPLER_BUFFER_EXPOSURE = 36, + R3D_SHADER_SAMPLER_BUFFER_SMAA_EDGES = 37, + R3D_SHADER_SAMPLER_BUFFER_SMAA_BLEND = 38, + R3D_SHADER_SAMPLER_BUFFER_SCENE = 39, + + // Unamed for special passes + R3D_SHADER_SAMPLER_SOURCE_1D_0 = 40, + R3D_SHADER_SAMPLER_SOURCE_2D_0 = 41, + R3D_SHADER_SAMPLER_SOURCE_2D_1 = 42, + R3D_SHADER_SAMPLER_SOURCE_CUBE_0 = 43, + + // Custom samplers + R3D_SHADER_SAMPLER_CUSTOM_1D = 45, + R3D_SHADER_SAMPLER_CUSTOM_2D = R3D_SHADER_SAMPLER_CUSTOM_1D + R3D_MAX_SHADER_SAMPLERS, + R3D_SHADER_SAMPLER_CUSTOM_3D = R3D_SHADER_SAMPLER_CUSTOM_2D + R3D_MAX_SHADER_SAMPLERS, + R3D_SHADER_SAMPLER_CUSTOM_CUBE = R3D_SHADER_SAMPLER_CUSTOM_3D + R3D_MAX_SHADER_SAMPLERS, + R3D_SHADER_SAMPLER_CUSTOM_SENTINEL = R3D_SHADER_SAMPLER_CUSTOM_CUBE + R3D_MAX_SHADER_SAMPLERS, + + // Sentinel + R3D_SHADER_SAMPLER_COUNT = R3D_SHADER_SAMPLER_CUSTOM_SENTINEL + +} r3d_shader_sampler_t; + +// ======================================== +// SAMPLER TYPES +// ======================================== + +static const GLenum R3D_MOD_SHADER_SAMPLER_TYPES[R3D_SHADER_SAMPLER_COUNT] = +{ + [R3D_SHADER_SAMPLER_MAP_ALBEDO] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_MAP_EMISSION] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_MAP_NORMAL] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_MAP_ORM] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_SHADOW_DIR] = GL_TEXTURE_2D_ARRAY, + [R3D_SHADER_SAMPLER_SHADOW_SPOT] = GL_TEXTURE_2D_ARRAY, + [R3D_SHADER_SAMPLER_SHADOW_OMNI] = GL_TEXTURE_CUBE_MAP_ARRAY, + [R3D_SHADER_SAMPLER_IBL_IRRADIANCE] = GL_TEXTURE_CUBE_MAP_ARRAY, + [R3D_SHADER_SAMPLER_IBL_PREFILTER] = GL_TEXTURE_CUBE_MAP_ARRAY, + [R3D_SHADER_SAMPLER_IBL_BRDF_LUT] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BONE_MATRICES] = GL_TEXTURE_1D, + [R3D_SHADER_SAMPLER_BUFFER_DEPTH] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_ALBEDO] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_NORMAL] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_ORM] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_DIFFUSE] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_SPECULAR] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_SELECTOR] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_GEOM_NORMAL] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_SSAO] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_SSIL] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_SSGI] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_SSR] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_DOF_COC] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_DOF] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_BLOOM] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_LUMINANCE] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_EXPOSURE] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_SMAA_EDGES] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_SMAA_BLEND] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_BUFFER_SCENE] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_SOURCE_1D_0] = GL_TEXTURE_1D, + [R3D_SHADER_SAMPLER_SOURCE_2D_0] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_SOURCE_2D_1] = GL_TEXTURE_2D, + [R3D_SHADER_SAMPLER_SOURCE_CUBE_0] = GL_TEXTURE_CUBE_MAP, + + // NOTE: Custom samplers are defined at initialization + // time in `R3D_MOD_SHADER.samplerTargets` +}; + +// ======================================== +// UNIFORMS TYPES +// ======================================== + +/* Represents any sampler type, stores only the corresponding texture slot */ +typedef struct { int slot; } r3d_shader_uniform_sampler_t; + +/* Represents scalars (bool/int and float), stores the value and uniform location */ +typedef struct { int val; int loc; } r3d_shader_uniform_int_t; +typedef struct { float val; int loc; } r3d_shader_uniform_float_t; + +/* Represents vectors, stores the value and uniform location */ +typedef struct { Vector2 val; int loc; } r3d_shader_uniform_vec2_t; +typedef struct { Vector3 val; int loc; } r3d_shader_uniform_vec3_t; +typedef struct { Vector4 val; int loc; } r3d_shader_uniform_vec4_t; + +/* Represents SDR color vectors plus the color space they should be interpreted in */ +typedef struct { Color val; R3D_ColorSpace colorSpace; int loc; } r3d_shader_uniform_col3_t; +typedef struct { Color val; R3D_ColorSpace colorSpace; int loc; } r3d_shader_uniform_col4_t; + +/* Represents matrices, stores only the uniform location for efficiency */ +typedef struct { int loc; } r3d_shader_uniform_mat4_t; + +// ======================================== +// UNIFORM BLOCK ENUM / SLOTS +// ======================================== + +typedef enum { + R3D_SHADER_BLOCK_FRAME, + R3D_SHADER_BLOCK_VIEW, + R3D_SHADER_BLOCK_ENV, + R3D_SHADER_BLOCK_LIGHT, + R3D_SHADER_BLOCK_LIGHT_ARRAY, + R3D_SHADER_BLOCK_FOG, + R3D_SHADER_BLOCK_COUNT, + R3D_SHADER_BLOCK_USER = R3D_SHADER_BLOCK_COUNT, + R3D_SHADER_BLOCK_SLOT_COUNT, +} r3d_shader_block_t; + +#define R3D_SHADER_BLOCK_SLOT_FRAME 0 +#define R3D_SHADER_BLOCK_SLOT_VIEW 1 +#define R3D_SHADER_BLOCK_SLOT_ENV 2 +#define R3D_SHADER_BLOCK_SLOT_LIGHT 3 +#define R3D_SHADER_BLOCK_SLOT_LIGHT_ARRAY 4 +#define R3D_SHADER_BLOCK_SLOT_FOG 5 +#define R3D_SHADER_BLOCK_SLOT_USER 6 + +// ======================================== +// UNIFORM BLOCK STRUCTS +// ======================================== + +typedef struct { + alignas(8) Vector2 screenSize; + alignas(8) Vector2 texelSize; + alignas(4) float time; + alignas(4) int32_t index; + alignas(4) int32_t pad0; +} r3d_shader_block_frame_t; + +typedef struct { + alignas(16) Vector3 position; + alignas(16) Matrix view; + alignas(16) Matrix invView; + alignas(16) Matrix proj; + alignas(16) Matrix invProj; + alignas(16) Matrix viewProj; + alignas(4) int32_t projMode; + alignas(4) float aspect; + alignas(4) float near; + alignas(4) float far; +} r3d_shader_block_view_t; + +typedef struct { + + struct r3d_shader_block_env_probe + { + alignas(16) Vector3 position; + alignas(4) float falloff; + alignas(4) float range; + alignas(4) int32_t irradiance; + alignas(4) int32_t prefilter; + } + uProbes[R3D_MAX_PROBE_ON_SCREEN]; + + struct r3d_shader_block_env_ambient + { + alignas(16) Vector4 rotation; + alignas(16) Vector4 color; + alignas(4) float energy; + alignas(4) int32_t irradiance; + alignas(4) int32_t prefilter; + } + uAmbient; + + alignas(4) int32_t uNumPrefilterLevels; + alignas(4) int32_t uNumProbes; + +} r3d_shader_block_env_t; + +typedef struct { + alignas(16) Matrix viewProj; + alignas(16) Vector3 color; + alignas(16) Vector3 position; + alignas(16) Vector3 direction; + alignas(4) float specular; + alignas(4) float energy; + alignas(4) float range; + alignas(4) float near; + alignas(4) float far; + alignas(4) float attenuation; + alignas(4) float innerCutOff; + alignas(4) float outerCutOff; + alignas(4) float shadowSoftness; + alignas(4) float shadowOpacity; + alignas(4) float shadowDepthBias; + alignas(4) float shadowSlopeBias; + alignas(4) int32_t shadowLayer; + alignas(4) int32_t type; +} r3d_shader_block_light_t; + +typedef struct { + alignas(16) r3d_shader_block_light_t uLights[R3D_MAX_LIGHT_FORWARD_PER_MESH]; + alignas(4) int32_t uNumLights; +} r3d_shader_block_light_array_t; + +typedef struct { + alignas(16) Vector3 color; + alignas(4) float start; + alignas(4) float end; + alignas(4) float density; + alignas(4) float skyAffect; + alignas(4) int32_t mode; +} r3d_shader_block_fog_t; + +// ======================================== +// UNIFORM BLOCK SIZES AND SLOTS +// ======================================== + +static const int R3D_SHADER_BLOCK_SIZES[R3D_SHADER_BLOCK_COUNT] = { + [R3D_SHADER_BLOCK_FRAME] = sizeof(r3d_shader_block_frame_t), + [R3D_SHADER_BLOCK_VIEW] = sizeof(r3d_shader_block_view_t), + [R3D_SHADER_BLOCK_ENV] = sizeof(r3d_shader_block_env_t), + [R3D_SHADER_BLOCK_LIGHT] = sizeof(r3d_shader_block_light_t), + [R3D_SHADER_BLOCK_LIGHT_ARRAY] = sizeof(r3d_shader_block_light_array_t), + [R3D_SHADER_BLOCK_FOG] = sizeof(r3d_shader_block_fog_t), +}; + +static const int R3D_SHADER_BLOCK_SLOTS[R3D_SHADER_BLOCK_COUNT] = { + [R3D_SHADER_BLOCK_FRAME] = R3D_SHADER_BLOCK_SLOT_FRAME, + [R3D_SHADER_BLOCK_VIEW] = R3D_SHADER_BLOCK_SLOT_VIEW, + [R3D_SHADER_BLOCK_ENV] = R3D_SHADER_BLOCK_SLOT_ENV, + [R3D_SHADER_BLOCK_LIGHT] = R3D_SHADER_BLOCK_SLOT_LIGHT, + [R3D_SHADER_BLOCK_LIGHT_ARRAY] = R3D_SHADER_BLOCK_SLOT_LIGHT_ARRAY, + [R3D_SHADER_BLOCK_FOG] = R3D_SHADER_BLOCK_SLOT_FOG, +}; + +// ======================================== +// BUILT-IN SHADERS STRUCTURES +// ======================================== + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSourceTex; + r3d_shader_uniform_sampler_t uNormalTex; + r3d_shader_uniform_sampler_t uDepthTex; + r3d_shader_uniform_float_t uNormalSharpness; + r3d_shader_uniform_float_t uDepthSharpness; + r3d_shader_uniform_float_t uInvStepWidth2; + r3d_shader_uniform_int_t uStepWidth; +} r3d_shader_prepare_denoiser_atrous_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSourceTex; + r3d_shader_uniform_sampler_t uNormalTex; + r3d_shader_uniform_sampler_t uDepthTex; + r3d_shader_uniform_float_t uNormalSharpness; + r3d_shader_uniform_float_t uDepthSharpness; + r3d_shader_uniform_float_t uInvBlurRadius2; + r3d_shader_uniform_float_t uBlurRadius; +} r3d_shader_prepare_denoiser_sparse_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSourceTex; + r3d_shader_uniform_int_t uSourceLod; +} r3d_shader_prepare_blur_down_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSourceTex; + r3d_shader_uniform_int_t uSourceLod; +} r3d_shader_prepare_blur_up_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uDepthTex; +} r3d_shader_prepare_depth_pyramid_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSelectorTex; + r3d_shader_uniform_sampler_t uNormalTex; +} r3d_shader_prepare_ssao_in_down_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uNormalTex; + r3d_shader_uniform_sampler_t uDepthTex; + r3d_shader_uniform_int_t uSampleCount; + r3d_shader_uniform_float_t uRadius; + r3d_shader_uniform_float_t uBias; + r3d_shader_uniform_float_t uIntensity; + r3d_shader_uniform_float_t uMaxSSRadius; +} r3d_shader_prepare_ssao_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSelectorTex; + r3d_shader_uniform_sampler_t uDiffuseTex; + r3d_shader_uniform_sampler_t uNormalTex; +} r3d_shader_prepare_ssil_in_down_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uDiffuseTex; + r3d_shader_uniform_sampler_t uNormalTex; + r3d_shader_uniform_sampler_t uDepthTex; + r3d_shader_uniform_int_t uSampleCount; + r3d_shader_uniform_float_t uRadius; + r3d_shader_uniform_float_t uBias; + r3d_shader_uniform_float_t uAoIntensity; + r3d_shader_uniform_float_t uMaxSSRadius; +} r3d_shader_prepare_ssil_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSelectorTex; + r3d_shader_uniform_sampler_t uDiffuseTex; + r3d_shader_uniform_sampler_t uNormalTex; +} r3d_shader_prepare_ssgi_in_down_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uDiffuseTex; + r3d_shader_uniform_sampler_t uNormalTex; + r3d_shader_uniform_sampler_t uDepthTex; + r3d_shader_uniform_int_t uSliceCount; + r3d_shader_uniform_float_t uEdgeFade; + r3d_shader_uniform_float_t uDistanceFalloff; + r3d_shader_uniform_float_t uNormalRejection; +} r3d_shader_prepare_ssgi_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSelectorTex; + r3d_shader_uniform_sampler_t uDiffuseTex; + r3d_shader_uniform_sampler_t uSpecularTex; + r3d_shader_uniform_sampler_t uNormalTex; +} r3d_shader_prepare_ssr_in_down_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uDiffuseTex; + r3d_shader_uniform_sampler_t uSpecularTex; + r3d_shader_uniform_sampler_t uNormalTex; + r3d_shader_uniform_sampler_t uDepthTex; + r3d_shader_uniform_int_t uMaxRaySteps; + r3d_shader_uniform_int_t uBinarySteps; + r3d_shader_uniform_float_t uStepSize; + r3d_shader_uniform_float_t uThickness; + r3d_shader_uniform_float_t uMaxDistance; + r3d_shader_uniform_float_t uEdgeFade; +} r3d_shader_prepare_ssr_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uDepthTex; + r3d_shader_uniform_float_t uFocusPoint; + r3d_shader_uniform_float_t uFocusScale; + r3d_shader_uniform_float_t uNearScale; +} r3d_shader_prepare_dof_coc_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSceneTex; + r3d_shader_uniform_sampler_t uCoCTex; +} r3d_shader_prepare_dof_down_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSceneTex; + r3d_shader_uniform_sampler_t uDepthTex; + r3d_shader_uniform_float_t uMaxBlurSize; +} r3d_shader_prepare_dof_blur_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uTexture; + r3d_shader_uniform_vec2_t uTexelSize; + r3d_shader_uniform_vec4_t uPrefilter; + r3d_shader_uniform_int_t uDstLevel; +} r3d_shader_prepare_bloom_down_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uTexture; + r3d_shader_uniform_vec2_t uFilterRadius; + r3d_shader_uniform_float_t uSrcLevel; +} r3d_shader_prepare_bloom_up_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSourceTex; +} r3d_shader_prepare_luminance_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uMeasuredLogLumTex; + r3d_shader_uniform_sampler_t uPrevAutoExposureTex; + r3d_shader_uniform_float_t uDeltaTime; + r3d_shader_uniform_float_t uMinLogLum; + r3d_shader_uniform_float_t uMaxLogLum; + r3d_shader_uniform_float_t uSpeedUp; + r3d_shader_uniform_float_t uSpeedDown; + r3d_shader_uniform_float_t uExposureCompLog; +} r3d_shader_prepare_exposure_adapt_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSceneTex; +} r3d_shader_prepare_smaa_edge_detection_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uEdgesTex; + r3d_shader_uniform_sampler_t uAreaTex; + r3d_shader_uniform_sampler_t uSearchTex; +} r3d_shader_prepare_smaa_blending_weights_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_mat4_t uMatProj; + r3d_shader_uniform_mat4_t uMatView; + r3d_shader_uniform_sampler_t uPanoramaTex; +} r3d_shader_prepare_cubemap_from_equirectangular_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_mat4_t uMatProj; + r3d_shader_uniform_mat4_t uMatView; + r3d_shader_uniform_sampler_t uSourceTex; +} r3d_shader_prepare_cubemap_irradiance_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_mat4_t uMatProj; + r3d_shader_uniform_mat4_t uMatView; + r3d_shader_uniform_sampler_t uSourceTex; + r3d_shader_uniform_float_t uSourceNumLevels; + r3d_shader_uniform_float_t uSourceFaceSize; + r3d_shader_uniform_float_t uRoughness; +} r3d_shader_prepare_cubemap_prefilter_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_mat4_t uMatProj; + r3d_shader_uniform_mat4_t uMatView; + r3d_shader_uniform_col3_t uSkyTopColor; + r3d_shader_uniform_col3_t uSkyHorizonColor; + r3d_shader_uniform_float_t uSkyHorizonCurve; + r3d_shader_uniform_float_t uSkyEnergy; + r3d_shader_uniform_col3_t uGroundBottomColor; + r3d_shader_uniform_col3_t uGroundHorizonColor; + r3d_shader_uniform_float_t uGroundHorizonCurve; + r3d_shader_uniform_float_t uGroundEnergy; + r3d_shader_uniform_vec3_t uSunDirection; + r3d_shader_uniform_col3_t uSunColor; + r3d_shader_uniform_float_t uSunSize; + r3d_shader_uniform_float_t uSunCurve; + r3d_shader_uniform_float_t uSunEnergy; +} r3d_shader_prepare_cubemap_procedural_sky_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_mat4_t uMatProj; + r3d_shader_uniform_mat4_t uMatView; +} r3d_shader_prepare_cubemap_custom_sky_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uBoneMatricesTex; + r3d_shader_uniform_mat4_t uMatNormal; + r3d_shader_uniform_mat4_t uMatModel; + r3d_shader_uniform_col4_t uAlbedoColor; + r3d_shader_uniform_float_t uEmissionEnergy; + r3d_shader_uniform_col3_t uEmissionColor; + r3d_shader_uniform_vec2_t uTexCoordOffset; + r3d_shader_uniform_vec2_t uTexCoordScale; + r3d_shader_uniform_int_t uInstancing; + r3d_shader_uniform_int_t uSkinning; + r3d_shader_uniform_int_t uBillboard; + r3d_shader_uniform_sampler_t uAlbedoMap; + r3d_shader_uniform_sampler_t uNormalMap; + r3d_shader_uniform_sampler_t uEmissionMap; + r3d_shader_uniform_sampler_t uOrmMap; + r3d_shader_uniform_float_t uAlphaCutoff; + r3d_shader_uniform_float_t uNormalScale; + r3d_shader_uniform_float_t uOcclusion; + r3d_shader_uniform_float_t uRoughness; + r3d_shader_uniform_float_t uMetalness; + r3d_shader_uniform_float_t uSpecular; +} r3d_shader_scene_geometry_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uBoneMatricesTex; + r3d_shader_uniform_mat4_t uMatNormal; + r3d_shader_uniform_mat4_t uMatModel; + r3d_shader_uniform_col4_t uAlbedoColor; + r3d_shader_uniform_col3_t uEmissionColor; + r3d_shader_uniform_float_t uEmissionEnergy; + r3d_shader_uniform_vec2_t uTexCoordOffset; + r3d_shader_uniform_vec2_t uTexCoordScale; + r3d_shader_uniform_int_t uInstancing; + r3d_shader_uniform_int_t uSkinning; + r3d_shader_uniform_int_t uBillboard; + r3d_shader_uniform_sampler_t uAlbedoMap; + r3d_shader_uniform_sampler_t uEmissionMap; + r3d_shader_uniform_sampler_t uNormalMap; + r3d_shader_uniform_sampler_t uOrmMap; + r3d_shader_uniform_sampler_t uShadowDirTex; + r3d_shader_uniform_sampler_t uShadowSpotTex; + r3d_shader_uniform_sampler_t uShadowOmniTex; + r3d_shader_uniform_sampler_t uIrradianceTex; + r3d_shader_uniform_sampler_t uPrefilterTex; + r3d_shader_uniform_sampler_t uBrdfLutTex; + r3d_shader_uniform_float_t uNormalScale; + r3d_shader_uniform_float_t uOcclusion; + r3d_shader_uniform_float_t uRoughness; + r3d_shader_uniform_float_t uMetalness; + r3d_shader_uniform_float_t uSpecular; + r3d_shader_uniform_vec3_t uViewPosition; +} r3d_shader_scene_forward_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uBoneMatricesTex; + r3d_shader_uniform_mat4_t uMatModel; + r3d_shader_uniform_mat4_t uMatNormal; + r3d_shader_uniform_col4_t uAlbedoColor; + r3d_shader_uniform_vec2_t uTexCoordOffset; + r3d_shader_uniform_vec2_t uTexCoordScale; + r3d_shader_uniform_int_t uInstancing; + r3d_shader_uniform_int_t uSkinning; + r3d_shader_uniform_int_t uBillboard; + r3d_shader_uniform_sampler_t uAlbedoMap; + r3d_shader_uniform_float_t uAlphaCutoff; +} r3d_shader_scene_unlit_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uBoneMatricesTex; + r3d_shader_uniform_mat4_t uMatModel; + r3d_shader_uniform_mat4_t uMatInvView; + r3d_shader_uniform_mat4_t uMatViewProj; + r3d_shader_uniform_col4_t uAlbedoColor; + r3d_shader_uniform_vec2_t uTexCoordOffset; + r3d_shader_uniform_vec2_t uTexCoordScale; + r3d_shader_uniform_int_t uInstancing; + r3d_shader_uniform_int_t uSkinning; + r3d_shader_uniform_int_t uBillboard; + r3d_shader_uniform_sampler_t uAlbedoMap; + r3d_shader_uniform_float_t uAlphaCutoff; +} r3d_shader_scene_depth_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uBoneMatricesTex; + r3d_shader_uniform_mat4_t uMatModel; + r3d_shader_uniform_mat4_t uMatInvView; + r3d_shader_uniform_mat4_t uMatViewProj; + r3d_shader_uniform_col4_t uAlbedoColor; + r3d_shader_uniform_vec2_t uTexCoordOffset; + r3d_shader_uniform_vec2_t uTexCoordScale; + r3d_shader_uniform_int_t uInstancing; + r3d_shader_uniform_int_t uSkinning; + r3d_shader_uniform_int_t uBillboard; + r3d_shader_uniform_sampler_t uAlbedoMap; + r3d_shader_uniform_float_t uAlphaCutoff; + r3d_shader_uniform_vec3_t uViewPosition; + r3d_shader_uniform_float_t uFar; +} r3d_shader_scene_depth_cube_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uBoneMatricesTex; + r3d_shader_uniform_mat4_t uMatNormal; + r3d_shader_uniform_mat4_t uMatModel; + r3d_shader_uniform_mat4_t uMatView; + r3d_shader_uniform_mat4_t uMatInvView; + r3d_shader_uniform_mat4_t uMatViewProj; + r3d_shader_uniform_col4_t uAlbedoColor; + r3d_shader_uniform_col3_t uEmissionColor; + r3d_shader_uniform_float_t uEmissionEnergy; + r3d_shader_uniform_vec2_t uTexCoordOffset; + r3d_shader_uniform_vec2_t uTexCoordScale; + r3d_shader_uniform_int_t uInstancing; + r3d_shader_uniform_int_t uSkinning; + r3d_shader_uniform_int_t uBillboard; + r3d_shader_uniform_sampler_t uAlbedoMap; + r3d_shader_uniform_sampler_t uEmissionMap; + r3d_shader_uniform_sampler_t uNormalMap; + r3d_shader_uniform_sampler_t uOrmMap; + r3d_shader_uniform_sampler_t uShadowDirTex; + r3d_shader_uniform_sampler_t uShadowSpotTex; + r3d_shader_uniform_sampler_t uShadowOmniTex; + r3d_shader_uniform_sampler_t uIrradianceTex; + r3d_shader_uniform_sampler_t uPrefilterTex; + r3d_shader_uniform_sampler_t uBrdfLutTex; + r3d_shader_uniform_float_t uNormalScale; + r3d_shader_uniform_float_t uOcclusion; + r3d_shader_uniform_float_t uRoughness; + r3d_shader_uniform_float_t uMetalness; + r3d_shader_uniform_float_t uSpecular; + r3d_shader_uniform_vec3_t uViewPosition; + r3d_shader_uniform_int_t uProbeInterior; +} r3d_shader_scene_probe_forward_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uBoneMatricesTex; + r3d_shader_uniform_mat4_t uMatModel; + r3d_shader_uniform_mat4_t uMatNormal; + r3d_shader_uniform_mat4_t uMatView; + r3d_shader_uniform_mat4_t uMatInvView; + r3d_shader_uniform_mat4_t uMatViewProj; + r3d_shader_uniform_col4_t uAlbedoColor; + r3d_shader_uniform_vec2_t uTexCoordOffset; + r3d_shader_uniform_vec2_t uTexCoordScale; + r3d_shader_uniform_int_t uInstancing; + r3d_shader_uniform_int_t uSkinning; + r3d_shader_uniform_int_t uBillboard; + r3d_shader_uniform_sampler_t uAlbedoMap; + r3d_shader_uniform_float_t uAlphaCutoff; +} r3d_shader_scene_probe_unlit_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_mat4_t uMatNormal; + r3d_shader_uniform_mat4_t uMatModel; + r3d_shader_uniform_col4_t uAlbedoColor; + r3d_shader_uniform_float_t uEmissionEnergy; + r3d_shader_uniform_col3_t uEmissionColor; + r3d_shader_uniform_vec2_t uTexCoordOffset; + r3d_shader_uniform_vec2_t uTexCoordScale; + r3d_shader_uniform_int_t uInstancing; + r3d_shader_uniform_sampler_t uAlbedoMap; + r3d_shader_uniform_sampler_t uNormalMap; + r3d_shader_uniform_sampler_t uEmissionMap; + r3d_shader_uniform_sampler_t uOrmMap; + r3d_shader_uniform_sampler_t uDepthTex; + r3d_shader_uniform_sampler_t uGeomNormalTex; + r3d_shader_uniform_float_t uAlphaCutoff; + r3d_shader_uniform_float_t uNormalScale; + r3d_shader_uniform_float_t uOcclusion; + r3d_shader_uniform_float_t uRoughness; + r3d_shader_uniform_float_t uMetalness; + r3d_shader_uniform_float_t uSpecular; + r3d_shader_uniform_float_t uNormalThreshold; + r3d_shader_uniform_float_t uFadeWidth; + r3d_shader_uniform_int_t uApplyColor; +} r3d_shader_scene_decal_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_vec4_t uColor; +} r3d_shader_scene_background_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_mat4_t uMatInvView; + r3d_shader_uniform_mat4_t uMatInvProj; + r3d_shader_uniform_sampler_t uSkyMap; + r3d_shader_uniform_vec4_t uRotation; + r3d_shader_uniform_float_t uEnergy; + r3d_shader_uniform_float_t uLod; +} r3d_shader_scene_skybox_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uAlbedoTex; + r3d_shader_uniform_sampler_t uNormalTex; + r3d_shader_uniform_sampler_t uDepthTex; + r3d_shader_uniform_sampler_t uSsaoTex; + r3d_shader_uniform_sampler_t uSsilTex; + r3d_shader_uniform_sampler_t uSsgiTex; + r3d_shader_uniform_sampler_t uOrmTex; + r3d_shader_uniform_sampler_t uIrradianceTex; + r3d_shader_uniform_sampler_t uPrefilterTex; + r3d_shader_uniform_sampler_t uBrdfLutTex; + r3d_shader_uniform_float_t uSsaoPower; + r3d_shader_uniform_float_t uSsilAoPower; + r3d_shader_uniform_float_t uSsilIntensity; + r3d_shader_uniform_float_t uSsgiIntensity; +} r3d_shader_deferred_ambient_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uAlbedoTex; + r3d_shader_uniform_sampler_t uNormalTex; + r3d_shader_uniform_sampler_t uDepthTex; + r3d_shader_uniform_sampler_t uOrmTex; + r3d_shader_uniform_sampler_t uShadowDirTex; + r3d_shader_uniform_sampler_t uShadowSpotTex; + r3d_shader_uniform_sampler_t uShadowOmniTex; +} r3d_shader_deferred_lighting_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uAlbedoTex; + r3d_shader_uniform_sampler_t uDiffuseTex; + r3d_shader_uniform_sampler_t uSpecularTex; + r3d_shader_uniform_sampler_t uOrmTex; + r3d_shader_uniform_sampler_t uSsrTex; + r3d_shader_uniform_float_t uSsrNumLevels; +} r3d_shader_deferred_compose_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uDepthTex; +} r3d_shader_deferred_fog_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSceneTex; + r3d_shader_uniform_sampler_t uBlurTex; +} r3d_shader_post_dof_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSceneTex; + r3d_shader_uniform_sampler_t uBloomTex; + r3d_shader_uniform_int_t uBloomMode; + r3d_shader_uniform_float_t uBloomIntensity; +} r3d_shader_post_bloom_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSceneTex; + r3d_shader_uniform_sampler_t uExposureTex; +} r3d_shader_post_auto_exposure_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSceneTex; + r3d_shader_uniform_sampler_t uNormalTex; + r3d_shader_uniform_sampler_t uDepthTex; +} r3d_shader_post_screen_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSceneTex; + r3d_shader_uniform_float_t uTonemapExposure; + r3d_shader_uniform_float_t uTonemapWhite; + r3d_shader_uniform_int_t uTonemapMode; + r3d_shader_uniform_float_t uBrightness; + r3d_shader_uniform_float_t uContrast; + r3d_shader_uniform_float_t uSaturation; +} r3d_shader_post_output_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSceneTex; +} r3d_shader_post_fxaa_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSceneTex; + r3d_shader_uniform_sampler_t uBlendTex; +} r3d_shader_post_smaa_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSourceTex; + r3d_shader_uniform_int_t uOutputMode; +} r3d_shader_post_visualizer_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSourceTex; + r3d_shader_uniform_vec2_t uSourceTexel; +} r3d_shader_blit_up_bicubic_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSourceTex; + r3d_shader_uniform_vec2_t uSourceTexel; +} r3d_shader_blit_up_lanczos_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSourceTex; + r3d_shader_uniform_vec2_t uDestTexel; +} r3d_shader_blit_down_rgss_t; + +typedef struct { + GLuint id; + r3d_shader_uniform_sampler_t uSourceTex; + r3d_shader_uniform_vec2_t uDestTexel; +} r3d_shader_blit_down_pdss_t; + +// ======================================== +// CUSTOM SHADERS STRUCTURES +// ======================================== + +typedef struct { + union { + // Must follow the same naming pattern as `r3d_shader_loader` + struct { + r3d_shader_prepare_cubemap_custom_sky_t cubemapCustomSky; + } prepare; + // Must follow the same naming pattern as `r3d_mod_shader` + struct { + r3d_shader_scene_geometry_t geometry; + r3d_shader_scene_forward_t forward; + r3d_shader_scene_unlit_t unlit; + r3d_shader_scene_depth_t depth; + r3d_shader_scene_depth_cube_t depthCube; + r3d_shader_scene_probe_forward_t probeForward; + r3d_shader_scene_probe_unlit_t probeUnlit; + r3d_shader_scene_decal_t decal; + } scene; + // Must follow the same naming pattern as `r3d_shader_loader` + struct { + r3d_shader_post_screen_t screen; + } post; + }; + char userCode[R3D_MAX_SHADER_CODE_LENGTH]; +} r3d_shader_custom_program_t; + +typedef struct { + r3d_rshade_sampler_t samplers[R3D_MAX_SHADER_SAMPLERS]; + r3d_rshade_uniform_buffer_t uniforms; +} r3d_shader_custom_data_t; + +typedef struct R3D_ShaderCustom { + r3d_shader_custom_program_t* program; + r3d_shader_custom_data_t data; + bool programOwner; +} r3d_shader_custom_t; + +// ======================================== +// MODULE STATE +// ======================================== + +extern struct r3d_mod_shader { + + // Program state + GLuint currentProgram; + + // Samplers state + GLenum samplerTargets[R3D_SHADER_SAMPLER_COUNT]; + GLuint samplerBindings[R3D_SHADER_SAMPLER_COUNT]; + + // Uniform buffers + GLuint uniformBuffers[R3D_SHADER_BLOCK_COUNT]; + GLuint uniformBindings[R3D_SHADER_BLOCK_SLOT_COUNT]; + + // Prepare shaders + struct { + r3d_shader_prepare_denoiser_atrous_t denoiserAtrous; + r3d_shader_prepare_denoiser_sparse_t denoiserSparse; + r3d_shader_prepare_blur_down_t blurDown; + r3d_shader_prepare_blur_up_t blurUp; + r3d_shader_prepare_depth_pyramid_t depthPyramid; + r3d_shader_prepare_ssao_in_down_t ssaoInDown; + r3d_shader_prepare_ssao_t ssao; + r3d_shader_prepare_ssil_in_down_t ssilInDown; + r3d_shader_prepare_ssil_t ssil; + r3d_shader_prepare_ssgi_in_down_t ssgiInDown; + r3d_shader_prepare_ssgi_t ssgi; + r3d_shader_prepare_ssr_in_down_t ssrInDown; + r3d_shader_prepare_ssr_t ssr; + r3d_shader_prepare_dof_coc_t dofCoc; + r3d_shader_prepare_dof_down_t dofDown; + r3d_shader_prepare_dof_blur_t dofBlur; + r3d_shader_prepare_bloom_down_t bloomDown; + r3d_shader_prepare_bloom_up_t bloomUp; + r3d_shader_prepare_luminance_t luminance; + r3d_shader_prepare_exposure_adapt_t exposureAdapt; + r3d_shader_prepare_smaa_edge_detection_t smaaEdgeDetection[R3D_ANTI_ALIASING_PRESET_COUNT]; + r3d_shader_prepare_smaa_blending_weights_t smaaBlendingWeights[R3D_ANTI_ALIASING_PRESET_COUNT]; + r3d_shader_prepare_cubemap_from_equirectangular_t cubemapFromEquirectangular; + r3d_shader_prepare_cubemap_irradiance_t cubemapIrradiance; + r3d_shader_prepare_cubemap_prefilter_t cubemapPrefilter; + r3d_shader_prepare_cubemap_procedural_sky_t cubemapProceduralSky; + } prepare; + + // Scene shaders + struct { + r3d_shader_scene_geometry_t geometry; + r3d_shader_scene_forward_t forward; + r3d_shader_scene_unlit_t unlit; + r3d_shader_scene_background_t background; + r3d_shader_scene_skybox_t skybox; + r3d_shader_scene_depth_t depth; + r3d_shader_scene_depth_cube_t depthCube; + r3d_shader_scene_probe_forward_t probeForward; + r3d_shader_scene_probe_unlit_t probeUnlit; + r3d_shader_scene_decal_t decal; + } scene; + + // Deferred shaders + struct { + r3d_shader_deferred_ambient_t ambient; + r3d_shader_deferred_lighting_t lighting; + r3d_shader_deferred_compose_t compose; + r3d_shader_deferred_fog_t fog; + } deferred; + + // Post shaders + struct { + r3d_shader_post_dof_t dof; + r3d_shader_post_bloom_t bloom; + r3d_shader_post_auto_exposure_t autoExposure; + r3d_shader_post_output_t output; + r3d_shader_post_fxaa_t fxaa[R3D_ANTI_ALIASING_PRESET_COUNT]; + r3d_shader_post_smaa_t smaa[R3D_ANTI_ALIASING_PRESET_COUNT]; + r3d_shader_post_visualizer_t visualizer; + } post; + + // Blit shaders + struct { + r3d_shader_blit_up_bicubic_t upBicubic; + r3d_shader_blit_up_lanczos_t upLanczos; + r3d_shader_blit_down_rgss_t downRgss; + r3d_shader_blit_down_pdss_t downPdss; + } blit; + +} R3D_MOD_SHADER; + +// ======================================== +// BUILT-IN SHADER LOADER +// ======================================== + +typedef bool (*r3d_shader_loader_func)(r3d_shader_custom_t* custom); + +bool r3d_shader_load_prepare_denoiser_atrous(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_denoiser_sparse(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_blur_down(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_blur_up(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_depth_pyramid(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_ssao_in_down(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_ssao(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_ssil_in_down(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_ssil(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_ssgi_in_down(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_ssgi(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_ssr_in_down(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_ssr(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_dof_coc(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_dof_down(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_dof_blur(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_bloom_down(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_bloom_up(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_luminance(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_exposure_adapt(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_smaa_edge_detection_low(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_smaa_edge_detection_medium(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_smaa_edge_detection_high(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_smaa_edge_detection_ultra(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_smaa_blending_weights_low(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_smaa_blending_weights_medium(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_smaa_blending_weights_high(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_smaa_blending_weights_ultra(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_cubemap_from_equirectangular(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_cubemap_irradiance(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_cubemap_prefilter(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_cubemap_procedural_sky(r3d_shader_custom_t* custom); +bool r3d_shader_load_prepare_cubemap_custom_sky(r3d_shader_custom_t* custom); +bool r3d_shader_load_scene_geometry(r3d_shader_custom_t* custom); +bool r3d_shader_load_scene_forward(r3d_shader_custom_t* custom); +bool r3d_shader_load_scene_unlit(r3d_shader_custom_t* custom); +bool r3d_shader_load_scene_background(r3d_shader_custom_t* custom); +bool r3d_shader_load_scene_skybox(r3d_shader_custom_t* custom); +bool r3d_shader_load_scene_depth(r3d_shader_custom_t* custom); +bool r3d_shader_load_scene_depth_cube(r3d_shader_custom_t* custom); +bool r3d_shader_load_scene_probe_forward(r3d_shader_custom_t* custom); +bool r3d_shader_load_scene_probe_unlit(r3d_shader_custom_t* custom); +bool r3d_shader_load_scene_decal(r3d_shader_custom_t* custom); +bool r3d_shader_load_deferred_ambient(r3d_shader_custom_t* custom); +bool r3d_shader_load_deferred_lighting(r3d_shader_custom_t* custom); +bool r3d_shader_load_deferred_compose(r3d_shader_custom_t* custom); +bool r3d_shader_load_deferred_fog(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_dof(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_bloom(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_auto_exposure(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_screen(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_output(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_fxaa_low(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_fxaa_medium(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_fxaa_high(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_fxaa_ultra(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_smaa_low(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_smaa_medium(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_smaa_high(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_smaa_ultra(r3d_shader_custom_t* custom); +bool r3d_shader_load_post_visualizer(r3d_shader_custom_t* custom); +bool r3d_shader_load_blit_up_bicubic(r3d_shader_custom_t* custom); +bool r3d_shader_load_blit_up_lanczos(r3d_shader_custom_t* custom); +bool r3d_shader_load_blit_down_rgss(r3d_shader_custom_t* custom); +bool r3d_shader_load_blit_down_pdss(r3d_shader_custom_t* custom); + +static const struct r3d_shader_loader { + + // Prepare shaders + struct { + r3d_shader_loader_func denoiserAtrous; + r3d_shader_loader_func denoiserSparse; + r3d_shader_loader_func blurDown; + r3d_shader_loader_func blurUp; + r3d_shader_loader_func depthPyramid; + r3d_shader_loader_func ssaoInDown; + r3d_shader_loader_func ssao; + r3d_shader_loader_func ssaoBlur; + r3d_shader_loader_func ssilInDown; + r3d_shader_loader_func ssil; + r3d_shader_loader_func ssgiInDown; + r3d_shader_loader_func ssgi; + r3d_shader_loader_func ssrInDown; + r3d_shader_loader_func ssr; + r3d_shader_loader_func dofCoc; + r3d_shader_loader_func dofDown; + r3d_shader_loader_func dofBlur; + r3d_shader_loader_func bloomDown; + r3d_shader_loader_func bloomUp; + r3d_shader_loader_func luminance; + r3d_shader_loader_func exposureAdapt; + r3d_shader_loader_func smaaEdgeDetection[R3D_ANTI_ALIASING_PRESET_COUNT]; + r3d_shader_loader_func smaaBlendingWeights[R3D_ANTI_ALIASING_PRESET_COUNT]; + r3d_shader_loader_func cubemapFromEquirectangular; + r3d_shader_loader_func cubemapIrradiance; + r3d_shader_loader_func cubemapPrefilter; + r3d_shader_loader_func cubemapProceduralSky; + r3d_shader_loader_func cubemapCustomSky; + } prepare; + + // Scene shaders + struct { + r3d_shader_loader_func geometry; + r3d_shader_loader_func forward; + r3d_shader_loader_func unlit; + r3d_shader_loader_func background; + r3d_shader_loader_func skybox; + r3d_shader_loader_func depth; + r3d_shader_loader_func depthCube; + r3d_shader_loader_func probeForward; + r3d_shader_loader_func probeUnlit; + r3d_shader_loader_func decal; + } scene; + + // Deferred shaders + struct { + r3d_shader_loader_func ambient; + r3d_shader_loader_func lighting; + r3d_shader_loader_func compose; + r3d_shader_loader_func fog; + } deferred; + + // Post shaders + struct { + r3d_shader_loader_func dof; + r3d_shader_loader_func bloom; + r3d_shader_loader_func autoExposure; + r3d_shader_loader_func screen; + r3d_shader_loader_func output; + r3d_shader_loader_func fxaa[R3D_ANTI_ALIASING_PRESET_COUNT]; + r3d_shader_loader_func smaa[R3D_ANTI_ALIASING_PRESET_COUNT]; + r3d_shader_loader_func visualizer; + } post; + + // Blit shaders + struct { + r3d_shader_loader_func upBicubic; + r3d_shader_loader_func upLanczos; + r3d_shader_loader_func downRgss; + r3d_shader_loader_func downPdss; + } blit; + +} R3D_MOD_SHADER_LOADER = { + + .prepare = { + .denoiserAtrous = r3d_shader_load_prepare_denoiser_atrous, + .denoiserSparse = r3d_shader_load_prepare_denoiser_sparse, + .blurDown = r3d_shader_load_prepare_blur_down, + .blurUp = r3d_shader_load_prepare_blur_up, + .depthPyramid = r3d_shader_load_prepare_depth_pyramid, + .ssaoInDown = r3d_shader_load_prepare_ssao_in_down, + .ssao = r3d_shader_load_prepare_ssao, + .ssilInDown = r3d_shader_load_prepare_ssil_in_down, + .ssil = r3d_shader_load_prepare_ssil, + .ssgiInDown = r3d_shader_load_prepare_ssgi_in_down, + .ssgi = r3d_shader_load_prepare_ssgi, + .ssrInDown = r3d_shader_load_prepare_ssr_in_down, + .ssr = r3d_shader_load_prepare_ssr, + .dofCoc = r3d_shader_load_prepare_dof_coc, + .dofDown = r3d_shader_load_prepare_dof_down, + .dofBlur = r3d_shader_load_prepare_dof_blur, + .bloomDown = r3d_shader_load_prepare_bloom_down, + .bloomUp = r3d_shader_load_prepare_bloom_up, + .luminance = r3d_shader_load_prepare_luminance, + .exposureAdapt = r3d_shader_load_prepare_exposure_adapt, + .smaaEdgeDetection[0] = r3d_shader_load_prepare_smaa_edge_detection_low, + .smaaEdgeDetection[1] = r3d_shader_load_prepare_smaa_edge_detection_medium, + .smaaEdgeDetection[2] = r3d_shader_load_prepare_smaa_edge_detection_high, + .smaaEdgeDetection[3] = r3d_shader_load_prepare_smaa_edge_detection_ultra, + .smaaBlendingWeights[0] = r3d_shader_load_prepare_smaa_blending_weights_low, + .smaaBlendingWeights[1] = r3d_shader_load_prepare_smaa_blending_weights_medium, + .smaaBlendingWeights[2] = r3d_shader_load_prepare_smaa_blending_weights_high, + .smaaBlendingWeights[3] = r3d_shader_load_prepare_smaa_blending_weights_ultra, + .cubemapFromEquirectangular = r3d_shader_load_prepare_cubemap_from_equirectangular, + .cubemapIrradiance = r3d_shader_load_prepare_cubemap_irradiance, + .cubemapPrefilter = r3d_shader_load_prepare_cubemap_prefilter, + .cubemapProceduralSky = r3d_shader_load_prepare_cubemap_procedural_sky, + .cubemapCustomSky = r3d_shader_load_prepare_cubemap_custom_sky, + }, + + .scene = { + .geometry = r3d_shader_load_scene_geometry, + .forward = r3d_shader_load_scene_forward, + .unlit = r3d_shader_load_scene_unlit, + .background = r3d_shader_load_scene_background, + .skybox = r3d_shader_load_scene_skybox, + .depth = r3d_shader_load_scene_depth, + .depthCube = r3d_shader_load_scene_depth_cube, + .probeForward = r3d_shader_load_scene_probe_forward, + .probeUnlit = r3d_shader_load_scene_probe_unlit, + .decal = r3d_shader_load_scene_decal, + }, + + .deferred = { + .ambient = r3d_shader_load_deferred_ambient, + .lighting = r3d_shader_load_deferred_lighting, + .compose = r3d_shader_load_deferred_compose, + .fog = r3d_shader_load_deferred_fog, + }, + + .post = { + .dof = r3d_shader_load_post_dof, + .bloom = r3d_shader_load_post_bloom, + .autoExposure = r3d_shader_load_post_auto_exposure, + .screen = r3d_shader_load_post_screen, + .output = r3d_shader_load_post_output, + .fxaa[0] = r3d_shader_load_post_fxaa_low, + .fxaa[1] = r3d_shader_load_post_fxaa_medium, + .fxaa[2] = r3d_shader_load_post_fxaa_high, + .fxaa[3] = r3d_shader_load_post_fxaa_ultra, + .smaa[0] = r3d_shader_load_post_smaa_low, + .smaa[1] = r3d_shader_load_post_smaa_medium, + .smaa[2] = r3d_shader_load_post_smaa_high, + .smaa[3] = r3d_shader_load_post_smaa_ultra, + .visualizer = r3d_shader_load_post_visualizer, + }, + + .blit = { + .upBicubic = r3d_shader_load_blit_up_bicubic, + .upLanczos = r3d_shader_load_blit_up_lanczos, + .downRgss = r3d_shader_load_blit_down_rgss, + .downPdss = r3d_shader_load_blit_down_pdss, + }, + +}; + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +/* + * Module initialization function. + * Called once during `R3D_Init()` + */ +bool r3d_shader_init(); + +/* + * Module deinitialization function. + * Called once during `R3D_Close()` + */ +void r3d_shader_quit(); + +/* + * Binds the texture to the specified sampler. + * Called by `R3D_SHADER_BIND_SAMPLER`, no need to call it manually. + */ +void r3d_shader_bind_sampler(r3d_shader_sampler_t sampler, GLuint texture); + +/* + * Upload and bind the specified uniform block with the provided data. + */ +void r3d_shader_set_uniform_block(r3d_shader_block_t block, const void* data); + +/* + * Only bind the specified uniform block without change its data. + */ +void r3d_shader_bind_uniform_block(r3d_shader_block_t block); + +/* + * Allocates a new custom shader with its program in a single contiguous block. + * The shader is marked as the program owner and is responsible for freeing it. + * Returns NULL on allocation failure. + */ +r3d_shader_custom_t* r3d_shader_custom_alloc(void); + +/* + * Creates a shallow clone of an existing custom shader. + * The clone shares the same program pointer but is not its owner. + * Sampler slots are copied without their texture bindings. + * If the source has a uniform buffer, a new GPU buffer is allocated + * with the same layout but zeroed data. + * Returns NULL on allocation failure. + */ +r3d_shader_custom_t* r3d_shader_custom_clone(r3d_shader_custom_t* custom); + +/* + * Releases the GPU and CPU resources of a custom shader. + * Always frees the uniform buffer if present. + * Only deletes the GL programs if the shader owns them. + * Safe to call with NULL. + */ +void r3d_shader_custom_free(r3d_shader_custom_t* custom); + +/* + * Finalizes the uniform buffer layout and allocates the GPU buffer. + * Must be called after all uniform entries have been registered, + * passing the current accumulated byte offset as `currentOffset`. + * Does nothing if no uniform entries are defined. + */ +void r3d_shader_custom_init_uniforms(r3d_shader_custom_t* custom, int currentOffset); + +/* + * Sets the value of a client-side uniform, marks its state as dirty, and flags it for upload. + */ +bool r3d_shader_custom_set_uniform(r3d_shader_custom_t* shader, const char* name, const void* value); + +/* + * Assigns a texture to a sampler; it must be bound afterwards. + */ +bool r3d_shader_custom_set_sampler(r3d_shader_custom_t* shader, const char* name, Texture texture); + +/* + * Checks if any uniforms are dirty and need to be uploaded then bind it. + * Automatically called when `R3D_SHADER_USE` (OVR/OPT) is invoked with a custom shader. + */ +void r3d_shader_custom_bind_uniforms(r3d_shader_custom_t* shader); + +/* + * Binds the textures of a custom shader and verifies the state of its samplers then bind them. + * Automatically called when `R3D_SHADER_USE` (OVR/OPT) is invoked with a custom shader. + */ +void r3d_shader_custom_bind_samplers(r3d_shader_custom_t* shader); + +/* + * Invalidate the internal state cache. + * Use program zero, and unbind all textures. + */ +void r3d_shader_invalidate_cache(void); + +#endif // R3D_MODULE_SHADER_H + +``` + +`src/modules/r3d_target.c`: + +```c +/* r3d_target.c -- Internal R3D render target module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_target.h" +#include +#include +#include +#include + +#include "../common/r3d_helper.h" +#include "../common/r3d_math.h" + +// ======================================== +// MODULE STATE +// ======================================== + +struct r3d_mod_target R3D_MOD_TARGET; + +// ======================================== +// INTERNAL OPENGL FORMAT TABLE +// ======================================== + +typedef struct { + GLenum internal; + GLenum format; + GLenum type; +} target_format_t; + +typedef enum { + FORMAT_R8, FORMAT_RG8, FORMAT_RGB8, FORMAT_RGBA8, + FORMAT_R16, FORMAT_RG16, FORMAT_RGB16, FORMAT_RGBA16, + FORMAT_R8UI, FORMAT_RG8UI, FORMAT_RGB8UI, FORMAT_RGBA8UI, + FORMAT_R16F, FORMAT_RG16F, FORMAT_RGB16F, FORMAT_RGBA16F, + FORMAT_R32F, FORMAT_RG32F, FORMAT_RGB32F, FORMAT_RGBA32F, +} target_format_enum_t; + +static const target_format_t TARGET_FORMAT[] = { + [FORMAT_R8] = { GL_R8, GL_RED, GL_UNSIGNED_BYTE }, + [FORMAT_RG8] = { GL_RG8, GL_RG, GL_UNSIGNED_BYTE }, + [FORMAT_RGB8] = { GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE }, + [FORMAT_RGBA8] = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, + [FORMAT_R16] = { GL_R16, GL_RED, GL_UNSIGNED_SHORT }, + [FORMAT_RG16] = { GL_RG16, GL_RG, GL_UNSIGNED_SHORT }, + [FORMAT_RGB16] = { GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT }, + [FORMAT_RGBA16] = { GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT }, + [FORMAT_R8UI] = { GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE }, + [FORMAT_RG8UI] = { GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE }, + [FORMAT_RGB8UI] = { GL_RGB8UI, GL_RGB_INTEGER, GL_UNSIGNED_BYTE }, + [FORMAT_RGBA8UI] = { GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE }, + [FORMAT_R16F] = { GL_R16F, GL_RED, GL_HALF_FLOAT }, + [FORMAT_RG16F] = { GL_RG16F, GL_RG, GL_HALF_FLOAT }, + [FORMAT_RGB16F] = { GL_RGB16F, GL_RGB, GL_HALF_FLOAT }, + [FORMAT_RGBA16F] = { GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT }, + [FORMAT_R32F] = { GL_R32F, GL_RED, GL_FLOAT }, + [FORMAT_RG32F] = { GL_RG32F, GL_RG, GL_FLOAT }, + [FORMAT_RGB32F] = { GL_RGB32F, GL_RGB, GL_FLOAT }, + [FORMAT_RGBA32F] = { GL_RGBA32F, GL_RGBA, GL_FLOAT }, +}; + +// ======================================== +// INTERNAL TARGET FUNCTIONS +// ======================================== + +typedef struct { + target_format_enum_t format; + float resolutionFactor; + GLenum minFilter; + GLenum magFilter; + int numLevels; + float clear[4]; +} target_config_t; + +static const target_config_t TARGET_CONFIG[] = { + [R3D_TARGET_ALBEDO] = { FORMAT_RGB8, 1.0f, GL_NEAREST, GL_NEAREST, 2, {0} }, + [R3D_TARGET_NORMAL] = { FORMAT_RG16, 1.0f, GL_NEAREST, GL_NEAREST, 2, {0} }, + [R3D_TARGET_ORM] = { FORMAT_RGBA8, 1.0f, GL_NEAREST, GL_NEAREST, 2, {0} }, + [R3D_TARGET_DEPTH] = { FORMAT_R16F, 1.0f, GL_NEAREST, GL_NEAREST, 2, {65504.0f, 65504.0f, 65504.0f, 65504.0f} }, + [R3D_TARGET_DIFFUSE] = { FORMAT_RGB16F, 1.0f, GL_NEAREST, GL_NEAREST, 2, {0} }, + [R3D_TARGET_SPECULAR] = { FORMAT_RGB16F, 1.0f, GL_NEAREST, GL_NEAREST, 2, {0} }, + [R3D_TARGET_GEOM_NORMAL] = { FORMAT_RG16, 1.0f, GL_NEAREST, GL_NEAREST, 1, {0} }, + [R3D_TARGET_SELECTOR] = { FORMAT_R8UI, 0.5f, GL_NEAREST, GL_NEAREST, 2, {0} }, + [R3D_TARGET_SSAO_0] = { FORMAT_R8, 0.5f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_SSAO_1] = { FORMAT_R8, 0.5f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_SSIL_0] = { FORMAT_RGBA16F, 0.5f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_SSIL_1] = { FORMAT_RGBA16F, 0.5f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_SSGI_0] = { FORMAT_RGB16F, 0.5f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_SSGI_1] = { FORMAT_RGB16F, 0.5f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_SSR] = { FORMAT_RGBA16F, 0.5f, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, 0, {0} }, + [R3D_TARGET_DOF_COC] = { FORMAT_R16F, 1.0f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_DOF_0] = { FORMAT_RGBA16F, 0.5f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_DOF_1] = { FORMAT_RGBA16F, 0.5f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_BLOOM] = { FORMAT_RGB16F, 0.5f, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, 0, {0} }, + [R3D_TARGET_SMAA_EDGES] = { FORMAT_RG8, 1.0f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_SMAA_BLEND] = { FORMAT_RGBA8, 1.0f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_LUMINANCE] = { FORMAT_R16F, 0.5f, GL_NEAREST, GL_NEAREST, 0, {0} }, + [R3D_TARGET_EXPOSURE_0] = { FORMAT_RG16F, 0.0f, GL_NEAREST, GL_NEAREST, 1, {1.0f, R3D_LOG018, 0.0f, 1.0f} }, + [R3D_TARGET_EXPOSURE_1] = { FORMAT_RG16F, 0.0f, GL_NEAREST, GL_NEAREST, 1, {1.0f, R3D_LOG018, 0.0f, 1.0f} }, + [R3D_TARGET_SCENE_0] = { FORMAT_RGB16F, 1.0f, GL_LINEAR, GL_LINEAR, 1, {0} }, + [R3D_TARGET_SCENE_1] = { FORMAT_RGB16F, 1.0f, GL_LINEAR, GL_LINEAR, 1, {0} }, +}; + +static void alloc_target_texture(r3d_target_t target) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, R3D_MOD_TARGET.targetTextures[target]); + + const target_config_t* config = &TARGET_CONFIG[target]; + const target_format_t* format = &TARGET_FORMAT[config->format]; + + int numLevels = r3d_target_get_num_levels(target); + + for (int i = 0; i < numLevels; ++i) { + int wLevel = 0, hLevel = 0; + r3d_target_get_resolution(&wLevel, &hLevel, target, i); + glTexImage2D(GL_TEXTURE_2D, i, format->internal, wLevel, hLevel, 0, format->format, format->type, NULL); + } + + // NOTE: By default, sampling is blocked at the first level + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, config->minFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, config->magFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); + + R3D_MOD_TARGET.targetStates[target] = (r3d_target_state_t) {0}; + R3D_MOD_TARGET.targetLoaded[target] = true; +} + +static void alloc_depth_stencil_renderbuffer(int resW, int resH) +{ + glBindRenderbuffer(GL_RENDERBUFFER, R3D_MOD_TARGET.depthRenderbuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, resW, resH); + glBindRenderbuffer(GL_RENDERBUFFER, 0); +} + +/* + * Returns the index of the FBO in the cache. + * If the combination doesn't exist, creates a new FBO and caches it. + */ +static int get_or_create_fbo(const r3d_target_t* targets, int count, bool depth) +{ + assert(targets || (!targets && count == 0)); + assert(count <= R3D_TARGET_MAX_ATTACHMENTS); + assert(count > 0 || (count == 0 && depth)); + + /* --- Search if the combination is already cached --- */ + + for (int i = 0; i < R3D_MOD_TARGET.fboCount; i++) { + const r3d_target_fbo_t* fbo = &R3D_MOD_TARGET.fbo[i]; + if (fbo->targetCount == count && fbo->hasDepth == depth) { + if (count == 0 || memcmp(fbo->targets, targets, count * sizeof(*targets)) == 0) { + return i; + } + } + } + + /* --- Create the FBO and cache it --- */ + + assert(R3D_MOD_TARGET.fboCount < R3D_TARGET_MAX_FRAMEBUFFERS); + + int newIndex = R3D_MOD_TARGET.fboCount++; + r3d_target_fbo_t* fbo = &R3D_MOD_TARGET.fbo[newIndex]; + + glGenFramebuffers(1, &fbo->id); + glBindFramebuffer(GL_FRAMEBUFFER, fbo->id); + + GLenum glColor[R3D_TARGET_MAX_ATTACHMENTS]; + int locCount = 0; + + for (int i = 0; i < count; ++i) { + if (!R3D_MOD_TARGET.targetLoaded[targets[i]]) { + alloc_target_texture(targets[i]); + } + + GLuint texture = R3D_MOD_TARGET.targetTextures[targets[i]]; + fbo->targetStates[i] = (r3d_target_attachment_state_t) {0}; + fbo->targets[i] = targets[i]; + + GLenum attachment = GL_COLOR_ATTACHMENT0 + locCount; + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, texture, 0); + glColor[locCount++] = attachment; + } + + if (depth) { + glFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, R3D_MOD_TARGET.depthRenderbuffer + ); + } + + fbo->targetCount = count; + fbo->hasDepth = depth; + + if (locCount > 0) { + glDrawBuffers(locCount, glColor); + } + else { + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + R3D_TRACELOG(LOG_ERROR, "Framebuffer incomplete (status: 0x%04x)", status); + } + + return newIndex; +} + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +bool r3d_target_init(int resW, int resH) +{ + memset(&R3D_MOD_TARGET, 0, sizeof(R3D_MOD_TARGET)); + + glGenTextures(R3D_TARGET_COUNT, R3D_MOD_TARGET.targetTextures); + glGenRenderbuffers(1, &R3D_MOD_TARGET.depthRenderbuffer); + alloc_depth_stencil_renderbuffer(resW, resH); + + R3D_MOD_TARGET.currentFbo = -1; + + R3D_MOD_TARGET.resW = resW; + R3D_MOD_TARGET.resH = resH; + R3D_MOD_TARGET.txlW = 1.0f / resW; + R3D_MOD_TARGET.txlH = 1.0f / resH; + + return true; +} + +void r3d_target_quit(void) +{ + glDeleteTextures(R3D_TARGET_COUNT, R3D_MOD_TARGET.targetTextures); + glDeleteRenderbuffers(1, &R3D_MOD_TARGET.depthRenderbuffer); + + for (int i = 0; i < R3D_MOD_TARGET.fboCount; i++) { + if (R3D_MOD_TARGET.fbo[i].id != 0) { + glDeleteFramebuffers(1, &R3D_MOD_TARGET.fbo[i].id); + } + } +} + +void r3d_target_resize(int resW, int resH) +{ + assert(resW > 0 && resH > 0); + + if (R3D_MOD_TARGET.resW == resW && R3D_MOD_TARGET.resH == resH) { + return; + } + + R3D_MOD_TARGET.resW = resW; + R3D_MOD_TARGET.resH = resH; + R3D_MOD_TARGET.txlW = 1.0f / resW; + R3D_MOD_TARGET.txlH = 1.0f / resH; + + alloc_depth_stencil_renderbuffer(resW, resH); + + for (int i = 0; i < R3D_TARGET_COUNT; i++) { + if (R3D_MOD_TARGET.targetLoaded[i]) { + alloc_target_texture(i); + } + } +} + +int r3d_target_get_num_levels(r3d_target_t target) +{ + const target_config_t* config = &TARGET_CONFIG[target]; + if (config->numLevels > 0) return config->numLevels; + if (config->resolutionFactor <= 0.0f) return 1; + + int w = (int)((float)R3D_MOD_TARGET.resW * config->resolutionFactor); + int h = (int)((float)R3D_MOD_TARGET.resH * config->resolutionFactor); + return r3d_get_mip_levels_2d(w, h); +} + +void r3d_target_get_resolution(int* w, int* h, r3d_target_t target, int level) +{ + const target_config_t* config = &TARGET_CONFIG[target]; + + if (config->resolutionFactor <= 0.0f) { + if (w) *w = 1; + if (h) *h = 1; + return; + } + + if (w) *w = (int)((float)R3D_MOD_TARGET.resW * config->resolutionFactor); + if (h) *h = (int)((float)R3D_MOD_TARGET.resH * config->resolutionFactor); + + if (level > 0) { + if (w) *w = *w >> level, *w = *w > 1 ? *w : 1; + if (h) *h = *h >> level, *h = *h > 1 ? *h : 1; + } +} + +void r3d_target_get_texel_size(float* w, float* h, r3d_target_t target, int level) +{ + const target_config_t* config = &TARGET_CONFIG[target]; + + if (config->resolutionFactor <= 0.0f) { + if (w) *w = 1.0f; + if (h) *h = 1.0f; + return; + } + + if (w) *w = R3D_MOD_TARGET.txlW / config->resolutionFactor; + if (h) *h = R3D_MOD_TARGET.txlH / config->resolutionFactor; + + if (level > 0) { + float scale = (float)(1 << level); + if (w) *w *= scale; + if (h) *h *= scale; + } +} + +r3d_target_t r3d_target_swap_scene(r3d_target_t scene) +{ + if (scene == R3D_TARGET_SCENE_0) { + return R3D_TARGET_SCENE_1; + } + return R3D_TARGET_SCENE_0; +} + +void r3d_target_clear(const r3d_target_t* targets, int count, int level, bool depth) +{ + assert((!depth || level == 0) && "If depth buffer bind, always bind at level zero"); + assert(count > 0 || depth); + + int fboIndex = get_or_create_fbo(targets, count, depth); + if (fboIndex != R3D_MOD_TARGET.currentFbo) { + glBindFramebuffer(GL_FRAMEBUFFER, R3D_MOD_TARGET.fbo[fboIndex].id); + R3D_MOD_TARGET.currentFbo = fboIndex; + } + + for (int i = 0; i < count; i++) { + r3d_target_set_write_level(i, level); + } + + if (count > 0) r3d_target_set_viewport(targets[0], level); + else glViewport(0, 0, R3D_MOD_TARGET.resW, R3D_MOD_TARGET.resH); + + for (int i = 0; i < count; i++) { + glClearBufferfv(GL_COLOR, i, TARGET_CONFIG[targets[i]].clear); + } + + if (depth) { + glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); + } +} + +void r3d_target_bind(const r3d_target_t* targets, int count, int level, bool depth) +{ + assert((!depth || level == 0) && "If depth buffer bind, always bind at level zero"); + assert(count > 0 || depth); + + int fboIndex = get_or_create_fbo(targets, count, depth); + if (fboIndex != R3D_MOD_TARGET.currentFbo) { + glBindFramebuffer(GL_FRAMEBUFFER, R3D_MOD_TARGET.fbo[fboIndex].id); + R3D_MOD_TARGET.currentFbo = fboIndex; + } + + for (int i = 0; i < count; i++) { + r3d_target_set_write_level(i, level); + } + + if (count > 0) r3d_target_set_viewport(targets[0], level); + else glViewport(0, 0, R3D_MOD_TARGET.resW, R3D_MOD_TARGET.resH); +} + +void r3d_target_bind_levels(const r3d_target_t* targets, int* levels, int count) +{ + assert(count > 0); + + int fboIndex = get_or_create_fbo(targets, count, false); + if (fboIndex != R3D_MOD_TARGET.currentFbo) { + glBindFramebuffer(GL_FRAMEBUFFER, R3D_MOD_TARGET.fbo[fboIndex].id); + R3D_MOD_TARGET.currentFbo = fboIndex; + } + + for (int i = 0; i < count; i++) { + r3d_target_set_write_level(i, levels[i]); + } + + r3d_target_set_viewport(targets[0], levels[0]); +} + +void r3d_target_set_viewport(r3d_target_t target, int level) +{ + int vpW = 0, vpH = 0; + r3d_target_get_resolution(&vpW, &vpH, target, level); + glViewport(0, 0, vpW, vpH); +} + +void r3d_target_set_write_level(int attachment, int level) +{ + assert(R3D_MOD_TARGET.currentFbo >= 0); + + r3d_target_fbo_t* fbo = &R3D_MOD_TARGET.fbo[R3D_MOD_TARGET.currentFbo]; + assert(fbo->targetCount > 0 && attachment < fbo->targetCount); + + r3d_target_t target = fbo->targets[attachment]; + assert(level < r3d_target_get_num_levels(target)); + r3d_target_attachment_state_t* state = &fbo->targetStates[attachment]; + + if (state->writeLevel != level) { + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachment, + GL_TEXTURE_2D, R3D_MOD_TARGET.targetTextures[target], level + ); + state->writeLevel = level; + } +} + +void r3d_target_set_read_level(r3d_target_t target, int level) +{ + r3d_target_set_read_levels(target, level, level); +} + +void r3d_target_set_read_levels(r3d_target_t target, int baseLevel, int maxLevel) +{ + assert(R3D_MOD_TARGET.targetLoaded[target]); + assert(baseLevel < r3d_target_get_num_levels(target)); + assert(maxLevel < r3d_target_get_num_levels(target)); + + r3d_target_state_t* state = &R3D_MOD_TARGET.targetStates[target]; + + if (state->baseLevel != baseLevel || state->maxLevel != maxLevel) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, R3D_MOD_TARGET.targetTextures[target]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, baseLevel); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, maxLevel); + glBindTexture(GL_TEXTURE_2D, 0); + state->baseLevel = baseLevel; + state->maxLevel = maxLevel; + } +} + +void r3d_target_gen_mipmap(r3d_target_t target) +{ + GLuint id = r3d_target_get_all_levels(target); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, id); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); +} + +bool r3d_target_exists(r3d_target_t target) +{ + return (r3d_target_get_or_null(target) != 0); +} + +GLuint r3d_target_get(r3d_target_t target) +{ + assert(target > R3D_TARGET_INVALID && target < R3D_TARGET_COUNT); + assert(R3D_MOD_TARGET.targetLoaded[target]); + return R3D_MOD_TARGET.targetTextures[target]; +} + +GLuint r3d_target_get_or_null(r3d_target_t target) +{ + if (target <= R3D_TARGET_INVALID || target >= R3D_TARGET_COUNT) return 0; + if (!R3D_MOD_TARGET.targetLoaded[target]) return 0; + return R3D_MOD_TARGET.targetTextures[target]; +} + +GLuint r3d_target_get_level(r3d_target_t target, int level) +{ + return r3d_target_get_levels(target, level, level); +} + +GLuint r3d_target_get_levels(r3d_target_t target, int baseLevel, int maxLevel) +{ + assert(target > R3D_TARGET_INVALID && target < R3D_TARGET_COUNT); + r3d_target_set_read_levels(target, baseLevel, maxLevel); + return R3D_MOD_TARGET.targetTextures[target]; +} + +GLuint r3d_target_get_all_levels(r3d_target_t target) +{ + assert(target > R3D_TARGET_INVALID && target < R3D_TARGET_COUNT); + int maxLevel = r3d_target_get_num_levels(target) - 1; + r3d_target_set_read_levels(target, 0, maxLevel); + return R3D_MOD_TARGET.targetTextures[target]; +} + +void r3d_target_blit(r3d_target_t target, bool depth, GLuint dstFbo, int dstX, int dstY, int dstW, int dstH, bool linear) +{ + assert(target > R3D_TARGET_INVALID || depth); + + int fboIndex = -1; + if (target > R3D_TARGET_INVALID) { + fboIndex = get_or_create_fbo(&target, 1, depth); + } + else { + fboIndex = get_or_create_fbo(NULL, 0, true); + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, R3D_MOD_TARGET.fbo[fboIndex].id); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFbo); + + if (linear) { + if (target > R3D_TARGET_INVALID) { + glBlitFramebuffer( + 0, 0, R3D_MOD_TARGET.resW, R3D_MOD_TARGET.resH, + dstX, dstY, dstX + dstW, dstY + dstH, GL_COLOR_BUFFER_BIT, + GL_LINEAR + ); + } + if (depth) { + glBlitFramebuffer( + 0, 0, R3D_MOD_TARGET.resW, R3D_MOD_TARGET.resH, + dstX, dstY, dstX + dstW, dstY + dstH, GL_DEPTH_BUFFER_BIT, + GL_NEAREST + ); + } + } + else { + GLbitfield mask = GL_NONE; + if (target > R3D_TARGET_INVALID) mask |= GL_COLOR_BUFFER_BIT; + if (depth) mask |= GL_DEPTH_BUFFER_BIT; + glBlitFramebuffer( + 0, 0, R3D_MOD_TARGET.resW, R3D_MOD_TARGET.resH, + dstX, dstY, dstX + dstW, dstY + dstH, mask, + GL_NEAREST + ); + } +} + +void r3d_target_invalidate_cache(void) +{ + R3D_MOD_TARGET.currentFbo = -1; +} + +``` + +`src/modules/r3d_target.h`: + +```h +/* r3d_target.h -- Internal R3D render target module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_MODULE_TARGET_H +#define R3D_MODULE_TARGET_H + +#include +#include + +// ======================================== +// TARGET ENUM +// ======================================== + +/* + * Enums for all internal render targets. + * To add a new target, define a new enum before `R3D_TARGET_DEPTH`, + * then add its creation parameters in `TARGET_CONFIG` in `r3d_target.c`. + * Loading happens on-demand during bind operations. + */ +typedef enum { + R3D_TARGET_INVALID = -1, + R3D_TARGET_ALBEDO, //< Full - Mip 2 - RGB8 + R3D_TARGET_NORMAL, //< Full - Mip 2 - RG16 + R3D_TARGET_ORM, //< Full - Mip 2 - RGBA8 + R3D_TARGET_DEPTH, //< Full - Mip 2 - R16F + R3D_TARGET_DIFFUSE, //< Full - Mip 2 - RGB16F + R3D_TARGET_SPECULAR, //< Full - Mip 2 - RGB16F + R3D_TARGET_GEOM_NORMAL, //< Full - Mip 1 - RG16 + R3D_TARGET_SELECTOR, //< Half - Mip 2 - R8UI + R3D_TARGET_SSAO_0, //< Half - Mip 1 - R8 + R3D_TARGET_SSAO_1, //< Half - Mip 1 - R8 + R3D_TARGET_SSIL_0, //< Half - Mip 1 - RGBA16F + R3D_TARGET_SSIL_1, //< Half - Mip 1 - RGBA16F + R3D_TARGET_SSGI_0, //< Half - Mip 1 - RGB16F + R3D_TARGET_SSGI_1, //< Half - Mip 1 - RGB16F + R3D_TARGET_SSR, //< Half - Mip N - RGBA16F + R3D_TARGET_DOF_COC, //< Full - Mip 1 - R16F + R3D_TARGET_DOF_0, //< Half - Mip 1 - RGBA16F + R3D_TARGET_DOF_1, //< Half - Mip 1 - RGBA16F + R3D_TARGET_BLOOM, //< Half - Mip N - RGB16F + R3D_TARGET_SMAA_EDGES, //< Full - Mip 1 - RG8 + R3D_TARGET_SMAA_BLEND, //< Full - Mip 1 - RGBA8 + R3D_TARGET_LUMINANCE, //< Half - Mip N - R16F + R3D_TARGET_EXPOSURE_0, //< 1x1 - Mip 1 - RG16F + R3D_TARGET_EXPOSURE_1, //< 1x1 - Mip 1 - RG16F + R3D_TARGET_SCENE_0, //< Full - Mip 1 - RGB16F + R3D_TARGET_SCENE_1, //< Full - Mip 1 - RGB16F + R3D_TARGET_COUNT +} r3d_target_t; + +// ======================================== +// HELPER TARGET PACKS +// ======================================== + +#define R3D_TARGET_ALL_DEFERRED \ + R3D_TARGET_ALBEDO, \ + R3D_TARGET_NORMAL, \ + R3D_TARGET_ORM, \ + R3D_TARGET_DEPTH, \ + R3D_TARGET_DIFFUSE, \ + R3D_TARGET_SPECULAR, \ + R3D_TARGET_GEOM_NORMAL \ + +#define R3D_TARGET_GBUFFER \ + R3D_TARGET_ALBEDO, \ + R3D_TARGET_DIFFUSE, \ + R3D_TARGET_NORMAL, \ + R3D_TARGET_ORM, \ + R3D_TARGET_GEOM_NORMAL, \ + R3D_TARGET_DEPTH \ + +#define R3D_TARGET_LIGHTING \ + R3D_TARGET_DIFFUSE, \ + R3D_TARGET_SPECULAR \ + +#define R3D_TARGET_DECAL \ + R3D_TARGET_ALBEDO, \ + R3D_TARGET_DIFFUSE, \ + R3D_TARGET_ORM, \ + R3D_TARGET_NORMAL \ + +// ======================================== +// HELPER MACROS +// ======================================== + +#define R3D_TARGET_SIZE_W R3D_MOD_TARGET.resW +#define R3D_TARGET_SIZE_H R3D_MOD_TARGET.resH +#define R3D_TARGET_TEXEL_W R3D_MOD_TARGET.txlW +#define R3D_TARGET_TEXEL_H R3D_MOD_TARGET.txlH + +#define R3D_TARGET_LEVEL_LIST(...) (int[]) {__VA_ARGS__} + +#define R3D_TARGET_CLEAR(depth, ...) \ + r3d_target_clear( \ + (r3d_target_t[]) {__VA_ARGS__}, \ + sizeof((r3d_target_t[]) {__VA_ARGS__}) / sizeof(r3d_target_t), \ + 0, (depth) \ + ) + +#define R3D_TARGET_CLEAR_LEVEL(level, ...) \ + r3d_target_clear( \ + (r3d_target_t[]) {__VA_ARGS__}, \ + sizeof((r3d_target_t[]) {__VA_ARGS__}) / sizeof(r3d_target_t), \ + (level), false \ + ) + +#define R3D_TARGET_BIND(depth, ...) \ + r3d_target_bind( \ + (r3d_target_t[]){ __VA_ARGS__ }, \ + sizeof((r3d_target_t[]) {__VA_ARGS__}) / sizeof(r3d_target_t), \ + 0, (depth) \ + ) + +#define R3D_TARGET_BIND_LEVEL(level, ...) \ + r3d_target_bind( \ + (r3d_target_t[]) {__VA_ARGS__}, \ + sizeof((r3d_target_t[]) {__VA_ARGS__}) / sizeof(r3d_target_t), \ + (level), false \ + ) + +#define R3D_TARGET_BIND_LEVELS(levelsArr, ...) \ + r3d_target_bind_levels( \ + (r3d_target_t[]) {__VA_ARGS__}, \ + (levelsArr), \ + sizeof((r3d_target_t[]) {__VA_ARGS__}) / sizeof(r3d_target_t) \ + ) + +/* + * Binds the target, then swaps to the alternate scene target. + * Modifies the target parameter to point to the other buffer. + */ +#define R3D_TARGET_BIND_AND_SWAP_SCENE(target) do { \ + R3D_TARGET_BIND(false, target); \ + target = r3d_target_swap_scene(target); \ +} while(0) + +// ======================================== +// TARGET FBO STRUCTURE +// ======================================== + +#define R3D_TARGET_MAX_FRAMEBUFFERS 32 +#define R3D_TARGET_MAX_ATTACHMENTS 8 + +typedef struct { + int writeLevel; //< Indicates the level currently attached to the FBO +} r3d_target_attachment_state_t; + +typedef struct { + r3d_target_attachment_state_t targetStates[R3D_TARGET_MAX_ATTACHMENTS]; + r3d_target_t targets[R3D_TARGET_MAX_ATTACHMENTS]; + int targetCount; + bool hasDepth; + GLuint id; +} r3d_target_fbo_t; + +// ======================================== +// TARGET BUFFER STRUCTURE +// ======================================== + +typedef struct { + int baseLevel; + int maxLevel; +} r3d_target_state_t; + +// ======================================== +// MODULE STATE +// ======================================== + +extern struct r3d_mod_target { + + r3d_target_fbo_t fbo[R3D_TARGET_MAX_FRAMEBUFFERS]; //< FBO combination cache. FBOs are automatically generated as needed during bind + int currentFbo; //< Cache index of currently bound FBO, -1 if none bound. Reset via `r3d_target_invalidate_cache()` + int fboCount; //< Number of FBOs created + + r3d_target_state_t targetStates[R3D_TARGET_COUNT]; //< Array of target states + GLuint targetTextures[R3D_TARGET_COUNT]; //< Array of target IDs (textures) + bool targetLoaded[R3D_TARGET_COUNT]; //< Indicates whether the targets have been allocated + + GLuint depthRenderbuffer; //< Internal depth buffer + uint32_t resW, resH; //< Full internal resolution + float txlW, txlH; //< Size of a texel for full resolution + +} R3D_MOD_TARGET; + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +/* + * Module initialization function. + * Called once during `R3D_Init()` + */ +bool r3d_target_init(int resW, int resH); + +/* + * Module deinitialization function. + * Called once during `R3D_Close()` + */ +void r3d_target_quit(void); + +/* + * Resizes the internal resolution. + * Performs a reallocation of all targets already allocated. + * Ignore the operation if the new resolution is identical to the one already defined. + */ +void r3d_target_resize(int resW, int resH); + +/* + * Returns the total number of mip levels of the internal buffers + * based on their full resolution. + */ +int r3d_target_get_num_levels(r3d_target_t target); + +/* + * Returns the internal resolution for the specified mip level. + */ +void r3d_target_get_resolution(int* w, int* h, r3d_target_t target, int level); + +/* + * Returns the texel size for the specified mip level. + */ +void r3d_target_get_texel_size(float* w, float* h, r3d_target_t target, int level); + +/* + * Returns target '1' if target '0' is provided, otherwise returns target '0'. + */ +r3d_target_t r3d_target_swap_scene(r3d_target_t scene); + +/* + * Creates, binds and clear the FBO with the requested attachment combination. + * Attachment locations follow the order provided. + * + * This function attaches the targets at the specified level and sets the corresponding viewport. + * Ensure that the provided target combination is compatible with the specified level. + * The depth buffer can only be attached when the level is zero. + */ +void r3d_target_clear(const r3d_target_t* targets, int count, int level, bool depth); + +/* + * Creates (or retrieves) and binds an FBO with the requested attachment combination. + * Attachment locations follow the order of targets (COLOR0 = targets[0], etc). + * + * This function attaches the targets at the specified level and sets the corresponding viewport. + * Ensure that the provided target combination is compatible with the specified level. + * The depth buffer can only be attached when the level is zero. + */ +void r3d_target_bind(const r3d_target_t* targets, int count, int level, bool depth); + +/* + * Creates (or retrieves) and binds an FBO for the given attachments. + * Attachment locations follow the order of targets (COLOR0 = targets[0], etc). + * + * Unlike r3d_target_bind(), each attachment can be bound at a different mip level + * via the corresponding entry in levels. + * + * The viewport is set from targets[0] at levels[0]. + * + * Notes: + * - All specified levels must have identical dimensions. + * - No hardware depth buffer is attached by this function. + */ +void r3d_target_bind_levels(const r3d_target_t* targets, int* levels, int count); + +/* + * Sets the viewport according to the target and specified level. + */ +void r3d_target_set_viewport(r3d_target_t target, int level); + +/* + * Changes the mip level of the specified attachment. + * Takes effect on the currently bound FBO. + * The attachment index corresponds to the target's location in 'r3d_target_bind'. + * Asserts that a valid FBO is currently bound and that the level is valid. + */ +void r3d_target_set_write_level(int attachment, int level); + +/* + * Defines the sampling level of the target. + * This function locks the sampling to a single level. + * See `r3d_target_set_read_levels` for multi-level sampling. + */ +void r3d_target_set_read_level(r3d_target_t target, int level); + +/* + * Defines the sampling levels of the target. + * baseLevel defines the first level and maxLevel the last. + * Asserts that the target has already been created/used and that the levels are valid. + */ +void r3d_target_set_read_levels(r3d_target_t target, int baseLevel, int maxLevel); + +/* + * Generates mipmaps for the specified target. + * Asserts that the target has already been created. + */ +void r3d_target_gen_mipmap(r3d_target_t target); + +/* + * Returns whether the requested target has a valid texture ID. + * Returns false if the target has not been created yet or if the enum is invalid. + */ +bool r3d_target_exists(r3d_target_t target); + +/* + * Returns the texture ID corresponding to the requested target. + * Asserts that the requested target has been created and if the target enum is valid. + * If not created yet, it means we never bound this target, so it would be empty. + */ +GLuint r3d_target_get(r3d_target_t target); + +/* + * Returns the texture ID corresponding to the requested target. + * Or returns 0 if the target has not been created or if the enum is invalid. + */ +GLuint r3d_target_get_or_null(r3d_target_t target); + +/* + * Returns the texture ID corresponding to the requested target and sampling level. + * Asserts that the requested target has been created and that the level is valid. + */ +GLuint r3d_target_get_level(r3d_target_t target, int level); + +/* + * Returns the texture ID corresponding to the requested target with base and max levels configured. + * Asserts that the requested target has been created and if the target enum is valid. + * If not created yet, it means we never bound this target, so it would be empty. + */ +GLuint r3d_target_get_levels(r3d_target_t target, int baseLevel, int maxLevel); + +/* + * Returns the texture ID corresponding to the requested target all levels configured to be sampled. + * Asserts that the requested target has been created and if the target enum is valid. + * If not created yet, it means we never bound this target, so it would be empty. + */ +GLuint r3d_target_get_all_levels(r3d_target_t target); + +/* + * Blits the provided targets to the specified FBO. + * Supports blitting with only a depth target. + */ +void r3d_target_blit(r3d_target_t target, bool depth, GLuint dstFbo, int dstX, int dstY, int dstW, int dstH, bool linear); + +/* + * Invalidate the internal state cache as the FBO target currently binds. + */ +void r3d_target_invalidate_cache(void); + +#endif // R3D_MODULE_TARGET_H + +``` + +`src/modules/r3d_texture.c`: + +```c +/* r3d_texture.c -- Internal R3D texture module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include "./r3d_texture.h" +#include +#include +#include +#include + +// ======================================== +// TEXTURE DATA INCLUDES +// ======================================== + +#include +#include +#include + +// ======================================== +// MODULE STATE +// ======================================== + +static struct r3d_texture { + GLuint textures[R3D_TEXTURE_COUNT]; + bool loaded[R3D_TEXTURE_COUNT]; +} R3D_MOD_TEXTURE; + +// ======================================== +// UTILITY FUNCTIONS +// ======================================== + +static void tex_params(GLenum target, GLint filter, GLint wrap) +{ + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); + glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap); + if (target != GL_TEXTURE_1D) { + glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap); + } +} + +// ======================================== +// TEXTURE LOADERS +// ======================================== + +typedef void (*texture_loader_func)(void); + +static void load_white(void); +static void load_black(void); +static void load_blank(void); +static void load_normal(void); +static void load_ibl_brdf_lut(void); +static void load_smaa_area(void); +static void load_smaa_search(void); + +static const texture_loader_func LOADERS[] = { + [R3D_TEXTURE_WHITE] = load_white, + [R3D_TEXTURE_BLACK] = load_black, + [R3D_TEXTURE_BLANK] = load_blank, + [R3D_TEXTURE_NORMAL] = load_normal, + [R3D_TEXTURE_BRDF_LUT] = load_ibl_brdf_lut, + [R3D_TEXTURE_SMAA_AREA] = load_smaa_area, + [R3D_TEXTURE_SMAA_SEARCH] = load_smaa_search, +}; + +void load_white(void) { + const uint8_t px[4] = {255, 255, 255, 255}; + glBindTexture(GL_TEXTURE_2D, R3D_MOD_TEXTURE.textures[R3D_TEXTURE_WHITE]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, px); + tex_params(GL_TEXTURE_2D, GL_NEAREST, GL_CLAMP_TO_EDGE); +} + +void load_black(void) { + const uint8_t px[4] = {0, 0, 0, 255}; + glBindTexture(GL_TEXTURE_2D, R3D_MOD_TEXTURE.textures[R3D_TEXTURE_BLACK]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, px); + tex_params(GL_TEXTURE_2D, GL_NEAREST, GL_CLAMP_TO_EDGE); +} + +void load_blank(void) { + const uint8_t px[4] = {0, 0, 0, 0}; + glBindTexture(GL_TEXTURE_2D, R3D_MOD_TEXTURE.textures[R3D_TEXTURE_BLANK]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, px); + tex_params(GL_TEXTURE_2D, GL_NEAREST, GL_CLAMP_TO_EDGE); +} + +void load_normal(void) { + const uint8_t px[4] = {127, 127, 255, 0}; + glBindTexture(GL_TEXTURE_2D, R3D_MOD_TEXTURE.textures[R3D_TEXTURE_NORMAL]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, px); + tex_params(GL_TEXTURE_2D, GL_NEAREST, GL_CLAMP_TO_EDGE); +} + +void load_ibl_brdf_lut(void) { + glBindTexture(GL_TEXTURE_2D, R3D_MOD_TEXTURE.textures[R3D_TEXTURE_BRDF_LUT]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, 512, 512, 0, GL_RG, GL_HALF_FLOAT, BRDF_LUT_512_RG16_FLOAT_RAW); + tex_params(GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE); +} + +void load_smaa_area(void) { + glBindTexture(GL_TEXTURE_2D, R3D_MOD_TEXTURE.textures[R3D_TEXTURE_SMAA_AREA]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, 160, 560, 0, GL_RG, GL_UNSIGNED_BYTE, SMAA_AREA_160X560_RG8_RAW); + tex_params(GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE); +} + +void load_smaa_search(void) { + glBindTexture(GL_TEXTURE_2D, R3D_MOD_TEXTURE.textures[R3D_TEXTURE_SMAA_SEARCH]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 64, 16, 0, GL_RED, GL_UNSIGNED_BYTE, SMAA_SEARCH_64X16_R8_RAW); + tex_params(GL_TEXTURE_2D, GL_NEAREST, GL_CLAMP_TO_EDGE); +} + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +bool r3d_texture_init(void) +{ + memset(&R3D_MOD_TEXTURE, 0, sizeof(R3D_MOD_TEXTURE)); + glGenTextures(R3D_TEXTURE_COUNT, R3D_MOD_TEXTURE.textures); + return true; +} + +void r3d_texture_quit(void) +{ + glDeleteTextures(R3D_TEXTURE_COUNT, R3D_MOD_TEXTURE.textures); +} + +bool r3d_texture_is_default(GLuint id) +{ + for (int i = 0; i < R3D_TEXTURE_COUNT; i++) { + if (id == R3D_MOD_TEXTURE.textures[i]) return true; + } + return false; +} + +GLuint r3d_texture_get(r3d_texture_t texture) +{ + if (!R3D_MOD_TEXTURE.loaded[texture]) { + R3D_MOD_TEXTURE.loaded[texture] = true; + LOADERS[texture](); + } + return R3D_MOD_TEXTURE.textures[texture]; +} + +``` + +`src/modules/r3d_texture.h`: + +```h +/* r3d_texture.h -- Internal R3D texture module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_MODULE_TEXTURE_H +#define R3D_MODULE_TEXTURE_H + +#include +#include + +// ======================================== +// TEXTURE ENUM +// ======================================== + +typedef enum { + R3D_TEXTURE_WHITE, + R3D_TEXTURE_BLACK, + R3D_TEXTURE_BLANK, + R3D_TEXTURE_NORMAL, + R3D_TEXTURE_BRDF_LUT, + R3D_TEXTURE_SMAA_AREA, + R3D_TEXTURE_SMAA_SEARCH, + R3D_TEXTURE_COUNT +} r3d_texture_t; + +// ======================================== +// HELPER MACROS +// ======================================== + +#define R3D_TEXTURE_SELECT(id, or) \ + ((id) != 0) ? (id) : r3d_texture_get(R3D_TEXTURE_##or) \ + +// ======================================== +// MODULE FUNCTIONS +// ======================================== + +/* + * Module initialization function. + * Called once during `R3D_Init()` + */ +bool r3d_texture_init(void); + +/* + * Module deinitialization function. + * Called once during `R3D_Close()` + */ +void r3d_texture_quit(void); + +/* + * Indicates whether the provided texture ID corresponds to a texture + * owned by this module. + */ +bool r3d_texture_is_default(GLuint id); + +/* + * Returns the texture ID of the texture specified as a parameter. + */ +GLuint r3d_texture_get(r3d_texture_t texture); + +#endif // R3D_MODULE_TEXTURE_H + +``` + +`src/r3d_ambient_map.c`: + +```c +/* r3d_ambient_map.c -- R3D Ambient Map Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#include "./common/r3d_helper.h" +#include "./common/r3d_pass.h" + +#include "./modules/r3d_driver.h" +#include "./modules/r3d_render.h" +#include "./modules/r3d_env.h" + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_AmbientMap R3D_LoadAmbientMap(const char* fileName, R3D_CubemapLayout layout, R3D_AmbientFlags flags) +{ + Image image = LoadImage(fileName); + R3D_AmbientMap ambientMap = R3D_LoadAmbientMapFromImage(image, layout, flags); + UnloadImage(image); + + return ambientMap; +} + +R3D_AmbientMap R3D_LoadAmbientMapFromImage(Image image, R3D_CubemapLayout layout, R3D_AmbientFlags flags) +{ + R3D_AmbientMap ambientMap = {0}; + + if (image.width <= 0 || image.height <= 0) { + R3D_TRACELOG(LOG_WARNING, "Invalid image for ambient map (width=%d, height=%d)", image.width, image.height); + return ambientMap; + } + + R3D_Cubemap cubemap = R3D_LoadCubemapFromImage(image, layout); + ambientMap = R3D_GenAmbientMap(cubemap, flags); + R3D_UnloadCubemap(cubemap); + + bool success = !( + ((flags & R3D_AMBIENT_ILLUMINATION) && !ambientMap.irradiance) || + ((flags & R3D_AMBIENT_REFLECTION) && !ambientMap.prefilter) + ); + + if (success) { + R3D_TRACELOG(LOG_INFO, "Ambient map loaded successfully (irradiance: %s | reflection: %s)", + (ambientMap.irradiance ? "yes" : "no"), (ambientMap.prefilter ? "yes" : "no") + ); + } + + return ambientMap; +} + +R3D_AmbientMap R3D_GenAmbientMap(R3D_Cubemap cubemap, R3D_AmbientFlags flags) +{ + R3D_AmbientMap ambientMap = {0}; + + r3d_driver_invalidate_cache(); + r3d_driver_store_viewport(); + r3d_render_prepare_drawing(); + + int irradiance = -1; + if (BIT_TEST(flags, R3D_AMBIENT_ILLUMINATION)) { + irradiance = r3d_env_irradiance_reserve_layer(); + if (irradiance < 0) { + R3D_TRACELOG(LOG_WARNING, "Failed to reserve irradiance cubemap for ambient map"); + return ambientMap; + } + r3d_pass_prepare_irradiance(irradiance, cubemap.texture, cubemap.size); + } + + int prefilter = -1; + if (BIT_TEST(flags, R3D_AMBIENT_REFLECTION)) { + prefilter = r3d_env_prefilter_reserve_layer(); + if (prefilter < 0) { + R3D_TRACELOG(LOG_WARNING, "Failed to reserve irradiance cubemap for ambient map"); + r3d_env_irradiance_release_layer(irradiance); + return ambientMap; + } + r3d_pass_prepare_prefilter(prefilter, cubemap.texture, cubemap.size); + } + + glBindVertexArray(0); + r3d_driver_restore_viewport(); + + ambientMap.irradiance = irradiance + 1; + ambientMap.prefilter = prefilter + 1; + ambientMap.flags = flags; + + return ambientMap; +} + +void R3D_UnloadAmbientMap(R3D_AmbientMap ambientMap) +{ + if (ambientMap.irradiance > 0) { + r3d_env_irradiance_release_layer((int)ambientMap.irradiance - 1); + } + + if (ambientMap.prefilter > 0) { + r3d_env_prefilter_release_layer((int)ambientMap.prefilter - 1); + } +} + +void R3D_UpdateAmbientMap(R3D_AmbientMap ambientMap, R3D_Cubemap cubemap) +{ + r3d_driver_invalidate_cache(); + r3d_driver_store_viewport(); + r3d_render_prepare_drawing(); + + if (BIT_TEST(ambientMap.flags, R3D_AMBIENT_ILLUMINATION) && ambientMap.irradiance > 0) { + r3d_pass_prepare_irradiance((int)ambientMap.irradiance - 1, cubemap.texture, cubemap.size); + } + + if (BIT_TEST(ambientMap.flags, R3D_AMBIENT_REFLECTION) && ambientMap.prefilter > 0) { + r3d_pass_prepare_prefilter((int)ambientMap.prefilter - 1, cubemap.texture, cubemap.size); + } + + glBindVertexArray(0); + r3d_driver_restore_viewport(); +} + +``` + +`src/r3d_animation.c`: + +```c +/* r3d_animation.c -- R3D Animation Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef R3D_SUPPORT_ASSIMP +# include "./importer/r3d_importer_internal.h" +#endif + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_AnimationLib R3D_LoadAnimationLib(const char* filePath) +{ + R3D_AnimationLib animLib = {0}; + +#ifdef R3D_SUPPORT_ASSIMP + R3D_Importer* importer = R3D_LoadImporter(filePath, 0); + if (importer == NULL) return (R3D_AnimationLib) {0}; + + animLib = R3D_LoadAnimationLibFromImporter(importer); + R3D_UnloadImporter(importer); + +#else + R3D_TRACELOG(LOG_WARNING, "Cannot load '%s': built without Assimp support", filePath); + +#endif // R3D_SUPPORT_ASSIMP + + return animLib; +} + +R3D_AnimationLib R3D_LoadAnimationLibFromMemory(const void* data, unsigned int size, const char* hint) +{ + R3D_AnimationLib animLib = {0}; + +#ifdef R3D_SUPPORT_ASSIMP + R3D_Importer* importer = R3D_LoadImporterFromMemory(data, size, hint, 0); + if (importer == NULL) return (R3D_AnimationLib) {0}; + + animLib = R3D_LoadAnimationLibFromImporter(importer); + R3D_UnloadImporter(importer); + +#else + if (hint && hint[0] != '\0') { + R3D_TRACELOG(LOG_WARNING, "Cannot load '%s' from memory: built without Assimp support", hint); + } + else { + R3D_TRACELOG(LOG_WARNING, "Cannot load asset from memory: built without Assimp support"); + } + +#endif // R3D_SUPPORT_ASSIMP + + return animLib; +} + +R3D_AnimationLib R3D_LoadAnimationLibFromImporter(const R3D_Importer* importer) +{ + R3D_AnimationLib animLib = {0}; + +#ifdef R3D_SUPPORT_ASSIMP + if (!importer) { + R3D_TRACELOG(LOG_WARNING, "Cannot load animation library from importer: NULL importer"); + return animLib; + } + + if (r3d_importer_load_animations(importer, &animLib)) { + R3D_TRACELOG(LOG_INFO, "Animation library loaded successfully (%d animations): '%s'", animLib.count, importer->name); + } + else { + R3D_TRACELOG(LOG_WARNING, "Failed to load animation library: '%s'", importer->name); + } + +#else + R3D_TRACELOG(LOG_WARNING, "Cannot load animation library from importer: built without Assimp support"); + +#endif // R3D_SUPPORT_ASSIMP + + return animLib; +} + +void R3D_UnloadAnimationLib(R3D_AnimationLib animLib) +{ + if (!animLib.animations) return; + + for (int i = 0; i < animLib.count; ++i) { + R3D_Animation* anim = &animLib.animations[i]; + + for (int j = 0; j < anim->channelCount; ++j) { + R3D_AnimationChannel* channel = &anim->channels[j]; + + RL_FREE((void*)channel->translation.times); + RL_FREE((void*)channel->translation.values); + + RL_FREE((void*)channel->rotation.times); + RL_FREE((void*)channel->rotation.values); + + RL_FREE((void*)channel->scale.times); + RL_FREE((void*)channel->scale.values); + } + + RL_FREE(anim->channels); + } + + RL_FREE(animLib.animations); +} + +int R3D_GetAnimationIndex(R3D_AnimationLib animLib, const char* name) +{ + for (int i = 0; i < animLib.count; i++) { + if (strcmp(animLib.animations[i].name, name) == 0) { + return i; + } + } + return -1; +} + +R3D_Animation* R3D_GetAnimation(R3D_AnimationLib animLib, const char* name) +{ + int index = R3D_GetAnimationIndex(animLib, name); + if (index < 0) return NULL; + + return &animLib.animations[index]; +} + +``` + +`src/r3d_animation_player.c`: + +```c +/* r3d_animation_player.c -- R3D Animation Player Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#include "./common/r3d_anim.h" + +// ======================================== +// INTERNAL FUNCTIONS DECLARATIONS +// ======================================== + +static void emit_event(R3D_AnimationPlayer* player, R3D_AnimationEvent event, int animIndex); +static bool is_anim_index_valid(R3D_AnimationPlayer* player, int animIndex); +static void reset_anim_time(R3D_AnimationPlayer* player, int animIndex); +static void compute_local_matrices(R3D_AnimationPlayer* player); + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_AnimationPlayer R3D_LoadAnimationPlayer(R3D_Skeleton skeleton, R3D_AnimationLib animLib) +{ + R3D_AnimationPlayer player = {0}; + + player.skeleton = skeleton; + player.animLib = animLib; + + // Allocate the required memory + player.states = RL_MALLOC(animLib.count * sizeof(*player.states)); + player.localPose = RL_MALLOC(skeleton.boneCount * sizeof(*player.localPose)); + player.modelPose = RL_MALLOC(skeleton.boneCount * sizeof(*player.modelPose)); + player.skinBuffer = RL_MALLOC(skeleton.boneCount * sizeof(*player.skinBuffer)); + + // Initialize animation states + for (int i = 0; i < animLib.count; i++) { + player.states[i] = (R3D_AnimationState) { + .currentTime = 0.0f, + .speed = 1.0f, + .play = false, + .loop = false + }; + } + player.activeAnimIndex = -1; + + // Load default poses for each space + memcpy(player.localPose, player.skeleton.localBind, player.skeleton.boneCount * sizeof(Matrix)); + memcpy(player.modelPose, player.skeleton.modelBind, player.skeleton.boneCount * sizeof(Matrix)); + for (int i = 0; i < player.skeleton.boneCount; i++) { + player.skinBuffer[i] = MatrixMultiply(player.skeleton.invBind[i], player.modelPose[i]); + } + + // Generate the skinning texture then uploading the computed matrices + glGenTextures(1, &player.skinTexture); + glBindTexture(GL_TEXTURE_1D, player.skinTexture); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16F, 4 * skeleton.boneCount, 0, GL_RGBA, GL_FLOAT, player.skinBuffer); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_1D, 0); + + return player; +} + +void R3D_UnloadAnimationPlayer(R3D_AnimationPlayer player) +{ + if (player.skinTexture > 0) { + glDeleteTextures(1, &player.skinTexture); + } + + RL_FREE(player.skinBuffer); + RL_FREE(player.modelPose); + RL_FREE(player.localPose); + RL_FREE(player.states); +} + +bool R3D_IsAnimationPlayerValid(R3D_AnimationPlayer player) +{ + return (player.skinTexture > 0); +} + +bool R3D_IsAnimationPlaying(R3D_AnimationPlayer player) +{ + if (is_anim_index_valid(&player, player.activeAnimIndex)) { + return player.states[player.activeAnimIndex].play; + } + return false; +} + +void R3D_PlayAnimation(R3D_AnimationPlayer* player, int animIndex) +{ + if (!is_anim_index_valid(player, animIndex)) { + R3D_TRACELOG(LOG_WARNING, "Failed to play animation %i; Animation index invalid", animIndex); + return; + } + + if (is_anim_index_valid(player, player->activeAnimIndex)) { + player->states[player->activeAnimIndex].play = false; + } + + player->states[animIndex].play = true; + player->activeAnimIndex = animIndex; +} + +void R3D_PauseAnimation(R3D_AnimationPlayer* player) +{ + if (is_anim_index_valid(player, player->activeAnimIndex)) { + player->states[player->activeAnimIndex].play = false; + } +} + +void R3D_StopAnimation(R3D_AnimationPlayer* player) +{ + if (is_anim_index_valid(player, player->activeAnimIndex)) { + player->states[player->activeAnimIndex].play = false; + reset_anim_time(player, player->activeAnimIndex); + } +} + +void R3D_RewindAnimation(R3D_AnimationPlayer* player) +{ + if (is_anim_index_valid(player, player->activeAnimIndex)) { + reset_anim_time(player, player->activeAnimIndex); + } +} + +float R3D_GetAnimationTime(R3D_AnimationPlayer player, int animIndex) +{ + if (is_anim_index_valid(&player, animIndex)) { + return player.states[animIndex].currentTime; + } + return 0.0f; +} + +void R3D_SetAnimationTime(R3D_AnimationPlayer* player, int animIndex, float time) +{ + if (!is_anim_index_valid(player, animIndex)) return; + + const R3D_Animation* anim = &player->animLib.animations[animIndex]; + float duration = anim->duration / anim->ticksPerSecond; + + player->states[animIndex].currentTime = Wrap(time, 0.0f, duration); +} + +float R3D_GetAnimationSpeed(R3D_AnimationPlayer player, int animIndex) +{ + if (is_anim_index_valid(&player, animIndex)) { + return player.states[animIndex].speed; + } + return 0.0f; +} + +void R3D_SetAnimationSpeed(R3D_AnimationPlayer* player, int animIndex, float speed) +{ + if (is_anim_index_valid(player, animIndex)) { + player->states[animIndex].speed = speed; + } +} + +bool R3D_GetAnimationLoop(R3D_AnimationPlayer player, int animIndex) +{ + if (is_anim_index_valid(&player, animIndex)) { + return player.states[animIndex].loop; + } + return false; +} + +void R3D_SetAnimationLoop(R3D_AnimationPlayer* player, int animIndex, bool loop) +{ + if (is_anim_index_valid(player, animIndex)) { + player->states[animIndex].loop = loop; + } +} + +void R3D_AdvanceAnimationTime(R3D_AnimationPlayer* player, float dt) +{ + int animIndex = player->activeAnimIndex; + if (!is_anim_index_valid(player, animIndex)) { + return; + } + + R3D_AnimationState* state = &player->states[animIndex]; + if (!state->play) return; + + state->currentTime += state->speed * dt; + + const R3D_Animation* anim = &player->animLib.animations[animIndex]; + float duration = anim->duration / anim->ticksPerSecond; + + if ((state->speed > 0.0f && state->currentTime >= duration) || (state->speed < 0.0f && state->currentTime <= 0.0f)) { + if ((state->play = state->loop)) { + emit_event(player, R3D_ANIM_EVENT_LOOPED, animIndex); + state->currentTime -= copysignf(duration, state->speed); + } + else { + emit_event(player, R3D_ANIM_EVENT_FINISHED, animIndex); + state->currentTime = Clamp(state->currentTime, 0.0f, duration); + } + } +} + +void R3D_ComputeAnimationLocalPose(R3D_AnimationPlayer* player) +{ + if (is_anim_index_valid(player, player->activeAnimIndex)) compute_local_matrices(player); + else memcpy(player->localPose, player->skeleton.localBind, player->skeleton.boneCount * sizeof(Matrix)); +} + +void R3D_ComputeAnimationModelPose(R3D_AnimationPlayer* player) +{ + if (is_anim_index_valid(player, player->activeAnimIndex)) r3d_anim_matrices_compute(player); + else memcpy(player->modelPose, player->skeleton.modelBind, player->skeleton.boneCount * sizeof(Matrix)); +} + +void R3D_ComputeAnimationPose(R3D_AnimationPlayer* player) +{ + if (is_anim_index_valid(player, player->activeAnimIndex)) { + compute_local_matrices(player); + r3d_anim_matrices_compute(player); + } + else { + memcpy(player->localPose, player->skeleton.localBind, player->skeleton.boneCount * sizeof(Matrix)); + memcpy(player->modelPose, player->skeleton.modelBind, player->skeleton.boneCount * sizeof(Matrix)); + } +} + +void R3D_UploadAnimationPose(R3D_AnimationPlayer* player) +{ + for (int i = 0; i < player->skeleton.boneCount; i++) { + player->skinBuffer[i] = MatrixMultiply(player->skeleton.invBind[i], player->modelPose[i]); + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_1D, player->skinTexture); + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 4 * player->skeleton.boneCount, GL_RGBA, GL_FLOAT, player->skinBuffer); + glBindTexture(GL_TEXTURE_1D, 0); +} + +void R3D_UpdateAnimationPlayer(R3D_AnimationPlayer* player, float dt) +{ + R3D_ComputeAnimationPose(player); + R3D_UploadAnimationPose(player); + + R3D_AdvanceAnimationTime(player, dt); +} + +// ======================================== +// INTERNAL FUNCTIONS DEFINITIONS +// ======================================== + +void emit_event(R3D_AnimationPlayer* player, R3D_AnimationEvent event, int animIndex) +{ + if (player->eventCallback != NULL) { + player->eventCallback(player, event, animIndex, player->eventUserData); + } +} + +bool is_anim_index_valid(R3D_AnimationPlayer* player, int animIndex) +{ + return animIndex >= 0 && animIndex < player->animLib.count; +} + +void reset_anim_time(R3D_AnimationPlayer* player, int animIndex) +{ + assert(is_anim_index_valid(player, animIndex)); + + R3D_AnimationState* state = &player->states[animIndex]; + const R3D_Animation* anim = &player->animLib.animations[animIndex]; + + state->currentTime = (state->speed >= 0.0f) + ? 0.0f : anim->duration / anim->ticksPerSecond; +} + +void compute_local_matrices(R3D_AnimationPlayer* player) +{ + assert(is_anim_index_valid(player, player->activeAnimIndex)); + + const R3D_AnimationState* state = &player->states[player->activeAnimIndex]; + const R3D_Animation* anim = &player->animLib.animations[player->activeAnimIndex]; + + float tick = state->currentTime * anim->ticksPerSecond; + int boneCount = player->skeleton.boneCount; + Matrix* localPose = player->localPose; + + for (int iBone = 0; iBone < boneCount; iBone++) { + const R3D_AnimationChannel* channel = r3d_anim_channel_find(anim, iBone); + if (channel == NULL) { + localPose[iBone] = player->skeleton.localBind[iBone]; + continue; + } + Transform local = r3d_anim_channel_lerp(channel, tick, NULL, NULL); + localPose[iBone] = r3d_matrix_srt_quat(local.scale, QuaternionNormalize(local.rotation), local.translation); + } +} + +``` + +`src/r3d_animation_tree.c`: + +```c +/* r3d_animation_tree.c -- R3D Animation Tree Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include + +#include "./common/r3d_anim.h" + +// ======================================== +// TREE NODE TYPES +// ======================================== + +enum r3d_animtree_type { + R3D_ANIMTREE_ANIM = 1, + R3D_ANIMTREE_BLEND2, + R3D_ANIMTREE_ADD2, + R3D_ANIMTREE_SWITCH, + R3D_ANIMTREE_STM, + R3D_ANIMTREE_STM_X +}; + +typedef enum r3d_animtree_type r3d_animtree_type_t; +typedef struct r3d_animtree_base r3d_animtree_base_t; +typedef struct r3d_animtree_anim r3d_animtree_anim_t; +typedef struct r3d_animtree_blend2 r3d_animtree_blend2_t; +typedef struct r3d_animtree_add2 r3d_animtree_add2_t; +typedef struct r3d_animtree_switch r3d_animtree_switch_t; +typedef struct r3d_animtree_stm r3d_animtree_stm_t; +typedef struct r3d_animtree_stm_x r3d_animtree_stm_x_t; + +union R3D_AnimationTreeNode { + r3d_animtree_base_t* base; + r3d_animtree_anim_t* anim; + r3d_animtree_blend2_t* bln2; + r3d_animtree_add2_t* add2; + r3d_animtree_switch_t* swch; + r3d_animtree_stm_t* stm; + r3d_animtree_stm_x_t* stmx; +}; + +// ======================================== +// STATE MACHINE STRUCTURES +// ======================================== + +typedef struct { + R3D_AnimationStmIndex beginIdx; + R3D_AnimationStmIndex endIdx; + float endWeight; + R3D_StmEdgeParams params; +} r3d_stmedge_t; + +typedef struct { + int outCount; + int maxOut; + r3d_stmedge_t** outList; + r3d_stmedge_t* activeIn; +} r3d_stmstate_t; + +typedef struct { + bool yes; + float when; +} r3d_stmvisit_t; + +// ======================================== +// TREE NODE STRUCTURES +// ======================================== + +struct r3d_animtree_base { + r3d_animtree_type_t type; +}; + +struct r3d_animtree_anim { + r3d_animtree_type_t type; + const R3D_Animation* animation; + R3D_AnimationNodeParams params; + struct { + Transform last; + Transform rest0; + Transform restN; + int loops; + } root; +}; + +struct r3d_animtree_blend2 { + r3d_animtree_type_t type; + R3D_AnimationTreeNode inMain; + R3D_AnimationTreeNode inBlend; + R3D_Blend2NodeParams params; +}; + +struct r3d_animtree_add2 { + r3d_animtree_type_t type; + R3D_AnimationTreeNode inMain; + R3D_AnimationTreeNode inAdd; + R3D_Add2NodeParams params; +}; + +struct r3d_animtree_switch { + r3d_animtree_type_t type; + R3D_AnimationTreeNode* inList; + float* inWeights; + int inCount; + int prevIn; + float weightsInvSum; + R3D_SwitchNodeParams params; +}; + +struct r3d_animtree_stm { + r3d_animtree_type_t type; + int statesCount; + int edgesCount; + int maxStates; + int maxEdges; + R3D_AnimationStmIndex activeIdx; + R3D_AnimationTreeNode* nodeList; + r3d_stmedge_t* edgeList; + r3d_stmstate_t* stateList; + r3d_stmvisit_t* visitList; + struct { + r3d_stmedge_t** edges; + int idx; + int len; + r3d_stmedge_t** open; + r3d_stmedge_t** next; + bool* mark; + } path; +}; + +struct r3d_animtree_stm_x { + r3d_animtree_type_t type; + R3D_AnimationTreeNode nested; +}; + +// ======================================== +// TREE UPDATE/EVAL SUPPORT INFO STRUCTURES +// ======================================== + +typedef struct { + bool anodeDone; + float xFade; + float consumedTime; +} upinfo_t; + +typedef struct { + Transform motion; + Transform distance; +} rminfo_t; + +// ======================================== +// TREE NODE COMPUTE FUNCTION PROTOTYPES +// ======================================== + +static bool anode_update(const R3D_AnimationTree* atree, R3D_AnimationTreeNode anode, + float elapsedTime, upinfo_t* info); + +static bool anode_eval(const R3D_AnimationTree* atree, R3D_AnimationTreeNode anode, + int boneIdx, Transform* out, rminfo_t* info); + +static void anode_reset(R3D_AnimationTreeNode anode); + +// ======================================== +// BONE FUNCTIONS +// ======================================== + +static bool is_root_bone(const R3D_AnimationTree* atree, int boneIdx) +{ + return atree->rootBone == boneIdx; +} + +static bool valid_root_bone(int boneIdx) +{ + return boneIdx >= 0; +} + +static bool masked_bone(const R3D_BoneMask* bMask, int boneIdx) +{ + int maskBits = (sizeof(bMask->mask[0]) * 8); + int part = boneIdx / maskBits; + int bit = boneIdx % maskBits; + return (bMask->mask[part] & (1 << bit)) != 0; +} + +// ======================================== +// ANIMATION STATE MACHINE +// ======================================== + +static r3d_stmedge_t* stm_find_edge(const r3d_animtree_stm_t* node, const r3d_stmstate_t* state) +{ + int pathLen = node->path.len; + int pathIdx = node->path.idx; + + if (pathIdx < pathLen) { + return node->path.edges[pathIdx]; + } + else { + int edgesCount = state->outCount; + for (int edgeIdx = 0; edgeIdx < edgesCount; edgeIdx++) { + r3d_stmedge_t* edge = state->outList[edgeIdx]; + R3D_StmEdgeStatus status = edge->params.status; + bool open = (status == R3D_STM_EDGE_AUTO || status == R3D_STM_EDGE_ONCE); + if (open) return edge; + } + } + + return NULL; +} + +static bool stm_update_edge(const R3D_AnimationTree* atree, r3d_animtree_stm_t* node, + r3d_stmedge_t* edge, float elapsedTime, float* consumedTime, + bool* done) +{ + float xFade = edge->params.xFadeTime; + bool doXFade = (xFade > elapsedTime); + + if (doXFade) { + float wInc = Remap(elapsedTime, 0.0f, xFade, 0.0f, 1.0f); + edge->endWeight += wInc; + + float wClamp = CLAMP(edge->endWeight, 0.0f, 1.0f); + float wDelta = edge->endWeight - wClamp; + edge->endWeight = wClamp; + *consumedTime = (wInc > 0.0f ? elapsedTime * (1.0f - wDelta/wInc) : elapsedTime); + *done = FloatEquals(edge->endWeight, 1.0f); + } + else { + edge->endWeight = 1.0f; + *consumedTime = xFade; + *done = true; + } + + if (*done) return true; + + return anode_update(atree, node->nodeList[edge->beginIdx], elapsedTime, NULL); +} + +static bool stm_next_state(r3d_animtree_stm_t* node, r3d_stmedge_t* edge, + bool edgeDone, bool nodeDone, R3D_AnimationStmIndex* nextIdx) +{ + R3D_StmEdgeMode mode = edge->params.mode; + + bool ready = ((mode == R3D_STM_EDGE_INSTANT && edgeDone) || + (mode == R3D_STM_EDGE_ONDONE && nodeDone)); + + if (!ready) return false; + + R3D_AnimationStmIndex endIdx = edge->endIdx; + r3d_stmstate_t* state = &node->stateList[endIdx]; + R3D_AnimationTreeNode anode = node->nodeList[endIdx]; + + state->activeIn = edge; + anode_reset(anode); + + edge->endWeight = 0.0f; + if (edge->params.status == R3D_STM_EDGE_ONCE) { + edge->params.status = edge->params.nextStatus; + } + + *nextIdx = endIdx; + + return true; +} + +static bool stm_update_state(const R3D_AnimationTree* atree, r3d_animtree_stm_t* node, + float elapsedTime, float* consumedTime, + R3D_AnimationStmIndex* activeIdx, bool* doNext, bool* stmDone) +{ + R3D_AnimationTreeNode* nodeList = node->nodeList; + r3d_stmstate_t* state = &node->stateList[*activeIdx]; + + bool edgeDone = true; + float edgeTime = 0.0f; + r3d_stmedge_t* activeEdge = state->activeIn; + + if (activeEdge) { + if (!stm_update_edge(atree, node, activeEdge, elapsedTime, &edgeTime, &edgeDone)) { + return false; + } + } + + if (edgeDone) state->activeIn = NULL; + + r3d_stmedge_t* nextEdge = stm_find_edge(node, state); + upinfo_t nodeInfo = {.xFade = (nextEdge ? nextEdge->params.xFadeTime : 0.0f)}; + if (!anode_update(atree, nodeList[*activeIdx], elapsedTime, &nodeInfo)) { + return false; + } + + bool nodeDone = edgeDone && nodeInfo.anodeDone; + bool isNext = (nextEdge ? stm_next_state(node, nextEdge, edgeDone, nodeDone, activeIdx) : false); + + *stmDone = (edgeDone && (nodeList[*activeIdx].base->type == R3D_ANIMTREE_STM_X)); + *doNext = nodeDone && isNext && !*stmDone; + *consumedTime = MAX(edgeTime, nodeInfo.consumedTime); + + return true; +} + +static int expand_path(r3d_animtree_stm_t* node, r3d_stmedge_t** edges, r3d_stmedge_t** next, + int nextCount, int pathLen, const r3d_stmstate_t* state) +{ + int maxOpenPaths = node->maxEdges; + int maxPathLen = node->maxStates; + int outCount = state->outCount; + + int added = 0; + for (int edgeIdx = 0; edgeIdx < outCount; edgeIdx++) { + r3d_stmedge_t* edge = state->outList[edgeIdx]; + + bool closed = edge->params.status == R3D_STM_EDGE_OFF; + if (closed) continue; + + if (nextCount < maxOpenPaths) { + memcpy(&next[nextCount*maxPathLen], edges, pathLen * sizeof(r3d_stmedge_t*)); + next[nextCount*maxPathLen + pathLen] = edge; + nextCount += 1; + added += 1; + } + else { + R3D_TRACELOG(LOG_WARNING, "Failed to find path: max open paths count exceeded (%d)", maxOpenPaths); + break; + } + } + return added; +} + +static bool stm_find_path(r3d_animtree_stm_t* node, R3D_AnimationStmIndex targetIdx) +{ + int maxPathLen = node->maxStates; + r3d_stmedge_t** openPaths = node->path.open; + r3d_stmedge_t** nextPaths = node->path.next; + bool* marked = node->path.mark; + + memset(marked, false, node->statesCount * sizeof(*node->path.mark)); + marked[node->activeIdx] = true; + + int pathsCount = expand_path( + node, openPaths, openPaths, 0, 0, + &node->stateList[node->activeIdx] + ); + + int pathLen = 1; + while(pathsCount > 0) { + int nextCount = 0; + for (int pIdx = 0; pIdx < pathsCount; pIdx++) { + R3D_AnimationStmIndex stateIdx = openPaths[pIdx*maxPathLen + pathLen-1]->endIdx; + if (stateIdx == targetIdx) { + memcpy(node->path.edges, &openPaths[pIdx*maxPathLen], pathLen * sizeof(r3d_stmedge_t*)); + node->path.idx = 0; + node->path.len = pathLen; + return true; + } + if (marked[stateIdx]) continue; + else marked[stateIdx] = true; + + nextCount += expand_path( + node, &openPaths[pIdx*maxPathLen], nextPaths, + nextCount, pathLen, &node->stateList[stateIdx] + ); + } + + if (pathLen < maxPathLen) { + pathLen += 1; + } + else { + R3D_TRACELOG(LOG_WARNING, "Failed to find path: max path lenght exceeded (%d)", maxPathLen); + break; + } + + memcpy(openPaths, nextPaths, maxPathLen * nextCount * sizeof(r3d_stmedge_t*)); + pathsCount = nextCount; + } + + return false; +} + +// ======================================== +// TREE NODE CREATE FUNCTION +// ======================================== + +static R3D_AnimationTreeNode* anode_create(R3D_AnimationTree* atree, r3d_animtree_type_t type, size_t node_size) +{ + R3D_AnimationTreeNode* node; + + int poolSize = atree->nodePoolSize; + if (poolSize < atree->nodePoolMaxSize) { + node = &atree->nodePool[poolSize]; + node->base = RL_CALLOC(1, node_size); + node->base->type = type; + } + else { + return NULL; + } + + atree->nodePoolSize += 1; + return node; +} + +// ======================================== +// TREE NODE RESET FUNCTIONS +// ======================================== + +static void anode_reset_anim(r3d_animtree_anim_t* node) +{ + const R3D_Animation* a = node->animation; + R3D_AnimationState* s = &node->params.state; + + float duration = a->duration / a->ticksPerSecond; + s->currentTime = (s->speed >= 0.0f) ? 0.0f : duration; +} + +static void anode_reset_blend2(r3d_animtree_blend2_t* node) +{ + anode_reset(node->inMain); + anode_reset(node->inBlend); +} + +static void anode_reset_add2(r3d_animtree_add2_t* node) +{ + anode_reset(node->inMain); + anode_reset(node->inAdd); +} + +static void anode_reset_switch(r3d_animtree_switch_t* node) +{ + int inCount = node->inCount; + int activeIn = node->params.activeInput; + + for (int i = 0; i < inCount; i++) { + node->inWeights[i] = 0.0f; + } + node->inWeights[activeIn] = 1.0f; + + for (int i = 0; i < inCount; i++) { + anode_reset(node->inList[i]); + } +} + +static void anode_reset_stm(r3d_animtree_stm_t* node) +{ + node->activeIdx = 0; + node->path.idx = 0; + node->path.len = 0; + node->stateList[0].activeIn = NULL; + + anode_reset(node->nodeList[0]); +} + +// other stm reset version: reset only active node+state +/* +static void anode_reset_stm(r3d_animtree_stm_t* node) +{ + int activeIdx = node->activeIdx; + r3d_stmedge_t* edge = node->stateList[activeIdx].activeIn; + if (edge) edge->endWeight = 0.0f; + + anode_reset(node->nodeList[activeIdx]); +} +*/ + +static void anode_reset_stm_x(r3d_animtree_stm_x_t* node) +{ + anode_reset(node->nested); +} + +static void anode_reset(R3D_AnimationTreeNode anode) +{ + switch(anode.base->type) { + case R3D_ANIMTREE_ANIM: anode_reset_anim(anode.anim); break; + case R3D_ANIMTREE_BLEND2: anode_reset_blend2(anode.bln2); break; + case R3D_ANIMTREE_ADD2: anode_reset_add2(anode.add2); break; + case R3D_ANIMTREE_SWITCH: anode_reset_switch(anode.swch); break; + case R3D_ANIMTREE_STM: anode_reset_stm(anode.stm); break; + case R3D_ANIMTREE_STM_X: anode_reset_stm_x(anode.stmx); break; + default: + R3D_TRACELOG(LOG_WARNING, "Failed to reset node: invalid type %d", anode.base->type); + break; + } +} + +// ======================================== +// TREE NODE UPDATE FUNCTIONS +// ======================================== + +static bool anode_update_anim(r3d_animtree_anim_t* node, float elapsedTime, upinfo_t* info) +{ + const R3D_Animation* a = node->animation; + + R3D_AnimationState* s = &node->params.state; + if (!s->play) { + if (info) { + *info = (upinfo_t) { + .anodeDone = true, + .xFade = info->xFade, + .consumedTime = 0.0f + }; + } + return true; + } + + float speed = s->speed; + float duration = a->duration / a->ticksPerSecond; + float tInc = speed * elapsedTime; + float tCur = s->currentTime + tInc; + + s->currentTime = tCur; + + bool cross = ((speed < 0.0f && tCur <= 0.0f) || (speed > 0.0f && tCur >= duration)); + + if (cross) { + if ((s->play = s->loop)) { + s->currentTime -= copysignf(duration, speed); + } + else { + float tClamp = CLAMP(s->currentTime, 0.0f, duration); + float tDelta = s->currentTime - tClamp; + elapsedTime = (!FloatEquals(tInc, 0.0f) ? elapsedTime * (1.0f - tDelta/tInc) : 0.0f); + s->currentTime = tClamp; + } + node->root.loops = (int)(elapsedTime / duration); + } + else { + node->root.loops = -1; + } + + if (info) { + float xFade = info->xFade; + float durXFade = CLAMP(duration - xFade, 0.0f, duration); + bool crossXFade = ((speed < 0.0f && tCur <= xFade) || (speed > 0.0f && tCur >= durXFade)); + *info = (upinfo_t) { + .anodeDone = crossXFade ? node->params.looper : false, + .xFade = xFade, + .consumedTime = !FloatEquals(speed, 0.0f) ? elapsedTime : 0.0f + }; + } + + return true; +} + +static bool anode_update_blend2(const R3D_AnimationTree* atree, r3d_animtree_blend2_t* node, float elapsedTime, upinfo_t* info) +{ + return ( + anode_update(atree, node->inMain, elapsedTime, info) && + anode_update(atree, node->inBlend, elapsedTime, NULL) + ); +} + +static bool anode_update_add2(const R3D_AnimationTree* atree, r3d_animtree_add2_t* node, float elapsedTime, upinfo_t* info) +{ + return ( + anode_update(atree, node->inMain, elapsedTime, info) && + anode_update(atree, node->inAdd, elapsedTime, NULL) + ); +} + +static bool anode_update_switch(const R3D_AnimationTree* atree, r3d_animtree_switch_t* node, float elapsedTime, upinfo_t* info) +{ + int inCount = node->inCount; + int activeIn = node->params.activeInput; + if (activeIn < 0 || activeIn >= inCount) { + R3D_TRACELOG(LOG_WARNING, "Failed to update switch: active input %d out of range (%d)", activeIn, inCount); + return false; + } + + bool reset = (activeIn == node->prevIn) ? false : !node->params.synced; + if (reset) anode_reset(node->inList[activeIn]); + + for (int i = 0; i < inCount; i++) { + if (!anode_update(atree, node->inList[i], elapsedTime, + (i == activeIn) ? info : NULL)) { + return false; + } + } + node->prevIn = activeIn; + + float xFade = node->params.xFadeTime; + bool noXFade = (xFade <= elapsedTime); + + if (noXFade) { + for (int i = 0; i < inCount; i++) { + node->inWeights[i] = 0.0f; + } + node->inWeights[activeIn] = 1.0f; + } + else { + float wFade = Remap(elapsedTime, 0.0f, xFade, 0.0f, 1.0f); + for (int i = 0; i < inCount; i++) { + float wSign = (i == activeIn) ? 1.0f : -1.0f; + node->inWeights[i] = CLAMP(node->inWeights[i] + wSign*wFade, 0.0f, 1.0f); + } + } + + float wSum = 0.0f; + for (int i = 0; i < inCount; i++) { + wSum += node->inWeights[i]; + } + node->weightsInvSum = 1.0f / wSum; + + return true; +} + +static bool anode_update_stm(const R3D_AnimationTree* atree, r3d_animtree_stm_t* node, float elapsedTime, upinfo_t* info) +{ + R3D_AnimationStmIndex activeIdx = node->activeIdx; + + r3d_stmvisit_t* visited = node->visitList; + memset(visited, 0, node->statesCount * sizeof(*node->visitList)); + + float startTime = elapsedTime; + bool doNext = true; + bool stmDone = false; + + while(doNext) + { + if (visited[activeIdx].yes && FloatEquals(visited[activeIdx].when, elapsedTime)) { + R3D_TRACELOG(LOG_WARNING, "Failed to update stm: cycle detected, aborted"); + return false; + } + visited[activeIdx] = (r3d_stmvisit_t){.yes = true, .when = elapsedTime}; + + float consumedTime; + if (!stm_update_state(atree, node, elapsedTime, &consumedTime, &activeIdx, &doNext, &stmDone)) { + return false; + } + elapsedTime -= consumedTime; + + if (doNext) { + int pathLen = node->path.len; + int pathIdx = node->path.idx; + if (pathIdx < pathLen) node->path.idx += 1; + } + + if (FloatEquals(elapsedTime, 0.0f)) { + break; + } + + if (elapsedTime < 0.0f) { + R3D_TRACELOG(LOG_WARNING, "Failed to update stm: incorrect time calculation (%f)", elapsedTime); + return false; + } + } + + node->activeIdx = activeIdx; + + if (info) { + *info = (upinfo_t) { + .anodeDone = stmDone, + .consumedTime = startTime - elapsedTime + }; + } + + return true; +} + +static bool anode_update_stm_x(const R3D_AnimationTree* atree, r3d_animtree_stm_x_t* node, float elapsedTime, upinfo_t* info) +{ + return anode_update(atree, node->nested, elapsedTime, info); +} + +static bool anode_update(const R3D_AnimationTree* atree, R3D_AnimationTreeNode anode, float elapsedTime, upinfo_t* info) +{ + switch(anode.base->type) { + case R3D_ANIMTREE_ANIM: return anode_update_anim(anode.anim, elapsedTime, info); + case R3D_ANIMTREE_BLEND2: return anode_update_blend2(atree, anode.bln2, elapsedTime, info); + case R3D_ANIMTREE_ADD2: return anode_update_add2(atree, anode.add2, elapsedTime, info); + case R3D_ANIMTREE_SWITCH: return anode_update_switch(atree, anode.swch, elapsedTime, info); + case R3D_ANIMTREE_STM: return anode_update_stm(atree, anode.stm, elapsedTime, info); + case R3D_ANIMTREE_STM_X: return anode_update_stm_x(atree, anode.stmx, elapsedTime, info); + default: + R3D_TRACELOG(LOG_WARNING, "Failed to update animation tree: invalid node type %d", anode.base->type); + break; + } + return false; +} + +// ======================================== +// TREE NODE EVAL FUNCTIONS +// ======================================== + +static bool anode_eval_anim(const R3D_AnimationTree* atree, r3d_animtree_anim_t* node, + int boneIdx, Transform* out, rminfo_t* info) +{ + const R3D_Animation* anim = node->animation; + const R3D_AnimationState state = node->params.state; + const R3D_AnimationChannel* channel = r3d_anim_channel_find(anim, boneIdx); + + if (channel) { + *out = r3d_anim_channel_lerp(channel, state.currentTime * anim->ticksPerSecond, NULL, NULL); + } + else { + MatrixDecompose(atree->player.skeleton.localBind[boneIdx], &out->translation, &out->rotation, &out->scale); + } + + if (node->params.evalCallback) { + node->params.evalCallback(anim, state, boneIdx, out, node->params.evalUserData); + } + + if (channel && is_root_bone(atree, boneIdx)) { + if (info) { + Transform motion = {0}; + const int loops = node->root.loops; + + if (loops > 0) { + motion = r3d_anim_transform_scale( + r3d_anim_transform_subtr(node->root.restN, node->root.rest0), + (float)loops + ); + } + + if (loops >= 0) { + const bool forward = state.speed > 0.0f; + const Transform rest0 = forward ? node->root.rest0 : node->root.restN; + const Transform restN = forward ? node->root.restN : node->root.rest0; + const Transform split = r3d_anim_transform_add( + r3d_anim_transform_subtr(restN, node->root.last), + r3d_anim_transform_subtr(*out, rest0) + ); + motion = r3d_anim_transform_add(motion, split); + motion.rotation = QuaternionNormalize(motion.rotation); + info->motion = motion; + } + else { + info->motion = r3d_anim_transform_subtr(*out, node->root.last); + } + + info->distance = r3d_anim_transform_subtr(*out, node->root.rest0); + } + + node->root.last = *out; + } + + return true; +} + +static bool anode_eval_blend2(const R3D_AnimationTree* atree, r3d_animtree_blend2_t* node, + int boneIdx, Transform* out, rminfo_t* info) +{ + const R3D_BoneMask* bmask = node->params.boneMask; + const bool doBlend = !bmask || masked_bone(bmask, boneIdx); + const bool isRm = info && is_root_bone(atree, boneIdx); + + rminfo_t rm[2] = {0}; + Transform in[2] = {0}; + + bool success = anode_eval(atree, node->inMain, boneIdx, &in[0], isRm ? &rm[0] : NULL); + if (!success) { + R3D_TRACELOG(LOG_WARNING, "Failed to eval blend2 node"); + return false; + } + + if (doBlend) { + success = anode_eval(atree, node->inBlend, boneIdx, &in[1], isRm ? &rm[1] : NULL); + if (!success) { + R3D_TRACELOG(LOG_WARNING, "Failed to eval blend2 node"); + return false; + } + } + + const float w = CLAMP(node->params.blend, 0.0f, 1.0f); + *out = doBlend ? r3d_anim_transform_lerp(in[0], in[1], w) : in[0]; + + if (isRm) { + *info = doBlend ? (rminfo_t){ + .motion = r3d_anim_transform_lerp(rm[0].motion, rm[1].motion, w), + .distance = r3d_anim_transform_lerp(rm[0].distance, rm[1].distance, w) + } : rm[0]; + } + + return true; +} + +static bool anode_eval_add2(const R3D_AnimationTree* atree, r3d_animtree_add2_t* node, + int boneIdx, Transform* out, rminfo_t* info) +{ + const R3D_BoneMask* bmask = node->params.boneMask; + const bool doAdd = !bmask || masked_bone(bmask, boneIdx); + const bool isRm = info && is_root_bone(atree, boneIdx); + + rminfo_t rm[2] = {0}; + Transform in[2] = {0}; + + bool success = anode_eval(atree, node->inMain, boneIdx, &in[0], isRm ? &rm[0] : NULL); + if (!success) { + R3D_TRACELOG(LOG_WARNING, "Failed to eval add2 node"); + return false; + } + + if (doAdd) { + success = anode_eval(atree, node->inAdd, boneIdx, &in[1], isRm ? &rm[1] : NULL); + if (!success) { + R3D_TRACELOG(LOG_WARNING, "Failed to eval add2 node"); + return false; + } + } + + const float w = CLAMP(node->params.weight, 0.0f, 1.0f); + *out = doAdd ? r3d_anim_transform_add_v(in[0], in[1], w) : in[0]; + + if (isRm) { + *info = doAdd ? (rminfo_t){ + .motion = r3d_anim_transform_lerp(rm[0].motion, rm[1].motion, w), + .distance = r3d_anim_transform_lerp(rm[0].distance, rm[1].distance, w) + } : rm[0]; + } + + return true; +} + +static bool anode_eval_switch(const R3D_AnimationTree* atree, r3d_animtree_switch_t* node, + int boneIdx, Transform* out, rminfo_t* info) +{ + const bool isRm = info && is_root_bone(atree, boneIdx); + const float wInvSum = node->weightsInvSum; + + rminfo_t rm = {0}; + Transform result = {0}; + + for (int i = 0; i < node->inCount; i++) + { + const float w = node->inWeights[i] * wInvSum; + if (FloatEquals(w, 0.0f)) continue; + + rminfo_t inRm = {0}; + Transform inTr = {0}; + + bool success = anode_eval(atree, node->inList[i], boneIdx, &inTr, isRm ? &inRm : NULL); + if (!success) { + R3D_TRACELOG(LOG_WARNING, "Failed to eval switch node: input %d failed", i); + return false; + } + + result = r3d_anim_transform_addx_v(result, inTr, w); + + if (isRm) { + rm.motion = r3d_anim_transform_addx_v(rm.motion, inRm.motion, w); + rm.distance = r3d_anim_transform_addx_v(rm.distance, inRm.distance, w); + } + } + + *out = result; + if (isRm) *info = rm; + + return true; +} + +static bool anode_eval_stm(const R3D_AnimationTree* atree, r3d_animtree_stm_t* node, + int boneIdx, Transform* out, rminfo_t* info) +{ + const R3D_AnimationStmIndex activeIdx = node->activeIdx; + const bool isRm = info && is_root_bone(atree, boneIdx); + + rminfo_t activeRm = {0}; + Transform activeTr = {0}; + + bool success = anode_eval(atree, node->nodeList[activeIdx], boneIdx, &activeTr, isRm ? &activeRm : NULL); + if (!success) { + R3D_TRACELOG(LOG_WARNING, "Failed to eval stm state %d", activeIdx); + return false; + } + + const r3d_stmedge_t* edge = node->stateList[activeIdx].activeIn; + + if (!edge) { + *out = activeTr; + if (isRm) *info = activeRm; + return true; + } + + rminfo_t edgeRm = {0}; + Transform edgeTr = {0}; + + success = anode_eval(atree, node->nodeList[edge->beginIdx], boneIdx, &edgeTr, isRm ? &edgeRm : NULL); + if (!success) { + R3D_TRACELOG(LOG_WARNING, "Failed to eval stm state %d", edge->beginIdx); + return false; + } + + const float endWeight = CLAMP(edge->endWeight, 0.0f, 1.0f); + *out = r3d_anim_transform_lerp(edgeTr, activeTr, endWeight); + + if (isRm) { + *info = (rminfo_t){ + .motion = r3d_anim_transform_lerp(edgeRm.motion, activeRm.motion, endWeight), + .distance = r3d_anim_transform_lerp(edgeRm.distance, activeRm.distance, endWeight) + }; + } + + return true; +} + +static bool anode_eval_stm_x(const R3D_AnimationTree* atree, r3d_animtree_stm_x_t* node, + int boneIdx, Transform* out, rminfo_t* info) +{ + return anode_eval(atree, node->nested, boneIdx, out, info); +} + +static bool anode_eval(const R3D_AnimationTree* atree, R3D_AnimationTreeNode anode, + int boneIdx, Transform* out, rminfo_t* info) +{ + switch(anode.base->type) { + case R3D_ANIMTREE_ANIM: return anode_eval_anim(atree, anode.anim, boneIdx, out, info); + case R3D_ANIMTREE_BLEND2: return anode_eval_blend2(atree, anode.bln2, boneIdx, out, info); + case R3D_ANIMTREE_ADD2: return anode_eval_add2(atree, anode.add2, boneIdx, out, info); + case R3D_ANIMTREE_SWITCH: return anode_eval_switch(atree, anode.swch, boneIdx, out, info); + case R3D_ANIMTREE_STM: return anode_eval_stm(atree, anode.stm, boneIdx, out, info); + case R3D_ANIMTREE_STM_X: return anode_eval_stm_x(atree, anode.stmx, boneIdx, out, info); + default: + R3D_TRACELOG(LOG_WARNING, "Failed to eval animation tree: invalid node type %d", anode.base->type); + break; + } + return false; +} + +// ======================================== +// INTERNAL ANIMATION TREE FUNCTIONS +// ======================================== + +static bool atree_blend2_add(r3d_animtree_blend2_t* parent, R3D_AnimationTreeNode anode, int inIdx) +{ + switch(inIdx) { + case 0: parent->inMain = anode; return true; + case 1: parent->inBlend = anode; return true; + default: + R3D_TRACELOG(LOG_WARNING, "Failed to add node into blend2: invalid input index %d", inIdx); + break; + }; + return false; +} + +static bool atree_add2_add(r3d_animtree_add2_t* parent, R3D_AnimationTreeNode anode, int inIdx) +{ + switch(inIdx) { + case 0: parent->inMain = anode; return true; + case 1: parent->inAdd = anode; return true; + default: + R3D_TRACELOG(LOG_WARNING, "Failed to add node into add2: invalid input index %d", inIdx); + }; + return false; +} + +static bool atree_switch_add(r3d_animtree_switch_t* parent, R3D_AnimationTreeNode anode, int inIdx) +{ + if (inIdx < parent->inCount) { + parent->inList[inIdx] = anode; + return true; + } + R3D_TRACELOG(LOG_WARNING, "Failed to add node into switch: invalid input index %d", inIdx); + return false; +} + +static R3D_AnimationTreeNode* atree_anim_create(R3D_AnimationTree* atree, R3D_AnimationNodeParams params, bool reset) +{ + const R3D_Animation* a = R3D_GetAnimation(atree->player.animLib, params.name); + if (!a) { + R3D_TRACELOG(LOG_WARNING, "Failed to create animation node: animation \"%s\" not found", params.name); + return NULL; + } + R3D_AnimationTreeNode* anode = anode_create(atree, R3D_ANIMTREE_ANIM, sizeof(r3d_animtree_anim_t)); + if (!anode) return NULL; + + r3d_animtree_anim_t* anim = anode->anim; + anim->animation = a; + anim->params = params; + if (reset) anode_reset_anim(anim); + + int boneIdx = atree->rootBone; + if (valid_root_bone(boneIdx)) { + const R3D_AnimationState* s = &anim->params.state; + const R3D_AnimationChannel* c = r3d_anim_channel_find(a, boneIdx); + if (c) { + anim->root.last = r3d_anim_channel_lerp( + c, s->currentTime * a->ticksPerSecond, + &anim->root.rest0, &anim->root.restN + ); + } + else { + Transform bind; + MatrixDecompose( + atree->player.skeleton.localBind[boneIdx], + &bind.translation, &bind.rotation, &bind.scale + ); + anim->root.last = bind; + anim->root.rest0 = bind; + anim->root.restN = bind; + } + anim->root.loops = -1; + } + + return anode; +} + +static R3D_AnimationTreeNode* atree_blend2_create(R3D_AnimationTree* atree, R3D_Blend2NodeParams params) +{ + R3D_AnimationTreeNode* anode = anode_create( + atree, R3D_ANIMTREE_BLEND2, + sizeof(r3d_animtree_blend2_t) + ); + if (!anode) return NULL; + + anode->bln2->params = params; + return anode; +} + +static R3D_AnimationTreeNode* atree_add2_create(R3D_AnimationTree* atree, R3D_Add2NodeParams params) +{ + R3D_AnimationTreeNode* anode = anode_create( + atree, R3D_ANIMTREE_ADD2, + sizeof(r3d_animtree_add2_t) + ); + if (!anode) return NULL; + + anode->add2->params = params; + return anode; +} + +static R3D_AnimationTreeNode* atree_switch_create(R3D_AnimationTree* atree, int inCount, R3D_SwitchNodeParams params) +{ + R3D_AnimationTreeNode* anode = anode_create( + atree, R3D_ANIMTREE_SWITCH, + sizeof(r3d_animtree_switch_t) + ); + if (!anode) return NULL; + + r3d_animtree_switch_t* swch = anode->swch; + swch->inList = RL_MALLOC(inCount * sizeof(*swch->inList)); + swch->inWeights = RL_CALLOC(inCount, sizeof(*swch->inWeights)); + swch->inCount = inCount; + swch->params = params; + + swch->inWeights[params.activeInput] = 1.0f; + return anode; +} + +static R3D_AnimationTreeNode* atree_stm_create(R3D_AnimationTree* atree, int statesCount, int edgesCount, bool travel) +{ + R3D_AnimationTreeNode* anode = anode_create(atree, R3D_ANIMTREE_STM, sizeof(r3d_animtree_stm_t)); + if (!anode) return NULL; + + r3d_animtree_stm_t* stm = anode->stm; + stm->nodeList = RL_MALLOC(statesCount * sizeof(*stm->nodeList)); + stm->edgeList = RL_MALLOC(edgesCount * sizeof(*stm->edgeList)); + stm->stateList = RL_MALLOC(statesCount * sizeof(*stm->stateList)); + stm->visitList = RL_MALLOC(statesCount * sizeof(*stm->visitList)); + stm->maxStates = statesCount; + stm->maxEdges = edgesCount; + if (travel) { + stm->path.edges = RL_MALLOC(statesCount * sizeof(*stm->path.edges)); + stm->path.open = RL_MALLOC(edgesCount * statesCount * sizeof(*stm->path.open)); + stm->path.next = RL_MALLOC(edgesCount * statesCount * sizeof(*stm->path.next)); + stm->path.mark = RL_MALLOC(statesCount * sizeof(*stm->path.mark)); + } + return anode; +} + +static R3D_AnimationTreeNode* atree_stm_x_create(R3D_AnimationTree* atree, R3D_AnimationTreeNode nested) +{ + R3D_AnimationTreeNode* anode = anode_create(atree, R3D_ANIMTREE_STM_X, sizeof(r3d_animtree_stm_x_t)); + if (!anode) return NULL; + + anode->stmx->nested = nested; + return anode; +} + +static R3D_AnimationStmIndex atree_state_create(r3d_animtree_stm_t* node, R3D_AnimationTreeNode anode, int edgesCount) +{ + R3D_AnimationStmIndex nextIdx = node->statesCount; + if (nextIdx >= node->maxStates) return -1; + + r3d_stmstate_t* state = &node->stateList[nextIdx]; + *state = (r3d_stmstate_t){ + .outList = (edgesCount > 0 ? RL_MALLOC(edgesCount * sizeof(*state->outList)) : NULL), + .outCount = 0, + .maxOut = edgesCount, + .activeIn = NULL + }; + + node->nodeList[nextIdx] = anode; + node->statesCount += 1; + + return nextIdx; +} + +static R3D_AnimationStmIndex atree_edge_create(r3d_animtree_stm_t* node, R3D_AnimationStmIndex beginIdx, + R3D_AnimationStmIndex endIdx, R3D_StmEdgeParams params) +{ + R3D_AnimationStmIndex nextIdx = node->edgesCount; + if (nextIdx >= node->maxEdges) return -1; + + r3d_stmedge_t* edge = &node->edgeList[nextIdx]; + *edge = (r3d_stmedge_t) { + .beginIdx = beginIdx, + .endIdx = endIdx, + .endWeight = 0.0f, + .params = params + }; + + r3d_stmstate_t* beginState = &node->stateList[beginIdx]; + int outCount = beginState->outCount; + if (outCount >= beginState->maxOut) return -1; + beginState->outList[outCount] = edge; + beginState->outCount += 1; + + node->edgesCount += 1; + return nextIdx; +} + +static void atree_delete(R3D_AnimationTreeNode anode) +{ + switch(anode.base->type) { + case R3D_ANIMTREE_ANIM: + case R3D_ANIMTREE_BLEND2: + case R3D_ANIMTREE_ADD2: + case R3D_ANIMTREE_STM_X: + return; + case R3D_ANIMTREE_SWITCH: + RL_FREE(anode.swch->inList); + RL_FREE(anode.swch->inWeights); + return; + case R3D_ANIMTREE_STM: + for (int i = 0; i < anode.stm->statesCount; i++) { + if (anode.stm->stateList[i].outList) { + RL_FREE(anode.stm->stateList[i].outList); + } + } + RL_FREE(anode.stm->nodeList); + RL_FREE(anode.stm->edgeList); + RL_FREE(anode.stm->stateList); + RL_FREE(anode.stm->visitList); + if (anode.stm->path.edges) RL_FREE(anode.stm->path.edges); + if (anode.stm->path.open) RL_FREE(anode.stm->path.open); + if (anode.stm->path.next) RL_FREE(anode.stm->path.next); + if (anode.stm->path.mark) RL_FREE(anode.stm->path.mark); + return; + default: + R3D_TRACELOG(LOG_WARNING, "Failed to delete node: invalid type %d", anode.base->type); + break; + } +} + +static void atree_update(R3D_AnimationTree* atree, float elapsedTime, + Transform* rootMotion, Transform* rootDistance) +{ + if (elapsedTime < 0.0f) return; + + R3D_AnimationPlayer* player = &atree->player; + const int boneCount = player->skeleton.boneCount; + + bool success = anode_update(atree, *atree->rootNode, elapsedTime, NULL); + if (!success) goto failure; + + for (int boneIdx = 0; boneIdx < boneCount; boneIdx++) + { + const bool isRootBone = is_root_bone(atree, boneIdx); + rminfo_t rmInfo = {0}; + Transform out = {0}; + + success = anode_eval(atree, *atree->rootNode, boneIdx, &out, isRootBone ? &rmInfo : NULL); + if (!success) goto failure; + + if (isRootBone) { + if (rootMotion) *rootMotion = rmInfo.motion; + if (rootDistance) *rootDistance = rmInfo.distance; + out = r3d_anim_transform_subtr(out, rmInfo.distance); + } + + if (atree->updateCallback) { + atree->updateCallback(player, boneIdx, &out, atree->updateUserData); + } + + player->localPose[boneIdx] = r3d_matrix_srt_quat(out.scale, out.rotation, out.translation); + } + + r3d_anim_matrices_compute(player); + R3D_UploadAnimationPose(player); + return; + +failure: + R3D_TRACELOG(LOG_ERROR, "Animation tree failed"); + memcpy(player->localPose, player->skeleton.localBind, boneCount * sizeof(Matrix)); + memcpy(player->modelPose, player->skeleton.modelBind, boneCount * sizeof(Matrix)); + R3D_UploadAnimationPose(player); +} + +static void atree_travel(r3d_animtree_stm_t* node, R3D_AnimationStmIndex targetIdx) +{ + if (node->activeIdx == targetIdx) return; + + bool found = stm_find_path(node, targetIdx); + if (!found) { + r3d_stmstate_t* state = &node->stateList[targetIdx]; + R3D_AnimationTreeNode anode = node->nodeList[targetIdx]; + state->activeIn = NULL; + node->activeIdx = targetIdx; + node->path.len = 0; + anode_reset(anode); + } +} + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_AnimationTree R3D_LoadAnimationTree(R3D_AnimationPlayer player, int maxSize) +{ + return R3D_LoadAnimationTreePro(player, maxSize, -1, NULL, NULL); +} + +R3D_AnimationTree R3D_LoadAnimationTreeEx(R3D_AnimationPlayer player, int maxSize, int rootBone) +{ + return R3D_LoadAnimationTreePro(player, maxSize, rootBone, NULL, NULL); +} + +R3D_AnimationTree R3D_LoadAnimationTreePro(R3D_AnimationPlayer player, int maxSize, int rootBone, + R3D_AnimationTreeCallback updateCallback, + void* updateUserData) +{ + R3D_AnimationTree tree = {0}; + tree.player = player; + tree.nodePool = RL_MALLOC(maxSize * sizeof(*tree.nodePool)); + tree.nodePoolMaxSize = maxSize; + tree.rootBone = rootBone; + tree.updateCallback = updateCallback; + tree.updateUserData = updateUserData; + return tree; +} + +void R3D_UnloadAnimationTree(R3D_AnimationTree tree) +{ + int poolSize = tree.nodePoolSize; + for (int i = 0; i < poolSize; i++) { + R3D_AnimationTreeNode node = tree.nodePool[i]; + atree_delete(node); + RL_FREE(node.base); + } + RL_FREE(tree.nodePool); +} + +void R3D_UpdateAnimationTree(R3D_AnimationTree* tree, float dt) +{ + R3D_UpdateAnimationTreeEx(tree, dt, NULL, NULL); +} + +void R3D_UpdateAnimationTreeEx(R3D_AnimationTree* tree, float dt, Transform* rootMotion, Transform* rootDistance) +{ + atree_update(tree, dt, rootMotion, rootDistance); +} + +void R3D_AddRootAnimationNode(R3D_AnimationTree* tree, R3D_AnimationTreeNode* node) +{ + tree->rootNode = node; +} + +bool R3D_AddAnimationNode(R3D_AnimationTreeNode* parent, R3D_AnimationTreeNode* node, int inputIndex) +{ + switch(parent->base->type) { + case R3D_ANIMTREE_ANIM: + case R3D_ANIMTREE_STM: + case R3D_ANIMTREE_STM_X: return false; + case R3D_ANIMTREE_BLEND2: return atree_blend2_add(parent->bln2, *node, inputIndex); + case R3D_ANIMTREE_ADD2: return atree_add2_add(parent->add2, *node, inputIndex); + case R3D_ANIMTREE_SWITCH: return atree_switch_add(parent->swch, *node, inputIndex); + default: + R3D_TRACELOG(LOG_WARNING, "Failed to add animation node: invalid parent type %d", parent->base->type); + break; + } + return false; +} + +R3D_AnimationTreeNode* R3D_CreateAnimationNode(R3D_AnimationTree* tree, R3D_AnimationNodeParams params) +{ + return atree_anim_create(tree, params, false); +} + +R3D_AnimationTreeNode* R3D_CreateAnimationNodeEx(R3D_AnimationTree* tree, R3D_AnimationNodeParams params, bool setTime) +{ + return atree_anim_create(tree, params, setTime); +} + +R3D_AnimationTreeNode* R3D_CreateBlend2Node(R3D_AnimationTree* tree, R3D_Blend2NodeParams params) +{ + return atree_blend2_create(tree, params); +} + +R3D_AnimationTreeNode* R3D_CreateAdd2Node(R3D_AnimationTree* tree, R3D_Add2NodeParams params) +{ + return atree_add2_create(tree, params); +} + +R3D_AnimationTreeNode* R3D_CreateSwitchNode(R3D_AnimationTree* tree, int inputCount, R3D_SwitchNodeParams params) +{ + return atree_switch_create(tree, inputCount, params); +} + +R3D_AnimationTreeNode* R3D_CreateStmNode(R3D_AnimationTree* tree, int statesCount, int edgesCount) +{ + return atree_stm_create(tree, statesCount, edgesCount, false); +} + +R3D_AnimationTreeNode* R3D_CreateStmNodeEx(R3D_AnimationTree* tree, int statesCount, int edgesCount, bool enableTravel) +{ + return atree_stm_create(tree, statesCount, edgesCount, enableTravel); +} + +R3D_AnimationTreeNode* R3D_CreateStmXNode(R3D_AnimationTree* tree, R3D_AnimationTreeNode* nestedNode) +{ + return atree_stm_x_create(tree, *nestedNode); +} + +R3D_AnimationStmIndex R3D_CreateStmNodeState(R3D_AnimationTreeNode* stmNode, R3D_AnimationTreeNode* stateNode, int outEdgesCount) +{ + return atree_state_create(stmNode->stm, *stateNode, outEdgesCount); +} + +R3D_AnimationStmIndex R3D_CreateStmNodeEdge(R3D_AnimationTreeNode* stmNode, R3D_AnimationStmIndex beginStateIndex, + R3D_AnimationStmIndex endStateIndex, R3D_StmEdgeParams params) +{ + return atree_edge_create(stmNode->stm, beginStateIndex, endStateIndex, params); +} + +void R3D_SetAnimationNodeParams(R3D_AnimationTreeNode* node, R3D_AnimationNodeParams params) +{ + node->anim->params = params; +} + +R3D_AnimationNodeParams R3D_GetAnimationNodeParams(R3D_AnimationTreeNode* node) +{ + return node->anim->params; +} + +void R3D_SetBlend2NodeParams(R3D_AnimationTreeNode* node, R3D_Blend2NodeParams params) +{ + node->bln2->params = params; +} + +R3D_Blend2NodeParams R3D_GetBlend2NodeParams(R3D_AnimationTreeNode* node) +{ + return node->bln2->params; +} + +void R3D_SetAdd2NodeParams(R3D_AnimationTreeNode* node, R3D_Add2NodeParams params) +{ + node->add2->params = params; +} + +R3D_Add2NodeParams R3D_GetAdd2NodeParams(R3D_AnimationTreeNode* node) +{ + return node->add2->params; +} + +void R3D_SetSwitchNodeParams(R3D_AnimationTreeNode* node, R3D_SwitchNodeParams params) +{ + node->swch->params = params; +} + +R3D_SwitchNodeParams R3D_GetSwitchNodeParams(R3D_AnimationTreeNode* node) +{ + return node->swch->params; +} + +void R3D_SetStmNodeEdgeParams(R3D_AnimationTreeNode* node, R3D_AnimationStmIndex edgeIndex, R3D_StmEdgeParams params) +{ + node->stm->edgeList[edgeIndex].params = params; +} + +R3D_StmEdgeParams R3D_GetStmNodeEdgeParams(R3D_AnimationTreeNode* node, R3D_AnimationStmIndex edgeIndex) +{ + return node->stm->edgeList[edgeIndex].params; +} + +R3D_AnimationStmIndex R3D_GetStmStateActiveIndex(R3D_AnimationTreeNode* node) +{ + return node->stm->activeIdx; +} + +void R3D_TravelToStmState(R3D_AnimationTreeNode* node, R3D_AnimationStmIndex targetStateIndex) +{ + atree_travel(node->stm, targetStateIndex); +} + +R3D_BoneMask R3D_ComputeBoneMask(const R3D_Skeleton* skeleton, const char* boneNames[], int boneNameCount) +{ + const R3D_BoneInfo* bones = skeleton->bones; + const int bCount = skeleton->boneCount; + R3D_BoneMask bMask = {.boneCount = bCount}; + + if ((size_t)bCount > sizeof(bMask.mask) * 8) { + R3D_TRACELOG(LOG_WARNING, "Failed to compute bone mask: max bone count exceeded (%d)", sizeof(bMask.mask) * 8); + return (R3D_BoneMask){0}; + } + + int maskBits = sizeof(bMask.mask[0]) * 8; + for (int i = 0; i < boneNameCount; i++) { + const char* name = boneNames[i]; + bool found = false; + for (int bIdx = 0; bIdx < bCount; bIdx++) + if (!strncmp(name, bones[bIdx].name, sizeof(bones[bIdx].name))) { + int part = bIdx / maskBits; + int bit = bIdx % maskBits; + bMask.mask[part] |= 1 << bit; + found = true; + break; + } + if (!found) { + R3D_TRACELOG(LOG_WARNING, "Failed to compute bone mask: bone \"%s\" not found", name); + return (R3D_BoneMask){0}; + } + } + + return bMask; +} + +``` + +`src/r3d_camera.c`: + +```c +/* r3d_camera.c -- R3D Camera Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided "as-is", without any express or implied warranty. + * For conditions of distribution and use, see the accompanying LICENSE file. + */ + +#include +#include +#include + +#include "./common/r3d_math.h" + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Camera R3D_CameraFromRL(Camera3D camera) +{ + R3D_Camera result = R3D_CAMERA_BASE; + + result.position = camera.position; + result.fovy = camera.fovy; + result.nearPlane = rlGetCullDistanceNear(); + result.farPlane = rlGetCullDistanceFar(); + + result.projection = + (camera.projection == CAMERA_ORTHOGRAPHIC) + ? R3D_PROJECTION_ORTHOGRAPHIC + : R3D_PROJECTION_PERSPECTIVE; + + R3D_CameraLookAt(&result, camera.target, camera.up); + + return result; +} + +Camera3D R3D_CameraToRL(R3D_Camera camera) +{ + Camera3D result = {0}; + + result.position = camera.position; + result.target = Vector3Add(camera.position, R3D_GetCameraForward(camera)); + result.up = R3D_GetCameraUp(camera); + result.fovy = (float)camera.fovy; + + result.projection = + (camera.projection == R3D_PROJECTION_ORTHOGRAPHIC) + ? CAMERA_ORTHOGRAPHIC + : CAMERA_PERSPECTIVE; + + return result; +} + +void R3D_CameraLookAt(R3D_Camera* camera, Vector3 target, Vector3 up) +{ + if (camera == NULL) return; + + Vector3 forward = Vector3Subtract(target, camera->position); + + if (r3d_vector3_len_sq(forward) <= 1e-12f) { + forward = (Vector3){0.0f, 0.0f, -1.0f}; + target = Vector3Add(camera->position, forward); + } + + if (r3d_vector3_len_sq(up) <= 1e-12f) { + up = (Vector3){0.0f, 1.0f, 0.0f}; + } + + Matrix view = MatrixLookAt(camera->position, target, up); + Matrix world = MatrixInvert(view); + + camera->rotation = QuaternionFromMatrix(world); + camera->rotation = r3d_quaternion_normalize_or_id(camera->rotation); +} + +Vector3 R3D_GetCameraForward(R3D_Camera camera) +{ + return Vector3RotateByQuaternion((Vector3){0, 0, -1}, camera.rotation); +} + +Vector3 R3D_GetCameraRight(R3D_Camera camera) +{ + return Vector3RotateByQuaternion((Vector3){1, 0, 0}, camera.rotation); +} + +Vector3 R3D_GetCameraUp(R3D_Camera camera) +{ + return Vector3RotateByQuaternion((Vector3){0, 1, 0}, camera.rotation); +} + +Matrix R3D_GetCameraView(R3D_Camera camera) +{ + Vector3 forward = R3D_GetCameraForward(camera); + Vector3 up = R3D_GetCameraUp(camera); + + return MatrixLookAt( + camera.position, + Vector3Add(camera.position, forward), + up + ); +} + +Matrix R3D_GetCameraProj(R3D_Camera camera, double aspect) +{ + if (aspect <= 0.0) aspect = 1.0; + + if (camera.projection == R3D_PROJECTION_ORTHOGRAPHIC) { + double top = camera.fovy * 0.5; + double right = top * aspect; + + return MatrixOrtho( + -right, + right, + -top, + top, + camera.nearPlane, + camera.farPlane + ); + } + + return MatrixPerspective( + camera.fovy * DEG2RAD, + aspect, + camera.nearPlane, + camera.farPlane + ); +} + +Matrix R3D_GetCameraViewProj(R3D_Camera camera, double aspect) +{ + Matrix view = R3D_GetCameraView(camera); + Matrix proj = R3D_GetCameraProj(camera, aspect); + + return MatrixMultiply(view, proj); +} + +void R3D_MoveCamera(R3D_Camera* camera, Vector3 delta) +{ + if (camera == NULL) return; + + camera->position = Vector3Add(camera->position, delta); +} + +void R3D_MoveCameraLocal(R3D_Camera* camera, Vector3 delta) +{ + if (camera == NULL) return; + + // Local convention: +X right, +Y up, -Z forward. + Vector3 world_delta = Vector3RotateByQuaternion(delta, camera->rotation); + camera->position = Vector3Add(camera->position, world_delta); +} + +void R3D_CameraRotate(R3D_Camera* camera, Quaternion rotation) +{ + if (camera == NULL) return; + + rotation = r3d_quaternion_normalize_or_id(rotation); + + // Local-space rotation composition. + camera->rotation = QuaternionMultiply(camera->rotation, rotation); + camera->rotation = r3d_quaternion_normalize_or_id(camera->rotation); +} + +void R3D_CameraPitch(R3D_Camera* camera, float angle) +{ + if (camera == NULL) return; + + Quaternion q = QuaternionFromAxisAngle((Vector3){1, 0, 0}, angle); + R3D_CameraRotate(camera, q); +} + +void R3D_CameraYaw(R3D_Camera* camera, float angle) +{ + if (camera == NULL) return; + + Quaternion q = QuaternionFromAxisAngle((Vector3){0, 1, 0}, angle); + R3D_CameraRotate(camera, q); +} + +void R3D_CameraRoll(R3D_Camera* camera, float angle) +{ + if (camera == NULL) return; + + // Roll around local forward. Since forward is -Z, the local axis is {0, 0, -1}. + Quaternion q = QuaternionFromAxisAngle((Vector3){0, 0, -1}, angle); + R3D_CameraRotate(camera, q); +} + +void R3D_SetCameraCullMask(R3D_Camera* camera, R3D_Layer cullMask) +{ + if (camera == NULL) return; + camera->cullMask = cullMask; +} + +void R3D_EnableCameraCullLayers(R3D_Camera* camera, R3D_Layer layerMask) +{ + if (camera == NULL) return; + camera->cullMask |= layerMask; +} + +void R3D_DisableCameraCullLayers(R3D_Camera* camera, R3D_Layer layerMask) +{ + if (camera == NULL) return; + camera->cullMask &= ~layerMask; +} + +void R3D_ToggleCameraCullLayers(R3D_Camera* camera, R3D_Layer layerMask) +{ + if (camera == NULL) return; + camera->cullMask ^= layerMask; +} + +bool R3D_IsCameraLayerVisible(R3D_Camera camera, R3D_Layer layerMask) +{ + return (camera.cullMask & layerMask) != 0; +} + +``` + +`src/r3d_color.c`: + +```c +/* r3d_color.c -- R3D Color Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include + +#include "./common/r3d_math.h" +#include "./r3d_core_state.h" + +// ======================================== +// PUBLIC API +// ======================================== + +Vector4 R3D_ColorSrgbToLinear(Color color) +{ + return r3d_color_srgb_to_linear_vec4(color); +} + +Vector3 R3D_ColorSrgbToLinearVector3(Color color) +{ + return r3d_color_srgb_to_linear_vec3(color); +} + +Color R3D_ColorLinearToSrgb(Vector4 color) +{ + return r3d_color_linear_to_srgb_vec4(color); +} + +Vector4 R3D_ColorFromCurrentSpace(Color color) +{ + return r3d_color_to_linear_vec4(color, R3D.colorSpace); +} + +Vector3 R3D_ColorFromCurrentSpaceVector3(Color color) +{ + return r3d_color_to_linear_vec3(color, R3D.colorSpace); +} + +``` + +`src/r3d_config.h.in`: + +```in +/* r3d_config.h.in -- R3D compile-time options + * + * This file is automatically generated by CMake, do not modify it directly. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_CONFIG_H +#define R3D_CONFIG_H + +// ======================================== +// CMake Options +// ======================================== + +#cmakedefine R3D_SUPPORT_ASSIMP + +// ======================================== +// Forward rendering limits +// ======================================== + +/** + * Maximum number of lights affecting a single mesh in the forward rendering path. + * This applies to transparent objects and/or non-mixed blending modes, + * and any rendering performed during probe capture. + */ +#define R3D_MAX_LIGHT_FORWARD_PER_MESH @R3D_MAX_LIGHT_FORWARD_PER_MESH@ + +// ======================================== +// Probe system +// ======================================== + +/** + * Maximum number of probes whose maps can be rendered simultaneously on screen. + */ +#define R3D_MAX_PROBE_ON_SCREEN @R3D_MAX_PROBE_ON_SCREEN@ + +/** + * Resolution of each face of the probe capture cubemap. + */ +#define R3D_PROBE_CAPTURE_SIZE @R3D_PROBE_CAPTURE_SIZE@ + +// ======================================== +// Shadow mapping +// ======================================== + +/** + * Resolution of directional light shadow maps. + */ +#define R3D_SHADOW_MAP_DIRECTIONAL_SIZE @R3D_SHADOW_MAP_DIRECTIONAL_SIZE@ + +/** + * Resolution of spot light shadow maps. + */ +#define R3D_SHADOW_MAP_SPOT_SIZE @R3D_SHADOW_MAP_SPOT_SIZE@ + +/** + * Resolution of omni (point) light shadow maps. + */ +#define R3D_SHADOW_MAP_OMNI_SIZE @R3D_SHADOW_MAP_OMNI_SIZE@ + +// ======================================== +// IBL / Probes +// ======================================== + +/** + * Resolution of each face of irradiance cubemaps + * used for ambient lighting and probes. + */ +#define R3D_CUBEMAP_IRRADIANCE_SIZE @R3D_CUBEMAP_IRRADIANCE_SIZE@ + +/** + * Resolution of each face of prefiltered cubemaps + * used for ambient lighting and probes. + */ +#define R3D_CUBEMAP_PREFILTER_SIZE @R3D_CUBEMAP_PREFILTER_SIZE@ + +// ======================================== +// Material Shaders +// ======================================== + +/* + * Maximum size of user-provided code for a custom shader. + */ +#define R3D_MAX_SHADER_CODE_LENGTH @R3D_MAX_SHADER_CODE_LENGTH@ + +/* + * Maximum number of samplers for custom shaders. + * Using up to 4 samplers is recommended for maximum compatibility. + * Going beyond 8 samplers is strongly discouraged, as it can cause + * compatibility issues on many devices. + */ +#define R3D_MAX_SHADER_SAMPLERS @R3D_MAX_SHADER_SAMPLERS@ + +/* + * Maximum number of uniforms for custom shaders. + * As long as you stay within the maximum UBO size supported by the target hardware, you are fine. + * To be safe, assume each uniform has the size of a mat4 (64b) and ensure the total does not exceed 16 KB; + * this should guarantee compatibility across all platforms. + */ +#define R3D_MAX_SHADER_UNIFORMS @R3D_MAX_SHADER_UNIFORMS@ + +/* + * Defines the maximum possible number in the screen shader chain. + */ +#define R3D_MAX_SCREEN_SHADERS @R3D_MAX_SCREEN_SHADERS@ + +// ======================================== +// Render Module +// ======================================== + +/* + * Initial number of vertex slots pre-allocated in the global VBO at startup. + */ +#define R3D_RENDER_INITIAL_VERTICES_RESERVE (1 << 16) // 65 536 vertices (~2.3 MB) + +/* + * Initial number of index slots pre-allocated in the global EBO at startup. + */ +#define R3D_RENDER_INITIAL_ELEMENTS_RESERVE (1 << 17) // 131 072 indices (~0.5 MB) + +/* + * Initial capacity of the CPU-side draw call, group, and cluster arrays. + */ +#define R3D_RENDER_INITIAL_DRAW_CALL_RESERVE 1024 + +/* + * Initial capacity of the vertex and element free list arrays. + */ +#define R3D_RENDER_INITIAL_FREE_LIST_RESERVE 128 + +// ======================================== +// Debug +// ======================================== + +#if @R3D_ENABLE_TRACELOG@ +# define R3D_TRACELOG(level, msg, ...) TraceLog(level, "R3D: " msg, ##__VA_ARGS__) +#else +# define R3D_TRACELOG(level, msg, ...) (void)0 +#endif + +#endif // R3D_CONFIG_H + +``` + +`src/r3d_core.c`: + +```c +/* r3d_core.h -- R3D Core Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include + +#include "./modules/r3d_texture.h" +#include "./modules/r3d_target.h" +#include "./modules/r3d_shader.h" +#include "./modules/r3d_driver.h" +#include "./modules/r3d_render.h" +#include "./modules/r3d_light.h" +#include "./modules/r3d_env.h" +#include "./r3d_core_state.h" + +// ======================================== +// SHARED CORE STATE +// ======================================== + +struct r3d_core_state R3D; + +// ======================================== +// PUBLIC API +// ======================================== + +bool R3D_Init(int resWidth, int resHeight) +{ + memset(&R3D, 0, sizeof(R3D)); + + R3D.matCubeViews[0] = MatrixLookAt((Vector3) {0}, (Vector3) { 1.0f, 0.0f, 0.0f}, (Vector3) {0.0f, -1.0f, 0.0f}); + R3D.matCubeViews[1] = MatrixLookAt((Vector3) {0}, (Vector3) {-1.0f, 0.0f, 0.0f}, (Vector3) {0.0f, -1.0f, 0.0f}); + R3D.matCubeViews[2] = MatrixLookAt((Vector3) {0}, (Vector3) { 0.0f, 1.0f, 0.0f}, (Vector3) {0.0f, 0.0f, 1.0f}); + R3D.matCubeViews[3] = MatrixLookAt((Vector3) {0}, (Vector3) { 0.0f, -1.0f, 0.0f}, (Vector3) {0.0f, 0.0f, -1.0f}); + R3D.matCubeViews[4] = MatrixLookAt((Vector3) {0}, (Vector3) { 0.0f, 0.0f, 1.0f}, (Vector3) {0.0f, -1.0f, 0.0f}); + R3D.matCubeViews[5] = MatrixLookAt((Vector3) {0}, (Vector3) { 0.0f, 0.0f, -1.0f}, (Vector3) {0.0f, -1.0f, 0.0f}); + + R3D.environment = R3D_ENVIRONMENT_BASE; + R3D.material = R3D_MATERIAL_BASE; + + R3D.viewState.camera = R3D_CAMERA_BASE; + R3D.viewState.view = R3D_MATRIX_IDENTITY; + R3D.viewState.invView = R3D_MATRIX_IDENTITY; + R3D.viewState.proj = R3D_MATRIX_IDENTITY; + R3D.viewState.invProj = R3D_MATRIX_IDENTITY; + R3D.viewState.viewProj = R3D_MATRIX_IDENTITY; + R3D.viewState.aspect = 1.0; + + R3D.aaMode = R3D_ANTI_ALIASING_MODE_NONE; + R3D.aaPreset = R3D_ANTI_ALIASING_PRESET_MEDIUM; + R3D.aspectMode = R3D_ASPECT_EXPAND; + R3D.upscaleMode = R3D_UPSCALE_NEAREST; + R3D.downscaleMode = R3D_DOWNSCALE_NEAREST; + R3D.outputMode = R3D_OUTPUT_SCENE; + + R3D.textureFilter = TEXTURE_FILTER_TRILINEAR; + R3D.textureWrap = TEXTURE_WRAP_CLAMP; + R3D.colorSpace = R3D_COLORSPACE_SRGB; + + if (!r3d_texture_init()) { R3D_TRACELOG(LOG_ERROR, "Failed to init texture module"); return false; } + if (!r3d_target_init(resWidth, resHeight)) { R3D_TRACELOG(LOG_ERROR, "Failed to init target module"); return false; } + if (!r3d_shader_init()) { R3D_TRACELOG(LOG_ERROR, "Failed to init shader module"); return false; } + if (!r3d_driver_init()) { R3D_TRACELOG(LOG_ERROR, "Failed to init driver module"); return false; } + if (!r3d_render_init()) { R3D_TRACELOG(LOG_ERROR, "Failed to init render module"); return false; } + if (!r3d_light_init()) { R3D_TRACELOG(LOG_ERROR, "Failed to init light module"); return false; } + if (!r3d_env_init()) { R3D_TRACELOG(LOG_ERROR, "Failed to init env module"); return false; } + + R3D_TRACELOG(LOG_INFO, "Initialized successfully (%dx%d)", resWidth, resHeight); + + return true; +} + +void R3D_Close(void) +{ + r3d_texture_quit(); + r3d_target_quit(); + r3d_shader_quit(); + r3d_driver_quit(); + r3d_render_quit(); + r3d_light_quit(); + r3d_env_quit(); +} + +void R3D_GetResolution(int* width, int* height) +{ + if (width) *width = R3D_TARGET_SIZE_W; + if (height) *height = R3D_TARGET_SIZE_H; +} + +void R3D_SetResolution(int width, int height) +{ + if (width <= 0 || height <= 0) { + R3D_TRACELOG(LOG_ERROR, "Invalid resolution given to 'R3D_SetResolution'"); + return; + } + + r3d_target_resize(width, height); +} + +R3D_AntiAliasingMode R3D_GetAntiAliasingMode(void) +{ + return R3D.aaMode; +} + +void R3D_SetAntiAliasingMode(R3D_AntiAliasingMode mode) +{ + R3D.aaMode = mode; +} + +R3D_AntiAliasingPreset R3D_GetAntiAliasingPreset(void) +{ + return R3D.aaPreset; +} + +void R3D_SetAntiAliasingPreset(R3D_AntiAliasingPreset preset) +{ + R3D.aaPreset = CLAMP(preset, 0, R3D_ANTI_ALIASING_PRESET_COUNT - 1); +} + +R3D_AspectMode R3D_GetAspectMode(void) +{ + return R3D.aspectMode; +} + +void R3D_SetAspectMode(R3D_AspectMode mode) +{ + R3D.aspectMode = mode; +} + +R3D_UpscaleMode R3D_GetUpscaleMode(void) +{ + return R3D.upscaleMode; +} + +void R3D_SetUpscaleMode(R3D_UpscaleMode mode) +{ + R3D.upscaleMode = mode; +} + +R3D_DownscaleMode R3D_GetDownscaleMode(void) +{ + return R3D.downscaleMode; +} + +void R3D_SetDownscaleMode(R3D_DownscaleMode mode) +{ + R3D.downscaleMode = mode; +} + +R3D_OutputMode R3D_GetOutputMode(void) +{ + return R3D.outputMode; +} + +void R3D_SetOutputMode(R3D_OutputMode mode) +{ + R3D.outputMode = mode; +} + +void R3D_SetTextureFilter(TextureFilter filter) +{ + R3D.textureFilter = filter; +} + +void R3D_SetTextureWrap(TextureWrap wrap) +{ + R3D.textureWrap = wrap; +} + +void R3D_SetColorSpace(R3D_ColorSpace space) +{ + R3D.colorSpace = space; +} + +``` + +`src/r3d_core_state.h`: + +```h +/* r3d_core_state.h -- Internal R3D Core State + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#ifndef R3D_CORE_STATE_H +#define R3D_CORE_STATE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +// ======================================== +// CORE STATE +// ======================================== + +/* + * Current view state including view frustum and transforms. + */ +typedef struct { + R3D_Camera camera; //< Complete camera data + Rectangle viewport; //< Viewport of the pass + R3D_Frustum frustum; //< View frustum for culling + Matrix view, invView; //< View matrix and its inverse + Matrix proj, invProj; //< Projection matrix and its inverse + Matrix viewProj; //< Combined view-projection matrix + double aspect; //< Camera aspect (based on target) +} r3d_core_view_t; + +/* + * Core state shared between all public modules. + */ +extern struct r3d_core_state { + RenderTexture screen; //< Texture target (screen if null id) + R3D_ScreenShader* screenShaders //< Chain of screen shaders + [R3D_SCREEN_SHADER_STAGE_COUNT] + [R3D_MAX_SCREEN_SHADERS]; + R3D_Environment environment; //< Current environment settings + R3D_Material material; //< Default material to use + r3d_core_view_t viewState; //< Current view state + R3D_AntiAliasingMode aaMode; //< Defines the anti aliasing mode + R3D_AntiAliasingPreset aaPreset; //< Defines the anti aliasing quality preset + R3D_AspectMode aspectMode; //< Defines how the aspect ratio is calculated + R3D_UpscaleMode upscaleMode; //< Upscaling mode used during the final blit + R3D_DownscaleMode downscaleMode; //< Downscaling mode used during the final blit + R3D_OutputMode outputMode; //< Defines which buffer we should output in R3D_End() + TextureFilter textureFilter; //< Default texture filter for model loading + TextureWrap textureWrap; //< Default texture wrap for material map loading + R3D_ColorSpace colorSpace; //< Color space that must be considered for supplied colors or surface colors + Matrix matCubeViews[6]; //< Pre-computed view matrices for cubemap faces +} R3D; + +#endif // R3D_CORE_STATE_H + +``` + +`src/r3d_cubemap.c`: + +```c +/* r3d_cubemap.c -- R3D Cubemap Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include + +#include "./modules/r3d_driver.h" +#include "./modules/r3d_shader.h" +#include "./modules/r3d_render.h" +#include "./r3d_core_state.h" + +// ======================================== +// INTERNAL SHARED FUNCTIONS +// ======================================== + +R3D_Cubemap r3d_cubemap_allocate(int size); +void r3d_cubemap_gen_mipmap(const R3D_Cubemap* cubemap); + +// ======================================== +// INTERNAL LOCAL FUNCTIONS +// ======================================== + +static const char* get_layout_name(R3D_CubemapLayout layout); +static R3D_CubemapLayout detect_cubemap_layout(Image image); +static int get_cubemap_size_from_layout(Image image, R3D_CubemapLayout layout); +static R3D_Cubemap load_cubemap_from_panorama(Image image, int size); +static R3D_Cubemap load_cubemap_from_line_vertical(Image image, int size); +static Image alloc_work_faces_image(Image source, int size); +static R3D_Cubemap load_cubemap_from_line_horizontal(Image image, int size); +static R3D_Cubemap load_cubemap_from_cross_three_by_four(Image image, int size); +static R3D_Cubemap load_cubemap_from_cross_four_by_three(Image image, int size); + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Cubemap R3D_LoadCubemap(const char* fileName, R3D_CubemapLayout layout) +{ + Image image = LoadImage(fileName); + R3D_Cubemap cubemap = R3D_LoadCubemapFromImage(image, layout); + UnloadImage(image); + return cubemap; +} + +R3D_Cubemap R3D_LoadCubemapFromImage(Image image, R3D_CubemapLayout layout) +{ + R3D_Cubemap cubemap = {0}; + + if (layout == R3D_CUBEMAP_LAYOUT_AUTO_DETECT) { + layout = detect_cubemap_layout(image); + if (layout == R3D_CUBEMAP_LAYOUT_AUTO_DETECT) { + R3D_TRACELOG(LOG_WARNING, "Failed to detect cubemap image layout"); + return cubemap; + } + } + + int size = get_cubemap_size_from_layout(image, layout); + if (size == 0) { + R3D_TRACELOG(LOG_WARNING, "Cubemap layout not recognized (layout: %i)", layout); + return cubemap; + } + + switch (layout) { + case R3D_CUBEMAP_LAYOUT_LINE_VERTICAL: + cubemap = load_cubemap_from_line_vertical(image, size); + break; + case R3D_CUBEMAP_LAYOUT_LINE_HORIZONTAL: + cubemap = load_cubemap_from_line_horizontal(image, size); + break; + case R3D_CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR: + cubemap = load_cubemap_from_cross_three_by_four(image, size); + break; + case R3D_CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE: + cubemap = load_cubemap_from_cross_four_by_three(image, size); + break; + case R3D_CUBEMAP_LAYOUT_PANORAMA: + cubemap = load_cubemap_from_panorama(image, size); + break; + case R3D_CUBEMAP_LAYOUT_AUTO_DETECT: + break; + } + + r3d_cubemap_gen_mipmap(&cubemap); + + R3D_TRACELOG(LOG_INFO, "Cubemap loaded from '%s' successfully (%dx%d)", get_layout_name(layout), size, size); + + return cubemap; +} + +void R3D_UnloadCubemap(R3D_Cubemap cubemap) +{ + glDeleteFramebuffers(1, &cubemap.fbo); + glDeleteTextures(1, &cubemap.texture); +} + +// ======================================== +// INTERNAL SHARED FUNCTIONS +// ======================================== + +R3D_Cubemap r3d_cubemap_allocate(int size) +{ + R3D_Cubemap cubemap = {0}; + cubemap.size = size; + + glGenFramebuffers(1, &cubemap.fbo); + + glGenTextures(1, &cubemap.texture); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.texture); + + int mipCount = r3d_get_mip_levels_1d(size); + for (int level = 0; level < mipCount; level++) { + int mipSize = size >> level; + for (int i = 0; i < 6; i++) { + glTexImage2D( + GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, level, GL_RGB16F, + mipSize, mipSize, 0, GL_RGB, GL_HALF_FLOAT, NULL + ); + } + } + + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, mipCount - 1); + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + + return cubemap; +} + +void r3d_cubemap_gen_mipmap(const R3D_Cubemap* cubemap) +{ + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap->texture); + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +} + +// ======================================== +// INTERNAL LOCAL FUNCTIONS +// ======================================== + +const char* get_layout_name(R3D_CubemapLayout layout) +{ + switch (layout) { + case R3D_CUBEMAP_LAYOUT_AUTO_DETECT: return "Auto"; + case R3D_CUBEMAP_LAYOUT_LINE_VERTICAL: return "Line Vertical"; + case R3D_CUBEMAP_LAYOUT_LINE_HORIZONTAL: return "Line Horizontal"; + case R3D_CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR: return "Cross 3/4"; + case R3D_CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE: return "Cross 4/3"; + case R3D_CUBEMAP_LAYOUT_PANORAMA: return "Panorama"; + default: break; + } + return "Unknown"; +} + +R3D_CubemapLayout detect_cubemap_layout(Image image) +{ + R3D_CubemapLayout layout = R3D_CUBEMAP_LAYOUT_AUTO_DETECT; + + if (image.width > image.height) { + if (image.width / 6 == image.height) { + layout = R3D_CUBEMAP_LAYOUT_LINE_HORIZONTAL; + } + else if (image.width / 4 == image.height / 3) { + layout = R3D_CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE; + } + else if (image.width / 2 == image.height) { + layout = R3D_CUBEMAP_LAYOUT_PANORAMA; + } + } + else if (image.height > image.width) { + if (image.height / 6 == image.width) { + layout = R3D_CUBEMAP_LAYOUT_LINE_VERTICAL; + } + else if (image.width / 3 == image.height/4) { + layout = R3D_CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR; + } + } + // Checking for cases where the ratio is not exactly 2:1 but close + else if (abs(image.width - 2 * image.height) < image.height / 10) { + layout = R3D_CUBEMAP_LAYOUT_PANORAMA; + } + + return layout; +} + +int get_cubemap_size_from_layout(Image image, R3D_CubemapLayout layout) +{ + int size = 0; + + switch (layout) { + case R3D_CUBEMAP_LAYOUT_LINE_VERTICAL: + size = image.height / 6; + break; + case R3D_CUBEMAP_LAYOUT_LINE_HORIZONTAL: + size = image.width / 6; + break; + case R3D_CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR: + size = image.width / 3; + break; + case R3D_CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE: + size = image.width / 4; + break; + case R3D_CUBEMAP_LAYOUT_PANORAMA: + size = image.height; + break; + default: + break; + } + + return size; +} + +R3D_Cubemap load_cubemap_from_panorama(Image image, int size) +{ + r3d_driver_invalidate_cache(); + r3d_driver_store_viewport(); + + R3D_Cubemap cubemap = r3d_cubemap_allocate(size); + Texture2D panorama = LoadTextureFromImage(image); + SetTextureFilter(panorama, TEXTURE_FILTER_BILINEAR); + Matrix matProj = MatrixPerspective(90.0 * DEG2RAD, 1.0, 0.1, 10.0); + + R3D_SHADER_USE(prepare.cubemapFromEquirectangular); + R3D_SHADER_SET_MAT4(prepare.cubemapFromEquirectangular, uMatProj, matProj); + R3D_SHADER_BIND_SAMPLER(prepare.cubemapFromEquirectangular, uPanoramaTex, panorama.id); + + glBindFramebuffer(GL_FRAMEBUFFER, cubemap.fbo); + glViewport(0, 0, size, size); + r3d_driver_disable(GL_CULL_FACE); + r3d_render_prepare_drawing(); + + for (int i = 0; i < 6; i++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap.texture, 0); + R3D_SHADER_SET_MAT4(prepare.cubemapFromEquirectangular, uMatView, R3D.matCubeViews[i]); + R3D_RENDER_CUBE(); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindVertexArray(0); + + r3d_driver_enable(GL_CULL_FACE); + r3d_driver_restore_viewport(); + + UnloadTexture(panorama); + + return cubemap; +} + +R3D_Cubemap load_cubemap_from_line_vertical(Image image, int size) +{ + Image workImage = image; + + if (image.format != PIXELFORMAT_UNCOMPRESSED_R16G16B16) { + workImage = ImageCopy(image); + ImageFormat(&workImage, PIXELFORMAT_UNCOMPRESSED_R16G16B16); + } + + int faceSize = size * size * 3 * sizeof(uint16_t); + R3D_Cubemap cubemap = r3d_cubemap_allocate(size); + + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.texture); + for (int i = 0; i < 6; i++) { + glTexSubImage2D( + GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, + 0, 0, size, size, GL_RGB, GL_HALF_FLOAT, + (uint8_t*)workImage.data + i * faceSize + ); + } + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + + if (workImage.data != image.data) { + UnloadImage(workImage); + } + + return cubemap; +} + +Image alloc_work_faces_image(Image source, int size) +{ + Image image = {0}; + + int faceSize = GetPixelDataSize(size, size, source.format); + + image.data = RL_MALLOC(6 * faceSize); + image.width = size; + image.height = 6 * size; + image.format = source.format; + image.mipmaps = 1; + + return image; +} + +R3D_Cubemap load_cubemap_from_line_horizontal(Image image, int size) +{ + Image faces = alloc_work_faces_image(image, size); + + for (int i = 0; i < 6; i++) { + Rectangle srcRect = {(float)i * size, 0, (float)size, (float)size}; + Rectangle dstRect = {0, (float)i * size, (float)size, (float)size}; + ImageDraw(&faces, image, srcRect, dstRect, WHITE); + } + + R3D_Cubemap cubemap = load_cubemap_from_line_vertical(faces, size); + UnloadImage(faces); + + return cubemap; +} + +R3D_Cubemap load_cubemap_from_cross_three_by_four(Image image, int size) +{ + Rectangle srcRecs[6] = {0}; + + for (int i = 0; i < 6; i++) { + srcRecs[i] = (Rectangle) {0, 0, (float)size, (float)size}; + } + + srcRecs[0].x = (float)size; srcRecs[0].y = (float)size; + srcRecs[1].x = (float)size; srcRecs[1].y = 3.0f * size; + srcRecs[2].x = (float)size; srcRecs[2].y = 0; + srcRecs[3].x = (float)size; srcRecs[3].y = 2.0f * size; + srcRecs[4].x = 0; srcRecs[4].y = (float)size; + srcRecs[5].x = 2.0f * size; srcRecs[5].y = (float)size; + + Image faces = alloc_work_faces_image(image, size); + + for (int i = 0; i < 6; i++) { + Rectangle dstRec = {0, (float)i * size, (float)size, (float)size}; + ImageDraw(&faces, image, srcRecs[i], dstRec, WHITE); + } + + R3D_Cubemap cubemap = load_cubemap_from_line_vertical(faces, size); + UnloadImage(faces); + + return cubemap; +} + +R3D_Cubemap load_cubemap_from_cross_four_by_three(Image image, int size) +{ + Rectangle srcRecs[6] = {0}; + + for (int i = 0; i < 6; i++) { + srcRecs[i] = (Rectangle) {0, 0, (float)size, (float)size}; + } + + srcRecs[0].x = 2.0f * size; srcRecs[0].y = (float)size; + srcRecs[1].x = 0; srcRecs[1].y = (float)size; + srcRecs[2].x = (float)size; srcRecs[2].y = 0; + srcRecs[3].x = (float)size; srcRecs[3].y = 2.0f * size; + srcRecs[4].x = (float)size; srcRecs[4].y = (float)size; + srcRecs[5].x = 3.0f * size; srcRecs[5].y = (float)size; + + Image faces = alloc_work_faces_image(image, size); + + for (int i = 0; i < 6; i++) { + Rectangle dstRec = {0, (float)i * size, (float)size, (float)size}; + ImageDraw(&faces, image, srcRecs[i], dstRec, WHITE); + } + + R3D_Cubemap cubemap = load_cubemap_from_line_vertical(faces, size); + UnloadImage(faces); + + return cubemap; +} + +``` + +`src/r3d_decal.c`: + +```c +/* r3d_decal.c -- R3D Decal Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include + + // ======================================== + // PUBLIC API + // ======================================== + +void R3D_UnloadDecalMaps(R3D_Decal decal) +{ + R3D_UnloadAlbedoMap(decal.albedo); + R3D_UnloadEmissionMap(decal.emission); + R3D_UnloadOrmMap(decal.orm); + R3D_UnloadNormalMap(decal.normal); +} +``` + +`src/r3d_draw.c`: + +```c +/* r3d_draw.h -- R3D Draw Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "./r3d_core_state.h" + +#include "./common/r3d_helper.h" +#include "./common/r3d_pass.h" +#include "./common/r3d_math.h" + +#include "./modules/r3d_texture.h" +#include "./modules/r3d_driver.h" +#include "./modules/r3d_target.h" +#include "./modules/r3d_shader.h" +#include "./modules/r3d_render.h" +#include "./modules/r3d_light.h" +#include "./modules/r3d_env.h" + +// ======================================== +// HELPER MACROS +// ======================================== + +#define IS_MESH_VALID(mesh) \ + (((mesh).vertexCount > 0) && ((mesh).layerMask != 0)) + +#define IS_MESH_VISIBLE(mesh, cullMask) \ + (BIT_TEST_ANY((cullMask), (mesh).layerMask)) + +#define IS_MESH_VISIBLE_CAMERA(mesh) \ + (IS_MESH_VISIBLE((mesh), R3D.viewState.camera.cullMask)) + +#define SHADOW_CAST_ONLY_MASK ( \ + (1 << R3D_SHADOW_CAST_ONLY_AUTO) | \ + (1 << R3D_SHADOW_CAST_ONLY_DOUBLE_SIDED) | \ + (1 << R3D_SHADOW_CAST_ONLY_FRONT_SIDE) | \ + (1 << R3D_SHADOW_CAST_ONLY_BACK_SIDE) \ +) + +#define IS_SHADOW_CAST_ONLY(mode) \ + ((R3D_SHADOW_CAST_ONLY_MASK & (1 << (mode))) != 0) + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +static void update_view_state(R3D_View view); +static void upload_light_array_block_for_mesh(const r3d_render_call_t* call, bool shadow); +static void upload_frame_block(void); +static void upload_view_block(void); +static void upload_env_block(void); +static void upload_fog_block(void); + +static void raster_depth(const r3d_render_call_t* call, const Matrix* viewProj, r3d_light_t* light); +static void raster_depth_cube(const r3d_render_call_t* call, const Matrix* viewProj, r3d_light_t* light); +static void raster_probe_forward(const r3d_render_call_t* call, const r3d_env_probe_t* probe, int face); +static void raster_probe_unlit(const r3d_render_call_t* call, const r3d_env_probe_t* probe, int face); +static void raster_geometry(const r3d_render_call_t* call, bool matchPrepass); +static void raster_decal(const r3d_render_call_t* call); +static void raster_forward(const r3d_render_call_t* call); +static void raster_unlit(const r3d_render_call_t* call); + +static void pass_scene_shadow(void); +static void pass_scene_probes(void); +static void pass_scene_geometry(void); +static void pass_scene_prepass(void); +static void pass_scene_decals(void); + +static void pass_prepare_depth_pyramid(void); +static r3d_target_t pass_prepare_ssao(void); +static r3d_target_t pass_prepare_ssil(void); +static r3d_target_t pass_prepare_ssgi(void); +static r3d_target_t pass_prepare_ssr(void); + +static void pass_deferred_lights(void); +static void pass_deferred_ambient(r3d_target_t ssaoSource, r3d_target_t ssilSource, r3d_target_t ssgiSource); +static void pass_deferred_compose(r3d_target_t sceneTarget, r3d_target_t ssrSource); +static void pass_deferred_fog(r3d_target_t sceneTarget); + +static void pass_scene_forward(r3d_target_t sceneTarget); +static void pass_scene_background(r3d_target_t sceneTarget); + +static r3d_target_t pass_post_setup(r3d_target_t sceneTarget); +static r3d_target_t pass_post_dof(r3d_target_t sceneTarget); +static r3d_target_t pass_post_bloom(r3d_target_t sceneTarget); +static r3d_target_t pass_post_auto_exposure(r3d_target_t sceneTarget); +static r3d_target_t pass_post_screen(R3D_ScreenShaderStage stage, r3d_target_t sceneTarget); +static r3d_target_t pass_post_output(r3d_target_t sceneTarget); +static r3d_target_t pass_post_fxaa(r3d_target_t sceneTarget); +static r3d_target_t pass_post_smaa(r3d_target_t sceneTarget); + +static void blit_to_screen(r3d_target_t source); +static void visualize_to_screen(r3d_target_t source); + +static void cleanup_after_render(void); + +// ======================================== +// PUBLIC API +// ======================================== + +void R3D_Begin(Camera3D camera) +{ + R3D_BeginEx(R3D_CameraFromRL(camera)); +} + +void R3D_BeginEx(R3D_Camera camera) +{ + R3D_View view = { + .camera = camera, + .viewport = {0}, + .target = {0}, + }; + + R3D_BeginPro(view); +} + +void R3D_BeginPro(R3D_View view) +{ + rlDrawRenderBatchActive(); + update_view_state(view); + R3D.screen = view.target; + r3d_render_clear(); +} + +void R3D_End(void) +{ + r3d_render_prepare_drawing(); // bind global VAO + + /* --- Invalidates OpenGL cache and save some infos --- */ + + r3d_driver_invalidate_cache(); + r3d_driver_store_viewport(); + + /* --- Upload and bind uniform buffers --- */ + + upload_frame_block(); + upload_view_block(); + upload_env_block(); + upload_fog_block(); + + /* --- Update all visible lights and render their shadow maps --- */ + + bool hasVisibleShadows = false; + r3d_light_update_and_cull( + &R3D.viewState.frustum, + R3D.viewState.camera, + R3D.viewState.aspect, + &hasVisibleShadows + ); + + if (hasVisibleShadows) { + pass_scene_shadow(); + r3d_shader_bind_sampler(R3D_SHADER_SAMPLER_SHADOW_DIR, r3d_light_shadow_get(R3D_LIGHT_DIR)); + r3d_shader_bind_sampler(R3D_SHADER_SAMPLER_SHADOW_SPOT, r3d_light_shadow_get(R3D_LIGHT_SPOT)); + r3d_shader_bind_sampler(R3D_SHADER_SAMPLER_SHADOW_OMNI, r3d_light_shadow_get(R3D_LIGHT_OMNI)); + } + + /* --- Update all visible environment probes and render their cubemaps --- */ + + bool hasVisibleProbes = false; + r3d_env_probe_update_and_cull(&R3D.viewState.frustum, &hasVisibleProbes); + + if (hasVisibleProbes || R3D.environment.ambient.map.flags != 0) { + r3d_shader_bind_sampler(R3D_SHADER_SAMPLER_IBL_IRRADIANCE, r3d_env_irradiance_get()); + r3d_shader_bind_sampler(R3D_SHADER_SAMPLER_IBL_PREFILTER, r3d_env_prefilter_get()); + r3d_shader_bind_sampler(R3D_SHADER_SAMPLER_IBL_BRDF_LUT, r3d_texture_get(R3D_TEXTURE_BRDF_LUT)); + if (hasVisibleProbes) pass_scene_probes(); // Must have the IBL bind in case of ambient map + } + + /* --- Cull groups and sort all draw calls before rendering --- */ + + r3d_render_cull_groups(&R3D.viewState.frustum); + + r3d_render_sort_list(R3D_RENDER_LIST_OPAQUE, R3D.viewState.camera.position, R3D_RENDER_SORT_FRONT_TO_BACK); + r3d_render_sort_list(R3D_RENDER_LIST_TRANSPARENT, R3D.viewState.camera.position, R3D_RENDER_SORT_BACK_TO_FRONT); + r3d_render_sort_list(R3D_RENDER_LIST_DECAL, R3D.viewState.camera.position, R3D_RENDER_SORT_MATERIAL_ONLY); + + r3d_render_sort_list(R3D_RENDER_LIST_OPAQUE_INST, R3D.viewState.camera.position, R3D_RENDER_SORT_MATERIAL_ONLY); + r3d_render_sort_list(R3D_RENDER_LIST_TRANSPARENT_INST, R3D.viewState.camera.position, R3D_RENDER_SORT_MATERIAL_ONLY); + r3d_render_sort_list(R3D_RENDER_LIST_DECAL_INST, R3D.viewState.camera.position, R3D_RENDER_SORT_MATERIAL_ONLY); + + /* --- Deferred path for opaques and decals --- */ + + r3d_target_t sceneTarget = R3D_TARGET_SCENE_0; + r3d_target_t ssaoSource = R3D_TARGET_INVALID; + r3d_target_t ssilSource = R3D_TARGET_INVALID; + r3d_target_t ssgiSource = R3D_TARGET_INVALID; + r3d_target_t ssrSource = R3D_TARGET_INVALID; + + r3d_driver_set_depth_mask(GL_TRUE); + r3d_driver_set_stencil_mask(0xFF); + + R3D_TARGET_CLEAR(true, R3D_TARGET_ALL_DEFERRED); + + if (r3d_render_has_deferred() || r3d_render_has_prepass()) { + if (r3d_render_has_deferred()) pass_scene_geometry(); + if (r3d_render_has_prepass()) pass_scene_prepass(); + if (r3d_render_has_decal()) pass_scene_decals(); + if (r3d_light_has_visible()) pass_deferred_lights(); + + bool ssao = R3D.environment.ssao.enabled; + bool ssil = R3D.environment.ssil.enabled; + bool ssgi = R3D.environment.ssgi.enabled; + bool ssr = R3D.environment.ssr.enabled; + bool dof = R3D.environment.dof.mode; + + if (ssao || ssil || ssgi || ssr || dof) { + pass_prepare_depth_pyramid(); + } + + if (ssao) ssaoSource = pass_prepare_ssao(); + if (ssil) ssilSource = pass_prepare_ssil(); + if (ssgi) ssgiSource = pass_prepare_ssgi(); + pass_deferred_ambient(ssaoSource, ssilSource, ssgiSource); + + if (ssr) ssrSource = pass_prepare_ssr(); + pass_deferred_compose(sceneTarget, ssrSource); + + if (R3D.environment.fog.mode != R3D_FOG_DISABLED) { + pass_deferred_fog(sceneTarget); + } + } + else { + int numLevels = r3d_target_get_num_levels(R3D_TARGET_DEPTH); + for (int i = 1; i < numLevels; i++) { + R3D_TARGET_CLEAR_LEVEL(i, R3D_TARGET_DEPTH); + } + } + + /* --- Then background and transparent rendering --- */ + + pass_scene_background(sceneTarget); + + if (r3d_render_has_forward() || r3d_render_has_prepass()) { + pass_scene_forward(sceneTarget); + } + + /* --- Applying effects over the scene and final blit --- */ + + sceneTarget = pass_post_setup(sceneTarget); + sceneTarget = pass_post_screen(R3D_SCREEN_SHADER_STAGE_SCENE, sceneTarget); + + if (R3D.environment.dof.mode != R3D_DOF_DISABLED) { + sceneTarget = pass_post_dof(sceneTarget); + } + + if (R3D.environment.bloom.mode != R3D_BLOOM_DISABLED) { + sceneTarget = pass_post_bloom(sceneTarget); + } + + if (R3D.environment.autoExposure.enabled) { + sceneTarget = pass_post_auto_exposure(sceneTarget); + } + + sceneTarget = pass_post_screen(R3D_SCREEN_SHADER_STAGE_POST, sceneTarget); + sceneTarget = pass_post_output(sceneTarget); + + sceneTarget = pass_post_screen(R3D_SCREEN_SHADER_STAGE_OUTPUT, sceneTarget); + + switch (R3D.aaMode) { + case R3D_ANTI_ALIASING_MODE_FXAA: + sceneTarget = pass_post_fxaa(sceneTarget); + break; + case R3D_ANTI_ALIASING_MODE_SMAA: + sceneTarget = pass_post_smaa(sceneTarget); + break; + default: + break; + } + + sceneTarget = pass_post_screen(R3D_SCREEN_SHADER_STAGE_FINAL, sceneTarget); + + switch (R3D.outputMode) { + case R3D_OUTPUT_SCENE: blit_to_screen(r3d_target_swap_scene(sceneTarget)); break; + case R3D_OUTPUT_ALBEDO: visualize_to_screen(R3D_TARGET_ALBEDO); break; + case R3D_OUTPUT_NORMAL: visualize_to_screen(R3D_TARGET_NORMAL); break; + case R3D_OUTPUT_ORM: visualize_to_screen(R3D_TARGET_ORM); break; + case R3D_OUTPUT_DIFFUSE: visualize_to_screen(R3D_TARGET_DIFFUSE); break; + case R3D_OUTPUT_SPECULAR: visualize_to_screen(R3D_TARGET_SPECULAR); break; + case R3D_OUTPUT_SSAO: visualize_to_screen(ssaoSource); break; + case R3D_OUTPUT_SSIL: visualize_to_screen(ssilSource); break; + case R3D_OUTPUT_SSGI: visualize_to_screen(ssgiSource); break; + case R3D_OUTPUT_SSR: visualize_to_screen(ssrSource); break; + case R3D_OUTPUT_BLOOM: visualize_to_screen(R3D_TARGET_BLOOM); break; + case R3D_OUTPUT_DOF: visualize_to_screen(R3D_TARGET_DOF_COC); break; + } + + /* --- Reset states changed by R3D --- */ + + cleanup_after_render(); +} + +void R3D_BeginCluster(BoundingBox aabb) +{ + if (!r3d_render_cluster_begin(aabb)) { + R3D_TRACELOG(LOG_WARNING, "Failed to begin cluster"); + } +} + +void R3D_EndCluster(void) +{ + if (!r3d_render_cluster_end()) { + R3D_TRACELOG(LOG_WARNING, "Failed to end cluster"); + } +} + +void R3D_DrawMesh(R3D_Mesh mesh, R3D_Material material, Vector3 position, float scale) +{ + Matrix transform = r3d_matrix_st((Vector3) {scale, scale, scale}, position); + R3D_DrawMeshPro(mesh, material, transform); +} + +void R3D_DrawMeshEx(R3D_Mesh mesh, R3D_Material material, Vector3 position, Quaternion rotation, Vector3 scale) +{ + Matrix transform = r3d_matrix_srt_quat(scale, rotation, position); + R3D_DrawMeshPro(mesh, material, transform); +} + +void R3D_DrawMeshPro(R3D_Mesh mesh, R3D_Material material, Matrix transform) +{ + if (!IS_MESH_VALID(mesh)) return; + + r3d_render_group_t drawGroup = {0}; + drawGroup.transform = transform; + drawGroup.obb = R3D_GetOrientedBox(mesh.aabb, transform); + + r3d_render_group_push(&drawGroup); + + r3d_render_call_t drawCall = {0}; + drawCall.type = R3D_RENDER_CALL_MESH; + drawCall.mesh.material = material; + drawCall.mesh.instance = mesh; + + r3d_render_call_push(&drawCall); +} + +void R3D_DrawMeshInstanced(R3D_Mesh mesh, R3D_Material material, R3D_InstanceBuffer instances, int count) +{ + R3D_DrawMeshInstancedPro(mesh, material, instances, 0, count, R3D_MATRIX_IDENTITY); +} + +void R3D_DrawMeshInstancedEx(R3D_Mesh mesh, R3D_Material material, R3D_InstanceBuffer instances, int offset, int count) +{ + R3D_DrawMeshInstancedPro(mesh, material, instances, offset, count, R3D_MATRIX_IDENTITY); +} + +void R3D_DrawMeshInstancedPro(R3D_Mesh mesh, R3D_Material material, R3D_InstanceBuffer instances, int offset, int count, Matrix transform) +{ + if (count <= 0) return; + if (!IS_MESH_VALID(mesh)) return; + + r3d_render_group_t drawGroup = {0}; + drawGroup.transform = transform; + drawGroup.instances = instances; + drawGroup.instanceOffset = CLAMP(offset, 0, instances.capacity); + drawGroup.instanceCount = CLAMP(count, 0, instances.capacity - offset); + + r3d_render_group_push(&drawGroup); + + r3d_render_call_t drawCall = {0}; + drawCall.type = R3D_RENDER_CALL_MESH; + drawCall.mesh.material = material; + drawCall.mesh.instance = mesh; + + r3d_render_call_push(&drawCall); +} + +void R3D_DrawModel(R3D_Model model, Vector3 position, float scale) +{ + Matrix transform = r3d_matrix_st((Vector3) {scale, scale, scale}, position); + R3D_DrawModelPro(model, transform); +} + +void R3D_DrawModelEx(R3D_Model model, Vector3 position, Quaternion rotation, Vector3 scale) +{ + Matrix transform = r3d_matrix_srt_quat(scale, rotation, position); + R3D_DrawModelPro(model, transform); +} + +void R3D_DrawModelPro(R3D_Model model, Matrix transform) +{ + r3d_render_group_t drawGroup = {0}; + drawGroup.transform = transform; + drawGroup.obb = R3D_GetOrientedBox(model.aabb, transform); + drawGroup.skinTexture = model.skeleton.skinTexture; + + r3d_render_group_push(&drawGroup); + + for (int i = 0; i < model.meshCount; i++) + { + const R3D_Mesh* mesh = &model.meshes[i]; + if (!IS_MESH_VALID(*mesh)) continue; + + r3d_render_call_t drawCall = {0}; + drawCall.type = R3D_RENDER_CALL_MESH; + drawCall.mesh.material = model.materials[model.meshMaterials[i]]; + drawCall.mesh.instance = *mesh; + + r3d_render_call_push(&drawCall); + } +} + +void R3D_DrawModelInstanced(R3D_Model model, R3D_InstanceBuffer instances, int count) +{ + R3D_DrawModelInstancedPro(model, instances, 0, count, R3D_MATRIX_IDENTITY); +} + +void R3D_DrawModelInstancedEx(R3D_Model model, R3D_InstanceBuffer instances, int offset, int count) +{ + R3D_DrawModelInstancedPro(model, instances, offset, count, R3D_MATRIX_IDENTITY); +} + +void R3D_DrawModelInstancedPro(R3D_Model model, R3D_InstanceBuffer instances, int offset, int count, Matrix transform) +{ + if (count <= 0) return; + + r3d_render_group_t drawGroup = {0}; + drawGroup.transform = transform; + drawGroup.skinTexture = model.skeleton.skinTexture; + drawGroup.instances = instances; + drawGroup.instanceOffset = CLAMP(offset, 0, instances.capacity); + drawGroup.instanceCount = CLAMP(count, 0, instances.capacity - offset); + + r3d_render_group_push(&drawGroup); + + for (int i = 0; i < model.meshCount; i++) + { + const R3D_Mesh* mesh = &model.meshes[i]; + if (!IS_MESH_VALID(*mesh)) continue; + + r3d_render_call_t drawCall = {0}; + drawCall.type = R3D_RENDER_CALL_MESH; + drawCall.mesh.material = model.materials[model.meshMaterials[i]]; + drawCall.mesh.instance = *mesh; + + r3d_render_call_push(&drawCall); + } +} + +void R3D_DrawAnimatedModel(R3D_Model model, R3D_AnimationPlayer player, Vector3 position, float scale) +{ + Matrix transform = r3d_matrix_st((Vector3) {scale, scale, scale}, position); + R3D_DrawAnimatedModelPro(model, player, transform); +} + +void R3D_DrawAnimatedModelEx(R3D_Model model, R3D_AnimationPlayer player, Vector3 position, Quaternion rotation, Vector3 scale) +{ + Matrix transform = r3d_matrix_srt_quat(scale, rotation, position); + R3D_DrawAnimatedModelPro(model, player, transform); +} + +void R3D_DrawAnimatedModelPro(R3D_Model model, R3D_AnimationPlayer player, Matrix transform) +{ + r3d_render_group_t drawGroup = {0}; + drawGroup.transform = transform; + drawGroup.obb = R3D_GetOrientedBox(model.aabb, transform); + + drawGroup.skinTexture = (player.skinTexture > 0) + ? player.skinTexture : model.skeleton.skinTexture; + + r3d_render_group_push(&drawGroup); + + for (int i = 0; i < model.meshCount; i++) + { + const R3D_Mesh* mesh = &model.meshes[i]; + if (!IS_MESH_VALID(*mesh)) continue; + + r3d_render_call_t drawCall = {0}; + drawCall.type = R3D_RENDER_CALL_MESH; + drawCall.mesh.material = model.materials[model.meshMaterials[i]]; + drawCall.mesh.instance = *mesh; + + r3d_render_call_push(&drawCall); + } +} + +void R3D_DrawAnimatedModelInstanced(R3D_Model model, R3D_AnimationPlayer player, R3D_InstanceBuffer instances, int count) +{ + R3D_DrawAnimatedModelInstancedPro(model, player, instances, 0, count, R3D_MATRIX_IDENTITY); +} + +void R3D_DrawAnimatedModelInstancedEx(R3D_Model model, R3D_AnimationPlayer player, R3D_InstanceBuffer instances, int offset, int count) +{ + R3D_DrawAnimatedModelInstancedPro(model, player, instances, offset, count, R3D_MATRIX_IDENTITY); +} + +void R3D_DrawAnimatedModelInstancedPro(R3D_Model model, R3D_AnimationPlayer player, R3D_InstanceBuffer instances, int offset, int count, Matrix transform) +{ + if (count <= 0) return; + + r3d_render_group_t drawGroup = {0}; + drawGroup.transform = transform; + drawGroup.instances = instances; + drawGroup.instanceOffset = CLAMP(offset, 0, instances.capacity); + drawGroup.instanceCount = CLAMP(count, 0, instances.capacity - offset); + + drawGroup.skinTexture = (player.skinTexture > 0) + ? player.skinTexture : model.skeleton.skinTexture; + + r3d_render_group_push(&drawGroup); + + for (int i = 0; i < model.meshCount; i++) + { + const R3D_Mesh* mesh = &model.meshes[i]; + if (!IS_MESH_VALID(*mesh)) continue; + + r3d_render_call_t drawCall = {0}; + drawCall.type = R3D_RENDER_CALL_MESH; + drawCall.mesh.material = model.materials[model.meshMaterials[i]]; + drawCall.mesh.instance = *mesh; + + r3d_render_call_push(&drawCall); + } +} + +void R3D_DrawDecal(R3D_Decal decal, Vector3 position, float scale) +{ + Matrix transform = r3d_matrix_st((Vector3) {scale, scale, scale}, position); + R3D_DrawDecalPro(decal, transform); +} + +void R3D_DrawDecalEx(R3D_Decal decal, Vector3 position, Quaternion rotation, Vector3 scale) +{ + Matrix transform = r3d_matrix_srt_quat(scale, rotation, position); + R3D_DrawDecalPro(decal, transform); +} + +void R3D_DrawDecalPro(R3D_Decal decal, Matrix transform) +{ + decal.normalThreshold = (decal.normalThreshold == 0.0) ? PI * 2 : decal.normalThreshold * DEG2RAD; + decal.fadeWidth = decal.fadeWidth * DEG2RAD; + + r3d_render_group_t drawGroup = {0}; + drawGroup.transform = transform; + drawGroup.obb = R3D_GetOrientedBox(R3D_AABB_UNIT, transform); + + r3d_render_group_push(&drawGroup); + + r3d_render_call_t drawCall = {0}; + drawCall.type = R3D_RENDER_CALL_DECAL; + drawCall.decal.instance = decal; + + r3d_render_call_push(&drawCall); +} + +void R3D_DrawDecalInstanced(R3D_Decal decal, R3D_InstanceBuffer instances, int count) +{ + R3D_DrawDecalInstancedPro(decal, instances, 0, count, R3D_MATRIX_IDENTITY); +} + +void R3D_DrawDecalInstancedEx(R3D_Decal decal, R3D_InstanceBuffer instances, int offset, int count) +{ + R3D_DrawDecalInstancedPro(decal, instances, offset, count, R3D_MATRIX_IDENTITY); +} + +void R3D_DrawDecalInstancedPro(R3D_Decal decal, R3D_InstanceBuffer instances, int offset, int count, Matrix transform) +{ + if (count <= 0) return; + + decal.normalThreshold = (decal.normalThreshold == 0.0) ? PI * 2 : decal.normalThreshold * DEG2RAD; + decal.fadeWidth = decal.fadeWidth * DEG2RAD; + + r3d_render_group_t drawGroup = {0}; + drawGroup.transform = transform; + drawGroup.instances = instances; + drawGroup.instanceOffset = CLAMP(offset, 0, instances.capacity); + drawGroup.instanceCount = CLAMP(count, 0, instances.capacity - offset); + + r3d_render_group_push(&drawGroup); + + r3d_render_call_t drawCall = {0}; + drawCall.type = R3D_RENDER_CALL_DECAL; + drawCall.decal.instance = decal; + + r3d_render_call_push(&drawCall); +} + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +static inline bool view_has_target(RenderTexture target) +{ + return target.id != 0 && target.texture.id != 0; +} + +static void view_get_target_size(RenderTexture target, int* width, int* height) +{ + if (view_has_target(target)) { + *width = target.texture.width; + *height = target.texture.height; + } + else { + *width = GetRenderWidth(); + *height = GetRenderHeight(); + } + + if (*width <= 0) *width = 1; + if (*height <= 0) *height = 1; +} + +static Rectangle view_resolve_viewport(R3D_View view) +{ + int targetW = 1; + int targetH = 1; + + view_get_target_size(view.target, &targetW, &targetH); + + if (view.viewport.width <= 0.0f || view.viewport.height <= 0.0f) { + return (Rectangle) { + 0.0f, + 0.0f, + (float)targetW, + (float)targetH + }; + } + + return view.viewport; +} + +static Rectangle view_fit_aspect(Rectangle rect, double aspect) +{ + if (rect.width <= 0.0f || rect.height <= 0.0f || aspect <= 0.0) { + return rect; + } + + double rectAspect = (double)rect.width / (double)rect.height; + + if (aspect > rectAspect) { + float newH = (float)((double)rect.width / aspect); + rect.y += (rect.height - newH) * 0.5f; + rect.height = newH; + } + else { + float newW = (float)((double)rect.height * aspect); + rect.x += (rect.width - newW) * 0.5f; + rect.width = newW; + } + + return rect; +} + +static Rectangle view_resolve_present_rect(R3D_View view) +{ + Rectangle viewport = view_resolve_viewport(view); + + switch (R3D.aspectMode) { + case R3D_ASPECT_KEEP: { + int srcW = 1; + int srcH = 1; + + r3d_target_get_resolution(&srcW, &srcH, R3D_TARGET_SCENE_0, 0); + + if (srcW <= 0) srcW = 1; + if (srcH <= 0) srcH = 1; + + double srcAspect = (double)srcW / (double)srcH; + return view_fit_aspect(viewport, srcAspect); + } + + case R3D_ASPECT_EXPAND: + default: + return viewport; + } +} + +void update_view_state(R3D_View view) +{ + Rectangle viewport = view_resolve_present_rect(view); + + double aspect = 1.0; + if (viewport.height > 0.0f) { + aspect = (double)viewport.width / (double)viewport.height; + } + + Matrix matView = R3D_GetCameraView(view.camera); + Matrix matProj = R3D_GetCameraProj(view.camera, aspect); + Matrix matViewProj = MatrixMultiply(matView, matProj); + + R3D.viewState.camera = view.camera; + R3D.viewState.viewport = viewport; + R3D.viewState.frustum = R3D_ComputeFrustum(matViewProj); + R3D.viewState.view = matView; + R3D.viewState.proj = matProj; + R3D.viewState.invView = MatrixInvert(matView); + R3D.viewState.invProj = MatrixInvert(matProj); + R3D.viewState.viewProj = matViewProj; + R3D.viewState.aspect = aspect; +} + +void upload_light_array_block_for_mesh(const r3d_render_call_t* call, bool shadow) +{ + assert(call->type == R3D_RENDER_CALL_MESH); //< Paranoid assert, should be fine + + r3d_shader_block_light_array_t lights = {0}; + + R3D_LIGHT_FOR_EACH_VISIBLE(light) + { + // Check if the geometry "touches" the light area + // It's not the most accurate possible but hey + if (light->type != R3D_LIGHT_DIR) { + if (!CheckCollisionBoxes(light->aabb, call->mesh.instance.aabb)) { + continue; + } + } + + r3d_shader_block_light_t* data = &lights.uLights[lights.uNumLights]; + data->viewProj = MatrixTranspose(light->viewProj[0]); + data->color = light->color; + data->position = light->position; + data->direction = light->direction; + data->specular = light->specular; + data->energy = light->energy; + data->range = light->range; + data->near = light->near; + data->far = light->far; + data->attenuation = light->attenuation; + data->innerCutOff = light->innerCutOff; + data->outerCutOff = light->outerCutOff; + data->shadowSoftness = light->shadowSoftness; + data->shadowOpacity = light->shadowOpacity; + data->shadowDepthBias = light->shadowDepthBias; + data->shadowSlopeBias = light->shadowSlopeBias; + data->shadowLayer = shadow ? light->shadowLayer : -1; + data->type = light->type; + + if (++lights.uNumLights == R3D_MAX_LIGHT_FORWARD_PER_MESH) { + break; + } + } + + r3d_shader_set_uniform_block(R3D_SHADER_BLOCK_LIGHT_ARRAY, &lights); +} + +void upload_frame_block(void) +{ + static int frameIndex = 0; + + r3d_shader_block_frame_t frame = { + .screenSize = (Vector2) {(float)R3D_TARGET_SIZE_W, (float)R3D_TARGET_SIZE_H}, + .texelSize = (Vector2) {R3D_TARGET_TEXEL_W, R3D_TARGET_TEXEL_H}, + .time = (float)GetTime(), + .index = frameIndex++, + }; + + r3d_shader_set_uniform_block(R3D_SHADER_BLOCK_FRAME, &frame); +} + +void upload_view_block(void) +{ + r3d_shader_block_view_t view = { + .position = R3D.viewState.camera.position, + .view = MatrixTranspose(R3D.viewState.view), + .invView = MatrixTranspose(R3D.viewState.invView), + .proj = MatrixTranspose(R3D.viewState.proj), + .invProj = MatrixTranspose(R3D.viewState.invProj), + .viewProj = MatrixTranspose(R3D.viewState.viewProj), + .projMode = R3D.viewState.camera.projection, + .aspect = (float)R3D.viewState.aspect, + .near = (float)R3D.viewState.camera.nearPlane, + .far = (float)R3D.viewState.camera.farPlane, + }; + + r3d_shader_set_uniform_block(R3D_SHADER_BLOCK_VIEW, &view); +} + +void upload_env_block(void) +{ + const R3D_EnvBackground* background = &R3D.environment.background; + const R3D_EnvAmbient* ambient = &R3D.environment.ambient; + + r3d_shader_block_env_t env = {0}; + + int iProbe = 0; + R3D_ENV_PROBE_FOR_EACH_VISIBLE(probe) { + env.uProbes[iProbe] = (struct r3d_shader_block_env_probe) { + .position = probe->position, + .falloff = probe->falloff, + .range = probe->range, + .irradiance = probe->irradiance, + .prefilter = probe->prefilter + }; + if (++iProbe >= R3D_MAX_PROBE_ON_SCREEN) { + break; + } + } + + env.uAmbient.rotation = background->rotation; + env.uAmbient.color = r3d_color_to_vec4(ambient->color); + env.uAmbient.energy = ambient->energy; + env.uAmbient.irradiance = (int)ambient->map.irradiance - 1; + env.uAmbient.prefilter = (int)ambient->map.prefilter - 1; + + env.uNumPrefilterLevels = r3d_get_mip_levels_1d(R3D_CUBEMAP_PREFILTER_SIZE); + env.uNumProbes = iProbe; + + r3d_shader_set_uniform_block(R3D_SHADER_BLOCK_ENV, &env); +} + +void upload_fog_block(void) +{ + const R3D_EnvFog* fog = &R3D.environment.fog; + static r3d_shader_block_fog_t prevFog = {0}; + r3d_shader_block_fog_t currFog = {0}; + + currFog.color = r3d_color_to_linear_vec3(fog->color, R3D.colorSpace); + currFog.start = fog->start; + currFog.end = fog->end; + currFog.density = fog->density; + currFog.skyAffect = fog->skyAffect; + currFog.mode = fog->mode; + + if (memcmp(&prevFog, &currFog, sizeof(currFog)) != 0) { + r3d_shader_set_uniform_block(R3D_SHADER_BLOCK_FOG, &currFog); + prevFog = currFog; + } + else { + r3d_shader_bind_uniform_block(R3D_SHADER_BLOCK_FOG); + } +} + +void raster_depth(const r3d_render_call_t* call, const Matrix* viewProj, r3d_light_t* light) +{ + assert(call->type == R3D_RENDER_CALL_MESH); //< Paranoid assert, should be fine + + const r3d_render_group_t* group = r3d_render_get_call_group(call); + const R3D_Material* material = &call->mesh.material; + const R3D_Mesh* mesh = &call->mesh.instance; + + /* --- Use shader --- */ + + R3D_SurfaceShader* shader = call->mesh.material.shader; + R3D_SHADER_USE_SELECT(scene.depth, shader); + + /* --- Send matrices --- */ + + R3D_SHADER_SET_MAT4_SELECT(scene.depth, shader, uMatModel, group->transform); + R3D_SHADER_SET_MAT4_SELECT(scene.depth, shader, uMatViewProj, *viewProj); + + /* --- Send skinning related data --- */ + + if (group->skinTexture > 0) { + R3D_SHADER_BIND_SAMPLER_SELECT(scene.depth, shader, uBoneMatricesTex, group->skinTexture); + R3D_SHADER_SET_INT_SELECT(scene.depth, shader, uSkinning, true); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.depth, shader, uSkinning, false); + } + + /* --- Send billboard related data --- */ + + R3D_SHADER_SET_INT_SELECT(scene.depth, shader, uBillboard, material->billboardMode); + if (material->billboardMode != R3D_BILLBOARD_DISABLED) { + R3D_SHADER_SET_MAT4_SELECT(scene.depth, shader, uMatInvView, R3D.viewState.invView); + } + + /* --- Set texcoord offset/scale --- */ + + R3D_SHADER_SET_VEC2_SELECT(scene.depth, shader, uTexCoordOffset, material->uvOffset); + R3D_SHADER_SET_VEC2_SELECT(scene.depth, shader, uTexCoordScale, material->uvScale); + + /* --- Set transparency material data --- */ + + R3D_SHADER_BIND_SAMPLER_SELECT(scene.depth, shader, uAlbedoMap, R3D_TEXTURE_SELECT(material->albedo.texture.id, WHITE)); + R3D_SHADER_SET_COL4_SELECT(scene.depth, shader, uAlbedoColor, R3D.colorSpace, material->albedo.color); + + if (material->transparencyMode == R3D_TRANSPARENCY_PREPASS) { + R3D_SHADER_SET_FLOAT_SELECT(scene.depth, shader, uAlphaCutoff, (light != NULL) ? 0.1f : 0.99f); + } + else { + R3D_SHADER_SET_FLOAT_SELECT(scene.depth, shader, uAlphaCutoff, material->alphaCutoff); + } + + /* --- Applying material parameters that are independent of shaders --- */ + + if (light != NULL) { + r3d_driver_set_shadow_cast_mode(mesh->shadowCastMode, material->cullMode); + } + else { + r3d_driver_set_depth_state(material->depth); + r3d_driver_set_stencil_state(material->stencil); + r3d_driver_set_cull_mode(material->cullMode); + } + + /* --- Rendering the object corresponding to the draw call --- */ + + if (r3d_render_has_instances(group)) { + R3D_SHADER_SET_INT_SELECT(scene.depth, shader, uInstancing, true); + r3d_render_draw_instanced(call); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.depth, shader, uInstancing, false); + r3d_render_draw(call); + } +} + +void raster_depth_cube(const r3d_render_call_t* call, const Matrix* viewProj, r3d_light_t* light) +{ + assert(call->type == R3D_RENDER_CALL_MESH); //< Paranoid assert, should be fine + + const r3d_render_group_t* group = r3d_render_get_call_group(call); + const R3D_Material* material = &call->mesh.material; + const R3D_Mesh* mesh = &call->mesh.instance; + + /* --- Use shader --- */ + + R3D_SurfaceShader* shader = call->mesh.material.shader; + R3D_SHADER_USE_SELECT(scene.depthCube, shader); + + /* --- Set shadow related data --- */ + + if (light != NULL) { + R3D_SHADER_SET_FLOAT_SELECT(scene.depthCube, shader, uFar, light->far); + R3D_SHADER_SET_VEC3_SELECT(scene.depthCube, shader, uViewPosition, light->position); + } + + /* --- Send matrices --- */ + + R3D_SHADER_SET_MAT4_SELECT(scene.depthCube, shader, uMatModel, group->transform); + R3D_SHADER_SET_MAT4_SELECT(scene.depthCube, shader, uMatViewProj, *viewProj); + + /* --- Send skinning related data --- */ + + if (group->skinTexture > 0) { + R3D_SHADER_BIND_SAMPLER_SELECT(scene.depthCube, shader, uBoneMatricesTex, group->skinTexture); + R3D_SHADER_SET_INT_SELECT(scene.depthCube, shader, uSkinning, true); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.depthCube, shader, uSkinning, false); + } + + /* --- Send billboard related data --- */ + + R3D_SHADER_SET_INT_SELECT(scene.depthCube, shader, uBillboard, material->billboardMode); + if (material->billboardMode != R3D_BILLBOARD_DISABLED) { + R3D_SHADER_SET_MAT4_SELECT(scene.depthCube, shader, uMatInvView, R3D.viewState.invView); + } + + /* --- Set texcoord offset/scale --- */ + + R3D_SHADER_SET_VEC2_SELECT(scene.depthCube, shader, uTexCoordOffset, material->uvOffset); + R3D_SHADER_SET_VEC2_SELECT(scene.depthCube, shader, uTexCoordScale, material->uvScale); + + /* --- Set transparency material data --- */ + + R3D_SHADER_BIND_SAMPLER_SELECT(scene.depthCube, shader, uAlbedoMap, R3D_TEXTURE_SELECT(material->albedo.texture.id, WHITE)); + R3D_SHADER_SET_COL4_SELECT(scene.depthCube, shader, uAlbedoColor, R3D.colorSpace, material->albedo.color); + + if (material->transparencyMode == R3D_TRANSPARENCY_PREPASS) { + R3D_SHADER_SET_FLOAT_SELECT(scene.depthCube, shader, uAlphaCutoff, (light != NULL) ? 0.1f : 0.99f); + } + else { + R3D_SHADER_SET_FLOAT_SELECT(scene.depthCube, shader, uAlphaCutoff, material->alphaCutoff); + } + + /* --- Applying material parameters that are independent of shaders --- */ + + if (light != NULL) { + r3d_driver_set_shadow_cast_mode(mesh->shadowCastMode, material->cullMode); + } + else { + r3d_driver_set_depth_state(material->depth); + r3d_driver_set_stencil_state(material->stencil); + r3d_driver_set_cull_mode(material->cullMode); + } + + /* --- Rendering the object corresponding to the draw call --- */ + + if (r3d_render_has_instances(group)) { + R3D_SHADER_SET_INT_SELECT(scene.depthCube, shader, uInstancing, true); + r3d_render_draw_instanced(call); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.depthCube, shader, uInstancing, false); + r3d_render_draw(call); + } +} + +void raster_probe_forward(const r3d_render_call_t* call, const r3d_env_probe_t* probe, int face) +{ + assert(call->type == R3D_RENDER_CALL_MESH); //< Paranoid assert, should be fine + + const r3d_render_group_t* group = r3d_render_get_call_group(call); + const R3D_Material* material = &call->mesh.material; + const R3D_Mesh* mesh = &call->mesh.instance; + + /* --- Use shader --- */ + + R3D_SurfaceShader* shader = call->mesh.material.shader; + R3D_SHADER_USE_SELECT(scene.probeForward, shader); + + /* --- Set probe related data --- */ + + R3D_SHADER_SET_VEC3_SELECT(scene.probeForward, shader, uViewPosition, probe->position); + R3D_SHADER_SET_INT_SELECT(scene.probeForward, shader, uProbeInterior, probe->interior); + + /* --- Send matrices --- */ + + Matrix matNormal = r3d_matrix_normal(&group->transform); + + R3D_SHADER_SET_MAT4_SELECT(scene.probeForward, shader, uMatModel, group->transform); + R3D_SHADER_SET_MAT4_SELECT(scene.probeForward, shader, uMatNormal, matNormal); + R3D_SHADER_SET_MAT4_SELECT(scene.probeForward, shader, uMatView, probe->view[face]); + R3D_SHADER_SET_MAT4_SELECT(scene.probeForward, shader, uMatInvView, probe->invView[face]); + R3D_SHADER_SET_MAT4_SELECT(scene.probeForward, shader, uMatViewProj, probe->viewProj[face]); + + /* --- Send skinning related data --- */ + + if (group->skinTexture > 0) { + R3D_SHADER_BIND_SAMPLER_SELECT(scene.probeForward, shader, uBoneMatricesTex, group->skinTexture); + R3D_SHADER_SET_INT_SELECT(scene.probeForward, shader, uSkinning, true); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.probeForward, shader, uSkinning, false); + } + + /* --- Send billboard related data --- */ + + R3D_SHADER_SET_INT_SELECT(scene.probeForward, shader, uBillboard, material->billboardMode); + + /* --- Set factor material maps --- */ + + R3D_SHADER_SET_FLOAT_SELECT(scene.probeForward, shader, uEmissionEnergy, material->emission.energy); + R3D_SHADER_SET_FLOAT_SELECT(scene.probeForward, shader, uNormalScale, material->normal.scale); + R3D_SHADER_SET_FLOAT_SELECT(scene.probeForward, shader, uOcclusion, material->orm.occlusion); + R3D_SHADER_SET_FLOAT_SELECT(scene.probeForward, shader, uRoughness, material->orm.roughness); + R3D_SHADER_SET_FLOAT_SELECT(scene.probeForward, shader, uMetalness, material->orm.metalness); + R3D_SHADER_SET_FLOAT_SELECT(scene.probeForward, shader, uSpecular, material->orm.specular); + + /* --- Set texcoord offset/scale --- */ + + R3D_SHADER_SET_VEC2_SELECT(scene.probeForward, shader, uTexCoordOffset, material->uvOffset); + R3D_SHADER_SET_VEC2_SELECT(scene.probeForward, shader, uTexCoordScale, material->uvScale); + + /* --- Set color material maps --- */ + + R3D_SHADER_SET_COL4_SELECT(scene.probeForward, shader, uAlbedoColor, R3D.colorSpace, material->albedo.color); + R3D_SHADER_SET_COL3_SELECT(scene.probeForward, shader, uEmissionColor, R3D.colorSpace, material->emission.color); + + /* --- Bind active texture maps --- */ + + R3D_SHADER_BIND_SAMPLER_SELECT(scene.probeForward, shader, uAlbedoMap, R3D_TEXTURE_SELECT(material->albedo.texture.id, WHITE)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.probeForward, shader, uNormalMap, R3D_TEXTURE_SELECT(material->normal.texture.id, NORMAL)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.probeForward, shader, uEmissionMap, R3D_TEXTURE_SELECT(material->emission.texture.id, WHITE)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.probeForward, shader, uOrmMap, R3D_TEXTURE_SELECT(material->orm.texture.id, WHITE)); + + /* --- Applying material parameters that are independent of shaders --- */ + + r3d_driver_set_depth_state(material->depth); + r3d_driver_set_stencil_state(material->stencil); + r3d_driver_set_blend_mode(material->blendMode, material->transparencyMode); + r3d_driver_set_cull_mode(material->cullMode); + + /* --- Rendering the object corresponding to the draw call --- */ + + if (r3d_render_has_instances(group)) { + R3D_SHADER_SET_INT_SELECT(scene.probeForward, shader, uInstancing, true); + r3d_render_draw_instanced(call); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.probeForward, shader, uInstancing, false); + r3d_render_draw(call); + } +} + +void raster_probe_unlit(const r3d_render_call_t* call, const r3d_env_probe_t* probe, int face) +{ + assert(call->type == R3D_RENDER_CALL_MESH); //< Paranoid assert, should be fine + + const r3d_render_group_t* group = r3d_render_get_call_group(call); + const R3D_Material* material = &call->mesh.material; + const R3D_Mesh* mesh = &call->mesh.instance; + + /* --- Use shader --- */ + + R3D_SurfaceShader* shader = call->mesh.material.shader; + R3D_SHADER_USE_SELECT(scene.probeUnlit, shader); + + /* --- Send matrices --- */ + + Matrix matNormal = r3d_matrix_normal(&group->transform); + + R3D_SHADER_SET_MAT4_SELECT(scene.probeUnlit, shader, uMatModel, group->transform); + R3D_SHADER_SET_MAT4_SELECT(scene.probeUnlit, shader, uMatNormal, matNormal); + R3D_SHADER_SET_MAT4_SELECT(scene.probeUnlit, shader, uMatView, probe->view[face]); + R3D_SHADER_SET_MAT4_SELECT(scene.probeUnlit, shader, uMatInvView, probe->invView[face]); + R3D_SHADER_SET_MAT4_SELECT(scene.probeUnlit, shader, uMatViewProj, probe->viewProj[face]); + + /* --- Send skinning related data --- */ + + if (group->skinTexture > 0) { + R3D_SHADER_BIND_SAMPLER_SELECT(scene.probeUnlit, shader, uBoneMatricesTex, group->skinTexture); + R3D_SHADER_SET_INT_SELECT(scene.probeUnlit, shader, uSkinning, true); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.probeUnlit, shader, uSkinning, false); + } + + /* --- Send billboard related data --- */ + + R3D_SHADER_SET_INT_SELECT(scene.probeUnlit, shader, uBillboard, material->billboardMode); + + /* --- Set misc material values --- */ + + R3D_SHADER_SET_FLOAT_SELECT(scene.probeUnlit, shader, uAlphaCutoff, material->alphaCutoff); + + /* --- Set texcoord offset/scale --- */ + + R3D_SHADER_SET_VEC2_SELECT(scene.probeUnlit, shader, uTexCoordOffset, material->uvOffset); + R3D_SHADER_SET_VEC2_SELECT(scene.probeUnlit, shader, uTexCoordScale, material->uvScale); + + /* --- Set color material maps --- */ + + R3D_SHADER_SET_COL4_SELECT(scene.probeUnlit, shader, uAlbedoColor, R3D.colorSpace, material->albedo.color); + + /* --- Bind active texture maps --- */ + + R3D_SHADER_BIND_SAMPLER_SELECT(scene.probeUnlit, shader, uAlbedoMap, R3D_TEXTURE_SELECT(material->albedo.texture.id, WHITE)); + + /* --- Applying material parameters that are independent of shaders --- */ + + r3d_driver_set_depth_state(material->depth); + r3d_driver_set_stencil_state(material->stencil); + r3d_driver_set_blend_mode(material->blendMode, material->transparencyMode); + r3d_driver_set_cull_mode(material->cullMode); + + /* --- Rendering the object corresponding to the draw call --- */ + + if (r3d_render_has_instances(group)) { + R3D_SHADER_SET_INT_SELECT(scene.probeUnlit, shader, uInstancing, true); + r3d_render_draw_instanced(call); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.probeUnlit, shader, uInstancing, false); + r3d_render_draw(call); + } +} + +void raster_geometry(const r3d_render_call_t* call, bool matchPrepass) +{ + assert(call->type == R3D_RENDER_CALL_MESH); //< Paranoid assert, should be fine + + const r3d_render_group_t* group = r3d_render_get_call_group(call); + const R3D_Material* material = &call->mesh.material; + const R3D_Mesh* mesh = &call->mesh.instance; + + /* --- Use shader --- */ + + R3D_SurfaceShader* shader = call->mesh.material.shader; + R3D_SHADER_USE_SELECT(scene.geometry, shader); + + /* --- Send matrices --- */ + + Matrix matNormal = r3d_matrix_normal(&group->transform); + + R3D_SHADER_SET_MAT4_SELECT(scene.geometry, shader, uMatModel, group->transform); + R3D_SHADER_SET_MAT4_SELECT(scene.geometry, shader, uMatNormal, matNormal); + + /* --- Send skinning related data --- */ + + if (group->skinTexture > 0) { + R3D_SHADER_BIND_SAMPLER_SELECT(scene.geometry, shader, uBoneMatricesTex, group->skinTexture); + R3D_SHADER_SET_INT_SELECT(scene.geometry, shader, uSkinning, true); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.geometry, shader, uSkinning, false); + } + + /* --- Send billboard related data --- */ + + R3D_SHADER_SET_INT_SELECT(scene.geometry, shader, uBillboard, material->billboardMode); + + /* --- Set factor material maps --- */ + + R3D_SHADER_SET_FLOAT_SELECT(scene.geometry, shader, uEmissionEnergy, material->emission.energy); + R3D_SHADER_SET_FLOAT_SELECT(scene.geometry, shader, uNormalScale, material->normal.scale); + R3D_SHADER_SET_FLOAT_SELECT(scene.geometry, shader, uOcclusion, material->orm.occlusion); + R3D_SHADER_SET_FLOAT_SELECT(scene.geometry, shader, uRoughness, material->orm.roughness); + R3D_SHADER_SET_FLOAT_SELECT(scene.geometry, shader, uMetalness, material->orm.metalness); + R3D_SHADER_SET_FLOAT_SELECT(scene.geometry, shader, uSpecular, material->orm.specular); + + /* --- Set misc material values --- */ + + R3D_SHADER_SET_FLOAT_SELECT(scene.geometry, shader, uAlphaCutoff, material->alphaCutoff); + + /* --- Set texcoord offset/scale --- */ + + R3D_SHADER_SET_VEC2_SELECT(scene.geometry, shader, uTexCoordOffset, material->uvOffset); + R3D_SHADER_SET_VEC2_SELECT(scene.geometry, shader, uTexCoordScale, material->uvScale); + + /* --- Set color material maps --- */ + + R3D_SHADER_SET_COL4_SELECT(scene.geometry, shader, uAlbedoColor, R3D.colorSpace, material->albedo.color); + R3D_SHADER_SET_COL3_SELECT(scene.geometry, shader, uEmissionColor, R3D.colorSpace, material->emission.color); + + /* --- Bind active texture maps --- */ + + R3D_SHADER_BIND_SAMPLER_SELECT(scene.geometry, shader, uAlbedoMap, R3D_TEXTURE_SELECT(material->albedo.texture.id, WHITE)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.geometry, shader, uNormalMap, R3D_TEXTURE_SELECT(material->normal.texture.id, NORMAL)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.geometry, shader, uEmissionMap, R3D_TEXTURE_SELECT(material->emission.texture.id, WHITE)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.geometry, shader, uOrmMap, R3D_TEXTURE_SELECT(material->orm.texture.id, WHITE)); + + /* --- Applying material parameters that are independent of shaders --- */ + + if (matchPrepass) { + r3d_driver_set_depth_offset(material->depth.offsetUnits, material->depth.offsetFactor); + r3d_driver_set_depth_range(material->depth.rangeNear, material->depth.rangeFar); + } + else { + r3d_driver_set_depth_state(material->depth); + r3d_driver_set_stencil_state(material->stencil); + } + + r3d_driver_set_cull_mode(material->cullMode); + + /* --- Rendering the object corresponding to the draw call --- */ + + if (r3d_render_has_instances(group)) { + R3D_SHADER_SET_INT_SELECT(scene.geometry, shader, uInstancing, true); + r3d_render_draw_instanced(call); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.geometry, shader, uInstancing, false); + r3d_render_draw(call); + } +} + +void raster_decal(const r3d_render_call_t* call) +{ + assert(call->type == R3D_RENDER_CALL_DECAL); //< Paranoid assert, should be fine + + const r3d_render_group_t* group = r3d_render_get_call_group(call); + const R3D_Decal* decal = &call->decal.instance; + + /* --- Use shader --- */ + + R3D_SurfaceShader* shader = call->decal.instance.shader; + R3D_SHADER_USE_SELECT(scene.decal, shader); + + /* --- Bind global textures --- */ + + R3D_SHADER_BIND_SAMPLER_SELECT(scene.decal, shader, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 0)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.decal, shader, uGeomNormalTex, r3d_target_get(R3D_TARGET_GEOM_NORMAL)); + + /* --- Set additional matrix uniforms --- */ + + Matrix matNormal = r3d_matrix_normal(&group->transform); + + R3D_SHADER_SET_MAT4_SELECT(scene.decal, shader, uMatModel, group->transform); + R3D_SHADER_SET_MAT4_SELECT(scene.decal, shader, uMatNormal, matNormal); + + /* --- Set factor material maps --- */ + + R3D_SHADER_SET_FLOAT_SELECT(scene.decal, shader, uEmissionEnergy, decal->emission.energy); + R3D_SHADER_SET_FLOAT_SELECT(scene.decal, shader, uNormalScale, decal->normal.scale); + R3D_SHADER_SET_FLOAT_SELECT(scene.decal, shader, uOcclusion, decal->orm.occlusion); + R3D_SHADER_SET_FLOAT_SELECT(scene.decal, shader, uRoughness, decal->orm.roughness); + R3D_SHADER_SET_FLOAT_SELECT(scene.decal, shader, uMetalness, decal->orm.metalness); + R3D_SHADER_SET_FLOAT_SELECT(scene.decal, shader, uSpecular, decal->orm.specular); + + /* --- Set misc material values --- */ + + R3D_SHADER_SET_FLOAT_SELECT(scene.decal, shader, uAlphaCutoff, decal->alphaCutoff); + + /* --- Set texcoord offset/scale --- */ + + R3D_SHADER_SET_VEC2_SELECT(scene.decal, shader, uTexCoordOffset, decal->uvOffset); + R3D_SHADER_SET_VEC2_SELECT(scene.decal, shader, uTexCoordScale, decal->uvScale); + + /* --- Set color material maps --- */ + + R3D_SHADER_SET_COL4_SELECT(scene.decal, shader, uAlbedoColor, R3D.colorSpace, decal->albedo.color); + R3D_SHADER_SET_COL3_SELECT(scene.decal, shader, uEmissionColor, R3D.colorSpace, decal->emission.color); + + /* --- Set decal specific values --- */ + + R3D_SHADER_SET_FLOAT_SELECT(scene.decal, shader, uNormalThreshold, decal->normalThreshold); + R3D_SHADER_SET_FLOAT_SELECT(scene.decal, shader, uFadeWidth, decal->fadeWidth); + R3D_SHADER_SET_INT_SELECT(scene.decal, shader, uApplyColor, decal->applyColor && (decal->albedo.texture.id != 0)); + + /* --- Bind active texture maps --- */ + + R3D_SHADER_BIND_SAMPLER_SELECT(scene.decal, shader, uAlbedoMap, R3D_TEXTURE_SELECT(decal->albedo.texture.id, WHITE)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.decal, shader, uNormalMap, R3D_TEXTURE_SELECT(decal->normal.texture.id, NORMAL)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.decal, shader, uEmissionMap, R3D_TEXTURE_SELECT(decal->emission.texture.id, WHITE)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.decal, shader, uOrmMap, R3D_TEXTURE_SELECT(decal->orm.texture.id, WHITE)); + + /* --- Rendering the object corresponding to the draw call --- */ + + if (r3d_render_has_instances(group)) { + R3D_SHADER_SET_INT_SELECT(scene.decal, shader, uInstancing, true); + r3d_render_draw_instanced(call); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.decal, shader, uInstancing, false); + r3d_render_draw(call); + } +} + +void raster_forward(const r3d_render_call_t* call) +{ + assert(call->type == R3D_RENDER_CALL_MESH); //< Paranoid assert, should be fine + + const r3d_render_group_t* group = r3d_render_get_call_group(call); + const R3D_Material* material = &call->mesh.material; + const R3D_Mesh* mesh = &call->mesh.instance; + + /* --- Use shader --- */ + + R3D_SurfaceShader* shader = call->mesh.material.shader; + R3D_SHADER_USE_SELECT(scene.forward, shader); + + /* --- Set view related data --- */ + + // NOTE: We don't use the UBO view position because this shader is reused by probes with their own view position + R3D_SHADER_SET_VEC3_SELECT(scene.forward, shader, uViewPosition, R3D.viewState.camera.position); + + /* --- Send matrices --- */ + + Matrix matNormal = r3d_matrix_normal(&group->transform); + + R3D_SHADER_SET_MAT4_SELECT(scene.forward, shader, uMatModel, group->transform); + R3D_SHADER_SET_MAT4_SELECT(scene.forward, shader, uMatNormal, matNormal); + + /* --- Send skinning related data --- */ + + if (group->skinTexture > 0) { + R3D_SHADER_BIND_SAMPLER_SELECT(scene.forward, shader, uBoneMatricesTex, group->skinTexture); + R3D_SHADER_SET_INT_SELECT(scene.forward, shader, uSkinning, true); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.forward, shader, uSkinning, false); + } + + /* --- Send billboard related data --- */ + + R3D_SHADER_SET_INT_SELECT(scene.forward, shader, uBillboard, material->billboardMode); + + /* --- Set factor material maps --- */ + + R3D_SHADER_SET_FLOAT_SELECT(scene.forward, shader, uEmissionEnergy, material->emission.energy); + R3D_SHADER_SET_FLOAT_SELECT(scene.forward, shader, uNormalScale, material->normal.scale); + R3D_SHADER_SET_FLOAT_SELECT(scene.forward, shader, uOcclusion, material->orm.occlusion); + R3D_SHADER_SET_FLOAT_SELECT(scene.forward, shader, uRoughness, material->orm.roughness); + R3D_SHADER_SET_FLOAT_SELECT(scene.forward, shader, uMetalness, material->orm.metalness); + R3D_SHADER_SET_FLOAT_SELECT(scene.forward, shader, uSpecular, material->orm.specular); + + /* --- Set texcoord offset/scale --- */ + + R3D_SHADER_SET_VEC2_SELECT(scene.forward, shader, uTexCoordOffset, material->uvOffset); + R3D_SHADER_SET_VEC2_SELECT(scene.forward, shader, uTexCoordScale, material->uvScale); + + /* --- Set color material maps --- */ + + R3D_SHADER_SET_COL4_SELECT(scene.forward, shader, uAlbedoColor, R3D.colorSpace, material->albedo.color); + R3D_SHADER_SET_COL3_SELECT(scene.forward, shader, uEmissionColor, R3D.colorSpace, material->emission.color); + + /* --- Bind active texture maps --- */ + + R3D_SHADER_BIND_SAMPLER_SELECT(scene.forward, shader, uAlbedoMap, R3D_TEXTURE_SELECT(material->albedo.texture.id, WHITE)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.forward, shader, uNormalMap, R3D_TEXTURE_SELECT(material->normal.texture.id, NORMAL)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.forward, shader, uEmissionMap, R3D_TEXTURE_SELECT(material->emission.texture.id, WHITE)); + R3D_SHADER_BIND_SAMPLER_SELECT(scene.forward, shader, uOrmMap, R3D_TEXTURE_SELECT(material->orm.texture.id, WHITE)); + + /* --- Applying material parameters that are independent of shaders --- */ + + r3d_driver_set_depth_state(material->depth); + r3d_driver_set_stencil_state(material->stencil); + r3d_driver_set_blend_mode(material->blendMode, material->transparencyMode); + r3d_driver_set_cull_mode(material->cullMode); + + /* --- Rendering the object corresponding to the draw call --- */ + + if (r3d_render_has_instances(group)) { + R3D_SHADER_SET_INT_SELECT(scene.forward, shader, uInstancing, true); + r3d_render_draw_instanced(call); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.forward, shader, uInstancing, false); + r3d_render_draw(call); + } +} + +void raster_unlit(const r3d_render_call_t* call) +{ + assert(call->type == R3D_RENDER_CALL_MESH); //< Paranoid assert, should be fine + + const r3d_render_group_t* group = r3d_render_get_call_group(call); + const R3D_Material* material = &call->mesh.material; + const R3D_Mesh* mesh = &call->mesh.instance; + + /* --- Use shader --- */ + + R3D_SurfaceShader* shader = call->mesh.material.shader; + R3D_SHADER_USE_SELECT(scene.unlit, shader); + + /* --- Send matrices --- */ + + Matrix matNormal = r3d_matrix_normal(&group->transform); + + R3D_SHADER_SET_MAT4_SELECT(scene.unlit, shader, uMatModel, group->transform); + R3D_SHADER_SET_MAT4_SELECT(scene.unlit, shader, uMatNormal, matNormal); + + /* --- Send skinning related data --- */ + + if (group->skinTexture > 0) { + R3D_SHADER_BIND_SAMPLER_SELECT(scene.unlit, shader, uBoneMatricesTex, group->skinTexture); + R3D_SHADER_SET_INT_SELECT(scene.unlit, shader, uSkinning, true); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.unlit, shader, uSkinning, false); + } + + /* --- Send billboard related data --- */ + + R3D_SHADER_SET_INT_SELECT(scene.unlit, shader, uBillboard, material->billboardMode); + + /* --- Set misc material values --- */ + + R3D_SHADER_SET_FLOAT_SELECT(scene.unlit, shader, uAlphaCutoff, material->alphaCutoff); + + /* --- Set texcoord offset/scale --- */ + + R3D_SHADER_SET_VEC2_SELECT(scene.unlit, shader, uTexCoordOffset, material->uvOffset); + R3D_SHADER_SET_VEC2_SELECT(scene.unlit, shader, uTexCoordScale, material->uvScale); + + /* --- Set color material maps --- */ + + R3D_SHADER_SET_COL4_SELECT(scene.unlit, shader, uAlbedoColor, R3D.colorSpace, material->albedo.color); + + /* --- Bind active texture maps --- */ + + R3D_SHADER_BIND_SAMPLER_SELECT(scene.unlit, shader, uAlbedoMap, R3D_TEXTURE_SELECT(material->albedo.texture.id, WHITE)); + + /* --- Applying material parameters that are independent of shaders --- */ + + r3d_driver_set_depth_state(material->depth); + r3d_driver_set_stencil_state(material->stencil); + r3d_driver_set_blend_mode(material->blendMode, material->transparencyMode); + r3d_driver_set_cull_mode(material->cullMode); + + /* --- Rendering the object corresponding to the draw call --- */ + + if (r3d_render_has_instances(group)) { + R3D_SHADER_SET_INT_SELECT(scene.unlit, shader, uInstancing, true); + r3d_render_draw_instanced(call); + } + else { + R3D_SHADER_SET_INT_SELECT(scene.unlit, shader, uInstancing, false); + r3d_render_draw(call); + } +} + +void pass_scene_shadow(void) +{ + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_enable(GL_DEPTH_TEST); + + r3d_driver_set_depth_func(GL_LEQUAL); + r3d_driver_set_depth_mask(GL_TRUE); + + #define COND ( \ + (call->mesh.instance.shadowCastMode != R3D_SHADOW_CAST_DISABLED) && \ + IS_MESH_VISIBLE(call->mesh.instance, light->casterMask) \ + ) + + R3D_LIGHT_FOR_EACH_VISIBLE(light) + { + if (!r3d_light_shadow_should_be_updated(light, true)) { + continue; + } + + if (light->type == R3D_LIGHT_OMNI) { + for (int iFace = 0; iFace < 6; iFace++) { + r3d_light_shadow_bind_fbo(light->type, light->shadowLayer, iFace); + glClear(GL_DEPTH_BUFFER_BIT); + + const R3D_Frustum* frustum = &light->frustum[iFace]; + r3d_render_cull_groups(frustum); + + R3D_RENDER_FOR_EACH(call, COND, frustum, R3D_RENDER_PACKLIST_SHADOW) { + if (r3d_render_should_cast_shadow(call)) { + raster_depth_cube(call, &light->viewProj[iFace], light); + } + } + } + } + else { + r3d_light_shadow_bind_fbo(light->type, light->shadowLayer, 0); + glClear(GL_DEPTH_BUFFER_BIT); + + const R3D_Frustum* frustum = &light->frustum[0]; + r3d_render_cull_groups(frustum); + + R3D_RENDER_FOR_EACH(call, COND, frustum, R3D_RENDER_PACKLIST_SHADOW) { + if (r3d_render_should_cast_shadow(call)) { + raster_depth(call, &light->viewProj[0], light); + } + } + } + } + + #undef COND +} + +void pass_scene_probes(void) +{ + const R3D_EnvBackground* bg = &R3D.environment.background; + const R3D_EnvFog* fog = &R3D.environment.fog; + + R3D_ENV_PROBE_FOR_EACH_VISIBLE(probe) + { + if (!r3d_env_probe_should_be_updated(probe, true)) { + continue; + } + + for (int iFace = 0; iFace < 6; iFace++) + { + /* --- Generates the list of visible groups for the current face of the capture --- */ + + const R3D_Frustum* frustum = &probe->frustum[iFace]; + r3d_render_cull_groups(frustum); + + /* --- Render scene --- */ + + r3d_driver_enable(GL_STENCIL_TEST); + r3d_driver_enable(GL_DEPTH_TEST); + r3d_driver_enable(GL_BLEND); + + r3d_driver_set_depth_mask(GL_TRUE); + + r3d_env_capture_bind_fbo(iFace, 0); + glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + R3D_RENDER_FOR_EACH(call, true, frustum, R3D_RENDER_PACKLIST_PROBE) { + if (call->mesh.material.unlit) { + raster_probe_unlit(call, probe, iFace); + } + else { + upload_light_array_block_for_mesh(call, probe->shadows); + raster_probe_forward(call, probe, iFace); + } + } + + r3d_driver_set_depth_offset(0.0f, 0.0f); + r3d_driver_set_depth_range(0.0f, 1.0f); + + /* --- Render background --- */ + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_CULL_FACE); + r3d_driver_disable(GL_BLEND); + + r3d_driver_set_depth_func(GL_LEQUAL); + r3d_driver_set_depth_mask(GL_FALSE); + + if (bg->sky.texture != 0) { + R3D_SHADER_USE(scene.skybox); + float lod = (float)r3d_get_mip_levels_1d(bg->sky.size); + R3D_SHADER_BIND_SAMPLER(scene.skybox, uSkyMap, bg->sky.texture); + R3D_SHADER_SET_FLOAT(scene.skybox, uEnergy, bg->energy); + R3D_SHADER_SET_FLOAT(scene.skybox, uLod, bg->skyBlur * lod); + R3D_SHADER_SET_VEC4(scene.skybox, uRotation, bg->rotation); + R3D_SHADER_SET_MAT4(scene.skybox, uMatInvView, probe->invView[iFace]); + R3D_SHADER_SET_MAT4(scene.skybox, uMatInvProj, probe->invProj); + } + else { + Vector3 bgColor = r3d_color_to_linear_scaled_vec3(bg->color, R3D.colorSpace, bg->energy); + if (fog->mode != R3D_FOG_DISABLED) { + Vector3 fogColor = r3d_color_to_linear_vec3(fog->color, R3D.colorSpace); + bgColor = Vector3Lerp(bgColor, fogColor, fog->skyAffect); + } + R3D_SHADER_USE(scene.background); + R3D_SHADER_SET_VEC4(scene.background, uColor, (Vector4) {bgColor.x, bgColor.y, bgColor.z, 1.0f}); + } + + R3D_RENDER_SCREEN(); + } + + /* --- Generate irradiance and prefilter maps --- */ + + r3d_env_capture_gen_mipmaps(); + + if (probe->irradiance >= 0) { + r3d_pass_prepare_irradiance(probe->irradiance, r3d_env_capture_get(), R3D_PROBE_CAPTURE_SIZE); + } + + if (probe->prefilter >= 0) { + r3d_pass_prepare_prefilter(probe->prefilter, r3d_env_capture_get(), R3D_PROBE_CAPTURE_SIZE); + } + + r3d_target_invalidate_cache(); //< The IBL gen functions bind framebuffers; resetting them prevents any problems + } +} + +void pass_scene_geometry(void) +{ + R3D_TARGET_BIND(true, R3D_TARGET_GBUFFER); + + r3d_driver_enable(GL_STENCIL_TEST); + r3d_driver_enable(GL_DEPTH_TEST); + r3d_driver_disable(GL_BLEND); + + r3d_driver_set_depth_mask(GL_TRUE); + + const R3D_Frustum* frustum = &R3D.viewState.frustum; + R3D_RENDER_FOR_EACH(call, IS_MESH_VISIBLE_CAMERA(call->mesh.instance), frustum, R3D_RENDER_LIST_OPAQUE_INST, R3D_RENDER_LIST_OPAQUE) { + if (!call->mesh.material.unlit) { + raster_geometry(call, false); + } + } + + r3d_driver_set_depth_offset(0.0f, 0.0f); + r3d_driver_set_depth_range(0.0f, 1.0f); +} + +void pass_scene_prepass(void) +{ + /* --- First render only depth --- */ + + r3d_target_bind(NULL, 0, 0, true); + + r3d_driver_enable(GL_STENCIL_TEST); + r3d_driver_enable(GL_DEPTH_TEST); + + r3d_driver_set_depth_mask(GL_TRUE); + + const R3D_Frustum* frustum = &R3D.viewState.frustum; + R3D_RENDER_FOR_EACH(call, IS_MESH_VISIBLE_CAMERA(call->mesh.instance), frustum, R3D_RENDER_LIST_TRANSPARENT_INST, R3D_RENDER_LIST_TRANSPARENT) { + if (r3d_render_is_prepass(call)) { + raster_depth(call, &R3D.viewState.viewProj, NULL); + } + } + + /* --- Render opaque only with GL_EQUAL --- */ + + // NOTE: The transparent part will be rendered in forward + R3D_TARGET_BIND(true, R3D_TARGET_GBUFFER); + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_BLEND); + + r3d_driver_set_depth_func(GL_EQUAL); + r3d_driver_set_depth_mask(GL_FALSE); + + R3D_RENDER_FOR_EACH(call, IS_MESH_VISIBLE_CAMERA(call->mesh.instance), frustum, R3D_RENDER_LIST_TRANSPARENT_INST, R3D_RENDER_LIST_TRANSPARENT) { + if (r3d_render_is_prepass(call)) { + raster_geometry(call, true); + } + } + + /* --- Reset undesired states --- */ + + r3d_driver_set_depth_offset(0.0f, 0.0f); + r3d_driver_set_depth_range(0.0f, 1.0f); +} + +void pass_scene_decals(void) +{ + R3D_TARGET_BIND(false, R3D_TARGET_DECAL); + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_DEPTH_TEST); + r3d_driver_enable(GL_CULL_FACE); + r3d_driver_enable(GL_BLEND); + + r3d_driver_set_cull_face(GL_FRONT); // Only render back faces to avoid clipping issues + + // FIXME: The decal shader uses the alpha channel of the ORM attachment as a blend factor, + // but this channel now stores the material specular (F0) written during the geometry + // pass. We mask alpha writes to preserve the underlying specular, at the cost of + // making orm.specular ineffective for decals. + glColorMaski(2, GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + + const R3D_Frustum* frustum = &R3D.viewState.frustum; + R3D_RENDER_FOR_EACH(call, true, frustum, R3D_RENDER_LIST_DECAL_INST, R3D_RENDER_LIST_DECAL) { + raster_decal(call); + } + + glColorMaski(2, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +void pass_prepare_depth_pyramid(void) +{ + int numLevels = r3d_target_get_num_levels(R3D_TARGET_DEPTH); + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_DEPTH_TEST); + r3d_driver_disable(GL_CULL_FACE); + r3d_driver_disable(GL_BLEND); + + R3D_SHADER_USE(prepare.depthPyramid); + + for (int iDst = 1; iDst < numLevels; iDst++) { + R3D_TARGET_BIND_LEVELS(R3D_TARGET_LEVEL_LIST(iDst, iDst-1), R3D_TARGET_DEPTH, R3D_TARGET_SELECTOR); + R3D_SHADER_BIND_SAMPLER(prepare.depthPyramid, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, iDst - 1)); + R3D_RENDER_SCREEN(); + } +} + +r3d_target_t pass_prepare_ssao(void) +{ + /* --- Setup OpenGL pipeline --- */ + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_DEPTH_TEST); + r3d_driver_disable(GL_CULL_FACE); + r3d_driver_disable(GL_BLEND); + + /* --- Downsample G-Buffer --- */ + + R3D_TARGET_BIND_LEVEL(1, R3D_TARGET_NORMAL); + R3D_SHADER_USE(prepare.ssaoInDown); + + R3D_SHADER_BIND_SAMPLER(prepare.ssaoInDown, uSelectorTex, r3d_target_get_level(R3D_TARGET_SELECTOR, 0)); + R3D_SHADER_BIND_SAMPLER(prepare.ssaoInDown, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 0)); + + R3D_RENDER_SCREEN(); + + /* --- Calculate SSAO --- */ + + R3D_TARGET_BIND(false, R3D_TARGET_SSAO_0); + R3D_SHADER_USE(prepare.ssao); + + R3D_SHADER_SET_INT(prepare.ssao, uSampleCount, R3D.environment.ssao.sampleCount); + R3D_SHADER_SET_FLOAT(prepare.ssao, uRadius, R3D.environment.ssao.radius); + R3D_SHADER_SET_FLOAT(prepare.ssao, uBias, R3D.environment.ssao.bias); + R3D_SHADER_SET_FLOAT(prepare.ssao, uIntensity, R3D.environment.ssao.intensity); + + int wSsao = 0, hSsao = 0; + r3d_target_get_resolution(&wSsao, &hSsao, R3D_TARGET_SSAO_0, 0); + float maxScreenRadius = (float)MIN(wSsao, hSsao) * R3D.environment.ssao.maxRadius; + R3D_SHADER_SET_FLOAT(prepare.ssao, uMaxSSRadius, maxScreenRadius); + + R3D_SHADER_BIND_SAMPLER(prepare.ssao, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.ssao, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 1)); + + R3D_RENDER_SCREEN(); + + /* --- Denoise SSAO --- */ + + R3D_TARGET_BIND(false, R3D_TARGET_SSAO_1); + R3D_SHADER_USE(prepare.denoiserSparse); + + R3D_SHADER_BIND_SAMPLER(prepare.denoiserSparse, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.denoiserSparse, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.denoiserSparse, uSourceTex, r3d_target_get(R3D_TARGET_SSAO_0)); + + const float radius = 4.0f; + + R3D_SHADER_SET_FLOAT(prepare.denoiserSparse, uNormalSharpness, 20.0f); + R3D_SHADER_SET_FLOAT(prepare.denoiserSparse, uDepthSharpness, 100.0f); + R3D_SHADER_SET_FLOAT(prepare.denoiserSparse, uInvBlurRadius2, 1.0f / (radius * radius)); + R3D_SHADER_SET_FLOAT(prepare.denoiserSparse, uBlurRadius, radius); + + R3D_RENDER_SCREEN(); + + return R3D_TARGET_SSAO_1; +} + +r3d_target_t pass_prepare_ssil(void) +{ + /* --- Setup OpenGL pipeline --- */ + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_DEPTH_TEST); + r3d_driver_disable(GL_CULL_FACE); + r3d_driver_disable(GL_BLEND); + + /* --- Downsample G-Buffer --- */ + + R3D_TARGET_BIND_LEVEL(1, R3D_TARGET_DIFFUSE, R3D_TARGET_NORMAL); + R3D_SHADER_USE(prepare.ssilInDown); + + R3D_SHADER_BIND_SAMPLER(prepare.ssilInDown, uSelectorTex, r3d_target_get_level(R3D_TARGET_SELECTOR, 0)); + R3D_SHADER_BIND_SAMPLER(prepare.ssilInDown, uDiffuseTex, r3d_target_get_level(R3D_TARGET_DIFFUSE, 0)); + R3D_SHADER_BIND_SAMPLER(prepare.ssilInDown, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 0)); + + R3D_RENDER_SCREEN(); + + /* --- Calculate SSIL --- */ + + R3D_TARGET_BIND(false, R3D_TARGET_SSIL_0); + R3D_SHADER_USE(prepare.ssil); + + R3D_SHADER_SET_INT(prepare.ssil, uSampleCount, R3D.environment.ssil.sampleCount); + R3D_SHADER_SET_FLOAT(prepare.ssil, uRadius, R3D.environment.ssil.radius); + R3D_SHADER_SET_FLOAT(prepare.ssil, uBias, R3D.environment.ssil.bias); + R3D_SHADER_SET_FLOAT(prepare.ssil, uAoIntensity, R3D.environment.ssil.aoIntensity); + + int wSsil = 0, hSsil = 0; + r3d_target_get_resolution(&wSsil, &hSsil, R3D_TARGET_SSIL_0, 0); + float maxScreenRadius = (float)MIN(wSsil, hSsil) * R3D.environment.ssil.maxRadius; + R3D_SHADER_SET_FLOAT(prepare.ssil, uMaxSSRadius, maxScreenRadius); + + R3D_SHADER_BIND_SAMPLER(prepare.ssil, uDiffuseTex, r3d_target_get_level(R3D_TARGET_DIFFUSE, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.ssil, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.ssil, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 1)); + + R3D_RENDER_SCREEN(); + + /* --- Denoise SSIL --- */ + + r3d_target_t src = R3D_TARGET_SSIL_0; + r3d_target_t dst = R3D_TARGET_SSIL_1; + + R3D_SHADER_USE(prepare.denoiserSparse); + + R3D_SHADER_BIND_SAMPLER(prepare.denoiserSparse, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.denoiserSparse, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 1)); + + R3D_SHADER_SET_FLOAT(prepare.denoiserSparse, uNormalSharpness, 20.0f); + R3D_SHADER_SET_FLOAT(prepare.denoiserSparse, uDepthSharpness, 100.0f); + + float radius = 16.0f; + for (int i = 0; i < 3; i++, radius *= 0.5f) { + R3D_TARGET_BIND(false, dst); + R3D_SHADER_SET_FLOAT(prepare.denoiserSparse, uBlurRadius, radius); + R3D_SHADER_SET_FLOAT(prepare.denoiserSparse, uInvBlurRadius2, 1.0f / (radius * radius)); + R3D_SHADER_BIND_SAMPLER(prepare.denoiserSparse, uSourceTex, r3d_target_get(src)); + R3D_RENDER_SCREEN(); + + SWAP(r3d_target_t, src, dst); + } + + return src; +} + +r3d_target_t pass_prepare_ssgi(void) +{ + /* --- Setup OpenGL pipeline --- */ + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_DEPTH_TEST); + r3d_driver_disable(GL_CULL_FACE); + r3d_driver_disable(GL_BLEND); + + /* --- Downsample G-Buffer --- */ + + R3D_TARGET_BIND_LEVEL(1, R3D_TARGET_DIFFUSE, R3D_TARGET_NORMAL); + R3D_SHADER_USE(prepare.ssgiInDown); + + R3D_SHADER_BIND_SAMPLER(prepare.ssgiInDown, uSelectorTex, r3d_target_get_level(R3D_TARGET_SELECTOR, 0)); + R3D_SHADER_BIND_SAMPLER(prepare.ssgiInDown, uDiffuseTex, r3d_target_get_level(R3D_TARGET_DIFFUSE, 0)); + R3D_SHADER_BIND_SAMPLER(prepare.ssgiInDown, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 0)); + + R3D_RENDER_SCREEN(); + + /* --- Calculate SSGI (RAW) --- */ + + R3D_TARGET_BIND_LEVEL(0, R3D_TARGET_SSGI_0); + R3D_SHADER_USE(prepare.ssgi); + + R3D_SHADER_BIND_SAMPLER(prepare.ssgi, uDiffuseTex, r3d_target_get_level(R3D_TARGET_DIFFUSE, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.ssgi, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.ssgi, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 1)); + + R3D_SHADER_SET_INT(prepare.ssgi, uSliceCount, R3D.environment.ssgi.sliceCount); + R3D_SHADER_SET_FLOAT(prepare.ssgi, uEdgeFade, R3D.environment.ssgi.edgeFade); + R3D_SHADER_SET_FLOAT(prepare.ssgi, uDistanceFalloff, R3D.environment.ssgi.distanceFalloff); + R3D_SHADER_SET_FLOAT(prepare.ssgi, uNormalRejection, R3D.environment.ssgi.normalRejection); + + R3D_RENDER_SCREEN(); + + /* + A-trous step schedule (largest -> smallest). + + We use a fixed pyramid: {16, 8, 4, 2, 1}. + When fewer iterations are requested we simply truncate the list. + + The largest steps are what stabilize the filter in motion. + The small ones mostly refine the result and hide the pattern left + by the large kernels. + + If we derived the pyramid from the iteration count (e.g. 16,8,4,2,1 + for 5 steps), the max radius would shrink and the result becomes + noticeably less stable when the camera moves. + + Keeping the same large radii and only dropping the final refinement + passes preserves the spatial stability of the 5-step filter while + allowing cheaper configurations. + + Examples: + 5 steps : 16 8 4 2 1 + 4 steps : 16 8 4 2 + 3 steps : 16 8 4 + */ + + r3d_target_t src = R3D_TARGET_SSGI_0; + r3d_target_t dst = R3D_TARGET_SSGI_1; + + int steps = R3D.environment.ssgi.denoiseSteps; + + if (steps > 0) + { + R3D_SHADER_USE(prepare.denoiserAtrous); + + R3D_SHADER_BIND_SAMPLER(prepare.denoiserAtrous, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.denoiserAtrous, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 1)); + + R3D_SHADER_SET_FLOAT(prepare.denoiserAtrous, uNormalSharpness, 20.0f); + R3D_SHADER_SET_FLOAT(prepare.denoiserAtrous, uDepthSharpness, 100.0f); + + int stepWidth[] = {16, 8, 4, 2, 1}; + steps = MIN(steps, ARRAY_SIZE(stepWidth)); + + for (int i = 0; i < steps; i++) + { + float invStepWidth2 = 1.0f / (stepWidth[i]*stepWidth[i]); + + R3D_TARGET_BIND(false, dst); + R3D_SHADER_BIND_SAMPLER(prepare.denoiserAtrous, uSourceTex, r3d_target_get(src)); + R3D_SHADER_SET_FLOAT(prepare.denoiserAtrous, uInvStepWidth2, invStepWidth2); + R3D_SHADER_SET_INT(prepare.denoiserAtrous, uStepWidth, stepWidth[i]); + R3D_RENDER_SCREEN(); + + SWAP(r3d_target_t, src, dst); + } + } + + return src; +} + +r3d_target_t pass_prepare_ssr(void) +{ + /* --- Setup OpenGL pipeline --- */ + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_DEPTH_TEST); + r3d_driver_disable(GL_CULL_FACE); + r3d_driver_disable(GL_BLEND); + + /* --- Downsample G-Buffer --- */ + + R3D_TARGET_BIND_LEVEL(1, R3D_TARGET_DIFFUSE, R3D_TARGET_SPECULAR, R3D_TARGET_NORMAL); + R3D_SHADER_USE(prepare.ssrInDown); + + R3D_SHADER_BIND_SAMPLER(prepare.ssrInDown, uSelectorTex, r3d_target_get_level(R3D_TARGET_SELECTOR, 0)); + R3D_SHADER_BIND_SAMPLER(prepare.ssrInDown, uDiffuseTex, r3d_target_get_level(R3D_TARGET_DIFFUSE, 0)); + R3D_SHADER_BIND_SAMPLER(prepare.ssrInDown, uSpecularTex, r3d_target_get_level(R3D_TARGET_SPECULAR, 0)); + R3D_SHADER_BIND_SAMPLER(prepare.ssrInDown, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 0)); + + R3D_RENDER_SCREEN(); + + /* --- Calculate SSR --- */ + + R3D_TARGET_BIND_LEVEL(0, R3D_TARGET_SSR); + R3D_SHADER_USE(prepare.ssr); + + R3D_SHADER_BIND_SAMPLER(prepare.ssr, uDiffuseTex, r3d_target_get_level(R3D_TARGET_DIFFUSE, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.ssr, uSpecularTex, r3d_target_get_level(R3D_TARGET_SPECULAR, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.ssr, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 1)); + R3D_SHADER_BIND_SAMPLER(prepare.ssr, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 1)); + + R3D_SHADER_SET_INT(prepare.ssr, uMaxRaySteps, R3D.environment.ssr.maxRaySteps); + R3D_SHADER_SET_INT(prepare.ssr, uBinarySteps, R3D.environment.ssr.binarySteps); + R3D_SHADER_SET_FLOAT(prepare.ssr, uStepSize, R3D.environment.ssr.stepSize); + R3D_SHADER_SET_FLOAT(prepare.ssr, uThickness, R3D.environment.ssr.thickness); + R3D_SHADER_SET_FLOAT(prepare.ssr, uMaxDistance, R3D.environment.ssr.maxDistance); + R3D_SHADER_SET_FLOAT(prepare.ssr, uEdgeFade, R3D.environment.ssr.edgeFade); + + R3D_RENDER_SCREEN(); + + /* --- Downsample SSR --- */ + + int numLevels = r3d_target_get_num_levels(R3D_TARGET_SSR); + r3d_target_set_read_levels(R3D_TARGET_SSR, 0, numLevels - 1); + + R3D_SHADER_USE(prepare.blurDown); + R3D_SHADER_BIND_SAMPLER(prepare.blurDown, uSourceTex, r3d_target_get(R3D_TARGET_SSR)); + + for (int iDst = 1; iDst < numLevels; iDst++) { + r3d_target_set_write_level(0, iDst); + r3d_target_set_viewport(R3D_TARGET_SSR, iDst); + R3D_SHADER_SET_INT(prepare.blurDown, uSourceLod, iDst - 1); + R3D_RENDER_SCREEN(); + } + + /* --- Upsample only once for each level below zero --- */ + + R3D_SHADER_USE(prepare.blurUp); + R3D_SHADER_BIND_SAMPLER(prepare.blurUp, uSourceTex, r3d_target_get(R3D_TARGET_SSR)); + + for (int iDst = 1; iDst < numLevels - 1; iDst++) { + r3d_target_set_write_level(0, iDst); + r3d_target_set_viewport(R3D_TARGET_SSR, iDst); + R3D_SHADER_SET_INT(prepare.blurUp, uSourceLod, iDst + 1); + R3D_RENDER_SCREEN(); + } + + return R3D_TARGET_SSR; +} + +void pass_deferred_lights(void) +{ + /* --- Setup OpenGL pipeline --- */ + + R3D_TARGET_BIND(true, R3D_TARGET_LIGHTING); + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_CULL_FACE); + + r3d_driver_enable(GL_SCISSOR_TEST); + r3d_driver_enable(GL_DEPTH_TEST); + r3d_driver_enable(GL_BLEND); + + // Set additive blending to accumulate light contributions + r3d_driver_set_blend_func(GL_FUNC_ADD, GL_ONE, GL_ONE); + r3d_driver_set_depth_func(GL_GREATER); + r3d_driver_set_depth_mask(GL_FALSE); + + /* --- Enable shader and setup constant stuff --- */ + + R3D_SHADER_USE(deferred.lighting); + + R3D_SHADER_BIND_SAMPLER(deferred.lighting, uAlbedoTex, r3d_target_get_level(R3D_TARGET_ALBEDO, 0)); + R3D_SHADER_BIND_SAMPLER(deferred.lighting, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 0)); + R3D_SHADER_BIND_SAMPLER(deferred.lighting, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 0)); + R3D_SHADER_BIND_SAMPLER(deferred.lighting, uOrmTex, r3d_target_get_level(R3D_TARGET_ORM, 0)); + + /* --- Calculate lighting contributions --- */ + + R3D_LIGHT_FOR_EACH_VISIBLE(light) + { + // Set scissors rect + r3d_rect_t dst = {0, 0, R3D_TARGET_SIZE_W, R3D_TARGET_SIZE_H}; + if (light->type != R3D_LIGHT_DIR) { + dst = r3d_light_get_screen_rect(light, &R3D.viewState.viewProj, dst.w, dst.h); + if (memcmp(&dst, &(r3d_rect_t){0}, sizeof(r3d_rect_t)) == 0) continue; + } + glScissor(dst.x, dst.y, dst.w, dst.h); + + // Send light data to the GPU + r3d_shader_block_light_t data = { + .viewProj = MatrixTranspose(light->viewProj[0]), + .color = light->color, + .position = light->position, + .direction = light->direction, + .specular = light->specular, + .energy = light->energy, + .range = light->range, + .near = light->near, + .far = light->far, + .attenuation = light->attenuation, + .innerCutOff = light->innerCutOff, + .outerCutOff = light->outerCutOff, + .shadowSoftness = light->shadowSoftness, + .shadowOpacity = light->shadowOpacity, + .shadowDepthBias = light->shadowDepthBias, + .shadowSlopeBias = light->shadowSlopeBias, + .shadowLayer = light->shadowLayer, + .type = light->type, + }; + r3d_shader_set_uniform_block(R3D_SHADER_BLOCK_LIGHT, &data); + + // Accumulate this light! + R3D_RENDER_SCREEN(); + } + + /* --- Reset undesired states --- */ + + r3d_driver_disable(GL_SCISSOR_TEST); +} + +void pass_deferred_ambient(r3d_target_t ssaoSource, r3d_target_t ssilSource, r3d_target_t ssgiSource) +{ + /* --- Setup OpenGL pipeline --- */ + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_CULL_FACE); + + r3d_driver_enable(GL_DEPTH_TEST); + r3d_driver_enable(GL_BLEND); + + // Set additive blending to accumulate light contributions + r3d_driver_set_blend_func(GL_FUNC_ADD, GL_ONE, GL_ONE); + r3d_driver_set_depth_func(GL_GREATER); + r3d_driver_set_depth_mask(GL_FALSE); + + /* --- Calculation and composition of ambient/indirect lighting --- */ + + R3D_TARGET_BIND(true, R3D_TARGET_LIGHTING); + R3D_SHADER_USE(deferred.ambient); + + R3D_SHADER_BIND_SAMPLER(deferred.ambient, uAlbedoTex, r3d_target_get_level(R3D_TARGET_ALBEDO, 0)); + R3D_SHADER_BIND_SAMPLER(deferred.ambient, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 0)); + R3D_SHADER_BIND_SAMPLER(deferred.ambient, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 0)); + R3D_SHADER_BIND_SAMPLER(deferred.ambient, uSsaoTex, R3D_TEXTURE_SELECT(r3d_target_get_or_null(ssaoSource), WHITE)); + R3D_SHADER_BIND_SAMPLER(deferred.ambient, uSsilTex, R3D_TEXTURE_SELECT(r3d_target_get_or_null(ssilSource), BLACK)); + R3D_SHADER_BIND_SAMPLER(deferred.ambient, uSsgiTex, R3D_TEXTURE_SELECT(r3d_target_get_or_null(ssgiSource), BLACK)); + R3D_SHADER_BIND_SAMPLER(deferred.ambient, uOrmTex, r3d_target_get_level(R3D_TARGET_ORM, 0)); + + R3D_SHADER_SET_FLOAT(deferred.ambient, uSsaoPower, R3D.environment.ssao.power); + R3D_SHADER_SET_FLOAT(deferred.ambient, uSsilAoPower, R3D.environment.ssil.aoPower); + R3D_SHADER_SET_FLOAT(deferred.ambient, uSsilIntensity, R3D.environment.ssil.giIntensity); + R3D_SHADER_SET_FLOAT(deferred.ambient, uSsgiIntensity, R3D.environment.ssgi.intensity); + + R3D_RENDER_SCREEN(); +} + +void pass_deferred_compose(r3d_target_t sceneTarget, r3d_target_t ssrSource) +{ + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_CULL_FACE); + r3d_driver_disable(GL_BLEND); + + r3d_driver_enable(GL_DEPTH_TEST); + r3d_driver_set_depth_func(GL_GREATER); + r3d_driver_set_depth_mask(GL_FALSE); + + R3D_TARGET_BIND(true, sceneTarget); + R3D_SHADER_USE(deferred.compose); + + R3D_SHADER_BIND_SAMPLER(deferred.compose, uAlbedoTex, r3d_target_get_level(R3D_TARGET_ALBEDO, 0)); + R3D_SHADER_BIND_SAMPLER(deferred.compose, uDiffuseTex, r3d_target_get_level(R3D_TARGET_DIFFUSE, 0)); + R3D_SHADER_BIND_SAMPLER(deferred.compose, uSpecularTex, r3d_target_get_level(R3D_TARGET_SPECULAR, 0)); + R3D_SHADER_BIND_SAMPLER(deferred.compose, uOrmTex, r3d_target_get_level(R3D_TARGET_ORM, 0)); + R3D_SHADER_BIND_SAMPLER(deferred.compose, uSsrTex, R3D_TEXTURE_SELECT(r3d_target_get_or_null(ssrSource), BLANK)); + + R3D_SHADER_SET_FLOAT(deferred.compose, uSsrNumLevels, (float)r3d_target_get_num_levels(R3D_TARGET_SSR)); + + R3D_RENDER_SCREEN(); +} + +void pass_deferred_fog(r3d_target_t sceneTarget) +{ + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_CULL_FACE); + + r3d_driver_enable(GL_DEPTH_TEST); + r3d_driver_set_depth_func(GL_GREATER); + r3d_driver_set_depth_mask(GL_FALSE); + + r3d_driver_enable(GL_BLEND); + r3d_driver_set_blend_func(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + R3D_TARGET_BIND(false, sceneTarget); + R3D_SHADER_USE(deferred.fog); + + R3D_SHADER_BIND_SAMPLER(deferred.fog, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 0)); + R3D_RENDER_SCREEN(); +} + +void pass_scene_forward(r3d_target_t sceneTarget) +{ + R3D_TARGET_BIND(true, sceneTarget); + + r3d_driver_enable(GL_STENCIL_TEST); + r3d_driver_enable(GL_DEPTH_TEST); + r3d_driver_enable(GL_BLEND); + + /* --- Render unlit opaque --- */ + + r3d_driver_set_depth_mask(GL_TRUE); + + const R3D_Frustum* frustum = &R3D.viewState.frustum; + R3D_RENDER_FOR_EACH(call, IS_MESH_VISIBLE_CAMERA(call->mesh.instance), frustum, R3D_RENDER_LIST_OPAQUE_INST, R3D_RENDER_LIST_OPAQUE) { + if (call->mesh.material.unlit) { + raster_unlit(call); + } + } + + /* --- Render all transparent in order - (prepass/alpha treated as same) --- */ + + r3d_driver_set_depth_mask(GL_FALSE); + + R3D_RENDER_FOR_EACH(call, IS_MESH_VISIBLE_CAMERA(call->mesh.instance), frustum, R3D_RENDER_LIST_TRANSPARENT_INST, R3D_RENDER_LIST_TRANSPARENT) { + if (call->mesh.material.unlit) { + raster_unlit(call); + } + else { + upload_light_array_block_for_mesh(call, true); + raster_forward(call); + } + } + + /* --- Reset undesired states --- */ + + r3d_driver_set_depth_offset(0.0f, 0.0f); + r3d_driver_set_depth_range(0.0f, 1.0f); +} + +void pass_scene_background(r3d_target_t sceneTarget) +{ + R3D_TARGET_BIND(true, sceneTarget); + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_CULL_FACE); + r3d_driver_disable(GL_BLEND); + + r3d_driver_enable(GL_DEPTH_TEST); + r3d_driver_set_depth_func(GL_LEQUAL); + r3d_driver_set_depth_mask(GL_FALSE); + + const R3D_EnvBackground* bg = &R3D.environment.background; + const R3D_EnvFog* fog = &R3D.environment.fog; + + if (bg->sky.texture != 0) { + R3D_SHADER_USE(scene.skybox); + float lod = (float)r3d_get_mip_levels_1d(bg->sky.size); + R3D_SHADER_BIND_SAMPLER(scene.skybox, uSkyMap, bg->sky.texture); + R3D_SHADER_SET_FLOAT(scene.skybox, uEnergy, bg->energy); + R3D_SHADER_SET_FLOAT(scene.skybox, uLod, bg->skyBlur * lod); + R3D_SHADER_SET_VEC4(scene.skybox, uRotation, bg->rotation); + R3D_SHADER_SET_MAT4(scene.skybox, uMatInvView, R3D.viewState.invView); + R3D_SHADER_SET_MAT4(scene.skybox, uMatInvProj, R3D.viewState.invProj); + } + else { + Vector3 bgColor = r3d_color_to_linear_scaled_vec3(bg->color, R3D.colorSpace, bg->energy); + if (fog->mode != R3D_FOG_DISABLED) { + Vector3 fogColor = r3d_color_to_linear_vec3(fog->color, R3D.colorSpace); + bgColor = Vector3Lerp(bgColor, fogColor, fog->skyAffect); + } + R3D_SHADER_USE(scene.background); + R3D_SHADER_SET_VEC4(scene.background, uColor, (Vector4) {bgColor.x, bgColor.y, bgColor.z, 1.0f}); + } + + R3D_RENDER_SCREEN(); +} + +r3d_target_t pass_post_setup(r3d_target_t sceneTarget) +{ + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_DEPTH_TEST); + r3d_driver_disable(GL_CULL_FACE); + r3d_driver_disable(GL_BLEND); + + return r3d_target_swap_scene(sceneTarget); +} + +r3d_target_t pass_post_dof(r3d_target_t sceneTarget) +{ + /* --- Calculate CoC --- */ + + R3D_TARGET_BIND_LEVEL(0, R3D_TARGET_DOF_COC); + R3D_SHADER_USE(prepare.dofCoc); + + R3D_SHADER_BIND_SAMPLER(prepare.dofCoc, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 0)); + R3D_SHADER_SET_FLOAT(prepare.dofCoc, uFocusPoint, R3D.environment.dof.focusPoint); + R3D_SHADER_SET_FLOAT(prepare.dofCoc, uFocusScale, R3D.environment.dof.focusScale); + R3D_SHADER_SET_FLOAT(prepare.dofCoc, uNearScale, R3D.environment.dof.nearScale); + + R3D_RENDER_SCREEN(); + + /* --- Downsample CoC to half resolution --- */ + + R3D_TARGET_BIND(false, R3D_TARGET_DOF_0); + + R3D_SHADER_USE(prepare.dofDown); + R3D_SHADER_BIND_SAMPLER(prepare.dofDown, uSceneTex, r3d_target_get(r3d_target_swap_scene(sceneTarget))); + R3D_SHADER_BIND_SAMPLER(prepare.dofDown, uCoCTex, r3d_target_get(R3D_TARGET_DOF_COC)); + + R3D_RENDER_SCREEN(); + + /* --- Calculate DoF in half resolution --- */ + + R3D_TARGET_BIND(false, R3D_TARGET_DOF_1); + + R3D_SHADER_USE(prepare.dofBlur); + R3D_SHADER_BIND_SAMPLER(prepare.dofBlur, uSceneTex, r3d_target_get(R3D_TARGET_DOF_0)); + R3D_SHADER_BIND_SAMPLER(prepare.dofBlur, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 1)); + R3D_SHADER_SET_FLOAT(prepare.dofBlur, uMaxBlurSize, R3D.environment.dof.maxBlurSize * 0.5f); + + R3D_RENDER_SCREEN(); + + /* --- Compose DoF with the scene --- */ + + R3D_TARGET_BIND_AND_SWAP_SCENE(sceneTarget); + R3D_SHADER_USE(post.dof); + + R3D_SHADER_BIND_SAMPLER(post.dof, uSceneTex, r3d_target_get(sceneTarget)); + R3D_SHADER_BIND_SAMPLER(post.dof, uBlurTex, r3d_target_get(R3D_TARGET_DOF_1)); + + R3D_RENDER_SCREEN(); + + return sceneTarget; +} + +r3d_target_t pass_post_bloom(r3d_target_t sceneTarget) +{ + r3d_target_t sceneSource = r3d_target_swap_scene(sceneTarget); + GLuint sceneSourceID = r3d_target_get(sceneSource); + + int numLevels = r3d_target_get_num_levels(R3D_TARGET_BLOOM); + float txSrcW = 0, txSrcH = 0; + + R3D_TARGET_BIND(false, R3D_TARGET_BLOOM); + + /* --- Calculate bloom prefilter --- */ + + float threshold = R3D.environment.bloom.threshold; + float softThreshold = R3D.environment.bloom.softThreshold; + + float knee = threshold * softThreshold; + + Vector4 prefilter = { + prefilter.x = threshold, + prefilter.y = threshold - knee, + prefilter.z = 2.0f * knee, + prefilter.w = 0.25f / (knee + 0.00001f), + }; + + /* --- Adjust max mip count --- */ + + int maxLevel = (int)((float)numLevels * R3D.environment.bloom.levels + 0.5f); + if (maxLevel > numLevels) maxLevel = numLevels; + else if (maxLevel < 1) maxLevel = 1; + + /* --- Karis average for the first downsampling to half res --- */ + + R3D_SHADER_USE(prepare.bloomDown); + R3D_SHADER_BIND_SAMPLER(prepare.bloomDown, uTexture, sceneSourceID); + + r3d_target_get_texel_size(&txSrcW, &txSrcH, R3D_TARGET_SCENE_0, 0); + R3D_SHADER_SET_VEC2(prepare.bloomDown, uTexelSize, (Vector2) {txSrcW, txSrcH}); + R3D_SHADER_SET_VEC4(prepare.bloomDown, uPrefilter, prefilter); + R3D_SHADER_SET_INT(prepare.bloomDown, uDstLevel, 0); + + R3D_RENDER_SCREEN(); + + /* --- Bloom Downsampling --- */ + + // It's okay to sample the target here + // Given that we'll be sampling a different level from where we're writing + R3D_SHADER_BIND_SAMPLER(prepare.bloomDown, uTexture, r3d_target_get(R3D_TARGET_BLOOM)); + + for (int dstLevel = 1; dstLevel < maxLevel; dstLevel++) + { + r3d_target_set_viewport(R3D_TARGET_BLOOM, dstLevel); + r3d_target_set_write_level(0, dstLevel); + + r3d_target_get_texel_size(&txSrcW, &txSrcH, R3D_TARGET_BLOOM, dstLevel - 1); + R3D_SHADER_SET_VEC2(prepare.bloomDown, uTexelSize, (Vector2) {txSrcW, txSrcH}); + R3D_SHADER_SET_INT(prepare.bloomDown, uDstLevel, dstLevel); + + R3D_RENDER_SCREEN(); + } + + /* --- Bloom Upsampling --- */ + + R3D_SHADER_USE(prepare.bloomUp); + + r3d_driver_enable(GL_BLEND); + r3d_driver_set_blend_func(GL_FUNC_ADD, GL_ONE, GL_ONE); + + R3D_SHADER_BIND_SAMPLER(prepare.bloomUp, uTexture, r3d_target_get(R3D_TARGET_BLOOM)); + + for (int dstLevel = maxLevel - 2; dstLevel >= 0; dstLevel--) + { + r3d_target_set_viewport(R3D_TARGET_BLOOM, dstLevel); + r3d_target_set_write_level(0, dstLevel); + + r3d_target_get_texel_size(&txSrcW, &txSrcH, R3D_TARGET_BLOOM, dstLevel + 1); + R3D_SHADER_SET_FLOAT(prepare.bloomUp, uSrcLevel, (float)(dstLevel + 1)); + R3D_SHADER_SET_VEC2(prepare.bloomUp, uFilterRadius, (Vector2) { + R3D.environment.bloom.filterRadius * txSrcW, + R3D.environment.bloom.filterRadius * txSrcH + }); + + R3D_RENDER_SCREEN(); + } + + r3d_driver_disable(GL_BLEND); + + /* --- Apply bloom to the scene --- */ + + R3D_TARGET_BIND_AND_SWAP_SCENE(sceneTarget); + R3D_SHADER_USE(post.bloom); + + R3D_SHADER_BIND_SAMPLER(post.bloom, uSceneTex, sceneSourceID); + R3D_SHADER_BIND_SAMPLER(post.bloom, uBloomTex, r3d_target_get_all_levels(R3D_TARGET_BLOOM)); + + R3D_SHADER_SET_INT(post.bloom, uBloomMode, R3D.environment.bloom.mode); + R3D_SHADER_SET_FLOAT(post.bloom, uBloomIntensity, R3D.environment.bloom.intensity); + + R3D_RENDER_SCREEN(); + + return sceneTarget; +} + +r3d_target_t pass_post_auto_exposure(r3d_target_t sceneTarget) +{ + r3d_target_t sceneSource = r3d_target_swap_scene(sceneTarget); + GLuint sceneSourceID = r3d_target_get(sceneSource); + + /* --- Build log-luminance pyramid --- */ + + R3D_TARGET_BIND_LEVEL(0, R3D_TARGET_LUMINANCE); + + R3D_SHADER_USE(prepare.luminance); + R3D_SHADER_BIND_SAMPLER(prepare.luminance, uSourceTex, sceneSourceID); + R3D_RENDER_SCREEN(); + + r3d_target_gen_mipmap(R3D_TARGET_LUMINANCE); + + /* --- Update auto-exposure history on GPU --- */ + + const R3D_EnvAutoExposure *autoExposure = &R3D.environment.autoExposure; + + float minLogLum = R3D_LOG018 + autoExposure->minEV * R3D_LOG2; + float maxLogLum = R3D_LOG018 + autoExposure->maxEV * R3D_LOG2; + + float timeToBright = fmaxf(autoExposure->adaptationToBright, 1e-3f); + float timeToDark = fmaxf(autoExposure->adaptationToDark, 1e-3f); + + float speedUp = 1.0f / timeToBright; + float speedDown = 1.0f / timeToDark; + + float exposureCompLog = autoExposure->exposureCompensation * R3D_LOG2; + + static r3d_target_t EXPOSURE_DST = R3D_TARGET_EXPOSURE_0; + static r3d_target_t EXPOSURE_SRC = R3D_TARGET_EXPOSURE_1; + + if (!r3d_target_exists(EXPOSURE_SRC)) { + R3D_TARGET_CLEAR(false, EXPOSURE_SRC); + } + + R3D_TARGET_BIND(false, EXPOSURE_DST); + + R3D_SHADER_USE(prepare.exposureAdapt); + + R3D_SHADER_SET_FLOAT(prepare.exposureAdapt, uDeltaTime, GetFrameTime()); + R3D_SHADER_SET_FLOAT(prepare.exposureAdapt, uMinLogLum, minLogLum); + R3D_SHADER_SET_FLOAT(prepare.exposureAdapt, uMaxLogLum, maxLogLum); + R3D_SHADER_SET_FLOAT(prepare.exposureAdapt, uSpeedUp, speedUp); + R3D_SHADER_SET_FLOAT(prepare.exposureAdapt, uSpeedDown, speedDown); + R3D_SHADER_SET_FLOAT(prepare.exposureAdapt, uExposureCompLog, exposureCompLog); + + int lumNumLevels = r3d_target_get_num_levels(R3D_TARGET_LUMINANCE); + + R3D_SHADER_BIND_SAMPLER(prepare.exposureAdapt, uMeasuredLogLumTex, r3d_target_get_level(R3D_TARGET_LUMINANCE, lumNumLevels - 1)); + R3D_SHADER_BIND_SAMPLER(prepare.exposureAdapt, uPrevAutoExposureTex, r3d_target_get(EXPOSURE_SRC)); + + R3D_RENDER_SCREEN(); + + SWAP(r3d_target_t, EXPOSURE_DST, EXPOSURE_SRC); + + /* --- Apply exposure --- */ + + R3D_TARGET_BIND_AND_SWAP_SCENE(sceneTarget); + + R3D_SHADER_USE(post.autoExposure); + R3D_SHADER_BIND_SAMPLER(post.autoExposure, uSceneTex, sceneSourceID); + R3D_SHADER_BIND_SAMPLER(post.autoExposure, uExposureTex, r3d_target_get(EXPOSURE_SRC)); + R3D_RENDER_SCREEN(); + + return sceneTarget; +} + +r3d_target_t pass_post_screen(R3D_ScreenShaderStage stage, r3d_target_t sceneTarget) +{ + for (int i = 0; i < ARRAY_SIZE(R3D.screenShaders[stage]); i++) + { + R3D_ScreenShader* shader = R3D.screenShaders[stage][i]; + if (shader == NULL) continue; + + R3D_TARGET_BIND_AND_SWAP_SCENE(sceneTarget); + R3D_SHADER_USE_CUSTOM(R3D.screenShaders[stage][i], post.screen); + + R3D_SHADER_BIND_SAMPLER_CUSTOM(shader, post.screen, uSceneTex, r3d_target_get(sceneTarget)); + R3D_SHADER_BIND_SAMPLER_CUSTOM(shader, post.screen, uNormalTex, r3d_target_get_level(R3D_TARGET_NORMAL, 0)); + R3D_SHADER_BIND_SAMPLER_CUSTOM(shader, post.screen, uDepthTex, r3d_target_get_level(R3D_TARGET_DEPTH, 0)); + + R3D_RENDER_SCREEN(); + } + + return sceneTarget; +} + +r3d_target_t pass_post_output(r3d_target_t sceneTarget) +{ + R3D_TARGET_BIND_AND_SWAP_SCENE(sceneTarget); + R3D_SHADER_USE(post.output); + + R3D_SHADER_BIND_SAMPLER(post.output, uSceneTex, r3d_target_get(sceneTarget)); + + R3D_SHADER_SET_FLOAT(post.output, uTonemapExposure, R3D.environment.tonemap.exposure); + R3D_SHADER_SET_FLOAT(post.output, uTonemapWhite, R3D.environment.tonemap.white); + R3D_SHADER_SET_INT(post.output, uTonemapMode, R3D.environment.tonemap.mode); + R3D_SHADER_SET_FLOAT(post.output, uBrightness, R3D.environment.color.brightness); + R3D_SHADER_SET_FLOAT(post.output, uContrast, R3D.environment.color.contrast); + R3D_SHADER_SET_FLOAT(post.output, uSaturation, R3D.environment.color.saturation); + + R3D_RENDER_SCREEN(); + + return sceneTarget; +} + +r3d_target_t pass_post_fxaa(r3d_target_t sceneTarget) +{ + R3D_TARGET_BIND_AND_SWAP_SCENE(sceneTarget); + R3D_SHADER_USE(post.fxaa[R3D.aaPreset]); + + R3D_SHADER_BIND_SAMPLER(post.fxaa[R3D.aaPreset], uSceneTex, r3d_target_get(sceneTarget)); + R3D_RENDER_SCREEN(); + + return sceneTarget; +} + +r3d_target_t pass_post_smaa(r3d_target_t sceneTarget) +{ + r3d_target_t sceneSource = r3d_target_swap_scene(sceneTarget); + + /* --- Clear previous content --- */ + + // Bind and clear the stencil buffer. Since AA is the last post-processing + // pass, clearing it here is safe. The stencil is used to avoid running the + // blending weight calculation on pixels that have no edges, pass 1 writes 1 + // to the stencil for each edge pixel (non-edge pixels are discarded by the + // shader), then pass 2 only executes where stencil == 1. + + r3d_driver_enable(GL_STENCIL_TEST); + r3d_driver_set_stencil_mask(0xFF); + + R3D_TARGET_CLEAR(true, R3D_TARGET_SMAA_EDGES, R3D_TARGET_SMAA_BLEND); + + /* --- Edge detection --- */ + + r3d_driver_set_stencil_func(GL_ALWAYS, 1, 0xFF); + r3d_driver_set_stencil_op(GL_KEEP, GL_KEEP, GL_REPLACE); + + R3D_TARGET_BIND(true, R3D_TARGET_SMAA_EDGES); + R3D_SHADER_USE(prepare.smaaEdgeDetection[R3D.aaPreset]); + + R3D_SHADER_BIND_SAMPLER(prepare.smaaEdgeDetection[R3D.aaPreset], uSceneTex, r3d_target_get(sceneSource)); + + R3D_RENDER_SCREEN(); + + /* --- Compute blending weights --- */ + + r3d_driver_set_stencil_func(GL_EQUAL, 1, 0xFF); + r3d_driver_set_stencil_op(GL_KEEP, GL_KEEP, GL_KEEP); + + R3D_TARGET_BIND(true, R3D_TARGET_SMAA_BLEND); + R3D_SHADER_USE(prepare.smaaBlendingWeights[R3D.aaPreset]); + + R3D_SHADER_BIND_SAMPLER(prepare.smaaBlendingWeights[R3D.aaPreset], uEdgesTex, r3d_target_get(R3D_TARGET_SMAA_EDGES)); + R3D_SHADER_BIND_SAMPLER(prepare.smaaBlendingWeights[R3D.aaPreset], uAreaTex, r3d_texture_get(R3D_TEXTURE_SMAA_AREA)); + R3D_SHADER_BIND_SAMPLER(prepare.smaaBlendingWeights[R3D.aaPreset], uSearchTex, r3d_texture_get(R3D_TEXTURE_SMAA_SEARCH)); + + R3D_RENDER_SCREEN(); + + /* --- Apply anti aliasing to the scene --- */ + + r3d_driver_disable(GL_STENCIL_TEST); + + R3D_TARGET_BIND_AND_SWAP_SCENE(sceneTarget); + R3D_SHADER_USE(post.smaa[R3D.aaPreset]); + + R3D_SHADER_BIND_SAMPLER(post.smaa[R3D.aaPreset], uSceneTex, r3d_target_get(sceneTarget)); + R3D_SHADER_BIND_SAMPLER(post.smaa[R3D.aaPreset], uBlendTex, r3d_target_get(R3D_TARGET_SMAA_BLEND)); + + R3D_RENDER_SCREEN(); + + return sceneTarget; +} + +void blit_to_screen(r3d_target_t source) +{ + if (!r3d_target_exists(source)) { + return; + } + + GLuint dstId = R3D.screen.id; + + int targetW = dstId ? R3D.screen.texture.width : GetRenderWidth(); + int targetH = dstId ? R3D.screen.texture.height : GetRenderHeight(); + + if (targetW <= 0) targetW = 1; + if (targetH <= 0) targetH = 1; + + Rectangle viewport = R3D.viewState.viewport; + + int dstX = (int)(viewport.x + 0.5f); + int dstY = (int)(viewport.y + 0.5f); + int dstW = (int)(viewport.width + 0.5f); + int dstH = (int)(viewport.height + 0.5f); + + if (dstW <= 0 || dstH <= 0) { + return; + } + + int glDstY = targetH - dstY - dstH; + int srcW = 0; + int srcH = 0; + + r3d_target_get_resolution(&srcW, &srcH, source, 0); + + if (srcW <= 0 || srcH <= 0) { + return; + } + + bool sameDim = (dstW == srcW) && (dstH == srcH); + bool upscale = (dstW >= srcW) && (dstH >= srcH) && !sameDim; + bool downscale = (dstW <= srcW) && (dstH <= srcH) && !sameDim; + bool mixedScale = !sameDim && !upscale && !downscale; + + if (sameDim || (upscale && R3D.upscaleMode == R3D_UPSCALE_NEAREST) || (downscale && R3D.downscaleMode == R3D_DOWNSCALE_NEAREST)) { + r3d_target_blit(source, true, dstId, dstX, glDstY, dstW, dstH, false); + return; + } + + if (mixedScale || (upscale && R3D.upscaleMode == R3D_UPSCALE_LINEAR) || (downscale && R3D.downscaleMode == R3D_DOWNSCALE_LINEAR)) { + r3d_target_blit(source, true, dstId, dstX, glDstY, dstW, dstH, true); + return; + } + + if (upscale) { + glBindFramebuffer(GL_FRAMEBUFFER, dstId); + glViewport(dstX, glDstY, dstW, dstH); + + switch (R3D.upscaleMode) { + case R3D_UPSCALE_BICUBIC: + R3D_SHADER_USE(blit.upBicubic); + R3D_SHADER_SET_VEC2(blit.upBicubic, uSourceTexel, (Vector2) {R3D_TARGET_TEXEL_W, R3D_TARGET_TEXEL_H}); + R3D_SHADER_BIND_SAMPLER(blit.upBicubic, uSourceTex, r3d_target_get(source)); + break; + + case R3D_UPSCALE_LANCZOS: + R3D_SHADER_USE(blit.upLanczos); + R3D_SHADER_SET_VEC2(blit.upLanczos, uSourceTexel, (Vector2) {R3D_TARGET_TEXEL_W, R3D_TARGET_TEXEL_H}); + R3D_SHADER_BIND_SAMPLER(blit.upLanczos, uSourceTex, r3d_target_get(source)); + break; + + default: + r3d_target_blit(source, true, dstId, dstX, glDstY, dstW, dstH, true); + return; + } + + R3D_RENDER_SCREEN(); + r3d_target_blit(-1, true, dstId, dstX, glDstY, dstW, dstH, false); + return; + } + + if (downscale) { + glBindFramebuffer(GL_FRAMEBUFFER, dstId); + glViewport(dstX, glDstY, dstW, dstH); + + switch (R3D.downscaleMode) { + case R3D_DOWNSCALE_RGSS: + R3D_SHADER_USE(blit.downRgss); + R3D_SHADER_SET_VEC2(blit.downRgss, uDestTexel, (Vector2) {1.0f / dstW, 1.0f / dstH}); + R3D_SHADER_BIND_SAMPLER(blit.downRgss, uSourceTex, r3d_target_get(source)); + break; + + case R3D_DOWNSCALE_PDSS: + R3D_SHADER_USE(blit.downPdss); + R3D_SHADER_SET_VEC2(blit.downPdss, uDestTexel, (Vector2) {1.0f / dstW, 1.0f / dstH}); + R3D_SHADER_BIND_SAMPLER(blit.downPdss, uSourceTex, r3d_target_get(source)); + break; + + default: + r3d_target_blit(source, true, dstId, dstX, glDstY, dstW, dstH, true); + return; + } + + R3D_RENDER_SCREEN(); + r3d_target_blit(-1, true, dstId, dstX, glDstY, dstW, dstH, false); + return; + } +} + +void visualize_to_screen(r3d_target_t source) +{ + if (!r3d_target_exists(source)) { + return; + } + + GLuint dstId = R3D.screen.id; + int dstW = dstId ? R3D.screen.texture.width : GetRenderWidth(); + int dstH = dstId ? R3D.screen.texture.height : GetRenderHeight(); + + int dstX = 0, dstY = 0; + if (R3D.aspectMode == R3D_ASPECT_KEEP) { + float srcRatio = (float)R3D_TARGET_SIZE_W / R3D_TARGET_SIZE_H; + float dstRatio = (float)dstW / dstH; + if (srcRatio > dstRatio) { + int newH = (int)(dstW / srcRatio + 0.5f); + dstY = (dstH - newH) / 2; + dstH = newH; + } + else { + int newW = (int)(dstH * srcRatio + 0.5f); + dstX = (dstW - newW) / 2; + dstW = newW; + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, dstId); + glViewport(dstX, dstY, dstW, dstH); + + R3D_SHADER_USE(post.visualizer); + R3D_SHADER_SET_INT(post.visualizer, uOutputMode, R3D.outputMode); + R3D_SHADER_BIND_SAMPLER(post.visualizer, uSourceTex, r3d_target_get(source)); + + R3D_RENDER_SCREEN(); + + r3d_target_blit(-1, true, dstId, dstX, dstY, dstW, dstH, false); +} + +void cleanup_after_render(void) +{ + r3d_shader_invalidate_cache(); + r3d_target_invalidate_cache(); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindVertexArray(0); + + r3d_driver_restore_viewport(); + + r3d_driver_disable(GL_STENCIL_TEST); + r3d_driver_disable(GL_DEPTH_TEST); + r3d_driver_enable(GL_CULL_FACE); + r3d_driver_enable(GL_BLEND); + + r3d_driver_set_blend_func(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + r3d_driver_set_depth_offset(0.0f, 0.0f); + r3d_driver_set_depth_range(0.0f, 1.0f); + r3d_driver_set_depth_func(GL_LEQUAL); + r3d_driver_set_depth_mask(GL_TRUE); + r3d_driver_set_cull_face(GL_BACK); + + // Here we re-define the blend mode via rlgl to ensure its internal state + // matches what we've just set manually with OpenGL. + + // It's not enough to change the blend mode only through rlgl, because if we + // previously used a different blend mode (not "alpha") but rlgl still thinks it's "alpha", + // then rlgl won't correctly apply the intended blend mode. + + // We do this at the end because calling rlSetBlendMode can trigger a draw call for + // any content accumulated by rlgl, and we want that to be rendered into the main + // framebuffer, not into one of R3D's internal framebuffers that will be discarded afterward. + + rlSetBlendMode(RL_BLEND_ALPHA); + + // Here we reset the target sampling levels to facilitate debugging with RenderDoc + // WARNING: Make sure that everything that affects levels works in release mode! + +#ifndef NDEBUG + for (int iTarget = 0; iTarget < R3D_TARGET_COUNT; iTarget++) { + if (r3d_target_exists(iTarget)) { + r3d_target_set_read_levels(iTarget, 0, r3d_target_get_num_levels(iTarget) - 1); + } + } +#endif +} + +``` + +`src/r3d_environment.c`: + +```c +/* r3d_environment.h -- R3D Environment Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include + +#include "./r3d_core_state.h" + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Environment* R3D_GetEnvironment(void) +{ + return &R3D.environment; +} + +void R3D_SetEnvironment(const R3D_Environment* env) +{ + R3D.environment = *env; +} + +``` + +`src/r3d_frustum.c`: + +```c +/* r3d_frustum.c -- Common R3D Frustum Functions + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include + +#include "./common/r3d_math.h" +#include "./r3d_core_state.h" + +// ======================================== +// INLINE FUNCTIONS +// ======================================== + +static inline Vector4 normalize_plane(Vector4 plane) +{ + float len = sqrtf(plane.x*plane.x + plane.y*plane.y + plane.z*plane.z); + if (len <= 1e-6f) return (Vector4) {0}; + + float invLen = 1.0f / len; + plane.x *= invLen; + plane.y *= invLen; + plane.z *= invLen; + plane.w *= invLen; + + return plane; +} + +static inline float distance_to_plane(Vector4 plane, Vector3 position) +{ + return plane.x*position.x + plane.y*position.y + plane.z*position.z + plane.w; +} + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Frustum R3D_GetFrustum(void) +{ + return R3D.viewState.frustum; +} + +R3D_Frustum R3D_ComputeFrustum(Matrix viewProj) +{ + R3D_Frustum frustum = {0}; + + frustum.planes[R3D_PLANE_RIGHT] = normalize_plane((Vector4) { + viewProj.m3 - viewProj.m0, + viewProj.m7 - viewProj.m4, + viewProj.m11 - viewProj.m8, + viewProj.m15 - viewProj.m12 + }); + + frustum.planes[R3D_PLANE_LEFT] = normalize_plane((Vector4) { + viewProj.m3 + viewProj.m0, + viewProj.m7 + viewProj.m4, + viewProj.m11 + viewProj.m8, + viewProj.m15 + viewProj.m12 + }); + + frustum.planes[R3D_PLANE_TOP] = normalize_plane((Vector4) { + viewProj.m3 - viewProj.m1, + viewProj.m7 - viewProj.m5, + viewProj.m11 - viewProj.m9, + viewProj.m15 - viewProj.m13 + }); + + frustum.planes[R3D_PLANE_BOTTOM] = normalize_plane((Vector4) { + viewProj.m3 + viewProj.m1, + viewProj.m7 + viewProj.m5, + viewProj.m11 + viewProj.m9, + viewProj.m15 + viewProj.m13 + }); + + frustum.planes[R3D_PLANE_BACK] = normalize_plane((Vector4) { + viewProj.m3 - viewProj.m2, + viewProj.m7 - viewProj.m6, + viewProj.m11 - viewProj.m10, + viewProj.m15 - viewProj.m14 + }); + + frustum.planes[R3D_PLANE_FRONT] = normalize_plane((Vector4) { + viewProj.m3 + viewProj.m2, + viewProj.m7 + viewProj.m6, + viewProj.m11 + viewProj.m10, + viewProj.m15 + viewProj.m14 + }); + + return frustum; +} + +BoundingBox R3D_ComputeFrustumBoundingBox(Matrix invViewProj) +{ + Vector4 clipCorners[8] = { + {-1, -1, -1, 1}, {1, -1, -1, 1}, {1, 1, -1, 1}, {-1, 1, -1, 1}, // Near + {-1, -1, 1, 1}, {1, -1, 1, 1}, {1, 1, 1, 1}, {-1, 1, 1, 1} // Far + }; + + BoundingBox bbox = { + .min = (Vector3){ FLT_MAX, FLT_MAX, FLT_MAX}, + .max = (Vector3){-FLT_MAX, -FLT_MAX, -FLT_MAX} + }; + + for (int i = 0; i < 8; i++) { + Vector4 p = r3d_vector4_transform(clipCorners[i], &invViewProj); + if (fabsf(p.w) > 1e-6f) { + float invW = 1.0f / p.w; + p.x *= invW; + p.y *= invW; + p.z *= invW; + } + bbox.min = Vector3Min(bbox.min, (Vector3) {p.x, p.y, p.z}); + bbox.max = Vector3Max(bbox.max, (Vector3) {p.x, p.y, p.z}); + } + + return bbox; +} + +void R3D_ComputeFrustumCorners(Matrix invViewProj, Vector3* corners) +{ + Vector4 clipCorners[8] = { + {-1, -1, -1, 1}, {1, -1, -1, 1}, {1, 1, -1, 1}, {-1, 1, -1, 1}, // Near + {-1, -1, 1, 1}, {1, -1, 1, 1}, {1, 1, 1, 1}, {-1, 1, 1, 1} // Far + }; + + for (int i = 0; i < 8; i++) { + Vector4 p = r3d_vector4_transform(clipCorners[i], &invViewProj); + if (fabsf(p.w) > 1e-6f) { + float invW = 1.0f / p.w; + p.x *= invW; + p.y *= invW; + p.z *= invW; + } + corners[i] = (Vector3) {p.x, p.y, p.z}; + } +} + +bool R3D_FrustumContainsPoint(const R3D_Frustum* frustum, Vector3 position) +{ + for (int i = 0; i < R3D_PLANE_COUNT; i++) { + if (distance_to_plane(frustum->planes[i], position) <= 0) { + return false; + } + } + + return true; +} + +bool R3D_FrustumContainsAnyPoint(const R3D_Frustum* frustum, const Vector3* positions, int count) +{ + for (int i = 0; i < count; i++) { + if (R3D_FrustumContainsPoint(frustum, positions[i])) { + return true; + } + } + + return false; +} + +bool R3D_FrustumIntersectsSphere(const R3D_Frustum* frustum, Vector3 position, float radius) +{ + for (int i = 0; i < R3D_PLANE_COUNT; i++) { + if (distance_to_plane(frustum->planes[i], position) < -radius) { + return false; + } + } + + return true; +} + +bool R3D_FrustumIntersectsBoundingBox(const R3D_Frustum* frustum, BoundingBox aabb) +{ + float xMin = aabb.min.x, yMin = aabb.min.y, zMin = aabb.min.z; + float xMax = aabb.max.x, yMax = aabb.max.y, zMax = aabb.max.z; + + for (int i = 0; i < R3D_PLANE_COUNT; i++) + { + const Vector4* plane = &frustum->planes[i]; + + // Choose the optimal coordinates according to the sign of the normal + float distance = distance_to_plane(*plane, (Vector3) { + .x = (plane->x >= 0.0f) ? xMax : xMin, + .y = (plane->y >= 0.0f) ? yMax : yMin, + .z = (plane->z >= 0.0f) ? zMax : zMin + }); + + if (distance < -EPSILON) { + return false; + } + } + + return true; +} + +bool R3D_FrustumIntersectsOrientedBox(const R3D_Frustum* frustum, R3D_OrientedBox obb) +{ + for (int i = 0; i < R3D_PLANE_COUNT; i++) + { + const Vector4* plane = &frustum->planes[i]; + + float centerDistance = + plane->x * obb.center.x + + plane->y * obb.center.y + + plane->z * obb.center.z + + plane->w; + + float projectedRadius = + fabsf(plane->x*obb.axisX.x + plane->y*obb.axisX.y + plane->z*obb.axisX.z) * obb.halfExtents.x + + fabsf(plane->x*obb.axisY.x + plane->y*obb.axisY.y + plane->z*obb.axisY.z) * obb.halfExtents.y + + fabsf(plane->x*obb.axisZ.x + plane->y*obb.axisZ.y + plane->z*obb.axisZ.z) * obb.halfExtents.z; + + if (centerDistance + projectedRadius < -EPSILON) return false; + } + + return true; +} + +``` + +`src/r3d_importer.c`: + +```c +/* r3d_importer.c -- R3D Importer Module + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include + +#ifdef R3D_SUPPORT_ASSIMP +# include "./importer/r3d_importer_internal.h" +# include "./common/r3d_helper.h" +# include +#endif + +// ======================================== +// INTERNAL CONSTANTS +// ======================================== + +#define POST_PROCESS_PRESET_FAST \ + aiProcess_CalcTangentSpace | \ + aiProcess_GenNormals | \ + aiProcess_JoinIdenticalVertices | \ + aiProcess_Triangulate | \ + aiProcess_GenUVCoords | \ + aiProcess_SortByPType | \ + aiProcess_FlipUVs + +#define POST_PROCESS_PRESET_QUALITY \ + aiProcess_CalcTangentSpace | \ + aiProcess_GenSmoothNormals | \ + aiProcess_JoinIdenticalVertices | \ + aiProcess_ImproveCacheLocality | \ + aiProcess_LimitBoneWeights | \ + aiProcess_RemoveRedundantMaterials | \ + aiProcess_SplitLargeMeshes | \ + aiProcess_Triangulate | \ + aiProcess_GenUVCoords | \ + aiProcess_SortByPType | \ + aiProcess_FindDegenerates | \ + aiProcess_FindInvalidData | \ + aiProcess_FlipUVs + +// ======================================== +// PRIVATE FUNCTIONS +// ======================================== + +#ifdef R3D_SUPPORT_ASSIMP + +static void determine_importer_name(char* outName, size_t outSize, const struct aiScene* scene, const char* hint) +{ + if (!outName || outSize == 0) return; + + if (scene && scene->mMetaData) { + struct aiMetadata* meta = scene->mMetaData; + for (unsigned int i = 0; i < meta->mNumProperties; i++) { + if ((strcmp(meta->mKeys[i].data, "SourceAsset_Filename") == 0 || + strcmp(meta->mKeys[i].data, "FileName") == 0) && + meta->mValues[i].mType == AI_AISTRING) + { + struct aiString* str = meta->mValues[i].mData; + const char* filename = strrchr(str->data, '/'); + if (!filename) filename = strrchr(str->data, '\\'); + filename = filename ? filename + 1 : str->data; + r3d_string_format(outName, outSize, "memory data (%s)", filename); + return; + } + } + } + + if (hint && hint[0] != '\0') { + r3d_string_format(outName, outSize, "memory data (%s)", hint); + return; + } + + r3d_string_format(outName, outSize, "memory data"); +} + +static void build_bone_mapping(R3D_Importer* importer) +{ + int totalBones = 0; + for (uint32_t meshIdx = 0; meshIdx < importer->scene->mNumMeshes; meshIdx++) { + const struct aiMesh* mesh = importer->scene->mMeshes[meshIdx]; + if (mesh && mesh->mNumBones) totalBones += mesh->mNumBones; + } + + if (totalBones == 0) { + importer->bones.array = NULL; + importer->bones.head = NULL; + importer->bones.count = 0; + return; + } + + importer->bones.array = RL_MALLOC(totalBones * sizeof(r3d_importer_bone_entry_t)); + importer->bones.head = NULL; + importer->bones.count = 0; + + for (uint32_t meshIdx = 0; meshIdx < importer->scene->mNumMeshes; meshIdx++) + { + const struct aiMesh* mesh = importer->scene->mMeshes[meshIdx]; + if (!mesh || !mesh->mNumBones) continue; + + for (uint32_t boneIdx = 0; boneIdx < mesh->mNumBones; boneIdx++) + { + const struct aiBone* bone = mesh->mBones[boneIdx]; + if (!bone) continue; + + const char* boneName = bone->mName.data; + + r3d_importer_bone_entry_t* entry = NULL; + HASH_FIND_STR(importer->bones.head, boneName, entry); + if (entry != NULL) continue; + + entry = &importer->bones.array[importer->bones.count]; + + strncpy(entry->name, boneName, sizeof(entry->name) - 1); + entry->name[sizeof(entry->name) - 1] = '\0'; + entry->index = importer->bones.count; + + HASH_ADD_STR(importer->bones.head, name, entry); + importer->bones.count++; + } + } + + if (importer->bones.count > 0) { + R3D_TRACELOG(LOG_DEBUG, "Built bone mapping with %d bones", importer->bones.count); + } +} + +#endif // R3D_SUPPORT_ASSIMP + +// ======================================== +// PUBLIC FUNCTIONS +// ======================================== + +R3D_Importer* R3D_LoadImporter(const char* filePath, R3D_ImportFlags flags) +{ +#ifdef R3D_SUPPORT_ASSIMP + enum aiPostProcessSteps aiFlags = POST_PROCESS_PRESET_FAST; + if (BIT_TEST(flags, R3D_IMPORT_QUALITY)) { + aiFlags = POST_PROCESS_PRESET_QUALITY; + } + + const struct aiScene* scene = aiImportFile(filePath, aiFlags); + if (!scene || !scene->mRootNode || (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { + R3D_TRACELOG(LOG_ERROR, "Assimp failed to load '%s': %s", filePath, aiGetErrorString()); + return NULL; + } + + R3D_Importer* importer = RL_CALLOC(1, sizeof(*importer)); + importer->scene = scene; + importer->flags = flags; + + strncpy(importer->name, filePath, sizeof(importer->name) - 1); + importer->name[sizeof(importer->name) - 1] = '\0'; + + build_bone_mapping(importer); + + R3D_TRACELOG(LOG_INFO, "Importer loaded successfully: '%s'", filePath); + + return importer; + +#else + R3D_TRACELOG(LOG_WARNING, "Cannot load '%s': built without Assimp support", filePath); + return NULL; + +#endif // R3D_SUPPORT_ASSIMP +} + +R3D_Importer* R3D_LoadImporterFromMemory(const void* data, unsigned int size, const char* hint, R3D_ImportFlags flags) +{ +#ifdef R3D_SUPPORT_ASSIMP + enum aiPostProcessSteps aiFlags = POST_PROCESS_PRESET_FAST; + if (BIT_TEST(flags, R3D_IMPORT_QUALITY)) { + aiFlags = POST_PROCESS_PRESET_QUALITY; + } + + const struct aiScene* scene = aiImportFileFromMemory(data, size, aiFlags, hint); + if (!scene || !scene->mRootNode || (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { + if (hint && hint[0] != '\0') { + R3D_TRACELOG(LOG_ERROR, "Assimp failed to load memory asset '%s': %s", hint, aiGetErrorString()); + } + else { + R3D_TRACELOG(LOG_ERROR, "Assimp failed to load memory asset: %s", aiGetErrorString()); + } + return NULL; + } + + R3D_Importer* importer = RL_CALLOC(1, sizeof(*importer)); + importer->scene = scene; + importer->flags = flags; + + determine_importer_name(importer->name, sizeof(importer->name), scene, hint); + build_bone_mapping(importer); + + if (hint && hint[0] != '\0') { + R3D_TRACELOG(LOG_INFO, "Importer loaded successfully from memory: '%s'", hint); + } + else { + R3D_TRACELOG(LOG_INFO, "Importer loaded successfully from memory"); + } + + return importer; + +#else + if (hint && hint[0] != '\0') { + R3D_TRACELOG(LOG_WARNING, "Cannot load '%s' from memory: built without Assimp support", hint); + } + else { + R3D_TRACELOG(LOG_WARNING, "Cannot load asset from memory: built without Assimp support"); + } + + return NULL; + +#endif // R3D_SUPPORT_ASSIMP +} + +void R3D_UnloadImporter(R3D_Importer* importer) +{ +#ifdef R3D_SUPPORT_ASSIMP + if (!importer) return; + + HASH_CLEAR(hh, importer->bones.head); + + if (importer->bones.array) { + RL_FREE(importer->bones.array); + } + + aiReleaseImport(importer->scene); + RL_FREE(importer); + +#else + (void)importer; + +#endif // R3D_SUPPORT_ASSIMP +} + +``` + +`src/r3d_instance.c`: + +```c +/* r3d_instance.c -- R3D Instance Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "./modules/r3d_driver.h" +#include "./common/r3d_helper.h" + +// ======================================== +// INTERNAL CONSTANTS +// ======================================== + +#define R3D_INSTANCE_VALID_FLAGS ((1u << R3D_INSTANCE_ATTRIBUTE_COUNT) - 1u) + +static const size_t INSTANCE_ATTRIBUTE_COMPONENTS[R3D_INSTANCE_ATTRIBUTE_COUNT] = { + /* POSITION */ 3, + /* ROTATION */ 4, + /* SCALE */ 3, + /* COLOR */ 4, + /* CUSTOM */ 4, +}; + +static const size_t INSTANCE_FORMAT_SIZE[R3D_INSTANCE_FORMAT_COUNT] = { + [R3D_INSTANCE_FORMAT_FLOAT32] = 4, + [R3D_INSTANCE_FORMAT_FLOAT16] = 2, + [R3D_INSTANCE_FORMAT_UNORM16] = 2, + [R3D_INSTANCE_FORMAT_SNORM16] = 2, + [R3D_INSTANCE_FORMAT_UNORM8] = 1, + [R3D_INSTANCE_FORMAT_SNORM8] = 1, +}; + +// ======================================== +// HELPER FUNCTIONS +// ======================================== + +static bool r3d_instance_format_is_valid(R3D_InstanceFormat format) +{ + return format >= 0 && format < R3D_INSTANCE_FORMAT_COUNT; +} + +static size_t get_attribute_size(int attrIndex, R3D_InstanceFormat format) +{ + if (attrIndex < 0 || attrIndex >= R3D_INSTANCE_ATTRIBUTE_COUNT) return 0; + if (!r3d_instance_format_is_valid(format)) return 0; + + return INSTANCE_ATTRIBUTE_COMPONENTS[attrIndex] * INSTANCE_FORMAT_SIZE[format]; +} + +static int r3d_instance_attribute_index(R3D_InstanceFlags attribute) +{ + if (attribute == 0 || (attribute & (attribute - 1)) != 0) return -1; + + int index = r3d_lsb_index(attribute); + if (index < 0 || index >= R3D_INSTANCE_ATTRIBUTE_COUNT) return -1; + + return index; +} + +static bool get_buffer_size(int capacity, int attrIndex, R3D_InstanceFormat format, size_t* size) +{ + if (size == NULL) return false; + + size_t attrSize = get_attribute_size(attrIndex, format); + if (capacity <= 0 || attrSize == 0) return false; + + if ((size_t)capacity > SIZE_MAX / attrSize) return false; + + *size = (size_t)capacity * attrSize; + return true; +} + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_InstanceBuffer R3D_LoadInstanceBuffer(int capacity, R3D_InstanceFlags flags) +{ + R3D_InstanceLayout layout = { + .formats = { + R3D_INSTANCE_FORMAT_FLOAT32, + R3D_INSTANCE_FORMAT_FLOAT32, + R3D_INSTANCE_FORMAT_FLOAT32, + R3D_INSTANCE_FORMAT_UNORM8, + R3D_INSTANCE_FORMAT_FLOAT32, + }, + .flags = flags, + }; + + return R3D_LoadInstanceBufferEx(capacity, layout); +} + +R3D_InstanceBuffer R3D_LoadInstanceBufferEx(int capacity, R3D_InstanceLayout layout) +{ + R3D_InstanceBuffer buffer = {0}; + + if (capacity <= 0) { + R3D_TRACELOG(LOG_WARNING, "InstanceBuffer -> invalid capacity (%d)", capacity); + return buffer; + } + + if ((layout.flags & ~R3D_INSTANCE_VALID_FLAGS) != 0) { + R3D_TRACELOG(LOG_WARNING, "InstanceBuffer -> invalid attribute flags (0x%04x)", layout.flags); + return buffer; + } + + if (layout.flags == 0) { + R3D_TRACELOG(LOG_WARNING, "InstanceBuffer -> no attributes requested"); + return buffer; + } + + glGenBuffers(R3D_INSTANCE_ATTRIBUTE_COUNT, buffer.buffers); + + for (int i = 0; i < R3D_INSTANCE_ATTRIBUTE_COUNT; i++) { + if (!BIT_TEST(layout.flags, 1u << i)) continue; + + size_t size = 0; + if (!get_buffer_size(capacity, i, layout.formats[i], &size)) { + R3D_TRACELOG(LOG_WARNING, "InstanceBuffer -> invalid size or format (attribute=%d, format=%d)", i, layout.formats[i]); + glDeleteBuffers(R3D_INSTANCE_ATTRIBUTE_COUNT, buffer.buffers); + return (R3D_InstanceBuffer){0}; + } + + glBindBuffer(GL_ARRAY_BUFFER, buffer.buffers[i]); + + r3d_driver_clear_errors(); + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)size, NULL, GL_DYNAMIC_DRAW); + + if (r3d_driver_check_error("InstanceBuffer -> glBufferData failed")) { + R3D_TRACELOG(LOG_WARNING, "InstanceBuffer -> failed to allocate attribute %d (%.2f MiB)", i, (double)size / (1024.0 * 1024.0)); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDeleteBuffers(R3D_INSTANCE_ATTRIBUTE_COUNT, buffer.buffers); + return (R3D_InstanceBuffer){0}; + } + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + buffer.capacity = capacity; + buffer.layout = layout; + + R3D_TRACELOG(LOG_INFO, "Instance buffer created successfully (capacity=%d | flags=0x%04x)", capacity, layout.flags); + + return buffer; +} + +void R3D_UnloadInstanceBuffer(R3D_InstanceBuffer buffer) +{ + glDeleteBuffers(R3D_INSTANCE_ATTRIBUTE_COUNT, buffer.buffers); +} + +void R3D_ResizeInstanceBuffer(R3D_InstanceBuffer* buffer, int newCapacity, bool keepData) +{ + if (buffer == NULL) { + R3D_TRACELOG(LOG_WARNING, "ResizeInstanceBuffer -> buffer is NULL"); + return; + } + + if (newCapacity <= buffer->capacity) { + return; + } + + if ((buffer->layout.flags & ~R3D_INSTANCE_VALID_FLAGS) != 0) { + R3D_TRACELOG(LOG_WARNING, "ResizeInstanceBuffer -> invalid attribute flags (0x%04x)", buffer->layout.flags); + return; + } + + if (!keepData) { + // Orphan path: reallocate existing buffers in-place, avoids GPU stall and new IDs + for (int i = 0; i < R3D_INSTANCE_ATTRIBUTE_COUNT; i++) { + if (!BIT_TEST(buffer->layout.flags, 1u << i)) continue; + + size_t newSize = 0; + if (!get_buffer_size(newCapacity, i, buffer->layout.formats[i], &newSize)) { + R3D_TRACELOG(LOG_WARNING, "ResizeInstanceBuffer -> invalid size or format (attribute=%d, format=%d)", i, buffer->layout.formats[i]); + glBindBuffer(GL_ARRAY_BUFFER, 0); + return; + } + + // Generate buffer on first use + if (buffer->buffers[i] == 0) { + glGenBuffers(1, &buffer->buffers[i]); + } + + glBindBuffer(GL_ARRAY_BUFFER, buffer->buffers[i]); + r3d_driver_clear_errors(); + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)newSize, NULL, GL_DYNAMIC_DRAW); + + if (r3d_driver_check_error("ResizeInstanceBuffer -> glBufferData failed")) { + R3D_TRACELOG(LOG_WARNING, "ResizeInstanceBuffer -> failed to orphan attribute %d (%.2f MiB)", i, (double)newSize / (1024.0 * 1024.0)); + glBindBuffer(GL_ARRAY_BUFFER, 0); + return; + } + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + else { + // Copy path: allocate new buffers, copy existing data, swap and delete old + GLuint newBuffers[R3D_INSTANCE_ATTRIBUTE_COUNT] = {0}; + + for (int i = 0; i < R3D_INSTANCE_ATTRIBUTE_COUNT; i++) { + if (!BIT_TEST(buffer->layout.flags, 1u << i)) continue; + + size_t newSize = 0; + if (!get_buffer_size(newCapacity, i, buffer->layout.formats[i], &newSize)) { + R3D_TRACELOG(LOG_WARNING, "ResizeInstanceBuffer -> invalid size or format (attribute=%d, format=%d)", i, buffer->layout.formats[i]); + glDeleteBuffers(R3D_INSTANCE_ATTRIBUTE_COUNT, newBuffers); + return; + } + + glGenBuffers(1, &newBuffers[i]); + glBindBuffer(GL_COPY_WRITE_BUFFER, newBuffers[i]); + r3d_driver_clear_errors(); + glBufferData(GL_COPY_WRITE_BUFFER, (GLsizeiptr)newSize, NULL, GL_DYNAMIC_DRAW); + + if (r3d_driver_check_error("ResizeInstanceBuffer -> glBufferData failed")) { + R3D_TRACELOG(LOG_WARNING, "ResizeInstanceBuffer -> failed to allocate attribute %d (%.2f MiB)", i, (double)newSize / (1024.0 * 1024.0)); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + glDeleteBuffers(R3D_INSTANCE_ATTRIBUTE_COUNT, newBuffers); + return; + } + + // Copy old content if it exists + if (buffer->capacity > 0 && buffer->buffers[i] != 0) { + size_t oldSize = 0; + if (!get_buffer_size(buffer->capacity, i, buffer->layout.formats[i], &oldSize)) { + R3D_TRACELOG(LOG_WARNING, "ResizeInstanceBuffer -> invalid old size (attribute=%d)", i); + glBindBuffer(GL_COPY_READ_BUFFER, 0); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + glDeleteBuffers(R3D_INSTANCE_ATTRIBUTE_COUNT, newBuffers); + return; + } + + glBindBuffer(GL_COPY_READ_BUFFER, buffer->buffers[i]); + r3d_driver_clear_errors(); + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, (GLsizeiptr)oldSize); + + if (r3d_driver_check_error("ResizeInstanceBuffer -> glCopyBufferSubData failed")) { + R3D_TRACELOG(LOG_WARNING, "ResizeInstanceBuffer -> failed to copy attribute %d", i); + glBindBuffer(GL_COPY_READ_BUFFER, 0); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + glDeleteBuffers(R3D_INSTANCE_ATTRIBUTE_COUNT, newBuffers); + return; + } + } + } + + glBindBuffer(GL_COPY_READ_BUFFER, 0); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + + // Swap: delete old GPU buffers and take ownership of new ones + glDeleteBuffers(R3D_INSTANCE_ATTRIBUTE_COUNT, buffer->buffers); + memcpy(buffer->buffers, newBuffers, sizeof(newBuffers)); + } + + buffer->capacity = newCapacity; + R3D_TRACELOG(LOG_INFO, "Instance buffer resized successfully (capacity=%d)", newCapacity); +} + +void R3D_UploadInstances(R3D_InstanceBuffer buffer, R3D_InstanceFlags flag, + int offset, int count, const void* data, bool discard) +{ + int index = r3d_instance_attribute_index(flag); + if (index < 0) { + R3D_TRACELOG(LOG_WARNING, "UploadInstances -> invalid attribute flag (0x%04x)", flag); + return; + } + + if (!BIT_TEST(buffer.layout.flags, flag)) { + R3D_TRACELOG(LOG_WARNING, "UploadInstances -> attribute not allocated (flag=0x%04x)", flag); + return; + } + + if (buffer.buffers[index] == 0) { + R3D_TRACELOG(LOG_WARNING, "UploadInstances -> invalid GPU buffer (flag=0x%04x)", flag); + return; + } + + if (offset < 0 || count <= 0 || offset > buffer.capacity || count > buffer.capacity - offset) { + R3D_TRACELOG(LOG_WARNING, "UploadInstances -> range out of bounds (offset=%d, count=%d, capacity=%d)", offset, count, buffer.capacity); + return; + } + + if (data == NULL) { + R3D_TRACELOG(LOG_WARNING, "UploadInstances -> data is NULL"); + return; + } + + size_t attrSize = get_attribute_size(index, buffer.layout.formats[index]); + if (attrSize == 0) { + R3D_TRACELOG(LOG_WARNING, "UploadInstances -> invalid attribute size (flag=0x%04x)", flag); + return; + } + + size_t uploadSize = (size_t)count * attrSize; + size_t offsetBytes = (size_t)offset * attrSize; + + glBindBuffer(GL_ARRAY_BUFFER, buffer.buffers[index]); + + // Orphan the buffer to avoid GPU sync; old data is discarded entirely + if (discard) { + size_t totalSize = (size_t)buffer.capacity * attrSize; + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)totalSize, NULL, GL_DYNAMIC_DRAW); + } + + r3d_driver_clear_errors(); + glBufferSubData(GL_ARRAY_BUFFER, (GLintptr)offsetBytes, (GLsizeiptr)uploadSize, data); + + if (r3d_driver_check_error("UploadInstances -> glBufferSubData failed")) { + R3D_TRACELOG(LOG_WARNING, "UploadInstances -> failed to upload attribute data (%.2f MiB)", (double)uploadSize / (1024.0 * 1024.0)); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void* R3D_MapInstances(R3D_InstanceBuffer buffer, R3D_InstanceFlags flag, bool discard) +{ + return R3D_MapInstancesEx(buffer, flag, 0, buffer.capacity, discard); +} + +void* R3D_MapInstancesEx(R3D_InstanceBuffer buffer, R3D_InstanceFlags flag, + int offset, int count, bool discard) +{ + int index = r3d_instance_attribute_index(flag); + if (index < 0) { + R3D_TRACELOG(LOG_WARNING, "MapInstancesEx -> invalid attribute flag (0x%04x)", flag); + return NULL; + } + + if (!BIT_TEST(buffer.layout.flags, flag)) { + R3D_TRACELOG(LOG_WARNING, "MapInstancesEx -> attribute not allocated (flag=0x%04x)", flag); + return NULL; + } + + if (buffer.buffers[index] == 0) { + R3D_TRACELOG(LOG_WARNING, "MapInstancesEx -> invalid GPU buffer (flag=0x%04x)", flag); + return NULL; + } + + if (offset < 0 || count <= 0 || offset > buffer.capacity || count > buffer.capacity - offset) { + R3D_TRACELOG(LOG_WARNING, "MapInstancesEx -> range out of bounds (offset=%d, count=%d, capacity=%d)", offset, count, buffer.capacity); + return NULL; + } + + size_t attrSize = get_attribute_size(index, buffer.layout.formats[index]); + if (attrSize == 0) { + R3D_TRACELOG(LOG_WARNING, "MapInstancesEx -> invalid attribute size (flag=0x%04x)", flag); + return NULL; + } + + GLbitfield mapFlags = GL_MAP_WRITE_BIT; + if (discard) mapFlags |= GL_MAP_INVALIDATE_RANGE_BIT; + + GLintptr offsetBytes = (GLintptr)((size_t)offset * attrSize); + GLsizeiptr rangeSize = (GLsizeiptr)((size_t)count * attrSize); + + glBindBuffer(GL_ARRAY_BUFFER, buffer.buffers[index]); + + r3d_driver_clear_errors(); + void* ptr = glMapBufferRange(GL_ARRAY_BUFFER, offsetBytes, rangeSize, mapFlags); + + if (r3d_driver_check_error("MapInstancesEx -> glMapBufferRange failed") || ptr == NULL) { + R3D_TRACELOG(LOG_WARNING, "MapInstancesEx -> failed to map GPU buffer (flag=0x%04x)", flag); + ptr = NULL; + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + return ptr; +} + +void R3D_UnmapInstances(R3D_InstanceBuffer buffer, R3D_InstanceFlags flags) +{ + if ((flags & ~R3D_INSTANCE_VALID_FLAGS) != 0) { + R3D_TRACELOG(LOG_WARNING, "UnmapInstances -> invalid attribute flags (0x%04x)", flags); + flags &= R3D_INSTANCE_VALID_FLAGS; + } + + for (int i = 0; i < R3D_INSTANCE_ATTRIBUTE_COUNT; i++) { + R3D_InstanceFlags flag = 1u << i; + + if (!BIT_TEST(flags, flag) || !BIT_TEST(buffer.layout.flags, flag)) { + continue; + } + + if (buffer.buffers[i] == 0) { + R3D_TRACELOG(LOG_WARNING, "UnmapInstances -> invalid GPU buffer (flag=0x%04x)", flag); + continue; + } + + glBindBuffer(GL_ARRAY_BUFFER, buffer.buffers[i]); + + r3d_driver_clear_errors(); + if (!glUnmapBuffer(GL_ARRAY_BUFFER)) { + R3D_TRACELOG(LOG_WARNING, "UnmapInstances -> GPU data may be corrupted (flag=0x%04x)", flag); + } + + r3d_driver_check_error("UnmapInstances -> glUnmapBuffer failed"); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void R3D_SetInstanceFormat(R3D_InstanceLayout* layout, R3D_InstanceFlags attribute, R3D_InstanceFormat format) +{ + if (layout == NULL) { + R3D_TRACELOG(LOG_WARNING, "SetInstanceFormat -> layout is NULL"); + return; + } + + int index = r3d_instance_attribute_index(attribute); + if (index < 0) { + R3D_TRACELOG(LOG_WARNING, "SetInstanceFormat -> invalid attribute flag (0x%04x)", attribute); + return; + } + + if (!r3d_instance_format_is_valid(format)) { + R3D_TRACELOG(LOG_WARNING, "SetInstanceFormat -> invalid format (%d)", format); + return; + } + + layout->formats[index] = format; +} + +R3D_InstanceFormat R3D_GetInstanceFormat(R3D_InstanceLayout layout, R3D_InstanceFlags attribute) +{ + int index = r3d_instance_attribute_index(attribute); + if (index < 0) { + R3D_TRACELOG(LOG_WARNING, "GetInstanceFormat -> invalid attribute flag (0x%04x)", attribute); + return R3D_INSTANCE_FORMAT_FLOAT32; + } + + if (!r3d_instance_format_is_valid(layout.formats[index])) { + R3D_TRACELOG(LOG_WARNING, "GetInstanceFormat -> invalid format for attribute flag (0x%04x)", attribute); + return R3D_INSTANCE_FORMAT_FLOAT32; + } + + return layout.formats[index]; +} + +``` + +`src/r3d_kinematics.c`: + +```c +/* r3d_kinematics.c -- R3D Kinematics Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include + +#include "./common/r3d_math.h" + +// ======================================== +// PUBLIC API +// ======================================== + +Vector3 R3D_ClipVelocity(Vector3 velocity, Vector3 normal) +{ + float dot = Vector3DotProduct(velocity, normal); + return Vector3Subtract(velocity, Vector3Scale(normal, dot)); +} + +Vector3 R3D_ReflectVelocity(Vector3 velocity, Vector3 normal, float bounciness) +{ + float dot = Vector3DotProduct(velocity, normal); + Vector3 reflection = Vector3Subtract(velocity, Vector3Scale(normal, 2.0f * dot)); + return Vector3Scale(reflection, bounciness); +} + +Vector3 R3D_SlideVelocity(Vector3 velocity, R3D_SweepCollision collision, Vector3* outNormal) +{ + if (!collision.hit) { + if (outNormal) *outNormal = (Vector3){0}; + return velocity; + } + + if (outNormal) *outNormal = collision.normal; + + float safeTime = fmaxf(0.0f, collision.time - 0.001f); + Vector3 safeVelocity = Vector3Scale(velocity, safeTime); + Vector3 remainingVelocity = Vector3Scale(velocity, 1.0f - safeTime); + Vector3 slidedRemaining = R3D_ClipVelocity(remainingVelocity, collision.normal); + + return Vector3Add(safeVelocity, slidedRemaining); +} + +Vector3 R3D_SlideSphereBoundingBox(Vector3 center, float radius, Vector3 velocity, BoundingBox box, Vector3* outNormal) +{ + return R3D_SlideVelocity(velocity, R3D_SweepSphereBoundingBox(center, radius, velocity, box), outNormal); +} + +Vector3 R3D_SlideSphereMesh(Vector3 center, float radius, Vector3 velocity, R3D_MeshData mesh, Matrix transform, Vector3* outNormal) +{ + return R3D_SlideVelocity(velocity, R3D_SweepSphereMesh(center, radius, velocity, mesh, transform), outNormal); +} + +Vector3 R3D_SlideCapsuleBoundingBox(R3D_Capsule capsule, Vector3 velocity, BoundingBox box, Vector3* outNormal) +{ + return R3D_SlideVelocity(velocity, R3D_SweepCapsuleBoundingBox(capsule, velocity, box), outNormal); +} + +Vector3 R3D_SlideCapsuleMesh(R3D_Capsule capsule, Vector3 velocity, R3D_MeshData mesh, Matrix transform, Vector3* outNormal) +{ + return R3D_SlideVelocity(velocity, R3D_SweepCapsuleMesh(capsule, velocity, mesh, transform), outNormal); +} + +bool R3D_DepenetrateSphereBoundingBox(Vector3* center, float radius, BoundingBox box, float* outPenetration) +{ + Vector3 closestPoint = R3D_ClosestPointOnBox(*center, box); + Vector3 delta = Vector3Subtract(*center, closestPoint); + float distSq = Vector3LengthSqr(delta); + float radiusSq = radius * radius; + + if (distSq >= radiusSq) return false; + + float dist = sqrtf(distSq); + float penetration = radius - dist; + + Vector3 direction = dist > 1e-6f ? Vector3Scale(delta, 1.0f / dist) : (Vector3){0, 1, 0}; + *center = Vector3Add(*center, Vector3Scale(direction, penetration)); + + if (outPenetration) *outPenetration = penetration; + return true; +} + +bool R3D_DepenetrateCapsuleBoundingBox(R3D_Capsule* capsule, BoundingBox box, float* outPenetration) +{ + Vector3 start = capsule->start; + Vector3 end = capsule->end; + Vector3 seg = Vector3Subtract(end, start); + + float tCandidates[8]; + int count = 0; + + tCandidates[count++] = 0.0f; + tCandidates[count++] = 1.0f; + +#define ADD_T_FOR_AXIS(s, a, mn, mx) do { \ + if (fabsf(s) > 1e-8f) { \ + float t0 = ((mn) - (a)) / (s); \ + float t1 = ((mx) - (a)) / (s); \ + if (t0 >= 0.0f && t0 <= 1.0f) tCandidates[count++] = t0; \ + if (t1 >= 0.0f && t1 <= 1.0f) tCandidates[count++] = t1; \ + } \ + } while (0) + + ADD_T_FOR_AXIS(seg.x, start.x, box.min.x, box.max.x); + ADD_T_FOR_AXIS(seg.y, start.y, box.min.y, box.max.y); + ADD_T_FOR_AXIS(seg.z, start.z, box.min.z, box.max.z); + +#undef ADD_T_FOR_AXIS + + float bestDistSq = FLT_MAX; + Vector3 bestDelta = {0}; + + for (int i = 0; i < count; i++) { + Vector3 onSeg = Vector3Add(start, Vector3Scale(seg, tCandidates[i])); + Vector3 onBox = R3D_ClosestPointOnBox(onSeg, box); + Vector3 delta = Vector3Subtract(onSeg, onBox); + + float distSq = Vector3LengthSqr(delta); + + if (distSq < bestDistSq) { + bestDistSq = distSq; + bestDelta = delta; + } + } + + float radius = capsule->radius; + float radiusSq = radius * radius; + + Vector3 correction = {0}; + float penetration = 0.0f; + + if (bestDistSq > 1e-12f) { + if (bestDistSq >= radiusSq) return false; + + float dist = sqrtf(bestDistSq); + penetration = radius - dist; + correction = Vector3Scale(bestDelta, penetration / dist); + } + else { + float segMinX = fminf(start.x, end.x); + float segMaxX = fmaxf(start.x, end.x); + float segMinY = fminf(start.y, end.y); + float segMaxY = fmaxf(start.y, end.y); + float segMinZ = fminf(start.z, end.z); + float segMaxZ = fmaxf(start.z, end.z); + + penetration = segMaxX - box.min.x + radius; + correction = (Vector3){-penetration, 0.0f, 0.0f}; + +#define TRY_FACE(pen, corr) do { \ + float p = (pen); \ + if (p < penetration) { \ + penetration = p; \ + correction = (corr); \ + } \ + } while (0) + + TRY_FACE(box.max.x - segMinX + radius, ((Vector3){ penetration, 0.0f, 0.0f })); + TRY_FACE(segMaxY - box.min.y + radius, ((Vector3){ 0.0f, -penetration, 0.0f })); + TRY_FACE(box.max.y - segMinY + radius, ((Vector3){ 0.0f, penetration, 0.0f })); + TRY_FACE(segMaxZ - box.min.z + radius, ((Vector3){ 0.0f, 0.0f, -penetration })); + TRY_FACE(box.max.z - segMinZ + radius, ((Vector3){ 0.0f, 0.0f, penetration })); + +#undef TRY_FACE + } + + capsule->start = Vector3Add(start, correction); + capsule->end = Vector3Add(end, correction); + + if (outPenetration) *outPenetration = penetration; + return true; +} + +bool R3D_CheckSphereSupportBoundingBox(Vector3 center, float radius, Vector3 direction, float distance, R3D_BoundingBox box, RayCollision* outHit) +{ + RayCollision hit = R3D_RaycastBoundingBox((Ray) {center, direction}, box); + bool supported = hit.hit && hit.distance <= (radius + distance); + if (outHit) *outHit = hit; + return supported; +} + +bool R3D_CheckSphereSupportMesh(Vector3 center, float radius, Vector3 direction, float distance, R3D_MeshData mesh, Matrix transform, RayCollision* outHit) +{ + RayCollision hit = R3D_RaycastMesh((Ray) {center, direction}, mesh, transform); + bool supported = hit.hit && hit.distance <= (radius + distance); + if (outHit) *outHit = hit; + return supported; +} + +bool R3D_CheckCapsuleSupportBoundingBox(R3D_Capsule capsule, Vector3 direction, float distance, R3D_BoundingBox box, RayCollision* outHit) +{ + Vector3 dir = Vector3Normalize(direction); + Vector3 axis = Vector3Subtract(capsule.end, capsule.start); + Vector3 base = Vector3DotProduct(axis, dir) > 0.0f ? capsule.end : capsule.start; + Vector3 origin = Vector3Add(base, Vector3Scale(dir, capsule.radius)); + RayCollision hit = R3D_RaycastBoundingBox((Ray) { origin, dir }, box); + bool supported = hit.hit && hit.distance <= distance; + if (outHit) *outHit = hit; + return supported; +} + +bool R3D_CheckCapsuleSupportMesh(R3D_Capsule capsule, Vector3 direction, float distance, R3D_MeshData mesh, Matrix transform, RayCollision* outHit) +{ + Vector3 dir = Vector3Normalize(direction); + Vector3 axis = Vector3Subtract(capsule.end, capsule.start); + Vector3 base = Vector3DotProduct(axis, dir) > 0.0f ? capsule.end : capsule.start; + Vector3 origin = Vector3Add(base, Vector3Scale(dir, capsule.radius)); + RayCollision hit = R3D_RaycastMesh((Ray) { origin, dir }, mesh, transform); + bool supported = hit.hit && hit.distance <= distance; + if (outHit) *outHit = hit; + return supported; +} + +R3D_SweepCollision R3D_SweepSpherePoint(Vector3 center, float radius, Vector3 velocity, Vector3 point) +{ + R3D_SweepCollision result = {0}; + + Vector3 m = Vector3Subtract(center, point); + float c = Vector3DotProduct(m, m) - radius * radius; + + if (c <= 0.0f) { + result.hit = true; + result.time = 0.0f; + result.point = Vector3Add(point, Vector3Scale(Vector3Normalize(m), radius)); + result.normal = Vector3Normalize(m); + return result; + } + + float a = Vector3DotProduct(velocity, velocity); + float b = Vector3DotProduct(m, velocity); + float discr = b * b - a * c; + + if (discr < 0.0f) return result; + + float t = (-b - sqrtf(discr)) / a; + if (t < 0.0f || t > 1.0f) return result; + + Vector3 hit = Vector3Add(center, Vector3Scale(velocity, t)); + + result.hit = true; + result.time = t; + result.point = hit; + result.normal = Vector3Normalize(Vector3Subtract(hit, point)); + return result; +} + +R3D_SweepCollision R3D_SweepSphereSegment(Vector3 center, float radius, Vector3 velocity, Vector3 a, Vector3 b) +{ + R3D_SweepCollision result = {0}; + + Vector3 d = Vector3Subtract(b, a); + Vector3 m = Vector3Subtract(center, a); + + float dd = Vector3DotProduct(d, d); + float md = Vector3DotProduct(m, d); + float nd = Vector3DotProduct(velocity, d); + + float a0 = dd * Vector3DotProduct(velocity, velocity) - nd * nd; + float b0 = dd * Vector3DotProduct(m, velocity) - md * nd; + float c0 = dd * (Vector3DotProduct(m, m) - radius * radius) - md * md; + + if (fabsf(a0) < 1e-8f) return result; + + float discr = b0 * b0 - a0 * c0; + if (discr < 0.0f) return result; + + float t = (-b0 - sqrtf(discr)) / a0; + if (t < 0.0f || t > 1.0f) return result; + + float s = (md + t * nd) / dd; + if (s < 0.0f || s > 1.0f) return result; + + Vector3 hit = Vector3Add(center, Vector3Scale(velocity, t)); + Vector3 closest = Vector3Add(a, Vector3Scale(d, s)); + + result.hit = true; + result.time = t; + result.point = hit; + result.normal = Vector3Normalize(Vector3Subtract(hit, closest)); + + return result; +} + +R3D_SweepCollision R3D_SweepSphereTrianglePlane(Vector3 center, float radius, Vector3 velocity, Vector3 a, Vector3 b, Vector3 c) +{ + R3D_SweepCollision result = {0}; + + Vector3 ab = Vector3Subtract(b, a); + Vector3 ac = Vector3Subtract(c, a); + Vector3 normal = Vector3Normalize(Vector3CrossProduct(ab, ac)); + + float dist = Vector3DotProduct(Vector3Subtract(center, a), normal); + float denom = Vector3DotProduct(velocity, normal); + + // Moving away or parallel + if (denom >= 0.0f) return result; + + float t = (radius - dist) / denom; + if (t < 0.0f || t > 1.0f) return result; + + Vector3 hitPoint = Vector3Add(center, Vector3Scale(velocity, t)); + Vector3 projected = Vector3Subtract(hitPoint, Vector3Scale(normal, radius)); + + Vector3 closest = R3D_ClosestPointOnTriangle(projected, a, b, c); + float d2 = Vector3LengthSqr(Vector3Subtract(projected, closest)); + + if (d2 > 1e-6f) return result; + + result.hit = true; + result.time = t; + result.point = hitPoint; + result.normal = normal; + + return result; +} + +R3D_SweepCollision R3D_SweepSphereTriangle(Vector3 center, float radius, Vector3 velocity, Vector3 a, Vector3 b, Vector3 c) +{ + R3D_SweepCollision result = {0}; + result.time = 1.0f; + + R3D_SweepCollision faceHit = R3D_SweepSphereTrianglePlane(center, radius, velocity, a, b, c); + if (faceHit.hit && faceHit.time < result.time) { + if (faceHit.time == 0.0f) return faceHit; + result = faceHit; + } + + Vector3 edges[3][2] = {{a, b}, {b, c}, {c, a}}; + for (int i = 0; i < 3; i++) { + R3D_SweepCollision edgeHit = R3D_SweepSphereSegment(center, radius, velocity, edges[i][0], edges[i][1]); + if (edgeHit.hit && edgeHit.time < result.time) { + if (edgeHit.time == 0.0f) return edgeHit; + result = edgeHit; + } + } + + Vector3 verts[3] = {a, b, c}; + for (int i = 0; i < 3; i++) { + R3D_SweepCollision vertHit = R3D_SweepSpherePoint(center, radius, velocity, verts[i]); + if (vertHit.hit && vertHit.time < result.time) { + if (vertHit.time == 0.0f) return vertHit; + result = vertHit; + } + } + + return result; +} + +R3D_SweepCollision R3D_SweepSphereBoundingBox(Vector3 center, float radius, Vector3 velocity, BoundingBox box) +{ + R3D_SweepCollision collision = {0}; + + float velocityLength = Vector3Length(velocity); + if (velocityLength < 1e-6f) return collision; + + BoundingBox expandedBox = { + Vector3Subtract(box.min, (Vector3){radius, radius, radius}), + Vector3Add(box.max, (Vector3){radius, radius, radius}) + }; + + Ray ray = {center, Vector3Scale(velocity, 1.0f / velocityLength)}; + RayCollision hit = GetRayCollisionBox(ray, expandedBox); + + if (hit.hit && hit.distance <= velocityLength) { + collision.hit = true; + collision.time = hit.distance / velocityLength; + collision.point = hit.point; + collision.normal = hit.normal; + } + + return collision; +} + +R3D_SweepCollision R3D_SweepSphereMesh(Vector3 center, float radius, Vector3 velocity, R3D_MeshData mesh, Matrix transform) +{ + R3D_SweepCollision result = {0}; + result.time = 1.0f; + + bool useIndices = (mesh.indices != NULL); + int triangleCount = useIndices ? (mesh.indexCount / 3) : (mesh.vertexCount / 3); + + for (int i = 0; i < triangleCount; i++) + { + Vector3 v0, v1, v2; + + if (useIndices) { + v0 = mesh.vertices[mesh.indices[i * 3 ]].position; + v1 = mesh.vertices[mesh.indices[i * 3 + 1]].position; + v2 = mesh.vertices[mesh.indices[i * 3 + 2]].position; + } + else { + v0 = mesh.vertices[i * 3 ].position; + v1 = mesh.vertices[i * 3 + 1].position; + v2 = mesh.vertices[i * 3 + 2].position; + } + + Vector3 a = r3d_vector3_transform(v0, &transform); + Vector3 b = r3d_vector3_transform(v1, &transform); + Vector3 c = r3d_vector3_transform(v2, &transform); + + R3D_SweepCollision hit = R3D_SweepSphereTriangle(center, radius, velocity, a, b, c); + if (hit.hit && hit.time < result.time) result = hit; + } + + return result; +} + +R3D_SweepCollision R3D_SweepCapsuleBoundingBox(R3D_Capsule capsule, Vector3 velocity, BoundingBox box) +{ + R3D_SweepCollision collision = {0}; + + float velocityLength = Vector3Length(velocity); + if (velocityLength < 1e-6f) return collision; + + BoundingBox expandedBox = { + Vector3Subtract(box.min, (Vector3){capsule.radius, capsule.radius, capsule.radius}), + Vector3Add(box.max, (Vector3){capsule.radius, capsule.radius, capsule.radius}) + }; + + Vector3 velocityDir = Vector3Scale(velocity, 1.0f / velocityLength); + Vector3 capsuleAxis = Vector3Subtract(capsule.end, capsule.start); + + RayCollision bestHit = {0}; + bestHit.distance = FLT_MAX; + bool foundHit = false; + + const int samples = 3; + for (int i = 0; i < samples; i++) + { + float t = (float)i / (float)(samples - 1); + Vector3 samplePoint = Vector3Add(capsule.start, Vector3Scale(capsuleAxis, t)); + + Ray ray = {samplePoint, velocityDir}; + RayCollision hit = GetRayCollisionBox(ray, expandedBox); + + if (hit.hit && hit.distance <= velocityLength && hit.distance < bestHit.distance) { + bestHit = hit; + foundHit = true; + } + } + + if (foundHit) { + collision.hit = true; + collision.time = bestHit.distance / velocityLength; + collision.point = bestHit.point; + collision.normal = bestHit.normal; + } + + return collision; +} + +R3D_SweepCollision R3D_SweepCapsuleMesh(R3D_Capsule capsule, Vector3 velocity, R3D_MeshData mesh, Matrix transform) +{ + R3D_SweepCollision result = {0}; + result.time = 1.0f; + + bool useIndices = (mesh.indices != NULL); + int triangleCount = useIndices ? (mesh.indexCount / 3) : (mesh.vertexCount / 3); + + for (int i = 0; i < triangleCount; i++) + { + Vector3 v0, v1, v2; + + if (useIndices) { + v0 = mesh.vertices[mesh.indices[i * 3 ]].position; + v1 = mesh.vertices[mesh.indices[i * 3 + 1]].position; + v2 = mesh.vertices[mesh.indices[i * 3 + 2]].position; + } + else { + v0 = mesh.vertices[i * 3 ].position; + v1 = mesh.vertices[i * 3 + 1].position; + v2 = mesh.vertices[i * 3 + 2].position; + } + + Vector3 a = r3d_vector3_transform(v0, &transform); + Vector3 b = r3d_vector3_transform(v1, &transform); + Vector3 c = r3d_vector3_transform(v2, &transform); + + // Face plane test + R3D_SweepCollision faceHit = R3D_SweepSphereTrianglePlane(capsule.start, capsule.radius, velocity, a, b, c); + if (faceHit.hit && faceHit.time < result.time) result = faceHit; + + faceHit = R3D_SweepSphereTrianglePlane(capsule.end, capsule.radius, velocity, a, b, c); + if (faceHit.hit && faceHit.time < result.time) result = faceHit; + + // Segment (cylindre) + R3D_SweepCollision segHit = R3D_SweepSphereSegment(capsule.start, capsule.radius, velocity, a, b); + if (segHit.hit && segHit.time < result.time) result = segHit; + + segHit = R3D_SweepSphereSegment(capsule.start, capsule.radius, velocity, b, c); + if (segHit.hit && segHit.time < result.time) result = segHit; + + segHit = R3D_SweepSphereSegment(capsule.start, capsule.radius, velocity, c, a); + if (segHit.hit && segHit.time < result.time) result = segHit; + + // Vertices (start) + R3D_SweepCollision vertHit = R3D_SweepSpherePoint(capsule.start, capsule.radius, velocity, a); + if (vertHit.hit && vertHit.time < result.time) result = vertHit; + + vertHit = R3D_SweepSpherePoint(capsule.start, capsule.radius, velocity, b); + if (vertHit.hit && vertHit.time < result.time) result = vertHit; + + vertHit = R3D_SweepSpherePoint(capsule.start, capsule.radius, velocity, c); + if (vertHit.hit && vertHit.time < result.time) result = vertHit; + + // Vertices (end) + vertHit = R3D_SweepSpherePoint(capsule.end, capsule.radius, velocity, a); + if (vertHit.hit && vertHit.time < result.time) result = vertHit; + + vertHit = R3D_SweepSpherePoint(capsule.end, capsule.radius, velocity, b); + if (vertHit.hit && vertHit.time < result.time) result = vertHit; + + vertHit = R3D_SweepSpherePoint(capsule.end, capsule.radius, velocity, c); + if (vertHit.hit && vertHit.time < result.time) result = vertHit; + } + + return result; +} + +``` + +`src/r3d_lighting.c`: + +```c +/* r3d_lighting.h -- R3D Lighting Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include + +#include "./modules/r3d_light.h" + +// ======================================== +// HELPER MACROS +// ======================================== + +#define GET_LIGHT_OR_RETURN(var_name, id, ...) \ + r3d_light_t* var_name; \ + do { \ + var_name = r3d_light_get(id); \ + if (var_name == NULL) { \ + R3D_TRACELOG(LOG_ERROR, "Invalid light [ID %i] given to '%s'", id, __func__); \ + return __VA_ARGS__; \ + } \ + } while(0) + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Light R3D_CreateLight(R3D_LightType type) +{ + return r3d_light_new(type); +} + +void R3D_DestroyLight(R3D_Light id) +{ + r3d_light_delete(id); +} + +bool R3D_IsLightExist(R3D_Light id) +{ + return r3d_light_is_valid(id); +} + +R3D_LightType R3D_GetLightType(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return light->type; +} + +bool R3D_IsLightActive(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, false); + return light->enabled; +} + +void R3D_ToggleLight(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id); + light->enabled = !light->enabled; + + if (light->enabled && light->shadowLayer >= 0) { + light->state.shadowShouldBeUpdated = true; + } +} + +void R3D_SetLightActive(R3D_Light id, bool active) +{ + GET_LIGHT_OR_RETURN(light, id); + + if (light->enabled == active) { + return; + } + + if (active && light->shadowLayer >= 0) { + light->state.shadowShouldBeUpdated = true; + } + + light->enabled = active; +} + +Color R3D_GetLightColor(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, BLANK); + return (Color) { + (uint8_t)Clamp(light->color.x * 255, 0, 255), + (uint8_t)Clamp(light->color.y * 255, 0, 255), + (uint8_t)Clamp(light->color.z * 255, 0, 255), + 255 + }; +} + +Vector3 R3D_GetLightColorV(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, (Vector3) {0}); + return light->color; +} + +void R3D_SetLightColor(R3D_Light id, Color color) +{ + GET_LIGHT_OR_RETURN(light, id); + light->color.x = color.r / 255.0f; + light->color.y = color.g / 255.0f; + light->color.z = color.b / 255.0f; +} + +void R3D_SetLightColorV(R3D_Light id, Vector3 color) +{ + GET_LIGHT_OR_RETURN(light, id); + light->color = color; +} + +Vector3 R3D_GetLightPosition(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, (Vector3) {0}); + return light->position; +} + +void R3D_SetLightPosition(R3D_Light id, Vector3 position) +{ + GET_LIGHT_OR_RETURN(light, id); + + if (light->type == R3D_LIGHT_DIR) { + R3D_TRACELOG(LOG_WARNING, "Can't set position for light [ID %i]; it's directional and position is set automatically", id); + return; + } + + light->state.matrixShouldBeUpdated = true; + light->position = position; +} + +Vector3 R3D_GetLightDirection(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, (Vector3) {0}); + return light->direction; +} + +void R3D_SetLightDirection(R3D_Light id, Vector3 direction) +{ + GET_LIGHT_OR_RETURN(light, id); + + if (light->type == R3D_LIGHT_OMNI) { + R3D_TRACELOG(LOG_WARNING, "Can't set direction for light [ID %i]; it's omni-directional and doesn't have a direction", id); + return; + } + + light->state.matrixShouldBeUpdated = true; + light->direction = Vector3Normalize(direction); +} + +void R3D_LightLookAt(R3D_Light id, Vector3 position, Vector3 target) +{ + GET_LIGHT_OR_RETURN(light, id); + + if (light->type != R3D_LIGHT_OMNI) { + light->state.matrixShouldBeUpdated = true; + light->direction = Vector3Normalize(Vector3Subtract(target, position)); + } + + if (light->type != R3D_LIGHT_DIR) { + light->state.matrixShouldBeUpdated = true; + light->position = position; + } +} + +float R3D_GetLightEnergy(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return light->energy; +} + +void R3D_SetLightEnergy(R3D_Light id, float energy) +{ + GET_LIGHT_OR_RETURN(light, id); + light->energy = energy; +} + +float R3D_GetLightSpecular(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return light->specular; +} + +void R3D_SetLightSpecular(R3D_Light id, float specular) +{ + GET_LIGHT_OR_RETURN(light, id); + light->specular = specular; +} + +float R3D_GetLightRange(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return light->range; +} + +void R3D_SetLightRange(R3D_Light id, float range) +{ + GET_LIGHT_OR_RETURN(light, id); + + light->state.matrixShouldBeUpdated = true; + light->range = range; +} + +float R3D_GetLightAttenuation(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return 1.0f / light->attenuation; +} + +void R3D_SetLightAttenuation(R3D_Light id, float attenuation) +{ + GET_LIGHT_OR_RETURN(light, id); + if (light->type == R3D_LIGHT_DIR) { + R3D_TRACELOG(LOG_WARNING, "Can't set attenuation for light [ID %i]; it's directional and doesn't have attenuation", id); + return; + } + light->attenuation = (attenuation > 1e-4f) ? 1.0f / attenuation : 10000.0f; +} + +float R3D_GetLightInnerCutOff(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return acosf(light->innerCutOff) * RAD2DEG; +} + +void R3D_SetLightInnerCutOff(R3D_Light id, float degrees) +{ + GET_LIGHT_OR_RETURN(light, id); + if (light->type == R3D_LIGHT_DIR || light->type == R3D_LIGHT_OMNI) { + R3D_TRACELOG(LOG_WARNING, "Can't set inner cutoff for light [ID %i]; it's directional or omni and doesn't have angle attenuation", id); + return; + } + light->innerCutOff = cosf(degrees * DEG2RAD); +} + +float R3D_GetLightOuterCutOff(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return acosf(light->outerCutOff) * RAD2DEG; +} + +void R3D_SetLightOuterCutOff(R3D_Light id, float degrees) +{ + GET_LIGHT_OR_RETURN(light, id); + + if (light->type == R3D_LIGHT_DIR || light->type == R3D_LIGHT_OMNI) { + R3D_TRACELOG(LOG_WARNING, "Can't set outer cutoff for light [ID %i]; it's directional or omni and doesn't have angle attenuation", id); + return; + } + + light->state.matrixShouldBeUpdated = true; + light->outerCutOff = cosf(degrees * DEG2RAD); +} + +void R3D_EnableShadow(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id); + + if (!r3d_light_enable_shadows(light)) { + R3D_TRACELOG(LOG_WARNING, "Failed to enable shadows for light [ID %i]", id); + } +} + +void R3D_DisableShadow(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id); + r3d_light_disable_shadows(light); +} + +bool R3D_IsShadowEnabled(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, false); + return light->shadowLayer >= 0; +} + +R3D_ShadowUpdateMode R3D_GetShadowUpdateMode(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return light->state.shadowUpdate; +} + +void R3D_SetShadowUpdateMode(R3D_Light id, R3D_ShadowUpdateMode mode) +{ + GET_LIGHT_OR_RETURN(light, id); + light->state.shadowUpdate = mode; +} + +int R3D_GetShadowUpdateFrequency(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return (int)(light->state.shadowFrequencySec * 1000); +} + +void R3D_SetShadowUpdateFrequency(R3D_Light id, int msec) +{ + GET_LIGHT_OR_RETURN(light, id); + light->state.shadowFrequencySec = (float)msec / 1000; +} + +void R3D_UpdateShadowMap(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id); + light->state.shadowShouldBeUpdated = true; +} + +float R3D_GetShadowSoftness(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return light->shadowSoftness * R3D_LIGHT_SHADOW_SIZE[light->type]; +} + +void R3D_SetShadowSoftness(R3D_Light id, float softness) +{ + GET_LIGHT_OR_RETURN(light, id); + light->shadowSoftness = softness / R3D_LIGHT_SHADOW_SIZE[light->type]; +} + +float R3D_GetShadowOpacity(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0.0f); + return light->shadowOpacity; +} + +void R3D_SetShadowOpacity(R3D_Light id, float opacity) +{ + GET_LIGHT_OR_RETURN(light, id); + light->shadowOpacity = opacity; +} + +float R3D_GetShadowDepthBias(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return light->shadowDepthBias; +} + +void R3D_SetShadowDepthBias(R3D_Light id, float value) +{ + GET_LIGHT_OR_RETURN(light, id); + light->shadowDepthBias = value; +} + +float R3D_GetShadowSlopeBias(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return light->shadowSlopeBias; +} + +void R3D_SetShadowSlopeBias(R3D_Light id, float value) +{ + GET_LIGHT_OR_RETURN(light, id); + light->shadowSlopeBias = value; +} + +R3D_Layer R3D_GetShadowCasterMask(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, 0); + return light->casterMask; +} + +void R3D_SetShadowCasterMask(R3D_Light id, R3D_Layer cullMask) +{ + GET_LIGHT_OR_RETURN(light, id); + light->casterMask = cullMask; +} + +void R3D_EnableShadowCasterLayers(R3D_Light id, R3D_Layer layerMask) +{ + GET_LIGHT_OR_RETURN(light, id); + light->casterMask |= layerMask; +} + +void R3D_DisableShadowCasterLayers(R3D_Light id, R3D_Layer layerMask) +{ + GET_LIGHT_OR_RETURN(light, id); + light->casterMask &= ~layerMask; +} + +void R3D_ToggleShadowCasterLayers(R3D_Light id, R3D_Layer layerMask) +{ + GET_LIGHT_OR_RETURN(light, id); + light->casterMask ^= layerMask; +} + +bool R3D_IsShadowCasterLayerVisible(R3D_Light id, R3D_Layer layerMask) +{ + GET_LIGHT_OR_RETURN(light, id, false); + return (light->casterMask & layerMask) != 0; +} + +BoundingBox R3D_GetLightBoundingBox(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id, (BoundingBox) {0}); + return light->aabb; +} + +void R3D_DrawLightShape(R3D_Light id) +{ + GET_LIGHT_OR_RETURN(light, id); + + if (light->type == R3D_LIGHT_DIR) { + return; + } + + Color color = { + (uint8_t)(light->color.x * 255), + (uint8_t)(light->color.y * 255), + (uint8_t)(light->color.z * 255), + 100 + }; + + DrawSphereEx(light->position, 0.25f, 4, 8, color); + + if (light->type == R3D_LIGHT_SPOT) + { + // Calculation of the circle radius (bottom of the spotlight) and the position of the base + float radius = fabsf(light->range * light->outerCutOff); + Vector3 spotDir = R3D_GetLightDirection(id); + Vector3 basePos = Vector3Add(light->position, Vector3Scale(spotDir, light->range)); + + // Calculation of an orthonormal reference frame for the circle plane + Vector3 right = {0}; + if (fabsf(spotDir.x) < fabsf(spotDir.y) && fabsf(spotDir.x) < fabsf(spotDir.z)) { + right = (Vector3) {0, -spotDir.z, spotDir.y}; + } + else if (fabsf(spotDir.y) < fabsf(spotDir.z)) { + right = (Vector3) {-spotDir.z, 0, spotDir.x}; + } + else { + right = (Vector3) {-spotDir.y, spotDir.x, 0}; + } + + right = Vector3Normalize(right); + Vector3 up = Vector3CrossProduct(spotDir, right); + up = Vector3Normalize(up); + + // Calculation of the 4 cardinal points on the circle (angles 0, 90, 180, and 270 degrees) + Vector3 circlePoints[4]; + float angles[4] = { 0, DEG2RAD * 90.0f, DEG2RAD * 180.0f, DEG2RAD * 270.0f }; + for (int i = 0; i < 4; i++) { + circlePoints[i].x = basePos.x + radius * (cosf(angles[i]) * right.x + sinf(angles[i]) * up.x); + circlePoints[i].y = basePos.y + radius * (cosf(angles[i]) * right.y + sinf(angles[i]) * up.y); + circlePoints[i].z = basePos.z + radius * (cosf(angles[i]) * right.z + sinf(angles[i]) * up.z); + } + + // Draw 4 lines from the light position to each of the 4 points on the circle + rlBegin(RL_LINES); + { + rlColor4ub(color.r, color.g, color.b, color.a); + for (int i = 0; i < 4; i++) { + rlVertex3f(light->position.x, light->position.y, light->position.z); + rlVertex3f(circlePoints[i].x, circlePoints[i].y, circlePoints[i].z); + } + } + rlEnd(); + + // Draw the circle at the base with 32 segments + rlBegin(RL_LINES); + { + rlColor4ub(color.r, color.g, color.b, color.a); + + const int SEGMENTS = 32; + const float STEP = 360.0f / SEGMENTS; + + for (int i = 0; i < SEGMENTS; i++) { + float angle1 = i * STEP * DEG2RAD; + float angle2 = (i + 1) * STEP * DEG2RAD; + + Vector3 p1 = { + basePos.x + radius * (cosf(angle1) * right.x + sinf(angle1) * up.x), + basePos.y + radius * (cosf(angle1) * right.y + sinf(angle1) * up.y), + basePos.z + radius * (cosf(angle1) * right.z + sinf(angle1) * up.z) + }; + Vector3 p2 = { + basePos.x + radius * (cosf(angle2) * right.x + sinf(angle2) * up.x), + basePos.y + radius * (cosf(angle2) * right.y + sinf(angle2) * up.y), + basePos.z + radius * (cosf(angle2) * right.z + sinf(angle2) * up.z) + }; + + rlVertex3f(p1.x, p1.y, p1.z); + rlVertex3f(p2.x, p2.y, p2.z); + } + } + rlEnd(); + } + else if (light->type == R3D_LIGHT_OMNI) + { + const int SEGMENTS = 32; + const float STEP = 360.0f / SEGMENTS; + + rlBegin(RL_LINES); + { + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i < SEGMENTS; i++) { + float angle1 = i * STEP * DEG2RAD; + float angle2 = (i + 1) * STEP * DEG2RAD; + + // Circle around X-axis (YZ plane) + rlVertex3f(light->position.x, light->position.y + light->range * cosf(angle1), light->position.z + light->range * sinf(angle1)); + rlVertex3f(light->position.x, light->position.y + light->range * cosf(angle2), light->position.z + light->range * sinf(angle2)); + + // Circle around Y-axis (XZ plane) + rlVertex3f(light->position.x + light->range * cosf(angle1), light->position.y, light->position.z + light->range * sinf(angle1)); + rlVertex3f(light->position.x + light->range * cosf(angle2), light->position.y, light->position.z + light->range * sinf(angle2)); + + // Circle around Z-axis (XY plane) + rlVertex3f(light->position.x + light->range * cosf(angle1), light->position.y + light->range * sinf(angle1), light->position.z); + rlVertex3f(light->position.x + light->range * cosf(angle2), light->position.y + light->range * sinf(angle2), light->position.z); + } + } + rlEnd(); + } +} + +``` + +`src/r3d_material.c`: + +```c +/* r3d_material.c -- R3D Material Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include + +#ifdef R3D_SUPPORT_ASSIMP +# include "./importer/r3d_importer_internal.h" +#endif + +#include "./r3d_core_state.h" + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Material R3D_GetDefaultMaterial(void) +{ + return R3D.material; +} + +void R3D_SetDefaultMaterial(R3D_Material material) +{ + R3D.material = material; +} + +R3D_Material* R3D_LoadMaterials(const char* filePath, int* materialCount) +{ + R3D_Material* materials = NULL; + +#ifdef R3D_SUPPORT_ASSIMP + R3D_Importer* importer = R3D_LoadImporter(filePath, 0); + if (importer == NULL) return materials; + + materials = R3D_LoadMaterialsFromImporter(importer, materialCount); + R3D_UnloadImporter(importer); + +#else + R3D_TRACELOG(LOG_WARNING, "Cannot load '%s': built without Assimp support", filePath); + +#endif // R3D_SUPPORT_ASSIMP + + return materials; +} + +R3D_Material* R3D_LoadMaterialsFromMemory(const void* data, unsigned int size, const char* hint, int* materialCount) +{ + R3D_Material* materials = NULL; + +#ifdef R3D_SUPPORT_ASSIMP + R3D_Importer* importer = R3D_LoadImporterFromMemory(data, size, hint, 0); + if (importer == NULL) return materials; + + materials = R3D_LoadMaterialsFromImporter(importer, materialCount); + R3D_UnloadImporter(importer); + +#else + if (hint && hint[0] != '\0') { + R3D_TRACELOG(LOG_WARNING, "Cannot load '%s' from memory: built without Assimp support", hint); + } + else { + R3D_TRACELOG(LOG_WARNING, "Cannot load asset from memory: built without Assimp support"); + } + +#endif // R3D_SUPPORT_ASSIMP + + return materials; +} + +R3D_Material* R3D_LoadMaterialsFromImporter(const R3D_Importer* importer, int* materialCount) +{ + R3D_Material* materials = NULL; + +#ifdef R3D_SUPPORT_ASSIMP + if (importer == NULL) { + R3D_TRACELOG(LOG_WARNING, "Cannot load materials from importer: NULL importer"); + return materials; + } + + r3d_importer_texture_cache_t* textureCache = r3d_importer_load_texture_cache( + importer, R3D.colorSpace, R3D.textureFilter); + + //if (textureCache == NULL) { + // R3D_TRACELOG(LOG_INFO, "The material will not have textures"); + //} + + if (!r3d_importer_load_materials(importer, &materials, materialCount, textureCache)) { + r3d_importer_unload_texture_cache(textureCache, true); + return NULL; + } + + r3d_importer_unload_texture_cache(textureCache, false); + +#else + R3D_TRACELOG(LOG_WARNING, "Cannot load materials from importer: built without Assimp support"); + +#endif // R3D_SUPPORT_ASSIMP + + return materials; +} + +void R3D_UnloadMaterial(R3D_Material material) +{ + R3D_UnloadAlbedoMap(material.albedo); + R3D_UnloadEmissionMap(material.emission); + R3D_UnloadNormalMap(material.normal); + R3D_UnloadOrmMap(material.orm); +} + +R3D_AlbedoMap R3D_LoadAlbedoMap(const char* fileName, Color color) +{ + R3D_AlbedoMap map = {0}; + map.texture = R3D_LoadTexture(fileName, true); + map.color = color; + return map; +} + +R3D_AlbedoMap R3D_LoadAlbedoMapFromMemory(const char* fileType, const void* fileData, int dataSize, Color color) +{ + R3D_AlbedoMap map = {0}; + map.texture = R3D_LoadTextureFromMemory(fileType, fileData, dataSize, true); + map.color = color; + return map; +} + +void R3D_UnloadAlbedoMap(R3D_AlbedoMap map) +{ + R3D_UnloadTexture(map.texture); +} + +R3D_EmissionMap R3D_LoadEmissionMap(const char* fileName, Color color, float energy) +{ + R3D_EmissionMap map = {0}; + map.texture = R3D_LoadTexture(fileName, true); + map.color = color; + map.energy = energy; + return map; +} + +R3D_EmissionMap R3D_LoadEmissionMapFromMemory(const char* fileType, const void* fileData, int dataSize, Color color, float energy) +{ + R3D_EmissionMap map = {0}; + map.texture = R3D_LoadTextureFromMemory(fileType, fileData, dataSize, true); + map.color = color; + map.energy = energy; + return map; +} + +void R3D_UnloadEmissionMap(R3D_EmissionMap map) +{ + R3D_UnloadTexture(map.texture); +} + +R3D_NormalMap R3D_LoadNormalMap(const char* fileName, float scale) +{ + R3D_NormalMap map = {0}; + map.texture = R3D_LoadTexture(fileName, false); + map.scale = scale; + return map; +} + +R3D_NormalMap R3D_LoadNormalMapFromMemory(const char* fileType, const void* fileData, int dataSize, float scale) +{ + R3D_NormalMap map = {0}; + map.texture = R3D_LoadTextureFromMemory(fileType, fileData, dataSize, false); + map.scale = scale; + return map; +} + +void R3D_UnloadNormalMap(R3D_NormalMap map) +{ + R3D_UnloadTexture(map.texture); +} + +R3D_OrmMap R3D_LoadOrmMap(const char* fileName, float occlusion, float roughness, float metalness, float specular) +{ + R3D_OrmMap map = {0}; + map.texture = R3D_LoadTexture(fileName, false); + map.occlusion = occlusion; + map.roughness = roughness; + map.metalness = metalness; + map.specular = specular; + return map; +} + +R3D_OrmMap R3D_LoadOrmMapFromMemory(const char* fileType, const void* fileData, int dataSize, + float occlusion, float roughness, float metalness, float specular) +{ + R3D_OrmMap map = {0}; + map.texture = R3D_LoadTextureFromMemory(fileType, fileData, dataSize, false); + map.occlusion = occlusion; + map.roughness = roughness; + map.metalness = metalness; + map.specular = specular; + return map; +} + +void R3D_UnloadOrmMap(R3D_OrmMap map) +{ + R3D_UnloadTexture(map.texture); +} + +``` + +`src/r3d_mesh.c`: + +```c +/* r3d_mesh.h -- R3D Mesh Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#include "./modules/r3d_render.h" +#include "./common/r3d_helper.h" + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Mesh R3D_LoadMesh(R3D_PrimitiveType type, R3D_MeshData data, const BoundingBox* aabb) +{ + R3D_Mesh mesh = {0}; + + if (!r3d_render_alloc_vertices(data.vertexCount, &mesh.vertexOffset)) { + R3D_TRACELOG(LOG_WARNING, "Failed to load mesh; Vertices allocation in VRAM failed"); + return mesh; + } + + if (data.indexCount > 0) { + if (!r3d_render_alloc_elements(data.indexCount, &mesh.indexOffset)) { + R3D_TRACELOG(LOG_WARNING, "Failed to load mesh; Elements allocation in VRAM failed"); + r3d_render_free_vertices(mesh.vertexOffset, data.vertexCount); + mesh.vertexOffset = 0; + return mesh; + } + } + + r3d_render_upload_vertices(mesh.vertexOffset, data.vertices, data.vertexCount); + if (data.indexCount > 0) { + r3d_render_upload_elements(mesh.indexOffset, data.indices, data.indexCount); + } + + mesh.vertexCount = mesh.vertexCapacity = data.vertexCount; + mesh.indexCount = mesh.indexCapacity = data.indexCount; + mesh.shadowCastMode = R3D_SHADOW_CAST_ON_AUTO; + mesh.layerMask = R3D_LAYER_01; + mesh.primitiveType = type; + + // Compute the bounding box, if needed + mesh.aabb = (aabb != NULL) ? *aabb + : R3D_CalculateMeshDataBoundingBox(data); + + return mesh; +} + +void R3D_UnloadMesh(R3D_Mesh mesh) +{ + if (mesh.vertexCapacity > 0) { + r3d_render_free_vertices(mesh.vertexOffset, mesh.vertexCapacity); + } + + if (mesh.indexCapacity > 0) { + r3d_render_free_elements(mesh.indexOffset, mesh.indexCapacity); + } +} + +bool R3D_IsMeshValid(R3D_Mesh mesh) +{ + return mesh.vertexCount > 0; +} + +R3D_Mesh R3D_GenMeshQuad(float width, float length, int resX, int resZ, Vector3 frontDir) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataQuad(width, length, resX, resZ, frontDir); + if (!R3D_IsMeshDataValid(data)) return mesh; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, NULL); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshPlane(float width, float length, int resX, int resZ) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataPlane(width, length, resX, resZ); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-width * 0.5f, 0.0f, -length * 0.5f}, + { width * 0.5f, 0.0f, length * 0.5f} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshPoly(int sides, float radius, Vector3 frontDir) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataPoly(sides, radius, frontDir); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-radius, 0.0f, -radius}, + { radius, 0.0f, radius} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshCube(float width, float height, float length) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataCube(width, height, length); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-width * 0.5f, -height * 0.5f, -length * 0.5f}, + { width * 0.5f, height * 0.5f, length * 0.5f} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshCubeEx(float width, float height, float length, int resX, int resY, int resZ) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataCubeEx(width, height, length, resX, resY, resZ); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-width * 0.5f, -height * 0.5f, -length * 0.5f}, + { width * 0.5f, height * 0.5f, length * 0.5f} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshSlope(float width, float height, float length, Vector3 slopeNormal) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataSlope(width, height, length, slopeNormal); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-width * 0.5f, -height * 0.5f, -length * 0.5f}, + { width * 0.5f, height * 0.5f, length * 0.5f} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshSphere(float radius, int rings, int slices) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataSphere(radius, rings, slices); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-radius, -radius, -radius}, + { radius, radius, radius} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshHemiSphere(float radius, int rings, int slices) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataHemiSphere(radius, rings, slices); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-radius, 0.0f, -radius}, + { radius, radius, radius} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshCylinder(float radius, float height, int slices) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataCylinder(radius, height, slices); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-radius, -height * 0.5f, -radius}, + { radius, height * 0.5f, radius} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshCylinderEx(float bottomRadius, float topRadius, float height, int slices, int stacks, bool bottomCap, bool topCap) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataCylinderEx(bottomRadius, topRadius, height, slices, stacks, bottomCap, topCap); + if (!R3D_IsMeshDataValid(data)) return mesh; + + float radius = MAX(bottomRadius, topRadius); + + BoundingBox aabb = { + {-radius, -height * 0.5f, -radius}, + { radius, height * 0.5f, radius} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshCapsule(float radius, float height, int rings, int slices) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataCapsule(radius, height, rings, slices); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-radius, -radius, -radius}, + { radius, height + radius, radius} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshTorus(float radius, float size, int radSeg, int sides) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataTorus(radius, size, radSeg, sides); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-radius - size, -size, -radius - size}, + { radius + size, size, radius + size} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshKnot(float radius, float size, int radSeg, int sides) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataKnot(radius, size, radSeg, sides); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-radius - size, -size, -radius - size}, + { radius + size, size, radius + size} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshHeightmap(Image heightmap, Vector3 size) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataHeightmap(heightmap, size); + if (!R3D_IsMeshDataValid(data)) return mesh; + + BoundingBox aabb = { + {-size.x * 0.5f, 0.0f, -size.z * 0.5f}, + { size.x * 0.5f, size.y, size.z * 0.5f} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +R3D_Mesh R3D_GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) +{ + R3D_Mesh mesh = {0}; + + R3D_MeshData data = R3D_GenMeshDataCubicmap(cubicmap, cubeSize); + if (!R3D_IsMeshDataValid(data)) return mesh; + + float hw = (float)cubicmap.width * cubeSize.x * 0.5f; + float hh = (float)cubicmap.height * cubeSize.z * 0.5f; + + BoundingBox aabb = { + {-hw, 0.0f, -hh}, + { hw, cubeSize.y, hh} + }; + + mesh = R3D_LoadMesh(R3D_PRIMITIVE_TRIANGLES, data, &aabb); + R3D_UnloadMeshData(data); + + return mesh; +} + +bool R3D_UpdateMesh(R3D_Mesh* mesh, R3D_MeshData data, const BoundingBox* aabb) +{ + if (!mesh) { + R3D_TRACELOG(LOG_WARNING, "Cannot update mesh; Invalid mesh instance"); + return false; + } + + if (!data.vertices || data.vertexCount <= 0) { + R3D_TRACELOG(LOG_WARNING, "Cannont update mesh; Invalid mesh data"); + return false; + } + + if (mesh->vertexCapacity < data.vertexCount) { + if (!r3d_render_realloc_vertices(&mesh->vertexOffset, &mesh->vertexCapacity, data.vertexCount, false)) { + R3D_TRACELOG(LOG_WARNING, "Cannot update mesh; Vertex reallocation failed"); + return false; + } + } + r3d_render_upload_vertices(mesh->vertexOffset, data.vertices, data.vertexCount); + + if (data.indexCount > 0) { + if (mesh->indexCapacity < data.indexCount) { + if (!r3d_render_realloc_elements(&mesh->indexOffset, &mesh->indexCapacity, data.indexCount, false)) { + R3D_TRACELOG(LOG_WARNING, "Cannot update mesh; Element reallocation failed"); + return false; + } + } + r3d_render_upload_elements(mesh->indexOffset, data.indices, data.indexCount); + } + + mesh->vertexCount = data.vertexCount; + mesh->indexCount = data.indexCount; + + mesh->aabb = (aabb != NULL) ? *aabb + : R3D_CalculateMeshDataBoundingBox(data); + + return true; +} + +``` + +`src/r3d_mesh_data.c`: + +```c +/* r3d_mesh_data.c -- R3D Mesh Data Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "./common/r3d_math.h" + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +static bool alloc_mesh(R3D_MeshData* meshData, int vertexCount, int indexCount); + +static inline uint32_t get_index(const R3D_MeshData* meshData, int i) +{ + return meshData->indexCount > 0 ? meshData->indices[i] : (uint32_t)i; +} + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_MeshData R3D_LoadMeshData(int vertexCount, int indexCount) +{ + R3D_MeshData meshData = {0}; + + if (vertexCount <= 0) { + R3D_TRACELOG(LOG_ERROR, "Invalid vertex count for mesh creation"); + return meshData; + } + + meshData.vertices = RL_CALLOC(vertexCount, sizeof(*meshData.vertices)); + if (meshData.vertices == NULL) { + R3D_TRACELOG(LOG_ERROR, "Failed to allocate memory for mesh vertices"); + return meshData; + } + meshData.vertexCapacity = vertexCount; + + if (indexCount > 0) { + meshData.indices = RL_CALLOC(indexCount, sizeof(*meshData.indices)); + if (meshData.indices == NULL) { + R3D_TRACELOG(LOG_ERROR, "Failed to allocate memory for mesh indices"); + RL_FREE(meshData.vertices); + meshData.vertexCapacity = 0; + meshData.vertices = NULL; + return meshData; + } + meshData.indexCapacity = indexCount; + } + + return meshData; +} + +void R3D_UnloadMeshData(R3D_MeshData meshData) +{ + RL_FREE(meshData.vertices); + RL_FREE(meshData.indices); +} + +bool R3D_IsMeshDataValid(R3D_MeshData meshData) +{ + return (meshData.vertices != NULL) && (meshData.vertexCount > 0); +} + +R3D_MeshData R3D_GenMeshDataQuad(float width, float length, int resX, int resZ, Vector3 frontDir) +{ + R3D_MeshData meshData = {0}; + + Vector3 normal = Vector3Normalize(frontDir); + Vector3 reference = (fabsf(normal.y) < 0.9f) ? (Vector3){0.0f, 1.0f, 0.0f} : (Vector3){1.0f, 0.0f, 0.0f}; + Vector3 tangent = Vector3Normalize(Vector3CrossProduct(normal, reference)); + Vector3 bitangent = Vector3CrossProduct(normal, tangent); + + Vector4 tangent4 = {tangent.x, tangent.y, tangent.z, 1.0f}; + float invResX = 1.0f / resX; + float invResZ = 1.0f / resZ; + int vertCountX = resX + 1; + int vertCountZ = resZ + 1; + + if (!alloc_mesh(&meshData, vertCountX * vertCountZ, resX * resZ * 6)) { + return meshData; + } + + R3D_Vertex* vertex = meshData.vertices; + for (int z = 0; z <= resZ; z++) + { + float v = z * invResZ; + float localY = (v - 0.5f) * length; + + for (int x = 0; x <= resX; x++, vertex++) + { + float u = x * invResX; + float localX = (u - 0.5f) * width; + + Vector3 position = { + localX * tangent.x + localY * bitangent.x, + localX * tangent.y + localY * bitangent.y, + localX * tangent.z + localY * bitangent.z + }; + + *vertex = R3D_MakeVertex(position, (Vector2){u, v}, normal, tangent4, WHITE); + } + } + + uint32_t* index = meshData.indices; + for (int z = 0; z < resZ; z++) + { + uint32_t rowStart = z * vertCountX; + uint32_t nextRowStart = rowStart + vertCountX; + + for (int x = 0; x < resX; x++) + { + uint32_t i0 = rowStart + x; + uint32_t i1 = i0 + 1; + uint32_t i2 = nextRowStart + x + 1; + uint32_t i3 = nextRowStart + x; + + *index++ = i0; *index++ = i1; *index++ = i2; + *index++ = i0; *index++ = i2; *index++ = i3; + } + } + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataPlane(float width, float length, int resX, int resZ) +{ + R3D_MeshData meshData = {0}; + + if (width <= 0.0f || length <= 0.0f || resX < 1 || resZ < 1) { + return meshData; + } + + int vertCountX = resX + 1; + int vertCountZ = resZ + 1; + + if (!alloc_mesh(&meshData, vertCountX * vertCountZ, resX * resZ * 6)) { + return meshData; + } + + float halfWidth = width * 0.5f; + float halfLength = length * 0.5f; + float stepX = width / resX; + float stepZ = length / resZ; + float invResX = 1.0f / resX; + float invResZ = 1.0f / resZ; + + R3D_Vertex* vertex = meshData.vertices; + for (int z = 0; z <= resZ; z++) + { + float posZ = -halfLength + z * stepZ; + float uvZ = z * invResZ; + + for (int x = 0; x <= resX; x++, vertex++) + { + float posX = -halfWidth + x * stepX; + float uvX = x * invResX; + + *vertex = R3D_MakeVertex( + (Vector3){posX, 0.0f, posZ}, + (Vector2){uvX, uvZ}, + (Vector3){0.0f, 1.0f, 0.0f}, + (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, + WHITE + ); + } + } + + uint32_t* index = meshData.indices; + for (int z = 0; z < resZ; z++) + { + uint32_t rowStart = z * vertCountX; + uint32_t nextRowStart = rowStart + vertCountX; + + for (int x = 0; x < resX; x++) + { + uint32_t i0 = rowStart + x; + uint32_t i1 = i0 + 1; + uint32_t i2 = nextRowStart + x; + uint32_t i3 = i2 + 1; + + *index++ = i0; *index++ = i2; *index++ = i1; + *index++ = i1; *index++ = i2; *index++ = i3; + } + } + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataPoly(int sides, float radius, Vector3 frontDir) +{ + R3D_MeshData meshData = {0}; + + if (sides < 3 || radius <= 0.0f) { + return meshData; + } + + if (!alloc_mesh(&meshData, sides + 1, sides * 3)) { + return meshData; + } + + Vector3 normal = Vector3Normalize(frontDir); + Vector3 reference = (fabsf(normal.y) < 0.9f) ? (Vector3){0.0f, 1.0f, 0.0f} : (Vector3){1.0f, 0.0f, 0.0f}; + Vector3 tangent = Vector3Normalize(Vector3CrossProduct(normal, reference)); + Vector3 bitangent = Vector3CrossProduct(normal, tangent); + + Vector4 tangent4 = {tangent.x, tangent.y, tangent.z, 1.0f}; + float angleStep = 2.0f * PI / sides; + + meshData.vertices[0] = R3D_MakeVertex( + (Vector3){0.0f, 0.0f, 0.0f}, + (Vector2){0.5f, 0.5f}, + normal, + tangent4, + WHITE + ); + + R3D_Vertex* vertex = &meshData.vertices[1]; + uint32_t* index = meshData.indices; + + for (int i = 0; i < sides; i++, vertex++) + { + float angle = i * angleStep; + float cosAngle = cosf(angle); + float sinAngle = sinf(angle); + + float localX = radius * cosAngle; + float localY = radius * sinAngle; + + Vector3 position = { + localX * tangent.x + localY * bitangent.x, + localX * tangent.y + localY * bitangent.y, + localX * tangent.z + localY * bitangent.z + }; + + Vector2 texcoord = { + 0.5f + 0.5f * cosAngle, + 0.5f + 0.5f * sinAngle + }; + + *vertex = R3D_MakeVertex(position, texcoord, normal, tangent4, WHITE); + + *index++ = 0; + *index++ = i + 1; + *index++ = (i + 1) % sides + 1; + } + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataCube(float width, float height, float length) +{ + R3D_MeshData meshData = {0}; + + if (width <= 0.0f || height <= 0.0f || length <= 0.0f) { + return meshData; + } + + if (!alloc_mesh(&meshData, 24, 36)) { + return meshData; + } + + float halfW = width * 0.5f; + float halfH = height * 0.5f; + float halfL = length * 0.5f; + + Vector2 uvs[4] = { + {0.0f, 0.0f}, {1.0f, 0.0f}, + {1.0f, 1.0f}, {0.0f, 1.0f} + }; + + R3D_Vertex* v = meshData.vertices; + + // Back face (+Z) + *v++ = R3D_MakeVertex((Vector3){-halfW, -halfH, halfL}, uvs[0], (Vector3){0.0f, 0.0f, 1.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){halfW, -halfH, halfL}, uvs[1], (Vector3){0.0f, 0.0f, 1.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){halfW, halfH, halfL}, uvs[2], (Vector3){0.0f, 0.0f, 1.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){-halfW, halfH, halfL}, uvs[3], (Vector3){0.0f, 0.0f, 1.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + + // Front face (-Z) + *v++ = R3D_MakeVertex((Vector3){halfW, -halfH, -halfL}, uvs[0], (Vector3){0.0f, 0.0f, -1.0f}, (Vector4){-1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){-halfW, -halfH, -halfL}, uvs[1], (Vector3){0.0f, 0.0f, -1.0f}, (Vector4){-1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){-halfW, halfH, -halfL}, uvs[2], (Vector3){0.0f, 0.0f, -1.0f}, (Vector4){-1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){halfW, halfH, -halfL}, uvs[3], (Vector3){0.0f, 0.0f, -1.0f}, (Vector4){-1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + + // Right face (+X) + *v++ = R3D_MakeVertex((Vector3){halfW, -halfH, halfL}, uvs[0], (Vector3){1.0f, 0.0f, 0.0f}, (Vector4){0.0f, 0.0f, -1.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){halfW, -halfH, -halfL}, uvs[1], (Vector3){1.0f, 0.0f, 0.0f}, (Vector4){0.0f, 0.0f, -1.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){halfW, halfH, -halfL}, uvs[2], (Vector3){1.0f, 0.0f, 0.0f}, (Vector4){0.0f, 0.0f, -1.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){halfW, halfH, halfL}, uvs[3], (Vector3){1.0f, 0.0f, 0.0f}, (Vector4){0.0f, 0.0f, -1.0f, 1.0f}, WHITE); + + // Left face (-X) + *v++ = R3D_MakeVertex((Vector3){-halfW, -halfH, -halfL}, uvs[0], (Vector3){-1.0f, 0.0f, 0.0f}, (Vector4){0.0f, 0.0f, 1.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){-halfW, -halfH, halfL}, uvs[1], (Vector3){-1.0f, 0.0f, 0.0f}, (Vector4){0.0f, 0.0f, 1.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){-halfW, halfH, halfL}, uvs[2], (Vector3){-1.0f, 0.0f, 0.0f}, (Vector4){0.0f, 0.0f, 1.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){-halfW, halfH, -halfL}, uvs[3], (Vector3){-1.0f, 0.0f, 0.0f}, (Vector4){0.0f, 0.0f, 1.0f, 1.0f}, WHITE); + + // Top face (+Y) + *v++ = R3D_MakeVertex((Vector3){-halfW, halfH, halfL}, uvs[0], (Vector3){0.0f, 1.0f, 0.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){halfW, halfH, halfL}, uvs[1], (Vector3){0.0f, 1.0f, 0.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){halfW, halfH, -halfL}, uvs[2], (Vector3){0.0f, 1.0f, 0.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){-halfW, halfH, -halfL}, uvs[3], (Vector3){0.0f, 1.0f, 0.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + + // Bottom face (-Y) + *v++ = R3D_MakeVertex((Vector3){-halfW, -halfH, -halfL}, uvs[0], (Vector3){0.0f, -1.0f, 0.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){halfW, -halfH, -halfL}, uvs[1], (Vector3){0.0f, -1.0f, 0.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){halfW, -halfH, halfL}, uvs[2], (Vector3){0.0f, -1.0f, 0.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + *v++ = R3D_MakeVertex((Vector3){-halfW, -halfH, halfL}, uvs[3], (Vector3){0.0f, -1.0f, 0.0f}, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, WHITE); + + // Indices + uint32_t* index = meshData.indices; + for (int face = 0; face < 6; face++) { + uint32_t base = face * 4; + *index++ = base; *index++ = base + 1; *index++ = base + 2; + *index++ = base + 2; *index++ = base + 3; *index++ = base; + } + + return meshData; +} + +static void gen_cube_face( + R3D_Vertex **vertexPtr, + uint32_t **indexPtr, + uint32_t *vertexOffset, + Vector3 origin, + Vector3 uAxis, + Vector3 vAxis, + float uSize, + float vSize, + int uRes, + int vRes, + Vector3 normal, + Vector3 tangent) +{ + float invU = 1.0f / (float)uRes; + float invV = 1.0f / (float)vRes; + Vector4 tangent4 = {tangent.x, tangent.y, tangent.z, 1.0f}; + + uint32_t faceVertStart = *vertexOffset; + R3D_Vertex *vertex = *vertexPtr; + uint32_t *index = *indexPtr; + + for (int v = 0; v <= vRes; v++) + { + float vt = v * invV; + float localV = (vt - 0.5f) * vSize; + + for (int u = 0; u <= uRes; u++) + { + float ut = u * invU; + float localU = (ut - 0.5f) * uSize; + + Vector3 position = { + origin.x + localU * uAxis.x + localV * vAxis.x, + origin.y + localU * uAxis.y + localV * vAxis.y, + origin.z + localU * uAxis.z + localV * vAxis.z + }; + + *vertex = R3D_MakeVertex(position, (Vector2){ut, vt}, normal, tangent4, WHITE); + + vertex++; + (*vertexOffset)++; + } + } + + for (int v = 0; v < vRes; v++) + { + uint32_t rowStart = faceVertStart + v * (uRes + 1); + uint32_t nextRowStart = rowStart + (uRes + 1); + + for (int u = 0; u < uRes; u++) + { + uint32_t i0 = rowStart + u; + uint32_t i1 = i0 + 1; + uint32_t i2 = nextRowStart + u; + uint32_t i3 = i2 + 1; + + *index++ = i0; *index++ = i1; *index++ = i2; + *index++ = i2; *index++ = i1; *index++ = i3; + } + } + + *vertexPtr = vertex; + *indexPtr = index; +} + +R3D_MeshData R3D_GenMeshDataCubeEx(float width, float height, float length, int resX, int resY, int resZ) +{ + R3D_MeshData meshData = {0}; + + if (width <= 0 || height <= 0 || length <= 0 || resX < 1 || resY < 1 || resZ < 1) { + return meshData; + } + + int vertXY = (resX + 1) * (resY + 1); + int vertXZ = (resX + 1) * (resZ + 1); + int vertYZ = (resY + 1) * (resZ + 1); + + int idxXY = resX * resY * 6; + int idxXZ = resX * resZ * 6; + int idxYZ = resY * resZ * 6; + + int totalVertices = 2 * (vertXY + vertXZ + vertYZ); + int totalIndices = 2 * (idxXY + idxXZ + idxYZ); + + if (!alloc_mesh(&meshData, totalVertices, totalIndices)) { + return meshData; + } + + float hw = width * 0.5f; + float hh = height * 0.5f; + float hl = length * 0.5f; + + R3D_Vertex *vertex = meshData.vertices; + uint32_t *index = meshData.indices; + uint32_t vertexOffset = 0; + + // +Z + gen_cube_face(&vertex, &index, &vertexOffset, + (Vector3){0, 0, hl}, (Vector3){ 1, 0, 0}, (Vector3){0, 1, 0}, + width, height, resX, resY, + (Vector3){0, 0, 1}, (Vector3){ 1, 0, 0}); + + // -Z + gen_cube_face(&vertex, &index, &vertexOffset, + (Vector3){0, 0, -hl}, (Vector3){-1, 0, 0}, (Vector3){0, 1, 0}, + width, height, resX, resY, + (Vector3){0, 0, -1}, (Vector3){-1, 0, 0}); + + // +X + gen_cube_face(&vertex, &index, &vertexOffset, + (Vector3){ hw, 0, 0}, (Vector3){0, 0,-1}, (Vector3){0, 1, 0}, + length, height, resZ, resY, + (Vector3){ 1, 0, 0}, (Vector3){0, 0,-1}); + + // -X + gen_cube_face(&vertex, &index, &vertexOffset, + (Vector3){-hw, 0, 0}, (Vector3){0, 0, 1}, (Vector3){0, 1, 0}, + length, height, resZ, resY, + (Vector3){-1, 0, 0}, (Vector3){0, 0, 1}); + + // +Y + gen_cube_face(&vertex, &index, &vertexOffset, + (Vector3){0, hh, 0}, (Vector3){1, 0, 0}, (Vector3){0, 0,-1}, + width, length, resX, resZ, + (Vector3){0, 1, 0}, (Vector3){1, 0, 0}); + + // -Y + gen_cube_face(&vertex, &index, &vertexOffset, + (Vector3){0, -hh, 0}, (Vector3){1, 0, 0}, (Vector3){0, 0, 1}, + width, length, resX, resZ, + (Vector3){0,-1, 0}, (Vector3){1, 0, 0}); + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataSlope(float width, float height, float length, Vector3 slopeNormal) +{ + R3D_MeshData meshData = {0}; + + if (width <= 0.0f || height <= 0.0f || length <= 0.0f) { + return meshData; + } + + Vector3 normal = Vector3Normalize(slopeNormal); + + float halfW = width * 0.5f; + float halfH = height * 0.5f; + float halfL = length * 0.5f; + + Vector3 corners[8] = { + {-halfW, -halfH, -halfL}, { halfW, -halfH, -halfL}, + { halfW, -halfH, halfL}, {-halfW, -halfH, halfL}, + {-halfW, halfH, -halfL}, { halfW, halfH, -halfL}, + { halfW, halfH, halfL}, {-halfW, halfH, halfL} + }; + + bool keepCorner[8]; + int keptCount = 0; + for (int i = 0; i < 8; i++) { + keepCorner[i] = (Vector3DotProduct(corners[i], normal) <= 0.0f); + keptCount += keepCorner[i]; + } + + if (keptCount == 0 || keptCount == 8) { + return meshData; + } + + static const int edges[12][2] = { + {0,1},{1,2},{2,3},{3,0}, + {4,5},{5,6},{6,7},{7,4}, + {0,4},{1,5},{2,6},{3,7} + }; + + Vector3 intersections[12]; + bool hasIntersection[12] = {0}; + int intersectionCount = 0; + + for (int i = 0; i < 12; i++) { + int c1 = edges[i][0], c2 = edges[i][1]; + if (keepCorner[c1] != keepCorner[c2]) { + Vector3 p1 = corners[c1], p2 = corners[c2]; + float d1 = Vector3DotProduct(p1, normal); + float d2 = Vector3DotProduct(p2, normal); + float t = -d1 / (d2 - d1); + intersections[i] = Vector3Lerp(p1, p2, t); + hasIntersection[i] = true; + intersectionCount++; + } + } + + if (!alloc_mesh(&meshData, 32, 48)) { + return meshData; + } + + R3D_Vertex* v = meshData.vertices; + uint32_t* idx = meshData.indices; + int vertexCount = 0; + int indexCount = 0; + + static const Vector2 uvs[4] = {{0,0},{1,0},{1,1},{0,1}}; + static const int faces[6][4] = {{3,2,1,0}, {4,5,6,7}, {0,1,5,4}, {2,3,7,6}, {1,2,6,5}, {3,0,4,7}}; + static const Vector3 faceNormals[6] = {{0,-1,0}, {0,1,0}, {0,0,-1}, {0,0,1}, {1,0,0}, {-1,0,0}}; + static const Vector3 faceTangents[6] = {{1,0,0}, {1,0,0}, {1,0,0}, {-1,0,0}, {0,0,-1}, {0,0,1}}; + + for (int f = 0; f < 6; f++) + { + const int* ci = faces[f]; + int keptInFace = keepCorner[ci[0]] + keepCorner[ci[1]] + + keepCorner[ci[2]] + keepCorner[ci[3]]; + + if (keptInFace == 0) continue; + + if (keptInFace == 4) { + int baseV = vertexCount; + for (int i = 0; i < 4; i++) { + v[vertexCount++] = R3D_MakeVertex( + corners[ci[i]], uvs[i], faceNormals[f], + (Vector4){faceTangents[f].x, faceTangents[f].y, faceTangents[f].z, 1.0f}, + WHITE + ); + } + idx[indexCount++] = baseV; idx[indexCount++] = baseV+2; idx[indexCount++] = baseV+1; + idx[indexCount++] = baseV+2; idx[indexCount++] = baseV; idx[indexCount++] = baseV+3; + continue; + } + + Vector3 polygon[6]; + Vector2 polyUVs[6]; + int polyCount = 0; + + Vector3 faceU = faceTangents[f]; + Vector3 faceV = Vector3CrossProduct(faceNormals[f], faceU); + + Vector3 faceMin = {1e6f, 1e6f, 1e6f}; + Vector3 faceMax = {-1e6f, -1e6f, -1e6f}; + + for (int i = 0; i < 4; i++) { + int curr = ci[i], next = ci[(i+1)%4]; + if (keepCorner[curr]) { + polygon[polyCount] = corners[curr]; + polyCount++; + } + for (int e = 0; e < 12; e++) { + if (((edges[e][0] == curr && edges[e][1] == next) || + (edges[e][0] == next && edges[e][1] == curr)) && hasIntersection[e]) { + polygon[polyCount] = intersections[e]; + polyCount++; + break; + } + } + } + + for (int i = 0; i < polyCount; i++) { + float u = Vector3DotProduct(polygon[i], faceU); + float v = Vector3DotProduct(polygon[i], faceV); + if (u < faceMin.x) faceMin.x = u; + if (u > faceMax.x) faceMax.x = u; + if (v < faceMin.y) faceMin.y = v; + if (v > faceMax.y) faceMax.y = v; + } + + float rangeU = faceMax.x - faceMin.x; + float rangeV = faceMax.y - faceMin.y; + if (rangeU < 0.001f) rangeU = 1.0f; + if (rangeV < 0.001f) rangeV = 1.0f; + + for (int i = 0; i < polyCount; i++) { + float u = Vector3DotProduct(polygon[i], faceU); + float v = Vector3DotProduct(polygon[i], faceV); + polyUVs[i].x = (u - faceMin.x) / rangeU; + polyUVs[i].y = (v - faceMin.y) / rangeV; + } + + if (polyCount >= 3) { + int baseV = vertexCount; + for (int i = 0; i < polyCount; i++) { + v[vertexCount++] = R3D_MakeVertex( + polygon[i], polyUVs[i], faceNormals[f], + (Vector4){faceTangents[f].x, faceTangents[f].y, faceTangents[f].z, 1.0f}, + WHITE + ); + } + for (int i = 1; i < polyCount - 1; i++) { + idx[indexCount++] = baseV; + idx[indexCount++] = baseV+i+1; + idx[indexCount++] = baseV+i; + } + } + } + + if (intersectionCount >= 3) { + Vector3 center = {0}; + Vector3 cutPolygon[12]; + int cutCount = 0; + + for (int i = 0; i < 12; i++) { + if (hasIntersection[i]) { + cutPolygon[cutCount++] = intersections[i]; + center = Vector3Add(center, intersections[i]); + } + } + center = Vector3Scale(center, 1.0f / cutCount); + + Vector3 cutNormal = normal; + + Vector3 u = fabsf(cutNormal.x) < 0.9f ? (Vector3){1,0,0} : (Vector3){0,1,0}; + u = Vector3Subtract(u, Vector3Scale(cutNormal, Vector3DotProduct(u, cutNormal))); + u = Vector3Normalize(u); + Vector3 w = Vector3CrossProduct(cutNormal, u); + + float angles[12]; + for (int i = 0; i < cutCount; i++) { + Vector3 vec = Vector3Subtract(cutPolygon[i], center); + angles[i] = atan2f(Vector3DotProduct(vec, w), Vector3DotProduct(vec, u)); + } + + for (int i = 0; i < cutCount - 1; i++) { + for (int j = 0; j < cutCount - i - 1; j++) { + if (angles[j] > angles[j+1]) { + float tmpA = angles[j]; angles[j] = angles[j+1]; angles[j+1] = tmpA; + Vector3 tmpV = cutPolygon[j]; cutPolygon[j] = cutPolygon[j+1]; cutPolygon[j+1] = tmpV; + } + } + } + + Vector3 uvMin = {1e6f, 1e6f, 1e6f}; + Vector3 uvMax = {-1e6f, -1e6f, -1e6f}; + + for (int i = 0; i < cutCount; i++) { + float projU = Vector3DotProduct(cutPolygon[i], u); + float projV = Vector3DotProduct(cutPolygon[i], w); + if (projU < uvMin.x) uvMin.x = projU; + if (projU > uvMax.x) uvMax.x = projU; + if (projV < uvMin.y) uvMin.y = projV; + if (projV > uvMax.y) uvMax.y = projV; + } + + float rangeU = uvMax.x - uvMin.x; + float rangeV = uvMax.y - uvMin.y; + if (rangeU < 0.001f) rangeU = 1.0f; + if (rangeV < 0.001f) rangeV = 1.0f; + + int baseV = vertexCount; + for (int i = 0; i < cutCount; i++) { + float projU = Vector3DotProduct(cutPolygon[i], u); + float projV = Vector3DotProduct(cutPolygon[i], w); + Vector2 uv = { + (projU - uvMin.x) / rangeU, + (projV - uvMin.y) / rangeV + }; + + v[vertexCount++] = R3D_MakeVertex( + cutPolygon[i], uv, cutNormal, + (Vector4){u.x, u.y, u.z, 1.0f}, + WHITE + ); + } + + for (int i = 1; i < cutCount - 1; i++) { + idx[indexCount++] = baseV; + idx[indexCount++] = baseV+i; + idx[indexCount++] = baseV+i+1; + } + } + + meshData.vertexCount = vertexCount; + meshData.indexCount = indexCount; + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataSphere(float radius, int rings, int slices) +{ + R3D_MeshData meshData = {0}; + + if (radius <= 0.0f || rings < 2 || slices < 3) { + return meshData; + } + + int vertCountPerRing = slices + 1; + if (!alloc_mesh(&meshData, (rings + 1) * vertCountPerRing, rings * slices * 6)) { + return meshData; + } + + float ringStep = PI / rings; + float sliceStep = 2.0f * PI / slices; + float invRings = 1.0f / rings; + float invSlices = 1.0f / slices; + float invRadius = 1.0f / radius; + + R3D_Vertex* vertex = meshData.vertices; + for (int ring = 0; ring <= rings; ring++) + { + float phi = ring * ringStep; + float sinPhi = sinf(phi); + float cosPhi = cosf(phi); + float y = radius * cosPhi; + float ringRadius = radius * sinPhi; + float v = ring * invRings; + + for (int slice = 0; slice <= slices; slice++, vertex++) + { + float theta = slice * sliceStep; + float sinTheta = sinf(theta); + float cosTheta = cosf(theta); + + float x = ringRadius * cosTheta; + float z = ringRadius * -sinTheta; + + *vertex = R3D_MakeVertex( + (Vector3){x, y, z}, + (Vector2){slice * invSlices, v}, + (Vector3){x * invRadius, y * invRadius, z * invRadius}, + (Vector4){-sinTheta, 0.0f, -cosTheta, 1.0f}, + WHITE + ); + } + } + + uint32_t* index = meshData.indices; + for (int ring = 0; ring < rings; ring++) + { + uint32_t currentRow = ring * vertCountPerRing; + uint32_t nextRow = currentRow + vertCountPerRing; + + for (int slice = 0; slice < slices; slice++) + { + uint32_t i0 = currentRow + slice; + uint32_t i1 = i0 + 1; + uint32_t i2 = nextRow + slice; + uint32_t i3 = i2 + 1; + + *index++ = i0; *index++ = i2; *index++ = i3; + *index++ = i0; *index++ = i3; *index++ = i1; + } + } + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataHemiSphere(float radius, int rings, int slices) +{ + R3D_MeshData meshData = {0}; + + if (radius <= 0.0f || rings < 1 || slices < 3) { + return meshData; + } + + int vertCountPerRing = slices + 1; + int hemisphereVertCount = (rings + 1) * vertCountPerRing; + int totalVertCount = hemisphereVertCount + 1 + vertCountPerRing; + int totalIndexCount = rings * slices * 6 + slices * 3; + + if (!alloc_mesh(&meshData, totalVertCount, totalIndexCount)) { + return meshData; + } + + float ringStep = (PI * 0.5f) / rings; + float sliceStep = 2.0f * PI / slices; + float invRings = 1.0f / rings; + float invSlices = 1.0f / slices; + float invRadius = 1.0f / radius; + + R3D_Vertex* vertex = meshData.vertices; + for (int ring = 0; ring <= rings; ring++) + { + float phi = ring * ringStep; + float sinPhi = sinf(phi); + float cosPhi = cosf(phi); + float y = radius * cosPhi; + float ringRadius = radius * sinPhi; + float v = ring * invRings; + + for (int slice = 0; slice <= slices; slice++, vertex++) + { + float theta = slice * sliceStep; + float sinTheta = sinf(theta); + float cosTheta = cosf(theta); + + float x = ringRadius * cosTheta; + float z = ringRadius * -sinTheta; + + *vertex = R3D_MakeVertex( + (Vector3){x, y, z}, + (Vector2){slice * invSlices, v}, + (Vector3){x * invRadius, y * invRadius, z * invRadius}, + (Vector4){-sinTheta, 0.0f, -cosTheta, 1.0f}, + WHITE + ); + } + } + + uint32_t baseCenterIdx = hemisphereVertCount; + *vertex++ = R3D_MakeVertex( + (Vector3){0.0f, 0.0f, 0.0f}, + (Vector2){0.5f, 0.5f}, + (Vector3){0.0f, -1.0f, 0.0f}, + (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, + WHITE + ); + + for (int slice = 0; slice <= slices; slice++, vertex++) + { + float theta = slice * sliceStep; + float sinTheta = sinf(theta); + float cosTheta = cosf(theta); + + float x = radius * cosTheta; + float z = radius * -sinTheta; + + *vertex = R3D_MakeVertex( + (Vector3){x, 0.0f, z}, + (Vector2){0.5f + 0.5f * cosTheta, 0.5f - 0.5f * sinTheta}, + (Vector3){0.0f, -1.0f, 0.0f}, + (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, + WHITE + ); + } + + uint32_t* index = meshData.indices; + for (int ring = 0; ring < rings; ring++) + { + uint32_t currentRow = ring * vertCountPerRing; + uint32_t nextRow = currentRow + vertCountPerRing; + + for (int slice = 0; slice < slices; slice++) + { + uint32_t i0 = currentRow + slice; + uint32_t i1 = i0 + 1; + uint32_t i2 = nextRow + slice; + uint32_t i3 = i2 + 1; + + *index++ = i0; *index++ = i2; *index++ = i3; + *index++ = i0; *index++ = i3; *index++ = i1; + } + } + + uint32_t basePerimeterStart = baseCenterIdx + 1; + for (int slice = 0; slice < slices; slice++) + { + *index++ = basePerimeterStart + slice; + *index++ = baseCenterIdx; + *index++ = basePerimeterStart + slice + 1; + } + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataCylinder(float radius, float height, int slices) +{ + return R3D_GenMeshDataCylinderEx(radius, radius, height, slices, 1, true, true); +} + +R3D_MeshData R3D_GenMeshDataCylinderEx(float bottomRadius, float topRadius, float height, int slices, int stacks, bool bottomCap, bool topCap) +{ + R3D_MeshData meshData = {0}; + + if (bottomRadius < 0.0f || topRadius < 0.0f || height <= 0.0f || slices < 3 || stacks < 1) { + return meshData; + } + + if (bottomRadius == 0.0f && topRadius == 0.0f) { + return meshData; + } + + bool hasBottom = bottomCap && (bottomRadius > 0.0f); + bool hasTop = topCap && (topRadius > 0.0f); + + int ringCount = stacks + 1; + int ringStride = slices + 1; + int bodyVertCount = ringCount * ringStride; + int capVertCount = (hasBottom ? 1 + slices : 0) + (hasTop ? 1 + slices : 0); + int totalVertCount = bodyVertCount + capVertCount; + + int bodyIndexCount = stacks * slices * 6; + int capIndexCount = (hasBottom ? slices * 3 : 0) + (hasTop ? slices * 3 : 0); + int totalIndexCount = bodyIndexCount + capIndexCount; + + if (!alloc_mesh(&meshData, totalVertCount, totalIndexCount)) { + return meshData; + } + + float halfHeight = height * 0.5f; + float sliceStep = 2.0f * PI / slices; + float invSlices = 1.0f / slices; + float invStacks = 1.0f / stacks; + + float slopeR = height; + float slopeY = bottomRadius - topRadius; + float invLen = 1.0f / sqrtf(slopeR * slopeR + slopeY * slopeY); + float normalR = slopeR * invLen; + float normalY = slopeY * invLen; + + R3D_Vertex* vertex = meshData.vertices; + + for (int stack = 0; stack <= stacks; stack++) + { + float t = stack * invStacks; + float radius = bottomRadius + (topRadius - bottomRadius) * t; + float y = -halfHeight + height * t; + + for (int slice = 0; slice <= slices; slice++, vertex++) + { + float theta = slice * sliceStep; + float cosTheta = cosf(theta); + float sinTheta = sinf(theta); + + *vertex = R3D_MakeVertex( + (Vector3){radius * cosTheta, y, -radius * sinTheta}, + (Vector2){slice * invSlices, t}, + (Vector3){normalR * cosTheta, normalY, -normalR * sinTheta}, + (Vector4){-sinTheta, 0.0f, -cosTheta, 1.0f}, + WHITE + ); + } + } + + uint32_t bottomCapStart = 0; + uint32_t topCapStart = 0; + + if (hasBottom) + { + bottomCapStart = (uint32_t)bodyVertCount; + + *vertex++ = R3D_MakeVertex( + (Vector3){0.0f, -halfHeight, 0.0f}, + (Vector2){0.5f, 0.5f}, + (Vector3){0.0f, -1.0f, 0.0f}, + (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, + WHITE + ); + + for (int slice = 0; slice < slices; slice++, vertex++) + { + float theta = slice * sliceStep; + float cosTheta = cosf(theta); + float sinTheta = sinf(theta); + + *vertex = R3D_MakeVertex( + (Vector3){bottomRadius * cosTheta, -halfHeight, -bottomRadius * sinTheta}, + (Vector2){0.5f + 0.5f * cosTheta, 0.5f - 0.5f * sinTheta}, + (Vector3){0.0f, -1.0f, 0.0f}, + (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, + WHITE + ); + } + } + + if (hasTop) + { + topCapStart = hasBottom + ? bottomCapStart + 1 + (uint32_t)slices + : (uint32_t)bodyVertCount; + + *vertex++ = R3D_MakeVertex( + (Vector3){0.0f, halfHeight, 0.0f}, + (Vector2){0.5f, 0.5f}, + (Vector3){0.0f, 1.0f, 0.0f}, + (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, + WHITE + ); + + for (int slice = 0; slice < slices; slice++, vertex++) + { + float theta = slice * sliceStep; + float cosTheta = cosf(theta); + float sinTheta = sinf(theta); + + *vertex = R3D_MakeVertex( + (Vector3){topRadius * cosTheta, halfHeight, -topRadius * sinTheta}, + (Vector2){0.5f + 0.5f * cosTheta, 0.5f - 0.5f * sinTheta}, + (Vector3){0.0f, 1.0f, 0.0f}, + (Vector4){1.0f, 0.0f, 0.0f, 1.0f}, + WHITE + ); + } + } + + uint32_t* index = meshData.indices; + + for (int stack = 0; stack < stacks; stack++) { + for (int slice = 0; slice < slices; slice++) { + uint32_t b0 = (uint32_t)(stack * ringStride + slice); + uint32_t b1 = b0 + 1; + uint32_t t0 = b0 + (uint32_t)ringStride; + uint32_t t1 = t0 + 1; + *index++ = b0; *index++ = b1; *index++ = t1; + *index++ = b0; *index++ = t1; *index++ = t0; + } + } + + if (hasBottom) { + uint32_t center = bottomCapStart; + uint32_t peri = bottomCapStart + 1; + for (int slice = 0; slice < slices; slice++) { + *index++ = center; + *index++ = peri + (slice + 1) % slices; + *index++ = peri + slice; + } + } + + if (hasTop) { + uint32_t center = topCapStart; + uint32_t peri = topCapStart + 1; + for (int slice = 0; slice < slices; slice++) { + *index++ = center; + *index++ = peri + slice; + *index++ = peri + (slice + 1) % slices; + } + } + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataCapsule(float radius, float height, int rings, int slices) +{ + R3D_MeshData meshData = {0}; + + if (radius <= 0.0f || height < 0.0f || rings < 1 || slices < 3) { + return meshData; + } + + int vertCountPerRing = slices + 1; + + int totalRings = rings * 2 + 3; + int totalVertCount = totalRings * vertCountPerRing; + int totalIndexCount = (totalRings - 1) * slices * 6; + + if (!alloc_mesh(&meshData, totalVertCount, totalIndexCount)) { + return meshData; + } + + float halfHeight = height * 0.5f; + float sliceStep = 2.0f * PI / slices; + float invSlices = 1.0f / slices; + + R3D_Vertex* vertex = meshData.vertices; + int vertIndex = 0; + + for (int ring = 0; ring <= rings; ring++) + { + float phi = (PI * 0.5f) * (1.0f - (float)ring / rings); + float sinPhi = sinf(phi); + float cosPhi = cosf(phi); + float y = radius * sinPhi + halfHeight; + float ringRadius = radius * cosPhi; + float v = (float)ring / (rings * 2 + 2); + + for (int slice = 0; slice <= slices; slice++, vertex++, vertIndex++) + { + float theta = slice * sliceStep; + float sinTheta = sinf(theta); + float cosTheta = cosf(theta); + + float x = ringRadius * cosTheta; + float z = ringRadius * sinTheta; + + *vertex = R3D_MakeVertex( + (Vector3){x, y, z}, + (Vector2){slice * invSlices, v}, + (Vector3){cosPhi * cosTheta, sinPhi, cosPhi * sinTheta}, + (Vector4){-sinTheta, 0.0f, cosTheta, 1.0f}, + WHITE + ); + } + } + + float v = (float)(rings + 1) / (rings * 2 + 2); + for (int slice = 0; slice <= slices; slice++, vertex++, vertIndex++) + { + float theta = slice * sliceStep; + float sinTheta = sinf(theta); + float cosTheta = cosf(theta); + + float x = radius * cosTheta; + float z = radius * sinTheta; + + *vertex = R3D_MakeVertex( + (Vector3){x, -halfHeight, z}, + (Vector2){slice * invSlices, v}, + (Vector3){cosTheta, 0.0f, sinTheta}, + (Vector4){-sinTheta, 0.0f, cosTheta, 1.0f}, + WHITE + ); + } + + for (int ring = 1; ring <= rings; ring++) + { + float phi = -(PI * 0.5f) * ((float)ring / rings); + float sinPhi = sinf(phi); + float cosPhi = cosf(phi); + float y = radius * sinPhi - halfHeight; + float ringRadius = radius * cosPhi; + float v = (float)(rings + 1 + ring) / (rings * 2 + 2); + + for (int slice = 0; slice <= slices; slice++, vertex++, vertIndex++) + { + float theta = slice * sliceStep; + float sinTheta = sinf(theta); + float cosTheta = cosf(theta); + + float x = ringRadius * cosTheta; + float z = ringRadius * sinTheta; + + *vertex = R3D_MakeVertex( + (Vector3){x, y, z}, + (Vector2){slice * invSlices, v}, + (Vector3){cosPhi * cosTheta, sinPhi, cosPhi * sinTheta}, + (Vector4){-sinTheta, 0.0f, cosTheta, 1.0f}, + WHITE + ); + } + } + + uint32_t* index = meshData.indices; + for (int ring = 0; ring < totalRings - 1; ring++) + { + uint32_t currentRow = ring * vertCountPerRing; + uint32_t nextRow = currentRow + vertCountPerRing; + + for (int slice = 0; slice < slices; slice++) + { + uint32_t i0 = currentRow + slice; + uint32_t i1 = i0 + 1; + uint32_t i2 = nextRow + slice; + uint32_t i3 = i2 + 1; + + *index++ = i0; *index++ = i3; *index++ = i2; + *index++ = i0; *index++ = i1; *index++ = i3; + } + } + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataTorus(float radius, float size, int radSeg, int sides) +{ + R3D_MeshData meshData = {0}; + + if (radius <= 0.0f || size <= 0.0f || radSeg < 3 || sides < 3) { + return meshData; + } + + int rings = radSeg + 1; + int segments = sides + 1; + + if (!alloc_mesh(&meshData, rings * segments, radSeg * sides * 6)) { + return meshData; + } + + float ringStep = 2.0f * PI / radSeg; + float sideStep = 2.0f * PI / sides; + float invRadSeg = 1.0f / radSeg; + float invSides = 1.0f / sides; + + R3D_Vertex* vertex = meshData.vertices; + for (int ring = 0; ring <= radSeg; ring++) + { + float theta = ring * ringStep; + float cosTheta = cosf(theta); + float sinTheta = sinf(theta); + + float ringCenterX = radius * cosTheta; + float ringCenterZ = -radius * sinTheta; + float u = ring * invRadSeg; + + for (int side = 0; side <= sides; side++, vertex++) + { + float phi = side * sideStep; + float cosPhi = cosf(phi); + float sinPhi = sinf(phi); + + Vector3 normal = { + cosTheta * cosPhi, + sinPhi, + -sinTheta * cosPhi + }; + + Vector3 position = { + ringCenterX + size * normal.x, + size * normal.y, + ringCenterZ + size * normal.z + }; + + *vertex = R3D_MakeVertex( + position, + (Vector2){u, side * invSides}, + normal, + (Vector4){-sinTheta, 0.0f, -cosTheta, 1.0f}, + WHITE + ); + } + } + + uint32_t* index = meshData.indices; + for (int ring = 0; ring < radSeg; ring++) + { + uint32_t currentRow = ring * segments; + uint32_t nextRow = currentRow + segments; + + for (int side = 0; side < sides; side++) + { + uint32_t i0 = currentRow + side; + uint32_t i1 = i0 + 1; + uint32_t i2 = nextRow + side; + uint32_t i3 = i2 + 1; + + *index++ = i0; *index++ = i2; *index++ = i3; + *index++ = i0; *index++ = i3; *index++ = i1; + } + } + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataKnot(float radius, float size, int radSeg, int sides) +{ + R3D_MeshData meshData = {0}; + + if (radius <= 0.0f || size <= 0.0f || radSeg < 6 || sides < 3) { + return meshData; + } + + int knotSegments = radSeg + 1; + int tubeSides = sides + 1; + + if (!alloc_mesh(&meshData, knotSegments * tubeSides, radSeg * sides * 6)) { + return meshData; + } + + float segmentStep = 2.0f * PI / radSeg; + float sideStep = 2.0f * PI / sides; + float invRadSeg = 1.0f / radSeg; + float invSides = 1.0f / sides; + float knotScale = radius * 0.2f; + + R3D_Vertex* vertex = meshData.vertices; + for (int seg = 0; seg <= radSeg; seg++) + { + float t = seg * segmentStep; + float t2 = 2.0f * t; + float t3 = 3.0f * t; + + float sinT = sinf(t); + float cosT = cosf(t); + float sin2T = sinf(t2); + float cos2T = cosf(t2); + float sin3T = sinf(t3); + float cos3T = cosf(t3); + + Vector3 knotCenter = { + knotScale * (sinT + 2.0f * sin2T), + knotScale * (cosT - 2.0f * cos2T), + knotScale * -sin3T + }; + + Vector3 tangent = { + cosT + 4.0f * cos2T, + -sinT + 4.0f * sin2T, + -3.0f * cos3T + }; + float tangentLen = sqrtf(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z * tangent.z); + if (tangentLen > 0.0f) { + float invTangentLen = 1.0f / tangentLen; + tangent.x *= invTangentLen; + tangent.y *= invTangentLen; + tangent.z *= invTangentLen; + } + + Vector3 binormal = { + -sinT - 8.0f * sin2T, + -cosT + 8.0f * cos2T, + 9.0f * sin3T + }; + float binormalLen = sqrtf(binormal.x * binormal.x + binormal.y * binormal.y + binormal.z * binormal.z); + if (binormalLen > 0.0f) { + float invBinormalLen = 1.0f / binormalLen; + binormal.x *= invBinormalLen; + binormal.y *= invBinormalLen; + binormal.z *= invBinormalLen; + } + + Vector3 normal = { + tangent.y * binormal.z - tangent.z * binormal.y, + tangent.z * binormal.x - tangent.x * binormal.z, + tangent.x * binormal.y - tangent.y * binormal.x + }; + + float u = seg * invRadSeg; + + for (int side = 0; side <= sides; side++, vertex++) + { + float phi = side * sideStep; + float cosPhi = cosf(phi); + float sinPhi = sinf(phi); + + Vector3 surfaceNormal = { + normal.x * cosPhi + binormal.x * sinPhi, + normal.y * cosPhi + binormal.y * sinPhi, + normal.z * cosPhi + binormal.z * sinPhi + }; + + Vector3 position = { + knotCenter.x + size * surfaceNormal.x, + knotCenter.y + size * surfaceNormal.y, + knotCenter.z + size * surfaceNormal.z + }; + + *vertex = R3D_MakeVertex( + position, + (Vector2){u, side * invSides}, + surfaceNormal, + (Vector4){tangent.x, tangent.y, tangent.z, 1.0f}, + WHITE + ); + } + } + + uint32_t* index = meshData.indices; + for (int seg = 0; seg < radSeg; seg++) + { + uint32_t currentRow = seg * tubeSides; + uint32_t nextRow = currentRow + tubeSides; + + for (int side = 0; side < sides; side++) + { + uint32_t i0 = currentRow + side; + uint32_t i1 = i0 + 1; + uint32_t i2 = nextRow + side; + uint32_t i3 = i2 + 1; + + *index++ = i0; *index++ = i2; *index++ = i3; + *index++ = i0; *index++ = i3; *index++ = i1; + } + } + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataHeightmap(Image heightmap, Vector3 size) +{ + R3D_MeshData meshData = {0}; + + if (heightmap.data == NULL || heightmap.width <= 1 || heightmap.height <= 1) { + return meshData; + } + + if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) { + return meshData; + } + + const int mapWidth = heightmap.width; + const int mapHeight = heightmap.height; + + if (!alloc_mesh(&meshData, mapWidth * mapHeight, (mapWidth - 1) * (mapHeight - 1) * 6)) { + return meshData; + } + + const float halfSizeX = size.x * 0.5f; + const float halfSizeZ = size.z * 0.5f; + const float stepX = size.x / (mapWidth - 1); + const float stepZ = size.z / (mapHeight - 1); + const float inv2StepX = 1.0f / (2.0f * stepX); + const float inv2StepZ = 1.0f / (2.0f * stepZ); + const float stepU = 1.0f / (mapWidth - 1); + const float stepV = 1.0f / (mapHeight - 1); + + #define GET_HEIGHT_VALUE(x, y) \ + ((x) < 0 || (x) >= mapWidth || (y) < 0 || (y) >= mapHeight) \ + ? 0.0f : ((float)GetImageColor(heightmap, x, y).r / 255) + + int vertexIndex = 0; + for (int z = 0; z < mapHeight; z++) { + for (int x = 0; x < mapWidth; x++) + { + float posX = -halfSizeX + x * stepX; + float posZ = -halfSizeZ + z * stepZ; + float posY = GET_HEIGHT_VALUE(x, z) * size.y; + + float heightL = GET_HEIGHT_VALUE(x - 1, z); + float heightR = GET_HEIGHT_VALUE(x + 1, z); + float heightD = GET_HEIGHT_VALUE(x, z - 1); + float heightU = GET_HEIGHT_VALUE(x, z + 1); + + float gradX = (heightR - heightL) * inv2StepX; + float gradZ = (heightU - heightD) * inv2StepZ; + + Vector3 normal = Vector3Normalize((Vector3) {-gradX, 1.0f, -gradZ}); + Vector3 tangent = Vector3Normalize((Vector3) {1.0f, gradX, 0.0f}); + + meshData.vertices[vertexIndex] = R3D_MakeVertex( + (Vector3){posX, posY, posZ}, + (Vector2){x * stepU, z * stepV}, + normal, + (Vector4){tangent.x, tangent.y, tangent.z, 1.0f}, + WHITE + ); + vertexIndex++; + } + } + + int indexOffset = 0; + for (int z = 0; z < mapHeight - 1; z++) { + for (int x = 0; x < mapWidth - 1; x++) + { + uint32_t topLeft = z * mapWidth + x; + uint32_t topRight = topLeft + 1; + uint32_t bottomLeft = (z + 1) * mapWidth + x; + uint32_t bottomRight = bottomLeft + 1; + + meshData.indices[indexOffset++] = topLeft; + meshData.indices[indexOffset++] = bottomLeft; + meshData.indices[indexOffset++] = topRight; + + meshData.indices[indexOffset++] = topRight; + meshData.indices[indexOffset++] = bottomLeft; + meshData.indices[indexOffset++] = bottomRight; + } + } + + #undef GET_HEIGHT_VALUE + + return meshData; +} + +R3D_MeshData R3D_GenMeshDataCubicmap(Image cubicmap, Vector3 cubeSize) +{ + #define IS_WALL(c) ((c).r >= 127) + #define PIXEL(x,z) pixels[(z)*W + (x)] + #define UV(r,u,v) (Vector2) {texUVs[r].x + (u)*texUVs[r].w, texUVs[r].y + (v)*texUVs[r].h} + + #define QUAD(v0,v1,v2,v3, uv0,uv1,uv2,uv3, ni,ti) do { \ + vertices[vc+0] = R3D_MakeVertex(v0, uv0, normals[ni], tangents[ti], WHITE); \ + vertices[vc+1] = R3D_MakeVertex(v1, uv1, normals[ni], tangents[ti], WHITE); \ + vertices[vc+2] = R3D_MakeVertex(v2, uv2, normals[ni], tangents[ti], WHITE); \ + vertices[vc+3] = R3D_MakeVertex(v3, uv3, normals[ni], tangents[ti], WHITE); \ + indices[ic+0]=vc+0; indices[ic+1]=vc+1; indices[ic+2]=vc+2; \ + indices[ic+3]=vc+0; indices[ic+4]=vc+2; indices[ic+5]=vc+3; \ + vc += 4; ic += 6; \ + } while(0) + + R3D_MeshData meshData = {0}; + + if (cubicmap.width <= 0 || cubicmap.height <= 0) { + return meshData; + } + + if (cubeSize.x <= 0.0f || cubeSize.y <= 0.0f || cubeSize.z <= 0.0f) { + return meshData; + } + + Color* pixels = LoadImageColors(cubicmap); + if (!pixels) return meshData; + + const int W = cubicmap.width; + const int H = cubicmap.height; + const float hw = cubeSize.x * 0.5f; + const float hh = cubeSize.y; + const float hl = cubeSize.z * 0.5f; + + static const Vector3 normals[6] = { + { 1, 0, 0}, {-1, 0, 0}, + { 0, 1, 0}, { 0,-1, 0}, + { 0, 0,-1}, { 0, 0, 1} + }; + static const Vector4 tangents[6] = { + { 0, 0,-1, 1}, { 0, 0, 1, 1}, + { 1, 0, 0, 1}, { 1, 0, 0, 1}, + {-1, 0, 0, 1}, { 1, 0, 0, 1} + }; + + struct UVRect { float x, y, w, h; }; + static const struct UVRect texUVs[6] = { + {0.0f, 0.0f, 0.5f, 0.5f}, + {0.5f, 0.0f, 0.5f, 0.5f}, + {0.0f, 0.5f, 0.5f, 0.5f}, + {0.5f, 0.5f, 0.5f, 0.5f}, + {0.5f, 0.0f, 0.5f, 0.5f}, + {0.0f, 0.0f, 0.5f, 0.5f} + }; + + int maxFaces = 0; + for (int i = 0; i < W * H; i++) { + maxFaces += IS_WALL(pixels[i]) ? 6 : 2; + } + + if (!alloc_mesh(&meshData, maxFaces * 4, maxFaces * 6)) { + UnloadImageColors(pixels); + return meshData; + } + + R3D_Vertex* vertices = meshData.vertices; + uint32_t* indices = meshData.indices; + int vc = 0, ic = 0; + + for (int z = 0; z < H; z++) { + for (int x = 0; x < W; x++) { + Color px = PIXEL(x, z); + + float cx = cubeSize.x * (x - W * 0.5f + 0.5f); + float cz = cubeSize.z * (z - H * 0.5f + 0.5f); + + Vector3 TLF={cx-hw,hh,cz-hl}, TRF={cx+hw,hh,cz-hl}; + Vector3 TLB={cx-hw,hh,cz+hl}, TRB={cx+hw,hh,cz+hl}; + Vector3 BLF={cx-hw, 0,cz-hl}, BRF={cx+hw, 0,cz-hl}; + Vector3 BLB={cx-hw, 0,cz+hl}, BRB={cx+hw, 0,cz+hl}; + + if (IS_WALL(px)) { + // Wall top / bottom + QUAD(TLF,TLB,TRB,TRF, UV(2,0,0),UV(2,0,1),UV(2,1,1),UV(2,1,0), 2,2); + QUAD(BRF,BRB,BLB,BLF, UV(3,1,0),UV(3,1,1),UV(3,0,1),UV(3,0,0), 3,3); + + // Walls with occlusion culling + if (z==H-1 || !IS_WALL(PIXEL(x,z+1))) QUAD(TLB,BLB,BRB,TRB, UV(5,0,0),UV(5,0,1),UV(5,1,1),UV(5,1,0), 5,5); // +Z + if (z==0 || !IS_WALL(PIXEL(x,z-1))) QUAD(TRF,BRF,BLF,TLF, UV(4,1,0),UV(4,1,1),UV(4,0,1),UV(4,0,0), 4,4); // -Z + if (x==W-1 || !IS_WALL(PIXEL(x+1,z))) QUAD(TRB,BRB,BRF,TRF, UV(0,0,0),UV(0,0,1),UV(0,1,1),UV(0,1,0), 0,0); // +X + if (x==0 || !IS_WALL(PIXEL(x-1,z))) QUAD(TLF,BLF,BLB,TLB, UV(1,0,0),UV(1,0,1),UV(1,1,1),UV(1,1,0), 1,1); // -X + } + else { + QUAD(TLF,TRF,TRB,TLB, UV(3,0,0),UV(3,1,0),UV(3,1,1),UV(3,0,1), 3,3); + QUAD(BLF,BLB,BRB,BRF, UV(2,0,0),UV(2,0,1),UV(2,1,1),UV(2,1,0), 2,2); + } + } + } + + UnloadImageColors(pixels); + meshData.vertexCount = vc; + meshData.indexCount = ic; + + #undef IS_WALL + #undef PIXEL + #undef UV + #undef QUAD + + return meshData; +} + +void R3D_ReserveMeshData(R3D_MeshData* meshData, int vertexCount, int indexCount) +{ + if (vertexCount > meshData->vertexCapacity) { + void* vertices = RL_REALLOC(meshData->vertices, vertexCount * sizeof(*meshData->vertices)); + if (vertices == NULL) { + R3D_TRACELOG(LOG_WARNING, "Failed to reserve vertices memory"); + return; + } + meshData->vertexCapacity = vertexCount; + meshData->vertices = vertices; + } + + if (indexCount > meshData->indexCapacity) { + void* indices = RL_REALLOC(meshData->indices, indexCount * sizeof(*meshData->indices)); + if (indices == NULL) { + R3D_TRACELOG(LOG_WARNING, "Failed to reserve indices memory"); + return; + } + meshData->indexCapacity = indexCount; + meshData->indices = indices; + } +} + +void R3D_ShrinkMeshData(R3D_MeshData* meshData) +{ + if (meshData->vertexCount > 0 && meshData->vertexCount != meshData->vertexCapacity) { + void* vertices = RL_REALLOC(meshData->vertices, meshData->vertexCount * sizeof(*meshData->vertices)); + if (vertices == NULL) { + R3D_TRACELOG(LOG_WARNING, "Failed to shrink vertices memory"); + return; + } + meshData->vertexCapacity = meshData->vertexCount; + meshData->vertices = vertices; + } + + if (meshData->indexCount > 0 && meshData->indexCount != meshData->indexCapacity) { + void* indices = RL_REALLOC(meshData->indices, meshData->indexCount * sizeof(*meshData->indices)); + if (indices == NULL) { + R3D_TRACELOG(LOG_WARNING, "Failed to shrink indices memory"); + return; + } + meshData->indexCapacity = meshData->indexCount; + meshData->indices = indices; + } +} + +void R3D_ResetMeshData(R3D_MeshData* meshData) +{ + meshData->vertexCount = 0; + meshData->indexCount = 0; +} + +R3D_MeshData R3D_CopyMeshData(R3D_MeshData meshData) +{ + R3D_MeshData duplicate = {0}; + + if (meshData.vertices == NULL) { + R3D_TRACELOG(LOG_ERROR, "Cannot duplicate null mesh data"); + return duplicate; + } + + duplicate = R3D_LoadMeshData(meshData.vertexCount, meshData.indexCount); + if (duplicate.vertices == NULL) { + return duplicate; + } + + memcpy(duplicate.vertices, meshData.vertices, meshData.vertexCount * sizeof(*meshData.vertices)); + + if (meshData.indexCount > 0 && meshData.indices != NULL && duplicate.indices != NULL) { + memcpy(duplicate.indices, meshData.indices, meshData.indexCount * sizeof(*meshData.indices)); + } + + return duplicate; +} + +R3D_MeshData R3D_MergeMeshData(R3D_MeshData a, R3D_MeshData b) +{ + R3D_MeshData merged = {0}; + + if (a.vertices == NULL || b.vertices == NULL) { + R3D_TRACELOG(LOG_ERROR, "Cannot merge null mesh data"); + return merged; + } + + int totalVertices = a.vertexCount + b.vertexCount; + int totalIndices = a.indexCount + b.indexCount; + + merged = R3D_LoadMeshData(totalVertices, totalIndices); + + if (merged.vertices == NULL) { + return merged; + } + + memcpy(merged.vertices, a.vertices, a.vertexCount * sizeof(*merged.vertices)); + memcpy(merged.vertices + a.vertexCount, b.vertices, b.vertexCount * sizeof(*merged.vertices)); + + if (a.indexCount > 0 && a.indices != NULL) { + memcpy(merged.indices, a.indices, a.indexCount * sizeof(*merged.indices)); + } + + if (b.indexCount > 0 && b.indices != NULL) { + for (int i = 0; i < b.indexCount; i++) { + merged.indices[a.indexCount + i] = b.indices[i] + a.vertexCount; + } + } + + return merged; +} + +void R3D_AppendMeshData(R3D_MeshData* meshData, R3D_Vertex* vertices, int vertexCount, uint32_t* indices, int indexCount) +{ + R3D_ReserveMeshData(meshData, meshData->vertexCount + vertexCount, meshData->indexCount + indexCount); + + for (int i = 0; i < vertexCount; i++) { + meshData->vertices[meshData->vertexCount++] = vertices[i]; + } + + for (int i = 0; i < indexCount; i++) { + meshData->indices[meshData->indexCount++] = indices[i]; + } +} + +void R3D_TransformMeshData(R3D_MeshData* meshData, Matrix transform) +{ + if (meshData == NULL || meshData->vertices == NULL) return; + + Matrix matNormal = r3d_matrix_normal(&transform); + + for (int i = 0; i < meshData->vertexCount; i++) + { + R3D_Vertex* v = &meshData->vertices[i]; + v->position = r3d_vector3_transform(v->position, &transform); + + Vector3 normal = R3D_UnpackNormal((int8_t*)v->normal); + R3D_PackNormal((int8_t*)v->normal, r3d_vector3_transform_normal(normal, &matNormal)); + + Vector4 tangent = R3D_UnpackTangent((int8_t*)v->tangent); + Vector3 t = Vector3Normalize(r3d_vector3_transform_normal((Vector3){tangent.x, tangent.y, tangent.z}, &matNormal)); + R3D_PackTangent((int8_t*)v->tangent, (Vector4){t.x, t.y, t.z, tangent.w}); + } +} + +void R3D_TranslateMeshData(R3D_MeshData* meshData, Vector3 translation) +{ + if (meshData == NULL || meshData->vertices == NULL) return; + + for (int i = 0; i < meshData->vertexCount; i++) { + meshData->vertices[i].position.x += translation.x; + meshData->vertices[i].position.y += translation.y; + meshData->vertices[i].position.z += translation.z; + } +} + +void R3D_RotateMeshData(R3D_MeshData* meshData, Quaternion rotation) +{ + if (meshData == NULL || meshData->vertices == NULL) return; + + for (int i = 0; i < meshData->vertexCount; i++) + { + R3D_Vertex* v = &meshData->vertices[i]; + + v->position = Vector3RotateByQuaternion(v->position, rotation); + + Vector3 normal = R3D_UnpackNormal((int8_t*)v->normal); + R3D_PackNormal((int8_t*)v->normal, Vector3RotateByQuaternion(normal, rotation)); + + Vector4 tangent = R3D_UnpackTangent((int8_t*)v->tangent); + Vector3 t = Vector3RotateByQuaternion((Vector3){tangent.x, tangent.y, tangent.z}, rotation); + R3D_PackTangent((int8_t*)v->tangent, (Vector4){t.x, t.y, t.z, tangent.w}); + } +} + +void R3D_ScaleMeshData(R3D_MeshData* meshData, Vector3 scale) +{ + if (meshData == NULL || meshData->vertices == NULL) return; + + bool uniform = (scale.x == scale.y && scale.y == scale.z); + + Vector3 invScale = { + scale.x != 0.0f ? 1.0f / scale.x : 0.0f, + scale.y != 0.0f ? 1.0f / scale.y : 0.0f, + scale.z != 0.0f ? 1.0f / scale.z : 0.0f + }; + + for (int i = 0; i < meshData->vertexCount; i++) + { + R3D_Vertex* v = &meshData->vertices[i]; + + v->position.x *= scale.x; + v->position.y *= scale.y; + v->position.z *= scale.z; + + if (!uniform) { + Vector3 normal = R3D_UnpackNormal((int8_t*)v->normal); + normal = Vector3Normalize((Vector3){normal.x * invScale.x, normal.y * invScale.y, normal.z * invScale.z}); + R3D_PackNormal((int8_t*)v->normal, normal); + + Vector4 tangent = R3D_UnpackTangent((int8_t*)v->tangent); + Vector3 t = Vector3Normalize((Vector3){tangent.x * scale.x, tangent.y * scale.y, tangent.z * scale.z}); + R3D_PackTangent((int8_t*)v->tangent, (Vector4){t.x, t.y, t.z, tangent.w}); + } + } +} + +void R3D_GenMeshDataUVsPlanar(R3D_MeshData* meshData, Vector2 uvScale, Vector3 axis) +{ + if (meshData == NULL || meshData->vertices == NULL) return; + + axis = Vector3Normalize(axis); + + Vector3 up = (fabsf(axis.y) < 0.999f) ? (Vector3) {0, 1, 0 } : (Vector3) {1, 0, 0}; + Vector3 tangent = Vector3Normalize(Vector3CrossProduct(up, axis)); + Vector3 bitangent = Vector3CrossProduct(axis, tangent); + + for (int i = 0; i < meshData->vertexCount; i++) { + Vector3 pos = meshData->vertices[i].position; + float u = Vector3DotProduct(pos, tangent) * uvScale.x; + float v = Vector3DotProduct(pos, bitangent) * uvScale.y; + R3D_PackTexCoord(meshData->vertices[i].texcoord, (Vector2){u, v}); + } +} + +void R3D_GenMeshDataUVsSpherical(R3D_MeshData* meshData) +{ + if (meshData == NULL || meshData->vertices == NULL) return; + + for (int i = 0; i < meshData->vertexCount; i++) { + Vector3 pos = Vector3Normalize(meshData->vertices[i].position); + float u = 0.5f + atan2f(pos.z, pos.x) / (2.0f * PI); + float v = 0.5f - asinf(pos.y) * (1.0f / PI); + R3D_PackTexCoord(meshData->vertices[i].texcoord, (Vector2){u, v}); + } +} + +void R3D_GenMeshDataUVsCylindrical(R3D_MeshData* meshData) +{ + if (meshData == NULL || meshData->vertices == NULL) return; + + for (int i = 0; i < meshData->vertexCount; i++) { + Vector3 pos = meshData->vertices[i].position; + float u = 0.5f + atan2f(pos.z, pos.x) / (2.0f * PI); + float v = pos.y; + R3D_PackTexCoord(meshData->vertices[i].texcoord, (Vector2){u, v}); + } +} + +static void accumulate_face_normal(Vector3* normals, R3D_MeshData* meshData, uint32_t i0, uint32_t i1, uint32_t i2) +{ + Vector3 v0 = meshData->vertices[i0].position; + Vector3 v1 = meshData->vertices[i1].position; + Vector3 v2 = meshData->vertices[i2].position; + + Vector3 faceNormal = Vector3CrossProduct( + Vector3Subtract(v1, v0), + Vector3Subtract(v2, v0) + ); + + normals[i0] = Vector3Add(normals[i0], faceNormal); + normals[i1] = Vector3Add(normals[i1], faceNormal); + normals[i2] = Vector3Add(normals[i2], faceNormal); +} + +void R3D_GenMeshDataNormals(R3D_MeshData* meshData, R3D_PrimitiveType type) +{ + if (meshData == NULL || meshData->vertices == NULL || meshData->vertexCount == 0) { + return; + } + + if (type == R3D_PRIMITIVE_POINTS || type == R3D_PRIMITIVE_LINES || + type == R3D_PRIMITIVE_LINE_STRIP || type == R3D_PRIMITIVE_LINE_LOOP) { + for (int i = 0; i < meshData->vertexCount; i++) { + R3D_PackNormal((int8_t*)meshData->vertices[i].normal, (Vector3){0.0f, 0.0f, 1.0f}); + } + return; + } + + // Accumulate in float to avoid precision loss + Vector3* normals = RL_CALLOC(meshData->vertexCount, sizeof(Vector3)); + if (normals == NULL) return; + + int count = meshData->indexCount > 0 ? meshData->indexCount : meshData->vertexCount; + + switch (type) { + case R3D_PRIMITIVE_TRIANGLES: + for (int i = 0; i + 2 < count; i += 3) { + accumulate_face_normal(normals, meshData, + get_index(meshData, i), + get_index(meshData, i + 1), + get_index(meshData, i + 2) + ); + } + break; + case R3D_PRIMITIVE_TRIANGLE_STRIP: + for (int i = 0; i + 2 < count; i++) { + uint32_t i0 = get_index(meshData, i % 2 == 0 ? i : i + 1); + uint32_t i1 = get_index(meshData, i % 2 == 0 ? i + 1 : i ); + uint32_t i2 = get_index(meshData, i + 2); + accumulate_face_normal(normals, meshData, i0, i1, i2); + } + break; + case R3D_PRIMITIVE_TRIANGLE_FAN: { + uint32_t center = get_index(meshData, 0); + for (int i = 1; i + 1 < count; i++) { + accumulate_face_normal(normals, meshData, + center, + get_index(meshData, i), + get_index(meshData, i + 1) + ); + } + } break; + default: + break; + } + + for (int i = 0; i < meshData->vertexCount; i++) { + R3D_PackNormal((int8_t*)meshData->vertices[i].normal, Vector3Normalize(normals[i])); + } + + RL_FREE(normals); +} + +static void process_triangle_tangents(Vector3* tangents, Vector3* bitangents, R3D_MeshData* meshData, uint32_t i0, uint32_t i1, uint32_t i2) +{ + Vector3 v0 = meshData->vertices[i0].position; + Vector3 v1 = meshData->vertices[i1].position; + Vector3 v2 = meshData->vertices[i2].position; + + Vector2 uv0 = R3D_UnpackTexCoord(meshData->vertices[i0].texcoord); + Vector2 uv1 = R3D_UnpackTexCoord(meshData->vertices[i1].texcoord); + Vector2 uv2 = R3D_UnpackTexCoord(meshData->vertices[i2].texcoord); + + Vector3 edge1 = Vector3Subtract(v1, v0); + Vector3 edge2 = Vector3Subtract(v2, v0); + + Vector2 deltaUV1 = Vector2Subtract(uv1, uv0); + Vector2 deltaUV2 = Vector2Subtract(uv2, uv0); + + float det = deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y; + if (fabsf(det) < 1e-6f) return; + + float invDet = 1.0f / det; + + Vector3 tangent = { + invDet * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x), + invDet * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y), + invDet * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z) + }; + + Vector3 bitangent = { + invDet * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x), + invDet * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y), + invDet * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z) + }; + + tangents[i0] = Vector3Add(tangents[i0], tangent); + tangents[i1] = Vector3Add(tangents[i1], tangent); + tangents[i2] = Vector3Add(tangents[i2], tangent); + bitangents[i0] = Vector3Add(bitangents[i0], bitangent); + bitangents[i1] = Vector3Add(bitangents[i1], bitangent); + bitangents[i2] = Vector3Add(bitangents[i2], bitangent); +} + +void R3D_GenMeshDataTangents(R3D_MeshData* meshData, R3D_PrimitiveType type) +{ + if (meshData == NULL || meshData->vertices == NULL || meshData->vertexCount == 0) { + return; + } + + if (type == R3D_PRIMITIVE_POINTS || type == R3D_PRIMITIVE_LINES || + type == R3D_PRIMITIVE_LINE_STRIP || type == R3D_PRIMITIVE_LINE_LOOP) { + for (int i = 0; i < meshData->vertexCount; i++) { + R3D_PackTangent((int8_t*)meshData->vertices[i].tangent, (Vector4){1.0f, 0.0f, 0.0f, 1.0f}); + } + return; + } + + Vector3* tangents = RL_CALLOC(meshData->vertexCount, sizeof(Vector3)); + Vector3* bitangents = RL_CALLOC(meshData->vertexCount, sizeof(Vector3)); + if (tangents == NULL || bitangents == NULL) { + R3D_TRACELOG(LOG_ERROR, "Failed to allocate memory for tangent calculation"); + RL_FREE(tangents); + RL_FREE(bitangents); + return; + } + + int count = meshData->indexCount > 0 ? meshData->indexCount : meshData->vertexCount; + + switch (type) { + case R3D_PRIMITIVE_TRIANGLES: + for (int i = 0; i + 2 < count; i += 3) { + process_triangle_tangents(tangents, bitangents, meshData, + get_index(meshData, i), + get_index(meshData, i + 1), + get_index(meshData, i + 2) + ); + } + break; + case R3D_PRIMITIVE_TRIANGLE_STRIP: + for (int i = 0; i + 2 < count; i++) { + uint32_t i0 = get_index(meshData, i % 2 == 0 ? i : i + 1); + uint32_t i1 = get_index(meshData, i % 2 == 0 ? i + 1 : i ); + uint32_t i2 = get_index(meshData, i + 2); + process_triangle_tangents(tangents, bitangents, meshData, i0, i1, i2); + } + break; + case R3D_PRIMITIVE_TRIANGLE_FAN: { + uint32_t center = get_index(meshData, 0); + for (int i = 1; i + 1 < count; i++) { + process_triangle_tangents(tangents, bitangents, meshData, + center, + get_index(meshData, i), + get_index(meshData, i + 1) + ); + } + } break; + default: + break; + } + + // Orthogonalization (Gram-Schmidt) and handedness calculation + for (int i = 0; i < meshData->vertexCount; i++) + { + Vector3 n = R3D_UnpackNormal((int8_t*)meshData->vertices[i].normal); + Vector3 t = tangents[i]; + + // Gram-Schmidt orthogonalization + t = Vector3Subtract(t, Vector3Scale(n, Vector3DotProduct(n, t))); + float tLength = Vector3Length(t); + if (tLength > 1e-6f) { + t = Vector3Scale(t, 1.0f / tLength); + } else { + // Fallback: generate an arbitrary tangent perpendicular to the normal + t = fabsf(n.x) < 0.9f ? (Vector3){1.0f, 0.0f, 0.0f} : (Vector3){0.0f, 1.0f, 0.0f}; + t = Vector3Normalize(Vector3Subtract(t, Vector3Scale(n, Vector3DotProduct(n, t)))); + } + + float handedness = Vector3DotProduct(Vector3CrossProduct(n, t), bitangents[i]) < 0.0f ? -1.0f : 1.0f; + R3D_PackTangent((int8_t*)meshData->vertices[i].tangent, (Vector4){t.x, t.y, t.z, handedness}); + } + + RL_FREE(tangents); + RL_FREE(bitangents); +} + +BoundingBox R3D_CalculateMeshDataBoundingBox(R3D_MeshData meshData) +{ + BoundingBox bounds = {0}; + + if (meshData.vertices == NULL || meshData.vertexCount == 0) { + return bounds; + } + + bounds.min = meshData.vertices[0].position; + bounds.max = meshData.vertices[0].position; + + for (int i = 1; i < meshData.vertexCount; i++) { + Vector3 pos = meshData.vertices[i].position; + bounds.min = Vector3Min(bounds.min, pos); + bounds.max = Vector3Max(bounds.max, pos); + } + + return bounds; +} + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +bool alloc_mesh(R3D_MeshData* meshData, int vertexCount, int indexCount) +{ + meshData->vertices = RL_CALLOC(vertexCount, sizeof(*meshData->vertices)); + meshData->indices = RL_CALLOC(indexCount, sizeof(*meshData->indices)); + + if (!meshData->vertices || !meshData->indices) { + if (meshData->vertices) RL_FREE(meshData->vertices); + if (meshData->indices) RL_FREE(meshData->indices); + return false; + } + + meshData->vertexCount = vertexCount; + meshData->indexCount = indexCount; + meshData->vertexCapacity = vertexCount; + meshData->indexCapacity = indexCount; + + return true; +} + +``` + +`src/r3d_model.c`: + +```c +/* r3d_model.h -- R3D Model Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef R3D_SUPPORT_ASSIMP +# include "./importer/r3d_importer_internal.h" +# include "./r3d_core_state.h" +#endif + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +#ifdef R3D_SUPPORT_ASSIMP + +static bool load_model_components(R3D_Model* model, const R3D_Importer* importer) +{ + if (!r3d_importer_load_meshes(importer, model)) { + return false; + } + + if (!r3d_importer_load_skeleton(importer, &model->skeleton)) { + return false; + } + + r3d_importer_texture_cache_t* textureCache = r3d_importer_load_texture_cache( + importer, R3D.colorSpace, R3D.textureFilter); + + //if (textureCache == NULL) { + // R3D_TRACELOG(LOG_INFO, "The model's materials will not have textures"); + //} + + if (!r3d_importer_load_materials(importer, &model->materials, &model->materialCount, textureCache)) { + r3d_importer_unload_texture_cache(textureCache, true); + return false; + } + + r3d_importer_unload_texture_cache(textureCache, false); + + return true; +} + +#endif // R3D_SUPPORT_ASSIMP + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Model R3D_LoadModel(const char* filePath) +{ + return R3D_LoadModelEx(filePath, 0); +} + +R3D_Model R3D_LoadModelEx(const char* filePath, R3D_ImportFlags flags) +{ + R3D_Model model = {0}; + +#ifdef R3D_SUPPORT_ASSIMP + R3D_Importer* importer = R3D_LoadImporter(filePath, flags); + if (importer == NULL) return model; + + model = R3D_LoadModelFromImporter(importer); + + R3D_UnloadImporter(importer); + +#else + R3D_TRACELOG(LOG_WARNING, "Cannot load '%s': built without Assimp support", filePath); + +#endif // R3D_SUPPORT_ASSIMP + + return model; +} + +R3D_Model R3D_LoadModelFromMemory(const void* data, unsigned int size, const char* hint) +{ + return R3D_LoadModelFromMemoryEx(data, size, hint, 0); +} + +R3D_Model R3D_LoadModelFromMemoryEx(const void* data, unsigned int size, const char* hint, R3D_ImportFlags flags) +{ + R3D_Model model = {0}; + +#ifdef R3D_SUPPORT_ASSIMP + R3D_Importer* importer = R3D_LoadImporterFromMemory(data, size, hint, flags); + if (importer == NULL) return model; + + model = R3D_LoadModelFromImporter(importer); + + R3D_UnloadImporter(importer); + +#else + if (hint && hint[0] != '\0') { + R3D_TRACELOG(LOG_WARNING, "Cannot load '%s' from memory: built without Assimp support", hint); + } + else { + R3D_TRACELOG(LOG_WARNING, "Cannot load asset from memory: built without Assimp support"); + } + +#endif // R3D_SUPPORT_ASSIMP + + return model; +} + +R3D_Model R3D_LoadModelFromImporter(const R3D_Importer* importer) +{ + R3D_Model model = {0}; + +#ifdef R3D_SUPPORT_ASSIMP + if (importer == NULL) { + R3D_TRACELOG(LOG_WARNING, "Cannot load model from importer: NULL importer"); + return model; + } + + if (load_model_components(&model, importer)) { + R3D_TRACELOG(LOG_INFO, "Model loaded successfully: '%s'", importer->name); + R3D_TRACELOG(LOG_INFO, " > Materials count: %i", model.materialCount); + R3D_TRACELOG(LOG_INFO, " > Meshes count: %i", model.meshCount); + R3D_TRACELOG(LOG_INFO, " > Bones count: %i", model.skeleton.boneCount); + } + else { + R3D_UnloadModel(model, false); + memset(&model, 0, sizeof(model)); + + R3D_TRACELOG(LOG_WARNING, "Failed to load model: '%s'", importer->name); + } + +#else + R3D_TRACELOG(LOG_WARNING, "Cannot load model from importer: built without Assimp support"); + +#endif // R3D_SUPPORT_ASSIMP + + return model; +} + +void R3D_UnloadModel(R3D_Model model, bool unloadMaterials) +{ + R3D_UnloadSkeleton(model.skeleton); + + if (model.meshes != NULL) { + for (int i = 0; i < model.meshCount; i++) { + R3D_UnloadMesh(model.meshes[i]); + } + } + + if (model.meshData != NULL) { + for (int i = 0; i < model.meshCount; i++) { + R3D_UnloadMeshData(model.meshData[i]); + } + } + + if (unloadMaterials && model.materials != NULL) { + for (int i = 0; i < model.materialCount; i++) { + R3D_UnloadMaterial(model.materials[i]); + } + } + + RL_FREE(model.meshMaterials); + RL_FREE(model.materials); + RL_FREE(model.meshData); + RL_FREE(model.meshes); +} + +int R3D_GetModelMeshIndex(R3D_Model model, const char* meshName) +{ + if (model.meshNames == NULL) return -1; + for (int i = 0; i < model.meshCount; i++) { + if (strncmp(model.meshNames[i], meshName, sizeof(R3D_MeshName)) == 0) { + return i; + } + } + return -1; +} + +R3D_Mesh* R3D_GetModelMesh(R3D_Model model, const char* meshName) +{ + int index = R3D_GetModelMeshIndex(model, meshName); + return index >= 0 ? &model.meshes[index] : NULL; +} + +R3D_MeshData* R3D_GetModelMeshData(R3D_Model model, const char* meshName) +{ + if (model.meshData == NULL) return NULL; + int index = R3D_GetModelMeshIndex(model, meshName); + return index >= 0 ? &model.meshData[index] : NULL; +} + +``` + +`src/r3d_pack.c`: + +```c +/* r3d_pack.c -- R3D Pack Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include + +#include "./common/r3d_half.h" + +// ======================================== +// PUBLIC API +// ======================================== + +uint16_t R3D_PackFloat16(float x) +{ + return r3d_half_from_float(x); +} + +uint16_t R3D_PackUnorm16(float x) +{ + x = fminf(fmaxf(x, 0.0f), 1.0f); + return (uint16_t)roundf(x * 65535.0f); +} + +int16_t R3D_PackSnorm16(float x) +{ + x = fminf(fmaxf(x, -1.0f), 1.0f); + return (int16_t)roundf(x * 32767.0f); +} + +uint8_t R3D_PackUnorm8(float x) +{ + x = fminf(fmaxf(x, 0.0f), 1.0f); + return (uint8_t)roundf(x * 255.0f); +} + +int8_t R3D_PackSnorm8(float x) +{ + x = fminf(fmaxf(x, -1.0f), 1.0f); + return (int8_t)roundf(x * 127.0f); +} + +float R3D_UnpackFloat16(uint16_t x) +{ + return r3d_half_to_float(x); +} + +float R3D_UnpackUnorm16(uint16_t x) +{ + return (float)x / 65535.0f; +} + +float R3D_UnpackSnorm16(int16_t x) +{ + return fmaxf((float)x / 32767.0f, -1.0f); +} + +float R3D_UnpackUnorm8(uint8_t x) +{ + return (float)x / 255.0f; +} + +float R3D_UnpackSnorm8(int8_t x) +{ + return fmaxf((float)x / 127.0f, -1.0f); +} + +``` + +`src/r3d_probe.c`: + +```c +/* r3d_probe.c -- R3D Probe Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include + +#include "./modules/r3d_env.h" + +// ======================================== +// HELPER MACROS +// ======================================== + +#define GET_PROBE_OR_RETURN(var_name, id, ...) \ + r3d_env_probe_t* var_name; \ + do { \ + var_name = r3d_env_probe_get(id); \ + if (var_name == NULL) { \ + R3D_TRACELOG(LOG_ERROR, "Invalid Probe [ID %i] given to '%s'", id, __func__); \ + return __VA_ARGS__; \ + } \ + } while(0) + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Probe R3D_CreateProbe(R3D_ProbeFlags flags) +{ + return r3d_env_probe_new(flags); +} + +void R3D_DestroyProbe(R3D_Probe id) +{ + r3d_env_probe_delete(id); +} + +bool R3D_IsProbeExist(R3D_Probe id) +{ + return r3d_env_probe_is_valid(id); +} + +R3D_ProbeFlags R3D_GetProbeType(R3D_Probe id) +{ + GET_PROBE_OR_RETURN(probe, id, 0); + return probe->flags; +} + +bool R3D_IsProbeActive(R3D_Probe id) +{ + GET_PROBE_OR_RETURN(probe, id, 0); + return probe->enabled; +} + +void R3D_SetProbeActive(R3D_Probe id, bool active) +{ + GET_PROBE_OR_RETURN(probe, id); + + if (probe->enabled == active) return; + if (active) probe->state.sceneShouldBeUpdated = true; + + probe->enabled = active; +} + +R3D_ProbeUpdateMode R3D_GetProbeUpdateMode(R3D_Probe id) +{ + GET_PROBE_OR_RETURN(probe, id, 0); + return probe->state.updateMode; +} + +void R3D_SetProbeUpdateMode(R3D_Probe id, R3D_ProbeUpdateMode mode) +{ + GET_PROBE_OR_RETURN(probe, id); + probe->state.updateMode = mode; +} + +bool R3D_GetProbeInterior(R3D_Probe id) +{ + GET_PROBE_OR_RETURN(probe, id, false); + return probe->interior; +} + +void R3D_SetProbeInterior(R3D_Probe id, bool active) +{ + GET_PROBE_OR_RETURN(probe, id); + + if (probe->interior != active) { + probe->state.sceneShouldBeUpdated = true; + probe->interior = active; + } +} + +bool R3D_GetProbeShadows(R3D_Probe id) +{ + GET_PROBE_OR_RETURN(probe, id, false); + return probe->shadows; +} + +void R3D_SetProbeShadows(R3D_Probe id, bool active) +{ + GET_PROBE_OR_RETURN(probe, id); + + if (probe->shadows != active) { + probe->state.sceneShouldBeUpdated = true; + probe->shadows = active; + } +} + +Vector3 R3D_GetProbePosition(R3D_Probe id) +{ + GET_PROBE_OR_RETURN(probe, id, (Vector3){0}); + return probe->position; +} + +void R3D_SetProbePosition(R3D_Probe id, Vector3 position) +{ + GET_PROBE_OR_RETURN(probe, id); + + if (!Vector3Equals(probe->position, position)) { + probe->state.matrixShouldBeUpdated = true; + probe->state.sceneShouldBeUpdated = true; + probe->position = position; + } +} + +float R3D_GetProbeRange(R3D_Probe id) +{ + GET_PROBE_OR_RETURN(probe, id, 0.0f); + return probe->range; +} + +void R3D_SetProbeRange(R3D_Probe id, float range) +{ + GET_PROBE_OR_RETURN(probe, id); + + if (probe->range != range) { + probe->state.matrixShouldBeUpdated = true; + probe->state.sceneShouldBeUpdated = true; + probe->range = range; + } +} + +float R3D_GetProbeFalloff(R3D_Probe id) +{ + GET_PROBE_OR_RETURN(probe, id, 0.0f); + return probe->falloff; +} + +void R3D_SetProbeFalloff(R3D_Probe id, float falloff) +{ + GET_PROBE_OR_RETURN(probe, id); + + if (probe->falloff != falloff) { + probe->state.sceneShouldBeUpdated = true; + probe->falloff = falloff; + } +} + +``` + +`src/r3d_screen_shader.c`: + +```c +/* r3d_screen_shader.c -- R3D Screen Shader Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#include "./modules/r3d_shader.h" +#include "./common/r3d_rshade.h" +#include "./common/r3d_helper.h" +#include "./r3d_core_state.h" + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +static bool compile_shader(R3D_ScreenShader* shader); + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_ScreenShader* R3D_LoadScreenShader(const char* filePath) +{ + char* code = LoadFileText(filePath); + if (code == NULL) { + R3D_TRACELOG(LOG_ERROR, "Failed to load screen shader; Unable to load shader file"); + return NULL; + } + + R3D_ScreenShader* shader = R3D_LoadScreenShaderFromMemory(code); + UnloadFileText(code); + + return shader; +} + +R3D_ScreenShader* R3D_LoadScreenShaderFromMemory(const char* code) +{ + size_t userCodeLen = strlen(code); + if (userCodeLen > R3D_MAX_SHADER_CODE_LENGTH) { + R3D_TRACELOG(LOG_ERROR, "Failed to load screen shader; User code too long"); + return NULL; + } + + if (!strstr(code, "void fragment()")) { + R3D_TRACELOG(LOG_WARNING, "Failed to load screen shader; Missing fragment() entry point"); + return NULL; + } + + R3D_ScreenShader* shader = r3d_shader_custom_alloc(); + if (!shader) { + R3D_TRACELOG(LOG_ERROR, "Bad alloc during screen shader loading"); + return NULL; + } + + int uniformCount = 0; + int samplerCount = 0; + int currentOffset = 0; + + r3d_rshade_parsed_function_t fragmentFunc = {0}; + + /* --- PHASE 1: Parse user code and collect metadata --- */ + + const char* ptr = code; + + while (*ptr) + { + r3d_rshade_skip_whitespace_and_comments(&ptr); + if (!*ptr) break; + + // Parse uniform declarations + if (r3d_rshade_match_keyword(ptr, "uniform", 7)) { + ptr += 7; + r3d_rshade_parse_uniform(&ptr, + shader->data.samplers, + &shader->data.uniforms, + &samplerCount, + &uniformCount, + ¤tOffset, + R3D_MAX_SHADER_SAMPLERS, + R3D_MAX_SHADER_UNIFORMS + ); + continue; + } + + // Parse fragment() function + r3d_rshade_parsed_function_t* func = r3d_rshade_check_shader_entry(ptr, NULL, &fragmentFunc); + if (func) { + r3d_rshade_skip_to_brace(&ptr); + if (*ptr == '{') { + func->bodyStart = ptr; + r3d_rshade_skip_to_matching_brace(&ptr); + func->bodyEnd = ptr; + } + continue; + } + + ptr++; + } + + /* --- PHASE 2: Generate transformed shader code --- */ + + r3d_rshade_writer_t writer = { + .ptr = shader->program->userCode, + .remaining = R3D_MAX_SHADER_CODE_LENGTH, + .overflow = false, + }; + + // Write uniform block and samplers + r3d_rshade_write_uniform_block(&writer, shader->data.uniforms.entries, uniformCount); + r3d_rshade_write_samplers(&writer, shader->data.samplers, samplerCount); + + // Copy global code (excluding comments, uniforms, fragment()) then write fragment stage section + r3d_rshade_copy_global_code(&writer, code, false, NULL, &fragmentFunc); + r3d_rshade_write_shader_function(&writer, "fragment", &fragmentFunc); + + if (writer.overflow) { + R3D_TRACELOG(LOG_ERROR, "Failed to load screen shader; Transformed code too long"); + RL_FREE(shader); + return NULL; + } + + *writer.ptr = '\0'; + + /* --- PHASE 3: Compile shader --- */ + + if (!compile_shader(shader)) { + R3D_UnloadScreenShader(shader); + return NULL; + } + + /* --- PHASE 4: Initialize uniform buffer --- */ + + r3d_shader_custom_init_uniforms(shader, currentOffset); + + R3D_TRACELOG(LOG_INFO, "Screen shader loaded successfully"); + R3D_TRACELOG(LOG_INFO, " > Sampler count: %i", samplerCount); + R3D_TRACELOG(LOG_INFO, " > Uniform count: %i", uniformCount); + + return shader; +} + +R3D_ScreenShader* R3D_LoadScreenShaderAlias(R3D_ScreenShader* shader) +{ + R3D_ScreenShader* alias = r3d_shader_custom_clone(shader); + if (!alias) { + R3D_TRACELOG(LOG_ERROR, "Bad alloc during screen shader alias loading"); + return NULL; + } + return alias; +} + +void R3D_UnloadScreenShader(R3D_ScreenShader* shader) +{ + r3d_shader_custom_free(shader); +} + +void R3D_SetScreenShaderUniform(R3D_ScreenShader* shader, const char* name, const void* value) +{ + if (!shader) { + R3D_TRACELOG(LOG_WARNING, "Cannot set uniform '%s' on NULL screen shader", name); + return; + } + + if (!r3d_shader_custom_set_uniform(shader, name, value)) { + R3D_TRACELOG(LOG_WARNING, "Failed to set custom uniform '%s'", name); + } +} + +void R3D_SetScreenShaderSampler(R3D_ScreenShader* shader, const char* name, Texture texture) +{ + if (!shader) { + R3D_TRACELOG(LOG_WARNING, "Cannot set sampler '%s' on NULL screen shader", name); + return; + } + + if (!r3d_shader_custom_set_sampler(shader, name, texture)) { + R3D_TRACELOG(LOG_WARNING, "Failed to set custom sampler '%s'", name); + } +} + +void R3D_SetScreenShaderChain(R3D_ScreenShaderStage stage, R3D_ScreenShader** shaders, int count) +{ + if (stage < 0 || stage >= R3D_SCREEN_SHADER_STAGE_COUNT) { + R3D_TRACELOG(LOG_WARNING, "Invalid screen shader stage: %d", stage); + } + + size_t maxChain = ARRAY_SIZE(R3D.screenShaders[stage]); + + if (count > (int)maxChain) { + R3D_TRACELOG( + LOG_WARNING, + "Too many screen shaders provided for chain: %d / %zu", + count, + maxChain + ); + + count = (int)maxChain; + } + + memset(R3D.screenShaders[stage], 0, sizeof(R3D.screenShaders[stage])); + if (shaders == NULL) return; + + for (int i = 0; i < count; i++) { + R3D.screenShaders[stage][i] = shaders[i]; + } +} + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +bool compile_shader(R3D_ScreenShader* shader) +{ + bool ok = R3D_MOD_SHADER_LOADER.post.screen(shader); + if (!ok) { + R3D_TRACELOG(LOG_ERROR, "Failed to compile screen shader"); + } + return ok; +} + +``` + +`src/r3d_shape.c`: + +```c +/* r3d_shape.c -- R3D Shape Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include + +#include "./common/r3d_math.h" + +// ======================================== +// INLINE FUNCTIONS +// ======================================== + +static inline bool raycast_triangle( + float* outT, Vector3* outEdge1, Vector3* outEdge2, + Vector3 localOrigin, Vector3 localDirection, + Vector3 v0, Vector3 v1, Vector3 v2) +{ + // Möller-Trumbore ray triangle intersection algorithm + // See: https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm + + Vector3 edge1 = Vector3Subtract(v1, v0); + Vector3 edge2 = Vector3Subtract(v2, v0); + + // If negative: it's a backface + // If zero: ray parallel to triangle + Vector3 h = Vector3CrossProduct(localDirection, edge2); + float a = Vector3DotProduct(edge1, h); + if (a < 1e-5f) return false; + + float f = 1.0f / a; + + Vector3 s = Vector3Subtract(localOrigin, v0); + float u = f * Vector3DotProduct(s, h); + if (u < 0.0f || u > 1.0f) return false; + + Vector3 q = Vector3CrossProduct(s, edge1); + float v = f * Vector3DotProduct(localDirection, q); + if (v < 0.0f || u + v > 1.0f) return false; + + float t = f * Vector3DotProduct(edge2, q); + if (t < 1e-5f) return false; + + *outT = t; + *outEdge1 = edge1; + *outEdge2 = edge2; + + return true; +} + +static inline void raycast_mesh_vertices( + float* closestT, Vector3* closestEdge1, Vector3* closestEdge2, + const R3D_Vertex* vertices, int triangleCount, + Vector3 localOrigin, Vector3 localDirection) +{ + for (int i = 0; i < triangleCount; i++) + { + int baseIdx = i * 3; + Vector3 v0 = vertices[baseIdx].position; + Vector3 v1 = vertices[baseIdx + 1].position; + Vector3 v2 = vertices[baseIdx + 2].position; + + float t; + Vector3 edge1, edge2; + if (raycast_triangle(&t, &edge1, &edge2, localOrigin, localDirection, v0, v1, v2)) { + if (t < *closestT) { + *closestT = t; + *closestEdge1 = edge1; + *closestEdge2 = edge2; + } + } + } +} + +static inline void raycast_mesh_indexed( + float* closestT, Vector3* closestEdge1, Vector3* closestEdge2, + const R3D_Vertex* vertices, const uint32_t* indices, int triangleCount, + Vector3 localOrigin, Vector3 localDirection) +{ + for (int i = 0; i < triangleCount; i++) + { + int baseIdx = i * 3; + Vector3 v0 = vertices[indices[baseIdx]].position; + Vector3 v1 = vertices[indices[baseIdx + 1]].position; + Vector3 v2 = vertices[indices[baseIdx + 2]].position; + + float t; + Vector3 edge1, edge2; + if (raycast_triangle(&t, &edge1, &edge2, localOrigin, localDirection, v0, v1, v2)) { + if (t < *closestT) { + *closestT = t; + *closestEdge1 = edge1; + *closestEdge2 = edge2; + } + } + } +} + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_BoundingBox R3D_GetBoundingBox(Vector3 center, Vector3 halfExtents) +{ + R3D_BoundingBox aabb; + + aabb.min = Vector3Subtract(center, halfExtents); + aabb.max = Vector3Add(center, halfExtents); + + return aabb; +} + +R3D_OrientedBox R3D_GetOrientedBox(BoundingBox aabb, Matrix transform) +{ + R3D_OrientedBox obb; + + obb.halfExtents.x = (aabb.max.x - aabb.min.x) * 0.5f; + obb.halfExtents.y = (aabb.max.y - aabb.min.y) * 0.5f; + obb.halfExtents.z = (aabb.max.z - aabb.min.z) * 0.5f; + + Vector3 localCenter = { + (aabb.min.x + aabb.max.x) * 0.5f, + (aabb.min.y + aabb.max.y) * 0.5f, + (aabb.min.z + aabb.max.z) * 0.5f + }; + + obb.center.x = transform.m0*localCenter.x + transform.m4*localCenter.y + transform.m8*localCenter.z + transform.m12; + obb.center.y = transform.m1*localCenter.x + transform.m5*localCenter.y + transform.m9*localCenter.z + transform.m13; + obb.center.z = transform.m2*localCenter.x + transform.m6*localCenter.y + transform.m10*localCenter.z + transform.m14; + + obb.axisX = (Vector3){transform.m0, transform.m1, transform.m2}; + obb.axisY = (Vector3){transform.m4, transform.m5, transform.m6}; + obb.axisZ = (Vector3){transform.m8, transform.m9, transform.m10}; + + return obb; +} + +bool R3D_CheckCollisionBoundingBoxes(R3D_BoundingBox box1, R3D_BoundingBox box2) +{ + return (box1.min.x <= box2.max.x && box1.max.x >= box2.min.x) + && (box1.min.y <= box2.max.y && box1.max.y >= box2.min.y) + && (box1.min.z <= box2.max.z && box1.max.z >= box2.min.z); +} + +bool R3D_CheckCollisionBoundingBoxSphere(R3D_BoundingBox box, Vector3 center, float radius) +{ + float cx = center.x < box.min.x ? box.min.x : (center.x > box.max.x ? box.max.x : center.x); + float cy = center.y < box.min.y ? box.min.y : (center.y > box.max.y ? box.max.y : center.y); + float cz = center.z < box.min.z ? box.min.z : (center.z > box.max.z ? box.max.z : center.z); + float dx = center.x - cx; + float dy = center.y - cy; + float dz = center.z - cz; + return (dx*dx + dy*dy + dz*dz) <= (radius * radius); +} + +bool R3D_CheckCollisionOrientedBoxes(R3D_OrientedBox box1, R3D_OrientedBox box2) +{ + Vector3 axes[15] = { + box1.axisX, box1.axisY, box1.axisZ, + box2.axisX, box2.axisY, box2.axisZ, + Vector3CrossProduct(box1.axisX, box2.axisX), + Vector3CrossProduct(box1.axisX, box2.axisY), + Vector3CrossProduct(box1.axisX, box2.axisZ), + Vector3CrossProduct(box1.axisY, box2.axisX), + Vector3CrossProduct(box1.axisY, box2.axisY), + Vector3CrossProduct(box1.axisY, box2.axisZ), + Vector3CrossProduct(box1.axisZ, box2.axisX), + Vector3CrossProduct(box1.axisZ, box2.axisY), + Vector3CrossProduct(box1.axisZ, box2.axisZ), + }; + + Vector3 d = Vector3Subtract(box2.center, box1.center); + + for (int i = 0; i < 15; i++) { + if (Vector3LengthSqr(axes[i]) < 1e-10f) { + continue; + } + + Vector3 axis = Vector3Normalize(axes[i]); + + float r1 = fabsf(Vector3DotProduct(box1.axisX, axis)) * box1.halfExtents.x + + fabsf(Vector3DotProduct(box1.axisY, axis)) * box1.halfExtents.y + + fabsf(Vector3DotProduct(box1.axisZ, axis)) * box1.halfExtents.z; + + float r2 = fabsf(Vector3DotProduct(box2.axisX, axis)) * box2.halfExtents.x + + fabsf(Vector3DotProduct(box2.axisY, axis)) * box2.halfExtents.y + + fabsf(Vector3DotProduct(box2.axisZ, axis)) * box2.halfExtents.z; + + if (fabsf(Vector3DotProduct(d, axis)) > r1 + r2) { + return false; + } + } + + return true; +} + +bool R3D_CheckCollisionOrientedBoxSphere(R3D_OrientedBox box, Vector3 center, float radius) +{ + Vector3 d = Vector3Subtract(center, box.center); + + Vector3 local = { + Vector3DotProduct(d, box.axisX), + Vector3DotProduct(d, box.axisY), + Vector3DotProduct(d, box.axisZ) + }; + + Vector3 closest = { + Clamp(local.x, -box.halfExtents.x, box.halfExtents.x), + Clamp(local.y, -box.halfExtents.y, box.halfExtents.y), + Clamp(local.z, -box.halfExtents.z, box.halfExtents.z) + }; + + Vector3 delta = Vector3Subtract(local, closest); + return Vector3LengthSqr(delta) <= radius * radius; +} + +bool R3D_CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2) +{ + float dx = center2.x - center1.x; + float dy = center2.y - center1.y; + float dz = center2.z - center1.z; + float r = radius1 + radius2; + return (dx*dx + dy*dy + dz*dz) <= (r * r); +} + +bool R3D_CheckCollisionCapsuleBoundingBox(R3D_Capsule capsule, BoundingBox box) +{ + Vector3 closestOnSegment = R3D_ClosestPointOnSegment( + R3D_ClosestPointOnBox(capsule.start, box), + capsule.start, + capsule.end + ); + + Vector3 closestOnBox = R3D_ClosestPointOnBox(closestOnSegment, box); + float distSq = Vector3DistanceSqr(closestOnBox, closestOnSegment); + + return distSq <= (capsule.radius * capsule.radius); +} + +bool R3D_CheckCollisionCapsuleOrientedBox(R3D_Capsule capsule, R3D_OrientedBox box) +{ + Vector3 ds = Vector3Subtract(capsule.start, box.center); + Vector3 de = Vector3Subtract(capsule.end, box.center); + + Vector3 localStart = { + Vector3DotProduct(ds, box.axisX), + Vector3DotProduct(ds, box.axisY), + Vector3DotProduct(ds, box.axisZ) + }; + Vector3 localEnd = { + Vector3DotProduct(de, box.axisX), + Vector3DotProduct(de, box.axisY), + Vector3DotProduct(de, box.axisZ) + }; + + BoundingBox localBox = { + Vector3Negate(box.halfExtents), + box.halfExtents + }; + + float rSq = capsule.radius * capsule.radius; + + Vector3 seeds[3] = { + localStart, + localEnd, + R3D_ClosestPointOnSegment((Vector3){0}, localStart, localEnd), + }; + + for (int i = 0; i < 3; i++) { + Vector3 onSeg = seeds[i]; + for (int iter = 0; iter < 3; iter++) { + Vector3 onBox = R3D_ClosestPointOnBox(onSeg, localBox); + onSeg = R3D_ClosestPointOnSegment(onBox, localStart, localEnd); + } + Vector3 onBox = R3D_ClosestPointOnBox(onSeg, localBox); + if (Vector3DistanceSqr(onSeg, onBox) <= rSq) return true; + } + + return false; +} + +bool R3D_CheckCollisionCapsuleSphere(R3D_Capsule capsule, Vector3 center, float radius) +{ + Vector3 closestPoint = R3D_ClosestPointOnSegment(center, capsule.start, capsule.end); + float distSq = Vector3DistanceSqr(center, closestPoint); + float radiusSum = capsule.radius + radius; + + return distSq <= (radiusSum * radiusSum); +} + +bool R3D_CheckCollisionCapsules(R3D_Capsule a, R3D_Capsule b) +{ + Vector3 dirA = Vector3Subtract(a.end, a.start); + Vector3 dirB = Vector3Subtract(b.end, b.start); + Vector3 r = Vector3Subtract(a.start, b.start); + + float dotAA = Vector3DotProduct(dirA, dirA); + float dotBB = Vector3DotProduct(dirB, dirB); + float dotAB = Vector3DotProduct(dirA, dirB); + float dotAR = Vector3DotProduct(dirA, r); + float dotBR = Vector3DotProduct(dirB, r); + + float denom = dotAA * dotBB - dotAB * dotAB; + float s = 0.0f, t = 0.0f; + + if (denom > 1e-6f) { + s = (dotAB * dotBR - dotBB * dotAR) / denom; + s = fmaxf(0.0f, fminf(1.0f, s)); + t = (dotAB * s + dotBR) / dotBB; + + if (t < 0.0f) { + t = 0.0f; + s = fmaxf(0.0f, fminf(1.0f, -dotAR / dotAA)); + } + else if (t > 1.0f) { + t = 1.0f; + s = fmaxf(0.0f, fminf(1.0f, (dotAB - dotAR) / dotAA)); + } + } + else { + s = 0.5f; + t = fmaxf(0.0f, fminf(1.0f, dotBR / dotBB)); + } + + Vector3 closestA = Vector3Add(a.start, Vector3Scale(dirA, s)); + Vector3 closestB = Vector3Add(b.start, Vector3Scale(dirB, t)); + + float distSq = Vector3DistanceSqr(closestA, closestB); + float radiusSum = a.radius + b.radius; + + return distSq <= (radiusSum * radiusSum); +} + +bool R3D_CheckCollisionCapsuleMesh(R3D_Capsule capsule, R3D_MeshData mesh, Matrix transform) +{ + Vector3 axis = Vector3Subtract(capsule.end, capsule.start); + float radiusSq = capsule.radius * capsule.radius; + + bool useIndices = (mesh.indices != NULL); + int triangleCount = useIndices ? (mesh.indexCount / 3) : (mesh.vertexCount / 3); + + for (int i = 0; i < triangleCount; i++) + { + Vector3 v0, v1, v2; + + if (useIndices) { + v0 = mesh.vertices[mesh.indices[i*3 ]].position; + v1 = mesh.vertices[mesh.indices[i*3 + 1]].position; + v2 = mesh.vertices[mesh.indices[i*3 + 2]].position; + } + else { + v0 = mesh.vertices[i*3 ].position; + v1 = mesh.vertices[i*3 + 1].position; + v2 = mesh.vertices[i*3 + 2].position; + } + + Vector3 a = r3d_vector3_transform(mesh.vertices[mesh.indices[i]].position, &transform); + Vector3 b = r3d_vector3_transform(mesh.vertices[mesh.indices[i+1]].position, &transform); + Vector3 c = r3d_vector3_transform(mesh.vertices[mesh.indices[i+2]].position, &transform); + + const int samples = 5; + for (int s = 0; s < samples; s++) + { + float t = (float)s / (samples - 1); + Vector3 p = Vector3Add(capsule.start, Vector3Scale(axis, t)); + + Vector3 closest = R3D_ClosestPointOnTriangle(p, a, b, c); + if (Vector3LengthSqr(Vector3Subtract(closest, p)) <= radiusSq) { + return true; + } + } + } + + return false; +} + +R3D_Penetration R3D_CheckPenetrationBoundingBoxes(R3D_BoundingBox box1, R3D_BoundingBox box2) +{ + float ox = fminf(box1.max.x, box2.max.x) - fmaxf(box1.min.x, box2.min.x); + if (ox <= 0) return (R3D_Penetration){0}; + + float oy = fminf(box1.max.y, box2.max.y) - fmaxf(box1.min.y, box2.min.y); + if (oy <= 0) return (R3D_Penetration){0}; + + float oz = fminf(box1.max.z, box2.max.z) - fmaxf(box1.min.z, box2.min.z); + if (oz <= 0) return (R3D_Penetration){0}; + + float dx = (box2.min.x + box2.max.x) - (box1.min.x + box1.max.x); + float dy = (box2.min.y + box2.max.y) - (box1.min.y + box1.max.y); + float dz = (box2.min.z + box2.max.z) - (box1.min.z + box1.max.z); + + Vector3 normal; + float depth; + + if (ox <= oy && ox <= oz) { + depth = ox; + normal = (Vector3) {dx >= 0 ? 1.0f : -1.0f, 0, 0}; + } + else if (oy <= oz) { + depth = oy; + normal = (Vector3) {0, dy >= 0 ? 1.0f : -1.0f, 0}; + } + else { + depth = oz; + normal = (Vector3) {0, 0, dz >= 0 ? 1.0f : -1.0f}; + } + + return (R3D_Penetration) { + .collides = true, .depth = depth, + .normal = normal, .mtv = Vector3Scale(normal, depth), + }; +} + +R3D_Penetration R3D_CheckPenetrationBoundingBoxSphere(R3D_BoundingBox box, Vector3 center, float radius) +{ + float cx = center.x < box.min.x ? box.min.x : (center.x > box.max.x ? box.max.x : center.x); + float cy = center.y < box.min.y ? box.min.y : (center.y > box.max.y ? box.max.y : center.y); + float cz = center.z < box.min.z ? box.min.z : (center.z > box.max.z ? box.max.z : center.z); + + float dx = center.x - cx, dy = center.y - cy, dz = center.z - cz; + float distSq = dx*dx + dy*dy + dz*dz; + + if (distSq > radius * radius) { + return (R3D_Penetration){0}; + } + + Vector3 normal; + float depth; + + if (distSq > 1e-10f) { + float dist = sqrtf(distSq); + normal = (Vector3){ dx/dist, dy/dist, dz/dist }; + depth = radius - dist; + } + else { + float d[6] = { + center.x - box.min.x, box.max.x - center.x, + center.y - box.min.y, box.max.y - center.y, + center.z - box.min.z, box.max.z - center.z, + }; + Vector3 normals[6] = { + {-1,0,0}, {1,0,0}, {0,-1,0}, + {0,1,0}, {0,0,-1}, {0,0,1} + }; + int best = 0; + for (int i = 1; i < 6; i++) { + if (d[i] < d[best]) best = i; + } + normal = normals[best]; + depth = radius + d[best]; + } + + return (R3D_Penetration) { + .collides = true, .depth = depth, + .normal = normal, .mtv = Vector3Scale(normal, depth), + }; +} + +R3D_Penetration R3D_CheckPenetrationOrientedBoxes(R3D_OrientedBox box1, R3D_OrientedBox box2) +{ + Vector3 d = Vector3Subtract(box2.center, box1.center); + + Vector3 axes[15] = { + box1.axisX, box1.axisY, box1.axisZ, + box2.axisX, box2.axisY, box2.axisZ, + Vector3CrossProduct(box1.axisX, box2.axisX), + Vector3CrossProduct(box1.axisX, box2.axisY), + Vector3CrossProduct(box1.axisX, box2.axisZ), + Vector3CrossProduct(box1.axisY, box2.axisX), + Vector3CrossProduct(box1.axisY, box2.axisY), + Vector3CrossProduct(box1.axisY, box2.axisZ), + Vector3CrossProduct(box1.axisZ, box2.axisX), + Vector3CrossProduct(box1.axisZ, box2.axisY), + Vector3CrossProduct(box1.axisZ, box2.axisZ), + }; + + float minDepth = FLT_MAX; + Vector3 minAxis = {0, 1, 0}; + + for (int i = 0; i < 15; i++) { + if (Vector3LengthSqr(axes[i]) < 1e-10f) continue; + Vector3 axis = Vector3Normalize(axes[i]); + + float r1 = fabsf(Vector3DotProduct(box1.axisX, axis)) * box1.halfExtents.x + + fabsf(Vector3DotProduct(box1.axisY, axis)) * box1.halfExtents.y + + fabsf(Vector3DotProduct(box1.axisZ, axis)) * box1.halfExtents.z; + + float r2 = fabsf(Vector3DotProduct(box2.axisX, axis)) * box2.halfExtents.x + + fabsf(Vector3DotProduct(box2.axisY, axis)) * box2.halfExtents.y + + fabsf(Vector3DotProduct(box2.axisZ, axis)) * box2.halfExtents.z; + + float overlap = r1 + r2 - fabsf(Vector3DotProduct(d, axis)); + if (overlap <= 0) return (R3D_Penetration){0}; + + if (overlap < minDepth) { + minDepth = overlap; + minAxis = axis; + } + } + + // Ensure normal points from box1 toward box2 + if (Vector3DotProduct(minAxis, d) < 0) { + minAxis = Vector3Negate(minAxis); + } + + return (R3D_Penetration){ + .collides = true, .depth = minDepth, + .normal = minAxis, .mtv = Vector3Scale(minAxis, minDepth), + }; +} + +R3D_Penetration R3D_CheckPenetrationOrientedBoxSphere(R3D_OrientedBox box, Vector3 center, float radius) +{ + Vector3 d = Vector3Subtract(center, box.center); + Vector3 local = { + Vector3DotProduct(d, box.axisX), + Vector3DotProduct(d, box.axisY), + Vector3DotProduct(d, box.axisZ), + }; + + R3D_BoundingBox localBox = {Vector3Negate(box.halfExtents), box.halfExtents}; + R3D_Penetration p = R3D_CheckPenetrationBoundingBoxSphere(localBox, local, radius); + if (!p.collides) return (R3D_Penetration){0}; + + Vector3 worldNormal = { + box.axisX.x * p.normal.x + box.axisY.x * p.normal.y + box.axisZ.x * p.normal.z, + box.axisX.y * p.normal.x + box.axisY.y * p.normal.y + box.axisZ.y * p.normal.z, + box.axisX.z * p.normal.x + box.axisY.z * p.normal.y + box.axisZ.z * p.normal.z, + }; + + return (R3D_Penetration){ + .collides = true, .depth = p.depth, + .normal = worldNormal, .mtv = Vector3Scale(worldNormal, p.depth), + }; +} + +R3D_Penetration R3D_CheckPenetrationSpheres(Vector3 c1, float r1, Vector3 c2, float r2) +{ + float dx = c2.x - c1.x, dy = c2.y - c1.y, dz = c2.z - c1.z; + float distSq = dx*dx + dy*dy + dz*dz; + float rSum = r1 + r2; + + if (distSq >= rSum * rSum) { + return (R3D_Penetration){0}; + } + + float dist = sqrtf(distSq); + Vector3 normal = dist > 1e-6f + ? (Vector3) {dx/dist, dy/dist, dz/dist} + : (Vector3) {0, 1, 0}; + + float depth = rSum - dist; + + return (R3D_Penetration) { + .collides = true, .depth = depth, + .normal = normal, .mtv = Vector3Scale(normal, depth), + }; +} + +R3D_Penetration R3D_CheckPenetrationCapsuleBoundingBox(R3D_Capsule capsule, BoundingBox box) +{ + R3D_Penetration result = {0}; + + Vector3 closestOnSegment = R3D_ClosestPointOnSegment( + R3D_ClosestPointOnBox(capsule.start, box), + capsule.start, + capsule.end + ); + + Vector3 closestOnBox = R3D_ClosestPointOnBox(closestOnSegment, box); + Vector3 delta = Vector3Subtract(closestOnSegment, closestOnBox); + + float distSq = Vector3LengthSqr(delta); + float radiusSq = capsule.radius * capsule.radius; + + if (distSq >= radiusSq) return result; + + float dist = sqrtf(distSq); + result.collides = true; + result.depth = capsule.radius - dist; + + if (dist > 1e-6f) { + result.normal = Vector3Scale(delta, 1.0f / dist); + } + else { + Vector3 boxCenter = { + (box.min.x + box.max.x) * 0.5f, + (box.min.y + box.max.y) * 0.5f, + (box.min.z + box.max.z) * 0.5f + }; + Vector3 toCenter = Vector3Subtract(closestOnSegment, boxCenter); + + float ax = fabsf(toCenter.x); + float ay = fabsf(toCenter.y); + float az = fabsf(toCenter.z); + + if (ax >= ay && ax >= az) { + result.normal = (Vector3){toCenter.x > 0 ? 1.0f : -1.0f, 0, 0}; + } + else if (ay >= az) { + result.normal = (Vector3){0, toCenter.y > 0 ? 1.0f : -1.0f, 0}; + } + else { + result.normal = (Vector3){0, 0, toCenter.z > 0 ? 1.0f : -1.0f}; + } + } + + result.mtv = Vector3Scale(result.normal, result.depth); + return result; +} + +R3D_Penetration R3D_CheckPenetrationCapsuleOrientedBox(R3D_Capsule capsule, R3D_OrientedBox box) +{ + Vector3 ds = Vector3Subtract(capsule.start, box.center); + Vector3 de = Vector3Subtract(capsule.end, box.center); + + Vector3 localStart = { + Vector3DotProduct(ds, box.axisX), + Vector3DotProduct(ds, box.axisY), + Vector3DotProduct(ds, box.axisZ), + }; + Vector3 localEnd = { + Vector3DotProduct(de, box.axisX), + Vector3DotProduct(de, box.axisY), + Vector3DotProduct(de, box.axisZ), + }; + + BoundingBox localBox = {Vector3Negate(box.halfExtents), box.halfExtents}; + + float rSq = capsule.radius * capsule.radius; + + Vector3 seeds[3] = { + localStart, + localEnd, + R3D_ClosestPointOnSegment((Vector3){0}, localStart, localEnd), + }; + + float bestDistSq = FLT_MAX; + Vector3 bestOnSeg = localStart; + Vector3 bestOnBox = R3D_ClosestPointOnBox(localStart, localBox); + + for (int i = 0; i < 3; i++) { + Vector3 onSeg = seeds[i]; + for (int iter = 0; iter < 3; iter++) { + Vector3 onBox = R3D_ClosestPointOnBox(onSeg, localBox); + onSeg = R3D_ClosestPointOnSegment(onBox, localStart, localEnd); + } + Vector3 onBox = R3D_ClosestPointOnBox(onSeg, localBox); + float distSq = Vector3DistanceSqr(onSeg, onBox); + + if (distSq < bestDistSq) { + bestDistSq = distSq; + bestOnSeg = onSeg; + bestOnBox = onBox; + } + } + + if (bestDistSq > rSq) { + return (R3D_Penetration){0}; + } + + Vector3 localNormal; + float depth; + + if (bestDistSq > 1e-10f) { + float dist = sqrtf(bestDistSq); + Vector3 d = Vector3Subtract(bestOnSeg, bestOnBox); + localNormal = (Vector3){ d.x/dist, d.y/dist, d.z/dist }; + depth = capsule.radius - dist; + } + else { + float faces[6] = { + bestOnSeg.x - localBox.min.x, localBox.max.x - bestOnSeg.x, + bestOnSeg.y - localBox.min.y, localBox.max.y - bestOnSeg.y, + bestOnSeg.z - localBox.min.z, localBox.max.z - bestOnSeg.z, + }; + Vector3 faceNormals[6] = { + {-1,0,0}, {1,0,0}, {0,-1,0}, + {0,1,0}, {0,0,-1}, {0,0,1} + }; + int best = 0; + for (int i = 1; i < 6; i++) { + if (faces[i] < faces[best]) best = i; + } + localNormal = faceNormals[best]; + depth = capsule.radius + faces[best]; + } + + Vector3 worldNormal = { + box.axisX.x * localNormal.x + box.axisY.x * localNormal.y + box.axisZ.x * localNormal.z, + box.axisX.y * localNormal.x + box.axisY.y * localNormal.y + box.axisZ.y * localNormal.z, + box.axisX.z * localNormal.x + box.axisY.z * localNormal.y + box.axisZ.z * localNormal.z, + }; + + return (R3D_Penetration) { + .collides = true, .depth = depth, .normal = worldNormal, + .mtv = Vector3Scale(worldNormal, depth), + }; +} + +R3D_Penetration R3D_CheckPenetrationCapsuleSphere(R3D_Capsule capsule, Vector3 center, float radius) +{ + R3D_Penetration result = {0}; + + Vector3 closestOnSegment = R3D_ClosestPointOnSegment(center, capsule.start, capsule.end); + Vector3 delta = Vector3Subtract(center, closestOnSegment); + + float distSq = Vector3LengthSqr(delta); + float combinedRadius = capsule.radius + radius; + float combinedRadiusSq = combinedRadius * combinedRadius; + + if (distSq >= combinedRadiusSq) return result; + + float dist = sqrtf(distSq); + result.collides = true; + result.depth = combinedRadius - dist; + + if (dist > 1e-6f) { + result.normal = Vector3Scale(delta, 1.0f / dist); + } + else { + Vector3 capsuleDir = Vector3Subtract(capsule.end, capsule.start); + float capsuleLengthSq = Vector3LengthSqr(capsuleDir); + + if (capsuleLengthSq > 1e-6f) { + result.normal = (Vector3){capsuleDir.y, -capsuleDir.x, 0}; + float normalLengthSq = Vector3LengthSqr(result.normal); + + if (normalLengthSq < 1e-6f) { + result.normal = (Vector3){0, capsuleDir.z, -capsuleDir.y}; + normalLengthSq = Vector3LengthSqr(result.normal); + } + + if (normalLengthSq > 1e-6f) { + result.normal = Vector3Normalize(result.normal); + } + else { + result.normal = (Vector3){0, 1, 0}; + } + } + else { + result.normal = (Vector3){0, 1, 0}; + } + } + + result.mtv = Vector3Scale(result.normal, result.depth); + return result; +} + +R3D_Penetration R3D_CheckPenetrationCapsules(R3D_Capsule a, R3D_Capsule b) +{ + R3D_Penetration result = {0}; + + Vector3 dirA = Vector3Subtract(a.end, a.start); + Vector3 dirB = Vector3Subtract(b.end, b.start); + Vector3 r = Vector3Subtract(a.start, b.start); + + float dotAA = Vector3DotProduct(dirA, dirA); + float dotBB = Vector3DotProduct(dirB, dirB); + float dotAB = Vector3DotProduct(dirA, dirB); + float dotAR = Vector3DotProduct(dirA, r); + float dotBR = Vector3DotProduct(dirB, r); + + float denom = dotAA * dotBB - dotAB * dotAB; + + float s = 0.0f, t = 0.0f; + + if (denom > 1e-6f) { + s = (dotAB * dotBR - dotBB * dotAR) / denom; + s = fmaxf(0.0f, fminf(1.0f, s)); + t = (dotAB * s + dotBR) / dotBB; + + if (t < 0.0f) { + t = 0.0f; + s = fmaxf(0.0f, fminf(1.0f, -dotAR / dotAA)); + } + else if (t > 1.0f) { + t = 1.0f; + s = fmaxf(0.0f, fminf(1.0f, (dotAB - dotAR) / dotAA)); + } + } + else { + s = 0.5f; + t = fmaxf(0.0f, fminf(1.0f, dotBR / dotBB)); + } + + Vector3 closestA = Vector3Add(a.start, Vector3Scale(dirA, s)); + Vector3 closestB = Vector3Add(b.start, Vector3Scale(dirB, t)); + + Vector3 delta = Vector3Subtract(closestA, closestB); + float distSq = Vector3LengthSqr(delta); + float combinedRadius = a.radius + b.radius; + float combinedRadiusSq = combinedRadius * combinedRadius; + + if (distSq >= combinedRadiusSq) return result; + + float dist = sqrtf(distSq); + result.collides = true; + result.depth = combinedRadius - dist; + + if (dist > 1e-6f) { + result.normal = Vector3Scale(delta, 1.0f / dist); + } + else { + Vector3 cross = Vector3CrossProduct(dirA, dirB); + float crossLengthSq = Vector3LengthSqr(cross); + + if (crossLengthSq > 1e-6f) { + result.normal = Vector3Normalize(cross); + } + else { + Vector3 perp = (Vector3){dirA.y, -dirA.x, 0}; + float perpLengthSq = Vector3LengthSqr(perp); + + if (perpLengthSq < 1e-6f) { + perp = (Vector3){0, dirA.z, -dirA.y}; + perpLengthSq = Vector3LengthSqr(perp); + } + + if (perpLengthSq > 1e-6f) { + result.normal = Vector3Normalize(perp); + } + else { + result.normal = (Vector3){0, 1, 0}; + } + } + } + + result.mtv = Vector3Scale(result.normal, result.depth); + return result; +} + +RayCollision R3D_RaycastTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) +{ + float t = 0.0f; + Vector3 edge1 = {0}; + Vector3 edge2 = {0}; + + if (!raycast_triangle(&t, &edge1, &edge2, ray.position, ray.direction, p1, p2, p3)) { + return (RayCollision){0}; + } + + Vector3 point = Vector3Add(ray.position, Vector3Scale(ray.direction, t)); + Vector3 normal = Vector3Normalize(Vector3CrossProduct(edge1, edge2)); + + return (RayCollision) { + .hit = true, + .distance = t, + .point = point, + .normal = normal + }; +} + +RayCollision R3D_RaycastQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4) +{ + RayCollision collision = R3D_RaycastTriangle(ray, p1, p2, p3); + if (collision.hit) return collision; + + return R3D_RaycastTriangle(ray, p1, p3, p4); +} + +RayCollision R3D_RaycastBoundingBox(Ray ray, R3D_BoundingBox box) +{ + float tmin = 0.0f; + float tmax = FLT_MAX; + int axis = -1; + bool flip = false; + + float origin[3] = {ray.position.x, ray.position.y, ray.position.z}; + float dir[3] = {ray.direction.x, ray.direction.y, ray.direction.z}; + float boxMin[3] = {box.min.x, box.min.y, box.min.z}; + float boxMax[3] = {box.max.x, box.max.y, box.max.z}; + + for (int i = 0; i < 3; i++) { + if (fabsf(dir[i]) < 1e-8f) { + if (origin[i] < boxMin[i] || origin[i] > boxMax[i]) { + return (RayCollision){0}; + } + continue; + } + + float invD = 1.0f / dir[i]; + float t1 = (boxMin[i] - origin[i]) * invD; + float t2 = (boxMax[i] - origin[i]) * invD; + + bool near = t1 <= t2; + float tNear = near ? t1 : t2; + float tFar = near ? t2 : t1; + + if (tNear > tmin) tmin = tNear, axis = i, flip = !near; + if (tFar < tmax) tmax = tFar; + if (tmin > tmax) return (RayCollision){0}; + } + + if (tmin < 0) return (RayCollision){0}; + + Vector3 normals[3] = {{1,0,0}, {0,1,0}, {0,0,1}}; + Vector3 normal = normals[axis]; + if (flip) normal = Vector3Negate(normal); + + Vector3 point = Vector3Add(ray.position, Vector3Scale(ray.direction, tmin)); + + return (RayCollision) { + .hit = true, .distance = tmin, + .point = point, .normal = normal + }; +} + +RayCollision R3D_RaycastOrientedBox(Ray ray, R3D_OrientedBox box) +{ + Vector3 d = Vector3Subtract(ray.position, box.center); + + Ray localRay = { + .position = { + Vector3DotProduct(d, box.axisX), + Vector3DotProduct(d, box.axisY), + Vector3DotProduct(d, box.axisZ), + }, + .direction = { + Vector3DotProduct(ray.direction, box.axisX), + Vector3DotProduct(ray.direction, box.axisY), + Vector3DotProduct(ray.direction, box.axisZ), + }, + }; + + R3D_BoundingBox localBox = {Vector3Negate(box.halfExtents), box.halfExtents}; + RayCollision hit = R3D_RaycastBoundingBox(localRay, localBox); + if (!hit.hit) return (RayCollision){0}; + + Vector3 worldNormal = { + box.axisX.x * hit.normal.x + box.axisY.x * hit.normal.y + box.axisZ.x * hit.normal.z, + box.axisX.y * hit.normal.x + box.axisY.y * hit.normal.y + box.axisZ.y * hit.normal.z, + box.axisX.z * hit.normal.x + box.axisY.z * hit.normal.y + box.axisZ.z * hit.normal.z, + }; + + return (RayCollision) { + .hit = true, + .distance = hit.distance, + .point = Vector3Add(ray.position, Vector3Scale(ray.direction, hit.distance)), + .normal = worldNormal, + }; +} + +RayCollision R3D_RaycastSphere(Ray ray, Vector3 center, float radius) +{ + Vector3 oc = Vector3Subtract(ray.position, center); + float b = Vector3DotProduct(oc, ray.direction); + float c = Vector3DotProduct(oc, oc) - radius * radius; + + if (b > 0 && c > 0) return (RayCollision){0}; + + float disc = b*b - c; + if (disc < 0) return (RayCollision){0}; + + float sqrtDisc = sqrtf(disc); + float t = -b - sqrtDisc; + + if (t < 0) t = -b + sqrtDisc; + if (t < 0) return (RayCollision){0}; + + Vector3 point = Vector3Add(ray.position, Vector3Scale(ray.direction, t)); + Vector3 normal = Vector3Scale(Vector3Subtract(point, center), 1.0f / radius); + + return (RayCollision) { + .hit = true, .distance = t, + .point = point, .normal = normal + }; +} + +RayCollision R3D_RaycastCapsule(Ray ray, R3D_Capsule capsule) +{ + Vector3 ab = Vector3Subtract(capsule.end, capsule.start); + Vector3 ao = Vector3Subtract(ray.position, capsule.start); + + float abLen2 = Vector3DotProduct(ab, ab); + float abDir = Vector3DotProduct(ray.direction, ab); + float abOri = Vector3DotProduct(ao, ab); + + float a = abLen2 - abDir * abDir; + float b = abLen2 * Vector3DotProduct(ao, ray.direction) - abOri * abDir; + float c = abLen2 * Vector3DotProduct(ao, ao) - abOri * abOri - capsule.radius * capsule.radius * abLen2; + + RayCollision best = {0}; + best.distance = FLT_MAX; + + if (fabsf(a) > 1e-8f) { + float disc = b*b - a*c; + if (disc >= 0) { + float sqrtDisc = sqrtf(disc); + float t[2] = {(-b - sqrtDisc) / a, (-b + sqrtDisc) / a}; + + for (int i = 0; i < 2; i++) { + if (t[i] < 0) continue; + + float proj = abOri + t[i] * abDir; + if (proj < 0 || proj > abLen2) continue; + + Vector3 point = Vector3Add(ray.position, Vector3Scale(ray.direction, t[i])); + Vector3 axisPoint = Vector3Add(capsule.start, Vector3Scale(ab, proj / abLen2)); + Vector3 normal = Vector3Normalize(Vector3Subtract(point, axisPoint)); + + if (t[i] < best.distance) { + best = (RayCollision) { + .hit = true, .distance = t[i], + .point = point, .normal = normal + }; + } + break; // Nearest valid hit found + } + } + } + + RayCollision capStart = R3D_RaycastSphere(ray, capsule.start, capsule.radius); + RayCollision capEnd = R3D_RaycastSphere(ray, capsule.end, capsule.radius); + + if (capStart.hit && capStart.distance < best.distance) best = capStart; + if (capEnd.hit && capEnd.distance < best.distance) best = capEnd; + + return best.hit ? best : (RayCollision){0}; +} + +RayCollision R3D_RaycastMesh(Ray ray, R3D_MeshData mesh, Matrix transform) +{ + RayCollision collision = {0}; + collision.distance = FLT_MAX; + + if (mesh.vertices == NULL) { + return collision; + } + + Matrix invTransform = MatrixInvert(transform); + Vector3 localOrigin = r3d_vector3_transform(ray.position, &invTransform); + Vector3 localDirection = Vector3Normalize(r3d_vector3_transform_normal(ray.direction, &invTransform)); + + int triangleCount = mesh.indices ? (mesh.indexCount / 3) : (mesh.vertexCount / 3); + + Vector3 closestEdge1 = {0}; + Vector3 closestEdge2 = {0}; + float closestT = FLT_MAX; + + if (mesh.indices) { + raycast_mesh_indexed( + &closestT, &closestEdge1, &closestEdge2, + mesh.vertices, mesh.indices, triangleCount, + localOrigin, localDirection + ); + } + else { + raycast_mesh_vertices( + &closestT, &closestEdge1, &closestEdge2, + mesh.vertices, triangleCount, + localOrigin, localDirection + ); + } + + if (closestT < FLT_MAX) + { + Vector3 closestHitLocal = Vector3Add(localOrigin, Vector3Scale(localDirection, closestT)); + Vector3 normalLocal = Vector3Normalize(Vector3CrossProduct(closestEdge1, closestEdge2)); + Matrix normalMatrix = MatrixTranspose(invTransform); + + collision.hit = true; + collision.point = r3d_vector3_transform(closestHitLocal, &transform); + collision.distance = Vector3Distance(ray.position, collision.point); + collision.normal = Vector3Normalize(r3d_vector3_transform_normal(normalLocal, &normalMatrix)); + } + + return collision; +} + +RayCollision R3D_RaycastModel(Ray ray, R3D_Model model, Matrix transform) +{ + RayCollision collision = {0}; + collision.distance = FLT_MAX; + + if (model.meshData == NULL || model.meshCount <= 0) { + return collision; + } + + Matrix invTransform = MatrixInvert(transform); + Vector3 localOrigin = r3d_vector3_transform(ray.position, &invTransform); + Vector3 localDirection = Vector3Normalize(r3d_vector3_transform_normal(ray.direction, &invTransform)); + + Vector3 closestEdge1 = {0}; + Vector3 closestEdge2 = {0}; + float closestT = FLT_MAX; + + // Test each mesh + for (int meshIdx = 0; meshIdx < model.meshCount; meshIdx++) + { + R3D_MeshData mesh = model.meshData[meshIdx]; + if (mesh.vertices == NULL) continue; + + // Per-mesh AABB culling + RayCollision meshBoxCol = GetRayCollisionBox(ray, model.meshes[meshIdx].aabb); + if (!meshBoxCol.hit) continue; + + int triangleCount = mesh.indices ? (mesh.indexCount / 3) : (mesh.vertexCount / 3); + + if (mesh.indices) { + raycast_mesh_indexed( + &closestT, &closestEdge1, &closestEdge2, + mesh.vertices, mesh.indices, triangleCount, + localOrigin, localDirection + ); + } + else { + raycast_mesh_vertices( + &closestT, &closestEdge1, &closestEdge2, + mesh.vertices, triangleCount, + localOrigin, localDirection + ); + } + } + + if (closestT < FLT_MAX) + { + Vector3 closestHitLocal = Vector3Add(localOrigin, Vector3Scale(localDirection, closestT)); + Vector3 normalLocal = Vector3Normalize(Vector3CrossProduct(closestEdge1, closestEdge2)); + Matrix normalMatrix = MatrixTranspose(invTransform); + + collision.hit = true; + collision.point = r3d_vector3_transform(closestHitLocal, &transform); + collision.distance = Vector3Distance(ray.position, collision.point); + collision.normal = Vector3Normalize(r3d_vector3_transform_normal(normalLocal, &normalMatrix)); + } + + return collision; +} + +Vector3 R3D_ClosestPointOnSegment(Vector3 point, Vector3 start, Vector3 end) +{ + Vector3 dir = Vector3Subtract(end, start); + float lenSq = Vector3LengthSqr(dir); + + if (lenSq < 1e-10f) return start; + + float t = Vector3DotProduct(Vector3Subtract(point, start), dir) / lenSq; + t = fmaxf(0.0f, fminf(t, 1.0f)); + + return Vector3Add(start, Vector3Scale(dir, t)); +} + +Vector3 R3D_ClosestPointOnTriangle(Vector3 p, Vector3 a, Vector3 b, Vector3 c) +{ + Vector3 ab = Vector3Subtract(b, a); + Vector3 ac = Vector3Subtract(c, a); + Vector3 ap = Vector3Subtract(p, a); + + float d1 = Vector3DotProduct(ab, ap); + float d2 = Vector3DotProduct(ac, ap); + if (d1 <= 0.0f && d2 <= 0.0f) return a; + + Vector3 bp = Vector3Subtract(p, b); + float d3 = Vector3DotProduct(ab, bp); + float d4 = Vector3DotProduct(ac, bp); + if (d3 >= 0.0f && d4 <= d3) return b; + + Vector3 cp = Vector3Subtract(p, c); + float d5 = Vector3DotProduct(ab, cp); + float d6 = Vector3DotProduct(ac, cp); + if (d6 >= 0.0f && d5 <= d6) return c; + + float vc = d1 * d4 - d3 * d2; + if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) { + float v = d1 / (d1 - d3); + return Vector3Add(a, Vector3Scale(ab, v)); + } + + float vb = d5 * d2 - d1 * d6; + if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) { + float v = d2 / (d2 - d6); + return Vector3Add(a, Vector3Scale(ac, v)); + } + + float va = d3 * d6 - d5 * d4; + if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) { + float v = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + return Vector3Add(b, Vector3Scale(Vector3Subtract(c, b), v)); + } + + float denom = 1.f / (va + vb + vc); + float v = vb * denom; + float w = vc * denom; + + return Vector3Add(a, Vector3Add(Vector3Scale(ab, v), Vector3Scale(ac, w))); +} + +Vector3 R3D_ClosestPointOnBox(Vector3 point, BoundingBox box) +{ + Vector3 closest; + closest.x = fmaxf(box.min.x, fminf(point.x, box.max.x)); + closest.y = fmaxf(box.min.y, fminf(point.y, box.max.y)); + closest.z = fmaxf(box.min.z, fminf(point.z, box.max.z)); + return closest; +} + +``` + +`src/r3d_skeleton.c`: + +```c +/* r3d_skeleton.h -- R3D Skeleton Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef R3D_SUPPORT_ASSIMP +# include "./importer/r3d_importer_internal.h" +#endif + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Skeleton R3D_LoadSkeleton(const char* filePath) +{ + R3D_Skeleton skeleton = {0}; + +#ifdef R3D_SUPPORT_ASSIMP + R3D_Importer* importer = R3D_LoadImporter(filePath, 0); + if (importer == NULL) return skeleton; + + skeleton = R3D_LoadSkeletonFromImporter(importer); + R3D_UnloadImporter(importer); + +#else + R3D_TRACELOG(LOG_WARNING, "Cannot load '%s': built without Assimp support", filePath); + +#endif // R3D_SUPPORT_ASSIMP + + return skeleton; +} + +R3D_Skeleton R3D_LoadSkeletonFromMemory(const void* data, unsigned int size, const char* hint) +{ + R3D_Skeleton skeleton = {0}; + +#ifdef R3D_SUPPORT_ASSIMP + R3D_Importer* importer = R3D_LoadImporterFromMemory(data, size, hint, 0); + if (importer == NULL) return skeleton; + + skeleton = R3D_LoadSkeletonFromImporter(importer); + R3D_UnloadImporter(importer); + +#else + if (hint && hint[0] != '\0') { + R3D_TRACELOG(LOG_WARNING, "Cannot load '%s' from memory: built without Assimp support", hint); + } + else { + R3D_TRACELOG(LOG_WARNING, "Cannot load asset from memory: built without Assimp support"); + } + +#endif // R3D_SUPPORT_ASSIMP + + return skeleton; +} + +R3D_Skeleton R3D_LoadSkeletonFromImporter(const R3D_Importer* importer) +{ + R3D_Skeleton skeleton = {0}; + +#ifdef R3D_SUPPORT_ASSIMP + if (importer == NULL) { + R3D_TRACELOG(LOG_WARNING, "Cannot load skeleton from importer: NULL importer"); + return skeleton; + } + + if (r3d_importer_load_skeleton(importer, &skeleton)) { + R3D_TRACELOG(LOG_INFO, "Skeleton loaded successfully (%u bones): '%s'", importer->name, skeleton.boneCount); + } + else { + R3D_TRACELOG(LOG_WARNING, "Failed to load skeleton: '%s'", importer->name, skeleton.boneCount); + } + +#else + R3D_TRACELOG(LOG_WARNING, "Cannot load skeleton from importer: built without Assimp support"); + +#endif // R3D_SUPPORT_ASSIMP + + return skeleton; +} + +void R3D_UnloadSkeleton(R3D_Skeleton skeleton) +{ + if (skeleton.skinTexture > 0) { + glDeleteTextures(1, &skeleton.skinTexture); + } + + RL_FREE(skeleton.bones); + RL_FREE(skeleton.invBind); + RL_FREE(skeleton.modelBind); + RL_FREE(skeleton.localBind); +} + +bool R3D_IsSkeletonValid(R3D_Skeleton skeleton) +{ + return (skeleton.skinTexture > 0); +} + +int R3D_GetSkeletonBoneIndex(R3D_Skeleton skeleton, const char* boneName) +{ + for (int i = 0; i < skeleton.boneCount; i++) { + if (strcmp(skeleton.bones[i].name, boneName) == 0) { + return i; + } + } + return -1; +} + +R3D_BoneInfo* R3D_GetSkeletonBone(R3D_Skeleton skeleton, const char* boneName) +{ + for (int i = 0; i < skeleton.boneCount; i++) { + if (strcmp(skeleton.bones[i].name, boneName) == 0) { + return &skeleton.bones[i]; + } + } + return NULL; +} + +``` + +`src/r3d_sky.c`: + +```c +/* r3d_cubemap.c -- R3D Cubemap Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#include "./modules/r3d_driver.h" +#include "./modules/r3d_shader.h" +#include "./modules/r3d_render.h" +#include "./r3d_core_state.h" + +// ======================================== +// EXTERNAL FUNCTIONS +// ======================================== + +R3D_Cubemap r3d_cubemap_allocate(int size); +void r3d_cubemap_gen_mipmap(const R3D_Cubemap* cubemap); + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Cubemap R3D_GenProceduralSky(int size, R3D_ProceduralSky params) +{ + R3D_Cubemap cubemap = r3d_cubemap_allocate(size); + R3D_UpdateProceduralSky(&cubemap, params); + return cubemap; +} + +R3D_Cubemap R3D_GenCustomSky(int size, R3D_SkyShader* shader) +{ + R3D_Cubemap cubemap = r3d_cubemap_allocate(size); + R3D_UpdateCustomSky(&cubemap, shader); + return cubemap; +} + +void R3D_UpdateProceduralSky(R3D_Cubemap* cubemap, R3D_ProceduralSky params) +{ + r3d_driver_invalidate_cache(); + r3d_driver_store_viewport(); + + Matrix matProj = MatrixPerspective(90.0 * DEG2RAD, 1.0, 0.1, 10.0); + + glBindFramebuffer(GL_FRAMEBUFFER, cubemap->fbo); + glViewport(0, 0, cubemap->size, cubemap->size); + + R3D_SHADER_USE(prepare.cubemapProceduralSky); + r3d_driver_disable(GL_CULL_FACE); + r3d_render_prepare_drawing(); + + R3D_SHADER_SET_MAT4(prepare.cubemapProceduralSky, uMatProj, matProj); + R3D_SHADER_SET_COL3(prepare.cubemapProceduralSky, uSkyTopColor, R3D.colorSpace, params.skyTopColor); + R3D_SHADER_SET_COL3(prepare.cubemapProceduralSky, uSkyHorizonColor, R3D.colorSpace, params.skyHorizonColor); + R3D_SHADER_SET_FLOAT(prepare.cubemapProceduralSky, uSkyHorizonCurve, params.skyHorizonCurve); + R3D_SHADER_SET_FLOAT(prepare.cubemapProceduralSky, uSkyEnergy, params.skyEnergy); + R3D_SHADER_SET_COL3(prepare.cubemapProceduralSky, uGroundBottomColor, R3D.colorSpace, params.groundBottomColor); + R3D_SHADER_SET_COL3(prepare.cubemapProceduralSky, uGroundHorizonColor, R3D.colorSpace, params.groundHorizonColor); + R3D_SHADER_SET_FLOAT(prepare.cubemapProceduralSky, uGroundHorizonCurve, params.groundHorizonCurve); + R3D_SHADER_SET_FLOAT(prepare.cubemapProceduralSky, uGroundEnergy, params.groundEnergy); + R3D_SHADER_SET_VEC3(prepare.cubemapProceduralSky, uSunDirection, Vector3Normalize(Vector3Negate(params.sunDirection))); + R3D_SHADER_SET_COL3(prepare.cubemapProceduralSky, uSunColor, R3D.colorSpace, params.sunColor); + R3D_SHADER_SET_FLOAT(prepare.cubemapProceduralSky, uSunSize, params.sunSize); + R3D_SHADER_SET_FLOAT(prepare.cubemapProceduralSky, uSunCurve, params.sunCurve); + R3D_SHADER_SET_FLOAT(prepare.cubemapProceduralSky, uSunEnergy, params.sunEnergy); + + for (int i = 0; i < 6; i++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap->texture, 0); + R3D_SHADER_SET_MAT4(prepare.cubemapProceduralSky, uMatView, R3D.matCubeViews[i]); + R3D_RENDER_CUBE(); + } + + r3d_cubemap_gen_mipmap(cubemap); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindVertexArray(0); + + r3d_driver_enable(GL_CULL_FACE); + r3d_driver_restore_viewport(); +} + +void R3D_UpdateCustomSky(R3D_Cubemap* cubemap, R3D_SkyShader* shader) +{ + if (shader == NULL) { + R3D_TRACELOG(LOG_WARNING, "Failed to generate custom sky; The sky shader is NULL"); + return; + } + + r3d_driver_invalidate_cache(); + r3d_driver_store_viewport(); + + Matrix matProj = MatrixPerspective(90.0 * DEG2RAD, 1.0, 0.1, 10.0); + + glBindFramebuffer(GL_FRAMEBUFFER, cubemap->fbo); + glViewport(0, 0, cubemap->size, cubemap->size); + + R3D_SHADER_USE_CUSTOM(shader, prepare.cubemapCustomSky); + r3d_driver_disable(GL_CULL_FACE); + r3d_render_prepare_drawing(); + + R3D_SHADER_SET_MAT4_CUSTOM(shader, prepare.cubemapCustomSky, uMatProj, matProj); + + for (int i = 0; i < 6; i++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap->texture, 0); + R3D_SHADER_SET_MAT4_CUSTOM(shader, prepare.cubemapCustomSky, uMatView, R3D.matCubeViews[i]); + R3D_RENDER_CUBE(); + } + + r3d_cubemap_gen_mipmap(cubemap); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindVertexArray(0); + + r3d_driver_enable(GL_CULL_FACE); + r3d_driver_restore_viewport(); +} + +``` + +`src/r3d_sky_shader.c`: + +```c +/* r3d_sky_shader.c -- R3D Sky Shader Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#include "./modules/r3d_shader.h" +#include "./common/r3d_rshade.h" + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +static bool compile_shader(R3D_SkyShader* shader); + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_SkyShader* R3D_LoadSkyShader(const char* filePath) +{ + char* code = LoadFileText(filePath); + if (code == NULL) { + R3D_TRACELOG(LOG_ERROR, "Failed to load sky shader; Unable to load shader file"); + return NULL; + } + + R3D_SkyShader* shader = R3D_LoadSkyShaderFromMemory(code); + UnloadFileText(code); + + return shader; +} + +R3D_SkyShader* R3D_LoadSkyShaderFromMemory(const char* code) +{ + size_t userCodeLen = strlen(code); + if (userCodeLen > R3D_MAX_SHADER_CODE_LENGTH) { + R3D_TRACELOG(LOG_ERROR, "Failed to load sky shader; User code too long"); + return NULL; + } + + if (!strstr(code, "void fragment()")) { + R3D_TRACELOG(LOG_WARNING, "Failed to load sky shader; Missing fragment() entry point"); + return NULL; + } + + R3D_SkyShader* shader = r3d_shader_custom_alloc(); + if (!shader) { + R3D_TRACELOG(LOG_ERROR, "Bad alloc during sky shader loading"); + return NULL; + } + + int uniformCount = 0; + int samplerCount = 0; + int currentOffset = 0; + + r3d_rshade_parsed_function_t fragmentFunc = {0}; + + /* --- PHASE 1: Parse user code and collect metadata --- */ + + const char* ptr = code; + + while (*ptr) + { + r3d_rshade_skip_whitespace_and_comments(&ptr); + if (!*ptr) break; + + // Parse uniform declarations + if (r3d_rshade_match_keyword(ptr, "uniform", 7)) { + ptr += 7; + r3d_rshade_parse_uniform(&ptr, + shader->data.samplers, + &shader->data.uniforms, + &samplerCount, + &uniformCount, + ¤tOffset, + R3D_MAX_SHADER_SAMPLERS, + R3D_MAX_SHADER_UNIFORMS + ); + continue; + } + + // Parse fragment() function + r3d_rshade_parsed_function_t* func = r3d_rshade_check_shader_entry(ptr, NULL, &fragmentFunc); + if (func) { + r3d_rshade_skip_to_brace(&ptr); + if (*ptr == '{') { + func->bodyStart = ptr; + r3d_rshade_skip_to_matching_brace(&ptr); + func->bodyEnd = ptr; + } + continue; + } + + ptr++; + } + + /* --- PHASE 2: Generate transformed shader code --- */ + + r3d_rshade_writer_t writer = { + .ptr = shader->program->userCode, + .remaining = R3D_MAX_SHADER_CODE_LENGTH, + .overflow = false, + }; + + // Write uniform block and samplers + r3d_rshade_write_uniform_block(&writer, shader->data.uniforms.entries, uniformCount); + r3d_rshade_write_samplers(&writer, shader->data.samplers, samplerCount); + + // Copy global code (excluding comments, uniforms, fragment()) then write fragment stage section + r3d_rshade_copy_global_code(&writer, code, false, NULL, &fragmentFunc); + r3d_rshade_write_shader_function(&writer, "fragment", &fragmentFunc); + + if (writer.overflow) { + R3D_TRACELOG(LOG_ERROR, "Failed to load sky shader; Transformed code too long"); + RL_FREE(shader); + return NULL; + } + + *writer.ptr = '\0'; + + /* --- PHASE 3: Compile shader --- */ + + if (!compile_shader(shader)) { + R3D_UnloadSkyShader(shader); + return NULL; + } + + /* --- PHASE 4: Initialize uniform buffer --- */ + + r3d_shader_custom_init_uniforms(shader, currentOffset); + + R3D_TRACELOG(LOG_INFO, "Sky shader loaded successfully"); + R3D_TRACELOG(LOG_INFO, " > Sampler count: %i", samplerCount); + R3D_TRACELOG(LOG_INFO, " > Uniform count: %i", uniformCount); + + return shader; +} + +R3D_SkyShader* R3D_LoadSkyShaderAlias(R3D_SkyShader* shader) +{ + R3D_SkyShader* alias = r3d_shader_custom_clone(shader); + if (!alias) { + R3D_TRACELOG(LOG_ERROR, "Bad alloc during sky shader alias loading"); + return NULL; + } + return alias; +} + +void R3D_UnloadSkyShader(R3D_SkyShader* shader) +{ + r3d_shader_custom_free(shader); +} + +void R3D_SetSkyShaderUniform(R3D_SkyShader* shader, const char* name, const void* value) +{ + if (!shader) { + R3D_TRACELOG(LOG_WARNING, "Cannot set uniform '%s' on NULL sky shader", name); + return; + } + + if (!r3d_shader_custom_set_uniform(shader, name, value)) { + R3D_TRACELOG(LOG_WARNING, "Failed to set custom uniform '%s'", name); + } +} + +void R3D_SetSkyShaderSampler(R3D_SkyShader* shader, const char* name, Texture texture) +{ + if (!shader) { + R3D_TRACELOG(LOG_WARNING, "Cannot set sampler '%s' on NULL sky shader", name); + return; + } + + if (!r3d_shader_custom_set_sampler(shader, name, texture)) { + R3D_TRACELOG(LOG_WARNING, "Failed to set custom sampler '%s'", name); + } +} + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +bool compile_shader(R3D_SkyShader* shader) +{ + bool ok = R3D_MOD_SHADER_LOADER.prepare.cubemapCustomSky(shader); + if (!ok) { + R3D_TRACELOG(LOG_ERROR, "Failed to compile sky shader"); + } + return ok; +} + +``` + +`src/r3d_surface_shader.c`: + +```c +/* r3d_surface_shader.c -- R3D Surface Shader Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "./modules/r3d_shader.h" +#include "./common/r3d_rshade.h" +#include "./common/r3d_helper.h" + +// ======================================== +// INTERNAL ENUMS +// ======================================== + +typedef uint32_t usage_hint_t; + +#define USAGE_HINT_OPAQUE (1 << 0) //< Build: geometry +#define USAGE_HINT_PREPASS (1 << 1) //< Build: geometry, forward, depth +#define USAGE_HINT_TRANSPARENT (1 << 2) //< Build: forward +#define USAGE_HINT_UNLIT (1 << 3) //< Build: unlit +#define USAGE_HINT_SHADOW (1 << 4) //< Build: depth, depthCube +#define USAGE_HINT_DECAL (1 << 5) //< Build: decal +#define USAGE_HINT_PROBE (1 << 6) //< Build: probe + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +/* Parse usage pragma to decide wich shader to pre-compile */ +static usage_hint_t parse_pragma_usage(const char** ptr); + +/* Returns a static string containing the list of usage hints */ +static const char* get_usage_hint_string(usage_hint_t hints); + +/* Compile all needed variants accordingly to the usage hints specified in pragma */ +static bool compile_shader_variants(R3D_SurfaceShader* shader, usage_hint_t usage); + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_SurfaceShader* R3D_LoadSurfaceShader(const char* filePath) +{ + char* code = LoadFileText(filePath); + if (code == NULL) { + R3D_TRACELOG(LOG_ERROR, "Failed to load surface shader; Unable to load shader file"); + return NULL; + } + + R3D_SurfaceShader* shader = R3D_LoadSurfaceShaderFromMemory(code); + UnloadFileText(code); + + return shader; +} + +R3D_SurfaceShader* R3D_LoadSurfaceShaderFromMemory(const char* code) +{ + size_t userCodeLen = strlen(code); + if (userCodeLen > R3D_MAX_SHADER_CODE_LENGTH) { + R3D_TRACELOG(LOG_ERROR, "Failed to load surface shader; User code too long"); + return NULL; + } + + if (!strstr(code, "void vertex()") && !strstr(code, "void fragment()")) { + R3D_TRACELOG(LOG_WARNING, "Failed to load surface shader; Missing entry points"); + return NULL; + } + + R3D_SurfaceShader* shader = r3d_shader_custom_alloc(); + if (!shader) { + R3D_TRACELOG(LOG_ERROR, "Bad alloc during surface shader loading"); + return NULL; + } + + int uniformCount = 0; + int samplerCount = 0; + int varyingCount = 0; + int currentOffset = 0; + + usage_hint_t usage = 0; + r3d_rshade_varying_t varyings[32] = {0}; + r3d_rshade_parsed_function_t vertexFunc = {0}; + r3d_rshade_parsed_function_t fragmentFunc = {0}; + + /* --- PHASE 1: Parse user code and collect metadata --- */ + + const char* ptr = code; + + while (*ptr) + { + r3d_rshade_skip_whitespace_and_comments(&ptr); + if (!*ptr) break; + + // Parse #pragma usage directive + if (strncmp(ptr, "#pragma", 7) == 0 && isspace(ptr[7])) { + usage |= parse_pragma_usage(&ptr); + continue; + } + + // Parse uniform declarations + if (r3d_rshade_match_keyword(ptr, "uniform", 7)) { + ptr += 7; + r3d_rshade_parse_uniform(&ptr, + shader->data.samplers, + &shader->data.uniforms, + &samplerCount, + &uniformCount, + ¤tOffset, + R3D_MAX_SHADER_SAMPLERS, + R3D_MAX_SHADER_UNIFORMS + ); + continue; + } + + // Parse varying declarations + if (r3d_rshade_match_varying_keyword(ptr)) { + if (varyingCount < 32) { + if (r3d_rshade_parse_varying(&ptr, &varyings[varyingCount])) { + varyingCount++; + } + } + else { + r3d_rshade_skip_to_semicolon(&ptr); + } + continue; + } + + // Parse vertex() and fragment() functions + r3d_rshade_parsed_function_t* func = r3d_rshade_check_shader_entry(ptr, &vertexFunc, &fragmentFunc); + if (func) { + r3d_rshade_skip_to_brace(&ptr); + if (*ptr == '{') { + func->bodyStart = ptr; + r3d_rshade_skip_to_matching_brace(&ptr); + func->bodyEnd = ptr; + } + continue; + } + + ptr++; + } + + /* --- PHASE 2: Generate transformed shader code --- */ + + r3d_rshade_writer_t writer = { + .ptr = shader->program->userCode, + .remaining = R3D_MAX_SHADER_CODE_LENGTH, + .overflow = false, + }; + + // Write uniform block and samplers + r3d_rshade_write_uniform_block(&writer, shader->data.uniforms.entries, uniformCount); + r3d_rshade_write_samplers(&writer, shader->data.samplers, samplerCount); + + // Copy global code (excluding pragma, comments, uniforms, varyings, entry points) + r3d_rshade_copy_global_code(&writer, code, true, &vertexFunc, &fragmentFunc); + + // Write vertex stage section + r3d_rshade_writer_printf(&writer, "\n#ifdef STAGE_VERT\n\n"); + r3d_rshade_write_varyings(&writer, "out", varyings, varyingCount); + r3d_rshade_write_shader_function(&writer, "vertex", &vertexFunc); + r3d_rshade_writer_printf(&writer, "\n#endif\n\n"); + + // Write fragment stage section + r3d_rshade_writer_printf(&writer, "#ifdef STAGE_FRAG\n\n"); + r3d_rshade_write_varyings(&writer, "in", varyings, varyingCount); + r3d_rshade_write_shader_function(&writer, "fragment", &fragmentFunc); + r3d_rshade_writer_printf(&writer, "\n#endif\n"); + + if (writer.overflow) { + R3D_TRACELOG(LOG_ERROR, "Failed to load surface shader; Transformed code too long"); + RL_FREE(shader); + return NULL; + } + + *writer.ptr = '\0'; + + /* --- PHASE 3: Pre-compile needed shader variants --- */ + + if (!compile_shader_variants(shader, usage)) { + R3D_UnloadSurfaceShader(shader); + return NULL; + } + + /* --- PHASE 4: Initialize uniform buffer --- */ + + r3d_shader_custom_init_uniforms(shader, currentOffset); + + R3D_TRACELOG(LOG_INFO, "Surface shader loaded successfully"); + R3D_TRACELOG(LOG_INFO, " > Usage hints: %s", get_usage_hint_string(usage)); + R3D_TRACELOG(LOG_INFO, " > Varying count: %i", varyingCount); + R3D_TRACELOG(LOG_INFO, " > Sampler count: %i", samplerCount); + R3D_TRACELOG(LOG_INFO, " > Uniform count: %i", uniformCount); + + return shader; +} + +R3D_SurfaceShader* R3D_LoadSurfaceShaderAlias(R3D_SurfaceShader* shader) +{ + R3D_SurfaceShader* alias = r3d_shader_custom_clone(shader); + if (!alias) { + R3D_TRACELOG(LOG_ERROR, "Bad alloc during surface shader alias loading"); + return NULL; + } + return alias; +} + +void R3D_UnloadSurfaceShader(R3D_SurfaceShader* shader) +{ + r3d_shader_custom_free(shader); +} + +void R3D_SetSurfaceShaderUniform(R3D_SurfaceShader* shader, const char* name, const void* value) +{ + if (!shader) { + R3D_TRACELOG(LOG_WARNING, "Cannot set uniform '%s' on NULL surface shader", name); + return; + } + + if (!r3d_shader_custom_set_uniform(shader, name, value)) { + R3D_TRACELOG(LOG_WARNING, "Failed to set custom uniform '%s'", name); + } +} + +void R3D_SetSurfaceShaderSampler(R3D_SurfaceShader* shader, const char* name, Texture texture) +{ + if (!shader) { + R3D_TRACELOG(LOG_WARNING, "Cannot set sampler '%s' on NULL surface shader", name); + return; + } + + if (!r3d_shader_custom_set_sampler(shader, name, texture)) { + R3D_TRACELOG(LOG_WARNING, "Failed to set custom sampler '%s'", name); + } +} + +// ======================================== +// INTERNAL FUNCTIONS +// ======================================== + +usage_hint_t parse_pragma_usage(const char** ptr) +{ + usage_hint_t result = 0; + + // Skip "#pragma" + *ptr += 7; // strlen("#pragma") + r3d_rshade_skip_whitespace(ptr); + + // Check for "usage" keyword + if (strncmp(*ptr, "usage", 5) != 0 || !isspace((*ptr)[5])) { + r3d_rshade_skip_to_end_of_line(ptr); + return 0; + } + + *ptr += 5; // strlen("usage") + r3d_rshade_skip_whitespace(ptr); + + // Parse usage hints until end of line (CR, LF, or CRLF) + while (**ptr && **ptr != '\n' && **ptr != '\r') + { + r3d_rshade_skip_whitespace(ptr); + if (!**ptr || **ptr == '\n' || **ptr == '\r') break; + + // Match usage hint keywords + static const struct { + const char* name; + size_t len; + usage_hint_t flag; + } hints[] = { + {"opaque", 6, USAGE_HINT_OPAQUE}, + {"prepass", 7, USAGE_HINT_PREPASS}, + {"transparent", 11, USAGE_HINT_TRANSPARENT}, + {"unlit", 5, USAGE_HINT_UNLIT}, + {"shadow", 6, USAGE_HINT_SHADOW}, + {"decal", 5, USAGE_HINT_DECAL}, + {"probe", 5, USAGE_HINT_PROBE} + }; + + bool matched = false; + for (int i = 0; i < 7; i++) { + if (strncmp(*ptr, hints[i].name, hints[i].len) == 0 && + (isspace((*ptr)[hints[i].len]) || + (*ptr)[hints[i].len] == '\n' || + (*ptr)[hints[i].len] == '\r' || + (*ptr)[hints[i].len] == '\0')) + { + result |= hints[i].flag; + *ptr += hints[i].len; + matched = true; + break; + } + } + + if (!matched) { + // Unknown hint, skip to next whitespace or end of line + while (**ptr && !isspace(**ptr)) (*ptr)++; + } + } + + r3d_rshade_skip_to_end_of_line(ptr); + return result; +} + +const char* get_usage_hint_string(usage_hint_t hints) +{ + static char buffer[128]; + char *p = buffer; + + if (hints == 0) { + return "None"; + } + + *p = '\0'; + + #define APPEND(str) do { \ + if (p != buffer) { *p++ = ','; *p++ = ' '; } \ + const char *s = str; \ + while (*s) *p++ = *s++; \ + } while(0) + + if (hints & USAGE_HINT_OPAQUE) APPEND("Opaque"); + if (hints & USAGE_HINT_PREPASS) APPEND("Prepass"); + if (hints & USAGE_HINT_TRANSPARENT) APPEND("Transparent"); + if (hints & USAGE_HINT_UNLIT) APPEND("Unlit"); + if (hints & USAGE_HINT_SHADOW) APPEND("Shadow"); + if (hints & USAGE_HINT_DECAL) APPEND("Decal"); + if (hints & USAGE_HINT_PROBE) APPEND("Probe"); + + #undef APPEND + *p = '\0'; + return buffer; +} + +bool compile_shader_variants(R3D_SurfaceShader* shader, usage_hint_t usage) +{ + if (usage == 0) { + bool ok = R3D_MOD_SHADER_LOADER.scene.geometry(shader); + if (!ok) { + R3D_TRACELOG(LOG_ERROR, "Failed to compile surface shader"); + } + return ok; + } + + const struct { + const char* name; + usage_hint_t condition; + r3d_shader_loader_func func; + } variants[] = { + {"geometry", USAGE_HINT_OPAQUE | USAGE_HINT_PREPASS, R3D_MOD_SHADER_LOADER.scene.geometry}, + {"forward", USAGE_HINT_PREPASS | USAGE_HINT_TRANSPARENT, R3D_MOD_SHADER_LOADER.scene.forward}, + {"unlit", USAGE_HINT_UNLIT, R3D_MOD_SHADER_LOADER.scene.unlit}, + {"depth", USAGE_HINT_SHADOW | USAGE_HINT_PREPASS, R3D_MOD_SHADER_LOADER.scene.depth}, + {"depth-cube", USAGE_HINT_SHADOW, R3D_MOD_SHADER_LOADER.scene.depthCube}, + {"decal", USAGE_HINT_DECAL, R3D_MOD_SHADER_LOADER.scene.decal}, + {"probe-forward", USAGE_HINT_PROBE, R3D_MOD_SHADER_LOADER.scene.probeForward}, + {"probe-unlit", USAGE_HINT_PROBE | USAGE_HINT_UNLIT, R3D_MOD_SHADER_LOADER.scene.probeUnlit}, + }; + + for (int i = 0; i < 6; i++) { + if (BIT_TEST_ANY(usage, variants[i].condition)) { + if (!variants[i].func(shader)) { + R3D_TRACELOG(LOG_ERROR, "Failed to compile surface shader (variant: '%s')", variants[i].name); + return false; + } + } + } + + return true; +} + +``` + +`src/r3d_texture.c`: + +```c +/* r3d_texture.h -- R3D Texture Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include + +#include "./modules/r3d_texture.h" +#include "./common/r3d_image.h" +#include "./r3d_core_state.h" + +// ======================================== +// PUBLIC API +// ======================================== + +Texture2D R3D_LoadTexture(const char* fileName, bool isColor) +{ + return R3D_LoadTextureEx(fileName, R3D.textureWrap, R3D.textureFilter, isColor); +} + +Texture2D R3D_LoadTextureEx(const char* fileName, TextureWrap wrap, TextureFilter filter, bool isColor) +{ + Texture2D texture = {0}; + + Image image = LoadImage(fileName); + if (!IsImageValid(image)) { + R3D_TRACELOG(LOG_WARNING, "Failed to load texture (path: '%s')", fileName); + return texture; + } + + bool srgb = isColor ? (R3D.colorSpace == R3D_COLORSPACE_SRGB) : false; + texture = r3d_image_upload(&image, wrap, filter, srgb); + + if (texture.id != 0) { + R3D_TRACELOG(LOG_INFO, "Texture loaded successfully (path: '%s')", fileName); + } + else { + R3D_TRACELOG(LOG_WARNING, "Failed to load texture (path: '%s')", fileName); + } + + UnloadImage(image); + + return texture; +} + +Texture2D R3D_LoadTextureFromImage(Image image, bool isColor) +{ + return R3D_LoadTextureFromImageEx(image, R3D.textureWrap, R3D.textureFilter, isColor); +} + +Texture2D R3D_LoadTextureFromImageEx(Image image, TextureWrap wrap, TextureFilter filter, bool isColor) +{ + bool srgb = isColor ? (R3D.colorSpace == R3D_COLORSPACE_SRGB) : false; + Texture2D texture = r3d_image_upload(&image, wrap, filter, srgb); + + if (texture.id != 0) { + R3D_TRACELOG(LOG_INFO, "Texture loaded successfully from image"); + } + else { + R3D_TRACELOG(LOG_WARNING, "Failed to load texture from image"); + } + + UnloadImage(image); + + return texture; +} + +Texture2D R3D_LoadTextureFromMemory(const char* fileType, const void* fileData, int dataSize, bool isColor) +{ + return R3D_LoadTextureFromMemoryEx(fileType, fileData, dataSize, R3D.textureWrap, R3D.textureFilter, isColor); +} + +Texture2D R3D_LoadTextureFromMemoryEx(const char* fileType, const void* fileData, int dataSize, TextureWrap wrap, TextureFilter filter, bool isColor) +{ + Texture2D texture = {0}; + + Image image = LoadImageFromMemory(fileType, fileData, dataSize); + if (!IsImageValid(image)) { + R3D_TRACELOG(LOG_WARNING, "Failed to load texture from memory (type: '%s')", fileType); + return texture; + } + + bool srgb = isColor ? (R3D.colorSpace == R3D_COLORSPACE_SRGB) : false; + texture = r3d_image_upload(&image, wrap, filter, srgb); + + if (texture.id != 0) { + R3D_TRACELOG(LOG_INFO, "Texture loaded successfully from memory (type: '%s')", fileType); + } + else { + R3D_TRACELOG(LOG_WARNING, "Failed to load texture from memory (type: '%s')", fileType); + } + + UnloadImage(image); + + return texture; +} + +void R3D_UnloadTexture(Texture2D texture) +{ + if (texture.id == 0 || r3d_texture_is_default(texture.id)) { + return; + } + + UnloadTexture(texture); +} + +``` + +`src/r3d_utils.c`: + +```c +/* r3d_utils.h -- R3D Utility Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include + +#include "./modules/r3d_texture.h" +#include "./modules/r3d_target.h" +#include "./r3d_core_state.h" + +// ======================================== +// PUBLIC API +// ======================================== + +Texture2D R3D_GetWhiteTexture(void) +{ + Texture2D texture = { 0 }; + texture.id = r3d_texture_get(R3D_TEXTURE_WHITE); + texture.width = 1; + texture.height = 1; + texture.mipmaps = 1; + texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + return texture; +} + +Texture2D R3D_GetBlackTexture(void) +{ + Texture2D texture = { 0 }; + texture.id = r3d_texture_get(R3D_TEXTURE_BLACK); + texture.width = 1; + texture.height = 1; + texture.mipmaps = 1; + texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + return texture; +} + +Texture2D R3D_GetNormalTexture(void) +{ + Texture2D texture = { 0 }; + texture.id = r3d_texture_get(R3D_TEXTURE_NORMAL); + texture.width = 1; + texture.height = 1; + texture.mipmaps = 1; + texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; + return texture; +} + +Texture2D R3D_GetBufferNormal(void) +{ + Texture2D texture = { 0 }; + texture.id = r3d_target_get(R3D_TARGET_NORMAL); + texture.width = R3D_TARGET_SIZE_W; + texture.height = R3D_TARGET_SIZE_H; + texture.mipmaps = 1; + texture.format = PIXELFORMAT_UNCOMPRESSED_R32; + return texture; +} + +Texture2D R3D_GetBufferDepth(void) +{ + Texture2D texture = { 0 }; + texture.id = r3d_target_get(R3D_TARGET_DEPTH); + texture.width = R3D_TARGET_SIZE_W; + texture.height = R3D_TARGET_SIZE_H; + texture.mipmaps = 1; + texture.format = PIXELFORMAT_UNCOMPRESSED_R16; + return texture; +} + +Matrix R3D_GetMatrixView(void) +{ + return R3D.viewState.view; +} + +Matrix R3D_GetMatrixInvView(void) +{ + return R3D.viewState.invView; +} + +Matrix R3D_GetMatrixProjection(void) +{ + return R3D.viewState.proj; +} + +Matrix R3D_GetMatrixInvProjection(void) +{ + return R3D.viewState.invProj; +} + +Matrix R3D_GetMatrixViewProjection(void) +{ + return R3D.viewState.viewProj; +} + +``` + +`src/r3d_vertex.c`: + +```c +/* r3d_vertex.c -- R3D Vertex Module. + * + * Copyright (c) 2025-2026 Le Juez Victor + * + * This software is provided 'as-is', without any express or implied warranty. + * For conditions of distribution and use, see accompanying LICENSE file. + */ + +#include +#include + +// ======================================== +// PUBLIC API +// ======================================== + +R3D_Vertex R3D_MakeVertex(Vector3 position, Vector2 texcoord, Vector3 normal, Vector4 tangent, Color color) +{ + R3D_Vertex v = {0}; + + v.position = position; + R3D_PackTexCoord(v.texcoord, texcoord); + R3D_PackNormal((int8_t*)v.normal, normal); + R3D_PackTangent((int8_t*)v.tangent, tangent); + v.color = color; + + return v; +} + +void R3D_PackTexCoord(uint16_t* dst, Vector2 src) +{ + dst[0] = R3D_PackFloat16(src.x); + dst[1] = R3D_PackFloat16(src.y); +} + +Vector2 R3D_UnpackTexCoord(const uint16_t* src) +{ + return (Vector2) { + R3D_UnpackFloat16(src[0]), + R3D_UnpackFloat16(src[1]) + }; +} + +void R3D_PackNormal(int8_t* dst, Vector3 src) +{ + dst[0] = R3D_PackSnorm8(src.x); + dst[1] = R3D_PackSnorm8(src.y); + dst[2] = R3D_PackSnorm8(src.z); + dst[3] = 0; +} + +Vector3 R3D_UnpackNormal(const int8_t* src) +{ + return (Vector3) { + R3D_UnpackSnorm8(src[0]), + R3D_UnpackSnorm8(src[1]), + R3D_UnpackSnorm8(src[2]) + }; +} + +void R3D_PackTangent(int8_t* dst, Vector4 src) +{ + dst[0] = R3D_PackSnorm8(src.x); + dst[1] = R3D_PackSnorm8(src.y); + dst[2] = R3D_PackSnorm8(src.z); + dst[3] = (src.w >= 0.0f) ? 127 : -127; +} + +Vector4 R3D_UnpackTangent(const int8_t* src) +{ + return (Vector4) { + R3D_UnpackSnorm8(src[0]), + R3D_UnpackSnorm8(src[1]), + R3D_UnpackSnorm8(src[2]), + (src[3] >= 0) ? 1.0f : -1.0f + }; +} + +``` \ No newline at end of file