Compare commits

...

18 Commits

Author SHA1 Message Date
92912a321c Initial Wayland support
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-27 00:55:28 +02:00
e04f1cf291 Add useful wayland RAII wrappers
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-26 18:27:27 +02:00
efa6e289b6 Add logging for KMS backend
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-17 15:05:25 +02:00
596af80622 Hands and formatting
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-17 14:55:15 +02:00
f4fad2c1ac Hand tracking
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-17 14:40:34 +02:00
e9ae017e9b Make OpenXR work!
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-17 12:42:23 +02:00
cddfa30cfe Add openxr related stuff
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-17 12:36:41 +02:00
5ca02ed9e2 Add untested OpenXR
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-12 19:36:25 +02:00
9f2dab344d DRM/KMS backebd
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-12 16:38:28 +02:00
402cdd43da Make the constructor and destructor of Application private
Application is a singleton now, no need to have it be constructed
outside of the().

Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 17:45:10 +02:00
fc66ce2fd3 Make Application into a singleton
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 17:44:02 +02:00
46f5fab55e Fix initializers, more stuff
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 16:55:10 +02:00
e55601b5a6 Add skybox
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 15:01:48 +02:00
979dab81b1 Add cubemap
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 11:56:07 +02:00
26edfcbe89 Fix some bugs
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 11:43:06 +02:00
46c428b13a CPU texture
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 11:28:48 +02:00
447114e38d Screenshots
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 11:00:42 +02:00
7978606a52 Update smath
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 10:58:04 +02:00
471 changed files with 122187 additions and 478 deletions

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ result
.cache .cache
.direnv .direnv
.clangd .clangd
screenshot*

BIN
assets/cubemap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 MiB

View File

@@ -23,6 +23,7 @@
pkg-config pkg-config
glslang glslang
shaderc shaderc
wayland-scanner
]; ];
buildInputs = with pkgs; [ buildInputs = with pkgs; [
vulkan-loader vulkan-loader

View File

@@ -1,4 +1,4 @@
project('vr-compositor', 'cpp', project('vr-compositor', 'c', 'cpp',
version: '0.1', version: '0.1',
default_options: [ default_options: [
'cpp_std=c++26', 'cpp_std=c++26',
@@ -17,14 +17,32 @@ fastgltf_opts.set_override_option('werror', 'false')
fastgltf = cmake.subproject('fastgltf', options: fastgltf_opts) fastgltf = cmake.subproject('fastgltf', options: fastgltf_opts)
cc = meson.get_compiler('cpp') cc = meson.get_compiler('cpp')
wl_mod = import('wayland')
wayland_dep = dependency('wayland-server', include_type: 'system') wayland_dep = dependency('wayland-server', include_type: 'system')
wayland_client_dep = dependency('wayland-client', include_type: 'system')
vulkan_dep = dependency('vulkan', include_type: 'system') vulkan_dep = dependency('vulkan', include_type: 'system')
openxr_dep = dependency('openxr', include_type: 'system') openxr_dep = dependency('openxr', include_type: 'system')
zlib_dep = dependency('zlib', include_type: 'system') zlib_dep = dependency('zlib', include_type: 'system')
sdl3_dep = dependency('sdl3', include_type: 'system') sdl3_dep = dependency('sdl3', include_type: 'system')
libinput_dep = dependency('libinput', include_type: 'system') libinput_dep = dependency('libinput', include_type: 'system')
libudev_dep = dependency('libudev', include_type: 'system') libudev_dep = dependency('libudev', include_type: 'system')
wayland_protocol = wl_mod.scan_xml(
'protocols/wayland.xml',
client: false,
server: true,
)
wayland_protocol_source = wayland_protocol[0]
wayland_server_header = wayland_protocol[1]
xdg_shell_protocol = wl_mod.scan_xml(
'protocols/xdg-shell.xml',
client: true,
server: true,
)
xdg_shell_protocol_source = xdg_shell_protocol[0]
xdg_shell_client_header = xdg_shell_protocol[1]
xdg_shell_server_header = xdg_shell_protocol[2]
imgui_src = files( imgui_src = files(
'thirdparty/imgui/imgui.cpp', 'thirdparty/imgui/imgui.cpp',
'thirdparty/imgui/imgui_draw.cpp', 'thirdparty/imgui/imgui_draw.cpp',
@@ -95,6 +113,7 @@ add_project_arguments(
'-Wno-zero-as-null-pointer-constant', '-Wno-zero-as-null-pointer-constant',
'-Wno-unused-macros', '-Wno-unused-macros',
'-Wno-reserved-macro-identifier', '-Wno-reserved-macro-identifier',
'-Wno-reserved-identifier',
'-Wno-suggest-override', '-Wno-suggest-override',
'-Wno-macro-redefined', '-Wno-macro-redefined',
'-DVULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE', '-DVULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE',
@@ -159,11 +178,25 @@ exe = executable('vr-compositor',
'src/Pipeline.cpp', 'src/Pipeline.cpp',
'src/Loader.cpp', 'src/Loader.cpp',
'src/DescriptorWriter.cpp', 'src/DescriptorWriter.cpp',
'src/CPUTexture.cpp',
'src/wayland/WaylandServer.cpp',
'src/wayland/Surface.cpp',
'src/wayland/Shm.cpp',
'src/wayland/protocols/CompositorProtocol.cpp',
'src/wayland/protocols/ShmProtocol.cpp',
'src/wayland/protocols/XdgShellProtocol.cpp',
'src/Skybox.cpp',
'src/VulkanRenderer.cpp', 'src/VulkanRenderer.cpp',
'src/Application.cpp', 'src/Application.cpp',
wayland_protocol_source,
xdg_shell_protocol_source,
wayland_server_header,
xdg_shell_server_header,
], ],
c_args: ['-Wno-missing-variable-declarations'],
include_directories: [ include_directories: [
vkbootstrap_inc, vkbootstrap_inc,
include_directories('.'),
imgui_inc, imgui_inc,
'thirdparty/smath/include' 'thirdparty/smath/include'
], ],
@@ -173,3 +206,11 @@ exe = executable('vr-compositor',
'--embed-dir=' + join_paths(meson.project_build_root(), 'shaders'), '--embed-dir=' + join_paths(meson.project_build_root(), 'shaders'),
], ],
) )
executable('shm-life',
'tools/shm_life.cpp',
dependencies: [wayland_client_dep],
sources: [xdg_shell_protocol_source, xdg_shell_client_header],
c_args: ['-Wno-missing-variable-declarations'],
cpp_args: ['-Wno-cast-qual'],
)

3314
protocols/wayland.xml Normal file

File diff suppressed because it is too large Load Diff

1418
protocols/xdg-shell.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,8 @@ shader_sources = files(
'triangle_mesh.frag', 'triangle_mesh.frag',
'triangle_mesh.vert', 'triangle_mesh.vert',
'tex_image.frag', 'tex_image.frag',
'skybox.frag',
'skybox.vert',
) )
spirv_shaders = [] spirv_shaders = []

12
shaders/skybox.frag Normal file
View File

@@ -0,0 +1,12 @@
#version 450
layout (location = 0) in vec3 in_dir;
layout (location = 0) out vec4 out_frag_color;
layout (set = 0, binding = 0) uniform samplerCube environment_map;
void main() {
vec3 dir = normalize(in_dir);
out_frag_color = vec4(texture(environment_map, dir).rgb, 1.0f);
}

14
shaders/skybox.vert Normal file
View File

@@ -0,0 +1,14 @@
#version 450
layout (location = 0) in vec3 in_position;
layout (location = 0) out vec3 out_dir;
layout(push_constant) uniform constants {
mat4 mvp;
} PushConstants;
void main() {
out_dir = in_position;
vec4 pos = PushConstants.mvp * vec4(in_position, 1.0f);
gl_Position = vec4(pos.xy, pos.w, pos.w);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,24 @@
#pragma once #pragma once
#include <array> #include <array>
#include <cstdint>
#include <filesystem>
#include <functional>
#include <memory> #include <memory>
#include <vector>
#include <SDL3/SDL_video.h> #include <SDL3/SDL_video.h>
#include <imgui.h> #include <imgui.h>
#include <linux/input-event-codes.h> #include <linux/input-event-codes.h>
#include <openxr/openxr.h>
#include "smath.hpp"
#include "Loader.h"
#include "Logger.h" #include "Logger.h"
#include "Skybox.h"
#include "Types.h" #include "Types.h"
#include "wayland/Display.h"
struct libinput; struct libinput;
struct libinput_event_keyboard; struct libinput_event_keyboard;
@@ -18,36 +28,80 @@ struct udev;
namespace Lunar { namespace Lunar {
struct VulkanRenderer; struct VulkanRenderer;
struct OpenXrState;
namespace Wayland {
struct WaylandServer;
}
struct Application { struct Application {
Application();
~Application();
auto run() -> void; auto run() -> void;
auto binary_directory() const -> std::filesystem::path;
auto mouse_captured(bool new_state) -> void; auto mouse_captured(bool new_state) -> void;
auto mouse_captured() const -> bool { return m_mouse_captured; } auto mouse_captured() const -> bool { return m_mouse_captured; }
auto toggle_mouse_captured() -> void { mouse_captured(!m_mouse_captured); } auto toggle_mouse_captured() -> void { mouse_captured(!m_mouse_captured); }
auto is_key_down(uint32_t key) const -> bool;
auto is_key_up(uint32_t key) const -> bool;
auto is_key_pressed(uint32_t key) const -> bool; auto is_key_pressed(uint32_t key) const -> bool;
auto is_key_released(uint32_t key) const -> bool;
static auto the() -> Application &;
private: private:
enum class Backend {
SDL,
KMS,
};
Application();
~Application();
auto init_input() -> void; auto init_input() -> void;
auto init_test_meshes() -> void;
auto init_wayland() -> void;
auto asset_directory() -> std::filesystem::path;
auto shutdown_input() -> void; auto shutdown_input() -> void;
auto process_libinput_events() -> void; auto process_libinput_events() -> void;
auto handle_keyboard_event(libinput_event_keyboard *event) -> void; auto handle_keyboard_event(libinput_event_keyboard *event) -> void;
auto handle_pointer_motion(libinput_event_pointer *event) -> void;
auto handle_pointer_button(libinput_event_pointer *event) -> void;
auto handle_pointer_axis(libinput_event_pointer *event) -> void;
auto handle_pointer_frame() -> void;
auto handle_pointer_end_frame() -> void;
auto handle_pointer_cancel() -> void;
auto handle_keyboard_key(std::optional<uint32_t> key, bool pressed) -> void;
auto clamp_mouse_to_window(int width, int height) -> void; auto clamp_mouse_to_window(int width, int height) -> void;
auto init_openxr() -> void;
auto init_openxr_session() -> void;
auto shutdown_openxr() -> void;
auto poll_openxr_events() -> void;
auto render_openxr_frame(
std::function<void(VulkanRenderer::GL &)> const &record,
float dt_seconds) -> bool;
auto update_camera_from_xr_view(XrView const &view) -> void;
auto update_hands(XrTime display_time) -> void;
auto render_hands(
VulkanRenderer::GL &gl, smath::Mat4 const &view_projection) -> void;
SDL_Window *m_window { nullptr }; SDL_Window *m_window { nullptr };
Backend m_backend { Backend::SDL };
Logger m_logger { "Lunar" }; Logger m_logger { "Lunar" };
std::unique_ptr<VulkanRenderer> m_renderer; std::unique_ptr<VulkanRenderer> m_renderer;
Skybox m_skybox;
std::vector<std::shared_ptr<Mesh>> m_test_meshes;
udev *m_udev { nullptr }; udev *m_udev { nullptr };
libinput *m_libinput { nullptr }; libinput *m_libinput { nullptr };
std::unique_ptr<OpenXrState> m_openxr {};
std::unique_ptr<Wayland::WaylandServer> m_wayland {};
bool m_running { true }; bool m_running { true };
bool m_mouse_captured { false }; bool m_mouse_captured { false };
bool m_show_imgui { false }; bool m_show_imgui { false };
bool m_window_focused { true };
int m_ctrl_pressed_count { 0 }; int m_ctrl_pressed_count { 0 };
std::uint32_t m_screenshot_index { 0 };
double m_mouse_x { 0.0 }; double m_mouse_x { 0.0 };
double m_mouse_y { 0.0 }; double m_mouse_y { 0.0 };
@@ -56,9 +110,17 @@ private:
float m_mouse_sensitivity { 0.001f }; float m_mouse_sensitivity { 0.001f };
std::array<bool, KEY_MAX + 1> m_key_state {}; std::array<bool, KEY_MAX + 1> m_key_state {};
std::array<bool, KEY_MAX + 1> m_key_state_previous {};
Camera m_camera; Camera m_camera;
PolarCoordinate m_cursor; PolarCoordinate m_cursor;
static inline std::array<smath::Vec3, XR_HAND_JOINT_COUNT_EXT>
m_left_joints {};
static inline std::array<smath::Vec3, XR_HAND_JOINT_COUNT_EXT>
m_right_joints {};
static inline bool m_left_hand_valid { false };
static inline bool m_right_hand_valid { false };
}; };
} // namespace Lunar } // namespace Lunar

88
src/CPUTexture.cpp Normal file
View File

@@ -0,0 +1,88 @@
#include "CPUTexture.h"
#include <format>
#include <stdexcept>
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wreserved-identifier"
# pragma clang diagnostic ignored "-Wcast-qual"
# pragma clang diagnostic ignored "-Wimplicit-fallthrough"
# pragma clang diagnostic ignored "-Wmissing-field-initializers"
# pragma clang diagnostic ignored "-Wused-but-marked-unused"
# pragma clang diagnostic ignored "-Wmissing-prototypes"
# pragma clang diagnostic ignored "-Wextra-semi-stmt"
# pragma clang diagnostic ignored "-Wimplicit-int-conversion"
# pragma clang diagnostic ignored "-Wsign-conversion"
# pragma clang diagnostic ignored "-Wshorten-64-to-32"
# pragma clang diagnostic ignored "-Wconversion"
# pragma clang diagnostic ignored "-Wcomma"
# pragma clang diagnostic ignored "-Wdouble-promotion"
# pragma clang diagnostic ignored "-Wimplicit-float-conversion"
# pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
# pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
# pragma clang diagnostic ignored "-Wsign-compare"
# pragma clang diagnostic ignored "-Wfloat-equal"
# pragma clang diagnostic ignored "-Wpacked"
# pragma clang diagnostic ignored "-Wold-style-cast"
# pragma clang diagnostic ignored "-Wexit-time-destructors"
# pragma clang diagnostic ignored "-Wglobal-constructors"
# pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
# pragma clang diagnostic ignored "-Wcast-align"
# pragma clang diagnostic ignored "-Wcast-qual"
# pragma clang diagnostic ignored "-Wshadow"
# pragma clang diagnostic ignored "-Wnewline-eof"
# pragma clang diagnostic ignored "-Wformat-nonliteral"
# pragma clang diagnostic ignored "-Wswitch-default"
# pragma clang diagnostic ignored "-Wswitch-enum"
# pragma clang diagnostic ignored "-Wcovered-switch-default"
# pragma clang diagnostic ignored "-Wdocumentation"
# pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
# pragma clang diagnostic ignored "-Wextra-semi"
# pragma clang diagnostic ignored "-Wundef"
# pragma clang diagnostic ignored "-Wreserved-macro-identifier"
# pragma clang diagnostic ignored "-Wc++98-compat"
# pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
# pragma clang diagnostic ignored "-Wweak-vtables"
# pragma clang diagnostic ignored "-Wswitch"
# pragma clang diagnostic ignored "-Wunused-macros"
# pragma clang diagnostic ignored "-Wextra"
#endif
#define STB_IMAGE_IMPLEMENTATION
#include "../thirdparty/stb/stb_image.h"
#if defined(__clang__)
# pragma clang diagnostic pop
#endif
namespace Lunar {
CPUTexture::CPUTexture(std::filesystem::path const &path)
{
int width_out { 0 };
int height_out { 0 };
int channels_out { 0 };
stbi_uc *data { stbi_load(path.string().c_str(), &width_out, &height_out,
&channels_out, STBI_rgb_alpha) };
if (!data) {
throw std::runtime_error(
std::format("Failed to load texture: {}", path.string()));
}
width = static_cast<uint32_t>(width_out);
height = static_cast<uint32_t>(height_out);
format = vk::Format::eR8G8B8A8Unorm;
pixels.assign(data, data + (width * height * 4));
stbi_image_free(data);
}
CPUTexture::CPUTexture(std::vector<uint8_t> pixels, uint32_t width,
uint32_t height, vk::Format format)
: pixels(std::move(pixels))
, width(width)
, height(height)
, format(format)
{
}
} // namespace Lunar

22
src/CPUTexture.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <cstdint>
#include <filesystem>
#include <vector>
#include <vulkan/vulkan.hpp>
namespace Lunar {
struct CPUTexture {
std::vector<uint8_t> pixels;
uint32_t width { 0 };
uint32_t height { 0 };
vk::Format format { vk::Format::eR8G8B8A8Unorm };
explicit CPUTexture(std::filesystem::path const &path);
CPUTexture(std::vector<uint8_t> pixels, uint32_t width, uint32_t height,
vk::Format format);
};
} // namespace Lunar

View File

@@ -78,7 +78,7 @@ auto DescriptorAllocatorGrowable::destroy_pools(VkDevice dev) -> void
auto DescriptorAllocatorGrowable::allocate(Logger &logger, VkDevice dev, auto DescriptorAllocatorGrowable::allocate(Logger &logger, VkDevice dev,
VkDescriptorSetLayout layout, void *p_next) -> VkDescriptorSet VkDescriptorSetLayout layout, void *p_next) -> VkDescriptorSet
{ {
auto pool_to_use = get_pool(dev); auto pool_to_use { get_pool(dev) };
VkDescriptorSetAllocateInfo alloci {}; VkDescriptorSetAllocateInfo alloci {};
alloci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;

View File

@@ -32,6 +32,8 @@ auto GraphicsPipelineBuilder::clear() -> GraphicsPipelineBuilder &
m_render_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; m_render_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
m_shader_stages.clear(); m_shader_stages.clear();
m_vertex_bindings.clear();
m_vertex_attributes.clear();
return *this; return *this;
} }
@@ -168,6 +170,17 @@ auto GraphicsPipelineBuilder::set_pipeline_layout(VkPipelineLayout layout)
return *this; return *this;
} }
auto GraphicsPipelineBuilder::set_vertex_input(
std::span<VkVertexInputBindingDescription const> bindings,
std::span<VkVertexInputAttributeDescription const> attributes)
-> GraphicsPipelineBuilder &
{
m_vertex_bindings.assign(bindings.begin(), bindings.end());
m_vertex_attributes.assign(attributes.begin(), attributes.end());
return *this;
}
auto GraphicsPipelineBuilder::disable_depth_testing() auto GraphicsPipelineBuilder::disable_depth_testing()
-> GraphicsPipelineBuilder & -> GraphicsPipelineBuilder &
{ {
@@ -223,6 +236,12 @@ auto GraphicsPipelineBuilder::build(VkDevice dev) -> VkPipeline
VkPipelineVertexInputStateCreateInfo vertex_input_ci {}; VkPipelineVertexInputStateCreateInfo vertex_input_ci {};
vertex_input_ci.sType vertex_input_ci.sType
= VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_ci.vertexBindingDescriptionCount
= static_cast<uint32_t>(m_vertex_bindings.size());
vertex_input_ci.pVertexBindingDescriptions = m_vertex_bindings.data();
vertex_input_ci.vertexAttributeDescriptionCount
= static_cast<uint32_t>(m_vertex_attributes.size());
vertex_input_ci.pVertexAttributeDescriptions = m_vertex_attributes.data();
VkGraphicsPipelineCreateInfo pipeline_ci {}; VkGraphicsPipelineCreateInfo pipeline_ci {};
pipeline_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipeline_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <span>
#include <vector> #include <vector>
#include <vulkan/vulkan_core.h> #include <vulkan/vulkan_core.h>
@@ -35,6 +36,10 @@ struct GraphicsPipelineBuilder {
auto set_depth_format(VkFormat format) -> GraphicsPipelineBuilder &; auto set_depth_format(VkFormat format) -> GraphicsPipelineBuilder &;
auto set_pipeline_layout(VkPipelineLayout layout) auto set_pipeline_layout(VkPipelineLayout layout)
-> GraphicsPipelineBuilder &; -> GraphicsPipelineBuilder &;
auto set_vertex_input(
std::span<VkVertexInputBindingDescription const> bindings,
std::span<VkVertexInputAttributeDescription const> attributes)
-> GraphicsPipelineBuilder &;
auto disable_depth_testing() -> GraphicsPipelineBuilder &; auto disable_depth_testing() -> GraphicsPipelineBuilder &;
auto enable_depth_testing(bool depth_write_enable = true, auto enable_depth_testing(bool depth_write_enable = true,
VkCompareOp op = VK_COMPARE_OP_LESS_OR_EQUAL) VkCompareOp op = VK_COMPARE_OP_LESS_OR_EQUAL)
@@ -52,6 +57,8 @@ private:
VkFormat m_color_attachment_format {}; VkFormat m_color_attachment_format {};
std::vector<VkPipelineShaderStageCreateInfo> m_shader_stages {}; std::vector<VkPipelineShaderStageCreateInfo> m_shader_stages {};
std::vector<VkVertexInputBindingDescription> m_vertex_bindings {};
std::vector<VkVertexInputAttributeDescription> m_vertex_attributes {};
Logger &m_logger; Logger &m_logger;
}; };

View File

@@ -61,7 +61,7 @@ auto Mesh::load_gltf_meshes(
{ {
renderer.logger().debug("Loading GLTF from file: {}", path); renderer.logger().debug("Loading GLTF from file: {}", path);
auto data = fastgltf::GltfDataBuffer::FromPath(path); auto data { fastgltf::GltfDataBuffer::FromPath(path) };
if (data.error() != fastgltf::Error::None) { if (data.error() != fastgltf::Error::None) {
renderer.logger().err("Failed to open glTF file: {} (error {})", path, renderer.logger().err("Failed to open glTF file: {} (error {})", path,
fastgltf::to_underlying(data.error())); fastgltf::to_underlying(data.error()));
@@ -98,7 +98,7 @@ auto Mesh::load_gltf_meshes(
new_surface.count = static_cast<uint32_t>( new_surface.count = static_cast<uint32_t>(
gltf.accessors[p.indicesAccessor.value()].count); gltf.accessors[p.indicesAccessor.value()].count);
size_t initial_vertex = vertices.size(); size_t initial_vertex { vertices.size() };
{ // Indices { // Indices
auto &accessor = gltf.accessors[p.indicesAccessor.value()]; auto &accessor = gltf.accessors[p.indicesAccessor.value()];
@@ -128,7 +128,7 @@ auto Mesh::load_gltf_meshes(
if (auto attr = p.findAttribute("NORMAL")) { // Normals if (auto attr = p.findAttribute("NORMAL")) { // Normals
auto &accessor = gltf.accessors[attr->accessorIndex]; auto &accessor = gltf.accessors[attr->accessorIndex];
size_t local_index = 0; size_t local_index { 0 };
for (auto normal : for (auto normal :
fastgltf::iterateAccessor<smath::Vec3>(gltf, accessor)) { fastgltf::iterateAccessor<smath::Vec3>(gltf, accessor)) {
vertices[initial_vertex + local_index].normal = normal; vertices[initial_vertex + local_index].normal = normal;
@@ -138,7 +138,7 @@ auto Mesh::load_gltf_meshes(
if (auto attr = p.findAttribute("TEXCOORD_0")) { // UVs if (auto attr = p.findAttribute("TEXCOORD_0")) { // UVs
auto &accessor = gltf.accessors[attr->accessorIndex]; auto &accessor = gltf.accessors[attr->accessorIndex];
size_t local_index = 0; size_t local_index { 0 };
for (auto uv : for (auto uv :
fastgltf::iterateAccessor<smath::Vec2>(gltf, accessor)) { fastgltf::iterateAccessor<smath::Vec2>(gltf, accessor)) {
uv.unpack(vertices[initial_vertex + local_index].u, uv.unpack(vertices[initial_vertex + local_index].u,
@@ -149,7 +149,7 @@ auto Mesh::load_gltf_meshes(
if (auto attr = p.findAttribute("COLOR_0")) { // Colors if (auto attr = p.findAttribute("COLOR_0")) { // Colors
auto &accessor = gltf.accessors[attr->accessorIndex]; auto &accessor = gltf.accessors[attr->accessorIndex];
size_t local_index = 0; size_t local_index { 0 };
switch (accessor.type) { switch (accessor.type) {
case fastgltf::AccessorType::Vec3: { case fastgltf::AccessorType::Vec3: {

View File

@@ -34,7 +34,7 @@
static std::filesystem::path get_log_path(std::string_view app_name) static std::filesystem::path get_log_path(std::string_view app_name)
{ {
#ifdef _WIN32 #ifdef _WIN32
PWSTR path = nullptr; PWSTR path { nullptr };
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path); SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path);
std::wstring wpath(path); std::wstring wpath(path);
CoTaskMemFree(path); CoTaskMemFree(path);
@@ -70,7 +70,7 @@ static int compress_file(std::filesystem::path const &input_path,
std::vector<char> buffer(chunk_size); std::vector<char> buffer(chunk_size);
while (in) { while (in) {
in.read(buffer.data(), static_cast<std::streamsize>(buffer.size())); in.read(buffer.data(), static_cast<std::streamsize>(buffer.size()));
std::streamsize bytes = in.gcount(); std::streamsize bytes { in.gcount() };
if (bytes > 0) if (bytes > 0)
gzwrite(out, buffer.data(), static_cast<unsigned int>(bytes)); gzwrite(out, buffer.data(), static_cast<unsigned int>(bytes));
} }
@@ -99,20 +99,20 @@ Logger::Logger(std::string_view app_name)
if (!file.is_regular_file()) if (!file.is_regular_file())
continue; continue;
auto name = file.path().filename().stem().string(); auto name { file.path().filename().stem().string() };
constexpr std::string_view prefix = "log_"; constexpr std::string_view prefix = "log_";
if (name.rfind(prefix, 0) != 0) { if (name.rfind(prefix, 0) != 0) {
continue; continue;
} }
int v = std::stoi(name.substr(prefix.size())); int v { std::stoi(name.substr(prefix.size())) };
if (v > max) if (v > max)
max = v; max = v;
auto ext = file.path().filename().extension().string(); auto ext { file.path().filename().extension().string() };
if (ext == ".txt") { if (ext == ".txt") {
auto np = file.path(); auto np { file.path() };
np.replace_extension(ext + ".gz"); np.replace_extension(ext + ".gz");
compress_file(file.path(), np); compress_file(file.path(), np);
} }
@@ -153,7 +153,7 @@ static std::string get_current_time_string()
void Logger::log(Level level, std::string_view msg) void Logger::log(Level level, std::string_view msg)
{ {
auto time_str = get_current_time_string(); auto time_str { get_current_time_string() };
std::string level_str; std::string level_str;
switch (level) { switch (level) {
case Logger::Level::Debug: case Logger::Level::Debug:

View File

@@ -27,13 +27,13 @@ auto Pipeline::Builder::set_push_constant_ranges(
auto Pipeline::Builder::build_compute( auto Pipeline::Builder::build_compute(
vk::PipelineShaderStageCreateInfo const &stage) -> Pipeline vk::PipelineShaderStageCreateInfo const &stage) -> Pipeline
{ {
auto pipeline_layout = build_layout(); auto pipeline_layout { build_layout() };
vk::ComputePipelineCreateInfo pipeline_ci {}; vk::ComputePipelineCreateInfo pipeline_ci {};
pipeline_ci.layout = pipeline_layout.get(); pipeline_ci.layout = pipeline_layout.get();
pipeline_ci.stage = stage; pipeline_ci.stage = stage;
auto pipeline_ret = m_device.createComputePipelineUnique({}, pipeline_ci); auto pipeline_ret { m_device.createComputePipelineUnique({}, pipeline_ci) };
VK_CHECK(m_logger, pipeline_ret.result); VK_CHECK(m_logger, pipeline_ret.result);
return Pipeline { return Pipeline {
@@ -46,14 +46,14 @@ auto Pipeline::Builder::build_graphics(
std::function<GraphicsPipelineBuilder &(GraphicsPipelineBuilder &)> const std::function<GraphicsPipelineBuilder &(GraphicsPipelineBuilder &)> const
&configure) -> Pipeline &configure) -> Pipeline
{ {
auto pipeline_layout = build_layout(); auto pipeline_layout { build_layout() };
auto builder = GraphicsPipelineBuilder { m_logger }; auto builder { GraphicsPipelineBuilder { m_logger } };
builder.set_pipeline_layout( builder.set_pipeline_layout(
static_cast<VkPipelineLayout>(pipeline_layout.get())); static_cast<VkPipelineLayout>(pipeline_layout.get()));
configure(builder); configure(builder);
auto pipeline_handle = builder.build(static_cast<VkDevice>(m_device)); auto pipeline_handle { builder.build(static_cast<VkDevice>(m_device)) };
vk::UniquePipeline pipeline_unique(pipeline_handle, vk::UniquePipeline pipeline_unique(pipeline_handle,
vk::detail::ObjectDestroy<vk::Device, vk::detail::ObjectDestroy<vk::Device,
VULKAN_HPP_DEFAULT_DISPATCHER_TYPE>(m_device)); VULKAN_HPP_DEFAULT_DISPATCHER_TYPE>(m_device));

314
src/Skybox.cpp Normal file
View File

@@ -0,0 +1,314 @@
#include "Skybox.h"
#include <array>
#include <cstddef>
#include <span>
#include <vector>
#include "CPUTexture.h"
#include "DescriptorWriter.h"
#include "GraphicsPipelineBuilder.h"
#include "Util.h"
namespace Lunar {
namespace {
struct SkyboxPushConstants {
smath::Mat4 mvp;
};
struct FaceOffset {
uint32_t x;
uint32_t y;
};
constexpr std::array<FaceOffset, 6> CROSS_OFFSETS {
FaceOffset { 2, 1 }, // +X
FaceOffset { 0, 1 }, // -X
FaceOffset { 1, 0 }, // +Y
FaceOffset { 1, 2 }, // -Y
FaceOffset { 1, 1 }, // +Z
FaceOffset { 3, 1 }, // -Z
};
} // namespace
auto Skybox::rebuild_pipeline(VulkanRenderer &renderer) -> bool
{
Pipeline::Builder pipeline_builder { renderer.device(), renderer.logger() };
uint8_t skybox_vert_shader_data[] {
#embed "skybox_vert.spv"
};
auto skybox_vert_shader
= vkutil::load_shader_module(std::span<uint8_t>(skybox_vert_shader_data,
sizeof(skybox_vert_shader_data)),
renderer.device());
if (!skybox_vert_shader) {
renderer.logger().err("Failed to load skybox vert shader");
return false;
}
uint8_t skybox_frag_shader_data[] {
#embed "skybox_frag.spv"
};
auto skybox_frag_shader
= vkutil::load_shader_module(std::span<uint8_t>(skybox_frag_shader_data,
sizeof(skybox_frag_shader_data)),
renderer.device());
if (!skybox_frag_shader) {
renderer.logger().err("Failed to load skybox frag shader");
return false;
}
vk::PushConstantRange push_constant_range {};
push_constant_range.stageFlags = vk::ShaderStageFlagBits::eVertex;
push_constant_range.offset = 0;
push_constant_range.size = sizeof(SkyboxPushConstants);
std::array push_constant_ranges { push_constant_range };
pipeline_builder.set_push_constant_ranges(push_constant_ranges);
std::array descriptor_set_layouts {
renderer.single_image_descriptor_layout()
};
pipeline_builder.set_descriptor_set_layouts(descriptor_set_layouts);
VkVertexInputBindingDescription binding {};
binding.binding = 0;
binding.stride = sizeof(Vertex);
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription attribute {};
attribute.location = 0;
attribute.binding = 0;
attribute.format = VK_FORMAT_R32G32B32_SFLOAT;
attribute.offset = offsetof(Vertex, position);
std::array bindings { binding };
std::array attributes { attribute };
m_pipeline = pipeline_builder.build_graphics(
[&](GraphicsPipelineBuilder &builder) -> GraphicsPipelineBuilder & {
builder.set_vertex_input(bindings, attributes);
return builder
.set_shaders(skybox_vert_shader.get(), skybox_frag_shader.get())
.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
.set_polygon_mode(VK_POLYGON_MODE_FILL)
.set_cull_mode(
VK_CULL_MODE_FRONT_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE)
.set_multisampling(
static_cast<VkSampleCountFlagBits>(renderer.msaa_samples()))
.disable_blending()
.enable_depth_testing(false, VK_COMPARE_OP_LESS_OR_EQUAL)
.set_color_attachment_format(
static_cast<VkFormat>(renderer.draw_image_format()))
.set_depth_format(
static_cast<VkFormat>(renderer.depth_image_format()));
});
m_pipeline_samples = renderer.msaa_samples();
return true;
}
auto Skybox::init(VulkanRenderer &renderer, std::filesystem::path const &path)
-> void
{
if (ok) {
destroy(renderer);
ok = false;
}
CPUTexture texture { path };
if (texture.width == 0 || texture.height == 0) {
renderer.logger().err("Skybox texture is empty: {}", path.string());
ok = false;
return;
}
if (texture.width % 4 != 0 || texture.height % 3 != 0
|| texture.width / 4 != texture.height / 3) {
renderer.logger().err(
"Skybox texture must be 4x3 faces: {}", path.string());
ok = false;
return;
}
uint32_t const face_size = texture.width / 4;
size_t const face_bytes = static_cast<size_t>(face_size) * face_size * 4;
std::vector<uint8_t> cubemap_pixels(face_bytes * CROSS_OFFSETS.size());
for (size_t face = 0; face < CROSS_OFFSETS.size(); ++face) {
auto const offset = CROSS_OFFSETS[face];
for (uint32_t y = 0; y < face_size; ++y) {
for (uint32_t x = 0; x < face_size; ++x) {
uint32_t const src_x = offset.x * face_size + x;
uint32_t const src_y = offset.y * face_size + y;
size_t const src_index
= (static_cast<size_t>(src_y) * texture.width + src_x) * 4;
size_t const dst_index = face * face_bytes
+ (static_cast<size_t>(y) * face_size + x) * 4;
std::copy_n(texture.pixels.data() + src_index, 4,
cubemap_pixels.data() + dst_index);
}
}
}
m_cubemap = renderer.create_cubemap(cubemap_pixels, face_size,
texture.format, vk::ImageUsageFlagBits::eSampled);
if (!m_cubemap.image) {
renderer.logger().err("Failed to create cubemap image");
return;
}
vk::SamplerCreateInfo sampler_ci {};
sampler_ci.magFilter = vk::Filter::eLinear;
sampler_ci.minFilter = vk::Filter::eLinear;
sampler_ci.mipmapMode = vk::SamplerMipmapMode::eLinear;
sampler_ci.addressModeU = vk::SamplerAddressMode::eClampToEdge;
sampler_ci.addressModeV = vk::SamplerAddressMode::eClampToEdge;
sampler_ci.addressModeW = vk::SamplerAddressMode::eClampToEdge;
m_sampler = renderer.device().createSamplerUnique(sampler_ci);
vk::DescriptorPoolSize pool_size {};
pool_size.type = vk::DescriptorType::eCombinedImageSampler;
pool_size.descriptorCount = 1;
vk::DescriptorPoolCreateInfo pool_ci {};
pool_ci.maxSets = 1;
pool_ci.poolSizeCount = 1;
pool_ci.pPoolSizes = &pool_size;
m_descriptor_pool = renderer.device().createDescriptorPoolUnique(pool_ci);
vk::DescriptorSetAllocateInfo alloc_info {};
alloc_info.descriptorPool = m_descriptor_pool.get();
alloc_info.descriptorSetCount = 1;
vk::DescriptorSetLayout layout {
renderer.single_image_descriptor_layout()
};
alloc_info.pSetLayouts = &layout;
m_descriptor_set
= renderer.device().allocateDescriptorSets(alloc_info).front();
DescriptorWriter()
.write_image(0, m_cubemap.image_view, m_sampler.get(),
static_cast<VkImageLayout>(vk::ImageLayout::eShaderReadOnlyOptimal),
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
.update_set(renderer.device(), m_descriptor_set);
std::vector<Vertex> vertices;
vertices.reserve(8);
auto push_vertex = [&](smath::Vec3 const &pos) {
Vertex v {};
v.position = pos;
v.u = 0.0f;
v.v = 0.0f;
v.normal = smath::Vec3 { 0.0f, 0.0f, 1.0f };
v.color = smath::Vec4 { 1.0f, 1.0f, 1.0f, 1.0f };
vertices.emplace_back(v);
};
push_vertex(smath::Vec3 { -1.0f, -1.0f, -1.0f });
push_vertex(smath::Vec3 { 1.0f, -1.0f, -1.0f });
push_vertex(smath::Vec3 { 1.0f, 1.0f, -1.0f });
push_vertex(smath::Vec3 { -1.0f, 1.0f, -1.0f });
push_vertex(smath::Vec3 { -1.0f, -1.0f, 1.0f });
push_vertex(smath::Vec3 { 1.0f, -1.0f, 1.0f });
push_vertex(smath::Vec3 { 1.0f, 1.0f, 1.0f });
push_vertex(smath::Vec3 { -1.0f, 1.0f, 1.0f });
std::vector<uint32_t> indices {
4,
5,
6,
4,
6,
7, // +Z
1,
0,
3,
1,
3,
2, // -Z
5,
1,
2,
5,
2,
6, // +X
0,
4,
7,
0,
7,
3, // -X
7,
6,
2,
7,
2,
3, // +Y
0,
1,
5,
0,
5,
4, // -Y
};
m_index_count = static_cast<uint32_t>(indices.size());
m_cube_mesh = renderer.upload_mesh(indices, vertices);
if (!rebuild_pipeline(renderer)) {
ok = false;
return;
}
ok = true;
}
auto Skybox::destroy(VulkanRenderer &renderer) -> void
{
if (m_cube_mesh.index_buffer.buffer) {
renderer.destroy_buffer(m_cube_mesh.index_buffer);
}
if (m_cube_mesh.vertex_buffer.buffer) {
renderer.destroy_buffer(m_cube_mesh.vertex_buffer);
}
if (m_cubemap.image) {
renderer.destroy_image(m_cubemap);
}
m_sampler.reset();
m_descriptor_pool.reset();
m_pipeline.reset();
m_pipeline_samples = vk::SampleCountFlagBits::e1;
m_descriptor_set = vk::DescriptorSet {};
m_cube_mesh = {};
m_cubemap = {};
m_index_count = 0;
ok = false;
}
auto Skybox::draw(VulkanRenderer::GL &gl, VulkanRenderer &renderer,
smath::Mat4 const &mvp) -> void
{
if (!ok) {
return;
}
if (m_pipeline_samples != renderer.msaa_samples()) {
if (!rebuild_pipeline(renderer)) {
return;
}
}
SkyboxPushConstants push_constants { mvp };
auto bytes { std::as_bytes(std::span { &push_constants, 1 }) };
gl.draw_indexed(m_pipeline, m_descriptor_set, m_cube_mesh.vertex_buffer,
m_cube_mesh.index_buffer, m_index_count, bytes);
}
} // namespace Lunar

36
src/Skybox.h Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include <cstdint>
#include <filesystem>
#include <vulkan/vulkan.hpp>
#include "Pipeline.h"
#include "Types.h"
#include "VulkanRenderer.h"
namespace Lunar {
struct Skybox {
bool ok { false };
auto init(VulkanRenderer &renderer, std::filesystem::path const &path)
-> void;
auto destroy(VulkanRenderer &renderer) -> void;
auto draw(VulkanRenderer::GL &gl, VulkanRenderer &renderer,
smath::Mat4 const &mvp) -> void;
private:
auto rebuild_pipeline(VulkanRenderer &renderer) -> bool;
Pipeline m_pipeline {};
GPUMeshBuffers m_cube_mesh {};
AllocatedImage m_cubemap {};
vk::UniqueSampler m_sampler {};
vk::UniqueDescriptorPool m_descriptor_pool {};
vk::DescriptorSet m_descriptor_set {};
vk::SampleCountFlagBits m_pipeline_samples { vk::SampleCountFlagBits::e1 };
uint32_t m_index_count { 0 };
};
} // namespace Lunar

View File

@@ -1,6 +1,8 @@
#pragma once #pragma once
#include <cmath> #include <cmath>
#include <cstdint>
#include <vector>
#include <smath.hpp> #include <smath.hpp>
#include <vk_mem_alloc.h> #include <vk_mem_alloc.h>
@@ -33,6 +35,15 @@ struct FrameData {
DeletionQueue deletion_queue; DeletionQueue deletion_queue;
DescriptorAllocatorGrowable frame_descriptors; DescriptorAllocatorGrowable frame_descriptors;
AllocatedBuffer frame_image_buffer {};
vk::Extent2D frame_image_extent {};
std::vector<std::uint8_t> frame_image_rgba;
bool frame_image_ready { false };
bool tracy_frame_ready { false };
AllocatedBuffer screenshot_buffer {};
vk::Extent2D screenshot_extent {};
std::vector<std::uint8_t> screenshot_rgba;
bool screenshot_ready { false };
}; };
struct Vertex { struct Vertex {
@@ -85,7 +96,7 @@ struct PolarCoordinate {
smath::Vec3 to_vec3() const smath::Vec3 to_vec3() const
{ {
float sin_phi = std::sin(phi); float sin_phi { std::sin(phi) };
return smath::Vec3 { r * sin_phi * std::cos(theta), r * std::cos(phi), return smath::Vec3 { r * sin_phi * std::cos(theta), r * std::cos(phi),
r * sin_phi * std::sin(theta) }; r * sin_phi * std::sin(theta) };

View File

@@ -29,13 +29,21 @@ template<typename F> privDefer<F> defer_func(F f) { return privDefer<F>(f); }
#define VK_CHECK(logger, x) \ #define VK_CHECK(logger, x) \
do { \ do { \
auto err { x }; \ auto err { x }; \
auto result = vk::Result(err); \ auto result { vk::Result(err) }; \
if (result != vk::Result::eSuccess) { \ if (result != vk::Result::eSuccess) { \
(logger).err("Detected Vulkan error: {}", vk::to_string(result)); \ (logger).err("Detected Vulkan error: {}", vk::to_string(result)); \
throw std::runtime_error("Vulkan error"); \ throw std::runtime_error("Vulkan error"); \
} \ } \
} while (0) } while (0)
#if defined(TRACY_ENABLE)
# define GZoneScopedN(name) ZoneScopedN(name)
#else
# define GZoneScopedN(name) \
do { \
} while (0)
#endif
namespace vkutil { namespace vkutil {
auto transition_image(vk::CommandBuffer cmd, vk::Image image, auto transition_image(vk::CommandBuffer cmd, vk::Image image,

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,13 @@
#pragma once #pragma once
#include <array> #include <array>
#include <cstddef>
#include <cstdint>
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <span>
#include <string>
#include <variant> #include <variant>
#include <vector> #include <vector>
@@ -13,6 +17,7 @@
#include <vk_mem_alloc.h> #include <vk_mem_alloc.h>
#include <vulkan/vulkan.hpp> #include <vulkan/vulkan.hpp>
#include "CPUTexture.h"
#include "Colors.h" #include "Colors.h"
#include "DeletionQueue.h" #include "DeletionQueue.h"
#include "Loader.h" #include "Loader.h"
@@ -30,14 +35,13 @@ struct GPUDrawPushConstants {
constexpr unsigned FRAME_OVERLAP = 2; constexpr unsigned FRAME_OVERLAP = 2;
struct VulkanRenderer { struct VulkanRenderer {
enum class AntiAliasingKind { struct ScreenshotPixels {
NONE, std::span<std::uint8_t const> pixels;
MSAA_2X, vk::Extent2D extent;
MSAA_4X,
MSAA_8X,
}; };
struct GL { struct GL {
enum class GeometryKind { enum class GeometryKind {
Triangles, Triangles,
TriangleStrip, TriangleStrip,
@@ -90,6 +94,10 @@ struct VulkanRenderer {
auto draw_mesh(GPUMeshBuffers const &mesh, smath::Mat4 const &transform, auto draw_mesh(GPUMeshBuffers const &mesh, smath::Mat4 const &transform,
uint32_t index_count, uint32_t first_index = 0, uint32_t index_count, uint32_t first_index = 0,
int32_t vertex_offset = 0) -> void; int32_t vertex_offset = 0) -> void;
auto draw_indexed(Pipeline &pipeline, vk::DescriptorSet descriptor_set,
AllocatedBuffer const &vertex_buffer,
AllocatedBuffer const &index_buffer, uint32_t index_count,
std::span<std::byte const> push_constants) -> void;
private: private:
auto push_vertex(smath::Vec3 const &pos) -> void; auto push_vertex(smath::Vec3 const &pos) -> void;
@@ -116,12 +124,30 @@ struct VulkanRenderer {
std::vector<uint32_t> m_indices; std::vector<uint32_t> m_indices;
}; };
VulkanRenderer(SDL_Window *window, Logger &logger); enum class AntiAliasingKind {
NONE,
MSAA_2X,
MSAA_4X,
MSAA_8X,
};
struct KmsSurfaceConfig { };
VulkanRenderer(SDL_Window *window, Logger &logger,
std::span<std::string const> instance_extensions = {},
std::span<std::string const> device_extensions = {});
VulkanRenderer(KmsSurfaceConfig config, Logger &logger,
std::span<std::string const> instance_extensions = {},
std::span<std::string const> device_extensions = {});
~VulkanRenderer(); ~VulkanRenderer();
auto render(std::function<void(GL &)> const &record = {}) -> void; auto render(std::function<void(GL &)> const &record = {}) -> void;
auto render_to_image(vk::Image target_image, vk::Extent2D target_extent,
std::function<void(GL &)> const &record = {}) -> void;
auto resize(uint32_t width, uint32_t height) -> void; auto resize(uint32_t width, uint32_t height) -> void;
auto set_offscreen_extent(vk::Extent2D extent) -> void;
auto set_antialiasing(AntiAliasingKind kind) -> void; auto set_antialiasing(AntiAliasingKind kind) -> void;
auto set_antialiasing_immediate(AntiAliasingKind kind) -> void;
auto antialiasing() const -> AntiAliasingKind auto antialiasing() const -> AntiAliasingKind
{ {
return m_vk.antialiasing_kind; return m_vk.antialiasing_kind;
@@ -132,14 +158,17 @@ struct VulkanRenderer {
bool clear_frame_descriptors = true) -> void; bool clear_frame_descriptors = true) -> void;
auto upload_mesh(std::span<uint32_t> indices, std::span<Vertex> vertices) auto upload_mesh(std::span<uint32_t> indices, std::span<Vertex> vertices)
-> GPUMeshBuffers; -> GPUMeshBuffers;
auto destroy_buffer(AllocatedBuffer const &buffer) -> void;
auto create_image(CPUTexture const &texture, vk::ImageUsageFlags flags,
bool mipmapped = false) -> AllocatedImage;
auto create_cubemap(std::span<uint8_t const> pixels, uint32_t face_size,
vk::Format format, vk::ImageUsageFlags flags) -> AllocatedImage;
auto destroy_image(AllocatedImage const &img) -> void;
auto destroy_image_later(AllocatedImage img) -> void;
auto rectangle_mesh() const -> GPUMeshBuffers const & auto rectangle_mesh() const -> GPUMeshBuffers const &
{ {
return m_vk.rectangle; return m_vk.rectangle;
} }
auto test_meshes() const -> std::vector<std::shared_ptr<Mesh>> const &
{
return m_vk.test_meshes;
}
auto white_texture() const -> AllocatedImage const & auto white_texture() const -> AllocatedImage const &
{ {
return m_vk.white_image; return m_vk.white_image;
@@ -158,13 +187,63 @@ struct VulkanRenderer {
} }
auto draw_extent() const -> vk::Extent2D { return m_vk.draw_extent; } auto draw_extent() const -> vk::Extent2D { return m_vk.draw_extent; }
auto mesh_pipeline() -> Pipeline & { return m_vk.mesh_pipeline; } auto mesh_pipeline() -> Pipeline & { return m_vk.mesh_pipeline; }
auto wayland_pipeline() -> Pipeline & { return m_vk.wayland_pipeline; }
auto triangle_pipeline() -> Pipeline & { return m_vk.triangle_pipeline; } auto triangle_pipeline() -> Pipeline & { return m_vk.triangle_pipeline; }
auto device() const -> vk::Device { return m_device; }
auto instance() const -> vk::Instance { return m_instance; }
auto physical_device() const -> vk::PhysicalDevice
{
return m_physical_device;
}
auto graphics_queue() const -> vk::Queue { return m_vk.graphics_queue; }
auto graphics_queue_family() const -> uint32_t
{
return m_vk.graphics_queue_family;
}
auto draw_image_format() const -> vk::Format
{
return m_vk.draw_image.format;
}
auto depth_image_format() const -> vk::Format
{
return m_vk.depth_image.format;
}
auto msaa_samples() const -> vk::SampleCountFlagBits
{
return m_vk.msaa_samples;
}
auto single_image_descriptor_layout() const -> vk::DescriptorSetLayout
{
return m_vk.single_image_descriptor_layout;
}
auto gl_api() -> GL & { return gl; } auto gl_api() -> GL & { return gl; }
auto get_screenshot() const -> std::optional<AllocatedImage>
{
return m_latest_screenshot;
}
auto get_screenshot_pixels() const
-> std::optional<VulkanRenderer::ScreenshotPixels>
{
if (m_latest_screenshot_pixels.empty()
|| m_latest_screenshot_extent.width == 0
|| m_latest_screenshot_extent.height == 0) {
return {};
}
auto const span { std::span<std::uint8_t const> {
m_latest_screenshot_pixels.data(),
m_latest_screenshot_pixels.size() } };
return ScreenshotPixels { span, m_latest_screenshot_extent };
}
auto logger() const -> Logger & { return m_logger; } auto logger() const -> Logger & { return m_logger; }
GL gl; GL gl;
std::optional<AllocatedImage> m_latest_screenshot {};
std::vector<std::uint8_t> m_latest_screenshot_pixels {};
vk::Extent2D m_latest_screenshot_extent {};
private: private:
struct RenderCommand { struct RenderCommand {
struct SetAntiAliasing { struct SetAntiAliasing {
@@ -176,6 +255,7 @@ private:
auto vk_init() -> void; auto vk_init() -> void;
auto swapchain_init() -> void; auto swapchain_init() -> void;
auto setup_kms_surface() -> void;
auto commands_init() -> void; auto commands_init() -> void;
auto sync_init() -> void; auto sync_init() -> void;
auto descriptors_init() -> void; auto descriptors_init() -> void;
@@ -197,17 +277,27 @@ private:
auto destroy_msaa_color_image() -> void; auto destroy_msaa_color_image() -> void;
auto recreate_swapchain(uint32_t width, uint32_t height) -> void; auto recreate_swapchain(uint32_t width, uint32_t height) -> void;
auto destroy_swapchain() -> void; auto destroy_swapchain() -> void;
auto ensure_screenshot_buffers(vk::Extent2D extent) -> void;
auto destroy_screenshot_buffers() -> void;
auto emit_frame_screenshot(FrameData &frame) -> void;
#if defined(TRACY_ENABLE)
auto ensure_tracy_frame_buffers(vk::Extent2D extent) -> void;
auto destroy_tracy_frame_buffers() -> void;
auto emit_tracy_frame_image(FrameData &frame) -> void;
#endif
auto create_image(vk::Extent3D size, vk::Format format, auto create_image(vk::Extent3D size, vk::Format format,
vk::ImageUsageFlags flags, vk::ImageUsageFlags flags,
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1, vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1,
bool mipmapped = false) -> AllocatedImage; bool mipmapped = false) -> AllocatedImage;
auto create_image_no_view(vk::Extent3D size, vk::Format format,
vk::ImageUsageFlags flags,
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1,
bool mipmapped = false) -> AllocatedImage;
auto create_image(void const *data, vk::Extent3D size, vk::Format format, auto create_image(void const *data, vk::Extent3D size, vk::Format format,
vk::ImageUsageFlags flags, bool mipmapped = false) -> AllocatedImage; vk::ImageUsageFlags flags, bool mipmapped = false) -> AllocatedImage;
auto destroy_image(AllocatedImage const &img) -> void;
auto create_buffer(size_t alloc_size, vk::BufferUsageFlags usage, auto create_buffer(size_t alloc_size, vk::BufferUsageFlags usage,
VmaMemoryUsage memory_usage) -> AllocatedBuffer; VmaMemoryUsage memory_usage) -> AllocatedBuffer;
auto destroy_buffer(AllocatedBuffer const &buffer) -> void;
auto enqueue_render_command(RenderCommand &&command) -> void; auto enqueue_render_command(RenderCommand &&command) -> void;
auto process_render_commands() -> void; auto process_render_commands() -> void;
auto apply_antialiasing(AntiAliasingKind kind) -> void; auto apply_antialiasing(AntiAliasingKind kind) -> void;
@@ -248,6 +338,13 @@ private:
vk::ImageLayout msaa_color_image_layout { vk::ImageLayout::eUndefined }; vk::ImageLayout msaa_color_image_layout { vk::ImageLayout::eUndefined };
AllocatedImage depth_image {}; AllocatedImage depth_image {};
vk::ImageLayout depth_image_layout { vk::ImageLayout::eUndefined }; vk::ImageLayout depth_image_layout { vk::ImageLayout::eUndefined };
#if defined(TRACY_ENABLE)
AllocatedImage tracy_capture_image {};
vk::ImageLayout tracy_capture_image_layout {
vk::ImageLayout::eUndefined
};
vk::Extent2D tracy_capture_extent {};
#endif
vk::Extent2D draw_extent {}; vk::Extent2D draw_extent {};
AntiAliasingKind antialiasing_kind { AntiAliasingKind::NONE }; AntiAliasingKind antialiasing_kind { AntiAliasingKind::NONE };
vk::SampleCountFlagBits msaa_samples { vk::SampleCountFlagBits::e1 }; vk::SampleCountFlagBits msaa_samples { vk::SampleCountFlagBits::e1 };
@@ -264,6 +361,7 @@ private:
Pipeline triangle_pipeline_culled; Pipeline triangle_pipeline_culled;
Pipeline mesh_pipeline; Pipeline mesh_pipeline;
Pipeline mesh_pipeline_culled; Pipeline mesh_pipeline_culled;
Pipeline wayland_pipeline;
GPUMeshBuffers rectangle; GPUMeshBuffers rectangle;
@@ -277,8 +375,6 @@ private:
uint64_t frame_number { 0 }; uint64_t frame_number { 0 };
std::vector<std::shared_ptr<Mesh>> test_meshes;
AllocatedImage white_image {}; AllocatedImage white_image {};
AllocatedImage black_image {}; AllocatedImage black_image {};
AllocatedImage gray_image {}; AllocatedImage gray_image {};
@@ -288,10 +384,27 @@ private:
vk::UniqueSampler default_sampler_nearest; vk::UniqueSampler default_sampler_nearest;
} m_vk; } m_vk;
struct KmsState {
vk::DisplayKHR display {};
vk::DisplayModeKHR mode {};
vk::Extent2D extent {};
uint32_t plane_index { 0 };
uint32_t plane_stack_index { 0 };
std::string display_name {};
};
SDL_Window *m_window { nullptr }; SDL_Window *m_window { nullptr };
Logger &m_logger; Logger &m_logger;
std::mutex m_command_mutex; std::mutex m_command_mutex;
std::vector<RenderCommand> m_pending_render_commands; std::vector<RenderCommand> m_pending_render_commands;
bool m_use_kms { false };
bool m_imgui_enabled { true };
std::optional<KmsState> m_kms_state {};
vk::PhysicalDevice m_kms_physical_device {};
vk::Extent2D m_kms_extent {};
bool m_kms_physical_device_set { false };
std::vector<std::string> m_extra_instance_extensions {};
std::vector<std::string> m_extra_device_extensions {};
}; };
} // namespace Lunar } // namespace Lunar

View File

@@ -1,7 +1,3 @@
#include "src/Application.h" #include "src/Application.h"
auto main() -> int auto main() -> int { Lunar::Application::the().run(); }
{
Lunar::Application app {};
app.run();
}

113
src/wayland/Client.h Normal file
View File

@@ -0,0 +1,113 @@
#pragma once
#include <cassert>
#include <format>
#include <functional>
#include <optional>
#include <tuple>
#include <wayland-server-core.h>
#include "Display.h"
#include "List.h"
namespace Lunar::Wayland {
struct Client {
Client(wl_client *client)
: m_client { std::move(client) }
{
assert(m_client);
}
~Client() = default;
inline auto c_ptr() const -> wl_client * { return m_client; }
static auto from_link(wl_list *link) -> Client
{
return Client { wl_client_from_link(link) };
}
auto flush() { wl_client_flush(m_client); }
auto get_display() -> Display
{
return Display { wl_client_get_display(m_client) };
}
auto get_credentials() noexcept -> std::tuple<pid_t, uid_t, gid_t>
{
std::tuple<pid_t, uid_t, gid_t> ret {};
auto &[pid, uid, gid] { ret };
wl_client_get_credentials(m_client, &pid, &uid, &gid);
return ret;
}
auto get_fd() noexcept -> int { return wl_client_get_fd(m_client); }
auto get_object(uint32_t id) -> std::optional<wl_resource *>
{
if (auto *res { wl_client_get_object(m_client, id) }; res != NULL) {
return res;
} else {
return {};
}
}
auto post_implementation_error(std::string_view string)
{
wl_client_post_implementation_error(
m_client, "%.*s", static_cast<int>(string.size()), string.data());
}
template<typename... Args>
auto post_implementation_error(
std::format_string<Args...> fmt, Args &&...args)
{
post_implementation_error(
std::format(fmt, std::forward<Args>(args)...));
}
auto add_destroy_listener(wl_listener *listener)
{
wl_client_add_destroy_listener(m_client, listener);
}
auto add_destroy_late_listener(wl_listener *listener)
{
wl_client_add_destroy_late_listener(m_client, listener);
}
auto get_link() -> wl_list * { return wl_client_get_link(m_client); }
auto add_resource_created_listener(wl_listener *listener)
{
wl_client_add_resource_created_listener(m_client, listener);
}
auto for_each_resource(
std::function<wl_iterator_result(wl_resource *)> const &fn) -> void
{
wl_client_for_each_resource(
m_client,
(wl_client_for_each_resource_iterator_func_t)[](
wl_resource * res, void *user_data)
->wl_iterator_result {
auto *f { static_cast<
std::function<wl_iterator_result(wl_resource *)> *>(
user_data) };
return (*f)(res);
},
const_cast<void *>(static_cast<void const *>(&fn)));
}
auto set_max_buffer_size(size_t max_buffer_size)
{
wl_client_set_max_buffer_size(m_client, max_buffer_size);
}
private:
wl_client *m_client {};
};
} // namespace Lunar::Wayland

103
src/wayland/Display.h Normal file
View File

@@ -0,0 +1,103 @@
#pragma once
#include <cassert>
#include <format>
#include <stdexcept>
#include <vector>
#include <wayland-server-core.h>
namespace Lunar::Wayland {
struct Display {
Display()
: m_display(wl_display_create())
{
}
Display(wl_display *display)
: m_display { std::move(display) }
, m_should_cleanup { false }
{
}
~Display()
{
if (!m_should_cleanup)
return;
wl_display_destroy_clients(m_display);
wl_display_destroy(m_display);
}
inline auto c_ptr() const -> wl_display * { return m_display; }
auto set_global_filter(
wl_display_global_filter_func_t filter, void *data) noexcept
{
wl_display_set_global_filter(m_display, filter, data);
}
auto next_serial() noexcept -> uint32_t
{
return wl_display_next_serial(m_display);
}
auto set_default_max_buffer_size(size_t max_buffer_size) noexcept
{
wl_display_set_default_max_buffer_size(m_display, max_buffer_size);
}
auto add_socket_fd(int fd)
{
if (wl_display_add_socket_fd(m_display, fd) == -1) {
throw std::runtime_error(
"Failed to add socket fd to Wayland display");
}
}
auto add_socket(char const *name)
{
if (wl_display_add_socket(m_display, name) == -1) {
throw std::runtime_error(std::format(
"Failed to add socket `{}` to Wayland display", name));
}
}
auto add_protocol_logger(wl_protocol_logger_func_t func, void *user_data)
-> wl_protocol_logger *
{
if (auto *logger {
wl_display_add_protocol_logger(m_display, func, user_data) };
logger != NULL) {
return logger;
} else {
throw std::runtime_error(
"Failed to add protocol logger to Wayland display");
}
}
auto add_shm_format(uint32_t format) -> uint32_t *
{
if (auto *fmt { wl_display_add_shm_format(m_display, format) };
fmt != NULL) {
return fmt;
} else {
throw std::runtime_error(
"Failed to add SHM format to Wayland display");
}
}
auto get_client_list() -> std::vector<wl_client *>
{
std::vector<wl_client *> ret {};
auto const list { wl_display_get_client_list(m_display) };
assert(list);
wl_client *client {};
wl_client_for_each(client, list) { ret.push_back(client); }
return ret;
}
private:
wl_display *m_display {};
bool m_should_cleanup { true };
};
} // namespace Lunar::Wayland

71
src/wayland/Global.h Normal file
View File

@@ -0,0 +1,71 @@
#pragma once
#include <cassert>
#include <optional>
#include <utility>
#include <wayland-server-core.h>
#include "Client.h"
#include "Display.h"
namespace Lunar::Wayland {
struct Global {
Global() = delete;
explicit Global(Display &display, wl_interface const *interface,
int version, void *data, wl_global_bind_func_t bind)
: m_global {
wl_global_create(display.c_ptr(), interface, version, data, bind),
}
{
}
Global(wl_global *global)
: m_global { std::move(global) }
, m_should_cleanup { false }
{
assert(m_global);
}
~Global()
{
if (!m_should_cleanup)
return;
wl_global_destroy(m_global);
}
inline auto c_ptr() const -> wl_global * { return m_global; }
auto get_name(Client &client) const -> std::optional<uint32_t>
{
if (auto const ret { wl_global_get_name(m_global, client.c_ptr()) };
ret != 0) {
return ret;
} else {
return {};
}
}
inline auto get_version() const -> uint32_t
{
return wl_global_get_version(m_global);
}
inline auto get_display() const -> Display
{
return wl_global_get_display(m_global);
}
inline auto get_interface() const -> wl_interface const *
{
return wl_global_get_interface(m_global);
}
private:
wl_global *m_global {};
bool m_should_cleanup { true };
};
} // namespace Lunar::Wayland

287
src/wayland/List.h Normal file
View File

@@ -0,0 +1,287 @@
#pragma once
#include <cstddef>
#include <iterator>
#include <wayland-server-core.h>
namespace Lunar::Wayland {
namespace detail {
template<typename T, wl_list T::*Member>
constexpr std::ptrdiff_t member_offset() noexcept
{
return reinterpret_cast<std::ptrdiff_t>(
&(reinterpret_cast<T const volatile *>(0)->*Member));
}
template<typename T, wl_list T::*Member>
inline T *container_of(wl_list *node) noexcept
{
auto *p { reinterpret_cast<std::byte *>(node)
- member_offset<T, Member>() };
return reinterpret_cast<T *>(p);
}
template<typename T, wl_list T::*Member>
inline T const *container_of(wl_list const *node) noexcept
{
auto *p { reinterpret_cast<std::byte const *>(node)
- member_offset<T, Member>() };
return reinterpret_cast<T const *>(p);
}
} // namespace detail
template<typename T, wl_list T::*Member> struct List {
struct Iterator {
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T *;
using reference = T &;
Iterator() = default;
Iterator(wl_list *cur, wl_list *head)
: m_cur(cur)
, m_head(head)
{
}
auto operator*() const -> reference
{
return *detail::container_of<T, Member>(m_cur);
}
auto operator->() const -> pointer
{
return detail::container_of<T, Member>(m_cur);
}
auto operator++() -> Iterator &
{
m_cur = m_cur->next;
return *this;
}
auto operator++(int) -> Iterator
{
auto t { *this };
++(*this);
return t;
}
auto operator--() -> Iterator &
{
m_cur = (m_cur == m_head) ? m_head->prev : m_cur->prev;
return *this;
}
auto operator--(int) -> Iterator
{
auto t { *this };
--(*this);
return t;
}
friend auto operator==(Iterator a, Iterator b) -> bool
{
return a.m_cur == b.m_cur;
}
friend auto operator!=(Iterator a, Iterator b) -> bool
{
return !(a == b);
}
private:
wl_list *m_cur { nullptr };
wl_list *m_head { nullptr };
};
struct ConstIterator {
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T const;
using difference_type = std::ptrdiff_t;
using pointer = T const *;
using reference = T const &;
ConstIterator() = default;
ConstIterator(wl_list const *cur, wl_list const *head)
: m_cur(cur)
, m_head(head)
{
}
auto operator*() const -> reference
{
return *detail::container_of<T, Member>(m_cur);
}
auto operator->() const -> pointer
{
return detail::container_of<T, Member>(m_cur);
}
auto operator++() -> ConstIterator &
{
m_cur = m_cur->next;
return *this;
}
auto operator++(int) -> ConstIterator
{
auto t { *this };
++(*this);
return t;
}
auto operator--() -> ConstIterator &
{
m_cur = (m_cur == m_head) ? m_head->prev : m_cur->prev;
return *this;
}
auto operator--(int) -> ConstIterator
{
auto t { *this };
--(*this);
return t;
}
friend auto operator==(ConstIterator a, ConstIterator b) -> bool
{
return a.m_cur == b.m_cur;
}
friend auto operator!=(ConstIterator a, ConstIterator b) -> bool
{
return !(a == b);
}
private:
wl_list const *m_cur { nullptr };
wl_list const *m_head { nullptr };
};
List()
: m_head {}
, m_external_head { nullptr }
, m_should_cleanup { true }
{
wl_list_init(&m_head);
}
explicit List(
wl_list *existing_head, bool should_cleanup = false, bool init = false)
: m_head {}
, m_external_head { existing_head }
, m_should_cleanup { should_cleanup }
{
if (init && m_external_head)
wl_list_init(m_external_head);
}
~List()
{
if (!m_should_cleanup)
return;
clear();
if (auto *h { head_ptr() }; h)
wl_list_init(h);
}
List(List const &) = delete;
auto operator=(List const &) -> List & = delete;
List(List &&other) noexcept { move_from(other); }
auto operator=(List &&other) noexcept -> List &
{
if (this != &other) {
this->~List();
move_from(other);
}
return *this;
}
inline auto c_ptr() -> wl_list * { return head_ptr(); }
inline auto c_ptr() const -> wl_list const * { return head_ptr(); }
auto empty() const noexcept -> bool { return wl_list_empty(head_ptr()); }
auto length() const noexcept -> int { return wl_list_length(head_ptr()); }
auto push_front(T *elem) noexcept -> void
{
wl_list_insert(head_ptr(), &(elem->*Member));
}
auto push_back(T *elem) noexcept -> void
{
auto *h { head_ptr() };
wl_list_insert(h->prev, &(elem->*Member));
}
auto remove(T *elem) noexcept -> void
{
wl_list_remove(&(elem->*Member));
wl_list_init(&(elem->*Member));
}
auto clear() noexcept -> void
{
auto *h { head_ptr() };
while (!wl_list_empty(h)) {
auto *node { h->next };
wl_list_remove(node);
wl_list_init(node);
}
}
auto begin() noexcept -> Iterator
{
auto *h { head_ptr() };
return Iterator(h->next, h);
}
auto end() noexcept -> Iterator
{
auto *h { head_ptr() };
return Iterator(h, h);
}
auto begin() const noexcept -> ConstIterator
{
auto const *h { head_ptr() };
return ConstIterator(h->next, h);
}
auto end() const noexcept -> ConstIterator
{
auto const *h { head_ptr() };
return ConstIterator(h, h);
}
private:
auto head_ptr() noexcept -> wl_list *
{
return m_external_head ? m_external_head : &m_head;
}
auto head_ptr() const noexcept -> wl_list const *
{
return m_external_head ? m_external_head : &m_head;
}
auto move_from(List &other) noexcept -> void
{
m_head = other.m_head;
m_external_head = other.m_external_head;
m_should_cleanup = other.m_should_cleanup;
other.m_external_head = nullptr;
other.m_should_cleanup = false;
wl_list_init(&other.m_head);
}
private:
wl_list m_head {};
wl_list *m_external_head { nullptr };
bool m_should_cleanup { true };
};
} // namespace Lunar::Wayland

43
src/wayland/Region.h Normal file
View File

@@ -0,0 +1,43 @@
#pragma once
#include <cstdint>
#include <vector>
#include <wayland-server-core.h>
namespace Lunar::Wayland {
struct Region {
struct Box {
std::int32_t x {};
std::int32_t y {};
std::int32_t width {};
std::int32_t height {};
};
explicit Region(wl_resource *resource)
: m_resource(resource)
{
}
auto resource() const -> wl_resource * { return m_resource; }
auto add(std::int32_t x, std::int32_t y, std::int32_t width,
std::int32_t height) -> void
{
m_boxes.push_back(Box { x, y, width, height });
}
auto subtract(std::int32_t x, std::int32_t y, std::int32_t width,
std::int32_t height) -> void
{
m_subtract_boxes.push_back(Box { x, y, width, height });
}
private:
wl_resource *m_resource {};
std::vector<Box> m_boxes {};
std::vector<Box> m_subtract_boxes {};
};
} // namespace Lunar::Wayland

96
src/wayland/Shm.cpp Normal file
View File

@@ -0,0 +1,96 @@
#include "Shm.h"
#include <cerrno>
#include <cstring>
#include <sys/mman.h>
#include <unistd.h>
#include <utility>
namespace Lunar::Wayland {
ShmPool::ShmPool(int fd, std::size_t size, Logger &logger)
: m_logger(logger)
, m_fd(fd)
, m_size(size)
{
m_data = mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
if (m_data == MAP_FAILED) {
m_data = nullptr;
m_logger.err("Failed to mmap shm pool: {}", std::strerror(errno));
}
}
ShmPool::~ShmPool()
{
if (m_data) {
munmap(m_data, m_size);
}
if (m_fd >= 0) {
close(m_fd);
}
}
auto ShmPool::resize(std::size_t new_size) -> bool
{
if (!m_data) {
return false;
}
void *new_data = mremap(m_data, m_size, new_size, MREMAP_MAYMOVE);
if (new_data == MAP_FAILED) {
m_logger.err("Failed to resize shm pool: {}", std::strerror(errno));
return false;
}
m_data = new_data;
m_size = new_size;
return true;
}
auto ShmPool::data() const -> std::byte *
{
return static_cast<std::byte *>(m_data);
}
ShmBuffer::ShmBuffer(std::shared_ptr<ShmPool> pool, wl_resource *resource,
std::int32_t offset, std::int32_t width, std::int32_t height,
std::int32_t stride, std::uint32_t format)
: pool(std::move(pool))
, resource(resource)
, offset(offset)
, width(width)
, height(height)
, stride(stride)
, format(format)
{
}
auto ShmBuffer::data() const -> std::byte *
{
if (!pool || !pool->data()) {
return nullptr;
}
return pool->data() + offset;
}
auto ShmBuffer::byte_size() const -> std::size_t
{
if (height <= 0 || stride <= 0) {
return 0;
}
return static_cast<std::size_t>(height) * static_cast<std::size_t>(stride);
}
auto shm_buffer_from_resource(wl_resource *resource)
-> std::shared_ptr<ShmBuffer>
{
if (!resource) {
return {};
}
auto *handle { static_cast<std::shared_ptr<ShmBuffer> *>(
wl_resource_get_user_data(resource)) };
if (!handle) {
return {};
}
return *handle;
}
} // namespace Lunar::Wayland

48
src/wayland/Shm.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <memory>
#include <wayland-server-core.h>
#include "../Logger.h"
namespace Lunar::Wayland {
struct ShmPool {
explicit ShmPool(int fd, std::size_t size, Logger &logger);
~ShmPool();
auto resize(std::size_t new_size) -> bool;
auto data() const -> std::byte *;
auto size() const -> std::size_t { return m_size; }
private:
Logger &m_logger;
int m_fd { -1 };
std::size_t m_size { 0 };
void *m_data { nullptr };
};
struct ShmBuffer {
ShmBuffer(std::shared_ptr<ShmPool> pool, wl_resource *resource,
std::int32_t offset, std::int32_t width, std::int32_t height,
std::int32_t stride, std::uint32_t format);
auto data() const -> std::byte *;
auto byte_size() const -> std::size_t;
std::shared_ptr<ShmPool> pool;
wl_resource *resource {};
std::int32_t offset {};
std::int32_t width {};
std::int32_t height {};
std::int32_t stride {};
std::uint32_t format {};
};
auto shm_buffer_from_resource(wl_resource *resource)
-> std::shared_ptr<ShmBuffer>;
} // namespace Lunar::Wayland

28
src/wayland/Signal.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include <cassert>
#include <wayland-server-core.h>
namespace Lunar::Wayland {
struct Signal {
Signal(wl_signal *signal)
: m_signal { std::move(signal) }
{
assert(m_signal);
}
~Signal() = default;
inline auto c_ptr() const -> wl_signal * { return m_signal; }
template<typename T = void> auto flush(T *data)
{
wl_signal_emit_mutable(m_signal, (void *)data);
}
private:
wl_signal *m_signal {};
};
} // namespace Lunar::Wayland

98
src/wayland/Surface.cpp Normal file
View File

@@ -0,0 +1,98 @@
#include "Surface.h"
#include <algorithm>
#include <utility>
#include "WaylandServer.h"
#include "wayland-server-protocol.h"
namespace Lunar::Wayland {
Surface::Surface(WaylandServer &server, wl_resource *resource)
: m_server(server)
, m_resource(resource)
{
m_server.register_surface(this);
}
Surface::~Surface()
{
m_server.unregister_surface(this);
for (auto *callback : m_frame_callbacks) {
if (callback) {
wl_callback_send_done(callback, m_server.now_ms());
wl_resource_destroy(callback);
}
}
}
auto Surface::attach(
std::shared_ptr<ShmBuffer> buffer, std::int32_t x, std::int32_t y) -> void
{
m_pending_buffer = std::move(buffer);
m_pending_offset_x = x;
m_pending_offset_y = y;
}
auto Surface::damage(std::int32_t, std::int32_t, std::int32_t, std::int32_t)
-> void
{
}
auto Surface::damage_buffer(
std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
{
}
auto Surface::frame(wl_resource *callback) -> void
{
if (!callback) {
return;
}
m_frame_callbacks.push_back(callback);
}
auto Surface::commit() -> void
{
auto previous { m_current_buffer };
m_current_buffer = m_pending_buffer;
m_pending_buffer.reset();
if (previous && previous != m_current_buffer && previous->resource) {
wl_buffer_send_release(previous->resource);
}
if (!m_frame_callbacks.empty()) {
auto callbacks { std::move(m_frame_callbacks) };
auto done_time { m_server.now_ms() };
for (auto *callback : callbacks) {
if (callback) {
wl_callback_send_done(callback, done_time);
wl_resource_destroy(callback);
}
}
}
}
auto Surface::set_opaque_region(std::shared_ptr<Region> region) -> void
{
m_opaque_region = std::move(region);
}
auto Surface::set_input_region(std::shared_ptr<Region> region) -> void
{
m_input_region = std::move(region);
}
auto Surface::set_buffer_transform(std::int32_t transform) -> void
{
m_buffer_transform = transform;
}
auto Surface::set_buffer_scale(std::int32_t scale) -> void
{
m_buffer_scale = std::max(1, scale);
}
} // namespace Lunar::Wayland

53
src/wayland/Surface.h Normal file
View File

@@ -0,0 +1,53 @@
#pragma once
#include <cstdint>
#include <memory>
#include <vector>
#include <wayland-server-core.h>
#include "Region.h"
#include "Shm.h"
namespace Lunar::Wayland {
struct WaylandServer;
struct Surface {
explicit Surface(WaylandServer &server, wl_resource *resource);
~Surface();
auto resource() const -> wl_resource * { return m_resource; }
auto current_buffer() const -> std::shared_ptr<ShmBuffer> const &
{
return m_current_buffer;
}
auto attach(std::shared_ptr<ShmBuffer> buffer, std::int32_t x,
std::int32_t y) -> void;
auto damage(std::int32_t x, std::int32_t y, std::int32_t width,
std::int32_t height) -> void;
auto damage_buffer(std::int32_t x, std::int32_t y, std::int32_t width,
std::int32_t height) -> void;
auto frame(wl_resource *callback) -> void;
auto commit() -> void;
auto set_opaque_region(std::shared_ptr<Region> region) -> void;
auto set_input_region(std::shared_ptr<Region> region) -> void;
auto set_buffer_transform(std::int32_t transform) -> void;
auto set_buffer_scale(std::int32_t scale) -> void;
private:
WaylandServer &m_server;
wl_resource *m_resource {};
std::shared_ptr<ShmBuffer> m_pending_buffer {};
std::shared_ptr<ShmBuffer> m_current_buffer {};
std::shared_ptr<Region> m_opaque_region {};
std::shared_ptr<Region> m_input_region {};
std::vector<wl_resource *> m_frame_callbacks {};
std::int32_t m_buffer_transform { 0 };
std::int32_t m_buffer_scale { 1 };
std::int32_t m_pending_offset_x { 0 };
std::int32_t m_pending_offset_y { 0 };
};
} // namespace Lunar::Wayland

View File

@@ -0,0 +1,81 @@
#include "WaylandServer.h"
#include <algorithm>
#include <chrono>
#include <span>
#include <stdexcept>
namespace Lunar::Wayland {
WaylandServer::WaylandServer(Logger &logger)
: m_logger(logger)
{
m_loop = wl_display_get_event_loop(m_display.c_ptr());
if (!m_loop) {
throw std::runtime_error("Failed to get Wayland event loop");
}
auto *socket_name { wl_display_add_socket_auto(m_display.c_ptr()) };
if (!socket_name) {
throw std::runtime_error("Failed to create Wayland socket");
}
if (socket_name) {
m_socket_name = socket_name;
}
m_logger.info("Wayland listening on {}", m_socket_name);
m_compositor_global = create_compositor_global();
m_shm_global = create_shm_global();
m_xdg_wm_base_global = create_xdg_wm_base_global();
}
WaylandServer::~WaylandServer() = default;
auto WaylandServer::dispatch() -> void
{
if (!m_loop) {
return;
}
wl_event_loop_dispatch(m_loop, 0);
}
auto WaylandServer::flush() -> void
{
wl_display_flush_clients(m_display.c_ptr());
}
auto WaylandServer::now_ms() const -> std::uint32_t
{
using Clock = std::chrono::steady_clock;
auto now { Clock::now().time_since_epoch() };
auto ms {
std::chrono::duration_cast<std::chrono::milliseconds>(now).count()
};
return static_cast<std::uint32_t>(ms);
}
auto WaylandServer::register_surface(Surface *surface) -> void
{
if (!surface) {
return;
}
m_surfaces.push_back(surface);
}
auto WaylandServer::unregister_surface(Surface *surface) -> void
{
if (!surface) {
return;
}
auto it { std::remove(m_surfaces.begin(), m_surfaces.end(), surface) };
m_surfaces.erase(it, m_surfaces.end());
}
auto WaylandServer::surfaces() const -> std::span<Surface *const>
{
return { m_surfaces.data(), m_surfaces.size() };
}
} // namespace Lunar::Wayland
#include "Surface.h"

View File

@@ -0,0 +1,50 @@
#pragma once
#include <cstdint>
#include <memory>
#include <span>
#include <string>
#include <string_view>
#include <vector>
#include <wayland-server-core.h>
#include "../Logger.h"
#include "Display.h"
#include "Global.h"
namespace Lunar::Wayland {
struct Surface;
struct WaylandServer {
explicit WaylandServer(Logger &logger);
~WaylandServer();
auto display() -> Display & { return m_display; }
auto logger() -> Logger & { return m_logger; }
auto socket_name() const -> std::string_view { return m_socket_name; }
auto dispatch() -> void;
auto flush() -> void;
auto now_ms() const -> std::uint32_t;
auto register_surface(Surface *surface) -> void;
auto unregister_surface(Surface *surface) -> void;
auto surfaces() const -> std::span<Surface *const>;
private:
auto create_compositor_global() -> std::unique_ptr<Global>;
auto create_shm_global() -> std::unique_ptr<Global>;
auto create_xdg_wm_base_global() -> std::unique_ptr<Global>;
Logger &m_logger;
Display m_display {};
wl_event_loop *m_loop { nullptr };
std::string m_socket_name {};
std::unique_ptr<Global> m_compositor_global {};
std::unique_ptr<Global> m_shm_global {};
std::unique_ptr<Global> m_xdg_wm_base_global {};
std::vector<Surface *> m_surfaces {};
};
} // namespace Lunar::Wayland

View File

@@ -0,0 +1,254 @@
#include <algorithm>
#include <cstdint>
#include <memory>
#include <utility>
#include <wayland-server-core.h>
#include "wayland-server-protocol.h"
#include "../Region.h"
#include "../Shm.h"
#include "../Surface.h"
#include "../WaylandServer.h"
namespace Lunar::Wayland {
namespace {
constexpr std::uint32_t COMPOSITOR_VERSION = 4;
auto resource_version(wl_resource *resource) -> std::uint32_t
{
return std::min<std::uint32_t>(
wl_resource_get_version(resource), COMPOSITOR_VERSION);
}
auto region_from_resource(wl_resource *resource) -> std::shared_ptr<Region>
{
if (!resource) {
return {};
}
auto *handle { static_cast<std::shared_ptr<Region> *>(
wl_resource_get_user_data(resource)) };
if (!handle) {
return {};
}
return *handle;
}
void region_destroy(wl_resource *resource)
{
auto *handle { static_cast<std::shared_ptr<Region> *>(
wl_resource_get_user_data(resource)) };
delete handle;
}
void region_handle_destroy(wl_client *, wl_resource *resource)
{
wl_resource_destroy(resource);
}
void region_handle_add(wl_client *, wl_resource *resource, std::int32_t x,
std::int32_t y, std::int32_t width, std::int32_t height)
{
if (auto region { region_from_resource(resource) }) {
region->add(x, y, width, height);
}
}
void region_handle_subtract(wl_client *, wl_resource *resource, std::int32_t x,
std::int32_t y, std::int32_t width, std::int32_t height)
{
if (auto region { region_from_resource(resource) }) {
region->subtract(x, y, width, height);
}
}
struct wl_region_interface const REGION_INTERFACE = {
.destroy = region_handle_destroy,
.add = region_handle_add,
.subtract = region_handle_subtract,
};
auto surface_from_resource(wl_resource *resource) -> Surface *
{
return static_cast<Surface *>(wl_resource_get_user_data(resource));
}
void surface_destroy_resource(wl_resource *resource)
{
auto *surface { surface_from_resource(resource) };
delete surface;
}
void surface_handle_destroy(wl_client *, wl_resource *resource)
{
wl_resource_destroy(resource);
}
void surface_handle_attach(wl_client *, wl_resource *resource,
wl_resource *buffer_resource, std::int32_t x, std::int32_t y)
{
auto *surface { surface_from_resource(resource) };
if (!surface) {
return;
}
surface->attach(shm_buffer_from_resource(buffer_resource), x, y);
}
void surface_handle_damage(wl_client *, wl_resource *resource, std::int32_t x,
std::int32_t y, std::int32_t width, std::int32_t height)
{
if (auto *surface { surface_from_resource(resource) }) {
surface->damage(x, y, width, height);
}
}
void surface_handle_frame(
wl_client *client, wl_resource *resource, std::uint32_t callback_id)
{
auto *surface { surface_from_resource(resource) };
if (!surface) {
return;
}
auto version { wl_resource_get_version(resource) };
auto *callback_resource { wl_resource_create(
client, &wl_callback_interface, version, callback_id) };
if (!callback_resource) {
return;
}
wl_resource_set_implementation(
callback_resource, nullptr, nullptr, nullptr);
surface->frame(callback_resource);
}
void surface_handle_set_opaque_region(
wl_client *, wl_resource *resource, wl_resource *region_resource)
{
if (auto *surface { surface_from_resource(resource) }) {
surface->set_opaque_region(region_from_resource(region_resource));
}
}
void surface_handle_set_input_region(
wl_client *, wl_resource *resource, wl_resource *region_resource)
{
if (auto *surface { surface_from_resource(resource) }) {
surface->set_input_region(region_from_resource(region_resource));
}
}
void surface_handle_commit(wl_client *, wl_resource *resource)
{
if (auto *surface { surface_from_resource(resource) }) {
surface->commit();
}
}
void surface_handle_set_buffer_transform(
wl_client *, wl_resource *resource, std::int32_t transform)
{
if (auto *surface { surface_from_resource(resource) }) {
surface->set_buffer_transform(transform);
}
}
void surface_handle_set_buffer_scale(
wl_client *, wl_resource *resource, std::int32_t scale)
{
if (auto *surface { surface_from_resource(resource) }) {
surface->set_buffer_scale(scale);
}
}
void surface_handle_damage_buffer(wl_client *, wl_resource *resource,
std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
{
if (auto *surface { surface_from_resource(resource) }) {
surface->damage_buffer(x, y, width, height);
}
}
void surface_handle_offset(
wl_client *, wl_resource *resource, std::int32_t x, std::int32_t y)
{
(void)resource;
(void)x;
(void)y;
}
struct wl_surface_interface const SURFACE_INTERFACE = {
.destroy = surface_handle_destroy,
.attach = surface_handle_attach,
.damage = surface_handle_damage,
.frame = surface_handle_frame,
.set_opaque_region = surface_handle_set_opaque_region,
.set_input_region = surface_handle_set_input_region,
.commit = surface_handle_commit,
.set_buffer_transform = surface_handle_set_buffer_transform,
.set_buffer_scale = surface_handle_set_buffer_scale,
.damage_buffer = surface_handle_damage_buffer,
.offset = surface_handle_offset,
};
void compositor_handle_create_surface(
wl_client *client, wl_resource *resource, std::uint32_t id)
{
auto *server { static_cast<WaylandServer *>(
wl_resource_get_user_data(resource)) };
if (!server) {
return;
}
auto version { resource_version(resource) };
auto *surface_resource { wl_resource_create(
client, &wl_surface_interface, version, id) };
if (!surface_resource) {
return;
}
auto *surface { new Surface(*server, surface_resource) };
wl_resource_set_implementation(surface_resource, &SURFACE_INTERFACE,
surface, surface_destroy_resource);
}
void compositor_handle_create_region(
wl_client *client, wl_resource *resource, std::uint32_t id)
{
auto version { wl_resource_get_version(resource) };
auto *region_resource { wl_resource_create(
client, &wl_region_interface, version, id) };
if (!region_resource) {
return;
}
auto region { std::make_shared<Region>(region_resource) };
auto *handle { new std::shared_ptr<Region>(std::move(region)) };
wl_resource_set_implementation(
region_resource, &REGION_INTERFACE, handle, region_destroy);
}
struct wl_compositor_interface const COMPOSITOR_INTERFACE = {
.create_surface = compositor_handle_create_surface,
.create_region = compositor_handle_create_region,
};
void bind_compositor(
wl_client *client, void *data, std::uint32_t version, std::uint32_t id)
{
auto *server { static_cast<WaylandServer *>(data) };
auto *resource { wl_resource_create(client, &wl_compositor_interface,
std::min(version, COMPOSITOR_VERSION), id) };
if (!resource) {
return;
}
wl_resource_set_implementation(
resource, &COMPOSITOR_INTERFACE, server, nullptr);
}
} // namespace
auto WaylandServer::create_compositor_global() -> std::unique_ptr<Global>
{
return std::make_unique<Global>(display(), &wl_compositor_interface,
COMPOSITOR_VERSION, this, bind_compositor);
}
} // namespace Lunar::Wayland

View File

@@ -0,0 +1,171 @@
#include <algorithm>
#include <cstdint>
#include <memory>
#include <unistd.h>
#include <utility>
#include <wayland-server-core.h>
#include "wayland-server-protocol.h"
#include "../Shm.h"
#include "../WaylandServer.h"
namespace Lunar::Wayland {
namespace {
constexpr std::uint32_t SHM_VERSION = 2;
auto shm_pool_from_resource(wl_resource *resource) -> std::shared_ptr<ShmPool>
{
auto *handle { static_cast<std::shared_ptr<ShmPool> *>(
wl_resource_get_user_data(resource)) };
if (!handle) {
return {};
}
return *handle;
}
void shm_pool_destroy_resource(wl_resource *resource)
{
auto *handle { static_cast<std::shared_ptr<ShmPool> *>(
wl_resource_get_user_data(resource)) };
delete handle;
}
void shm_buffer_destroy_resource(wl_resource *resource)
{
auto *handle { static_cast<std::shared_ptr<ShmBuffer> *>(
wl_resource_get_user_data(resource)) };
delete handle;
}
void shm_pool_handle_destroy(wl_client *, wl_resource *resource)
{
wl_resource_destroy(resource);
}
void shm_pool_handle_resize(
wl_client *, wl_resource *resource, std::int32_t size)
{
auto pool { shm_pool_from_resource(resource) };
if (!pool) {
return;
}
if (size <= 0) {
return;
}
pool->resize(static_cast<std::size_t>(size));
}
void shm_handle_release(wl_client *, wl_resource *) { }
void shm_pool_handle_create_buffer(wl_client *client, wl_resource *resource,
std::uint32_t id, std::int32_t offset, std::int32_t width,
std::int32_t height, std::int32_t stride, std::uint32_t format)
{
auto pool { shm_pool_from_resource(resource) };
if (!pool) {
return;
}
if (width <= 0 || height <= 0 || stride <= 0 || offset < 0) {
wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE,
"Invalid shm buffer geometry");
return;
}
if (format != WL_SHM_FORMAT_XRGB8888 && format != WL_SHM_FORMAT_ARGB8888) {
wl_resource_post_error(
resource, WL_SHM_ERROR_INVALID_FORMAT, "Unsupported shm format");
return;
}
auto required { static_cast<std::size_t>(offset)
+ static_cast<std::size_t>(height) * static_cast<std::size_t>(stride) };
if (required > pool->size()) {
wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE,
"Shm buffer size out of bounds");
return;
}
auto *buffer_resource { wl_resource_create(
client, &wl_buffer_interface, 1, id) };
if (!buffer_resource) {
return;
}
auto buffer { std::make_shared<ShmBuffer>(
pool, buffer_resource, offset, width, height, stride, format) };
auto *handle { new std::shared_ptr<ShmBuffer>(std::move(buffer)) };
wl_resource_set_implementation(
buffer_resource, nullptr, handle, shm_buffer_destroy_resource);
}
struct wl_shm_pool_interface const SHM_POOL_INTERFACE = {
.create_buffer = shm_pool_handle_create_buffer,
.destroy = shm_pool_handle_destroy,
.resize = shm_pool_handle_resize,
};
void shm_handle_create_pool(wl_client *client, wl_resource *resource,
std::uint32_t id, int fd, std::int32_t size)
{
auto *server
= static_cast<WaylandServer *>(wl_resource_get_user_data(resource));
if (!server) {
return;
}
if (size <= 0) {
wl_resource_post_error(
resource, WL_SHM_ERROR_INVALID_STRIDE, "Invalid shm pool size");
close(fd);
return;
}
auto pool { std::make_shared<ShmPool>(
fd, static_cast<std::size_t>(size), server->logger()) };
if (!pool->data()) {
wl_resource_post_error(
resource, WL_SHM_ERROR_INVALID_FD, "Failed to mmap shm pool");
return;
}
auto *pool_resource
= wl_resource_create(client, &wl_shm_pool_interface, 1, id);
if (!pool_resource) {
return;
}
auto *handle { new std::shared_ptr<ShmPool>(std::move(pool)) };
wl_resource_set_implementation(
pool_resource, &SHM_POOL_INTERFACE, handle, shm_pool_destroy_resource);
}
struct wl_shm_interface const SHM_INTERFACE = {
.create_pool = shm_handle_create_pool,
.release = shm_handle_release,
};
void bind_shm(
wl_client *client, void *data, std::uint32_t version, std::uint32_t id)
{
auto *server { static_cast<WaylandServer *>(data) };
auto *resource { wl_resource_create(
client, &wl_shm_interface, std::min(version, SHM_VERSION), id) };
if (!resource) {
return;
}
wl_resource_set_implementation(resource, &SHM_INTERFACE, server, nullptr);
wl_shm_send_format(resource, WL_SHM_FORMAT_XRGB8888);
wl_shm_send_format(resource, WL_SHM_FORMAT_ARGB8888);
}
} // namespace
auto WaylandServer::create_shm_global() -> std::unique_ptr<Global>
{
return std::make_unique<Global>(
display(), &wl_shm_interface, SHM_VERSION, this, bind_shm);
}
} // namespace Lunar::Wayland

View File

@@ -0,0 +1,327 @@
#include <algorithm>
#include <cstdint>
#include <wayland-server-core.h>
#include "xdg-shell-server-protocol.h"
#include "../WaylandServer.h"
namespace Lunar::Wayland {
namespace {
constexpr std::uint32_t XDG_WM_BASE_VERSION = 7;
struct XdgSurface {
WaylandServer &server;
wl_resource *resource {};
wl_resource *surface_resource {};
wl_resource *toplevel_resource {};
std::uint32_t last_serial { 0 };
};
struct XdgToplevel {
WaylandServer &server;
wl_resource *resource {};
XdgSurface *surface {};
};
extern struct xdg_toplevel_interface const XDG_TOPLEVEL_INTERFACE_IMPL;
auto xdg_surface_from_resource(wl_resource *resource) -> XdgSurface *
{
return static_cast<XdgSurface *>(wl_resource_get_user_data(resource));
}
auto xdg_toplevel_from_resource(wl_resource *resource) -> XdgToplevel *
{
return static_cast<XdgToplevel *>(wl_resource_get_user_data(resource));
}
auto send_configure(XdgSurface &surface) -> void
{
std::uint32_t serial = surface.server.display().next_serial();
if (surface.toplevel_resource) {
wl_array states;
wl_array_init(&states);
xdg_toplevel_send_configure(surface.toplevel_resource, 0, 0, &states);
wl_array_release(&states);
}
surface.last_serial = serial;
xdg_surface_send_configure(surface.resource, serial);
}
auto xdg_positioner_handle_destroy(wl_client *, wl_resource *resource) -> void
{
wl_resource_destroy(resource);
}
auto xdg_positioner_handle_set_size(
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
{
}
auto xdg_positioner_handle_set_anchor_rect(wl_client *, wl_resource *,
std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
{
}
auto xdg_positioner_handle_set_anchor(wl_client *, wl_resource *, std::uint32_t)
-> void
{
}
auto xdg_positioner_handle_set_gravity(
wl_client *, wl_resource *, std::uint32_t) -> void
{
}
auto xdg_positioner_handle_set_constraint_adjustment(
wl_client *, wl_resource *, std::uint32_t) -> void
{
}
auto xdg_positioner_handle_set_offset(
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
{
}
auto xdg_positioner_handle_set_reactive(wl_client *, wl_resource *) -> void { }
auto xdg_positioner_handle_set_parent_size(
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
{
}
auto xdg_positioner_handle_set_parent_configure(
wl_client *, wl_resource *, std::uint32_t) -> void
{
}
struct xdg_positioner_interface const XDG_POSITIONER_INTERFACE_IMPL = {
.destroy = xdg_positioner_handle_destroy,
.set_size = xdg_positioner_handle_set_size,
.set_anchor_rect = xdg_positioner_handle_set_anchor_rect,
.set_anchor = xdg_positioner_handle_set_anchor,
.set_gravity = xdg_positioner_handle_set_gravity,
.set_constraint_adjustment
= xdg_positioner_handle_set_constraint_adjustment,
.set_offset = xdg_positioner_handle_set_offset,
.set_reactive = xdg_positioner_handle_set_reactive,
.set_parent_size = xdg_positioner_handle_set_parent_size,
.set_parent_configure = xdg_positioner_handle_set_parent_configure,
};
auto xdg_surface_destroy_resource(wl_resource *resource) -> void
{
auto *surface { xdg_surface_from_resource(resource) };
delete surface;
}
auto xdg_surface_handle_destroy(wl_client *, wl_resource *resource) -> void
{
wl_resource_destroy(resource);
}
auto xdg_surface_handle_get_toplevel(
wl_client *client, wl_resource *resource, std::uint32_t id) -> void
{
auto *surface { xdg_surface_from_resource(resource) };
if (!surface) {
return;
}
auto version { wl_resource_get_version(resource) };
auto *toplevel_resource { wl_resource_create(
client, &::xdg_toplevel_interface, version, id) };
if (!toplevel_resource) {
return;
}
auto *toplevel { new XdgToplevel {
surface->server, toplevel_resource, surface } };
surface->toplevel_resource = toplevel_resource;
wl_resource_set_implementation(toplevel_resource,
&XDG_TOPLEVEL_INTERFACE_IMPL, toplevel, [](wl_resource *res) {
auto *tl { xdg_toplevel_from_resource(res) };
if (tl && tl->surface && tl->surface->toplevel_resource == res) {
tl->surface->toplevel_resource = nullptr;
}
delete tl;
});
send_configure(*surface);
}
auto xdg_surface_handle_get_popup(wl_client *, wl_resource *, std::uint32_t,
wl_resource *, wl_resource *) -> void
{
}
auto xdg_surface_handle_set_window_geometry(wl_client *, wl_resource *,
std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
{
}
auto xdg_surface_handle_ack_configure(
wl_client *, wl_resource *resource, std::uint32_t serial) -> void
{
if (auto *surface { xdg_surface_from_resource(resource) }) {
surface->last_serial = serial;
}
}
struct xdg_surface_interface const XDG_SURFACE_INTERFACE_IMPL = {
.destroy = xdg_surface_handle_destroy,
.get_toplevel = xdg_surface_handle_get_toplevel,
.get_popup = xdg_surface_handle_get_popup,
.set_window_geometry = xdg_surface_handle_set_window_geometry,
.ack_configure = xdg_surface_handle_ack_configure,
};
auto xdg_toplevel_handle_destroy(wl_client *, wl_resource *resource) -> void
{
wl_resource_destroy(resource);
}
auto xdg_toplevel_handle_set_parent(wl_client *, wl_resource *, wl_resource *)
-> void
{
}
auto xdg_toplevel_handle_set_title(wl_client *, wl_resource *, char const *)
-> void
{
}
auto xdg_toplevel_handle_set_app_id(wl_client *, wl_resource *, char const *)
-> void
{
}
auto xdg_toplevel_handle_show_window_menu(wl_client *, wl_resource *,
wl_resource *, std::uint32_t, std::int32_t, std::int32_t) -> void
{
}
auto xdg_toplevel_handle_move(
wl_client *, wl_resource *, wl_resource *, std::uint32_t) -> void
{
}
auto xdg_toplevel_handle_resize(wl_client *, wl_resource *, wl_resource *,
std::uint32_t, std::uint32_t) -> void
{
}
auto xdg_toplevel_handle_set_max_size(
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
{
}
auto xdg_toplevel_handle_set_min_size(
wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
{
}
auto xdg_toplevel_handle_set_maximized(wl_client *, wl_resource *) -> void { }
auto xdg_toplevel_handle_unset_maximized(wl_client *, wl_resource *) -> void { }
auto xdg_toplevel_handle_set_fullscreen(
wl_client *, wl_resource *, wl_resource *) -> void
{
}
auto xdg_toplevel_handle_unset_fullscreen(wl_client *, wl_resource *) -> void {
}
auto xdg_toplevel_handle_set_minimized(wl_client *, wl_resource *) -> void { }
struct xdg_toplevel_interface const XDG_TOPLEVEL_INTERFACE_IMPL = {
.destroy = xdg_toplevel_handle_destroy,
.set_parent = xdg_toplevel_handle_set_parent,
.set_title = xdg_toplevel_handle_set_title,
.set_app_id = xdg_toplevel_handle_set_app_id,
.show_window_menu = xdg_toplevel_handle_show_window_menu,
.move = xdg_toplevel_handle_move,
.resize = xdg_toplevel_handle_resize,
.set_max_size = xdg_toplevel_handle_set_max_size,
.set_min_size = xdg_toplevel_handle_set_min_size,
.set_maximized = xdg_toplevel_handle_set_maximized,
.unset_maximized = xdg_toplevel_handle_unset_maximized,
.set_fullscreen = xdg_toplevel_handle_set_fullscreen,
.unset_fullscreen = xdg_toplevel_handle_unset_fullscreen,
.set_minimized = xdg_toplevel_handle_set_minimized,
};
auto xdg_wm_base_handle_destroy(wl_client *, wl_resource *resource) -> void
{
wl_resource_destroy(resource);
}
auto xdg_wm_base_handle_create_positioner(
wl_client *client, wl_resource *resource, std::uint32_t id) -> void
{
auto version { wl_resource_get_version(resource) };
auto *positioner { wl_resource_create(
client, &::xdg_positioner_interface, version, id) };
if (!positioner) {
return;
}
wl_resource_set_implementation(
positioner, &XDG_POSITIONER_INTERFACE_IMPL, nullptr, nullptr);
}
auto xdg_wm_base_handle_get_xdg_surface(wl_client *client,
wl_resource *resource, std::uint32_t id, wl_resource *surface_resource)
-> void
{
auto *server
= static_cast<WaylandServer *>(wl_resource_get_user_data(resource));
if (!server) {
return;
}
auto version { wl_resource_get_version(resource) };
auto *xdg_surface_resource { wl_resource_create(
client, &::xdg_surface_interface, version, id) };
if (!xdg_surface_resource) {
return;
}
auto *surface { new XdgSurface {
*server, xdg_surface_resource, surface_resource, nullptr, 0 } };
wl_resource_set_implementation(xdg_surface_resource,
&XDG_SURFACE_INTERFACE_IMPL, surface, xdg_surface_destroy_resource);
}
auto xdg_wm_base_handle_pong(wl_client *, wl_resource *, std::uint32_t) -> void
{
}
struct xdg_wm_base_interface const XDG_WM_BASE_INTERFACE_IMPL = {
.destroy = xdg_wm_base_handle_destroy,
.create_positioner = xdg_wm_base_handle_create_positioner,
.get_xdg_surface = xdg_wm_base_handle_get_xdg_surface,
.pong = xdg_wm_base_handle_pong,
};
auto bind_xdg_wm_base(wl_client *client, void *data, std::uint32_t version,
std::uint32_t id) -> void
{
auto *server { static_cast<WaylandServer *>(data) };
auto *resource { wl_resource_create(client, &::xdg_wm_base_interface,
std::min(version, XDG_WM_BASE_VERSION), id) };
if (!resource) {
return;
}
wl_resource_set_implementation(
resource, &XDG_WM_BASE_INTERFACE_IMPL, server, nullptr);
}
} // namespace
auto WaylandServer::create_xdg_wm_base_global() -> std::unique_ptr<Global>
{
return std::make_unique<Global>(display(), &::xdg_wm_base_interface,
XDG_WM_BASE_VERSION, this, bind_xdg_wm_base);
}
} // namespace Lunar::Wayland

32
thirdparty/stb/.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,32 @@
Pull Requests and Issues are both welcome.
# Responsiveness
General priority order is:
* Crashes
* Security issues in stb_image
* Bugs
* Security concerns in other libs
* Warnings
* Enhancements (new features, performance improvement, etc)
Pull requests get priority over Issues. Some pull requests I take
as written; some I modify myself; some I will request changes before
accepting them. Because I've ended up supporting a lot of libraries
(20 as I write this, with more on the way), I am somewhat slow to
address things. Many issues have been around for a long time.
# Pull requests
* Make sure you're using a special branch just for this pull request. (Sometimes people unknowingly use a default branch, then later update that branch, which updates the pull request with the other changes if it hasn't been merged yet.)
* Do NOT update the version number in the file. (This just causes conflicts.)
* Do add your name to the list of contributors. (Don't worry about the formatting.) I'll try to remember to add it if you don't, but I sometimes forget as it's an extra step.
* Your change needs to compile as both C and C++. Pre-C99 compilers should be supported (e.g. declare at start of block)
# Specific libraries
I generally do not want new file formats for stb_image because
we are trying to improve its security, so increasing its attack
surface is counter-productive.

View File

@@ -0,0 +1,15 @@
---
name: stb_image Doesn't Load Specific Image Correctly
about: if an image displays wrong in your program, and you've verified stb_image is
the problem
title: ''
labels: 1 stb_image
assignees: ''
---
1. **Confirm that, after loading the image with stbi_load, you've immediately written it out with stbi_write_png or similar, and that version of the image is also wrong.** If it is correct when written out, the problem is not in stb_image. If it displays wrong in a program you're writing, it's probably your display code. For example, people writing OpenGL programs frequently do not upload or display the image correctly and assume stb_image is at fault even though writing out the image demonstrates that it loads correctly.
2. *Provide an image that does not load correctly using stb_image* so we can reproduce the problem.
3. *Provide an image or description of what part of the image is incorrect and how* so we can be sure we've reproduced the problem correctly.

View File

@@ -0,0 +1,24 @@
---
name: Bug report
about: if you're having trouble using a library, try the support forum instead
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 4 enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: support forum
url: https://github.com/nothings/stb/discussions/categories/q-a
about: having trouble using an stb library? don't create an issue, post in the forum

View File

@@ -0,0 +1,6 @@
* Delete this list before clicking CREATE PULL REQUEST
* Make sure you're using a special branch just for this pull request. (Sometimes people unknowingly use a default branch, then later update that branch, which updates the pull request with the other changes if it hasn't been merged yet.)
* Do NOT update the version number in the file. (This just causes conflicts.)
* Do add your name to the list of contributors. (Don't worry about the formatting.) I'll try to remember to add it if you don't, but I sometimes forget as it's an extra step.
If you get something above wrong, don't fret it, it's not the end of the world.

View File

@@ -0,0 +1,23 @@
name: CIFuzz
on: [pull_request]
jobs:
Fuzzing:
runs-on: ubuntu-latest
steps:
- name: Build Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'stb'
dry-run: false
- name: Run Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'stb'
fuzz-seconds: 900
dry-run: false
- name: Upload Crash
uses: actions/upload-artifact@v1
if: failure()
with:
name: artifacts
path: ./out/artifacts

3
thirdparty/stb/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.o
*.obj
*.exe

8
thirdparty/stb/.travis.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
language: C
arch:
- AMD64
- ppc64le
install: true
script:
- cd tests
- make all

37
thirdparty/stb/LICENSE vendored Normal file
View File

@@ -0,0 +1,37 @@
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

184
thirdparty/stb/README.md vendored Normal file
View File

@@ -0,0 +1,184 @@
<!--- THIS FILE IS AUTOMATICALLY GENERATED, DO NOT CHANGE IT BY HAND --->
stb
===
single-file public domain (or MIT licensed) libraries for C/C++
# This project discusses security-relevant bugs in public in Github Issues and Pull Requests, and it may take significant time for security fixes to be implemented or merged. If this poses an unreasonable risk to your project, do not use stb libraries.
Noteworthy:
* image loader: [stb_image.h](stb_image.h)
* image writer: [stb_image_write.h](stb_image_write.h)
* image resizer: [stb_image_resize2.h](stb_image_resize2.h)
* font text rasterizer: [stb_truetype.h](stb_truetype.h)
* typesafe containers: [stb_ds.h](stb_ds.h)
Most libraries by stb, except: stb_dxt by Fabian "ryg" Giesen, original stb_image_resize
by Jorge L. "VinoBS" Rodriguez, and stb_image_resize2 and stb_sprintf by Jeff Roberts.
<a name="stb_libs"></a>
library | latest version | category | LoC | description
--------------------- | ---- | -------- | --- | --------------------------------
**[stb_vorbis.c](stb_vorbis.c)** | 1.22 | audio | 5584 | decode ogg vorbis files from file/memory to float/16-bit signed output
**[stb_hexwave.h](stb_hexwave.h)** | 0.5 | audio | 680 | audio waveform synthesizer
**[stb_image.h](stb_image.h)** | 2.30 | graphics | 7988 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
**[stb_truetype.h](stb_truetype.h)** | 1.26 | graphics | 5079 | parse, decode, and rasterize characters from truetype fonts
**[stb_image_write.h](stb_image_write.h)** | 1.16 | graphics | 1724 | image writing to disk: PNG, TGA, BMP
**[stb_image_resize2.h](stb_image_resize2.h)** | 2.16 | graphics | 10650 | resize images larger/smaller with good quality
**[stb_rect_pack.h](stb_rect_pack.h)** | 1.01 | graphics | 623 | simple 2D rectangle packer with decent quality
**[stb_perlin.h](stb_perlin.h)** | 0.5 | graphics | 428 | perlin's revised simplex noise w/ different seeds
**[stb_ds.h](stb_ds.h)** | 0.67 | utility | 1895 | typesafe dynamic array and hash tables for C, will compile in C++
**[stb_sprintf.h](stb_sprintf.h)** | 1.10 | utility | 1906 | fast sprintf, snprintf for C/C++
**[stb_textedit.h](stb_textedit.h)** | 1.14 | user&nbsp;interface | 1429 | guts of a text editor for games etc implementing them from scratch
**[stb_voxel_render.h](stb_voxel_render.h)** | 0.89 | 3D&nbsp;graphics | 3807 | Minecraft-esque voxel rendering "engine" with many more features
**[stb_dxt.h](stb_dxt.h)** | 1.12 | 3D&nbsp;graphics | 719 | Fabian "ryg" Giesen's real-time DXT compressor
**[stb_easy_font.h](stb_easy_font.h)** | 1.1 | 3D&nbsp;graphics | 305 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc
**[stb_tilemap_editor.h](stb_tilemap_editor.h)** | 0.42 | game&nbsp;dev | 4187 | embeddable tilemap editor
**[stb_herringbone_wa...](stb_herringbone_wang_tile.h)** | 0.7 | game&nbsp;dev | 1221 | herringbone Wang tile map generator
**[stb_c_lexer.h](stb_c_lexer.h)** | 0.12 | parsing | 941 | simplify writing parsers for C-like languages
**[stb_divide.h](stb_divide.h)** | 0.94 | math | 433 | more useful 32-bit modulus e.g. "euclidean divide"
**[stb_connected_comp...](stb_connected_components.h)** | 0.96 | misc | 1049 | incrementally compute reachability on grids
**[stb_leakcheck.h](stb_leakcheck.h)** | 0.6 | misc | 194 | quick-and-dirty malloc/free leak-checking
**[stb_include.h](stb_include.h)** | 0.02 | misc | 295 | implement recursive #include support, particularly for GLSL
Total libraries: 21
Total lines of C code: 51137
FAQ
---
#### What's the license?
These libraries are in the public domain. You can do anything you
want with them. You have no legal obligation
to do anything else, although I appreciate attribution.
They are also licensed under the MIT open source license, if you have lawyers
who are unhappy with public domain. Every source file includes an explicit
dual-license for you to choose from.
#### How do I use these libraries?
The idea behind single-header file libraries is that they're easy to distribute and deploy
because all the code is contained in a single file. By default, the .h files in here act as
their own header files, i.e. they declare the functions contained in the file but don't
actually result in any code getting compiled.
So in addition, you should select _exactly one_ C/C++ source file that actually instantiates
the code, preferably a file you're not editing frequently. This file should define a
specific macro (this is documented per-library) to actually enable the function definitions.
For example, to use stb_image, you should have exactly one C/C++ file that doesn't
include stb_image.h regularly, but instead does
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
The right macro to define is pointed out right at the top of each of these libraries.
#### <a name="other_libs"></a> Are there other single-file public-domain/open source libraries with minimal dependencies out there?
[Yes.](https://github.com/nothings/single_file_libs)
#### If I wrap an stb library in a new library, does the new library have to be public domain/MIT?
No, because it's public domain you can freely relicense it to whatever license your new
library wants to be.
#### What's the deal with SSE support in GCC-based compilers?
stb_image will either use SSE2 (if you compile with -msse2) or
will not use any SIMD at all, rather than trying to detect the
processor at runtime and handle it correctly. As I understand it,
the approved path in GCC for runtime-detection require
you to use multiple source files, one for each CPU configuration.
Because stb_image is a header-file library that compiles in only
one source file, there's no approved way to build both an
SSE-enabled and a non-SSE-enabled variation.
While we've tried to work around it, we've had multiple issues over
the years due to specific versions of gcc breaking what we're doing,
so we've given up on it. See https://github.com/nothings/stb/issues/280
and https://github.com/nothings/stb/issues/410 for examples.
#### Some of these libraries seem redundant to existing open source libraries. Are they better somehow?
Generally they're only better in that they're easier to integrate,
easier to use, and easier to release (single file; good API; no
attribution requirement). They may be less featureful, slower,
and/or use more memory. If you're already using an equivalent
library, there's probably no good reason to switch.
#### Can I link directly to the table of stb libraries?
You can use [this URL](https://github.com/nothings/stb#stb_libs) to link directly to that list.
#### Why do you list "lines of code"? It's a terrible metric.
Just to give you some idea of the internal complexity of the library,
to help you manage your expectations, or to let you know what you're
getting into. While not all the libraries are written in the same
style, they're certainly similar styles, and so comparisons between
the libraries are probably still meaningful.
Note though that the lines do include both the implementation, the
part that corresponds to a header file, and the documentation.
#### Why single-file headers?
Windows doesn't have standard directories where libraries
live. That makes deploying libraries in Windows a lot more
painful than open source developers on Unix-derivates generally
realize. (It also makes library dependencies a lot worse in Windows.)
There's also a common problem in Windows where a library was built
against a different version of the runtime library, which causes
link conflicts and confusion. Shipping the libs as headers means
you normally just compile them straight into your project without
making libraries, thus sidestepping that problem.
Making them a single file makes it very easy to just
drop them into a project that needs them. (Of course you can
still put them in a proper shared library tree if you want.)
Why not two files, one a header and one an implementation?
The difference between 10 files and 9 files is not a big deal,
but the difference between 2 files and 1 file is a big deal.
You don't need to zip or tar the files up, you don't have to
remember to attach *two* files, etc.
#### Why "stb"? Is this something to do with Set-Top Boxes?
No, they are just the initials for my name, Sean T. Barrett.
This was not chosen out of egomania, but as a moderately sane
way of namespacing the filenames and source function names.
#### Will you add more image types to stb_image.h?
No. As stb_image use has grown, it has become more important
for us to focus on security of the codebase. Adding new image
formats increases the amount of code we need to secure, so it
is no longer worth adding new formats.
#### Do you have any advice on how to create my own single-file library?
Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
#### Why public domain?
I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons.
Some of them are listed here:
https://github.com/nothings/stb/blob/master/docs/why_public_domain.md
#### Why C?
Primarily, because I use C, not C++. But it does also make it easier
for other people to use them from other languages.
#### Why not C99? stdint.h, declare-anywhere, etc.
I still use MSVC 6 (1998) as my IDE because it has better human factors
for me than later versions of MSVC.

2
thirdparty/stb/SECURITY.md vendored Normal file
View File

@@ -0,0 +1,2 @@
# Security Policy
This project discusses security-relevant bugs in public in Github Issues and Pull Requests, and it may take significant time for security fixes to be implemented or merged. If this poses an unreasonable risk to your project, do not use stb libraries.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
thirdparty/stb/data/easy_font_raw.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

View File

@@ -0,0 +1,4 @@
All files in this directory are in the public domain. Where
a public domain declaration is not recognized, you are granted
a license to freely use, modify, and redistribute them in
any way you choose.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
thirdparty/stb/data/map_01.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
thirdparty/stb/data/map_02.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
thirdparty/stb/data/map_03.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

1055
thirdparty/stb/deprecated/rrsprintf.h vendored Normal file

File diff suppressed because it is too large Load Diff

13111
thirdparty/stb/deprecated/stb.h vendored Normal file

File diff suppressed because it is too large Load Diff

4678
thirdparty/stb/deprecated/stb_image.c vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
// check that stb_truetype compiles with no stb_rect_pack.h
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
#define STB_DS_IMPLEMENTATION
#include "stb_ds.h"
#include <assert.h>
int main(int arg, char **argv)
{
int i;
int *arr = NULL;
for (i=0; i < 1000000; ++i)
arrput(arr, i);
assert(arrlen(arr) == 1000000);
for (i=0; i < 1000000; ++i)
assert(arr[i] == i);
arrfree(arr);
arr = NULL;
for (i=0; i < 1000; ++i)
arrput(arr, 1000);
assert(arrlen(arr) == 1000000);
return 0;
}

View File

@@ -0,0 +1,263 @@
// stretchy_buffer.h - v1.04 - public domain - nothings.org/stb
// a vector<>-like dynamic array for C
//
// version history:
// 1.04 - fix warning
// 1.03 - compile as C++ maybe
// 1.02 - tweaks to syntax for no good reason
// 1.01 - added a "common uses" documentation section
// 1.0 - fixed bug in the version I posted prematurely
// 0.9 - rewrite to try to avoid strict-aliasing optimization
// issues, but won't compile as C++
//
// Will probably not work correctly with strict-aliasing optimizations.
//
// The idea:
//
// This implements an approximation to C++ vector<> for C, in that it
// provides a generic definition for dynamic arrays which you can
// still access in a typesafe way using arr[i] or *(arr+i). However,
// it is simply a convenience wrapper around the common idiom of
// of keeping a set of variables (in a struct or globals) which store
// - pointer to array
// - the length of the "in-use" part of the array
// - the current size of the allocated array
//
// I find it to be the single most useful non-built-in-structure when
// programming in C (hash tables a close second), but to be clear
// it lacks many of the capabilities of C++ vector<>: there is no
// range checking, the object address isn't stable (see next section
// for details), the set of methods available is small (although
// the file stb.h has another implementation of stretchy buffers
// called 'stb_arr' which provides more methods, e.g. for insertion
// and deletion).
//
// How to use:
//
// Unlike other stb header file libraries, there is no need to
// define an _IMPLEMENTATION symbol. Every #include creates as
// much implementation is needed.
//
// stretchy_buffer.h does not define any types, so you do not
// need to #include it to before defining data types that are
// stretchy buffers, only in files that *manipulate* stretchy
// buffers.
//
// If you want a stretchy buffer aka dynamic array containing
// objects of TYPE, declare such an array as:
//
// TYPE *myarray = NULL;
//
// (There is no typesafe way to distinguish between stretchy
// buffers and regular arrays/pointers; this is necessary to
// make ordinary array indexing work on these objects.)
//
// Unlike C++ vector<>, the stretchy_buffer has the same
// semantics as an object that you manually malloc and realloc.
// The pointer may relocate every time you add a new object
// to it, so you:
//
// 1. can't take long-term pointers to elements of the array
// 2. have to return the pointer from functions which might expand it
// (either as a return value or by storing it to a ptr-to-ptr)
//
// Now you can do the following things with this array:
//
// sb_free(TYPE *a) free the array
// sb_count(TYPE *a) the number of elements in the array
// sb_push(TYPE *a, TYPE v) adds v on the end of the array, a la push_back
// sb_add(TYPE *a, int n) adds n uninitialized elements at end of array & returns pointer to first added
// sb_last(TYPE *a) returns an lvalue of the last item in the array
// a[n] access the nth (counting from 0) element of the array
//
// #define STRETCHY_BUFFER_NO_SHORT_NAMES to only export
// names of the form 'stb_sb_' if you have a name that would
// otherwise collide.
//
// Note that these are all macros and many of them evaluate
// their arguments more than once, so the arguments should
// be side-effect-free.
//
// Note that 'TYPE *a' in sb_push and sb_add must be lvalues
// so that the library can overwrite the existing pointer if
// the object has to be reallocated.
//
// In an out-of-memory condition, the code will try to
// set up a null-pointer or otherwise-invalid-pointer
// exception to happen later. It's possible optimizing
// compilers could detect this write-to-null statically
// and optimize away some of the code, but it should only
// be along the failure path. Nevertheless, for more security
// in the face of such compilers, #define STRETCHY_BUFFER_OUT_OF_MEMORY
// to a statement such as assert(0) or exit(1) or something
// to force a failure when out-of-memory occurs.
//
// Common use:
//
// The main application for this is when building a list of
// things with an unknown quantity, either due to loading from
// a file or through a process which produces an unpredictable
// number.
//
// My most common idiom is something like:
//
// SomeStruct *arr = NULL;
// while (something)
// {
// SomeStruct new_one;
// new_one.whatever = whatever;
// new_one.whatup = whatup;
// new_one.foobar = barfoo;
// sb_push(arr, new_one);
// }
//
// and various closely-related factorings of that. For example,
// you might have several functions to create/init new SomeStructs,
// and if you use the above idiom, you might prefer to make them
// return structs rather than take non-const-pointers-to-structs,
// so you can do things like:
//
// SomeStruct *arr = NULL;
// while (something)
// {
// if (case_A) {
// sb_push(arr, some_func1());
// } else if (case_B) {
// sb_push(arr, some_func2());
// } else {
// sb_push(arr, some_func3());
// }
// }
//
// Note that the above relies on the fact that sb_push doesn't
// evaluate its second argument more than once. The macros do
// evaluate the *array* argument multiple times, and numeric
// arguments may be evaluated multiple times, but you can rely
// on the second argument of sb_push being evaluated only once.
//
// Of course, you don't have to store bare objects in the array;
// if you need the objects to have stable pointers, store an array
// of pointers instead:
//
// SomeStruct **arr = NULL;
// while (something)
// {
// SomeStruct *new_one = malloc(sizeof(*new_one));
// new_one->whatever = whatever;
// new_one->whatup = whatup;
// new_one->foobar = barfoo;
// sb_push(arr, new_one);
// }
//
// How it works:
//
// A long-standing tradition in things like malloc implementations
// is to store extra data before the beginning of the block returned
// to the user. The stretchy buffer implementation here uses the
// same trick; the current-count and current-allocation-size are
// stored before the beginning of the array returned to the user.
// (This means you can't directly free() the pointer, because the
// allocated pointer is different from the type-safe pointer provided
// to the user.)
//
// The details are trivial and implementation is straightforward;
// the main trick is in realizing in the first place that it's
// possible to do this in a generic, type-safe way in C.
//
// Contributors:
//
// Timothy Wright (github:ZenToad)
//
// LICENSE
//
// See end of file for license information.
#ifndef STB_STRETCHY_BUFFER_H_INCLUDED
#define STB_STRETCHY_BUFFER_H_INCLUDED
#ifndef NO_STRETCHY_BUFFER_SHORT_NAMES
#define sb_free stb_sb_free
#define sb_push stb_sb_push
#define sb_count stb_sb_count
#define sb_add stb_sb_add
#define sb_last stb_sb_last
#endif
#define stb_sb_free(a) ((a) ? free(stb__sbraw(a)),0 : 0)
#define stb_sb_push(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v))
#define stb_sb_count(a) ((a) ? stb__sbn(a) : 0)
#define stb_sb_add(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)])
#define stb_sb_last(a) ((a)[stb__sbn(a)-1])
#define stb__sbraw(a) ((int *) (void *) (a) - 2)
#define stb__sbm(a) stb__sbraw(a)[0]
#define stb__sbn(a) stb__sbraw(a)[1]
#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a))
#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
#define stb__sbgrow(a,n) (*((void **)&(a)) = stb__sbgrowf((a), (n), sizeof(*(a))))
#include <stdlib.h>
static void * stb__sbgrowf(void *arr, int increment, int itemsize)
{
int dbl_cur = arr ? 2*stb__sbm(arr) : 0;
int min_needed = stb_sb_count(arr) + increment;
int m = dbl_cur > min_needed ? dbl_cur : min_needed;
int *p = (int *) realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2);
if (p) {
if (!arr)
p[1] = 0;
p[0] = m;
return p+2;
} else {
#ifdef STRETCHY_BUFFER_OUT_OF_MEMORY
STRETCHY_BUFFER_OUT_OF_MEMORY ;
#endif
return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later
}
}
#endif // STB_STRETCHY_BUFFER_H_INCLUDED
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

View File

@@ -0,0 +1,28 @@
// stretchy buffer // init: NULL // free: sbfree() // push_back: sbpush() // size: sbcount() //
#define sbfree(a) ((a) ? free(stb__sbraw(a)),0 : 0)
#define sbpush(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v))
#define sbcount(a) ((a) ? stb__sbn(a) : 0)
#define sbadd(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)])
#define sblast(a) ((a)[stb__sbn(a)-1])
#include <stdlib.h>
#define stb__sbraw(a) ((int *) (a) - 2)
#define stb__sbm(a) stb__sbraw(a)[0]
#define stb__sbn(a) stb__sbraw(a)[1]
#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+n >= stb__sbm(a))
#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
#define stb__sbgrow(a,n) stb__sbgrowf((void **) &(a), (n), sizeof(*(a)))
static void stb__sbgrowf(void **arr, int increment, int itemsize)
{
int m = *arr ? 2*stb__sbm(*arr)+increment : increment+1;
void *p = realloc(*arr ? stb__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
assert(p);
if (p) {
if (!*arr) ((int *) p)[1] = 0;
*arr = (void *) ((int *) p + 2);
stb__sbm(*arr) = m;
}
}

1
thirdparty/stb/docs/other_libs.md vendored Normal file
View File

@@ -0,0 +1 @@
Moved to https://github.com/nothings/single_file_libs

185
thirdparty/stb/docs/stb_howto.txt vendored Normal file
View File

@@ -0,0 +1,185 @@
Lessons learned about how to make a header-file library
V1.0
September 2013 Sean Barrett
Things to do in an stb-style header-file library,
and rationales:
1. #define LIBRARYNAME_IMPLEMENTATION
Use a symbol like the above to control creating
the implementation. (I used a far-less-clear name
in my first header-file library; it became
clear that was a mistake once I had multiple
libraries.)
Include a "header-file" section with header-file
guards and declarations for all the functions,
but only guard the implementation with LIBRARYNAME_IMPLEMENTATION,
not the header-file guard. That way, if client's
header file X includes your header file for
declarations, they can still include header file X
in the source file that creates the implementation;
if you guard the implementation too, then the first
include (before the #define) creates the declarations,
and the second one (after the #define) does nothing.
2. AVOID DEPENDENCIES
Don't rely on anything other than the C standard libraries.
(If you're creating a library specifically to leverage/wrap
some other library, then obviously you can rely on that
library. But if that library is public domain, you might
be better off directly embedding the source, to reduce
dependencies for your clients. But of course now you have
to update whenever that library updates.)
If you use stdlib, consider wrapping all stdlib calls in
macros, and then conditionally define those macros to the
stdlib function, allowing the user to replace them.
For functions with side effects, like memory allocations,
consider letting the user pass in a context and pass
that in to the macros. (The stdlib versions will ignore
the parameter.) Otherwise, users may have to use global
or thread-local variables to achieve the same effect.
3. AVOID MALLOC
You can't always do this, but when you can, embedded developers
will appreciate it. I almost never bother avoiding, as it's
too much work (and in some cases is pretty infeasible;
see http://nothings.org/gamedev/font_rendering_malloc.txt ).
But it's definitely something one of the things I've gotten
the most pushback on from potential users.
4. ALLOW STATIC IMPLEMENTATION
Have a #define which makes function declarations and
function definitions static. This makes the implementation
private to the source file that creates it. This allows
people to use your library multiple times in their project
without collision. (This is only necessary if your library
has configuration macros or global state, or if your
library has multiple versions that are not backwards
compatible. I've run into both of those cases.)
5. MAKE ACCESSIBLE FROM C
Making your code accessible from C instead of C++ (i.e.
either coding in C, or using extern "C") makes it more
straightforward to be used in C and in other languages,
which often only have support for C bindings, not C++.
(One of the earliest results I found in googling for
stb_image was a Haskell wrapper.) Otherwise, people
have to wrap it in another set of function calls, and
the whole point here is to make it convenient for people
to use, isn't it? (See below.)
I prefer to code entirely in C, so the source file that
instantiates the implementation can be C itself, for
those crazy people out there who are programming in C.
But it's probably not a big hardship for a C programmer
to create a single C++ source file to instantiate your
library.
6. NAMESPACE PRIVATE FUNCTIONS
Try to avoid having names in your source code that
will cause conflicts with identical names in client
code. You can do this either by namespacing in C++,
or prefixing with your library name in C.
In C, generally, I use the same prefix for API
functions and private symbols, such as "stbtt_"
for stb_truetype; but private functions (and
static globals) use a second underscore as
in "stbtt__" to further minimize the chance of
additional collisions in the unlikely but not
impossible event that users write wrapper
functions that have names of the form "stbtt_".
(Consider the user that has used "stbtt_foo"
*successfully*, and then upgrades to a new
version of your library which has a new private
function named either "stbtt_foo" or "stbtt__foo".)
Note that the double-underscore is reserved for
use by the compiler, but (1) there is nothing
reserved for "middleware", i.e. libraries
desiring to avoid conflicts with user symbols
have no other good options, and (2) in practice
no compilers use double-underscore in the middle
rather than the beginning/end. (Unfortunately,
there is at least one videogame-console compiler that
will warn about double-underscores by default.)
7. EASY-TO-COMPLY LICENSE
I make my libraries public domain. You don't have to.
But my goal in releasing stb-style libraries is to
reduce friction for potential users as much as
possible. That means:
a. easy to build (what this file is mostly about)
b. easy to invoke (which requires good API design)
c. easy to deploy (which is about licensing)
I choose to place all my libraries in the public
domain, abjuring copyright, rather than license
the libraries. This has some benefits and some
drawbacks.
Any license which is "viral" to modifications
causes worries for lawyers, even if their programmers
aren't modifying it.
Any license which requires crediting in documentation
adds friction which can add up. Valve used to have
a page with a list of all of these on their web site,
and it was insane, and obviously nobody ever looked
at it so why would you care whether your credit appeared
there?
Permissive licenses like zlib and BSD license are
perfectly reasonable, but they are very wordy and
have only two benefits over public domain: legally-mandated
attribution and liability-control. I do not believe these
are worth the excessive verbosity and user-unfriendliness
these licenses induce, especially in the single-file
case where those licenses tend to be at the top of
the file, the first thing you see. (To the specific
points, I have had no trouble receiving attribution
for my libraries; liability in the face of no explicit
disclaimer of liability is an open question.)
However, public domain has frictions of its own, because
public domain declarations aren't necessary recognized
in the USA and some other locations. For that reason,
I recommend a declaration along these lines:
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
I typically place this declaration at the end of the initial
comment block of the file and just say 'public domain'
at the top.
I have had people say they couldn't use one of my
libraries because it was only "public domain" and didn't
have the additional fallback clause, who asked if
I could dual-license it under a traditional license.
My answer: they can create a derivative work by
modifying one character, and then license that however
they like. (Indeed, *adding* the zlib or BSD license
would be such a modification!) Unfortunately, their
lawyers reportedly didn't like that answer. :(

View File

@@ -0,0 +1,173 @@
# An interview with STB about stb_voxel_render.h
**Q:**
I suppose you really like Minecraft?
**A:**
Not really. I mean, I do own it and play it some, and
I do watch YouTube videos of other people playing it
once in a while, but I'm not saying it's that great.
But I do love voxels. I've been playing with voxel rendering
since the mid-late 90's when we were still doing software
rendering and thinking maybe polygons weren't the answer.
Once GPUs came along that kind of died off, at least until
Minecraft brought it back to attention.
**Q:**
Do you expect people will make a lot of Minecraft clones
with this?
**A:**
I hope not!
For one thing, it's a terrible idea for the
developer. Remember before Minecraft was on the Xbox 360,
there were a ton of "indie" clones (some maybe making
decent money even), but then the real Minecraft came out
and just crushed them (as far as I know). It's just not
something you really want to compete with.
The reason I made this library is because I'd like
to see more games with Minecraft's *art style*, not
necessary its *gameplay*.
I can understand the urge to clone the gameplay. When
you have a world made of voxels/blocks, there are a
few things that become incredibly easy to do that would
otherwise be very hard (at least for an indie) to do in 3D.
One thing is that procedural generation becomes much easier.
Another is that destructible environments are easy. Another
is that you have a world where your average user can build
stuff that they find satisfactory.
Minecraft is at a sort of local maximum, a sweet spot, where
it leverages all of those easy-to-dos. And so I'm sure it's
hard to look at the space of 'games using voxels' and move
away from that local maximum, to give up some of that.
But I think that's what people should do.
**Q:**
So what else can people do with stb_voxel_render?
**A:**
All of those benefits I mentioned above are still valid even
if you stay away from the sweet spot. You can make a 3D roguelike
without player-creation/destruction that uses procedural generation.
You could make a shooter with pre-designed maps but destructible
environments.
And I'm sure there are other possible benefits to using voxels/blocks.
Hopefully this will make it easier for people to explore the space.
The library has a pretty wide range of features to allow
people to come up with some distinctive looks. For example,
the art style of Continue?9876543210 was one of the inspirations
for trying to make the multitexturing capabilities flexible.
I'm terrible at art, so this isn't really something I can
come up with myself, but I tried to put in flexible
technology that could be used multiple ways.
One thing I did intentionally was try to make it possible to
make nicer looking ground terrain, using the half-height
slopes and "weird slopes". There are Minecraft mods with
drivable cars and they just go up these blocky slopes and,
like, what? So I wanted you to be able to make smoother
terrain, either just for the look, or for vehicles etc.
Also, you can spatially cross-fade between two ground textures for
that classic bad dirt/grass transition that has shipped
in plenty of professional games. Of course, you could
just use a separate non-voxel ground renderer for all of
this. But this way, you can seamlessly integrate everything
else with it. E.g. in your authoring tool (or procedural
generation) you can make smooth ground and then cut a
sharp-edged hole in it for a building's basement or whatever.
Another thing you can do is work at a very different scale.
In Minecraft, a person is just under 2 blocks tall. In
Ace of Spades, a person is just under 3 blocks tall. Why
not 4 or 6? Well, partly because you just need a lot more
voxels; if a meter is 2 voxels in Mineraft and 4 voxels in
your game, and you draw the same number of voxels due to
hardware limits, then your game has half the view distance
of Minecraft. Since stb_voxel_render is designed to keep
the meshes small and render efficiently, you can push the
view distance out further than Minecraft--or use a similar
view distance and a higher voxel resolution. You could also
stop making infinite worlds and work at entirely different
scales; where Minecraft is 1 voxel per meter, you could
have 20 voxels per meter and make a small arena that's
50 meters wide and 5 meters tall.
Back when the voxel game Voxatron was announced, the weekend
after the trailer came out I wrote my own little GPU-accelerated
version of the engine and thought that was pretty cool. I've
been tempted many times to extract that and release it
as a library, but
I don't want to steal Voxatron's thunder so I've avoided
it. You could use this engine to do the same kind of thing,
although it won't be as efficient as an engine dedicated to
that style of thing would be.
**Q:**
What one thing would you really like to see somebody do?
**A:**
Before Unity, 3D has seemed deeply problematic in the indie
space. Software like GameMaker has tried to support 3D but
it seems like little of note has been done with it.
Minecraft has shown that people can build worlds with the
Minecraft toolset far more easily than we've ever seen from those
other tools. Obviously people have done great things with
Unity, but those people are much closer to professional
developers; typically they still need real 3D modelling
and all of that stuff.
So what I'd really like to see is someone build some kind
of voxel-game-construction-set. Start with stb_voxel_render,
maybe expose all the flexibility of stb_voxel_render (so
people can do different things). Thrown in lua or something
else for scripting, make some kind of editor that feels
at least as good as Minecraft and Infinifactory, and see
where that gets you.
**Q:**
Why'd you make this library?
**A:**
Mainly as a way of releasing this technology I've been working
on since 2011 and seemed unlikely to ever ship myself. In 2011
I was playing the voxel shooter Ace of Spades. One of the maps
that we played on was a partial port of Broville (which is the
first Minecraft map in stb_voxel_render release trailer). I'd
made a bunch of procedural level generators for the game, and
I started trying to make a city generator inspired by Broville.
But I realized it would be a lot of work, and of very little
value (most of my maps didn't get much play because people
preferred to play on maps where they could charge straight
at the enemies and shoot them as fast as possible). So I
wrote my own voxel engine and started working on a procedural
city game. But I got bogged down after I finally got the road
generator working and never got anywhere with building
generation or gameplay.
stb_voxel_render is actually a complete rewrite from scratch,
but it's based a lot on what I learned from that previous work.
**Q:**
About the release video... how long did that take to edit?
**A:**
About seven or eight hours. I had the first version done in
maybe six or seven hours, but then I realized I'd left out
one clip, and when I went back to add it I also gussied up
a couple other moments in the video. But there was something
basically identical to it that was done in around six.
**Q:**
Ok, that's it. Thanks, me.
**A:**
Thanks *me!*

117
thirdparty/stb/docs/why_public_domain.md vendored Normal file
View File

@@ -0,0 +1,117 @@
My collected rationales for placing these libraries
in the public domain:
1. Public domain vs. viral licenses
Why is this library public domain?
Because more people will use it. Because it's not viral, people are
not obligated to give back, so you could argue that it hurts the
development of it, and then because it doesn't develop as well it's
not as good, and then because it's not as good, in the long run
maybe fewer people will use it. I have total respect for that
opinion, but I just don't believe it myself for most software.
2. Public domain vs. attribution-required licenses
The primary difference between public domain and, say, a Creative Commons
commercial / non-share-alike / attribution license is solely the
requirement for attribution. (Similarly the BSD license and such.)
While I would *appreciate* acknowledgement and attribution, I believe
that it is foolish to place a legal encumberment (i.e. a license) on
the software *solely* to get attribution.
In other words, I'm arguing that PD is superior to the BSD license and
the Creative Commons 'Attribution' license. If the license offers
anything besides attribution -- as does, e.g., CC NonCommercial-ShareAlike,
or the GPL -- that's a separate discussion.
3. Other aspects of BSD-style licenses besides attribution
Permissive licenses like zlib and BSD license are perfectly reasonable
in their requirements, but they are very wordy and
have only two benefits over public domain: legally-mandated
attribution and liability-control. I do not believe these
are worth the excessive verbosity and user-unfriendliness
these licenses induce, especially in the single-file
case where those licenses tend to be at the top of
the file, the first thing you see.
To the specific points, I have had no trouble receiving
attribution for my libraries; liability in the face of
no explicit disclaimer of liability is an open question,
but one I have a lot of difficulty imagining there being
any actual doubt about in court. Sometimes I explicitly
note in my libraries that I make no guarantees about them
being fit for purpose, but it's pretty absurd to do this;
as a whole, it comes across as "here is a library to decode
vorbis audio files, but it may not actually work and if
you have problems it's not my fault, but also please
report bugs so I can fix them"--so dumb!
4. full discussion from stb_howto.txt on what YOU should do for YOUR libs
```
EASY-TO-COMPLY LICENSE
I make my libraries public domain. You don't have to.
But my goal in releasing stb-style libraries is to
reduce friction for potential users as much as
possible. That means:
a. easy to build (what this file is mostly about)
b. easy to invoke (which requires good API design)
c. easy to deploy (which is about licensing)
I choose to place all my libraries in the public
domain, abjuring copyright, rather than license
the libraries. This has some benefits and some
drawbacks.
Any license which is "viral" to modifications
causes worries for lawyers, even if their programmers
aren't modifying it.
Any license which requires crediting in documentation
adds friction which can add up. Valve has a huge list
(http://nothings.org/remote/ThirdPartyLegalNotices_steam_2019.html)
of all of these included in each game they ship,
and it's insane, and obviously nobody ever looks
at it so why would you care whether your credit
appeared there?
Permissive licenses like zlib and BSD license are
perfectly reasonable, but they are very wordy and
have only two benefits over public domain: legally-mandated
attribution and liability-control. I do not believe these
are worth the excessive verbosity and user-unfriendliness
these licenses induce, especially in the single-file
case where those licenses tend to be at the top of
the file, the first thing you see. (To the specific
points, I have had no trouble receiving attribution
for my libraries; liability in the face of no explicit
disclaimer of liability is an open question.)
However, public domain has frictions of its own, because
public domain declarations aren't necessary recognized
in the USA and some other locations. For that reason,
I recommend a declaration along these lines:
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
I typically place this declaration at the end of the initial
comment block of the file and just say 'public domain'
at the top.
I have had people say they couldn't use one of my
libraries because it was only "public domain" and didn't
have the additional fallback clause, who asked if
I could dual-license it under a traditional license.
My answer: they can create a derivative work by
modifying one character, and then license that however
they like. (Indeed, *adding* the zlib or BSD license
would be such a modification!) Unfortunately, their
lawyers reportedly didn't like that answer. :(
```

941
thirdparty/stb/stb_c_lexer.h vendored Normal file
View File

@@ -0,0 +1,941 @@
// stb_c_lexer.h - v0.12 - public domain Sean Barrett 2013
// lexer for making little C-like languages with recursive-descent parsers
//
// This file provides both the interface and the implementation.
// To instantiate the implementation,
// #define STB_C_LEXER_IMPLEMENTATION
// in *ONE* source file, before #including this file.
//
// The default configuration is fairly close to a C lexer, although
// suffixes on integer constants are not handled (you can override this).
//
// History:
// 0.12 fix compilation bug for NUL support; better support separate inclusion
// 0.11 fix clang static analysis warning
// 0.10 fix warnings
// 0.09 hex floats, no-stdlib fixes
// 0.08 fix bad pointer comparison
// 0.07 fix mishandling of hexadecimal constants parsed by strtol
// 0.06 fix missing next character after ending quote mark (Andreas Fredriksson)
// 0.05 refixed get_location because github version had lost the fix
// 0.04 fix octal parsing bug
// 0.03 added STB_C_LEX_DISCARD_PREPROCESSOR option
// refactor API to simplify (only one struct instead of two)
// change literal enum names to have 'lit' at the end
// 0.02 first public release
//
// Status:
// - haven't tested compiling as C++
// - haven't tested the float parsing path
// - haven't tested the non-default-config paths (e.g. non-stdlib)
// - only tested default-config paths by eyeballing output of self-parse
//
// - haven't implemented multiline strings
// - haven't implemented octal/hex character constants
// - haven't implemented support for unicode CLEX_char
// - need to expand error reporting so you don't just get "CLEX_parse_error"
//
// Contributors:
// Arpad Goretity (bugfix)
// Alan Hickman (hex floats)
// github:mundusnine (bugfix)
//
// LICENSE
//
// See end of file for license information.
#ifdef STB_C_LEXER_IMPLEMENTATION
#ifndef STB_C_LEXER_DEFINITIONS
// to change the default parsing rules, copy the following lines
// into your C/C++ file *before* including this, and then replace
// the Y's with N's for the ones you don't want. This needs to be
// set to the same values for every place in your program where
// stb_c_lexer.h is included.
// --BEGIN--
#if defined(Y) || defined(N)
#error "Can only use stb_c_lexer in contexts where the preprocessor symbols 'Y' and 'N' are not defined"
#endif
#define STB_C_LEX_C_DECIMAL_INTS Y // "0|[1-9][0-9]*" CLEX_intlit
#define STB_C_LEX_C_HEX_INTS Y // "0x[0-9a-fA-F]+" CLEX_intlit
#define STB_C_LEX_C_OCTAL_INTS Y // "[0-7]+" CLEX_intlit
#define STB_C_LEX_C_DECIMAL_FLOATS Y // "[0-9]*(.[0-9]*([eE][-+]?[0-9]+)?) CLEX_floatlit
#define STB_C_LEX_C99_HEX_FLOATS N // "0x{hex}+(.{hex}*)?[pP][-+]?{hex}+ CLEX_floatlit
#define STB_C_LEX_C_IDENTIFIERS Y // "[_a-zA-Z][_a-zA-Z0-9]*" CLEX_id
#define STB_C_LEX_C_DQ_STRINGS Y // double-quote-delimited strings with escapes CLEX_dqstring
#define STB_C_LEX_C_SQ_STRINGS N // single-quote-delimited strings with escapes CLEX_ssstring
#define STB_C_LEX_C_CHARS Y // single-quote-delimited character with escape CLEX_charlits
#define STB_C_LEX_C_COMMENTS Y // "/* comment */"
#define STB_C_LEX_CPP_COMMENTS Y // "// comment to end of line\n"
#define STB_C_LEX_C_COMPARISONS Y // "==" CLEX_eq "!=" CLEX_noteq "<=" CLEX_lesseq ">=" CLEX_greatereq
#define STB_C_LEX_C_LOGICAL Y // "&&" CLEX_andand "||" CLEX_oror
#define STB_C_LEX_C_SHIFTS Y // "<<" CLEX_shl ">>" CLEX_shr
#define STB_C_LEX_C_INCREMENTS Y // "++" CLEX_plusplus "--" CLEX_minusminus
#define STB_C_LEX_C_ARROW Y // "->" CLEX_arrow
#define STB_C_LEX_EQUAL_ARROW N // "=>" CLEX_eqarrow
#define STB_C_LEX_C_BITWISEEQ Y // "&=" CLEX_andeq "|=" CLEX_oreq "^=" CLEX_xoreq
#define STB_C_LEX_C_ARITHEQ Y // "+=" CLEX_pluseq "-=" CLEX_minuseq
// "*=" CLEX_muleq "/=" CLEX_diveq "%=" CLEX_modeq
// if both STB_C_LEX_SHIFTS & STB_C_LEX_ARITHEQ:
// "<<=" CLEX_shleq ">>=" CLEX_shreq
#define STB_C_LEX_PARSE_SUFFIXES N // letters after numbers are parsed as part of those numbers, and must be in suffix list below
#define STB_C_LEX_DECIMAL_SUFFIXES "" // decimal integer suffixes e.g. "uUlL" -- these are returned as-is in string storage
#define STB_C_LEX_HEX_SUFFIXES "" // e.g. "uUlL"
#define STB_C_LEX_OCTAL_SUFFIXES "" // e.g. "uUlL"
#define STB_C_LEX_FLOAT_SUFFIXES "" //
#define STB_C_LEX_0_IS_EOF N // if Y, ends parsing at '\0'; if N, returns '\0' as token
#define STB_C_LEX_INTEGERS_AS_DOUBLES N // parses integers as doubles so they can be larger than 'int', but only if STB_C_LEX_STDLIB==N
#define STB_C_LEX_MULTILINE_DSTRINGS N // allow newlines in double-quoted strings
#define STB_C_LEX_MULTILINE_SSTRINGS N // allow newlines in single-quoted strings
#define STB_C_LEX_USE_STDLIB Y // use strtod,strtol for parsing #s; otherwise inaccurate hack
#define STB_C_LEX_DOLLAR_IDENTIFIER Y // allow $ as an identifier character
#define STB_C_LEX_FLOAT_NO_DECIMAL Y // allow floats that have no decimal point if they have an exponent
#define STB_C_LEX_DEFINE_ALL_TOKEN_NAMES N // if Y, all CLEX_ token names are defined, even if never returned
// leaving it as N should help you catch config bugs
#define STB_C_LEX_DISCARD_PREPROCESSOR Y // discard C-preprocessor directives (e.g. after prepocess
// still have #line, #pragma, etc)
//#define STB_C_LEX_ISWHITE(str) ... // return length in bytes of whitespace characters if first char is whitespace
#define STB_C_LEXER_DEFINITIONS // This line prevents the header file from replacing your definitions
// --END--
#endif
#endif
#ifndef INCLUDE_STB_C_LEXER_H
#define INCLUDE_STB_C_LEXER_H
typedef struct
{
// lexer variables
char *input_stream;
char *eof;
char *parse_point;
char *string_storage;
int string_storage_len;
// lexer parse location for error messages
char *where_firstchar;
char *where_lastchar;
// lexer token variables
long token;
double real_number;
long int_number;
char *string;
int string_len;
} stb_lexer;
typedef struct
{
int line_number;
int line_offset;
} stb_lex_location;
#ifdef __cplusplus
extern "C" {
#endif
extern void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length);
// this function initialize the 'lexer' structure
// Input:
// - input_stream points to the file to parse, loaded into memory
// - input_stream_end points to the end of the file, or NULL if you use 0-for-EOF
// - string_store is storage the lexer can use for storing parsed strings and identifiers
// - store_length is the length of that storage
extern int stb_c_lexer_get_token(stb_lexer *lexer);
// this function returns non-zero if a token is parsed, or 0 if at EOF
// Output:
// - lexer->token is the token ID, which is unicode code point for a single-char token, < 0 for a multichar or eof or error
// - lexer->real_number is a double constant value for CLEX_floatlit, or CLEX_intlit if STB_C_LEX_INTEGERS_AS_DOUBLES
// - lexer->int_number is an integer constant for CLEX_intlit if !STB_C_LEX_INTEGERS_AS_DOUBLES, or character for CLEX_charlit
// - lexer->string is a 0-terminated string for CLEX_dqstring or CLEX_sqstring or CLEX_identifier
// - lexer->string_len is the byte length of lexer->string
extern void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc);
// this inefficient function returns the line number and character offset of a
// given location in the file as returned by stb_lex_token. Because it's inefficient,
// you should only call it for errors, not for every token.
// For error messages of invalid tokens, you typically want the location of the start
// of the token (which caused the token to be invalid). For bugs involving legit
// tokens, you can report the first or the range.
// Output:
// - loc->line_number is the line number in the file, counting from 1, of the location
// - loc->line_offset is the char-offset in the line, counting from 0, of the location
#ifdef __cplusplus
}
#endif
enum
{
CLEX_eof = 256,
CLEX_parse_error,
CLEX_intlit ,
CLEX_floatlit ,
CLEX_id ,
CLEX_dqstring ,
CLEX_sqstring ,
CLEX_charlit ,
CLEX_eq ,
CLEX_noteq ,
CLEX_lesseq ,
CLEX_greatereq ,
CLEX_andand ,
CLEX_oror ,
CLEX_shl ,
CLEX_shr ,
CLEX_plusplus ,
CLEX_minusminus ,
CLEX_pluseq ,
CLEX_minuseq ,
CLEX_muleq ,
CLEX_diveq ,
CLEX_modeq ,
CLEX_andeq ,
CLEX_oreq ,
CLEX_xoreq ,
CLEX_arrow ,
CLEX_eqarrow ,
CLEX_shleq, CLEX_shreq,
CLEX_first_unused_token
};
#endif // INCLUDE_STB_C_LEXER_H
#ifdef STB_C_LEXER_IMPLEMENTATION
// Hacky definitions so we can easily #if on them
#define Y(x) 1
#define N(x) 0
#if STB_C_LEX_INTEGERS_AS_DOUBLES(x)
typedef double stb__clex_int;
#define intfield real_number
#define STB__clex_int_as_double
#else
typedef long stb__clex_int;
#define intfield int_number
#endif
// Convert these config options to simple conditional #defines so we can more
// easily test them once we've change the meaning of Y/N
#if STB_C_LEX_PARSE_SUFFIXES(x)
#define STB__clex_parse_suffixes
#endif
#if STB_C_LEX_C99_HEX_FLOATS(x)
#define STB__clex_hex_floats
#endif
#if STB_C_LEX_C_HEX_INTS(x)
#define STB__clex_hex_ints
#endif
#if STB_C_LEX_C_DECIMAL_INTS(x)
#define STB__clex_decimal_ints
#endif
#if STB_C_LEX_C_OCTAL_INTS(x)
#define STB__clex_octal_ints
#endif
#if STB_C_LEX_C_DECIMAL_FLOATS(x)
#define STB__clex_decimal_floats
#endif
#if STB_C_LEX_DISCARD_PREPROCESSOR(x)
#define STB__clex_discard_preprocessor
#endif
#if STB_C_LEX_USE_STDLIB(x) && (!defined(STB__clex_hex_floats) || __STDC_VERSION__ >= 199901L)
#define STB__CLEX_use_stdlib
#include <stdlib.h>
#endif
// Now for the rest of the file we'll use the basic definition where
// where Y expands to its contents and N expands to nothing
#undef Y
#define Y(a) a
#undef N
#define N(a)
// API function
void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length)
{
lexer->input_stream = (char *) input_stream;
lexer->eof = (char *) input_stream_end;
lexer->parse_point = (char *) input_stream;
lexer->string_storage = string_store;
lexer->string_storage_len = store_length;
}
// API function
void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc)
{
char *p = lexer->input_stream;
int line_number = 1;
int char_offset = 0;
while (*p && p < where) {
if (*p == '\n' || *p == '\r') {
p += (p[0]+p[1] == '\r'+'\n' ? 2 : 1); // skip newline
line_number += 1;
char_offset = 0;
} else {
++p;
++char_offset;
}
}
loc->line_number = line_number;
loc->line_offset = char_offset;
}
// main helper function for returning a parsed token
static int stb__clex_token(stb_lexer *lexer, int token, char *start, char *end)
{
lexer->token = token;
lexer->where_firstchar = start;
lexer->where_lastchar = end;
lexer->parse_point = end+1;
return 1;
}
// helper function for returning eof
static int stb__clex_eof(stb_lexer *lexer)
{
lexer->token = CLEX_eof;
return 0;
}
static int stb__clex_iswhite(int x)
{
return x == ' ' || x == '\t' || x == '\r' || x == '\n' || x == '\f';
}
static const char *stb__strchr(const char *str, int ch)
{
for (; *str; ++str)
if (*str == ch)
return str;
return 0;
}
// parse suffixes at the end of a number
static int stb__clex_parse_suffixes(stb_lexer *lexer, long tokenid, char *start, char *cur, const char *suffixes)
{
#ifdef STB__clex_parse_suffixes
lexer->string = lexer->string_storage;
lexer->string_len = 0;
while ((*cur >= 'a' && *cur <= 'z') || (*cur >= 'A' && *cur <= 'Z')) {
if (stb__strchr(suffixes, *cur) == 0)
return stb__clex_token(lexer, CLEX_parse_error, start, cur);
if (lexer->string_len+1 >= lexer->string_storage_len)
return stb__clex_token(lexer, CLEX_parse_error, start, cur);
lexer->string[lexer->string_len++] = *cur++;
}
#else
suffixes = suffixes; // attempt to suppress warnings
#endif
return stb__clex_token(lexer, tokenid, start, cur-1);
}
#ifndef STB__CLEX_use_stdlib
static double stb__clex_pow(double base, unsigned int exponent)
{
double value=1;
for ( ; exponent; exponent >>= 1) {
if (exponent & 1)
value *= base;
base *= base;
}
return value;
}
static double stb__clex_parse_float(char *p, char **q)
{
char *s = p;
double value=0;
int base=10;
int exponent=0;
#ifdef STB__clex_hex_floats
if (*p == '0') {
if (p[1] == 'x' || p[1] == 'X') {
base=16;
p += 2;
}
}
#endif
for (;;) {
if (*p >= '0' && *p <= '9')
value = value*base + (*p++ - '0');
#ifdef STB__clex_hex_floats
else if (base == 16 && *p >= 'a' && *p <= 'f')
value = value*base + 10 + (*p++ - 'a');
else if (base == 16 && *p >= 'A' && *p <= 'F')
value = value*base + 10 + (*p++ - 'A');
#endif
else
break;
}
if (*p == '.') {
double pow, addend = 0;
++p;
for (pow=1; ; pow*=base) {
if (*p >= '0' && *p <= '9')
addend = addend*base + (*p++ - '0');
#ifdef STB__clex_hex_floats
else if (base == 16 && *p >= 'a' && *p <= 'f')
addend = addend*base + 10 + (*p++ - 'a');
else if (base == 16 && *p >= 'A' && *p <= 'F')
addend = addend*base + 10 + (*p++ - 'A');
#endif
else
break;
}
value += addend / pow;
}
#ifdef STB__clex_hex_floats
if (base == 16) {
// exponent required for hex float literal
if (*p != 'p' && *p != 'P') {
*q = s;
return 0;
}
exponent = 1;
} else
#endif
exponent = (*p == 'e' || *p == 'E');
if (exponent) {
int sign = p[1] == '-';
unsigned int exponent=0;
double power=1;
++p;
if (*p == '-' || *p == '+')
++p;
while (*p >= '0' && *p <= '9')
exponent = exponent*10 + (*p++ - '0');
#ifdef STB__clex_hex_floats
if (base == 16)
power = stb__clex_pow(2, exponent);
else
#endif
power = stb__clex_pow(10, exponent);
if (sign)
value /= power;
else
value *= power;
}
*q = p;
return value;
}
#endif
static int stb__clex_parse_char(char *p, char **q)
{
if (*p == '\\') {
*q = p+2; // tentatively guess we'll parse two characters
switch(p[1]) {
case '\\': return '\\';
case '\'': return '\'';
case '"': return '"';
case 't': return '\t';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case '0': return '\0'; // @TODO ocatal constants
case 'x': case 'X': return -1; // @TODO hex constants
case 'u': return -1; // @TODO unicode constants
}
}
*q = p+1;
return (unsigned char) *p;
}
static int stb__clex_parse_string(stb_lexer *lexer, char *p, int type)
{
char *start = p;
char delim = *p++; // grab the " or ' for later matching
char *out = lexer->string_storage;
char *outend = lexer->string_storage + lexer->string_storage_len;
while (*p != delim) {
int n;
if (*p == '\\') {
char *q;
n = stb__clex_parse_char(p, &q);
if (n < 0)
return stb__clex_token(lexer, CLEX_parse_error, start, q);
p = q;
} else {
// @OPTIMIZE: could speed this up by looping-while-not-backslash
n = (unsigned char) *p++;
}
if (out+1 > outend)
return stb__clex_token(lexer, CLEX_parse_error, start, p);
// @TODO expand unicode escapes to UTF8
*out++ = (char) n;
}
*out = 0;
lexer->string = lexer->string_storage;
lexer->string_len = (int) (out - lexer->string_storage);
return stb__clex_token(lexer, type, start, p);
}
int stb_c_lexer_get_token(stb_lexer *lexer)
{
char *p = lexer->parse_point;
// skip whitespace and comments
for (;;) {
#ifdef STB_C_LEX_ISWHITE
while (p != lexer->stream_end) {
int n;
n = STB_C_LEX_ISWHITE(p);
if (n == 0) break;
if (lexer->eof && lexer->eof - lexer->parse_point < n)
return stb__clex_token(tok, CLEX_parse_error, p,lexer->eof-1);
p += n;
}
#else
while (p != lexer->eof && stb__clex_iswhite(*p))
++p;
#endif
STB_C_LEX_CPP_COMMENTS(
if (p != lexer->eof && p[0] == '/' && p[1] == '/') {
while (p != lexer->eof && *p != '\r' && *p != '\n')
++p;
continue;
}
)
STB_C_LEX_C_COMMENTS(
if (p != lexer->eof && p[0] == '/' && p[1] == '*') {
char *start = p;
p += 2;
while (p != lexer->eof && (p[0] != '*' || p[1] != '/'))
++p;
if (p == lexer->eof)
return stb__clex_token(lexer, CLEX_parse_error, start, p-1);
p += 2;
continue;
}
)
#ifdef STB__clex_discard_preprocessor
// @TODO this discards everything after a '#', regardless
// of where in the line the # is, rather than requiring it
// be at the start. (because this parser doesn't otherwise
// check for line breaks!)
if (p != lexer->eof && p[0] == '#') {
while (p != lexer->eof && *p != '\r' && *p != '\n')
++p;
continue;
}
#endif
break;
}
if (p == lexer->eof)
return stb__clex_eof(lexer);
switch (*p) {
default:
if ( (*p >= 'a' && *p <= 'z')
|| (*p >= 'A' && *p <= 'Z')
|| *p == '_' || (unsigned char) *p >= 128 // >= 128 is UTF8 char
STB_C_LEX_DOLLAR_IDENTIFIER( || *p == '$' ) )
{
int n = 0;
lexer->string = lexer->string_storage;
do {
if (n+1 >= lexer->string_storage_len)
return stb__clex_token(lexer, CLEX_parse_error, p, p+n);
lexer->string[n] = p[n];
++n;
} while (
(p[n] >= 'a' && p[n] <= 'z')
|| (p[n] >= 'A' && p[n] <= 'Z')
|| (p[n] >= '0' && p[n] <= '9') // allow digits in middle of identifier
|| p[n] == '_' || (unsigned char) p[n] >= 128
STB_C_LEX_DOLLAR_IDENTIFIER( || p[n] == '$' )
);
lexer->string[n] = 0;
lexer->string_len = n;
return stb__clex_token(lexer, CLEX_id, p, p+n-1);
}
// check for EOF
STB_C_LEX_0_IS_EOF(
if (*p == 0)
return stb__clex_eof(lexer);
)
single_char:
// not an identifier, return the character as itself
return stb__clex_token(lexer, *p, p, p);
case '+':
if (p+1 != lexer->eof) {
STB_C_LEX_C_INCREMENTS(if (p[1] == '+') return stb__clex_token(lexer, CLEX_plusplus, p,p+1);)
STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_pluseq , p,p+1);)
}
goto single_char;
case '-':
if (p+1 != lexer->eof) {
STB_C_LEX_C_INCREMENTS(if (p[1] == '-') return stb__clex_token(lexer, CLEX_minusminus, p,p+1);)
STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_minuseq , p,p+1);)
STB_C_LEX_C_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_arrow , p,p+1);)
}
goto single_char;
case '&':
if (p+1 != lexer->eof) {
STB_C_LEX_C_LOGICAL( if (p[1] == '&') return stb__clex_token(lexer, CLEX_andand, p,p+1);)
STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_andeq , p,p+1);)
}
goto single_char;
case '|':
if (p+1 != lexer->eof) {
STB_C_LEX_C_LOGICAL( if (p[1] == '|') return stb__clex_token(lexer, CLEX_oror, p,p+1);)
STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_oreq, p,p+1);)
}
goto single_char;
case '=':
if (p+1 != lexer->eof) {
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_eq, p,p+1);)
STB_C_LEX_EQUAL_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_eqarrow, p,p+1);)
}
goto single_char;
case '!':
STB_C_LEX_C_COMPARISONS(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_noteq, p,p+1);)
goto single_char;
case '^':
STB_C_LEX_C_BITWISEEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_xoreq, p,p+1));
goto single_char;
case '%':
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_modeq, p,p+1));
goto single_char;
case '*':
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_muleq, p,p+1));
goto single_char;
case '/':
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_diveq, p,p+1));
goto single_char;
case '<':
if (p+1 != lexer->eof) {
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_lesseq, p,p+1);)
STB_C_LEX_C_SHIFTS( if (p[1] == '<') {
STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=')
return stb__clex_token(lexer, CLEX_shleq, p,p+2);)
return stb__clex_token(lexer, CLEX_shl, p,p+1);
}
)
}
goto single_char;
case '>':
if (p+1 != lexer->eof) {
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_greatereq, p,p+1);)
STB_C_LEX_C_SHIFTS( if (p[1] == '>') {
STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=')
return stb__clex_token(lexer, CLEX_shreq, p,p+2);)
return stb__clex_token(lexer, CLEX_shr, p,p+1);
}
)
}
goto single_char;
case '"':
STB_C_LEX_C_DQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_dqstring);)
goto single_char;
case '\'':
STB_C_LEX_C_SQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_sqstring);)
STB_C_LEX_C_CHARS(
{
char *start = p;
lexer->int_number = stb__clex_parse_char(p+1, &p);
if (lexer->int_number < 0)
return stb__clex_token(lexer, CLEX_parse_error, start,start);
if (p == lexer->eof || *p != '\'')
return stb__clex_token(lexer, CLEX_parse_error, start,p);
return stb__clex_token(lexer, CLEX_charlit, start, p+1);
})
goto single_char;
case '0':
#if defined(STB__clex_hex_ints) || defined(STB__clex_hex_floats)
if (p+1 != lexer->eof) {
if (p[1] == 'x' || p[1] == 'X') {
char *q;
#ifdef STB__clex_hex_floats
for (q=p+2;
q != lexer->eof && ((*q >= '0' && *q <= '9') || (*q >= 'a' && *q <= 'f') || (*q >= 'A' && *q <= 'F'));
++q);
if (q != lexer->eof) {
if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'p' || *q == 'P')) {
#ifdef STB__CLEX_use_stdlib
lexer->real_number = strtod((char *) p, (char**) &q);
#else
lexer->real_number = stb__clex_parse_float(p, &q);
#endif
if (p == q)
return stb__clex_token(lexer, CLEX_parse_error, p,q);
return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES);
}
}
#endif // STB__CLEX_hex_floats
#ifdef STB__clex_hex_ints
#ifdef STB__CLEX_use_stdlib
lexer->int_number = strtol((char *) p, (char **) &q, 16);
#else
{
stb__clex_int n=0;
for (q=p+2; q != lexer->eof; ++q) {
if (*q >= '0' && *q <= '9')
n = n*16 + (*q - '0');
else if (*q >= 'a' && *q <= 'f')
n = n*16 + (*q - 'a') + 10;
else if (*q >= 'A' && *q <= 'F')
n = n*16 + (*q - 'A') + 10;
else
break;
}
lexer->int_number = n;
}
#endif
if (q == p+2)
return stb__clex_token(lexer, CLEX_parse_error, p-2,p-1);
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_HEX_SUFFIXES);
#endif
}
}
#endif // defined(STB__clex_hex_ints) || defined(STB__clex_hex_floats)
// can't test for octal because we might parse '0.0' as float or as '0' '.' '0',
// so have to do float first
/* FALL THROUGH */
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
#ifdef STB__clex_decimal_floats
{
char *q = p;
while (q != lexer->eof && (*q >= '0' && *q <= '9'))
++q;
if (q != lexer->eof) {
if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'e' || *q == 'E')) {
#ifdef STB__CLEX_use_stdlib
lexer->real_number = strtod((char *) p, (char**) &q);
#else
lexer->real_number = stb__clex_parse_float(p, &q);
#endif
return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES);
}
}
}
#endif // STB__clex_decimal_floats
#ifdef STB__clex_octal_ints
if (p[0] == '0') {
char *q = p;
#ifdef STB__CLEX_use_stdlib
lexer->int_number = strtol((char *) p, (char **) &q, 8);
#else
stb__clex_int n=0;
while (q != lexer->eof) {
if (*q >= '0' && *q <= '7')
n = n*8 + (*q - '0');
else
break;
++q;
}
if (q != lexer->eof && (*q == '8' || *q=='9'))
return stb__clex_token(lexer, CLEX_parse_error, p, q);
lexer->int_number = n;
#endif
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES);
}
#endif // STB__clex_octal_ints
#ifdef STB__clex_decimal_ints
{
char *q = p;
#ifdef STB__CLEX_use_stdlib
lexer->int_number = strtol((char *) p, (char **) &q, 10);
#else
stb__clex_int n=0;
while (q != lexer->eof) {
if (*q >= '0' && *q <= '9')
n = n*10 + (*q - '0');
else
break;
++q;
}
lexer->int_number = n;
#endif
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES);
}
#endif // STB__clex_decimal_ints
goto single_char;
}
}
#endif // STB_C_LEXER_IMPLEMENTATION
#ifdef STB_C_LEXER_SELF_TEST
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
static void print_token(stb_lexer *lexer)
{
switch (lexer->token) {
case CLEX_id : printf("_%s", lexer->string); break;
case CLEX_eq : printf("=="); break;
case CLEX_noteq : printf("!="); break;
case CLEX_lesseq : printf("<="); break;
case CLEX_greatereq : printf(">="); break;
case CLEX_andand : printf("&&"); break;
case CLEX_oror : printf("||"); break;
case CLEX_shl : printf("<<"); break;
case CLEX_shr : printf(">>"); break;
case CLEX_plusplus : printf("++"); break;
case CLEX_minusminus: printf("--"); break;
case CLEX_arrow : printf("->"); break;
case CLEX_andeq : printf("&="); break;
case CLEX_oreq : printf("|="); break;
case CLEX_xoreq : printf("^="); break;
case CLEX_pluseq : printf("+="); break;
case CLEX_minuseq : printf("-="); break;
case CLEX_muleq : printf("*="); break;
case CLEX_diveq : printf("/="); break;
case CLEX_modeq : printf("%%="); break;
case CLEX_shleq : printf("<<="); break;
case CLEX_shreq : printf(">>="); break;
case CLEX_eqarrow : printf("=>"); break;
case CLEX_dqstring : printf("\"%s\"", lexer->string); break;
case CLEX_sqstring : printf("'\"%s\"'", lexer->string); break;
case CLEX_charlit : printf("'%s'", lexer->string); break;
#if defined(STB__clex_int_as_double) && !defined(STB__CLEX_use_stdlib)
case CLEX_intlit : printf("#%g", lexer->real_number); break;
#else
case CLEX_intlit : printf("#%ld", lexer->int_number); break;
#endif
case CLEX_floatlit : printf("%g", lexer->real_number); break;
default:
if (lexer->token >= 0 && lexer->token < 256)
printf("%c", (int) lexer->token);
else {
printf("<<<UNKNOWN TOKEN %ld >>>\n", lexer->token);
}
break;
}
}
/* Force a test
of parsing
multiline comments */
/*/ comment /*/
/**/ extern /**/
void dummy(void)
{
double some_floats[] = {
1.0501, -10.4e12, 5E+10,
#if 0 // not supported in C++ or C-pre-99, so don't try to compile it, but let our parser test it
0x1.0p+24, 0xff.FP-8, 0x1p-23,
#endif
4.
};
(void) sizeof(some_floats);
(void) some_floats[1];
printf("test %d",1); // https://github.com/nothings/stb/issues/13
}
int main(int argc, char **argv)
{
FILE *f = fopen("stb_c_lexer.h","rb");
char *text = (char *) malloc(1 << 20);
int len = f ? (int) fread(text, 1, 1<<20, f) : -1;
stb_lexer lex;
if (len < 0) {
fprintf(stderr, "Error opening file\n");
free(text);
fclose(f);
return 1;
}
fclose(f);
stb_c_lexer_init(&lex, text, text+len, (char *) malloc(0x10000), 0x10000);
while (stb_c_lexer_get_token(&lex)) {
if (lex.token == CLEX_parse_error) {
printf("\n<<<PARSE ERROR>>>\n");
break;
}
print_token(&lex);
printf(" ");
}
return 0;
}
#endif
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

1049
thirdparty/stb/stb_connected_components.h vendored Normal file

File diff suppressed because it is too large Load Diff

433
thirdparty/stb/stb_divide.h vendored Normal file
View File

@@ -0,0 +1,433 @@
// stb_divide.h - v0.94 - public domain - Sean Barrett, Feb 2010
// Three kinds of divide/modulus of signed integers.
//
// HISTORY
//
// v0.94 Fix integer overflow issues
// v0.93 2020-02-02 Write useful exit() value from main()
// v0.92 2019-02-25 Fix warning
// v0.91 2010-02-27 Fix euclidean division by INT_MIN for non-truncating C
// Check result with 64-bit math to catch such cases
// v0.90 2010-02-24 First public release
//
// USAGE
//
// In *ONE* source file, put:
//
// #define STB_DIVIDE_IMPLEMENTATION
// // #define C_INTEGER_DIVISION_TRUNCATES // see Note 1
// // #define C_INTEGER_DIVISION_FLOORS // see Note 2
// #include "stb_divide.h"
//
// Other source files should just include stb_divide.h
//
// Note 1: On platforms/compilers that you know signed C division
// truncates, you can #define C_INTEGER_DIVISION_TRUNCATES.
//
// Note 2: On platforms/compilers that you know signed C division
// floors (rounds to negative infinity), you can #define
// C_INTEGER_DIVISION_FLOORS.
//
// You can #define STB_DIVIDE_TEST in which case the implementation
// will generate a main() and compiling the result will create a
// program that tests the implementation. Run it with no arguments
// and any output indicates an error; run it with any argument and
// it will also print the test results. Define STB_DIVIDE_TEST_64
// to a 64-bit integer type to avoid overflows in the result-checking
// which give false negatives.
//
// ABOUT
//
// This file provides three different consistent divide/mod pairs
// implemented on top of arbitrary C/C++ division, including correct
// handling of overflow of intermediate calculations:
//
// trunc: a/b truncates to 0, a%b has same sign as a
// floor: a/b truncates to -inf, a%b has same sign as b
// eucl: a/b truncates to sign(b)*inf, a%b is non-negative
//
// Not necessarily optimal; I tried to keep it generally efficient,
// but there may be better ways.
//
// Briefly, for those who are not familiar with the problem, we note
// the reason these divides exist and are interesting:
//
// 'trunc' is easy to implement in hardware (strip the signs,
// compute, reapply the signs), thus is commonly defined
// by many languages (including C99)
//
// 'floor' is simple to define and better behaved than trunc;
// for example it divides integers into fixed-size buckets
// without an extra-wide bucket at 0, and for a fixed
// divisor N there are only |N| possible moduli.
//
// 'eucl' guarantees fixed-sized buckets *and* a non-negative
// modulus and defines division to be whatever is needed
// to achieve that result.
//
// See "The Euclidean definition of the functions div and mod"
// by Raymond Boute (1992), or "Division and Modulus for Computer
// Scientists" by Daan Leijen (2001)
//
// We assume of the built-in C division:
// (a) modulus is the remainder for the corresponding division
// (b) a/b truncates if a and b are the same sign
//
// Property (a) requires (a/b)*b + (a%b)==a, and is required by C.
// Property (b) seems to be true of all hardware but is *not* satisfied
// by the euclidean division operator we define, so it's possibly not
// always true. If any such platform turns up, we can add more cases.
// (Possibly only stb_div_trunc currently relies on property (b).)
//
// LICENSE
//
// See end of file for license information.
#ifndef INCLUDE_STB_DIVIDE_H
#define INCLUDE_STB_DIVIDE_H
#ifdef __cplusplus
extern "C" {
#endif
extern int stb_div_trunc(int value_to_be_divided, int value_to_divide_by);
extern int stb_div_floor(int value_to_be_divided, int value_to_divide_by);
extern int stb_div_eucl (int value_to_be_divided, int value_to_divide_by);
extern int stb_mod_trunc(int value_to_be_divided, int value_to_divide_by);
extern int stb_mod_floor(int value_to_be_divided, int value_to_divide_by);
extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by);
#ifdef __cplusplus
}
#endif
#ifdef STB_DIVIDE_IMPLEMENTATION
#if defined(__STDC_VERSION) && __STDC_VERSION__ >= 19901
#ifndef C_INTEGER_DIVISION_TRUNCATES
#define C_INTEGER_DIVISION_TRUNCATES
#endif
#endif
#ifndef INT_MIN
#include <limits.h> // if you have no limits.h, #define INT_MIN yourself
#endif
// the following macros are designed to allow testing
// other platforms by simulating them
#ifndef STB_DIVIDE_TEST_FLOOR
#define stb__div(a,b) ((a)/(b))
#define stb__mod(a,b) ((a)%(b))
#else
// implement floor-style divide on trunc platform
#ifndef C_INTEGER_DIVISION_TRUNCATES
#error "floor test requires truncating division"
#endif
#undef C_INTEGER_DIVISION_TRUNCATES
int stb__div(int v1, int v2)
{
int q = v1/v2, r = v1%v2;
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
return q-1;
else
return q;
}
int stb__mod(int v1, int v2)
{
int r = v1%v2;
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
return r+v2;
else
return r;
}
#endif
int stb_div_trunc(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_TRUNCATES
return v1/v2;
#else
if (v1 >= 0 && v2 <= 0)
return -stb__div(-v1,v2); // both negative to avoid overflow
if (v1 <= 0 && v2 >= 0)
if (v1 != INT_MIN)
return -stb__div(v1,-v2); // both negative to avoid overflow
else
return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point
else
return v1/v2; // same sign, so expect truncation
#endif
}
int stb_div_floor(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_FLOORS
return v1/v2;
#else
if (v1 >= 0 && v2 < 0) {
if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows
return -stb__div((v2+1)-v1,v2); // nope, so just compute it
else
return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0);
}
if (v1 < 0 && v2 >= 0) {
if (v1 != INT_MIN) {
if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows
return -stb__div((v1+1)-v2,-v2); // nope, so just compute it
else
return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0);
} else // it must be possible to compute -(v1+v2) without overflowing
return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1);
} else
return v1/v2; // same sign, so expect truncation
#endif
}
int stb_div_eucl(int v1, int v2)
{
int q,r;
#ifdef C_INTEGER_DIVISION_TRUNCATES
q = v1/v2;
r = v1%v2;
#else
// handle every quadrant separately, since we can't rely on q and r flor
if (v1 >= 0)
if (v2 >= 0)
return stb__div(v1,v2);
else if (v2 != INT_MIN)
q = -stb__div(v1,-v2), r = stb__mod(v1,-v2);
else
q = 0, r = v1;
else if (v1 != INT_MIN)
if (v2 >= 0)
q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2);
else if (v2 != INT_MIN)
q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2);
else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2
q = 1, r = v1-q*v2;
else // if v1 is INT_MIN, we have to move away from overflow place
if (v2 >= 0)
q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2);
else if (v2 != INT_MIN)
q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2);
else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow
q = 1, r = 0;
#endif
if (r >= 0)
return q;
else
return q + (v2 > 0 ? -1 : 1);
}
int stb_mod_trunc(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_TRUNCATES
return v1%v2;
#else
if (v1 >= 0) { // modulus result should always be positive
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
else
return r - (v2 < 0 ? v2 : -v2);
} else { // modulus result should always be negative
int r = stb__mod(v1,v2);
if (r <= 0)
return r;
else
return r + (v2 < 0 ? v2 : -v2);
}
#endif
}
int stb_mod_floor(int v1, int v2)
{
#ifdef C_INTEGER_DIVISION_FLOORS
return v1%v2;
#else
if (v2 >= 0) { // result should always be positive
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
else
return r + v2;
} else { // result should always be negative
int r = stb__mod(v1,v2);
if (r <= 0)
return r;
else
return r + v2;
}
#endif
}
int stb_mod_eucl(int v1, int v2)
{
int r = stb__mod(v1,v2);
if (r >= 0)
return r;
else
return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow]
}
#ifdef STB_DIVIDE_TEST
#include <stdio.h>
#include <math.h>
#include <limits.h>
int show=0;
int err=0;
void stbdiv_check(int q, int r, int a, int b, char *type, int dir)
{
if ((dir > 0 && r < 0) || (dir < 0 && r > 0)) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r);
err++;
} else
if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid
if (r <= -abs(b) || r >= abs(b)) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r);
err++;
}
#ifdef STB_DIVIDE_TEST_64
{
STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b;
if (q64*b64+r64 != a64) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
err++;
}
}
#else
if (q*b+r != a) {
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
err++;
}
#endif
}
void test(int a, int b)
{
int q,r;
if (show) printf("(%+11d,%+d) | ", a,b);
q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b);
if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "trunc",a);
q = stb_div_floor(a,b), r = stb_mod_floor(a,b);
if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "floor",b);
q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b);
if (show) printf("(%+11d,%+2d)\n", q,r); stbdiv_check(q,r,a,b, "euclidean",1);
}
void testh(int a, int b)
{
int q,r;
if (show) printf("(%08x,%08x) |\n", a,b);
q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); stbdiv_check(q,r,a,b, "trunc",a);
if (show) printf(" (%08x,%08x)", q,r);
q = stb_div_floor(a,b), r = stb_mod_floor(a,b); stbdiv_check(q,r,a,b, "floor",b);
if (show) printf(" (%08x,%08x)", q,r);
q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); stbdiv_check(q,r,a,b, "euclidean",1);
if (show) printf(" (%08x,%08x)\n ", q,r);
}
int main(int argc, char **argv)
{
if (argc > 1) show=1;
test(8,3);
test(8,-3);
test(-8,3);
test(-8,-3);
test(1,2);
test(1,-2);
test(-1,2);
test(-1,-2);
test(8,4);
test(8,-4);
test(-8,4);
test(-8,-4);
test(INT_MAX,1);
test(INT_MIN,1);
test(INT_MIN+1,1);
test(INT_MAX,-1);
//test(INT_MIN,-1); // this traps in MSVC, so we leave it untested
test(INT_MIN+1,-1);
test(INT_MIN,-2);
test(INT_MIN+1,2);
test(INT_MIN+1,-2);
test(INT_MAX,2);
test(INT_MAX,-2);
test(INT_MIN+1,2);
test(INT_MIN+1,-2);
test(INT_MIN,2);
test(INT_MIN,-2);
test(INT_MIN,7);
test(INT_MIN,-7);
test(INT_MIN+1,4);
test(INT_MIN+1,-4);
testh(-7, INT_MIN);
testh(-1, INT_MIN);
testh(1, INT_MIN);
testh(7, INT_MIN);
testh(INT_MAX-1, INT_MIN);
testh(INT_MAX, INT_MIN);
testh(INT_MIN, INT_MIN);
testh(INT_MIN+1, INT_MIN);
testh(INT_MAX-1, INT_MAX);
testh(INT_MAX , INT_MAX);
testh(INT_MIN , INT_MAX);
testh(INT_MIN+1, INT_MAX);
return err > 0 ? 1 : 0;
}
#endif // STB_DIVIDE_TEST
#endif // STB_DIVIDE_IMPLEMENTATION
#endif // INCLUDE_STB_DIVIDE_H
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

1895
thirdparty/stb/stb_ds.h vendored Normal file

File diff suppressed because it is too large Load Diff

719
thirdparty/stb/stb_dxt.h vendored Normal file
View File

@@ -0,0 +1,719 @@
// stb_dxt.h - v1.12 - DXT1/DXT5 compressor - public domain
// original by fabian "ryg" giesen - ported to C by stb
// use '#define STB_DXT_IMPLEMENTATION' before including to create the implementation
//
// USAGE:
// call stb_compress_dxt_block() for every block (you must pad)
// source should be a 4x4 block of RGBA data in row-major order;
// Alpha channel is not stored if you specify alpha=0 (but you
// must supply some constant alpha in the alpha channel).
// You can turn on dithering and "high quality" using mode.
//
// version history:
// v1.12 - (ryg) fix bug in single-color table generator
// v1.11 - (ryg) avoid racy global init, better single-color tables, remove dither
// v1.10 - (i.c) various small quality improvements
// v1.09 - (stb) update documentation re: surprising alpha channel requirement
// v1.08 - (stb) fix bug in dxt-with-alpha block
// v1.07 - (stb) bc4; allow not using libc; add STB_DXT_STATIC
// v1.06 - (stb) fix to known-broken 1.05
// v1.05 - (stb) support bc5/3dc (Arvids Kokins), use extern "C" in C++ (Pavel Krajcevski)
// v1.04 - (ryg) default to no rounding bias for lerped colors (as per S3TC/DX10 spec);
// single color match fix (allow for inexact color interpolation);
// optimal DXT5 index finder; "high quality" mode that runs multiple refinement steps.
// v1.03 - (stb) endianness support
// v1.02 - (stb) fix alpha encoding bug
// v1.01 - (stb) fix bug converting to RGB that messed up quality, thanks ryg & cbloom
// v1.00 - (stb) first release
//
// contributors:
// Rich Geldreich (more accurate index selection)
// Kevin Schmidt (#defines for "freestanding" compilation)
// github:ppiastucki (BC4 support)
// Ignacio Castano - improve DXT endpoint quantization
// Alan Hickman - static table initialization
//
// LICENSE
//
// See end of file for license information.
#ifndef STB_INCLUDE_STB_DXT_H
#define STB_INCLUDE_STB_DXT_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef STB_DXT_STATIC
#define STBDDEF static
#else
#define STBDDEF extern
#endif
// compression mode (bitflags)
#define STB_DXT_NORMAL 0
#define STB_DXT_DITHER 1 // use dithering. was always dubious, now deprecated. does nothing!
#define STB_DXT_HIGHQUAL 2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower.
STBDDEF void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src_rgba_four_bytes_per_pixel, int alpha, int mode);
STBDDEF void stb_compress_bc4_block(unsigned char *dest, const unsigned char *src_r_one_byte_per_pixel);
STBDDEF void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src_rg_two_byte_per_pixel);
#define STB_COMPRESS_DXT_BLOCK
#ifdef __cplusplus
}
#endif
#endif // STB_INCLUDE_STB_DXT_H
#ifdef STB_DXT_IMPLEMENTATION
// configuration options for DXT encoder. set them in the project/makefile or just define
// them at the top.
// STB_DXT_USE_ROUNDING_BIAS
// use a rounding bias during color interpolation. this is closer to what "ideal"
// interpolation would do but doesn't match the S3TC/DX10 spec. old versions (pre-1.03)
// implicitly had this turned on.
//
// in case you're targeting a specific type of hardware (e.g. console programmers):
// NVidia and Intel GPUs (as of 2010) as well as DX9 ref use DXT decoders that are closer
// to STB_DXT_USE_ROUNDING_BIAS. AMD/ATI, S3 and DX10 ref are closer to rounding with no bias.
// you also see "(a*5 + b*3) / 8" on some old GPU designs.
// #define STB_DXT_USE_ROUNDING_BIAS
#include <stdlib.h>
#if !defined(STBD_FABS)
#include <math.h>
#endif
#ifndef STBD_FABS
#define STBD_FABS(x) fabs(x)
#endif
static const unsigned char stb__OMatch5[256][2] = {
{ 0, 0 }, { 0, 0 }, { 0, 1 }, { 0, 1 }, { 1, 0 }, { 1, 0 }, { 1, 0 }, { 1, 1 },
{ 1, 1 }, { 1, 1 }, { 1, 2 }, { 0, 4 }, { 2, 1 }, { 2, 1 }, { 2, 1 }, { 2, 2 },
{ 2, 2 }, { 2, 2 }, { 2, 3 }, { 1, 5 }, { 3, 2 }, { 3, 2 }, { 4, 0 }, { 3, 3 },
{ 3, 3 }, { 3, 3 }, { 3, 4 }, { 3, 4 }, { 3, 4 }, { 3, 5 }, { 4, 3 }, { 4, 3 },
{ 5, 2 }, { 4, 4 }, { 4, 4 }, { 4, 5 }, { 4, 5 }, { 5, 4 }, { 5, 4 }, { 5, 4 },
{ 6, 3 }, { 5, 5 }, { 5, 5 }, { 5, 6 }, { 4, 8 }, { 6, 5 }, { 6, 5 }, { 6, 5 },
{ 6, 6 }, { 6, 6 }, { 6, 6 }, { 6, 7 }, { 5, 9 }, { 7, 6 }, { 7, 6 }, { 8, 4 },
{ 7, 7 }, { 7, 7 }, { 7, 7 }, { 7, 8 }, { 7, 8 }, { 7, 8 }, { 7, 9 }, { 8, 7 },
{ 8, 7 }, { 9, 6 }, { 8, 8 }, { 8, 8 }, { 8, 9 }, { 8, 9 }, { 9, 8 }, { 9, 8 },
{ 9, 8 }, { 10, 7 }, { 9, 9 }, { 9, 9 }, { 9, 10 }, { 8, 12 }, { 10, 9 }, { 10, 9 },
{ 10, 9 }, { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 11 }, { 9, 13 }, { 11, 10 }, { 11, 10 },
{ 12, 8 }, { 11, 11 }, { 11, 11 }, { 11, 11 }, { 11, 12 }, { 11, 12 }, { 11, 12 }, { 11, 13 },
{ 12, 11 }, { 12, 11 }, { 13, 10 }, { 12, 12 }, { 12, 12 }, { 12, 13 }, { 12, 13 }, { 13, 12 },
{ 13, 12 }, { 13, 12 }, { 14, 11 }, { 13, 13 }, { 13, 13 }, { 13, 14 }, { 12, 16 }, { 14, 13 },
{ 14, 13 }, { 14, 13 }, { 14, 14 }, { 14, 14 }, { 14, 14 }, { 14, 15 }, { 13, 17 }, { 15, 14 },
{ 15, 14 }, { 16, 12 }, { 15, 15 }, { 15, 15 }, { 15, 15 }, { 15, 16 }, { 15, 16 }, { 15, 16 },
{ 15, 17 }, { 16, 15 }, { 16, 15 }, { 17, 14 }, { 16, 16 }, { 16, 16 }, { 16, 17 }, { 16, 17 },
{ 17, 16 }, { 17, 16 }, { 17, 16 }, { 18, 15 }, { 17, 17 }, { 17, 17 }, { 17, 18 }, { 16, 20 },
{ 18, 17 }, { 18, 17 }, { 18, 17 }, { 18, 18 }, { 18, 18 }, { 18, 18 }, { 18, 19 }, { 17, 21 },
{ 19, 18 }, { 19, 18 }, { 20, 16 }, { 19, 19 }, { 19, 19 }, { 19, 19 }, { 19, 20 }, { 19, 20 },
{ 19, 20 }, { 19, 21 }, { 20, 19 }, { 20, 19 }, { 21, 18 }, { 20, 20 }, { 20, 20 }, { 20, 21 },
{ 20, 21 }, { 21, 20 }, { 21, 20 }, { 21, 20 }, { 22, 19 }, { 21, 21 }, { 21, 21 }, { 21, 22 },
{ 20, 24 }, { 22, 21 }, { 22, 21 }, { 22, 21 }, { 22, 22 }, { 22, 22 }, { 22, 22 }, { 22, 23 },
{ 21, 25 }, { 23, 22 }, { 23, 22 }, { 24, 20 }, { 23, 23 }, { 23, 23 }, { 23, 23 }, { 23, 24 },
{ 23, 24 }, { 23, 24 }, { 23, 25 }, { 24, 23 }, { 24, 23 }, { 25, 22 }, { 24, 24 }, { 24, 24 },
{ 24, 25 }, { 24, 25 }, { 25, 24 }, { 25, 24 }, { 25, 24 }, { 26, 23 }, { 25, 25 }, { 25, 25 },
{ 25, 26 }, { 24, 28 }, { 26, 25 }, { 26, 25 }, { 26, 25 }, { 26, 26 }, { 26, 26 }, { 26, 26 },
{ 26, 27 }, { 25, 29 }, { 27, 26 }, { 27, 26 }, { 28, 24 }, { 27, 27 }, { 27, 27 }, { 27, 27 },
{ 27, 28 }, { 27, 28 }, { 27, 28 }, { 27, 29 }, { 28, 27 }, { 28, 27 }, { 29, 26 }, { 28, 28 },
{ 28, 28 }, { 28, 29 }, { 28, 29 }, { 29, 28 }, { 29, 28 }, { 29, 28 }, { 30, 27 }, { 29, 29 },
{ 29, 29 }, { 29, 30 }, { 29, 30 }, { 30, 29 }, { 30, 29 }, { 30, 29 }, { 30, 30 }, { 30, 30 },
{ 30, 30 }, { 30, 31 }, { 30, 31 }, { 31, 30 }, { 31, 30 }, { 31, 30 }, { 31, 31 }, { 31, 31 },
};
static const unsigned char stb__OMatch6[256][2] = {
{ 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 }, { 1, 1 }, { 1, 2 }, { 2, 1 }, { 2, 2 },
{ 2, 2 }, { 2, 3 }, { 3, 2 }, { 3, 3 }, { 3, 3 }, { 3, 4 }, { 4, 3 }, { 4, 4 },
{ 4, 4 }, { 4, 5 }, { 5, 4 }, { 5, 5 }, { 5, 5 }, { 5, 6 }, { 6, 5 }, { 6, 6 },
{ 6, 6 }, { 6, 7 }, { 7, 6 }, { 7, 7 }, { 7, 7 }, { 7, 8 }, { 8, 7 }, { 8, 8 },
{ 8, 8 }, { 8, 9 }, { 9, 8 }, { 9, 9 }, { 9, 9 }, { 9, 10 }, { 10, 9 }, { 10, 10 },
{ 10, 10 }, { 10, 11 }, { 11, 10 }, { 8, 16 }, { 11, 11 }, { 11, 12 }, { 12, 11 }, { 9, 17 },
{ 12, 12 }, { 12, 13 }, { 13, 12 }, { 11, 16 }, { 13, 13 }, { 13, 14 }, { 14, 13 }, { 12, 17 },
{ 14, 14 }, { 14, 15 }, { 15, 14 }, { 14, 16 }, { 15, 15 }, { 15, 16 }, { 16, 14 }, { 16, 15 },
{ 17, 14 }, { 16, 16 }, { 16, 17 }, { 17, 16 }, { 18, 15 }, { 17, 17 }, { 17, 18 }, { 18, 17 },
{ 20, 14 }, { 18, 18 }, { 18, 19 }, { 19, 18 }, { 21, 15 }, { 19, 19 }, { 19, 20 }, { 20, 19 },
{ 20, 20 }, { 20, 20 }, { 20, 21 }, { 21, 20 }, { 21, 21 }, { 21, 21 }, { 21, 22 }, { 22, 21 },
{ 22, 22 }, { 22, 22 }, { 22, 23 }, { 23, 22 }, { 23, 23 }, { 23, 23 }, { 23, 24 }, { 24, 23 },
{ 24, 24 }, { 24, 24 }, { 24, 25 }, { 25, 24 }, { 25, 25 }, { 25, 25 }, { 25, 26 }, { 26, 25 },
{ 26, 26 }, { 26, 26 }, { 26, 27 }, { 27, 26 }, { 24, 32 }, { 27, 27 }, { 27, 28 }, { 28, 27 },
{ 25, 33 }, { 28, 28 }, { 28, 29 }, { 29, 28 }, { 27, 32 }, { 29, 29 }, { 29, 30 }, { 30, 29 },
{ 28, 33 }, { 30, 30 }, { 30, 31 }, { 31, 30 }, { 30, 32 }, { 31, 31 }, { 31, 32 }, { 32, 30 },
{ 32, 31 }, { 33, 30 }, { 32, 32 }, { 32, 33 }, { 33, 32 }, { 34, 31 }, { 33, 33 }, { 33, 34 },
{ 34, 33 }, { 36, 30 }, { 34, 34 }, { 34, 35 }, { 35, 34 }, { 37, 31 }, { 35, 35 }, { 35, 36 },
{ 36, 35 }, { 36, 36 }, { 36, 36 }, { 36, 37 }, { 37, 36 }, { 37, 37 }, { 37, 37 }, { 37, 38 },
{ 38, 37 }, { 38, 38 }, { 38, 38 }, { 38, 39 }, { 39, 38 }, { 39, 39 }, { 39, 39 }, { 39, 40 },
{ 40, 39 }, { 40, 40 }, { 40, 40 }, { 40, 41 }, { 41, 40 }, { 41, 41 }, { 41, 41 }, { 41, 42 },
{ 42, 41 }, { 42, 42 }, { 42, 42 }, { 42, 43 }, { 43, 42 }, { 40, 48 }, { 43, 43 }, { 43, 44 },
{ 44, 43 }, { 41, 49 }, { 44, 44 }, { 44, 45 }, { 45, 44 }, { 43, 48 }, { 45, 45 }, { 45, 46 },
{ 46, 45 }, { 44, 49 }, { 46, 46 }, { 46, 47 }, { 47, 46 }, { 46, 48 }, { 47, 47 }, { 47, 48 },
{ 48, 46 }, { 48, 47 }, { 49, 46 }, { 48, 48 }, { 48, 49 }, { 49, 48 }, { 50, 47 }, { 49, 49 },
{ 49, 50 }, { 50, 49 }, { 52, 46 }, { 50, 50 }, { 50, 51 }, { 51, 50 }, { 53, 47 }, { 51, 51 },
{ 51, 52 }, { 52, 51 }, { 52, 52 }, { 52, 52 }, { 52, 53 }, { 53, 52 }, { 53, 53 }, { 53, 53 },
{ 53, 54 }, { 54, 53 }, { 54, 54 }, { 54, 54 }, { 54, 55 }, { 55, 54 }, { 55, 55 }, { 55, 55 },
{ 55, 56 }, { 56, 55 }, { 56, 56 }, { 56, 56 }, { 56, 57 }, { 57, 56 }, { 57, 57 }, { 57, 57 },
{ 57, 58 }, { 58, 57 }, { 58, 58 }, { 58, 58 }, { 58, 59 }, { 59, 58 }, { 59, 59 }, { 59, 59 },
{ 59, 60 }, { 60, 59 }, { 60, 60 }, { 60, 60 }, { 60, 61 }, { 61, 60 }, { 61, 61 }, { 61, 61 },
{ 61, 62 }, { 62, 61 }, { 62, 62 }, { 62, 62 }, { 62, 63 }, { 63, 62 }, { 63, 63 }, { 63, 63 },
};
static int stb__Mul8Bit(int a, int b)
{
int t = a*b + 128;
return (t + (t >> 8)) >> 8;
}
static void stb__From16Bit(unsigned char *out, unsigned short v)
{
int rv = (v & 0xf800) >> 11;
int gv = (v & 0x07e0) >> 5;
int bv = (v & 0x001f) >> 0;
// expand to 8 bits via bit replication
out[0] = (rv * 33) >> 2;
out[1] = (gv * 65) >> 4;
out[2] = (bv * 33) >> 2;
out[3] = 0;
}
static unsigned short stb__As16Bit(int r, int g, int b)
{
return (unsigned short)((stb__Mul8Bit(r,31) << 11) + (stb__Mul8Bit(g,63) << 5) + stb__Mul8Bit(b,31));
}
// linear interpolation at 1/3 point between a and b, using desired rounding type
static int stb__Lerp13(int a, int b)
{
#ifdef STB_DXT_USE_ROUNDING_BIAS
// with rounding bias
return a + stb__Mul8Bit(b-a, 0x55);
#else
// without rounding bias
// replace "/ 3" by "* 0xaaab) >> 17" if your compiler sucks or you really need every ounce of speed.
return (2*a + b) / 3;
#endif
}
// lerp RGB color
static void stb__Lerp13RGB(unsigned char *out, unsigned char *p1, unsigned char *p2)
{
out[0] = (unsigned char)stb__Lerp13(p1[0], p2[0]);
out[1] = (unsigned char)stb__Lerp13(p1[1], p2[1]);
out[2] = (unsigned char)stb__Lerp13(p1[2], p2[2]);
}
/****************************************************************************/
static void stb__EvalColors(unsigned char *color,unsigned short c0,unsigned short c1)
{
stb__From16Bit(color+ 0, c0);
stb__From16Bit(color+ 4, c1);
stb__Lerp13RGB(color+ 8, color+0, color+4);
stb__Lerp13RGB(color+12, color+4, color+0);
}
// The color matching function
static unsigned int stb__MatchColorsBlock(unsigned char *block, unsigned char *color)
{
unsigned int mask = 0;
int dirr = color[0*4+0] - color[1*4+0];
int dirg = color[0*4+1] - color[1*4+1];
int dirb = color[0*4+2] - color[1*4+2];
int dots[16];
int stops[4];
int i;
int c0Point, halfPoint, c3Point;
for(i=0;i<16;i++)
dots[i] = block[i*4+0]*dirr + block[i*4+1]*dirg + block[i*4+2]*dirb;
for(i=0;i<4;i++)
stops[i] = color[i*4+0]*dirr + color[i*4+1]*dirg + color[i*4+2]*dirb;
// think of the colors as arranged on a line; project point onto that line, then choose
// next color out of available ones. we compute the crossover points for "best color in top
// half"/"best in bottom half" and then the same inside that subinterval.
//
// relying on this 1d approximation isn't always optimal in terms of euclidean distance,
// but it's very close and a lot faster.
// http://cbloomrants.blogspot.com/2008/12/12-08-08-dxtc-summary.html
c0Point = (stops[1] + stops[3]);
halfPoint = (stops[3] + stops[2]);
c3Point = (stops[2] + stops[0]);
for (i=15;i>=0;i--) {
int dot = dots[i]*2;
mask <<= 2;
if(dot < halfPoint)
mask |= (dot < c0Point) ? 1 : 3;
else
mask |= (dot < c3Point) ? 2 : 0;
}
return mask;
}
// The color optimization function. (Clever code, part 1)
static void stb__OptimizeColorsBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16)
{
int mind,maxd;
unsigned char *minp, *maxp;
double magn;
int v_r,v_g,v_b;
static const int nIterPower = 4;
float covf[6],vfr,vfg,vfb;
// determine color distribution
int cov[6];
int mu[3],min[3],max[3];
int ch,i,iter;
for(ch=0;ch<3;ch++)
{
const unsigned char *bp = ((const unsigned char *) block) + ch;
int muv,minv,maxv;
muv = minv = maxv = bp[0];
for(i=4;i<64;i+=4)
{
muv += bp[i];
if (bp[i] < minv) minv = bp[i];
else if (bp[i] > maxv) maxv = bp[i];
}
mu[ch] = (muv + 8) >> 4;
min[ch] = minv;
max[ch] = maxv;
}
// determine covariance matrix
for (i=0;i<6;i++)
cov[i] = 0;
for (i=0;i<16;i++)
{
int r = block[i*4+0] - mu[0];
int g = block[i*4+1] - mu[1];
int b = block[i*4+2] - mu[2];
cov[0] += r*r;
cov[1] += r*g;
cov[2] += r*b;
cov[3] += g*g;
cov[4] += g*b;
cov[5] += b*b;
}
// convert covariance matrix to float, find principal axis via power iter
for(i=0;i<6;i++)
covf[i] = cov[i] / 255.0f;
vfr = (float) (max[0] - min[0]);
vfg = (float) (max[1] - min[1]);
vfb = (float) (max[2] - min[2]);
for(iter=0;iter<nIterPower;iter++)
{
float r = vfr*covf[0] + vfg*covf[1] + vfb*covf[2];
float g = vfr*covf[1] + vfg*covf[3] + vfb*covf[4];
float b = vfr*covf[2] + vfg*covf[4] + vfb*covf[5];
vfr = r;
vfg = g;
vfb = b;
}
magn = STBD_FABS(vfr);
if (STBD_FABS(vfg) > magn) magn = STBD_FABS(vfg);
if (STBD_FABS(vfb) > magn) magn = STBD_FABS(vfb);
if(magn < 4.0f) { // too small, default to luminance
v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000.
v_g = 587;
v_b = 114;
} else {
magn = 512.0 / magn;
v_r = (int) (vfr * magn);
v_g = (int) (vfg * magn);
v_b = (int) (vfb * magn);
}
minp = maxp = block;
mind = maxd = block[0]*v_r + block[1]*v_g + block[2]*v_b;
// Pick colors at extreme points
for(i=1;i<16;i++)
{
int dot = block[i*4+0]*v_r + block[i*4+1]*v_g + block[i*4+2]*v_b;
if (dot < mind) {
mind = dot;
minp = block+i*4;
}
if (dot > maxd) {
maxd = dot;
maxp = block+i*4;
}
}
*pmax16 = stb__As16Bit(maxp[0],maxp[1],maxp[2]);
*pmin16 = stb__As16Bit(minp[0],minp[1],minp[2]);
}
static const float stb__midpoints5[32] = {
0.015686f, 0.047059f, 0.078431f, 0.111765f, 0.145098f, 0.176471f, 0.207843f, 0.241176f, 0.274510f, 0.305882f, 0.337255f, 0.370588f, 0.403922f, 0.435294f, 0.466667f, 0.5f,
0.533333f, 0.564706f, 0.596078f, 0.629412f, 0.662745f, 0.694118f, 0.725490f, 0.758824f, 0.792157f, 0.823529f, 0.854902f, 0.888235f, 0.921569f, 0.952941f, 0.984314f, 1.0f
};
static const float stb__midpoints6[64] = {
0.007843f, 0.023529f, 0.039216f, 0.054902f, 0.070588f, 0.086275f, 0.101961f, 0.117647f, 0.133333f, 0.149020f, 0.164706f, 0.180392f, 0.196078f, 0.211765f, 0.227451f, 0.245098f,
0.262745f, 0.278431f, 0.294118f, 0.309804f, 0.325490f, 0.341176f, 0.356863f, 0.372549f, 0.388235f, 0.403922f, 0.419608f, 0.435294f, 0.450980f, 0.466667f, 0.482353f, 0.500000f,
0.517647f, 0.533333f, 0.549020f, 0.564706f, 0.580392f, 0.596078f, 0.611765f, 0.627451f, 0.643137f, 0.658824f, 0.674510f, 0.690196f, 0.705882f, 0.721569f, 0.737255f, 0.754902f,
0.772549f, 0.788235f, 0.803922f, 0.819608f, 0.835294f, 0.850980f, 0.866667f, 0.882353f, 0.898039f, 0.913725f, 0.929412f, 0.945098f, 0.960784f, 0.976471f, 0.992157f, 1.0f
};
static unsigned short stb__Quantize5(float x)
{
unsigned short q;
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
q = (unsigned short)(x * 31);
q += (x > stb__midpoints5[q]);
return q;
}
static unsigned short stb__Quantize6(float x)
{
unsigned short q;
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
q = (unsigned short)(x * 63);
q += (x > stb__midpoints6[q]);
return q;
}
// The refinement function. (Clever code, part 2)
// Tries to optimize colors to suit block contents better.
// (By solving a least squares system via normal equations+Cramer's rule)
static int stb__RefineBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16, unsigned int mask)
{
static const int w1Tab[4] = { 3,0,2,1 };
static const int prods[4] = { 0x090000,0x000900,0x040102,0x010402 };
// ^some magic to save a lot of multiplies in the accumulating loop...
// (precomputed products of weights for least squares system, accumulated inside one 32-bit register)
float f;
unsigned short oldMin, oldMax, min16, max16;
int i, akku = 0, xx,xy,yy;
int At1_r,At1_g,At1_b;
int At2_r,At2_g,At2_b;
unsigned int cm = mask;
oldMin = *pmin16;
oldMax = *pmax16;
if((mask ^ (mask<<2)) < 4) // all pixels have the same index?
{
// yes, linear system would be singular; solve using optimal
// single-color match on average color
int r = 8, g = 8, b = 8;
for (i=0;i<16;++i) {
r += block[i*4+0];
g += block[i*4+1];
b += block[i*4+2];
}
r >>= 4; g >>= 4; b >>= 4;
max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0];
min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1];
} else {
At1_r = At1_g = At1_b = 0;
At2_r = At2_g = At2_b = 0;
for (i=0;i<16;++i,cm>>=2) {
int step = cm&3;
int w1 = w1Tab[step];
int r = block[i*4+0];
int g = block[i*4+1];
int b = block[i*4+2];
akku += prods[step];
At1_r += w1*r;
At1_g += w1*g;
At1_b += w1*b;
At2_r += r;
At2_g += g;
At2_b += b;
}
At2_r = 3*At2_r - At1_r;
At2_g = 3*At2_g - At1_g;
At2_b = 3*At2_b - At1_b;
// extract solutions and decide solvability
xx = akku >> 16;
yy = (akku >> 8) & 0xff;
xy = (akku >> 0) & 0xff;
f = 3.0f / 255.0f / (xx*yy - xy*xy);
max16 = stb__Quantize5((At1_r*yy - At2_r * xy) * f) << 11;
max16 |= stb__Quantize6((At1_g*yy - At2_g * xy) * f) << 5;
max16 |= stb__Quantize5((At1_b*yy - At2_b * xy) * f) << 0;
min16 = stb__Quantize5((At2_r*xx - At1_r * xy) * f) << 11;
min16 |= stb__Quantize6((At2_g*xx - At1_g * xy) * f) << 5;
min16 |= stb__Quantize5((At2_b*xx - At1_b * xy) * f) << 0;
}
*pmin16 = min16;
*pmax16 = max16;
return oldMin != min16 || oldMax != max16;
}
// Color block compression
static void stb__CompressColorBlock(unsigned char *dest, unsigned char *block, int mode)
{
unsigned int mask;
int i;
int refinecount;
unsigned short max16, min16;
unsigned char color[4*4];
refinecount = (mode & STB_DXT_HIGHQUAL) ? 2 : 1;
// check if block is constant
for (i=1;i<16;i++)
if (((unsigned int *) block)[i] != ((unsigned int *) block)[0])
break;
if(i == 16) { // constant color
int r = block[0], g = block[1], b = block[2];
mask = 0xaaaaaaaa;
max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0];
min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1];
} else {
// first step: PCA+map along principal axis
stb__OptimizeColorsBlock(block,&max16,&min16);
if (max16 != min16) {
stb__EvalColors(color,max16,min16);
mask = stb__MatchColorsBlock(block,color);
} else
mask = 0;
// third step: refine (multiple times if requested)
for (i=0;i<refinecount;i++) {
unsigned int lastmask = mask;
if (stb__RefineBlock(block,&max16,&min16,mask)) {
if (max16 != min16) {
stb__EvalColors(color,max16,min16);
mask = stb__MatchColorsBlock(block,color);
} else {
mask = 0;
break;
}
}
if(mask == lastmask)
break;
}
}
// write the color block
if(max16 < min16)
{
unsigned short t = min16;
min16 = max16;
max16 = t;
mask ^= 0x55555555;
}
dest[0] = (unsigned char) (max16);
dest[1] = (unsigned char) (max16 >> 8);
dest[2] = (unsigned char) (min16);
dest[3] = (unsigned char) (min16 >> 8);
dest[4] = (unsigned char) (mask);
dest[5] = (unsigned char) (mask >> 8);
dest[6] = (unsigned char) (mask >> 16);
dest[7] = (unsigned char) (mask >> 24);
}
// Alpha block compression (this is easy for a change)
static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src, int stride)
{
int i,dist,bias,dist4,dist2,bits,mask;
// find min/max color
int mn,mx;
mn = mx = src[0];
for (i=1;i<16;i++)
{
if (src[i*stride] < mn) mn = src[i*stride];
else if (src[i*stride] > mx) mx = src[i*stride];
}
// encode them
dest[0] = (unsigned char)mx;
dest[1] = (unsigned char)mn;
dest += 2;
// determine bias and emit color indices
// given the choice of mx/mn, these indices are optimal:
// http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/
dist = mx-mn;
dist4 = dist*4;
dist2 = dist*2;
bias = (dist < 8) ? (dist - 1) : (dist/2 + 2);
bias -= mn * 7;
bits = 0,mask=0;
for (i=0;i<16;i++) {
int a = src[i*stride]*7 + bias;
int ind,t;
// select index. this is a "linear scale" lerp factor between 0 (val=min) and 7 (val=max).
t = (a >= dist4) ? -1 : 0; ind = t & 4; a -= dist4 & t;
t = (a >= dist2) ? -1 : 0; ind += t & 2; a -= dist2 & t;
ind += (a >= dist);
// turn linear scale into DXT index (0/1 are extremal pts)
ind = -ind & 7;
ind ^= (2 > ind);
// write index
mask |= ind << bits;
if((bits += 3) >= 8) {
*dest++ = (unsigned char)mask;
mask >>= 8;
bits -= 8;
}
}
}
void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode)
{
unsigned char data[16][4];
if (alpha) {
int i;
stb__CompressAlphaBlock(dest,(unsigned char*) src+3, 4);
dest += 8;
// make a new copy of the data in which alpha is opaque,
// because code uses a fast test for color constancy
memcpy(data, src, 4*16);
for (i=0; i < 16; ++i)
data[i][3] = 255;
src = &data[0][0];
}
stb__CompressColorBlock(dest,(unsigned char*) src,mode);
}
void stb_compress_bc4_block(unsigned char *dest, const unsigned char *src)
{
stb__CompressAlphaBlock(dest,(unsigned char*) src, 1);
}
void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src)
{
stb__CompressAlphaBlock(dest,(unsigned char*) src,2);
stb__CompressAlphaBlock(dest + 8,(unsigned char*) src+1,2);
}
#endif // STB_DXT_IMPLEMENTATION
// Compile with STB_DXT_IMPLEMENTATION and STB_DXT_GENERATE_TABLES
// defined to generate the tables above.
#ifdef STB_DXT_GENERATE_TABLES
#include <stdio.h>
int main()
{
int i, j;
const char *omatch_names[] = { "stb__OMatch5", "stb__OMatch6" };
int dequant_mults[2] = { 33*4, 65 }; // .4 fixed-point dequant multipliers
// optimal endpoint tables
for (i = 0; i < 2; ++i) {
int dequant = dequant_mults[i];
int size = i ? 64 : 32;
printf("static const unsigned char %s[256][2] = {\n", omatch_names[i]);
for (int j = 0; j < 256; ++j) {
int mn, mx;
int best_mn = 0, best_mx = 0;
int best_err = 256 * 100;
for (mn=0;mn<size;mn++) {
for (mx=0;mx<size;mx++) {
int mine = (mn * dequant) >> 4;
int maxe = (mx * dequant) >> 4;
int err = abs(stb__Lerp13(maxe, mine) - j) * 100;
// DX10 spec says that interpolation must be within 3% of "correct" result,
// add this as error term. Normally we'd expect a random distribution of
// +-1.5% error, but nowhere in the spec does it say that the error has to be
// unbiased - better safe than sorry.
err += abs(maxe - mine) * 3;
if(err < best_err) {
best_mn = mn;
best_mx = mx;
best_err = err;
}
}
}
if ((j % 8) == 0) printf(" "); // 2 spaces, third is done below
printf(" { %2d, %2d },", best_mx, best_mn);
if ((j % 8) == 7) printf("\n");
}
printf("};\n");
}
return 0;
}
#endif
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

305
thirdparty/stb/stb_easy_font.h vendored Normal file
View File

@@ -0,0 +1,305 @@
// stb_easy_font.h - v1.1 - bitmap font for 3D rendering - public domain
// Sean Barrett, Feb 2015
//
// Easy-to-deploy,
// reasonably compact,
// extremely inefficient performance-wise,
// crappy-looking,
// ASCII-only,
// bitmap font for use in 3D APIs.
//
// Intended for when you just want to get some text displaying
// in a 3D app as quickly as possible.
//
// Doesn't use any textures, instead builds characters out of quads.
//
// DOCUMENTATION:
//
// int stb_easy_font_width(char *text)
// int stb_easy_font_height(char *text)
//
// Takes a string and returns the horizontal size and the
// vertical size (which can vary if 'text' has newlines).
//
// int stb_easy_font_print(float x, float y,
// char *text, unsigned char color[4],
// void *vertex_buffer, int vbuf_size)
//
// Takes a string (which can contain '\n') and fills out a
// vertex buffer with renderable data to draw the string.
// Output data assumes increasing x is rightwards, increasing y
// is downwards.
//
// The vertex data is divided into quads, i.e. there are four
// vertices in the vertex buffer for each quad.
//
// The vertices are stored in an interleaved format:
//
// x:float
// y:float
// z:float
// color:uint8[4]
//
// You can ignore z and color if you get them from elsewhere
// This format was chosen in the hopes it would make it
// easier for you to reuse existing vertex-buffer-drawing code.
//
// If you pass in NULL for color, it becomes 255,255,255,255.
//
// Returns the number of quads.
//
// If the buffer isn't large enough, it will truncate.
// Expect it to use an average of ~270 bytes per character.
//
// If your API doesn't draw quads, build a reusable index
// list that allows you to render quads as indexed triangles.
//
// void stb_easy_font_spacing(float spacing)
//
// Use positive values to expand the space between characters,
// and small negative values (no smaller than -1.5) to contract
// the space between characters.
//
// E.g. spacing = 1 adds one "pixel" of spacing between the
// characters. spacing = -1 is reasonable but feels a bit too
// compact to me; -0.5 is a reasonable compromise as long as
// you're scaling the font up.
//
// LICENSE
//
// See end of file for license information.
//
// VERSION HISTORY
//
// (2020-02-02) 1.1 make everything static so can compile it in more than one src file
// (2017-01-15) 1.0 space character takes same space as numbers; fix bad spacing of 'f'
// (2016-01-22) 0.7 width() supports multiline text; add height()
// (2015-09-13) 0.6 #include <math.h>; updated license
// (2015-02-01) 0.5 First release
//
// CONTRIBUTORS
//
// github:vassvik -- bug report
// github:podsvirov -- fix multiple definition errors
#if 0
// SAMPLE CODE:
//
// Here's sample code for old OpenGL; it's a lot more complicated
// to make work on modern APIs, and that's your problem.
//
void print_string(float x, float y, char *text, float r, float g, float b)
{
static char buffer[99999]; // ~500 chars
int num_quads;
num_quads = stb_easy_font_print(x, y, text, NULL, buffer, sizeof(buffer));
glColor3f(r,g,b);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 16, buffer);
glDrawArrays(GL_QUADS, 0, num_quads*4);
glDisableClientState(GL_VERTEX_ARRAY);
}
#endif
#ifndef INCLUDE_STB_EASY_FONT_H
#define INCLUDE_STB_EASY_FONT_H
#include <stdlib.h>
#include <math.h>
static struct stb_easy_font_info_struct {
unsigned char advance;
unsigned char h_seg;
unsigned char v_seg;
} stb_easy_font_charinfo[96] = {
{ 6, 0, 0 }, { 3, 0, 0 }, { 5, 1, 1 }, { 7, 1, 4 },
{ 7, 3, 7 }, { 7, 6, 12 }, { 7, 8, 19 }, { 4, 16, 21 },
{ 4, 17, 22 }, { 4, 19, 23 }, { 23, 21, 24 }, { 23, 22, 31 },
{ 20, 23, 34 }, { 22, 23, 36 }, { 19, 24, 36 }, { 21, 25, 36 },
{ 6, 25, 39 }, { 6, 27, 43 }, { 6, 28, 45 }, { 6, 30, 49 },
{ 6, 33, 53 }, { 6, 34, 57 }, { 6, 40, 58 }, { 6, 46, 59 },
{ 6, 47, 62 }, { 6, 55, 64 }, { 19, 57, 68 }, { 20, 59, 68 },
{ 21, 61, 69 }, { 22, 66, 69 }, { 21, 68, 69 }, { 7, 73, 69 },
{ 9, 75, 74 }, { 6, 78, 81 }, { 6, 80, 85 }, { 6, 83, 90 },
{ 6, 85, 91 }, { 6, 87, 95 }, { 6, 90, 96 }, { 7, 92, 97 },
{ 6, 96,102 }, { 5, 97,106 }, { 6, 99,107 }, { 6,100,110 },
{ 6,100,115 }, { 7,101,116 }, { 6,101,121 }, { 6,101,125 },
{ 6,102,129 }, { 7,103,133 }, { 6,104,140 }, { 6,105,145 },
{ 7,107,149 }, { 6,108,151 }, { 7,109,155 }, { 7,109,160 },
{ 7,109,165 }, { 7,118,167 }, { 6,118,172 }, { 4,120,176 },
{ 6,122,177 }, { 4,122,181 }, { 23,124,182 }, { 22,129,182 },
{ 4,130,182 }, { 22,131,183 }, { 6,133,187 }, { 22,135,191 },
{ 6,137,192 }, { 22,139,196 }, { 6,144,197 }, { 22,147,198 },
{ 6,150,202 }, { 19,151,206 }, { 21,152,207 }, { 6,155,209 },
{ 3,160,210 }, { 23,160,211 }, { 22,164,216 }, { 22,165,220 },
{ 22,167,224 }, { 22,169,228 }, { 21,171,232 }, { 21,173,233 },
{ 5,178,233 }, { 22,179,234 }, { 23,180,238 }, { 23,180,243 },
{ 23,180,248 }, { 22,189,248 }, { 22,191,252 }, { 5,196,252 },
{ 3,203,252 }, { 5,203,253 }, { 22,210,253 }, { 0,214,253 },
};
static unsigned char stb_easy_font_hseg[214] = {
97,37,69,84,28,51,2,18,10,49,98,41,65,25,81,105,33,9,97,1,97,37,37,36,
81,10,98,107,3,100,3,99,58,51,4,99,58,8,73,81,10,50,98,8,73,81,4,10,50,
98,8,25,33,65,81,10,50,17,65,97,25,33,25,49,9,65,20,68,1,65,25,49,41,
11,105,13,101,76,10,50,10,50,98,11,99,10,98,11,50,99,11,50,11,99,8,57,
58,3,99,99,107,10,10,11,10,99,11,5,100,41,65,57,41,65,9,17,81,97,3,107,
9,97,1,97,33,25,9,25,41,100,41,26,82,42,98,27,83,42,98,26,51,82,8,41,
35,8,10,26,82,114,42,1,114,8,9,73,57,81,41,97,18,8,8,25,26,26,82,26,82,
26,82,41,25,33,82,26,49,73,35,90,17,81,41,65,57,41,65,25,81,90,114,20,
84,73,57,41,49,25,33,65,81,9,97,1,97,25,33,65,81,57,33,25,41,25,
};
static unsigned char stb_easy_font_vseg[253] = {
4,2,8,10,15,8,15,33,8,15,8,73,82,73,57,41,82,10,82,18,66,10,21,29,1,65,
27,8,27,9,65,8,10,50,97,74,66,42,10,21,57,41,29,25,14,81,73,57,26,8,8,
26,66,3,8,8,15,19,21,90,58,26,18,66,18,105,89,28,74,17,8,73,57,26,21,
8,42,41,42,8,28,22,8,8,30,7,8,8,26,66,21,7,8,8,29,7,7,21,8,8,8,59,7,8,
8,15,29,8,8,14,7,57,43,10,82,7,7,25,42,25,15,7,25,41,15,21,105,105,29,
7,57,57,26,21,105,73,97,89,28,97,7,57,58,26,82,18,57,57,74,8,30,6,8,8,
14,3,58,90,58,11,7,74,43,74,15,2,82,2,42,75,42,10,67,57,41,10,7,2,42,
74,106,15,2,35,8,8,29,7,8,8,59,35,51,8,8,15,35,30,35,8,8,30,7,8,8,60,
36,8,45,7,7,36,8,43,8,44,21,8,8,44,35,8,8,43,23,8,8,43,35,8,8,31,21,15,
20,8,8,28,18,58,89,58,26,21,89,73,89,29,20,8,8,30,7,
};
typedef struct
{
unsigned char c[4];
} stb_easy_font_color;
static int stb_easy_font_draw_segs(float x, float y, unsigned char *segs, int num_segs, int vertical, stb_easy_font_color c, char *vbuf, int vbuf_size, int offset)
{
int i,j;
for (i=0; i < num_segs; ++i) {
int len = segs[i] & 7;
x += (float) ((segs[i] >> 3) & 1);
if (len && offset+64 <= vbuf_size) {
float y0 = y + (float) (segs[i]>>4);
for (j=0; j < 4; ++j) {
* (float *) (vbuf+offset+0) = x + (j==1 || j==2 ? (vertical ? 1 : len) : 0);
* (float *) (vbuf+offset+4) = y0 + ( j >= 2 ? (vertical ? len : 1) : 0);
* (float *) (vbuf+offset+8) = 0.f;
* (stb_easy_font_color *) (vbuf+offset+12) = c;
offset += 16;
}
}
}
return offset;
}
static float stb_easy_font_spacing_val = 0;
static void stb_easy_font_spacing(float spacing)
{
stb_easy_font_spacing_val = spacing;
}
static int stb_easy_font_print(float x, float y, char *text, unsigned char color[4], void *vertex_buffer, int vbuf_size)
{
char *vbuf = (char *) vertex_buffer;
float start_x = x;
int offset = 0;
stb_easy_font_color c = { 255,255,255,255 }; // use structure copying to avoid needing depending on memcpy()
if (color) { c.c[0] = color[0]; c.c[1] = color[1]; c.c[2] = color[2]; c.c[3] = color[3]; }
while (*text && offset < vbuf_size) {
if (*text == '\n') {
y += 12;
x = start_x;
} else {
unsigned char advance = stb_easy_font_charinfo[*text-32].advance;
float y_ch = advance & 16 ? y+1 : y;
int h_seg, v_seg, num_h, num_v;
h_seg = stb_easy_font_charinfo[*text-32 ].h_seg;
v_seg = stb_easy_font_charinfo[*text-32 ].v_seg;
num_h = stb_easy_font_charinfo[*text-32+1].h_seg - h_seg;
num_v = stb_easy_font_charinfo[*text-32+1].v_seg - v_seg;
offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_hseg[h_seg], num_h, 0, c, vbuf, vbuf_size, offset);
offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_vseg[v_seg], num_v, 1, c, vbuf, vbuf_size, offset);
x += advance & 15;
x += stb_easy_font_spacing_val;
}
++text;
}
return (unsigned) offset/64;
}
static int stb_easy_font_width(char *text)
{
float len = 0;
float max_len = 0;
while (*text) {
if (*text == '\n') {
if (len > max_len) max_len = len;
len = 0;
} else {
len += stb_easy_font_charinfo[*text-32].advance & 15;
len += stb_easy_font_spacing_val;
}
++text;
}
if (len > max_len) max_len = len;
return (int) ceil(max_len);
}
static int stb_easy_font_height(char *text)
{
float y = 0;
int nonempty_line=0;
while (*text) {
if (*text == '\n') {
y += 12;
nonempty_line = 0;
} else {
nonempty_line = 1;
}
++text;
}
return (int) ceil(y + (nonempty_line ? 12 : 0));
}
#endif
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

1221
thirdparty/stb/stb_herringbone_wang_tile.h vendored Normal file

File diff suppressed because it is too large Load Diff

680
thirdparty/stb/stb_hexwave.h vendored Normal file
View File

@@ -0,0 +1,680 @@
// stb_hexwave - v0.5 - public domain, initial release 2021-04-01
//
// A flexible anti-aliased (bandlimited) digital audio oscillator.
//
// This library generates waveforms of a variety of shapes made of
// line segments. It does not do envelopes, LFO effects, etc.; it
// merely tries to solve the problem of generating an artifact-free
// morphable digital waveform with a variety of spectra, and leaves
// it to the user to rescale the waveform and mix multiple voices, etc.
//
// Compiling:
//
// In one C/C++ file that #includes this file, do
//
// #define STB_HEXWAVE_IMPLEMENTATION
// #include "stb_hexwave.h"
//
// Optionally, #define STB_HEXWAVE_STATIC before including
// the header to cause the definitions to be private to the
// implementation file (i.e. to be "static" instead of "extern").
//
// Notes:
//
// Optionally performs memory allocation during initialization,
// never allocates otherwise.
//
// License:
//
// See end of file for license information.
//
// Usage:
//
// Initialization:
//
// hexwave_init(32,16,NULL); // read "header section" for alternatives
//
// Create oscillator:
//
// HexWave *osc = malloc(sizeof(*osc)); // or "new HexWave", or declare globally or on stack
// hexwave_create(osc, reflect_flag, peak_time, half_height, zero_wait);
// see "Waveform shapes" below for the meaning of these parameters
//
// Generate audio:
//
// hexwave_generate_samples(output, number_of_samples, osc, oscillator_freq)
// where:
// output is a buffer where the library will store floating point audio samples
// number_of_samples is the number of audio samples to generate
// osc is a pointer to a Hexwave
// oscillator_freq is the frequency of the oscillator divided by the sample rate
//
// The output samples will continue from where the samples generated by the
// previous hexwave_generate_samples() on this oscillator ended.
//
// Change oscillator waveform:
//
// hexwave_change(osc, reflect_flag, peak_time, half_height, zero_wait);
// can call in between calls to hexwave_generate_samples
//
// Waveform shapes:
//
// All waveforms generated by hexwave are constructed from six line segments
// characterized by 3 parameters.
//
// See demonstration: https://www.youtube.com/watch?v=hsUCrAsDN-M
//
// reflect=0 reflect=1
//
// 0-----P---1 0-----P---1 peak_time = P
// . 1 . 1
// /\_ : /\_ :
// / \_ : / \_ :
// / \.H / \.H half_height = H
// / | : / | :
// _____/ |_:___ _____/ | : _____
// . : \ | . | : /
// . : \ | . | : /
// . : \ _/ . \_: /
// . : \ _/ . :_ /
// . -1 \/ . -1 \/
// 0 - Z - - - - 1 0 - Z - - - - 1 zero_wait = Z
//
// Classic waveforms:
// peak half zero
// reflect time height wait
// Sawtooth 1 0 0 0
// Square 1 0 1 0
// Triangle 1 0.5 0 0
//
// Some waveforms can be produced in multiple ways, which is useful when morphing
// into other waveforms, and there are a few more notable shapes:
//
// peak half zero
// reflect time height wait
// Sawtooth 1 1 any 0
// Sawtooth (8va) 1 0 -1 0
// Triangle 1 0.5 0 0
// Square 1 0 1 0
// Square 0 0 1 0
// Triangle 0 0.5 0 0
// Triangle 0 0 -1 0
// AlternatingSaw 0 0 0 0
// AlternatingSaw 0 1 any 0
// Stairs 0 0 1 0.5
//
// The "Sawtooth (8va)" waveform is identical to a sawtooth wave with 2x the
// frequency, but when morphed with other values, it becomes an overtone of
// the base frequency.
//
// Morphing waveforms:
//
// Sweeping peak_time morphs the waveform while producing various spectra.
// Sweeping half_height effectively crossfades between two waveforms; useful, but less exciting.
// Sweeping zero_wait produces a similar effect no matter the reset of the waveform,
// a sort of high-pass/PWM effect where the wave becomes silent at zero_wait=1.
//
// You can trivially morph between any two waveforms from the above table
// which only differ in one column.
//
// Crossfade between classic waveforms:
// peak half zero
// Start End reflect time height wait
// ----- --- ------- ---- ------ ----
// Triangle Square 0 0 -1..1 0
// Saw Square 1 0 0..1 0
// Triangle Saw 1 0.5 0..2 0
//
// The last morph uses uses half-height values larger than 1, which means it will
// be louder and the output should be scaled down by half to compensate, or better
// by dynamically tracking the morph: volume_scale = 1 - half_height/4
//
// Non-crossfade morph between classic waveforms, most require changing
// two parameters at the same time:
// peak half zero
// Start End reflect time height wait
// ----- --- ------- ---- ------ ----
// Square Triangle any 0..0.5 1..0 0
// Square Saw 1 0..1 1..any 0
// Triangle Saw 1 0.5..1 0..-1 0
//
// Other noteworthy morphs between simple shapes:
// peak half zero
// Start Halfway End reflect time height wait
// ----- --------- --- ------- ---- ------ ----
// Saw (8va,neg) Saw (pos) 1 0..1 -1 0
// Saw (neg) Saw (pos) 1 0..1 0 0
// Triangle AlternatingSaw 0 0..1 -1 0
// AlternatingSaw Triangle AlternatingSaw 0 0..1 0 0
// Square AlternatingSaw 0 0..1 1 0
// Triangle Triangle AlternatingSaw 0 0..1 -1..1 0
// Square AlternatingSaw 0 0..1 1..0 0
// Saw (8va) Triangle Saw 1 0..1 -1..1 0
// Saw (neg) Saw (pos) 1 0..1 0..1 0
// AlternatingSaw AlternatingSaw 0 0..1 0..any 0
//
// The last entry is noteworthy because the morph from the halfway point to either
// endpoint sounds very different. For example, an LFO sweeping back and forth over
// the whole range will morph between the middle timbre and the AlternatingSaw
// timbre in two different ways, alternating.
//
// Entries with "any" for half_height are whole families of morphs, as you can pick
// any value you want as the endpoint for half_height.
//
// You can always morph between any two waveforms with the same value of 'reflect'
// by just sweeping the parameters simultaneously. There will never be artifacts
// and the result will always be useful, if not necessarily what you want.
//
// You can vary the sound of two-parameter morphs by ramping them differently,
// e.g. if the morph goes from t=0..1, then square-to-triangle looks like:
// peak_time = lerp(t, 0, 0.5)
// half_height = lerp(t, 1, 0 )
// but you can also do things like:
// peak_time = lerp(smoothstep(t), 0, 0.5)
// half_height = cos(PI/2 * t)
//
// How it works:
//
// hexwave use BLEP to bandlimit discontinuities and BLAMP
// to bandlimit C1 discontinuities. This is not polyBLEP
// (polynomial BLEP), it is table-driven BLEP. It is
// also not minBLEP (minimum-phase BLEP), as that complicates
// things for little benefit once BLAMP is involved.
//
// The previous oscillator frequency is remembered, and when
// the frequency changes, a BLAMP is generated to remove the
// C1 discontinuity, which reduces artifacts for sweeps/LFO.
//
// Changes to an oscillator timbre using hexwave_change() actually
// wait until the oscillator finishes its current cycle. All
// waveforms with non-zero "zero_wait" settings pass through 0
// and have 0-slope at the start of a cycle, which means changing
// the settings is artifact free at that time. (If zero_wait is 0,
// the code still treats it as passing through 0 with 0-slope; it'll
// apply the necessary fixups to make it artifact free as if it does
// transition to 0 with 0-slope vs. the waveform at the end of
// the cycle, then adds the fixups for a non-0 and non-0 slope
// at the start of the cycle, which cancels out if zero_wait is 0,
// and still does the right thing if zero_wait is 0 when the
// settings are updated.)
//
// BLEP/BLAMP normally requires overlapping buffers, but this
// is hidden from the user by generating the waveform to a
// temporary buffer and saving the overlap regions internally
// between calls. (It is slightly more complicated; see code.)
//
// By design all shapes have 0 DC offset; this is one reason
// hexwave uses zero_wait instead of standard PWM.
//
// The internals of hexwave could support any arbitrary shape
// made of line segments, but I chose not to expose this
// generality in favor of a simple, easy-to-use API.
#ifndef STB_INCLUDE_STB_HEXWAVE_H
#define STB_INCLUDE_STB_HEXWAVE_H
#ifndef STB_HEXWAVE_MAX_BLEP_LENGTH
#define STB_HEXWAVE_MAX_BLEP_LENGTH 64 // good enough for anybody
#endif
#ifdef STB_HEXWAVE_STATIC
#define STB_HEXWAVE_DEF static
#else
#define STB_HEXWAVE_DEF extern
#endif
typedef struct HexWave HexWave;
STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer);
// width: size of BLEP, from 4..64, larger is slower & more memory but less aliasing
// oversample: 2+, number of subsample positions, larger uses more memory but less noise
// user_buffer: optional, if provided the library will perform no allocations.
// 16*width*(oversample+1) bytes, must stay allocated as long as library is used
// technically it only needs: 8*( width * (oversample + 1))
// + 8*((width * oversample) + 1) bytes
//
// width can be larger than 64 if you define STB_HEXWAVE_MAX_BLEP_LENGTH to a larger value
STB_HEXWAVE_DEF void hexwave_shutdown(float *user_buffer);
// user_buffer: pass in same parameter as passed to hexwave_init
STB_HEXWAVE_DEF void hexwave_create(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait);
// see docs above for description
//
// reflect is tested as 0 or non-zero
// peak_time is clamped to 0..1
// half_height is not clamped
// zero_wait is clamped to 0..1
STB_HEXWAVE_DEF void hexwave_change(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait);
// see docs
STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, HexWave *hex, float freq);
// output: buffer where the library will store generated floating point audio samples
// number_of_samples: the number of audio samples to generate
// osc: pointer to a Hexwave initialized with 'hexwave_create'
// oscillator_freq: frequency of the oscillator divided by the sample rate
// private:
typedef struct
{
int reflect;
float peak_time;
float zero_wait;
float half_height;
} HexWaveParameters;
struct HexWave
{
float t, prev_dt;
HexWaveParameters current, pending;
int have_pending;
float buffer[STB_HEXWAVE_MAX_BLEP_LENGTH];
};
#endif
#ifdef STB_HEXWAVE_IMPLEMENTATION
#ifndef STB_HEXWAVE_NO_ALLOCATION
#include <stdlib.h> // malloc,free
#endif
#include <string.h> // memset,memcpy,memmove
#include <math.h> // sin,cos,fabs
#define hexwave_clamp(v,a,b) ((v) < (a) ? (a) : (v) > (b) ? (b) : (v))
STB_HEXWAVE_DEF void hexwave_change(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait)
{
hex->pending.reflect = reflect;
hex->pending.peak_time = hexwave_clamp(peak_time,0,1);
hex->pending.half_height = half_height;
hex->pending.zero_wait = hexwave_clamp(zero_wait,0,1);
// put a barrier here to allow changing from a different thread than the generator
hex->have_pending = 1;
}
STB_HEXWAVE_DEF void hexwave_create(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait)
{
memset(hex, 0, sizeof(*hex));
hexwave_change(hex, reflect, peak_time, half_height, zero_wait);
hex->current = hex->pending;
hex->have_pending = 0;
hex->t = 0;
hex->prev_dt = 0;
}
static struct
{
int width; // width of fixup in samples
int oversample; // number of oversampled versions (there's actually one more to allow lerpign)
float *blep;
float *blamp;
} hexblep;
static void hex_add_oversampled_bleplike(float *output, float time_since_transition, float scale, float *data)
{
float *d1,*d2;
float lerpweight;
int i, bw = hexblep.width;
int slot = (int) (time_since_transition * hexblep.oversample);
if (slot >= hexblep.oversample)
slot = hexblep.oversample-1; // clamp in case the floats overshoot
d1 = &data[ slot *bw];
d2 = &data[(slot+1)*bw];
lerpweight = time_since_transition * hexblep.oversample - slot;
for (i=0; i < bw; ++i)
output[i] += scale * (d1[i] + (d2[i]-d1[i])*lerpweight);
}
static void hex_blep (float *output, float time_since_transition, float scale)
{
hex_add_oversampled_bleplike(output, time_since_transition, scale, hexblep.blep);
}
static void hex_blamp(float *output, float time_since_transition, float scale)
{
hex_add_oversampled_bleplike(output, time_since_transition, scale, hexblep.blamp);
}
typedef struct
{
float t,v,s; // time, value, slope
} hexvert;
// each half of the waveform needs 4 vertices to represent 3 line
// segments, plus 1 more for wraparound
static void hexwave_generate_linesegs(hexvert vert[9], HexWave *hex, float dt)
{
int j;
float min_len = dt / 256.0f;
vert[0].t = 0;
vert[0].v = 0;
vert[1].t = hex->current.zero_wait*0.5f;
vert[1].v = 0;
vert[2].t = 0.5f*hex->current.peak_time + vert[1].t*(1-hex->current.peak_time);
vert[2].v = 1;
vert[3].t = 0.5f;
vert[3].v = hex->current.half_height;
if (hex->current.reflect) {
for (j=4; j <= 7; ++j) {
vert[j].t = 1 - vert[7-j].t;
vert[j].v = - vert[7-j].v;
}
} else {
for (j=4; j <= 7; ++j) {
vert[j].t = 0.5f + vert[j-4].t;
vert[j].v = - vert[j-4].v;
}
}
vert[8].t = 1;
vert[8].v = 0;
for (j=0; j < 8; ++j) {
if (vert[j+1].t <= vert[j].t + min_len) {
// if change takes place over less than a fraction of a sample treat as discontinuity
//
// otherwise the slope computation can blow up to arbitrarily large and we
// try to generate a huge BLAMP and the result is wrong.
//
// why does this happen if the math is right? i believe if done perfectly,
// the two BLAMPs on either side of the slope would cancel out, but our
// BLAMPs have only limited sub-sample precision and limited integration
// accuracy. or maybe it's just the math blowing up w/ floating point precision
// limits as we try to make x * (1/x) cancel out
//
// min_len verified artifact-free even near nyquist with only oversample=4
vert[j+1].t = vert[j].t;
}
}
if (vert[8].t != 1.0f) {
// if the above fixup moved the endpoint away from 1.0, move it back,
// along with any other vertices that got moved to the same time
float t = vert[8].t;
for (j=5; j <= 8; ++j)
if (vert[j].t == t)
vert[j].t = 1.0f;
}
// compute the exact slopes from the final fixed-up positions
for (j=0; j < 8; ++j)
if (vert[j+1].t == vert[j].t)
vert[j].s = 0;
else
vert[j].s = (vert[j+1].v - vert[j].v) / (vert[j+1].t - vert[j].t);
// wraparound at end
vert[8].t = 1;
vert[8].v = vert[0].v;
vert[8].s = vert[0].s;
}
STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, HexWave *hex, float freq)
{
hexvert vert[9];
int pass,i,j;
float t = hex->t;
float temp_output[2*STB_HEXWAVE_MAX_BLEP_LENGTH];
int buffered_length = sizeof(float)*hexblep.width;
float dt = (float) fabs(freq);
float recip_dt = (dt == 0.0f) ? 0.0f : 1.0f / dt;
int halfw = hexblep.width/2;
// all sample times are biased by halfw to leave room for BLEP/BLAMP to go back in time
if (num_samples <= 0)
return;
// convert parameters to times and slopes
hexwave_generate_linesegs(vert, hex, dt);
if (hex->prev_dt != dt) {
// if frequency changes, add a fixup at the derivative discontinuity starting at now
float slope;
for (j=1; j < 6; ++j)
if (t < vert[j].t)
break;
slope = vert[j].s;
if (slope != 0)
hex_blamp(output, 0, (dt - hex->prev_dt)*slope);
hex->prev_dt = dt;
}
// copy the buffered data from last call and clear the rest of the output array
memset(output, 0, sizeof(float)*num_samples);
memset(temp_output, 0, 2*hexblep.width*sizeof(float));
if (num_samples >= hexblep.width) {
memcpy(output, hex->buffer, buffered_length);
} else {
// if the output is shorter than hexblep.width, we do all synthesis to temp_output
memcpy(temp_output, hex->buffer, buffered_length);
}
for (pass=0; pass < 2; ++pass) {
int i0,i1;
float *out;
// we want to simulate having one buffer that is num_output + hexblep.width
// samples long, without putting that requirement on the user, and without
// allocating a temp buffer that's as long as the whole thing. so we use two
// overlapping buffers, one the user's buffer and one a fixed-length temp
// buffer.
if (pass == 0) {
if (num_samples < hexblep.width)
continue;
// run as far as we can without overwriting the end of the user's buffer
out = output;
i0 = 0;
i1 = num_samples - hexblep.width;
} else {
// generate the rest into a temp buffer
out = temp_output;
i0 = 0;
if (num_samples >= hexblep.width)
i1 = hexblep.width;
else
i1 = num_samples;
}
// determine current segment
for (j=0; j < 8; ++j)
if (t < vert[j+1].t)
break;
i = i0;
for(;;) {
while (t < vert[j+1].t) {
if (i == i1)
goto done;
out[i+halfw] += vert[j].v + vert[j].s*(t - vert[j].t);
t += dt;
++i;
}
// transition from lineseg starting at j to lineseg starting at j+1
if (vert[j].t == vert[j+1].t)
hex_blep(out+i, recip_dt*(t-vert[j+1].t), (vert[j+1].v - vert[j].v));
hex_blamp(out+i, recip_dt*(t-vert[j+1].t), dt*(vert[j+1].s - vert[j].s));
++j;
if (j == 8) {
// change to different waveform if there's a change pending
j = 0;
t -= 1.0; // t was >= 1.f if j==8
if (hex->have_pending) {
float prev_s0 = vert[j].s;
float prev_v0 = vert[j].v;
hex->current = hex->pending;
hex->have_pending = 0;
hexwave_generate_linesegs(vert, hex, dt);
// the following never occurs with this oscillator, but it makes
// the code work in more general cases
if (vert[j].v != prev_v0)
hex_blep (out+i, recip_dt*t, (vert[j].v - prev_v0));
if (vert[j].s != prev_s0)
hex_blamp(out+i, recip_dt*t, dt*(vert[j].s - prev_s0));
}
}
}
done:
;
}
// at this point, we've written output[] and temp_output[]
if (num_samples >= hexblep.width) {
// the first half of temp[] overlaps the end of output, the second half will be the new start overlap
for (i=0; i < hexblep.width; ++i)
output[num_samples-hexblep.width + i] += temp_output[i];
memcpy(hex->buffer, temp_output+hexblep.width, buffered_length);
} else {
for (i=0; i < num_samples; ++i)
output[i] = temp_output[i];
memcpy(hex->buffer, temp_output+num_samples, buffered_length);
}
hex->t = t;
}
STB_HEXWAVE_DEF void hexwave_shutdown(float *user_buffer)
{
#ifndef STB_HEXWAVE_NO_ALLOCATION
if (user_buffer != 0) {
free(hexblep.blep);
free(hexblep.blamp);
}
#endif
}
// buffer should be NULL or must be 4*(width*(oversample+1)*2 +
STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer)
{
int halfwidth = width/2;
int half = halfwidth*oversample;
int blep_buffer_count = width*(oversample+1);
int n = 2*half+1;
#ifdef STB_HEXWAVE_NO_ALLOCATION
float *buffers = user_buffer;
#else
float *buffers = user_buffer ? user_buffer : (float *) malloc(sizeof(float) * n * 2);
#endif
float *step = buffers+0*n;
float *ramp = buffers+1*n;
float *blep_buffer, *blamp_buffer;
double integrate_impulse=0, integrate_step=0;
int i,j;
if (width > STB_HEXWAVE_MAX_BLEP_LENGTH)
width = STB_HEXWAVE_MAX_BLEP_LENGTH;
if (user_buffer == 0) {
#ifndef STB_HEXWAVE_NO_ALLOCATION
blep_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
blamp_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
#endif
} else {
blep_buffer = ramp+n;
blamp_buffer = blep_buffer + blep_buffer_count;
}
// compute BLEP and BLAMP by integerating windowed sinc
for (i=0; i < n; ++i) {
for (j=0; j < 16; ++j) {
float sinc_t = 3.141592f* (i-half) / oversample;
float sinc = (i==half) ? 1.0f : (float) sin(sinc_t) / (sinc_t);
float wt = 2.0f*3.1415926f * i / (n-1);
float window = (float) (0.355768 - 0.487396*cos(wt) + 0.144232*cos(2*wt) - 0.012604*cos(3*wt)); // Nuttall
double value = window * sinc;
integrate_impulse += value/16;
integrate_step += integrate_impulse/16;
}
step[i] = (float) integrate_impulse;
ramp[i] = (float) integrate_step;
}
// renormalize
for (i=0; i < n; ++i) {
step[i] = step[i] * (float) (1.0 / step[n-1]); // step needs to reach to 1.0
ramp[i] = ramp[i] * (float) (halfwidth / ramp[n-1]); // ramp needs to become a slope of 1.0 after oversampling
}
// deinterleave to allow efficient interpolation e.g. w/SIMD
for (j=0; j <= oversample; ++j) {
for (i=0; i < width; ++i) {
blep_buffer [j*width+i] = step[j+i*oversample];
blamp_buffer[j*width+i] = ramp[j+i*oversample];
}
}
// subtract out the naive waveform; note we can't do this to the raw data
// above, because we want the discontinuity to be in a different locations
// for j=0 and j=oversample (which exists to provide something to interpolate against)
for (j=0; j <= oversample; ++j) {
// subtract step
for (i=halfwidth; i < width; ++i)
blep_buffer [j*width+i] -= 1.0f;
// subtract ramp
for (i=halfwidth; i < width; ++i)
blamp_buffer[j*width+i] -= (j+i*oversample-half)*(1.0f/oversample);
}
hexblep.blep = blep_buffer;
hexblep.blamp = blamp_buffer;
hexblep.width = width;
hexblep.oversample = oversample;
#ifndef STB_HEXWAVE_NO_ALLOCATION
if (user_buffer == 0)
free(buffers);
#endif
}
#endif // STB_HEXWAVE_IMPLEMENTATION
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

7988
thirdparty/stb/stb_image.h vendored Normal file

File diff suppressed because it is too large Load Diff

10651
thirdparty/stb/stb_image_resize2.h vendored Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More