initial commit
commit
f47ad817f1
|
@ -0,0 +1,3 @@
|
||||||
|
.DS_Store
|
||||||
|
bin
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710146030,
|
||||||
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1659877975,
|
||||||
|
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_3": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1705309234,
|
||||||
|
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixgl": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1713543440,
|
||||||
|
"narHash": "sha256-lnzZQYG0+EXl/6NkGpyIz+FEOc/DSEG57AP1VsdeNrM=",
|
||||||
|
"owner": "guibou",
|
||||||
|
"repo": "nixGL",
|
||||||
|
"rev": "310f8e49a149e4c9ea52f1adf70cdc768ec53f8a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "guibou",
|
||||||
|
"repo": "nixGL",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1660551188,
|
||||||
|
"narHash": "sha256-a1LARMMYQ8DPx1BgoI/UN4bXe12hhZkCNqdxNi6uS0g=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "441dc5d512153039f19ef198e662e4f3dbb9fd65",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1715087517,
|
||||||
|
"narHash": "sha256-CLU5Tsg24Ke4+7sH8azHWXKd0CFd4mhLWfhYgUiDBpQ=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "b211b392b8486ee79df6cdfb1157ad2133427a29",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1706487304,
|
||||||
|
"narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "90f456026d284c22b3e3497be980b2e47d0b28ac",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixgl": "nixgl",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"rust-overlay": "rust-overlay"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-overlay": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_3",
|
||||||
|
"nixpkgs": "nixpkgs_3"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1715221036,
|
||||||
|
"narHash": "sha256-81EKOdlmT/4hZpImRlvMVPgmCcJYZjwlWbJese/XqUw=",
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"rev": "5c4bc8a0a70093a31a12509c5653c147f2310bd2",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
nixgl.url = "github:guibou/nixGL";
|
||||||
|
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils, nixgl, rust-overlay, ... }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
overlays = [ nixgl.overlay (import rust-overlay) ];
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system overlays;
|
||||||
|
};
|
||||||
|
fixed-odin = pkgs.odin.overrideAttrs (finalAttrs: prevAttr: rec {
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "odin-lang";
|
||||||
|
repo = "Odin";
|
||||||
|
rev = "aab122ede8b04a9877e22c9013c0b020186bc9b4";
|
||||||
|
# hash = "sha256-pxvU5veB1NEYPfer5roiLp/od2Pv4l1jJah0OHwb5yo=";
|
||||||
|
};
|
||||||
|
LLVM_CONFIG = "${pkgs.llvmPackages_17.llvm.dev}/bin/llvm-config";
|
||||||
|
nativeBuildInputs = with pkgs; prevAttr.nativeBuildInputs ++ [ libcxx libcxxabi ];
|
||||||
|
postPatch = prevAttr.postPatch + ''
|
||||||
|
sed -i build_odin.sh \
|
||||||
|
-e 's|CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"|CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags) -I ${pkgs.libiconv.outPath}/include/"|' \
|
||||||
|
-e 's|LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"|LDFLAGS="$LDFLAGS -pthread -lm -lstdc++ -L ${pkgs.libiconv.outPath}/lib/ -L ${pkgs.llvmPackages_17.libcxxabi.outPath}/lib/"|'
|
||||||
|
'';
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp odin $out/bin/odin
|
||||||
|
|
||||||
|
mkdir -p $out/share
|
||||||
|
cp -r core $out/share/core
|
||||||
|
cp -r vendor $out/share/vendor
|
||||||
|
|
||||||
|
wrapProgram $out/bin/odin \
|
||||||
|
--set PATH ${pkgs.lib.makeBinPath (with pkgs; [
|
||||||
|
coreutils
|
||||||
|
llvmPackages_17.bintools
|
||||||
|
llvmPackages_17.lld
|
||||||
|
llvmPackages_17.clang
|
||||||
|
])} \
|
||||||
|
--set-default ODIN_ROOT $out/share
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShell = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; (if pkgs.system == "aarch64-darwin" || pkgs.system == "x86_64-darwin" then [
|
||||||
|
odin
|
||||||
|
darwin.apple_sdk.frameworks.CoreData
|
||||||
|
darwin.apple_sdk.frameworks.Kernel
|
||||||
|
darwin.apple_sdk.frameworks.CoreVideo
|
||||||
|
darwin.apple_sdk.frameworks.GLUT
|
||||||
|
darwin.apple_sdk.frameworks.IOKit
|
||||||
|
darwin.apple_sdk.frameworks.OpenGL
|
||||||
|
darwin.apple_sdk.frameworks.Cocoa
|
||||||
|
] else if pkgs.system == "x86_64-linux" then [
|
||||||
|
pkg-config
|
||||||
|
binutils
|
||||||
|
odin
|
||||||
|
libGL
|
||||||
|
xorg.libX11
|
||||||
|
xorg.libXi
|
||||||
|
xorg.xinput
|
||||||
|
xorg.libXcursor
|
||||||
|
xorg.libXrandr
|
||||||
|
xorg.libXinerama
|
||||||
|
pkgs.nixgl.nixGLIntel
|
||||||
|
] else throw "unsupported system" );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
alias b := build
|
||||||
|
alias r := run
|
||||||
|
|
||||||
|
c_flags := if os() == "macos" {
|
||||||
|
"$(curl-config --libs) -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC"
|
||||||
|
} else if os_family() == "unix" {
|
||||||
|
"$(curl-config --libs) -lEGL -lGLESv2 -lGL -lm -lwayland-client -lwayland-egl -lX11 -lXi -lXcursor -Wno-implicit-function-declaration"
|
||||||
|
} else { "" }
|
||||||
|
|
||||||
|
build: build_gfx
|
||||||
|
odin build src/ -out:bin/chat_client -debug -lld -extra-linker-flags:"-framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit"
|
||||||
|
|
||||||
|
[macos]
|
||||||
|
build_gfx: transpile_shaders_metal
|
||||||
|
mkdir -p bin
|
||||||
|
cc -O0 -g -c -ObjC -Wall -Wextra -D_FONT_WIDTH=12 -D_FONT_HEIGHT=24 -DED_GFX_IMPLEMENTATION vendor/pcleavelin/gfx.h -o bin/libgfx.a
|
||||||
|
|
||||||
|
[macos]
|
||||||
|
run: build
|
||||||
|
./bin/chat_client
|
||||||
|
|
||||||
|
[macos]
|
||||||
|
transpile_shaders_metal:
|
||||||
|
mkdir -p bin/transpiled_shaders
|
||||||
|
|
||||||
|
xcrun -sdk macosx metal -o bin/transpiled_shaders/text_atlas.ir -c shaders/text_atlas.metal
|
||||||
|
xcrun -sdk macosx metallib -o bin/shaders.metallib bin/transpiled_shaders/text_atlas.ir
|
|
@ -0,0 +1,145 @@
|
||||||
|
#include <metal_stdlib>
|
||||||
|
|
||||||
|
using namespace metal;
|
||||||
|
|
||||||
|
struct VertexInput {
|
||||||
|
float2 position;
|
||||||
|
float2 tex_coord;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
float4 position [[position]];
|
||||||
|
float4 color;
|
||||||
|
float2 tex_coord;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Glyph {
|
||||||
|
float2 atlas_position;
|
||||||
|
float2 size;
|
||||||
|
float2 target_position;
|
||||||
|
float y_offset;
|
||||||
|
float _haha_alignment;
|
||||||
|
float4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UniformParams {
|
||||||
|
float2 screen_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
float4 to_device_position(float2 position, float2 size) {
|
||||||
|
return float4(((position / size) * 2.0) - float2(1.0), 1.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex VertexOutput
|
||||||
|
vs_main(
|
||||||
|
uint vertex_id [[vertex_id]],
|
||||||
|
uint glyph_id [[instance_id]],
|
||||||
|
constant VertexInput *vertices [[buffer(0)]],
|
||||||
|
constant Glyph *glyphs [[buffer(1)]],
|
||||||
|
constant UniformParams ¶ms [[buffer(2)]]
|
||||||
|
)
|
||||||
|
{
|
||||||
|
VertexOutput out;
|
||||||
|
|
||||||
|
Glyph glyph = glyphs[glyph_id];
|
||||||
|
|
||||||
|
float2 scaled_size = ((vertices[vertex_id].position + 1.0) / 2.0) * (glyph.size/2.0);
|
||||||
|
float2 scaled_size_2 = ((vertices[vertex_id].position + 1.0) / 2.0) * (glyph.size);
|
||||||
|
float2 glyph_pos = scaled_size + glyph.target_position + float2(0, glyph.y_offset/2.0+24);
|
||||||
|
|
||||||
|
float4 device_position = to_device_position(glyph_pos, params.screen_size);
|
||||||
|
float2 atlas_position = (scaled_size_2 + glyph.atlas_position) / 512.0;
|
||||||
|
|
||||||
|
device_position.y = -device_position.y;
|
||||||
|
|
||||||
|
out.position = device_position;
|
||||||
|
out.color = glyph.color;
|
||||||
|
out.tex_coord = atlas_position;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment float4 fs_main(VertexOutput in [[stage_in]],
|
||||||
|
texture2d<float, access::sample> texture [[texture(0)]])
|
||||||
|
{
|
||||||
|
constexpr sampler texture_sampler (mag_filter::linear, min_filter::linear);
|
||||||
|
|
||||||
|
float text_color = texture.sample(texture_sampler, in.tex_coord).r;
|
||||||
|
return float4(in.color.rgb, text_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UiRect {
|
||||||
|
float4 position;
|
||||||
|
float4 size;
|
||||||
|
float4 border_size;
|
||||||
|
float4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UiRectFragment {
|
||||||
|
float4 device_position [[position]];
|
||||||
|
float2 position;
|
||||||
|
float2 size;
|
||||||
|
float2 border_size;
|
||||||
|
float2 screen_size;
|
||||||
|
float2 tex_coord;
|
||||||
|
float4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
vertex UiRectFragment
|
||||||
|
ui_rect_vs(
|
||||||
|
uint vertex_id [[vertex_id]],
|
||||||
|
uint rect_id [[instance_id]],
|
||||||
|
constant VertexInput *vertices [[buffer(0)]],
|
||||||
|
constant UiRect *rects [[buffer(1)]],
|
||||||
|
constant UniformParams ¶ms [[buffer(2)]]
|
||||||
|
)
|
||||||
|
{
|
||||||
|
UiRect rect = rects[rect_id];
|
||||||
|
|
||||||
|
float2 scaled_size = ((vertices[vertex_id].position.xy + 1.0) / 2.0) * (rect.size.xy);
|
||||||
|
float2 rect_pos = scaled_size + rect.position.xy;
|
||||||
|
|
||||||
|
float4 device_position = to_device_position(rect_pos, params.screen_size);
|
||||||
|
device_position.y = -device_position.y;
|
||||||
|
|
||||||
|
return UiRectFragment {
|
||||||
|
device_position,
|
||||||
|
rect.position.xy,
|
||||||
|
rect.size.xy,
|
||||||
|
rect.border_size.xy,
|
||||||
|
params.screen_size,
|
||||||
|
vertices[vertex_id].tex_coord,
|
||||||
|
rect.color,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
float rect_sdf(
|
||||||
|
float2 absolute_pixel_position,
|
||||||
|
float2 origin,
|
||||||
|
float2 size,
|
||||||
|
float corner_radius
|
||||||
|
) {
|
||||||
|
float2 half_size = size / 2;
|
||||||
|
float2 rect_center = origin + half_size;
|
||||||
|
|
||||||
|
float2 pixel_position = abs(absolute_pixel_position - rect_center);
|
||||||
|
float2 shrunk_corner_position = half_size - corner_radius;
|
||||||
|
|
||||||
|
float2 pixel_to_shrunk_corner = max(float2(0), pixel_position - shrunk_corner_position);
|
||||||
|
float distance_to_shrunk_corner = length(pixel_to_shrunk_corner);
|
||||||
|
float distance = distance_to_shrunk_corner - corner_radius;
|
||||||
|
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment float4 ui_rect_fs(UiRectFragment in [[stage_in]])
|
||||||
|
{
|
||||||
|
float2 pixel_pos = (in.device_position.xy + 1.0) / 2.0;
|
||||||
|
|
||||||
|
float distance = rect_sdf(pixel_pos, in.position, in.size, in.border_size.x);
|
||||||
|
if (distance <= 0.0) {
|
||||||
|
return in.color;
|
||||||
|
} else {
|
||||||
|
return float4(0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#version 440 core
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
vec4 position;
|
||||||
|
vec2 tex_coord;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
layout(location = 0) in VertexOutput out_vertex;
|
||||||
|
layout(location = 1) uniform highp sampler2D atlas_texture;
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float text_color = texture(atlas_texture, out_vertex.tex_coord).r;
|
||||||
|
|
||||||
|
color = vec4(text_color * vec3(1,1,1), text_color);
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
#version 440 core
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
vec2 position;
|
||||||
|
vec2 tex_coord;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
vec4 position;
|
||||||
|
vec2 tex_coord;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Glyph {
|
||||||
|
vec2 atlas_position;
|
||||||
|
vec2 size;
|
||||||
|
vec2 target_position;
|
||||||
|
float y_offset;
|
||||||
|
float _haha_alignment;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(std430, binding = 0) readonly buffer VertexBlock {
|
||||||
|
Vertex vertices[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(std430, binding = 1) readonly buffer GlyphBlock {
|
||||||
|
Glyph glyphs[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(std430, binding = 2) readonly buffer ParamsBlock {
|
||||||
|
vec2 screen_size;
|
||||||
|
vec2 font_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
vec4 to_device_position(vec2 position, vec2 size) {
|
||||||
|
return vec4(((position / size) * 2.0) - vec2(1.0), 1.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(location = 0) out VertexOutput out_vertex;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Glyph glyph = glyphs[gl_InstanceID];
|
||||||
|
|
||||||
|
vec2 scaled_size = ((vertices[gl_VertexID].position + 1.0) / 2.0) * (glyph.size/2.0);
|
||||||
|
vec2 scaled_size_2 = ((vertices[gl_VertexID].position + 1.0) / 2.0) * (glyph.size);
|
||||||
|
vec2 glyph_pos = scaled_size + glyph.target_position + vec2(0, glyph.y_offset/2.0+font_size.y);
|
||||||
|
|
||||||
|
vec4 device_position = to_device_position(glyph_pos, screen_size);
|
||||||
|
vec2 atlas_position = (scaled_size_2 + glyph.atlas_position) / 512.0;
|
||||||
|
|
||||||
|
device_position.y = -device_position.y;
|
||||||
|
|
||||||
|
out_vertex.position = device_position;
|
||||||
|
out_vertex.tex_coord = atlas_position;
|
||||||
|
|
||||||
|
gl_Position = device_position;
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
#version 440 core
|
||||||
|
|
||||||
|
struct UiRectFragment {
|
||||||
|
highp vec4 device_position;
|
||||||
|
highp vec2 position;
|
||||||
|
highp vec2 size;
|
||||||
|
highp vec2 border_size;
|
||||||
|
highp vec2 screen_size;
|
||||||
|
highp vec2 tex_coord;
|
||||||
|
highp vec4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
in UiRectFragment out_rect;
|
||||||
|
|
||||||
|
layout(location = 0) out highp vec4 color;
|
||||||
|
|
||||||
|
highp float rect_sdf(
|
||||||
|
highp vec2 absolute_pixel_position,
|
||||||
|
highp vec2 origin,
|
||||||
|
highp vec2 size,
|
||||||
|
highp float corner_radius
|
||||||
|
) {
|
||||||
|
highp vec2 half_size = size / 2.0;
|
||||||
|
highp vec2 rect_center = origin + half_size;
|
||||||
|
|
||||||
|
highp vec2 pixel_position = abs(absolute_pixel_position - rect_center);
|
||||||
|
highp vec2 shrunk_corner_position = half_size - corner_radius;
|
||||||
|
|
||||||
|
highp vec2 pixel_to_shrunk_corner = max(vec2(0), pixel_position - shrunk_corner_position);
|
||||||
|
highp float distance_to_shrunk_corner = length(pixel_to_shrunk_corner);
|
||||||
|
highp float distance = distance_to_shrunk_corner - corner_radius;
|
||||||
|
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
highp vec2 pixel_pos = out_rect.tex_coord.xy * out_rect.screen_size;
|
||||||
|
|
||||||
|
highp float distance = rect_sdf(pixel_pos, out_rect.position, out_rect.size, out_rect.border_size.x);
|
||||||
|
if (distance <= 0.0) {
|
||||||
|
color = out_rect.color;
|
||||||
|
} else {
|
||||||
|
color = vec4(0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
#version 440 core
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
vec2 position;
|
||||||
|
vec2 tex_coord;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UiRect {
|
||||||
|
vec4 position;
|
||||||
|
vec4 size;
|
||||||
|
vec4 border_size;
|
||||||
|
vec4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UiRectFragment {
|
||||||
|
highp vec4 device_position;
|
||||||
|
highp vec2 position;
|
||||||
|
highp vec2 size;
|
||||||
|
highp vec2 border_size;
|
||||||
|
highp vec2 screen_size;
|
||||||
|
highp vec2 tex_coord;
|
||||||
|
highp vec4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(std430, binding = 0) readonly buffer VertexBlock {
|
||||||
|
Vertex vertices[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(std430, binding = 1) readonly buffer RectBlock {
|
||||||
|
UiRect rects[];
|
||||||
|
};
|
||||||
|
layout(std430, binding = 2) readonly buffer ParamsBlock {
|
||||||
|
vec2 screen_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
out UiRectFragment out_rect;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
UiRect rect = rects[gl_InstanceID];
|
||||||
|
|
||||||
|
out_rect.device_position = vec4(vertices[gl_VertexID].position, 1, 1);
|
||||||
|
out_rect.position = rect.position.xy;
|
||||||
|
out_rect.size = rect.size.xy;
|
||||||
|
out_rect.border_size = rect.border_size.xy;
|
||||||
|
out_rect.screen_size = screen_size;
|
||||||
|
out_rect.tex_coord = vertices[gl_VertexID].tex_coord;
|
||||||
|
out_rect.color = rect.color;
|
||||||
|
|
||||||
|
gl_Position = vec4(vertices[gl_VertexID].position, 1, 1);
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package gfx;
|
||||||
|
|
||||||
|
import "core:strings"
|
||||||
|
import c "core:c"
|
||||||
|
|
||||||
|
foreign import gfx "../../bin/libgfx.a"
|
||||||
|
|
||||||
|
GfxFrameFunc :: proc "c" (mouse_x: int, mouse_y: int, mouse_left_down: bool, mouse_right_down: bool)
|
||||||
|
|
||||||
|
cx :: struct {}
|
||||||
|
|
||||||
|
GpuGlyph :: struct {
|
||||||
|
atlas_position: [2]f32,
|
||||||
|
size: [2]f32,
|
||||||
|
position: [2]f32,
|
||||||
|
y_offset: f32,
|
||||||
|
_haha_alignment: f32,
|
||||||
|
color: [2]f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
gfx_string :: struct {
|
||||||
|
data: [^]u8,
|
||||||
|
len: c.size_t,
|
||||||
|
|
||||||
|
// unused
|
||||||
|
owned: c.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign gfx {
|
||||||
|
keep_running: bool
|
||||||
|
|
||||||
|
@(link_name="gfx_init_context") init_context :: proc "c" (frame_func: GfxFrameFunc, width: u32, height: u32) -> ^cx ---
|
||||||
|
@(link_name="gfx_run_events") run_events :: proc "c" (gfx_cx: ^cx) ---
|
||||||
|
@(link_name="gfx_add_glyph") add_glyph :: proc "c" (gfx_cx: ^cx, glyph: GpuGlyph) ---
|
||||||
|
@(link_name="gfx_push_texture_buffer") push_texture_buffer :: proc "c" (gfx_cx: ^cx, width: u32, height: u32, data: [^]u8, len: u32) ---
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
gfx_queue_text :: proc "c" (gfx_cx: ^cx, text: gfx_string, position: [^]f32, max_x: f32, max_y: f32, color: [^]f32) ---
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_text :: proc(gfx_cx: ^cx, text: string, position: [2]f32, max_x: f32, max_y: f32, color: [4]f32) {
|
||||||
|
text_ := gfx_string {
|
||||||
|
data = raw_data(text),
|
||||||
|
len = len(text),
|
||||||
|
};
|
||||||
|
|
||||||
|
p := []f32 { position[0], position[1] }
|
||||||
|
c := []f32 { color[0], color[1], color[2], color[3] }
|
||||||
|
|
||||||
|
gfx_queue_text(gfx_cx, text_, raw_data(p), max_x, max_y, raw_data(c))
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "core:runtime";
|
||||||
|
import "core:fmt";
|
||||||
|
import "core:os";
|
||||||
|
import stbtt "vendor:stb/truetype";
|
||||||
|
|
||||||
|
import "gfx";
|
||||||
|
|
||||||
|
gfx_cx: ^gfx.cx = nil
|
||||||
|
|
||||||
|
frame_func :: proc "c" (mouse_x: int, mouse_y: int, mouse_left_down: bool, mouse_right_down: bool) {
|
||||||
|
context = runtime.default_context();
|
||||||
|
|
||||||
|
for y in 0..<100 {
|
||||||
|
for x in 0..<100 {
|
||||||
|
gfx.queue_text(gfx_cx, "Helope!", { f32(x)*100, f32(x)*24 + f32(y)*26}, 1000, 1000, { 1, 1, 1, 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
gfx_cx = gfx.init_context(frame_func, 800, 600)
|
||||||
|
|
||||||
|
font_data, success := os.read_entire_file_from_filename("./bin/JetBrainsMono-Medium.ttf");
|
||||||
|
if !success {
|
||||||
|
fmt.eprintln("failed to read font file");
|
||||||
|
os.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
font: stbtt.fontinfo;
|
||||||
|
if stbtt.InitFont(&font, raw_data(font_data), 0) == false {
|
||||||
|
fmt.eprintln("failed to init font");
|
||||||
|
os.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap_size: i32 = 512;
|
||||||
|
bitmap := make([]u8, bitmap_size * bitmap_size);
|
||||||
|
|
||||||
|
rasterized_font_height: i32 = 24;
|
||||||
|
|
||||||
|
ascent, descent, line_gap: i32;
|
||||||
|
scale := stbtt.ScaleForPixelHeight(&font, f32(rasterized_font_height * 2));
|
||||||
|
stbtt.GetFontVMetrics(&font, &ascent, &descent, &line_gap);
|
||||||
|
|
||||||
|
gfx.add_glyph(gfx_cx, gfx.GpuGlyph {
|
||||||
|
size = {f32(rasterized_font_height/4), 1},
|
||||||
|
y_offset = f32(-rasterized_font_height),
|
||||||
|
});
|
||||||
|
|
||||||
|
x := rasterized_font_height / 4;
|
||||||
|
y: i32 = 0;
|
||||||
|
for i in 33..<33+96 {
|
||||||
|
width, height, xoff, yoff: i32;
|
||||||
|
glyph_bitmap := stbtt.GetCodepointBitmap(
|
||||||
|
&font, scale, scale, rune(i), &width, &height, &xoff, &yoff);
|
||||||
|
|
||||||
|
if (x + width) >= bitmap_size {
|
||||||
|
x = 0;
|
||||||
|
y += i32(f32(ascent - descent + line_gap) * scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
xxx: i32 = x;
|
||||||
|
for xx in 0..<width {
|
||||||
|
yyy: i32 = y;
|
||||||
|
for yy in 0..<height {
|
||||||
|
bitmap[xxx + yyy * bitmap_size] =
|
||||||
|
glyph_bitmap[xx + yy * width];
|
||||||
|
|
||||||
|
yyy += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
xxx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gfx.add_glyph(gfx_cx,
|
||||||
|
gfx.GpuGlyph {
|
||||||
|
atlas_position = {f32(x), f32(y)},
|
||||||
|
size = {f32(width), f32(height)},
|
||||||
|
position = {f32(x), f32(y)},
|
||||||
|
y_offset = f32(yoff),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
x += width;
|
||||||
|
}
|
||||||
|
gfx.push_texture_buffer(gfx_cx, u32(bitmap_size), u32(bitmap_size), raw_data(bitmap), u32(bitmap_size * bitmap_size));
|
||||||
|
|
||||||
|
for(gfx.keep_running) {
|
||||||
|
gfx.run_events(gfx_cx)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
#ifndef ED_ARRAY_INCLUDED
|
||||||
|
#define ED_ARRAY_INCLUDED
|
||||||
|
#include <assert.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define array(T) struct T##_Array
|
||||||
|
#define arrayTemplate(T) \
|
||||||
|
array(T) { \
|
||||||
|
size_t size, capacity; \
|
||||||
|
T *data; \
|
||||||
|
}; \
|
||||||
|
array(T) T##_ArrayConstruct(size_t size) { \
|
||||||
|
array(T) this; \
|
||||||
|
this.size = 0; \
|
||||||
|
this.capacity = size; \
|
||||||
|
this.data = malloc(size * sizeof(T)); \
|
||||||
|
if (!this.data) { \
|
||||||
|
assert("failed to allocate memory for array"); \
|
||||||
|
} \
|
||||||
|
return this; \
|
||||||
|
}; \
|
||||||
|
void T##_PushArray(array(T) * arr, T value) { \
|
||||||
|
if (arr->size == arr->capacity) { \
|
||||||
|
arr->capacity *= 2; \
|
||||||
|
void *new_data = realloc(arr->data, arr->capacity * sizeof(T)); \
|
||||||
|
if (new_data == NULL) { \
|
||||||
|
fprintf(stderr, "out of memory when reallocating array\n"); \
|
||||||
|
} \
|
||||||
|
arr->data = new_data; \
|
||||||
|
} \
|
||||||
|
if (arr->size + 1 <= arr->capacity) { \
|
||||||
|
arr->data[arr->size] = value; \
|
||||||
|
arr->size += 1; \
|
||||||
|
} \
|
||||||
|
}; \
|
||||||
|
void T##_PushArrayMulti(array(T) * arr, T * values, size_t len) { \
|
||||||
|
for (size_t i = 0; i < len; ++i) { \
|
||||||
|
T##_PushArray(arr, values[i]); \
|
||||||
|
} \
|
||||||
|
}; \
|
||||||
|
void T##_InsertArrayAt(array(T) * arr, size_t loc, T value) { \
|
||||||
|
if (arr->size == arr->capacity) { \
|
||||||
|
arr->capacity *= 2; \
|
||||||
|
void *new_data = realloc(arr->data, arr->capacity * sizeof(T)); \
|
||||||
|
if (new_data == NULL) { \
|
||||||
|
fprintf(stderr, "out of memory when reallocating array\n"); \
|
||||||
|
} \
|
||||||
|
arr->data = new_data; \
|
||||||
|
} \
|
||||||
|
memcpy(&arr->data[loc + 1], &arr->data[loc], arr->size * sizeof(T)); \
|
||||||
|
arr->data[loc] = value; \
|
||||||
|
arr->size += 1; \
|
||||||
|
};
|
||||||
|
|
||||||
|
#define slice(T) struct T##_Slice
|
||||||
|
#define sliceTemplate(T) \
|
||||||
|
slice(T) { \
|
||||||
|
size_t len; \
|
||||||
|
T *data; \
|
||||||
|
}; \
|
||||||
|
array(T) T##_FromSlice(slice(T) s) { \
|
||||||
|
array(T) arr = T##_ArrayConstruct(s.len); \
|
||||||
|
memcpy(arr.data, s.data, sizeof(T) * s.len); \
|
||||||
|
arr.size = s.len; \
|
||||||
|
return arr; \
|
||||||
|
} \
|
||||||
|
slice(T) T##_SliceConstruct(void *data, size_t len) { \
|
||||||
|
slice(T) this; \
|
||||||
|
this.len = len; \
|
||||||
|
this.data = data; \
|
||||||
|
return this; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define newArray(T, size) T##_ArrayConstruct(size)
|
||||||
|
#define newArrayFromSlice(T, s) T##_FromSlice(s)
|
||||||
|
#define pushArray(T, arr, value) T##_PushArray(arr, (value))
|
||||||
|
#define pushArrayMulti(T, arr, values, len) T##_PushArrayMulti(arr, values, len)
|
||||||
|
|
||||||
|
#define insertArrayAt(T, arr, loc, value) T##_InsertArrayAt(arr, loc, (value))
|
||||||
|
|
||||||
|
#define newSlice(T, data, len) T##_SliceConstruct(data, len)
|
||||||
|
|
||||||
|
arrayTemplate(uint8_t);
|
||||||
|
sliceTemplate(uint8_t);
|
||||||
|
arrayTemplate(uint32_t);
|
||||||
|
sliceTemplate(uint32_t);
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,106 @@
|
||||||
|
#ifndef ED_STRING_INCLUDED
|
||||||
|
#define ED_STRING_INCLUDED
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define _String(text) \
|
||||||
|
((string){.data = (uint8_t *)text, .len = sizeof(text) - 1, .owned = false})
|
||||||
|
#define _CString_To_String(text) \
|
||||||
|
((string){.data = (uint8_t *)text, .len = strlen(text), .owned = false})
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *data;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
// FIXME: this is so terribly bad please don't do this
|
||||||
|
bool owned;
|
||||||
|
} string;
|
||||||
|
|
||||||
|
bool string_eq(string a, string b);
|
||||||
|
bool string_eq_cstring(string a, const char *b);
|
||||||
|
string string_copy(string s);
|
||||||
|
string string_copy_cstring(const char *str);
|
||||||
|
char *cstring_copy_string(string str);
|
||||||
|
|
||||||
|
#ifdef ED_STRING_IMPLEMENTATION
|
||||||
|
bool string_eq(string a, string b) {
|
||||||
|
if (a.len != b.len || a.data == NULL || b.data == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < a.len; ++i) {
|
||||||
|
if (a.data[i] != b.data[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool string_eq_cstring(string a, const char *b) {
|
||||||
|
if (b == NULL) {
|
||||||
|
if (a.len == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t b_len = strlen(b);
|
||||||
|
if (a.len != b_len)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < a.len; ++i) {
|
||||||
|
if (a.data[i] != b[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string string_copy(string a) {
|
||||||
|
if (a.data == NULL || a.len == 0) {
|
||||||
|
return (string){.data = NULL, .len = 0, .owned = false};
|
||||||
|
}
|
||||||
|
|
||||||
|
string new_string;
|
||||||
|
|
||||||
|
new_string.data = malloc(a.len * sizeof(uint8_t));
|
||||||
|
new_string.len = a.len;
|
||||||
|
new_string.owned = true;
|
||||||
|
|
||||||
|
memcpy(new_string.data, a.data, new_string.len * sizeof(uint8_t));
|
||||||
|
|
||||||
|
return new_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
string string_copy_cstring(const char *str) {
|
||||||
|
if (str == NULL) {
|
||||||
|
return (string){.data = NULL, .len = 0, .owned = false};
|
||||||
|
}
|
||||||
|
|
||||||
|
string new_string;
|
||||||
|
|
||||||
|
size_t len = strlen(str);
|
||||||
|
|
||||||
|
new_string.data = malloc(len * sizeof(uint8_t));
|
||||||
|
new_string.len = len;
|
||||||
|
new_string.owned = true;
|
||||||
|
|
||||||
|
memcpy(new_string.data, str, new_string.len * sizeof(uint8_t));
|
||||||
|
|
||||||
|
return new_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *cstring_copy_string(string str) {
|
||||||
|
char *new_str = malloc(str.len + 1);
|
||||||
|
|
||||||
|
memcpy(new_str, str.data, str.len);
|
||||||
|
new_str[str.len] = 0;
|
||||||
|
|
||||||
|
return new_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
Loading…
Reference in New Issue