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