Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e768b38322 | ||
| aba462447d | |||
| 6a1392b9b6 | |||
| 06f784d0b1 | |||
|
|
2165ac748d | ||
|
|
0cf82a800e | ||
| 554da7d0ec | |||
| fceac125c5 | |||
| e71ba138c0 | |||
|
|
e25004f59c | ||
| a7d03deba6 | |||
| 0864db1464 | |||
| e1ee236905 | |||
| ff41c8ace4 | |||
| c300276c7d | |||
| 2b410eee24 | |||
| 7a8efd8e5e | |||
| 1dd8020cbb | |||
|
|
737ff6ff3a | ||
|
|
7ec69b3067 | ||
|
|
344056744d | ||
|
|
6958228da7 | ||
|
|
95b781a4b9 | ||
|
|
a89ef8ddde | ||
|
|
f250254ae3 | ||
|
|
394aa07bb1 | ||
|
|
0d1367fb02 | ||
|
|
d4b1deb501 | ||
|
|
e463593f4e | ||
|
|
ec735331d2 | ||
|
|
eb0b03e15c | ||
|
|
aefccfa350 | ||
|
|
a0818f0950 | ||
|
|
c0deceec65 | ||
|
|
25384c743d | ||
|
|
994cbc688f | ||
|
|
b84b59df09 | ||
| 6872ebf418 | |||
| ef55ec5ab4 | |||
| 595259b2cc | |||
|
|
2daee71548 | ||
|
|
50f4501c86 | ||
|
|
256292c20d |
12
.clangd
Normal file
12
.clangd
Normal file
@@ -0,0 +1,12 @@
|
||||
CompileFlags:
|
||||
Add:
|
||||
- -std=c99
|
||||
- -xc
|
||||
- -Wno-initializer-overrides
|
||||
# LINUX FLAGS
|
||||
- -DOS_LINUX
|
||||
- -DCOMPOSITOR_WAYLAND
|
||||
- -DDJSTDLIB_DEBUG
|
||||
# WINDOW FLAGS
|
||||
# - -DOS_WINDOWS
|
||||
# - "-IC:\\source\\libs\\include"
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
.cache
|
||||
.vscode
|
||||
/zig-cache
|
||||
/zig-out
|
||||
/target
|
||||
perf.data
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "src/lib/djstdlib"]
|
||||
path = src/lib/djstdlib
|
||||
url = https://git.djledda.net/Ledda/djstdlib.git
|
||||
@@ -1,98 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(somaesque)
|
||||
|
||||
set(VENDOR_DIR "${CMAKE_CURRENT_SOURCE_DIR}/vendor")
|
||||
set(SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS true)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
option(GLFW_BUILD_DOCS OFF)
|
||||
option(GLFW_BUILD_EXAMPLES OFF)
|
||||
option(GLFW_BUILD_TESTS OFF)
|
||||
option(GLFW_INSTALL OFF)
|
||||
|
||||
find_package(glfw3 3.3 REQUIRED)
|
||||
find_package(glm REQUIRED)
|
||||
|
||||
# Glad
|
||||
add_library(glad
|
||||
STATIC
|
||||
${VENDOR_DIR}/glad/glad.c
|
||||
)
|
||||
target_include_directories(glad
|
||||
PUBLIC
|
||||
${VENDOR_DIR}
|
||||
)
|
||||
|
||||
# STB
|
||||
add_library(loaders
|
||||
STATIC
|
||||
${VENDOR_DIR}/loaders/tinyobj.cpp
|
||||
${VENDOR_DIR}/loaders/stb_image.cpp
|
||||
)
|
||||
target_include_directories(loaders
|
||||
PUBLIC
|
||||
${VENDOR_DIR}
|
||||
)
|
||||
|
||||
# somaesque
|
||||
add_executable(${PROJECT_NAME})
|
||||
target_sources(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${SRC_DIR}/main.cpp
|
||||
${SRC_DIR}/VoxelSpace.cpp
|
||||
${SRC_DIR}/VoxelSpace.h
|
||||
${SRC_DIR}/SomaSolve.cpp
|
||||
${SRC_DIR}/SomaSolve.h
|
||||
${SRC_DIR}/gfx/Texture.h
|
||||
${SRC_DIR}/gfx/Texture.cpp
|
||||
${SRC_DIR}/gfx/Mesh.h
|
||||
${SRC_DIR}/gfx/Mesh.cpp
|
||||
${SRC_DIR}/gfx/Shader.h
|
||||
${SRC_DIR}/gfx/Shader.cpp
|
||||
${SRC_DIR}/gfx/Color.h
|
||||
${SRC_DIR}/gfx/Color.cpp
|
||||
${SRC_DIR}/gfx/geometry.h
|
||||
${SRC_DIR}/gfx/geometry.cpp
|
||||
)
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
glfw
|
||||
GL
|
||||
X11
|
||||
pthread
|
||||
Xrandr
|
||||
dl
|
||||
glm::glm
|
||||
glad
|
||||
loaders
|
||||
)
|
||||
target_include_directories(somaesque
|
||||
PUBLIC
|
||||
${VENDOR_DIR}/KHR
|
||||
)
|
||||
|
||||
# TESTING
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
|
||||
)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
|
||||
enable_testing()
|
||||
add_executable(tests
|
||||
${SRC_DIR}/tests.cpp
|
||||
${SRC_DIR}/VoxelSpace.cpp
|
||||
${SRC_DIR}/VoxelSpace.h
|
||||
)
|
||||
|
||||
target_link_libraries(tests
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(tests)
|
||||
BIN
assets/fonts/KodeMono.ttf
Normal file
BIN
assets/fonts/KodeMono.ttf
Normal file
Binary file not shown.
57
assets/shaders/2d-solid.fragment.glsl
Normal file
57
assets/shaders/2d-solid.fragment.glsl
Normal file
@@ -0,0 +1,57 @@
|
||||
#version 330 core
|
||||
out vec4 pixel_color;
|
||||
|
||||
in vec4 frag_color;
|
||||
in vec2 frag_dest_position;
|
||||
in vec2 frag_dest_center;
|
||||
in vec2 frag_dest_half_size;
|
||||
in float frag_softness;
|
||||
in float frag_border_radius;
|
||||
in float frag_border_thickness;
|
||||
in vec4 frag_border_color;
|
||||
|
||||
float roundedRectSDF(vec2 sample_pos, vec2 rect_center, vec2 rect_half_size, float r) {
|
||||
vec2 d2 = (abs(rect_center - sample_pos) - rect_half_size + vec2(r, r));
|
||||
return min(max(d2.x, d2.y), 0.0) + length(max(d2, 0.0)) - r;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 softness_padding = vec2(
|
||||
max(0, frag_softness*2-1),
|
||||
max(0, frag_softness*2-1));
|
||||
|
||||
float border_factor = 1.0f;
|
||||
if (frag_border_thickness != 0) {
|
||||
vec2 interior_half_size = frag_dest_half_size - vec2(frag_border_thickness);
|
||||
|
||||
float interior_radius_reduce_f = min(
|
||||
interior_half_size.x / frag_dest_half_size.x,
|
||||
interior_half_size.y / frag_dest_half_size.y);
|
||||
|
||||
float interior_corner_radius = frag_border_radius * interior_radius_reduce_f * interior_radius_reduce_f;
|
||||
|
||||
float inside_d = roundedRectSDF(
|
||||
frag_dest_position,
|
||||
frag_dest_center,
|
||||
interior_half_size - softness_padding,
|
||||
interior_corner_radius);
|
||||
|
||||
|
||||
float inside_f = smoothstep(0, 2*frag_softness, inside_d);
|
||||
border_factor = inside_f;
|
||||
}
|
||||
|
||||
float dist = roundedRectSDF(
|
||||
frag_dest_position,
|
||||
frag_dest_center,
|
||||
frag_dest_half_size - softness_padding,
|
||||
frag_border_radius);
|
||||
|
||||
// For texturing later
|
||||
float sample = 1;
|
||||
|
||||
float sdf_factor = 1 - smoothstep(0, 2*frag_softness, dist);
|
||||
|
||||
pixel_color = frag_border_color * sample * sdf_factor * border_factor
|
||||
+ frag_color * sample * sdf_factor;
|
||||
};
|
||||
47
assets/shaders/2d-solid.vertex.glsl
Normal file
47
assets/shaders/2d-solid.vertex.glsl
Normal file
@@ -0,0 +1,47 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 p0;
|
||||
layout (location = 1) in vec2 p1;
|
||||
layout (location = 2) in vec4 color;
|
||||
layout (location = 3) in float border_radius;
|
||||
layout (location = 4) in float border_thickness;
|
||||
layout (location = 5) in vec4 border_color;
|
||||
layout (location = 6) in float edge_softness;
|
||||
|
||||
uniform mat4 projection;
|
||||
|
||||
out vec4 frag_color;
|
||||
out vec2 frag_dest_position;
|
||||
out vec2 frag_dest_center;
|
||||
out vec2 frag_dest_half_size;
|
||||
out float frag_softness;
|
||||
out float frag_border_radius;
|
||||
out float frag_border_thickness;
|
||||
out vec4 frag_border_color;
|
||||
|
||||
const vec2 rectangle_vertices[4] = vec2[](
|
||||
vec2(-1, -1),
|
||||
vec2(-1, 1),
|
||||
vec2( 1, -1),
|
||||
vec2( 1, 1)
|
||||
);
|
||||
|
||||
void main() {
|
||||
vec2 dest_half_size = (p1 - p0) / 2;
|
||||
vec2 dest_center = (p1 + p0) / 2;
|
||||
vec2 dest_position = rectangle_vertices[gl_VertexID] * dest_half_size + dest_center;
|
||||
|
||||
gl_Position = projection * vec4(
|
||||
dest_position,
|
||||
0,
|
||||
1
|
||||
);
|
||||
|
||||
frag_color = color;
|
||||
frag_dest_position = dest_position;
|
||||
frag_dest_center = dest_center;
|
||||
frag_dest_half_size = dest_half_size;
|
||||
frag_border_radius = border_radius;
|
||||
frag_border_thickness = border_thickness;
|
||||
frag_border_color = border_color;
|
||||
frag_softness = edge_softness;
|
||||
}
|
||||
10
assets/shaders/2d-tex.fragment.glsl
Normal file
10
assets/shaders/2d-tex.fragment.glsl
Normal file
@@ -0,0 +1,10 @@
|
||||
#version 330 core
|
||||
out vec4 frag_color;
|
||||
|
||||
uniform sampler2D texture;
|
||||
|
||||
in vec2 uv;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(texture, uv);
|
||||
};
|
||||
12
assets/shaders/2d-tex.vertex.glsl
Normal file
12
assets/shaders/2d-tex.vertex.glsl
Normal file
@@ -0,0 +1,12 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 xy;
|
||||
layout (location = 1) in vec2 uv;
|
||||
|
||||
uniform mat4 projection;
|
||||
|
||||
out vec2 tex_coord;
|
||||
|
||||
void main() {
|
||||
gl_Position = projection * vec4(xy, 0.0, 1.0);
|
||||
tex_coord = uv;
|
||||
}
|
||||
@@ -2,34 +2,28 @@
|
||||
out vec4 frag_color;
|
||||
|
||||
uniform vec3 light_pos;
|
||||
uniform vec3 solid_color;
|
||||
uniform vec4 solid_color;
|
||||
uniform vec3 camera;
|
||||
|
||||
in vec3 normal_cameraspace;
|
||||
in vec3 light_direction_cameraspace;
|
||||
in vec3 eye_direction_cameraspace;
|
||||
in vec3 position_worldspace;
|
||||
in vec3 normal;
|
||||
in vec3 frag_position;
|
||||
|
||||
void main() {
|
||||
vec4 material_diffuse_color = vec4(solid_color, 1);
|
||||
vec4 material_ambient_color = vec4(0.1, 0.1, 0.1, 1.0) * material_diffuse_color;
|
||||
vec4 material_specular_color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
vec3 normal = normalize(normal_cameraspace);
|
||||
vec3 light_dir = normalize(light_direction_cameraspace);
|
||||
float cos_theta = clamp(dot(normal, light_dir), 0, 1);
|
||||
|
||||
vec3 eye = normalize(eye_direction_cameraspace);
|
||||
vec3 reflected = reflect(-light_dir, normal);
|
||||
float cos_alpha = clamp(dot(eye, reflected), 0, 1);
|
||||
|
||||
vec4 light_color = vec4(1, 1, 1, 1);
|
||||
float light_power = 60;
|
||||
vec3 normal_norm = normalize(normal);
|
||||
vec3 light_direction_norm = normalize(light_pos - frag_position);
|
||||
|
||||
float distance = length(light_pos - position_worldspace);
|
||||
float dist_sq = pow(distance, 2);
|
||||
float ambient_strength = 0.15;
|
||||
vec4 ambient = ambient_strength * light_color;
|
||||
|
||||
vec4 diffuse = material_diffuse_color * light_color * light_power * cos_theta / dist_sq;
|
||||
vec4 specular = material_specular_color * light_color * light_power * pow(cos_alpha, 10) / dist_sq;
|
||||
float diffuse_strength = max(dot(normal_norm, light_direction_norm), 0.0);
|
||||
vec4 diffuse = diffuse_strength * light_color;
|
||||
|
||||
frag_color = material_ambient_color + diffuse + specular;
|
||||
};
|
||||
float specular_strength = 0.9;
|
||||
vec3 view_direction_norm = normalize(camera - frag_position);
|
||||
vec3 reflect_dir = reflect(-light_direction_norm, normal_norm);
|
||||
float spec = pow(max(dot(view_direction_norm, reflect_dir), 0.0), 32);
|
||||
vec4 specular = specular_strength * spec * light_color;
|
||||
|
||||
frag_color = specular + (ambient + diffuse) * solid_color;
|
||||
}
|
||||
|
||||
@@ -6,21 +6,14 @@ layout (location = 2) in vec3 a_normal;
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
uniform vec3 light_pos;
|
||||
|
||||
out vec3 normal_cameraspace;
|
||||
out vec3 light_direction_cameraspace;
|
||||
out vec3 eye_direction_cameraspace;
|
||||
out vec3 position_worldspace;
|
||||
out vec3 normal;
|
||||
out vec3 frag_position;
|
||||
|
||||
void main() {
|
||||
vec3 vertex_cameraspace = (view * model * vec4(a_xyz, 1)).xyz;
|
||||
vec3 light_pos_cameraspace = (view * vec4(light_pos, 1)).xyz;
|
||||
vec4 a_xyz_vec4 = vec4(a_xyz, 1);
|
||||
|
||||
normal_cameraspace = (transpose(inverse(view * model)) * vec4(a_normal, 0)).xyz;
|
||||
light_direction_cameraspace = light_pos_cameraspace + eye_direction_cameraspace;
|
||||
eye_direction_cameraspace = vec3(0, 0, 0) - vertex_cameraspace;
|
||||
position_worldspace = (model * vec4(a_xyz, 1)).xyz;
|
||||
|
||||
gl_Position = projection * view * model * vec4(a_xyz, 1);
|
||||
frag_position = (model * a_xyz_vec4).xyz;
|
||||
normal = mat3(transpose(inverse(model))) * a_normal;
|
||||
gl_Position = projection * view * model * a_xyz_vec4;
|
||||
}
|
||||
|
||||
11
assets/shaders/text.fragment.glsl
Normal file
11
assets/shaders/text.fragment.glsl
Normal file
@@ -0,0 +1,11 @@
|
||||
#version 330 core
|
||||
out vec4 pixel_color;
|
||||
|
||||
in vec4 frag_color;
|
||||
in vec2 frag_uv_position;
|
||||
|
||||
uniform sampler2D glyph_atlas;
|
||||
|
||||
void main() {
|
||||
pixel_color = vec4(1,1,1,texture(glyph_atlas, frag_uv_position).r);
|
||||
};
|
||||
58
assets/shaders/text.vertex.glsl
Normal file
58
assets/shaders/text.vertex.glsl
Normal file
@@ -0,0 +1,58 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec2 begin;
|
||||
layout (location = 1) in int glyph;
|
||||
layout (location = 2) in float fontSize;
|
||||
layout (location = 3) in vec4 color;
|
||||
|
||||
uniform samplerBuffer glyph_table;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform sampler2D font;
|
||||
|
||||
out vec4 frag_color;
|
||||
out vec2 frag_uv_position;
|
||||
|
||||
const vec2 rectangle_vertices[4] = vec2[](
|
||||
vec2(-1, -1), // bl
|
||||
vec2(-1, 1), // tl
|
||||
vec2( 1, -1), // br
|
||||
vec2( 1, 1) // tr
|
||||
);
|
||||
|
||||
const vec2 uv0_vertices[4] = vec2[](
|
||||
vec2(1, 0), // bl
|
||||
vec2(1, 1), // tl
|
||||
vec2(0, 0), // br
|
||||
vec2(0, 1) // tr
|
||||
);
|
||||
|
||||
const vec2 uv1_vertices[4] = vec2[](
|
||||
vec2(0, 1),
|
||||
vec2(0, 0),
|
||||
vec2(1, 1),
|
||||
vec2(1, 0)
|
||||
);
|
||||
|
||||
void main() {
|
||||
vec4 chunk1 = texelFetch(glyph_table, glyph * 2 + 0);
|
||||
vec4 chunk2 = texelFetch(glyph_table, glyph * 2 + 1);
|
||||
|
||||
vec2 uv0 = chunk1.xy;
|
||||
vec2 uv1 = chunk1.zw;
|
||||
vec2 offset = chunk2.xy;
|
||||
vec2 dims = chunk2.zw;
|
||||
|
||||
vec2 p0 = begin + offset*fontSize;
|
||||
vec2 p1 = begin + (offset + dims)*fontSize;
|
||||
|
||||
vec2 dest_half_size = (p1 - p0) / 2;
|
||||
vec2 dest_center = (p1 + p0) / 2;
|
||||
vec2 dest_position = rectangle_vertices[gl_VertexID] * dest_half_size + dest_center;
|
||||
|
||||
vec2 uv_position = uv0 * uv0_vertices[gl_VertexID] + uv1 * uv1_vertices[gl_VertexID];
|
||||
|
||||
gl_Position = projection * vec4(dest_position, 0, 1);
|
||||
|
||||
frag_color = color;
|
||||
frag_uv_position = vec2(uv_position.x, uv_position.y);
|
||||
}
|
||||
17
build
Executable file
17
build
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
LIB_INCLUDE="-lglfw -lGL -lm"
|
||||
COMMON_FLAGS="-DOS_LINUX=1 -DCOMPOSITOR_WAYLAND=1 -xc -std=c99 -Wno-initializer-overrides"
|
||||
|
||||
echo [Building target]
|
||||
if [ $DEBUG ]; then
|
||||
time clang -O0 -g -g2 $COMMON_FLAGS -DDJSTDLIB_DEBUG=1 ./src/main.c -o ./target/somaesque $LIB_INCLUDE
|
||||
else
|
||||
time clang -O2 $COMMON_FLAGS ./src/main.c -o ./target/somaesque $LIB_INCLUDE
|
||||
fi
|
||||
echo [Target built]
|
||||
|
||||
if [ "$1" == "run" ]; then
|
||||
./target/somaesque
|
||||
fi
|
||||
|
||||
29
build.bat
Normal file
29
build.bat
Normal file
@@ -0,0 +1,29 @@
|
||||
@echo off
|
||||
|
||||
if NOT EXIST .\target mkdir .\target
|
||||
|
||||
set LIBRARIES_ROOT="C:\source\libs"
|
||||
set INCLUDE_ROOT="C:\source\libs\include"
|
||||
|
||||
set LINK_LIBRARIES=".\glfw\glfw3.lib" "user32.lib" "gdi32.lib" "opengl32.lib" "shell32.lib" "kernel32.lib"
|
||||
set COMMON_LINKER_FLAGS=-opt:ref /LIBPATH:%LIBRARIES_ROOT% /NODEFAULTLIB:libcmt.lib
|
||||
set COMMON_COMPILER_FLAGS=^
|
||||
-MT %= Make sure the C runtime library is statically linked =%^
|
||||
-Gm- %= Turns off incremental building =%^
|
||||
-nologo %= No one cares you made the compiler Microsoft =%^
|
||||
-Oi %= Always use intrinsics =%^
|
||||
-WX -W4 -wd4201 -wd4100 -wd4189 -wd4505 %= Compiler warnings, -WX warnings as errors, -W4 warning level 4, -wdXXXX disable warning XXXX =%^
|
||||
-DAPP_DEBUG=0 -DENABLE_ASSERT=1 -DOS_WINDOWS=1 %= Custom #defines =%^
|
||||
-D_CRT_SECURE_NO_WARNINGS=1^
|
||||
-FC %= Full path of source code file in diagnostics =%^
|
||||
-Zi %= Generate debugger info =%
|
||||
|
||||
pushd .\target
|
||||
cl %COMMON_COMPILER_FLAGS% /EHsc /I %INCLUDE_ROOT% -Fe:.\somaesque.exe ..\src\main.cpp /link -incremental:no %COMMON_LINKER_FLAGS% %LINK_LIBRARIES%
|
||||
popd
|
||||
|
||||
exit /b
|
||||
|
||||
:error
|
||||
echo Failed with error #%errorlevel%.
|
||||
exit /b %errorlevel%
|
||||
55
build.zig
55
build.zig
@@ -1,55 +0,0 @@
|
||||
const std = @import("std");
|
||||
const zmath = @import("lib/zmath/build.zig");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
// Standard target options allows the person running `zig build` to choose
|
||||
// what target to build for. Here we do not override the defaults, which
|
||||
// means any target is allowed, and the default is native. Other options
|
||||
// for restricting supported target set are available.
|
||||
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
// Standard release options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||
const mode = b.standardOptimizeOption(.{});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "somaesque-native-zig",
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.target = target,
|
||||
.optimize = mode,
|
||||
});
|
||||
exe.addIncludePath("/usr/local/include");
|
||||
|
||||
exe.linkLibC();
|
||||
exe.linkSystemLibrary("glfw3");
|
||||
exe.linkSystemLibrary("glm");
|
||||
exe.linkSystemLibrary("GL");
|
||||
exe.addIncludePath("lib/c");
|
||||
|
||||
exe.addCSourceFile("lib/c/glad/glad.c", &[_][]const u8{"-std=c11"});
|
||||
|
||||
exe.install();
|
||||
|
||||
// zmath
|
||||
const zmath_pkg = zmath.package(b, target, mode, .{
|
||||
.options = .{ .enable_cross_platform_determinism = true },
|
||||
});
|
||||
zmath_pkg.link(exe);
|
||||
|
||||
const run_cmd = exe.run();
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
//const exe_tests = b.addTest("src/main.zig");
|
||||
//exe_tests.setTarget(target);
|
||||
//exe_tests.setBuildMode(mode);
|
||||
|
||||
//const test_step = b.step("test", "Run unit tests");
|
||||
//test_step.dependOn(&exe_tests.step);
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
# zmath v0.9.6 - SIMD math library for game developers
|
||||
|
||||
Tested on x86_64 and AArch64.
|
||||
|
||||
Provides ~140 optimized routines and ~70 extensive tests.
|
||||
|
||||
Can be used with any graphics API.
|
||||
|
||||
Documentation can be found [here](https://github.com/michal-z/zig-gamedev/blob/main/libs/zmath/src/zmath.zig).
|
||||
|
||||
Benchamrks can be found [here](https://github.com/michal-z/zig-gamedev/blob/main/libs/zmath/src/benchmark.zig).
|
||||
|
||||
An intro article can be found [here](https://zig.news/michalz/fast-multi-platform-simd-math-library-in-zig-2adn).
|
||||
|
||||
## Getting started
|
||||
|
||||
Copy `zmath` folder to a `libs` subdirectory of the root of your project.
|
||||
|
||||
Then in your `build.zig` add:
|
||||
|
||||
```zig
|
||||
const std = @import("std");
|
||||
const zmath = @import("libs/zmath/build.zig");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
...
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
zmath_pkg = zmath.package(b, target, optimize, .{
|
||||
.options = .{ .enable_cross_platform_determinism = true },
|
||||
});
|
||||
|
||||
zmath_pkg.link(exe);
|
||||
}
|
||||
```
|
||||
|
||||
Now in your code you may import and use zmath:
|
||||
|
||||
```zig
|
||||
const zm = @import("zmath");
|
||||
|
||||
pub fn main() !void {
|
||||
//
|
||||
// OpenGL/Vulkan example
|
||||
//
|
||||
const object_to_world = zm.rotationY(..);
|
||||
const world_to_view = zm.lookAtRh(
|
||||
zm.f32x4(3.0, 3.0, 3.0, 1.0), // eye position
|
||||
zm.f32x4(0.0, 0.0, 0.0, 1.0), // focus point
|
||||
zm.f32x4(0.0, 1.0, 0.0, 0.0), // up direction ('w' coord is zero because this is a vector not a point)
|
||||
);
|
||||
// `perspectiveFovRhGl` produces Z values in [-1.0, 1.0] range (Vulkan app should use `perspectiveFovRh`)
|
||||
const view_to_clip = zm.perspectiveFovRhGl(0.25 * math.pi, aspect_ratio, 0.1, 20.0);
|
||||
|
||||
const object_to_view = zm.mul(object_to_world, world_to_view);
|
||||
const object_to_clip = zm.mul(object_to_view, view_to_clip);
|
||||
|
||||
// Transposition is needed because GLSL uses column-major matrices by default
|
||||
gl.uniformMatrix4fv(0, 1, gl.TRUE, zm.arrNPtr(&object_to_clip));
|
||||
|
||||
// In GLSL: gl_Position = vec4(in_position, 1.0) * object_to_clip;
|
||||
|
||||
//
|
||||
// DirectX example
|
||||
//
|
||||
const object_to_world = zm.rotationY(..);
|
||||
const world_to_view = zm.lookAtLh(
|
||||
zm.f32x4(3.0, 3.0, -3.0, 1.0), // eye position
|
||||
zm.f32x4(0.0, 0.0, 0.0, 1.0), // focus point
|
||||
zm.f32x4(0.0, 1.0, 0.0, 0.0), // up direction ('w' coord is zero because this is a vector not a point)
|
||||
);
|
||||
const view_to_clip = zm.perspectiveFovLh(0.25 * math.pi, aspect_ratio, 0.1, 20.0);
|
||||
|
||||
const object_to_view = zm.mul(object_to_world, world_to_view);
|
||||
const object_to_clip = zm.mul(object_to_view, view_to_clip);
|
||||
|
||||
// Transposition is needed because HLSL uses column-major matrices by default
|
||||
const mem = allocateUploadMemory(...);
|
||||
zm.storeMat(mem, zm.transpose(object_to_clip));
|
||||
|
||||
// In HLSL: out_position_sv = mul(float4(in_position, 1.0), object_to_clip);
|
||||
|
||||
//
|
||||
// 'WASD' camera movement example
|
||||
//
|
||||
{
|
||||
const speed = zm.f32x4s(10.0);
|
||||
const delta_time = zm.f32x4s(demo.frame_stats.delta_time);
|
||||
const transform = zm.mul(zm.rotationX(demo.camera.pitch), zm.rotationY(demo.camera.yaw));
|
||||
var forward = zm.normalize3(zm.mul(zm.f32x4(0.0, 0.0, 1.0, 0.0), transform));
|
||||
|
||||
zm.storeArr3(&demo.camera.forward, forward);
|
||||
|
||||
const right = speed * delta_time * zm.normalize3(zm.cross3(zm.f32x4(0.0, 1.0, 0.0, 0.0), forward));
|
||||
forward = speed * delta_time * forward;
|
||||
|
||||
var cam_pos = zm.loadArr3(demo.camera.position);
|
||||
|
||||
if (keyDown('W')) {
|
||||
cam_pos += forward;
|
||||
} else if (keyDown('S')) {
|
||||
cam_pos -= forward;
|
||||
}
|
||||
if (keyDown('D')) {
|
||||
cam_pos += right;
|
||||
} else if (keyDown('A')) {
|
||||
cam_pos -= right;
|
||||
}
|
||||
|
||||
zm.storeArr3(&demo.camera.position, cam_pos);
|
||||
}
|
||||
|
||||
//
|
||||
// SIMD wave equation solver example (works with vector width 4, 8 and 16)
|
||||
// 'T' can be F32x4, F32x8 or F32x16
|
||||
//
|
||||
var z_index: i32 = 0;
|
||||
while (z_index < grid_size) : (z_index += 1) {
|
||||
const z = scale * @intToFloat(f32, z_index - grid_size / 2);
|
||||
const vz = zm.splat(T, z);
|
||||
|
||||
var x_index: i32 = 0;
|
||||
while (x_index < grid_size) : (x_index += zm.veclen(T)) {
|
||||
const x = scale * @intToFloat(f32, x_index - grid_size / 2);
|
||||
const vx = zm.splat(T, x) + voffset * zm.splat(T, scale);
|
||||
|
||||
const d = zm.sqrt(vx * vx + vz * vz);
|
||||
const vy = zm.sin(d - vtime);
|
||||
|
||||
const index = @intCast(usize, x_index + z_index * grid_size);
|
||||
zm.store(xslice[index..], vx, 0);
|
||||
zm.store(yslice[index..], vy, 0);
|
||||
zm.store(zslice[index..], vz, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,97 +0,0 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const Options = struct {
|
||||
enable_cross_platform_determinism: bool = true,
|
||||
};
|
||||
|
||||
pub const Package = struct {
|
||||
options: Options,
|
||||
zmath: *std.Build.Module,
|
||||
zmath_options: *std.Build.Module,
|
||||
|
||||
pub fn link(pkg: Package, exe: *std.Build.CompileStep) void {
|
||||
exe.addModule("zmath", pkg.zmath);
|
||||
exe.addModule("zmath_options", pkg.zmath_options);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn package(
|
||||
b: *std.Build,
|
||||
_: std.zig.CrossTarget,
|
||||
_: std.builtin.Mode,
|
||||
args: struct {
|
||||
options: Options = .{},
|
||||
},
|
||||
) Package {
|
||||
const step = b.addOptions();
|
||||
step.addOption(
|
||||
bool,
|
||||
"enable_cross_platform_determinism",
|
||||
args.options.enable_cross_platform_determinism,
|
||||
);
|
||||
|
||||
const zmath_options = step.createModule();
|
||||
|
||||
const zmath = b.createModule(.{
|
||||
.source_file = .{ .path = thisDir() ++ "/src/main.zig" },
|
||||
.dependencies = &.{
|
||||
.{ .name = "zmath_options", .module = zmath_options },
|
||||
},
|
||||
});
|
||||
|
||||
return .{
|
||||
.options = args.options,
|
||||
.zmath = zmath,
|
||||
.zmath_options = zmath_options,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
const test_step = b.step("test", "Run zmath tests");
|
||||
test_step.dependOn(runTests(b, optimize, target));
|
||||
|
||||
const benchmark_step = b.step("benchmark", "Run zmath benchmarks");
|
||||
benchmark_step.dependOn(runBenchmarks(b, target));
|
||||
}
|
||||
|
||||
pub fn runTests(
|
||||
b: *std.Build,
|
||||
optimize: std.builtin.Mode,
|
||||
target: std.zig.CrossTarget,
|
||||
) *std.Build.Step {
|
||||
const tests = b.addTest(.{
|
||||
.name = "zmath-tests",
|
||||
.root_source_file = .{ .path = thisDir() ++ "/src/main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const zmath_pkg = package(b, target, optimize, .{});
|
||||
tests.addModule("zmath_options", zmath_pkg.zmath_options);
|
||||
|
||||
return &tests.run().step;
|
||||
}
|
||||
|
||||
pub fn runBenchmarks(
|
||||
b: *std.Build,
|
||||
target: std.zig.CrossTarget,
|
||||
) *std.Build.Step {
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "zmath-benchmarks",
|
||||
.root_source_file = .{ .path = thisDir() ++ "/src/benchmark.zig" },
|
||||
.target = target,
|
||||
.optimize = .ReleaseFast,
|
||||
});
|
||||
|
||||
const zmath_pkg = package(b, target, .ReleaseFast, .{});
|
||||
exe.addModule("zmath", zmath_pkg.zmath);
|
||||
|
||||
return &exe.run().step;
|
||||
}
|
||||
|
||||
inline fn thisDir() []const u8 {
|
||||
return comptime std.fs.path.dirname(@src().file) orelse ".";
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// zmath - benchmarks
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// 'zig build benchmark' in the root project directory will build and run 'ReleaseFast' configuration.
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// 'AMD Ryzen 9 3950X 16-Core Processor', Windows 11, Zig 0.10.0-dev.2620+0e9458a3f
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// matrix mul benchmark (AOS) - scalar version: 1.5880s, zmath version: 1.0642s
|
||||
// cross3, scale, bias benchmark (AOS) - scalar version: 0.9318s, zmath version: 0.6888s
|
||||
// cross3, dot3, scale, bias benchmark (AOS) - scalar version: 1.2258s, zmath version: 1.1095s
|
||||
// quaternion mul benchmark (AOS) - scalar version: 1.4123s, zmath version: 0.6958s
|
||||
// wave benchmark (SOA) - scalar version: 4.8165s, zmath version: 0.7338s
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// 'AMD Ryzen 7 5800X 8-Core Processer', Linux 5.17.14, Zig 0.10.0-dev.2624+d506275a0
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// matrix mul benchmark (AOS) - scalar version: 1.3672s, zmath version: 0.8617s
|
||||
// cross3, scale, bias benchmark (AOS) - scalar version: 0.6586s, zmath version: 0.4803s
|
||||
// cross3, dot3, scale, bias benchmark (AOS) - scalar version: 1.0620s, zmath version: 0.8942s
|
||||
// quaternion mul benchmark (AOS) - scalar version: 1.1324s, zmath version: 0.6064s
|
||||
// wave benchmark (SOA) - scalar version: 3.6598s, zmath version: 0.4231s
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// 'Apple M1 Max', macOS Version 12.4, Zig 0.10.0-dev.2657+74442f350
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// matrix mul benchmark (AOS) - scalar version: 1.0297s, zmath version: 1.0538s
|
||||
// cross3, scale, bias benchmark (AOS) - scalar version: 0.6294s, zmath version: 0.6532s
|
||||
// cross3, dot3, scale, bias benchmark (AOS) - scalar version: 0.9807s, zmath version: 1.0988s
|
||||
// quaternion mul benchmark (AOS) - scalar version: 1.5413s, zmath version: 0.7800s
|
||||
// wave benchmark (SOA) - scalar version: 3.4220s, zmath version: 1.0255s
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// '11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz', Windows 11, Zig 0.10.0-dev.2620+0e9458a3f
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// matrix mul benchmark (AOS) - scalar version: 2.2308s, zmath version: 0.9376s
|
||||
// cross3, scale, bias benchmark (AOS) - scalar version: 1.0821s, zmath version: 0.5110s
|
||||
// cross3, dot3, scale, bias benchmark (AOS) - scalar version: 1.6580s, zmath version: 0.9167s
|
||||
// quaternion mul benchmark (AOS) - scalar version: 2.0139s, zmath version: 0.5856s
|
||||
// wave benchmark (SOA) - scalar version: 3.7832s, zmath version: 0.3642s
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
// m = mul(ma, mb); data set fits in L1 cache; AOS data layout.
|
||||
try mat4MulBenchmark(allocator, 100_000);
|
||||
|
||||
// v = 0.01 * cross3(va, vb) + vec3(1.0); data set fits in L1 cache; AOS data layout.
|
||||
try cross3ScaleBiasBenchmark(allocator, 10_000);
|
||||
|
||||
// v = dot3(va, vb) * (0.1 * cross3(va, vb) + vec3(1.0)); data set fits in L1 cache; AOS data layout.
|
||||
try cross3Dot3ScaleBiasBenchmark(allocator, 10_000);
|
||||
|
||||
// q = qmul(qa, qb); data set fits in L1 cache; AOS data layout.
|
||||
try quatBenchmark(allocator, 10_000);
|
||||
|
||||
// d = sqrt(x * x + z * z); y = sin(d - t); SOA layout.
|
||||
try waveBenchmark(allocator, 1_000);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const time = std.time;
|
||||
const Timer = time.Timer;
|
||||
const zm = @import("zmath");
|
||||
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
const random = prng.random();
|
||||
|
||||
noinline fn mat4MulBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
|
||||
std.debug.print("\n", .{});
|
||||
std.debug.print("{s:>42} - ", .{"matrix mul benchmark (AOS)"});
|
||||
|
||||
var data0 = std.ArrayList([16]f32).init(allocator);
|
||||
defer data0.deinit();
|
||||
var data1 = std.ArrayList([16]f32).init(allocator);
|
||||
defer data1.deinit();
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 64) : (i += 1) {
|
||||
try data0.append([16]f32{
|
||||
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||
});
|
||||
try data1.append([16]f32{
|
||||
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||
});
|
||||
}
|
||||
|
||||
// Warmup, fills L1 cache.
|
||||
i = 0;
|
||||
while (i < 100) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const ma = zm.loadMat(a[0..]);
|
||||
const mb = zm.loadMat(b[0..]);
|
||||
const r = zm.mul(ma, mb);
|
||||
std.mem.doNotOptimizeAway(&r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
i = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (i < count) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const r = [16]f32{
|
||||
a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12],
|
||||
a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13],
|
||||
a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14],
|
||||
a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15],
|
||||
a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12],
|
||||
a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13],
|
||||
a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14],
|
||||
a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15],
|
||||
a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12],
|
||||
a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13],
|
||||
a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14],
|
||||
a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15],
|
||||
a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12],
|
||||
a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13],
|
||||
a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14],
|
||||
a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15],
|
||||
};
|
||||
std.mem.doNotOptimizeAway(&r);
|
||||
}
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
|
||||
std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
|
||||
}
|
||||
|
||||
{
|
||||
i = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (i < count) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const ma = zm.loadMat(a[0..]);
|
||||
const mb = zm.loadMat(b[0..]);
|
||||
const r = zm.mul(ma, mb);
|
||||
std.mem.doNotOptimizeAway(&r);
|
||||
}
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
|
||||
std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
|
||||
}
|
||||
}
|
||||
|
||||
noinline fn cross3ScaleBiasBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
|
||||
std.debug.print("{s:>42} - ", .{"cross3, scale, bias benchmark (AOS)"});
|
||||
|
||||
var data0 = std.ArrayList([3]f32).init(allocator);
|
||||
defer data0.deinit();
|
||||
var data1 = std.ArrayList([3]f32).init(allocator);
|
||||
defer data1.deinit();
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 256) : (i += 1) {
|
||||
try data0.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
|
||||
try data1.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
|
||||
}
|
||||
|
||||
// Warmup, fills L1 cache.
|
||||
i = 0;
|
||||
while (i < 100) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const va = zm.loadArr3(a);
|
||||
const vb = zm.loadArr3(b);
|
||||
const cp = zm.f32x4s(0.01) * zm.cross3(va, vb) + zm.f32x4s(1.0);
|
||||
std.mem.doNotOptimizeAway(&cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
i = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (i < count) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const r = [3]f32{
|
||||
0.01 * (a[1] * b[2] - a[2] * b[1]) + 1.0,
|
||||
0.01 * (a[2] * b[0] - a[0] * b[2]) + 1.0,
|
||||
0.01 * (a[0] * b[1] - a[1] * b[0]) + 1.0,
|
||||
};
|
||||
std.mem.doNotOptimizeAway(&r);
|
||||
}
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
|
||||
std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
|
||||
}
|
||||
|
||||
{
|
||||
i = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (i < count) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const va = zm.loadArr3(a);
|
||||
const vb = zm.loadArr3(b);
|
||||
const cp = zm.f32x4s(0.01) * zm.cross3(va, vb) + zm.f32x4s(1.0);
|
||||
std.mem.doNotOptimizeAway(&cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
|
||||
std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
|
||||
}
|
||||
}
|
||||
|
||||
noinline fn cross3Dot3ScaleBiasBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
|
||||
std.debug.print("{s:>42} - ", .{"cross3, dot3, scale, bias benchmark (AOS)"});
|
||||
|
||||
var data0 = std.ArrayList([3]f32).init(allocator);
|
||||
defer data0.deinit();
|
||||
var data1 = std.ArrayList([3]f32).init(allocator);
|
||||
defer data1.deinit();
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 256) : (i += 1) {
|
||||
try data0.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
|
||||
try data1.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
|
||||
}
|
||||
|
||||
// Warmup, fills L1 cache.
|
||||
i = 0;
|
||||
while (i < 100) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const va = zm.loadArr3(a);
|
||||
const vb = zm.loadArr3(b);
|
||||
const r = (zm.dot3(va, vb) * (zm.f32x4s(0.1) * zm.cross3(va, vb) + zm.f32x4s(1.0)))[0];
|
||||
std.mem.doNotOptimizeAway(&r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
i = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (i < count) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const d = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
||||
const r = [3]f32{
|
||||
d * (0.1 * (a[1] * b[2] - a[2] * b[1]) + 1.0),
|
||||
d * (0.1 * (a[2] * b[0] - a[0] * b[2]) + 1.0),
|
||||
d * (0.1 * (a[0] * b[1] - a[1] * b[0]) + 1.0),
|
||||
};
|
||||
std.mem.doNotOptimizeAway(&r);
|
||||
}
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
|
||||
std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
|
||||
}
|
||||
|
||||
{
|
||||
i = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (i < count) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const va = zm.loadArr3(a);
|
||||
const vb = zm.loadArr3(b);
|
||||
const r = zm.dot3(va, vb) * (zm.f32x4s(0.1) * zm.cross3(va, vb) + zm.f32x4s(1.0));
|
||||
std.mem.doNotOptimizeAway(&r);
|
||||
}
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
|
||||
std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
|
||||
}
|
||||
}
|
||||
|
||||
noinline fn quatBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
|
||||
std.debug.print("{s:>42} - ", .{"quaternion mul benchmark (AOS)"});
|
||||
|
||||
var data0 = std.ArrayList([4]f32).init(allocator);
|
||||
defer data0.deinit();
|
||||
var data1 = std.ArrayList([4]f32).init(allocator);
|
||||
defer data1.deinit();
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 256) : (i += 1) {
|
||||
try data0.append([4]f32{ random.float(f32), random.float(f32), random.float(f32), random.float(f32) });
|
||||
try data1.append([4]f32{ random.float(f32), random.float(f32), random.float(f32), random.float(f32) });
|
||||
}
|
||||
|
||||
// Warmup, fills L1 cache.
|
||||
i = 0;
|
||||
while (i < 100) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const va = zm.loadArr4(a);
|
||||
const vb = zm.loadArr4(b);
|
||||
const r = zm.qmul(va, vb);
|
||||
std.mem.doNotOptimizeAway(&r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
i = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (i < count) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const r = [4]f32{
|
||||
(b[3] * a[0]) + (b[0] * a[3]) + (b[1] * a[2]) - (b[2] * a[1]),
|
||||
(b[3] * a[1]) - (b[0] * a[2]) + (b[1] * a[3]) + (b[2] * a[0]),
|
||||
(b[3] * a[2]) + (b[0] * a[1]) - (b[1] * a[0]) + (b[2] * a[3]),
|
||||
(b[3] * a[3]) - (b[0] * a[0]) - (b[1] * a[1]) - (b[2] * a[2]),
|
||||
};
|
||||
std.mem.doNotOptimizeAway(&r);
|
||||
}
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
|
||||
std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
|
||||
}
|
||||
|
||||
{
|
||||
i = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (i < count) : (i += 1) {
|
||||
for (data1.items) |b| {
|
||||
for (data0.items) |a| {
|
||||
const va = zm.loadArr4(a);
|
||||
const vb = zm.loadArr4(b);
|
||||
const r = zm.qmul(va, vb);
|
||||
std.mem.doNotOptimizeAway(&r);
|
||||
}
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
|
||||
std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
|
||||
}
|
||||
}
|
||||
|
||||
noinline fn waveBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
|
||||
_ = allocator;
|
||||
std.debug.print("{s:>42} - ", .{"wave benchmark (SOA)"});
|
||||
|
||||
const grid_size = 1024;
|
||||
{
|
||||
var t: f32 = 0.0;
|
||||
|
||||
const scale: f32 = 0.05;
|
||||
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
|
||||
var iter: usize = 0;
|
||||
while (iter < count) : (iter += 1) {
|
||||
var z_index: i32 = 0;
|
||||
while (z_index < grid_size) : (z_index += 1) {
|
||||
const z = scale * @intToFloat(f32, z_index - grid_size / 2);
|
||||
|
||||
var x_index: i32 = 0;
|
||||
while (x_index < grid_size) : (x_index += 4) {
|
||||
const x0 = scale * @intToFloat(f32, x_index + 0 - grid_size / 2);
|
||||
const x1 = scale * @intToFloat(f32, x_index + 1 - grid_size / 2);
|
||||
const x2 = scale * @intToFloat(f32, x_index + 2 - grid_size / 2);
|
||||
const x3 = scale * @intToFloat(f32, x_index + 3 - grid_size / 2);
|
||||
|
||||
const d0 = zm.sqrt(x0 * x0 + z * z);
|
||||
const d1 = zm.sqrt(x1 * x1 + z * z);
|
||||
const d2 = zm.sqrt(x2 * x2 + z * z);
|
||||
const d3 = zm.sqrt(x3 * x3 + z * z);
|
||||
|
||||
const y0 = zm.sin(d0 - t);
|
||||
const y1 = zm.sin(d1 - t);
|
||||
const y2 = zm.sin(d2 - t);
|
||||
const y3 = zm.sin(d3 - t);
|
||||
|
||||
std.mem.doNotOptimizeAway(&y0);
|
||||
std.mem.doNotOptimizeAway(&y1);
|
||||
std.mem.doNotOptimizeAway(&y2);
|
||||
std.mem.doNotOptimizeAway(&y3);
|
||||
}
|
||||
}
|
||||
t += 0.001;
|
||||
}
|
||||
const end = timer.read();
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
|
||||
std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
|
||||
}
|
||||
|
||||
{
|
||||
const T = zm.F32x16;
|
||||
|
||||
const static = struct {
|
||||
const offsets = [16]f32{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
};
|
||||
const voffset = zm.load(static.offsets[0..], T, 0);
|
||||
var vt = zm.splat(T, 0.0);
|
||||
|
||||
const scale: f32 = 0.05;
|
||||
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
|
||||
var iter: usize = 0;
|
||||
while (iter < count) : (iter += 1) {
|
||||
var z_index: i32 = 0;
|
||||
while (z_index < grid_size) : (z_index += 1) {
|
||||
const z = scale * @intToFloat(f32, z_index - grid_size / 2);
|
||||
const vz = zm.splat(T, z);
|
||||
|
||||
var x_index: i32 = 0;
|
||||
while (x_index < grid_size) : (x_index += zm.veclen(T)) {
|
||||
const x = scale * @intToFloat(f32, x_index - grid_size / 2);
|
||||
const vx = zm.splat(T, x) + voffset * zm.splat(T, scale);
|
||||
|
||||
const d = zm.sqrt(vx * vx + vz * vz);
|
||||
|
||||
const vy = zm.sin(d - vt);
|
||||
|
||||
std.mem.doNotOptimizeAway(&vy);
|
||||
}
|
||||
}
|
||||
vt += zm.splat(T, 0.001);
|
||||
}
|
||||
const end = timer.read();
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
|
||||
std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// SIMD math library for game developers
|
||||
// https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath
|
||||
//
|
||||
// See zmath.zig for more details.
|
||||
// See util.zig for additional functionality.
|
||||
//
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub const version = @import("std").SemanticVersion{ .major = 0, .minor = 9, .patch = 6 };
|
||||
|
||||
pub usingnamespace @import("zmath.zig");
|
||||
pub const util = @import("util.zig");
|
||||
|
||||
// ensure transitive closure of test coverage
|
||||
comptime {
|
||||
_ = util;
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
// ==============================================================================
|
||||
//
|
||||
// Collection of useful functions building on top of, and extending, core zmath.
|
||||
// https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath
|
||||
//
|
||||
// ------------------------------------------------------------------------------
|
||||
// 1. Matrix functions
|
||||
// ------------------------------------------------------------------------------
|
||||
//
|
||||
// As an example, in a left handed Y-up system:
|
||||
// getAxisX is equivalent to the right vector
|
||||
// getAxisY is equivalent to the up vector
|
||||
// getAxisZ is equivalent to the forward vector
|
||||
//
|
||||
// getTranslationVec(m: Mat) Vec
|
||||
// getAxisX(m: Mat) Vec
|
||||
// getAxisY(m: Mat) Vec
|
||||
// getAxisZ(m: Mat) Vec
|
||||
//
|
||||
// ==============================================================================
|
||||
|
||||
const zm = @import("zmath.zig");
|
||||
const std = @import("std");
|
||||
const math = std.math;
|
||||
const expect = std.testing.expect;
|
||||
|
||||
pub fn getTranslationVec(m: zm.Mat) zm.Vec {
|
||||
var translation = m[3];
|
||||
translation[3] = 0;
|
||||
return translation;
|
||||
}
|
||||
|
||||
pub fn getScaleVec(m: zm.Mat) zm.Vec {
|
||||
const scale_x = zm.length3(zm.f32x4(m[0][0], m[1][0], m[2][0], 0))[0];
|
||||
const scale_y = zm.length3(zm.f32x4(m[0][1], m[1][1], m[2][1], 0))[0];
|
||||
const scale_z = zm.length3(zm.f32x4(m[0][2], m[1][2], m[2][2], 0))[0];
|
||||
return zm.f32x4(scale_x, scale_y, scale_z, 0);
|
||||
}
|
||||
|
||||
pub fn getRotationQuat(_m: zm.Mat) zm.Quat {
|
||||
// Ortho normalize given matrix.
|
||||
const c1 = zm.normalize3(zm.f32x4(_m[0][0], _m[1][0], _m[2][0], 0));
|
||||
const c2 = zm.normalize3(zm.f32x4(_m[0][1], _m[1][1], _m[2][1], 0));
|
||||
const c3 = zm.normalize3(zm.f32x4(_m[0][2], _m[1][2], _m[2][2], 0));
|
||||
var m = _m;
|
||||
m[0][0] = c1[0];
|
||||
m[1][0] = c1[1];
|
||||
m[2][0] = c1[2];
|
||||
m[0][1] = c2[0];
|
||||
m[1][1] = c2[1];
|
||||
m[2][1] = c2[2];
|
||||
m[0][2] = c3[0];
|
||||
m[1][2] = c3[1];
|
||||
m[2][2] = c3[2];
|
||||
|
||||
// Extract rotation
|
||||
return zm.quatFromMat(m);
|
||||
}
|
||||
|
||||
pub fn getAxisX(m: zm.Mat) zm.Vec {
|
||||
return zm.normalize3(zm.f32x4(m[0][0], m[0][1], m[0][2], 0.0));
|
||||
}
|
||||
|
||||
pub fn getAxisY(m: zm.Mat) zm.Vec {
|
||||
return zm.normalize3(zm.f32x4(m[1][0], m[1][1], m[1][2], 0.0));
|
||||
}
|
||||
|
||||
pub fn getAxisZ(m: zm.Mat) zm.Vec {
|
||||
return zm.normalize3(zm.f32x4(m[2][0], m[2][1], m[2][2], 0.0));
|
||||
}
|
||||
|
||||
test "zmath.util.mat.translation" {
|
||||
// zig fmt: off
|
||||
const mat_data = [18]f32{
|
||||
1.0,
|
||||
2.0, 3.0, 4.0, 5.0,
|
||||
6.0, 7.0, 8.0, 9.0,
|
||||
10.0,11.0, 12.0,13.0,
|
||||
14.0, 15.0, 16.0, 17.0,
|
||||
18.0,
|
||||
};
|
||||
// zig fmt: on
|
||||
const mat = zm.loadMat(mat_data[1..]);
|
||||
const translation = getTranslationVec(mat);
|
||||
try expect(zm.approxEqAbs(translation, zm.f32x4(14.0, 15.0, 16.0, 0.0), 0.0001));
|
||||
}
|
||||
|
||||
test "zmath.util.mat.scale" {
|
||||
const mat = zm.mul(zm.scaling(3, 4, 5), zm.translation(6, 7, 8));
|
||||
const scale = getScaleVec(mat);
|
||||
try expect(zm.approxEqAbs(scale, zm.f32x4(3.0, 4.0, 5.0, 0.0), 0.0001));
|
||||
}
|
||||
|
||||
test "zmath.util.mat.rotation" {
|
||||
const rotate_origin = zm.matFromRollPitchYaw(0.1, 1.2, 2.3);
|
||||
const mat = zm.mul(zm.mul(rotate_origin, zm.scaling(3, 4, 5)), zm.translation(6, 7, 8));
|
||||
const rotate_get = getRotationQuat(mat);
|
||||
const v0 = zm.mul(zm.f32x4s(1), rotate_origin);
|
||||
const v1 = zm.mul(zm.f32x4s(1), zm.quatToMat(rotate_get));
|
||||
try expect(zm.approxEqAbs(v0, v1, 0.0001));
|
||||
}
|
||||
|
||||
test "zmath.util.mat.z_vec" {
|
||||
const degToRad = std.math.degreesToRadians;
|
||||
var identity = zm.identity();
|
||||
var z_vec = getAxisZ(identity);
|
||||
try expect(zm.approxEqAbs(z_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.0001));
|
||||
const rot_yaw = zm.rotationY(degToRad(f32, 90));
|
||||
identity = zm.mul(identity, rot_yaw);
|
||||
z_vec = getAxisZ(identity);
|
||||
try expect(zm.approxEqAbs(z_vec, zm.f32x4(1.0, 0.0, 0.0, 0), 0.0001));
|
||||
}
|
||||
|
||||
test "zmath.util.mat.y_vec" {
|
||||
const degToRad = std.math.degreesToRadians;
|
||||
var identity = zm.identity();
|
||||
var y_vec = getAxisY(identity);
|
||||
try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
|
||||
const rot_yaw = zm.rotationY(degToRad(f32, 90));
|
||||
identity = zm.mul(identity, rot_yaw);
|
||||
y_vec = getAxisY(identity);
|
||||
try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
|
||||
const rot_pitch = zm.rotationX(degToRad(f32, 90));
|
||||
identity = zm.mul(identity, rot_pitch);
|
||||
y_vec = getAxisY(identity);
|
||||
try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.01));
|
||||
}
|
||||
|
||||
test "zmath.util.mat.right" {
|
||||
const degToRad = std.math.degreesToRadians;
|
||||
var identity = zm.identity();
|
||||
var right = getAxisX(identity);
|
||||
try expect(zm.approxEqAbs(right, zm.f32x4(1.0, 0.0, 0.0, 0), 0.01));
|
||||
const rot_yaw = zm.rotationY(degToRad(f32, 90));
|
||||
identity = zm.mul(identity, rot_yaw);
|
||||
right = getAxisX(identity);
|
||||
try expect(zm.approxEqAbs(right, zm.f32x4(0.0, 0.0, -1.0, 0), 0.01));
|
||||
const rot_pitch = zm.rotationX(degToRad(f32, 90));
|
||||
identity = zm.mul(identity, rot_pitch);
|
||||
right = getAxisX(identity);
|
||||
try expect(zm.approxEqAbs(right, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// This software is available under 2 licenses -- choose whichever you prefer.
|
||||
// ------------------------------------------------------------------------------
|
||||
// ALTERNATIVE A - MIT License
|
||||
// Copyright (c) 2022 Michal Ziulek and Contributors
|
||||
// Permission is hereby granted, free of charge, to any person obtaining identity 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 identity 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.
|
||||
// ------------------------------------------------------------------------------
|
||||
File diff suppressed because it is too large
Load Diff
293
src/SomaSolve.c
Normal file
293
src/SomaSolve.c
Normal file
@@ -0,0 +1,293 @@
|
||||
#include "string.h"
|
||||
#include "SomaSolve.h"
|
||||
#include "math.h"
|
||||
|
||||
/*
|
||||
void get_dims_input(int dims[3]) {
|
||||
print("Enter dimensions separated by newlines. (x*y*z must not exceed 64)\n");
|
||||
bool success = false;
|
||||
while (!success) {
|
||||
std::cout << "x: ";
|
||||
std::cin >> dims[0];
|
||||
std::cout << "y: ";
|
||||
std::cin >> dims[1];
|
||||
std::cout << "z: ";
|
||||
std::cin >> dims[2];
|
||||
|
||||
int size = dims[0]*dims[1]*dims[2];
|
||||
if (size <= 64) {
|
||||
success = true;
|
||||
} else {
|
||||
print("That resulted in %zu units. Try again", size);
|
||||
}
|
||||
std::cin.ignore();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
std::vector<uint64> get_reprs_input(int units_required) {
|
||||
print("Enter bit-representations (big endian, max 64 bits, total 1s must add up to %zu). press ENTER twice to finish input.\n", units_required );
|
||||
std::vector<uint64> reprs = std::vector<uint64>();
|
||||
int total_units = 0;
|
||||
while (true) {
|
||||
std::string input = std::string();
|
||||
std::getline(std::cin, input);
|
||||
if (input.size() == 0) {
|
||||
if (total_units == units_required) {
|
||||
break;
|
||||
} else {
|
||||
std::cout << "Bad number of units. You entered: " << total_units << ", but exactly " << units_required << " were required.\n";
|
||||
total_units = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
uint64 bit_repr = 0;
|
||||
int i = 0;
|
||||
bool good_repr = true;
|
||||
for (auto it = input.rbegin(); it < input.rend(); it++, i++) {
|
||||
if (*it == '1') {
|
||||
bit_repr |= 1ull << i;
|
||||
total_units++;
|
||||
} else if (*it != '0' || i >= 64) {
|
||||
std::cout << "Input invalid. Enter a binary string only with max 64 bits." << '\n';
|
||||
good_repr = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (good_repr) {
|
||||
reprs.push_back(bit_repr);
|
||||
}
|
||||
}
|
||||
return reprs;
|
||||
}
|
||||
*/
|
||||
|
||||
DefineList(size_t, Offset);
|
||||
|
||||
typedef struct Solver Solver;
|
||||
struct Solver {
|
||||
VoxelSpaceReprList *input;
|
||||
OffsetList *offsets;
|
||||
SomaSolutionList *solutions;
|
||||
};
|
||||
|
||||
/*
|
||||
void backtrack_solve_iter(std::vector<uint64> *polycube_input, std::vector<int> *offsets) {
|
||||
size_t num_inputs = offsets->size() - 1;
|
||||
|
||||
std::vector<int> solns = std::vector<int>();
|
||||
|
||||
std::vector<int> iter_stack = std::vector<int>();
|
||||
std::vector<int> curr_soln_stack = std::vector<int>();
|
||||
std::vector<uint64> soln_spaces_stack = std::vector<uint64>();
|
||||
soln_spaces_stack.push_back(0ul);
|
||||
|
||||
int depth = 0;
|
||||
|
||||
while (depth >= 0) {
|
||||
if (depth >= iter_stack.size()) {
|
||||
iter_stack.push_back(offsets->at(depth));
|
||||
}
|
||||
int end = offsets->at(depth + 1);
|
||||
bool broke = false;
|
||||
for (; iter_stack[depth] < end; iter_stack[depth]++) {
|
||||
uint64 next_space = polycube_input->at(iter_stack[depth]);
|
||||
uint64 soln_space = soln_spaces_stack[depth];
|
||||
std::cout << next_space << " " << soln_space << std::endl;
|
||||
bool successful_fuse = (soln_space | next_space) == (soln_space ^ next_space);
|
||||
if (successful_fuse) {
|
||||
soln_spaces_stack.push_back(soln_space |= next_space);
|
||||
curr_soln_stack.push_back(iter_stack[depth]);
|
||||
depth++;
|
||||
if (curr_soln_stack.size() == num_inputs) {
|
||||
solns.push_back(1);
|
||||
curr_soln_stack.pop_back();
|
||||
soln_spaces_stack.pop_back();
|
||||
depth--;
|
||||
} else {
|
||||
depth++;
|
||||
broke = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!broke) {
|
||||
curr_soln_stack.pop_back();
|
||||
soln_spaces_stack.pop_back();
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
std::cout << "Done. Found " << solns.size() << " solutions." << std::endl;
|
||||
}
|
||||
*/
|
||||
|
||||
void backtrackSolve(Arena *arena, Solver *solver, uint64 working_solution, size_t curr_piece) {
|
||||
VoxelSpaceReprList *input = solver->input;
|
||||
OffsetList *offsets = solver->offsets;
|
||||
SomaSolutionList *solutions = solver->solutions;
|
||||
size_t start = offsets->data[curr_piece];
|
||||
size_t end = offsets->data[curr_piece + 1];
|
||||
size_t num_pieces = offsets->length - 1;
|
||||
for (size_t i = start; i < end; i++) {
|
||||
bool successful_fuse = !collides(working_solution, input->data[i]);
|
||||
if (successful_fuse) {
|
||||
uint64 new_working_solution = working_solution | input->data[i];
|
||||
solutions->data[solutions->length - 1].data[curr_piece] = input->data[i];
|
||||
if (curr_piece == num_pieces - 1) {
|
||||
VoxelSpaceReprList last_soln = solutions->data[solutions->length - 1];
|
||||
VoxelSpaceReprList last_soln_copy = PushList(arena, VoxelSpaceReprList, last_soln.length);
|
||||
last_soln_copy.length = last_soln.length;
|
||||
memcpy(last_soln_copy.data, last_soln.data, last_soln.length * ListElementSize(VoxelSpaceReprList));
|
||||
ListAppend(*solutions, last_soln_copy);
|
||||
return;
|
||||
} else {
|
||||
backtrackSolve(arena, solver, new_working_solution, curr_piece + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (curr_piece == 0) {
|
||||
solutions->length -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
SomaSolutionList getSolutionRotations(Arena *arena, SomaSolution *solution, int dims[3]) {
|
||||
SomaSolutionList result = PushFullList(arena, SomaSolutionList, NUM_ROTS_3D);
|
||||
for (EachIn(result, i)) {
|
||||
result.data[i] = PushList(arena, SomaSolution, solution->length);
|
||||
}
|
||||
for (int piece_i = 0; piece_i < solution->length; piece_i++) {
|
||||
VoxelSpace space = {
|
||||
solution->data[piece_i],
|
||||
dims[0],
|
||||
dims[1],
|
||||
dims[2],
|
||||
};
|
||||
VoxelSpaceList pieceRotations = getAllRotations(arena, &space);
|
||||
for (EachIn(pieceRotations, rot_i)) {
|
||||
ListAppend(result.data[rot_i], pieceRotations.data[rot_i].space);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SomaSolutionList filterUnique(Arena *arena, SomaSolutionList *solutions, int dims[3]) {
|
||||
if (solutions->length == 0) {
|
||||
return (SomaSolutionList)EmptyList();
|
||||
}
|
||||
SomaSolutionList uniqueSolns = PushList(arena, SomaSolutionList, solutions->length);
|
||||
for (EachEl(*solutions, SomaSolution, solution)) {
|
||||
bool foundMatch = false;
|
||||
Scratch temp = scratchStart(&arena, 1);
|
||||
SomaSolutionList rots = getSolutionRotations(temp.arena, solution, dims);
|
||||
for (EachEl(rots, SomaSolution, rotation)) {
|
||||
for (EachEl(uniqueSolns, SomaSolution, unique_soln)) {
|
||||
bool isMatch = true;
|
||||
for (EachIn(*unique_soln, piece_i)) {
|
||||
if (rotation->data[piece_i] != unique_soln->data[piece_i]) {
|
||||
isMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isMatch) {
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundMatch) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
scratchEnd(temp);
|
||||
if (!foundMatch) {
|
||||
SomaSolution solutionCopy = PushList(arena, SomaSolution, solution->length);
|
||||
solutionCopy.capacity = solution->length;
|
||||
solutionCopy.length = solution->length;
|
||||
memcpy(solutionCopy.data, solution->data, ListElementSize(SomaSolutionList) * solution->length);
|
||||
ListAppend(uniqueSolns, solutionCopy);
|
||||
}
|
||||
}
|
||||
return uniqueSolns;
|
||||
}
|
||||
|
||||
uint64 factorial(int n) {
|
||||
uint64 result = 1;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
result *= i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SomaSolutionList solveSoma(Arena *solutionsArena, VoxelSpaceReprList reprsInput, int dims[3]) {
|
||||
Arena *generalArena = arenaAlloc(Megabytes(64));
|
||||
Arena *permsArena = arenaAlloc(Megabytes(128));
|
||||
|
||||
OffsetList offsets = PushList(generalArena, OffsetList, reprsInput.length + 1);
|
||||
|
||||
VoxelSpaceReprList polycubes = PushList(generalArena, VoxelSpaceReprList, 0);
|
||||
|
||||
VoxelSpace emptyVoxelSpace = {
|
||||
0,
|
||||
dims[0],
|
||||
dims[1],
|
||||
dims[2],
|
||||
};
|
||||
|
||||
ListAppend(offsets, 0);
|
||||
|
||||
uint64 possibleCombos = 0;
|
||||
|
||||
{
|
||||
VoxelSpace voxelSpace = emptyVoxelSpace;
|
||||
voxelSpace.space = reprsInput.data[0];
|
||||
cullEmptySpace(&voxelSpace);
|
||||
VoxelSpaceReprList positions = getAllPositionsInPrism(permsArena, &voxelSpace, dims);
|
||||
possibleCombos += positions.length;
|
||||
VoxelSpaceReprList_underlying *insertion = PushArray(generalArena, uint64, positions.capacity);
|
||||
polycubes.capacity += positions.capacity;
|
||||
polycubes.length += positions.length;
|
||||
memcpy(insertion, positions.data, positions.capacity * ListElementSize(VoxelSpaceReprList));
|
||||
};
|
||||
|
||||
for (size_t i = 1; i < reprsInput.length; i++) {
|
||||
ListAppend(offsets, polycubes.capacity);
|
||||
VoxelSpace space = emptyVoxelSpace;
|
||||
space.space = reprsInput.data[i];
|
||||
cullEmptySpace(&space);
|
||||
VoxelSpaceReprList perms = getAllPermutationsInPrism(permsArena, &space, dims);
|
||||
possibleCombos *= perms.length;
|
||||
VoxelSpaceReprList_underlying *insertion = PushArray(generalArena, VoxelSpaceReprList_underlying, perms.capacity);
|
||||
polycubes.capacity += perms.capacity;
|
||||
polycubes.length += perms.length;
|
||||
memcpy(insertion, perms.data, perms.capacity * ListElementSize(VoxelSpaceReprList));
|
||||
}
|
||||
|
||||
ListAppend(offsets, polycubes.length);
|
||||
|
||||
SomaSolutionList solutions = PushList(permsArena, SomaSolutionList, (size_t)floor(sqrt(possibleCombos)));
|
||||
ListAppend(solutions, PushFullList(permsArena, SomaSolution, reprsInput.length));
|
||||
|
||||
Solver solver = {
|
||||
&polycubes,
|
||||
&offsets,
|
||||
&solutions,
|
||||
};
|
||||
|
||||
backtrackSolve(permsArena, &solver, 0, 0);
|
||||
|
||||
SomaSolutionList uniqueSolns = filterUnique(solutionsArena, solver.solutions, dims);
|
||||
|
||||
arenaFree(permsArena);
|
||||
arenaFree(generalArena);
|
||||
|
||||
return uniqueSolns;
|
||||
}
|
||||
|
||||
void interactiveCmdLineSolveSoma() {
|
||||
//get_dims_input(dims);
|
||||
//std::cout << '\n';
|
||||
//std::vector<uint64> reprs = get_reprs_input(dims[0]*dims[1]*dims[2]);
|
||||
//print("Great. Calculating solutions...\n");
|
||||
//SomaSolutionList solutions = solveSoma(AsList(VoxelSpaceReprList, STD_SOMA), (int[]){ 3, 3, 3 });
|
||||
//print("%zu solutions found.\n", solutions.length);
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
#include <bitset>
|
||||
#include <span>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "VoxelSpace.h"
|
||||
|
||||
auto get_dims_input(int dims[3]) -> void {
|
||||
std::cout << "Enter dimensions separated by newlines. (x*y*z must not exceed 64)\n";
|
||||
auto success = false;
|
||||
while (!success) {
|
||||
std::cout << "x: ";
|
||||
std::cin >> dims[0];
|
||||
std::cout << "y: ";
|
||||
std::cin >> dims[1];
|
||||
std::cout << "z: ";
|
||||
std::cin >> dims[2];
|
||||
|
||||
auto size = dims[0]*dims[1]*dims[2];
|
||||
if (size <= 64) {
|
||||
success = true;
|
||||
} else {
|
||||
std::cout << "That resulted in " << size << " units. Try again.\n";
|
||||
}
|
||||
std::cin.ignore();
|
||||
}
|
||||
}
|
||||
|
||||
auto get_reprs_input(int units_required) -> std::vector<uint64_t> {
|
||||
std::cout << "Enter bit-representations (big endian, max 64 bits, total 1s must add up to " << units_required << "). press ENTER twice to finish input.\n";
|
||||
auto reprs = std::vector<uint64_t>();
|
||||
auto total_units = 0;
|
||||
while (true) {
|
||||
auto input = std::string();
|
||||
std::getline(std::cin, input);
|
||||
if (input.size() == 0) {
|
||||
if (total_units == units_required) {
|
||||
break;
|
||||
} else {
|
||||
std::cout << "Bad number of units. You entered: " << total_units << ", but exactly " << units_required << " were required.\n";
|
||||
total_units = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto bit_repr = 0ul;
|
||||
auto i = 0;
|
||||
auto good_repr = true;
|
||||
for (auto it = input.rbegin(); it < input.rend(); it++, i++) {
|
||||
if (*it == '1') {
|
||||
bit_repr |= 1ul << i;
|
||||
total_units++;
|
||||
} else if (*it != '0' || i >= 64) {
|
||||
std::cout << "Input invalid. Enter a binary string only with max 64 bits." << '\n';
|
||||
good_repr = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (good_repr) {
|
||||
reprs.push_back(bit_repr);
|
||||
}
|
||||
}
|
||||
return reprs;
|
||||
}
|
||||
|
||||
namespace SomaSolve {
|
||||
using SomaSolution = std::vector<uint64_t>;
|
||||
|
||||
struct Solver {
|
||||
std::vector<uint64_t>* input;
|
||||
std::vector<int>* offsets;
|
||||
std::vector<SomaSolution>* solutions;
|
||||
};
|
||||
|
||||
auto STD_SOMA = std::vector<uint64_t>{ 23ul, 30ul, 15ul, 1043ul, 24594ul, 12306ul, 11ul };
|
||||
|
||||
auto backtrack_solve_iter(std::vector<uint64_t> *polycube_input, std::vector<int> *offsets)-> void {
|
||||
auto num_inputs = offsets->size() - 1;
|
||||
|
||||
auto solns = std::vector<int>();
|
||||
|
||||
auto iter_stack = std::vector<int>();
|
||||
auto curr_soln_stack = std::vector<int>();
|
||||
auto soln_spaces_stack = std::vector<uint64_t>();
|
||||
soln_spaces_stack.push_back(0ul);
|
||||
|
||||
auto depth = 0;
|
||||
|
||||
while (depth >= 0) {
|
||||
if (depth >= iter_stack.size()) {
|
||||
iter_stack.push_back(offsets->at(depth));
|
||||
}
|
||||
auto end = offsets->at(depth + 1);
|
||||
auto broke = false;
|
||||
for (; iter_stack[depth] < end; iter_stack[depth]++) {
|
||||
auto next_space = polycube_input->at(iter_stack[depth]);
|
||||
auto soln_space = soln_spaces_stack[depth];
|
||||
std::cout << next_space << " " << soln_space << std::endl;
|
||||
auto successful_fuse = (soln_space | next_space) == (soln_space ^ next_space);
|
||||
if (successful_fuse) {
|
||||
soln_spaces_stack.push_back(soln_space |= next_space);
|
||||
curr_soln_stack.push_back(iter_stack[depth]);
|
||||
depth++;
|
||||
if (curr_soln_stack.size() == num_inputs) {
|
||||
solns.push_back(1);
|
||||
curr_soln_stack.pop_back();
|
||||
soln_spaces_stack.pop_back();
|
||||
depth--;
|
||||
} else {
|
||||
depth++;
|
||||
auto broke = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!broke) {
|
||||
curr_soln_stack.pop_back();
|
||||
soln_spaces_stack.pop_back();
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
std::cout << "Done. Found " << solns.size() << " solutions." << std::endl;
|
||||
}
|
||||
|
||||
auto backtrack_solve(Solver *solver, uint64_t working_solution = 0ul, int curr_piece = 0) -> void {
|
||||
auto input = solver->input;
|
||||
auto offsets = solver->offsets;
|
||||
auto solutions = solver->solutions;
|
||||
auto start = offsets->at(curr_piece);
|
||||
auto end = offsets->at(curr_piece + 1);
|
||||
auto num_pieces = offsets->size() - 1;
|
||||
for (int i = start; i < end; i++) {
|
||||
auto successful_fuse = !Voxel::collides(working_solution, input->at(i));
|
||||
if (successful_fuse) {
|
||||
auto new_working_solution = working_solution | input->at(i);
|
||||
solutions->back().at(curr_piece) = input->at(i);
|
||||
if (curr_piece == num_pieces - 1) {
|
||||
auto last_soln = solutions->back();
|
||||
solutions->push_back(SomaSolution(last_soln.begin(), last_soln.end()));
|
||||
return;
|
||||
} else {
|
||||
backtrack_solve(solver, new_working_solution, curr_piece + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (curr_piece == 0) {
|
||||
solutions->pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
auto get_solution_rotations(SomaSolution *solution, int dims[3]) -> std::vector<SomaSolution> {
|
||||
auto result = std::vector<SomaSolution>(Voxel::NUM_ROTS_3D);
|
||||
for (int piece_i = 0; piece_i < solution->size(); piece_i++) {
|
||||
auto space = Voxel::Space{
|
||||
.space=solution->at(piece_i),
|
||||
.dim_x=dims[0],
|
||||
.dim_y=dims[1],
|
||||
.dim_z=dims[2],
|
||||
};
|
||||
auto piece_rotations = Voxel::getAllRotations(&space);
|
||||
for (int rot_i = 0; rot_i < piece_rotations.size(); rot_i++) {
|
||||
result[rot_i].push_back(piece_rotations[rot_i].space);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto filter_unique(std::vector<SomaSolution> *solutions, int dims[3]) -> std::vector<SomaSolution> {
|
||||
if (solutions->size() == 0) {
|
||||
return std::vector<SomaSolution>();
|
||||
}
|
||||
auto unique_solns = std::vector<SomaSolution>{};
|
||||
for (auto &solution : *solutions) {
|
||||
auto found_match = false;
|
||||
for (auto &rotation : get_solution_rotations(&solution, dims)) {
|
||||
for (auto &unique_soln : unique_solns) {
|
||||
auto is_match = true;
|
||||
for (int piece_i = 0; piece_i < unique_soln.size(); piece_i++) {
|
||||
if (rotation[piece_i] != unique_soln[piece_i]) {
|
||||
is_match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_match) {
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found_match) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_match) {
|
||||
unique_solns.push_back(SomaSolution(solution));
|
||||
}
|
||||
}
|
||||
return unique_solns;
|
||||
}
|
||||
|
||||
auto solve(std::vector<uint64_t> *reprs_in, int dims[3]) -> std::vector<SomaSolution> {
|
||||
auto reprs = *reprs_in;
|
||||
auto offsets = std::vector<int>();
|
||||
auto polycubes = std::vector<uint64_t>();
|
||||
polycubes.reserve(reprs.size() * 10);
|
||||
|
||||
auto model_space = Voxel::Space{
|
||||
.space={},
|
||||
.dim_x=dims[0],
|
||||
.dim_y=dims[1],
|
||||
.dim_z=dims[2],
|
||||
};
|
||||
|
||||
offsets.push_back(0);
|
||||
auto space = model_space;
|
||||
space.space = reprs[0];
|
||||
Voxel::cullEmptySpace(&space);
|
||||
auto positions = Voxel::getAllPositionsInPrism(&space, dims);
|
||||
polycubes.insert(polycubes.end(), positions.begin(), positions.end());
|
||||
|
||||
for (int i = 1; i < reprs.size(); i++) {
|
||||
offsets.push_back(polycubes.size());
|
||||
auto space = model_space;
|
||||
space.space = reprs[i];
|
||||
Voxel::cullEmptySpace(&space);
|
||||
auto perms = Voxel::getAllPermutationsInPrism(&space, dims);
|
||||
polycubes.insert(polycubes.end(), perms.begin(), perms.end());
|
||||
}
|
||||
|
||||
offsets.push_back(polycubes.size());
|
||||
|
||||
auto solutions = std::vector<SomaSolution>{std::vector<uint64_t>(reprs.size())};
|
||||
auto solver = Solver{
|
||||
.input=&polycubes,
|
||||
.offsets=&offsets,
|
||||
.solutions=&solutions,
|
||||
};
|
||||
|
||||
backtrack_solve(&solver);
|
||||
|
||||
return filter_unique(solver.solutions, dims);
|
||||
}
|
||||
|
||||
|
||||
auto interactive_cmd_line_solve_soma() -> void {
|
||||
int dims[3] = { 3, 3, 3 };
|
||||
//get_dims_input(dims);
|
||||
//std::cout << '\n';
|
||||
//auto reprs = get_reprs_input(dims[0]*dims[1]*dims[2]);
|
||||
std::cout << "Great. Calculating solutions...\n";
|
||||
auto solutions = SomaSolve::solve(&SomaSolve::STD_SOMA, std::array<int, 3>{ 3, 3, 3 }.data());
|
||||
std::cout << solutions.size() << " solutions found." << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include "VoxelSpace.h"
|
||||
#include "lib/djstdlib/core.h"
|
||||
|
||||
namespace SomaSolve {
|
||||
extern std::vector<uint64_t> STD_SOMA;
|
||||
using SomaSolution = std::vector<uint64_t>;
|
||||
auto solve(std::vector<uint64_t> *reprs_in, int dims[3]) -> std::vector<SomaSolution>;
|
||||
auto interactive_cmd_line_solve_soma() -> void;
|
||||
}
|
||||
#define STD_SOMA { 23ul, 30ul, 15ul, 1043ul, 24594ul, 12306ul, 11ul }
|
||||
typedef VoxelSpaceReprList SomaSolution;
|
||||
DefineList(SomaSolution, SomaSolution);
|
||||
|
||||
SomaSolutionList solveSoma(Arena *solutionsArena, VoxelSpaceReprList reprs_in, int dims[3]);
|
||||
void interactiveCmdLineSolveSoma();
|
||||
|
||||
314
src/VoxelSpace.c
Normal file
314
src/VoxelSpace.c
Normal file
@@ -0,0 +1,314 @@
|
||||
#include "string.h"
|
||||
#include "VoxelSpace.h"
|
||||
|
||||
int index(int dim_y, int dim_z, int x, int y, int z) {
|
||||
return dim_y * dim_z * x + dim_z * y + z;
|
||||
}
|
||||
|
||||
// ┌ ┐ ┌ ┐ ┌ ┐
|
||||
// │ 1, 0, 0 │ │ x │ │ x │
|
||||
// │ 0, 0, -1 │ * │ y │ = │-z │
|
||||
// │ 0, 1, 0 │ │ z │ │ y │
|
||||
// └ ┘ └ ┘ └ ┘
|
||||
int newIndexRotX(VoxelSpace *space, int x, int y, int z) {
|
||||
return space->dim_z * space->dim_y * x + space->dim_y * (space->dim_z - 1 - z) + y;
|
||||
}
|
||||
|
||||
// ┌ ┐ ┌ ┐ ┌ ┐
|
||||
// │ 0, 0, 1 │ │ x │ │ z │
|
||||
// │ 0, 1, 0 │ * │ y │ = │-y │
|
||||
// │ -1, 0, 0 │ │ z │ │ x │
|
||||
// └ ┘ └ ┘ └ ┘
|
||||
int newIndexRotY(VoxelSpace *space, int x, int y, int z) {
|
||||
return space->dim_y * space->dim_x * z + space->dim_x * y + (space->dim_x - 1 - x);
|
||||
}
|
||||
|
||||
// ┌ ┐ ┌ ┐ ┌ ┐
|
||||
// │ 0, -1, 0 │ │ x │ │-y │
|
||||
// │ 1, 0, 0 │ * │ y │ = │ x │
|
||||
// │ 0, 0, 1 │ │ z │ │ z │
|
||||
// └ ┘ └ ┘ └ ┘
|
||||
int newIndexRotZ(VoxelSpace *space, int x, int y, int z) {
|
||||
return space->dim_x * space->dim_z * (space->dim_y - 1 - y) + space->dim_z * x + z;
|
||||
}
|
||||
|
||||
inline uint64 toggle(uint64 space, int index) {
|
||||
space ^= 1ull << index;
|
||||
return space;
|
||||
}
|
||||
|
||||
uint64 set(uint64 space, int index, bool val) {
|
||||
if (val) {
|
||||
space |= 1ull << index;
|
||||
} else {
|
||||
space &= ~(1ull << index);
|
||||
}
|
||||
return space;
|
||||
}
|
||||
|
||||
inline bool collides(uint64 a, uint64 b) {
|
||||
return (a | b) != (a ^ b);
|
||||
}
|
||||
|
||||
inline bool filledAt(VoxelSpace *space, int x, int y, int z) {
|
||||
uint64 mask = 1ull << (space->dim_y * space->dim_z * x + space->dim_z * y + z);
|
||||
return (space->space & mask) != 0ull;
|
||||
}
|
||||
|
||||
Extrema getExtrema(VoxelSpace *space) {
|
||||
Extrema extrema = {
|
||||
0,
|
||||
space->dim_x,
|
||||
0,
|
||||
space->dim_y,
|
||||
0,
|
||||
space->dim_z,
|
||||
};
|
||||
|
||||
for (int x = 0; x < space->dim_x; x++) {
|
||||
for (int y = 0; y < space->dim_y; y++) {
|
||||
for (int z = 0; z < space->dim_z; z++) {
|
||||
if (filledAt(space, x, y, z)) {
|
||||
if (x > extrema.xMax) extrema.xMax = x;
|
||||
if (x < extrema.xMin) extrema.xMin = x;
|
||||
if (y > extrema.yMax) extrema.yMax = y;
|
||||
if (y < extrema.yMin) extrema.yMin = y;
|
||||
if (z > extrema.zMax) extrema.zMax = z;
|
||||
if (z < extrema.zMin) extrema.zMin = z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extrema;
|
||||
}
|
||||
|
||||
void cullEmptySpace(VoxelSpace *space) {
|
||||
Extrema extrema = getExtrema(space);
|
||||
int space_index = 0;
|
||||
uint64 newSpace = 0ull;
|
||||
for (int x = extrema.xMin; x <= extrema.xMax; x++) {
|
||||
for (int y = extrema.yMin; y <= extrema.yMax; y++) {
|
||||
for (int z = extrema.zMin; z <= extrema.zMax; z++) {
|
||||
if (filledAt(space, x, y, z)) {
|
||||
newSpace |= 1ull << space_index;
|
||||
}
|
||||
space_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
space->dim_x = extrema.xMax - extrema.xMin + 1;
|
||||
space->dim_y = extrema.yMax - extrema.yMin + 1;
|
||||
space->dim_z = extrema.zMax - extrema.zMin + 1;
|
||||
space->space = newSpace;
|
||||
}
|
||||
|
||||
void rotate90X(VoxelSpace *space) {
|
||||
uint64 new_space = 0;
|
||||
for (int x = 0; x < space->dim_x; x++) {
|
||||
for (int y = 0; y < space->dim_y; y++) {
|
||||
for (int z = 0; z < space->dim_z; z++) {
|
||||
if (filledAt(space, x, y, z)) {
|
||||
new_space |= 1ull << newIndexRotX(space, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int temp = space->dim_y;
|
||||
space->dim_y = space->dim_z;
|
||||
space->dim_z = temp;
|
||||
space->space = new_space;
|
||||
}
|
||||
|
||||
void rotate90Y(VoxelSpace *space) {
|
||||
uint64 new_space = 0;
|
||||
for (int x = 0; x < space->dim_x; x++) {
|
||||
for (int y = 0; y < space->dim_y; y++) {
|
||||
for (int z = 0; z < space->dim_z; z++) {
|
||||
if (filledAt(space, x, y, z)) {
|
||||
new_space |= 1ull << newIndexRotY(space, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int temp = space->dim_x;
|
||||
space->dim_x = space->dim_z;
|
||||
space->dim_z = temp;
|
||||
space->space = new_space;
|
||||
}
|
||||
|
||||
void rotate90Z(VoxelSpace *space) {
|
||||
uint64 new_space = 0;
|
||||
for (int x = 0; x < space->dim_x; x++) {
|
||||
for (int y = 0; y < space->dim_y; y++) {
|
||||
for (int z = 0; z < space->dim_z; z++) {
|
||||
if (filledAt(space, x, y, z)) {
|
||||
new_space |= 1ull << newIndexRotZ(space, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int temp = space->dim_x;
|
||||
space->dim_x = space->dim_y;
|
||||
space->dim_y = temp;
|
||||
space->space = new_space;
|
||||
}
|
||||
|
||||
bool isMatch(VoxelSpace *a, VoxelSpace *b) {
|
||||
return a->space == b->space
|
||||
&& a->dim_x == b->dim_x
|
||||
&& a->dim_y == b->dim_y
|
||||
&& a->dim_z == b->dim_z;
|
||||
}
|
||||
|
||||
void pushNewUniqueSpins(VoxelSpaceList *existingSpaces, VoxelSpace* spaceToSpin) {
|
||||
VoxelSpace spins[4] = {};
|
||||
spins[0] = *spaceToSpin;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
spins[i + 1] = spins[i];
|
||||
rotate90X(&spins[i + 1]);
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
bool matchFound = false;
|
||||
for (EachIn(*existingSpaces, j)) {
|
||||
if (isMatch(&existingSpaces->data[j], &spins[i])) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matchFound) {
|
||||
ListAppend(*existingSpaces, spins[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pushXAxisSpins(Arena *arena, VoxelSpaceList *existingSpaces, VoxelSpace* spaceToSpin) {
|
||||
VoxelSpace refSpace = *spaceToSpin;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
rotate90X(&refSpace);
|
||||
ListAppend(*existingSpaces, refSpace);
|
||||
}
|
||||
}
|
||||
|
||||
VoxelSpaceList getUniqueRotations(Arena *arena, VoxelSpace *space) {
|
||||
VoxelSpaceList rotations = PushList(arena, VoxelSpaceList, 24);
|
||||
VoxelSpace refSpace = *space;
|
||||
cullEmptySpace(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
rotations.capacity = rotations.length;
|
||||
arenaPopTo(arena, rotations.data + rotations.length);
|
||||
return rotations;
|
||||
}
|
||||
|
||||
VoxelSpaceList getAllRotations(Arena *arena, VoxelSpace *space) {
|
||||
VoxelSpaceList rotations = PushList(arena, VoxelSpaceList, 24);
|
||||
VoxelSpace refSpace = *space;
|
||||
pushXAxisSpins(arena, &rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushXAxisSpins(arena, &rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushXAxisSpins(arena, &rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushXAxisSpins(arena, &rotations, &refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
pushXAxisSpins(arena, &rotations, &refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
pushXAxisSpins(arena, &rotations, &refSpace);
|
||||
return rotations;
|
||||
}
|
||||
|
||||
VoxelSpaceReprList getAllPositionsInPrism(Arena *arena, VoxelSpace *space, int prism_dims[3]) {
|
||||
if (space->dim_x > prism_dims[0] || space->dim_y > prism_dims[1] || space->dim_z > prism_dims[2]) {
|
||||
return (VoxelSpaceReprList)EmptyList();
|
||||
}
|
||||
int count = 0;
|
||||
void *startList = 0;
|
||||
int xPositionCount = prism_dims[0] - space->dim_x + 1;
|
||||
int yPositionCount = prism_dims[1] - space->dim_y + 1;
|
||||
int zPositionCount = prism_dims[2] - space->dim_z + 1;
|
||||
for (int x = 0; x < xPositionCount; x++) {
|
||||
for (int y = 0; y < yPositionCount; y++) {
|
||||
for (int z = 0; z < zPositionCount; z++) {
|
||||
uint64 *new_space = PushStruct(arena, uint64);
|
||||
if (!startList) {
|
||||
startList = new_space;
|
||||
}
|
||||
*new_space = 0;
|
||||
count++;
|
||||
for (int posX = 0; posX < space->dim_x; posX++) {
|
||||
for (int posY = 0; posY < space->dim_y; posY++) {
|
||||
for (int posZ = 0; posZ < space->dim_z; posZ++) {
|
||||
bool set_val = filledAt(space, posX, posY, posZ);
|
||||
int index_to_set = index(prism_dims[1], prism_dims[2], x + posX, y + posY, z + posZ);
|
||||
*new_space = set(*new_space, index_to_set, set_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VoxelSpaceReprList result = {};
|
||||
result.data = (uint64 *)startList;
|
||||
result.capacity = count;
|
||||
result.length = count;
|
||||
return result;
|
||||
}
|
||||
|
||||
VoxelSpaceReprList getAllPermutationsInPrism(Arena *arena, VoxelSpace *space, int prism_dims[3]) {
|
||||
Scratch temp = scratchStart(&arena, 1);
|
||||
|
||||
VoxelSpaceList rotations = getUniqueRotations(temp.arena, space);
|
||||
VoxelSpaceReprList result = PushList(arena, VoxelSpaceReprList, 0);
|
||||
for (EachIn(rotations, i)) {
|
||||
VoxelSpaceReprList positions = getAllPositionsInPrism(temp.arena, &rotations.data[i], prism_dims);
|
||||
uint64 *listAppend = PushArray(arena, uint64, positions.capacity);
|
||||
memcpy(listAppend, positions.data, positions.capacity * sizeof(uint64));
|
||||
result.capacity += positions.capacity;
|
||||
result.length += positions.length;
|
||||
}
|
||||
|
||||
scratchEnd(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
int size(uint64 space) {
|
||||
int size = 0;
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if ((space & (1ull << i)) != 0) {
|
||||
size++;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool spaceValueAt(VoxelSpace *space, int x, int y, int z) {
|
||||
uint64 mask = 1 << (space->dim_y * space->dim_z * x + space->dim_z * y + z);
|
||||
return (space->space & mask) != 0;
|
||||
}
|
||||
|
||||
void spaceToggle(VoxelSpace *space, int x, int y, int z) {
|
||||
uint64 mask = 1 << (space->dim_y * space->dim_z * x + space->dim_z * y + z);
|
||||
space->space ^= mask;
|
||||
}
|
||||
|
||||
void spaceSet(VoxelSpace *space, bool val, int x, int y, int z) {
|
||||
uint64 mask = 1 << (space->dim_y * space->dim_z * x + space->dim_z * y + z);
|
||||
if (val) {
|
||||
space->space |= mask;
|
||||
} else {
|
||||
space->space &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,288 +0,0 @@
|
||||
#include <vector>
|
||||
#include <bitset>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include "VoxelSpace.h"
|
||||
|
||||
namespace Voxel {
|
||||
auto index(int dim_y, int dim_z, int x, int y, int z) -> int {
|
||||
return dim_y * dim_z * x + dim_z * y + z;
|
||||
}
|
||||
|
||||
// ┌ ┐ ┌ ┐ ┌ ┐
|
||||
// │ 1, 0, 0 │ │ x │ │ x │
|
||||
// │ 0, 0, -1 │ * │ y │ = │-z │
|
||||
// │ 0, 1, 0 │ │ z │ │ y │
|
||||
// └ ┘ └ ┘ └ ┘
|
||||
auto newIndexRotX(Space *space, int x, int y, int z) -> int {
|
||||
return space->dim_z * space->dim_y * x + space->dim_y * (space->dim_z - 1 - z) + y;
|
||||
}
|
||||
|
||||
// ┌ ┐ ┌ ┐ ┌ ┐
|
||||
// │ 0, 0, 1 │ │ x │ │ z │
|
||||
// │ 0, 1, 0 │ * │ y │ = │-y │
|
||||
// │ -1, 0, 0 │ │ z │ │ x │
|
||||
// └ ┘ └ ┘ └ ┘
|
||||
auto newIndexRotY(Space *space, int x, int y, int z) -> int {
|
||||
return space->dim_y * space->dim_x * z + space->dim_x * y + (space->dim_x - 1 - x);
|
||||
}
|
||||
|
||||
// ┌ ┐ ┌ ┐ ┌ ┐
|
||||
// │ 0, -1, 0 │ │ x │ │-y │
|
||||
// │ 1, 0, 0 │ * │ y │ = │ x │
|
||||
// │ 0, 0, 1 │ │ z │ │ z │
|
||||
// └ ┘ └ ┘ └ ┘
|
||||
auto newIndexRotZ(Space *space, int x, int y, int z) -> int {
|
||||
return space->dim_x * space->dim_z * (space->dim_y - 1 - y) + space->dim_z * x + z;
|
||||
}
|
||||
|
||||
auto toggle(uint64_t space, int index) -> uint64_t {
|
||||
space ^= 1ul << index;
|
||||
return space;
|
||||
}
|
||||
|
||||
auto set(uint64_t space, int index, bool val) -> uint64_t {
|
||||
if (val) {
|
||||
space |= 1ul << index;
|
||||
} else {
|
||||
space &= ~(1ul << index);
|
||||
}
|
||||
return space;
|
||||
}
|
||||
|
||||
auto collides(uint64_t a, uint64_t b) -> bool {
|
||||
return (a | b) != (a ^ b);
|
||||
}
|
||||
|
||||
auto collides(Space *a, Space *b) -> bool {
|
||||
return (a->space | b->space) != (a->space ^ b->space);
|
||||
}
|
||||
|
||||
auto filledAt(Space *space, int x, int y, int z) -> bool {
|
||||
auto mask = 1ul << (space->dim_y * space->dim_z * x + space->dim_z * y + z);
|
||||
return (space->space & mask) != 0ul;
|
||||
}
|
||||
|
||||
auto getExtrema(Space *space) -> Extrema {
|
||||
auto extrema = Extrema{
|
||||
.xMax=0,
|
||||
.xMin=space->dim_x,
|
||||
.yMax=0,
|
||||
.yMin=space->dim_y,
|
||||
.zMax=0,
|
||||
.zMin=space->dim_z,
|
||||
};
|
||||
|
||||
for (int x = 0; x < space->dim_x; x++) {
|
||||
for (int y = 0; y < space->dim_y; y++) {
|
||||
for (int z = 0; z < space->dim_z; z++) {
|
||||
if (filledAt(space, x, y, z)) {
|
||||
if (x > extrema.xMax) extrema.xMax = x;
|
||||
if (x < extrema.xMin) extrema.xMin = x;
|
||||
if (y > extrema.yMax) extrema.yMax = y;
|
||||
if (y < extrema.yMin) extrema.yMin = y;
|
||||
if (z > extrema.zMax) extrema.zMax = z;
|
||||
if (z < extrema.zMin) extrema.zMin = z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extrema;
|
||||
}
|
||||
|
||||
auto cullEmptySpace(Space *space) -> void {
|
||||
auto extrema = getExtrema(space);
|
||||
auto space_index = 0;
|
||||
auto newSpace = 0ul;
|
||||
for (int x = extrema.xMin; x <= extrema.xMax; x++) {
|
||||
for (int y = extrema.yMin; y <= extrema.yMax; y++) {
|
||||
for (int z = extrema.zMin; z <= extrema.zMax; z++) {
|
||||
if (filledAt(space, x, y, z)) {
|
||||
newSpace |= 1ul << space_index;
|
||||
}
|
||||
space_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
space->dim_x = extrema.xMax - extrema.xMin + 1;
|
||||
space->dim_y = extrema.yMax - extrema.yMin + 1;
|
||||
space->dim_z = extrema.zMax - extrema.zMin + 1;
|
||||
space->space = newSpace;
|
||||
}
|
||||
|
||||
auto rotate90X(Space *space) -> void {
|
||||
auto new_space = 0ul;
|
||||
for (int x = 0; x < space->dim_x; x++) {
|
||||
for (int y = 0; y < space->dim_y; y++) {
|
||||
for (int z = 0; z < space->dim_z; z++) {
|
||||
if (filledAt(space, x, y, z)) {
|
||||
new_space |= 1 << newIndexRotX(space, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
auto temp = space->dim_y;
|
||||
space->dim_y = space->dim_z;
|
||||
space->dim_z = temp;
|
||||
space->space = new_space;
|
||||
}
|
||||
|
||||
auto rotate90Y(Space *space) -> void {
|
||||
auto new_space = 0ul;
|
||||
for (int x = 0; x < space->dim_x; x++) {
|
||||
for (int y = 0; y < space->dim_y; y++) {
|
||||
for (int z = 0; z < space->dim_z; z++) {
|
||||
if (filledAt(space, x, y, z)) {
|
||||
new_space |= 1 << newIndexRotY(space, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
auto temp = space->dim_x;
|
||||
space->dim_x = space->dim_z;
|
||||
space->dim_z = temp;
|
||||
space->space = new_space;
|
||||
}
|
||||
|
||||
auto rotate90Z(Space *space) -> void {
|
||||
auto new_space = 0ul;
|
||||
for (int x = 0; x < space->dim_x; x++) {
|
||||
for (int y = 0; y < space->dim_y; y++) {
|
||||
for (int z = 0; z < space->dim_z; z++) {
|
||||
if (filledAt(space, x, y, z)) {
|
||||
new_space |= 1 << newIndexRotZ(space, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
auto temp = space->dim_x;
|
||||
space->dim_x = space->dim_y;
|
||||
space->dim_y = temp;
|
||||
space->space = new_space;
|
||||
}
|
||||
|
||||
auto isMatch(Space *a, Space *b) -> bool {
|
||||
return a->space == b->space
|
||||
&& a->dim_x == b->dim_x
|
||||
&& a->dim_y == b->dim_y
|
||||
&& a->dim_z == b->dim_z;
|
||||
}
|
||||
|
||||
auto pushNewUniqueSpins(std::vector<Space> *existingSpaces, Space* spaceToSpin) -> void {
|
||||
Space spins[4] = {};
|
||||
spins[0] = *spaceToSpin;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
spins[i + 1] = spins[i];
|
||||
rotate90X(&spins[i + 1]);
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto matchFound = false;
|
||||
for (auto &existingSpace : *existingSpaces) {
|
||||
if (isMatch(&existingSpace, &spins[i])) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matchFound) {
|
||||
existingSpaces->push_back(spins[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto pushXAxisSpins(std::vector<Space> *existingSpaces, Space* spaceToSpin) -> void {
|
||||
auto refSpace = *spaceToSpin;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
rotate90X(&refSpace);
|
||||
existingSpaces->push_back(refSpace);
|
||||
}
|
||||
}
|
||||
|
||||
auto getUniqueRotations(Space *space) -> std::vector<Space> {
|
||||
auto rotations = std::vector<Space>();
|
||||
rotations.reserve(24);
|
||||
auto refSpace = *space;
|
||||
cullEmptySpace(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
pushNewUniqueSpins(&rotations, &refSpace);
|
||||
return rotations;
|
||||
}
|
||||
|
||||
auto getAllRotations(Space *space) -> std::vector<Space> {
|
||||
auto rotations = std::vector<Space>();
|
||||
rotations.reserve(24);
|
||||
auto refSpace = *space;
|
||||
pushXAxisSpins(&rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushXAxisSpins(&rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushXAxisSpins(&rotations, &refSpace);
|
||||
rotate90Y(&refSpace);
|
||||
pushXAxisSpins(&rotations, &refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
pushXAxisSpins(&rotations, &refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
rotate90Z(&refSpace);
|
||||
pushXAxisSpins(&rotations, &refSpace);
|
||||
return rotations;
|
||||
}
|
||||
|
||||
auto getAllPositionsInPrism(Space *space, int prism_dims[3]) -> std::vector<uint64_t> {
|
||||
auto cubePositions = std::vector<uint64_t>();
|
||||
if (space->dim_x > prism_dims[0] || space->dim_y > prism_dims[1] || space->dim_z > prism_dims[2]) {
|
||||
return cubePositions;
|
||||
}
|
||||
auto xPositionCount = prism_dims[0] - space->dim_x + 1;
|
||||
auto yPositionCount = prism_dims[1] - space->dim_y + 1;
|
||||
auto zPositionCount = prism_dims[2] - space->dim_z + 1;
|
||||
for (int x = 0; x < xPositionCount; x++) {
|
||||
for (int y = 0; y < yPositionCount; y++) {
|
||||
for (int z = 0; z < zPositionCount; z++) {
|
||||
auto new_space = 0ul;
|
||||
for (int posX = 0; posX < space->dim_x; posX++) {
|
||||
for (int posY = 0; posY < space->dim_y; posY++) {
|
||||
for (int posZ = 0; posZ < space->dim_z; posZ++) {
|
||||
auto set_val = filledAt(space, posX, posY, posZ);
|
||||
auto index_to_set = index(prism_dims[1], prism_dims[2], x + posX, y + posY, z + posZ);
|
||||
new_space = set(new_space, index_to_set, set_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
cubePositions.push_back(new_space);
|
||||
}
|
||||
}
|
||||
}
|
||||
return cubePositions;
|
||||
}
|
||||
|
||||
auto getAllPermutationsInPrism(Space *space, int prism_dims[3]) -> std::vector<uint64_t> {
|
||||
auto rotations = getUniqueRotations(space);
|
||||
auto result = std::vector<uint64_t>();
|
||||
for (auto &rotation : rotations) {
|
||||
auto positions = getAllPositionsInPrism(&rotation, prism_dims);
|
||||
result.insert(result.end(), positions.begin(), positions.end());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto size(uint64_t space) -> int {
|
||||
auto size = 0;
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if ((space & (1ul << i)) != 0) {
|
||||
size++;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +1,76 @@
|
||||
#ifndef VOXELSPACE_H
|
||||
#define VOXELSPACE_H
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include "lib/djstdlib/core.h"
|
||||
|
||||
namespace Voxel {
|
||||
constexpr int NUM_ROTS_3D = 24;
|
||||
#define NUM_ROTS_3D 24
|
||||
|
||||
struct Extrema {
|
||||
int xMax;
|
||||
int xMin;
|
||||
int yMax;
|
||||
int yMin;
|
||||
int zMax;
|
||||
int zMin;
|
||||
};
|
||||
typedef struct Extrema Extrema;
|
||||
struct Extrema {
|
||||
int xMax;
|
||||
int xMin;
|
||||
int yMax;
|
||||
int yMin;
|
||||
int zMax;
|
||||
int zMin;
|
||||
};
|
||||
|
||||
struct Space {
|
||||
uint64_t space;
|
||||
int dim_x;
|
||||
int dim_y;
|
||||
int dim_z;
|
||||
};
|
||||
typedef struct VoxelSpace VoxelSpace;
|
||||
struct VoxelSpace {
|
||||
uint64 space;
|
||||
int dim_x;
|
||||
int dim_y;
|
||||
int dim_z;
|
||||
};
|
||||
|
||||
auto newIndexRotX(Space *space, int x, int y, int z) -> int;
|
||||
DefineList(VoxelSpace, VoxelSpace);
|
||||
DefineList(uint64, VoxelSpaceRepr);
|
||||
|
||||
auto newIndexRotY(Space *space, int x, int y, int z) -> int;
|
||||
int newIndexRotX(VoxelSpace *space, int x, int y, int z);
|
||||
|
||||
auto newIndexRotZ(Space *space, int x, int y, int z) -> int;
|
||||
int newIndexRotY(VoxelSpace *space, int x, int y, int z);
|
||||
|
||||
auto toggle(uint64_t space, int index) -> uint64_t;
|
||||
int newIndexRotZ(VoxelSpace *space, int x, int y, int z);
|
||||
|
||||
auto set(uint64_t space, int index, bool val) -> uint64_t;
|
||||
uint64 toggle(uint64 space, int index);
|
||||
|
||||
auto collides(Space *a, Space *b) -> bool;
|
||||
auto collides(uint64_t a, uint64_t b) -> bool;
|
||||
uint64 set(uint64 space, int index, bool val);
|
||||
|
||||
auto add(Space *a, Space *b) -> Space;
|
||||
//bool collides(VoxelSpace *a, VoxelSpace *b);
|
||||
bool collides(uint64 a, uint64 b);
|
||||
|
||||
auto filledAt(Space *space, int x, int y, int z) -> bool;
|
||||
VoxelSpace add(VoxelSpace *a, VoxelSpace *b);
|
||||
|
||||
auto getExtrema(Space *space) -> Extrema;
|
||||
bool filledAt(VoxelSpace *space, int x, int y, int z);
|
||||
|
||||
auto cullEmptySpace(Space *space) -> void;
|
||||
Extrema getExtrema(VoxelSpace *space);
|
||||
|
||||
auto isMatch(Space *a, Space *b) -> bool;
|
||||
void cullEmptySpace(VoxelSpace *space);
|
||||
|
||||
auto rotate90X(Space *space) -> void;
|
||||
bool isMatch(VoxelSpace *a, VoxelSpace *b);
|
||||
|
||||
auto rotate90Y(Space *space) -> void;
|
||||
void rotate90X(VoxelSpace *space);
|
||||
|
||||
auto rotate90Z(Space *space) -> void;
|
||||
void rotate90Y(VoxelSpace *space);
|
||||
|
||||
auto pushNewUniqueSpins(std::vector<Space> *existingSpaces, Space* spaceToSpin) -> void;
|
||||
void rotate90Z(VoxelSpace *space);
|
||||
|
||||
auto getUniqueRotations(Space *space) -> std::vector<Space>;
|
||||
void pushNewUniqueSpins(VoxelSpaceList *existingVoxelSpaces, VoxelSpace* spaceToSpin);
|
||||
|
||||
auto getAllRotations(Space *space) -> std::vector<Space>;
|
||||
VoxelSpaceList getUniqueRotations(Arena *arena, VoxelSpace *space);
|
||||
|
||||
auto getAllPositionsInPrism(Space *space, int prism_dims[3]) -> std::vector<uint64_t>;
|
||||
VoxelSpaceList getAllRotations(Arena *arena, VoxelSpace *space);
|
||||
|
||||
auto getAllPermutationsInPrism(Space *space, int prism_dims[3]) -> std::vector<uint64_t>;
|
||||
VoxelSpaceReprList getAllPositionsInPrism(Arena *arena, VoxelSpace *space, int prism_dims[3]);
|
||||
|
||||
auto size(uint64_t space) -> int;
|
||||
}
|
||||
VoxelSpaceReprList getAllPermutationsInPrism(Arena *arena, VoxelSpace *space, int prism_dims[3]);
|
||||
|
||||
int size(uint64 space);
|
||||
|
||||
bool spaceValueAt(VoxelSpace *space, int x, int y, int z);
|
||||
|
||||
void spaceToggle(VoxelSpace *space, int x, int y, int z);
|
||||
|
||||
void spaceSet(VoxelSpace *space, bool val, int x, int y, int z);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
pub usingnamespace @cImport({
|
||||
@cInclude("glad/glad.h");
|
||||
@cInclude("GLFW/glfw3.h");
|
||||
|
||||
@cDefine("STB_IMAGE_IMPLEMENTATION", "");
|
||||
@cDefine("TINYOBJ_LOADER_C_IMPLEMENTATION", "");
|
||||
@cInclude("loaders/stb_image.h");
|
||||
@cInclude("loaders/tinyobj.h");
|
||||
});
|
||||
12
src/common.h
Normal file
12
src/common.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include "lib/djstdlib/core.h"
|
||||
#include "lib/raymath.h"
|
||||
|
||||
DefineList(RLVector2, RLVec2);
|
||||
DefineList(RLVector4, RLVec4);
|
||||
DefineList(real32, Float);
|
||||
DefineList(uint32, UInt32);
|
||||
|
||||
#endif
|
||||
26
src/debug.c
Normal file
26
src/debug.c
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "lib/raymath.h"
|
||||
#include "lib/djstdlib/core.h"
|
||||
#include "debug.h"
|
||||
|
||||
void printRLVec3(RLVector3 vec) {
|
||||
print(
|
||||
"┌ ┐\n"
|
||||
"│%7.2f%, %7.2f, %7.2f │\n"
|
||||
"└ ┘\n",
|
||||
vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
void printMatrix(Matrix mat) {
|
||||
print(
|
||||
"┌ ┐\n"
|
||||
"│%7.2f%, %7.2f, %7.2f, %7.2f │\n"
|
||||
"│%7.2f%, %7.2f, %7.2f, %7.2f │\n"
|
||||
"│%7.2f%, %7.2f, %7.2f, %7.2f │\n"
|
||||
"│%7.2f%, %7.2f, %7.2f, %7.2f │\n"
|
||||
"└ ┘\n",
|
||||
mat.m0, mat.m1, mat.m2, mat.m3,
|
||||
mat.m4, mat.m5, mat.m6, mat.m7,
|
||||
mat.m8, mat.m9, mat.m10, mat.m11,
|
||||
mat.m12, mat.m13, mat.m14, mat.m15);
|
||||
}
|
||||
|
||||
9
src/debug.h
Normal file
9
src/debug.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef DEBUG_H
|
||||
#define DEBUG_H
|
||||
|
||||
#include "lib/raymath.h"
|
||||
|
||||
void printRLVec3(RLVector3 vector);
|
||||
void printMatrix(Matrix matrix);
|
||||
|
||||
#endif
|
||||
42
src/gfx/Color.c
Normal file
42
src/gfx/Color.c
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "math.h"
|
||||
#include "Color.h"
|
||||
|
||||
real32 hueToRGB(real32 p, real32 q, real32 t) {
|
||||
if (t < 0) {
|
||||
t += 1;
|
||||
} else if (t > 1) {
|
||||
t -= 1;
|
||||
}
|
||||
if (t < 1.0f / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1.0f / 2) return q;
|
||||
if (t < 2.0f / 3) return p + (q - p) * (2.0f / 3 - t) * 6;
|
||||
return p;
|
||||
};
|
||||
|
||||
RLVector4 hslToHex(real32 h, real32 s, real32 l) {
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
real32 r, g, b;
|
||||
if (s == 0) {
|
||||
r = g = b = l;
|
||||
} else {
|
||||
real32 q = l < 0.5f ? l * (1 + s) : l + s - l * s;
|
||||
real32 p = 2 * l - q;
|
||||
r = hueToRGB(p, q, h + 1.0f / 3);
|
||||
g = hueToRGB(p, q, h);
|
||||
b = hueToRGB(p, q, h - 1.0f / 3);
|
||||
}
|
||||
return (RLVector4){r, g, b, 1};
|
||||
}
|
||||
|
||||
RLVector4 colorFromIndex(int index) {
|
||||
real32 color_wheel_cycle = floorf(index / 6.0f);
|
||||
real32 darkness_cycle = floorf(index / 12.0f);
|
||||
real32 spacing = (360.0f / 6.0f);
|
||||
real32 offset = color_wheel_cycle == 0 ? 0 : spacing / (color_wheel_cycle + 2);
|
||||
real32 hue = spacing * (index % 6) + offset;
|
||||
real32 saturation = 100.0f;
|
||||
real32 lightness = 1.0f / (2 + darkness_cycle) * 100;
|
||||
return hslToHex(hue, saturation, lightness);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#include <cstdint>
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
#include <iostream>
|
||||
#include "Color.h"
|
||||
|
||||
auto hue_to_rgb(float p, float q, float t) -> float {
|
||||
if (t < 0) {
|
||||
t += 1;
|
||||
} else if (t > 1) {
|
||||
t -= 1;
|
||||
}
|
||||
if (t < 1.0f / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1.0f / 2) return q;
|
||||
if (t < 2.0f / 3) return p + (q - p) * (2.0f / 3 - t) * 6;
|
||||
return p;
|
||||
};
|
||||
|
||||
auto hsl_to_hex(float h, float s, float l) -> glm::vec3 {
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
float r, g, b;
|
||||
if (s == 0) {
|
||||
r = g = b = l;
|
||||
} else {
|
||||
auto q = l < 0.5f ? l * (1 + s) : l + s - l * s;
|
||||
auto p = 2 * l - q;
|
||||
r = hue_to_rgb(p, q, h + 1.0f / 3);
|
||||
g = hue_to_rgb(p, q, h);
|
||||
b = hue_to_rgb(p, q, h - 1.0f / 3);
|
||||
}
|
||||
return glm::vec3(r, g, b);
|
||||
}
|
||||
|
||||
auto Color::color_from_index(int index) -> glm::vec3 {
|
||||
auto color_wheel_cycle = floorf(index / 6.0f);
|
||||
auto darkness_cycle = floorf(index / 12.0f);
|
||||
auto spacing = (360.0f / 6.0f);
|
||||
auto offset = color_wheel_cycle == 0 ? 0 : spacing / (color_wheel_cycle + 2);
|
||||
auto hue = spacing * (index % 6) + offset;
|
||||
auto saturation = 100.0f;
|
||||
auto lightness = 1.0f / (2 + darkness_cycle) * 100;
|
||||
return hsl_to_hex(hue, saturation, lightness);
|
||||
}
|
||||
@@ -1,5 +1,18 @@
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
#ifndef COLOR_H
|
||||
#define COLOR_H
|
||||
|
||||
namespace Color {
|
||||
auto color_from_index(int index) -> glm::vec3;
|
||||
};
|
||||
#include "../lib/djstdlib/core.h"
|
||||
#include "../lib/raymath.h"
|
||||
|
||||
#define COLOR_BLACK (RLVector4){0, 0, 0, 1}
|
||||
#define COLOR_RED (RLVector4){1, 0, 0, 1}
|
||||
#define COLOR_GREEN (RLVector4){0, 1, 0, 1}
|
||||
#define COLOR_BLUE (RLVector4){0, 0, 1, 1}
|
||||
#define COLOR_MAGENTA (RLVector4){1, 0, 1, 1}
|
||||
#define COLOR_YELLOW (RLVector4){1, 1, 0, 1}
|
||||
#define COLOR_CYAN (RLVector4){0, 1, 1, 1}
|
||||
#define COLOR_WHITE (RLVector4){1, 1, 1, 1}
|
||||
|
||||
RLVector4 colorFromIndex(int index);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
fn hue_to_rgb(p: f32, q: f32, t: f32) f32 {
|
||||
if (t < 0) {
|
||||
t += 1;
|
||||
} else if (t > 1) {
|
||||
t -= 1;
|
||||
}
|
||||
if (t < 1.0 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1.0 / 2) return q;
|
||||
if (t < 2.0 / 3) return p + (q - p) * (2.0 / 3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
fn hsl_to_hex(h: f32, s: f32, l: f32) @Vector(3, f32) {
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
const r: f32;
|
||||
const g: f32;
|
||||
const b: f32;
|
||||
if (s == 0) {
|
||||
r = l;
|
||||
g = l;
|
||||
b = l;
|
||||
} else {
|
||||
const q = if (l < 0.5) l * (1 + s) else l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
r = hue_to_rgb(p, q, h + 1.0 / 3);
|
||||
g = hue_to_rgb(p, q, h);
|
||||
b = hue_to_rgb(p, q, h - 1.0 / 3);
|
||||
}
|
||||
return @Vector(3, f32){ r, g, b };
|
||||
}
|
||||
|
||||
pub fn color_from_index(index: i32) @Vector(3, f32) {
|
||||
const color_wheel_cycle = @floor(index / 6.0);
|
||||
const darkness_cycle = @floor(index / 12.0);
|
||||
const spacing = (360.0 / 6.0);
|
||||
const offset = if (color_wheel_cycle == 0) 0 else spacing / (color_wheel_cycle + 2);
|
||||
const hue = spacing * (index % 6) + offset;
|
||||
const saturation = 100.0f;
|
||||
const lightness = 1.0f / (2 + darkness_cycle) * 100;
|
||||
return hsl_to_hex(hue, saturation, lightness);
|
||||
}
|
||||
116
src/gfx/Mesh.c
Normal file
116
src/gfx/Mesh.c
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "Mesh.h"
|
||||
#include "../lib/loaders/tinyobj.h"
|
||||
#include "../lib/djstdlib/os.h"
|
||||
|
||||
static void tinyobj_get_filedata(void* ctx, const char* filename, const int is_mtl, const char* obj_filename, char** data, size_t* len) {
|
||||
string file = os_readEntireFile((Arena *)ctx, s("./assets/models/cube.obj"));
|
||||
*data = file.str;
|
||||
*len = file.length;
|
||||
}
|
||||
|
||||
DefineList(uint32, MeshIndex);
|
||||
DefineList(real32, MeshValue);
|
||||
|
||||
Mesh createMesh(const char* obj_file) {
|
||||
Scratch temp = scratchStart(0, 0);
|
||||
|
||||
Mesh result = {0};
|
||||
|
||||
tinyobj_attrib_t attrib;
|
||||
tinyobj_shape_t* shapes = NULL;
|
||||
size_t num_shapes;
|
||||
tinyobj_material_t* materials = NULL;
|
||||
size_t num_materials;
|
||||
|
||||
int success = tinyobj_parse_obj(
|
||||
&attrib,
|
||||
&shapes,
|
||||
&num_shapes,
|
||||
&materials,
|
||||
&num_materials,
|
||||
obj_file,
|
||||
tinyobj_get_filedata,
|
||||
(void *)temp.arena,
|
||||
TINYOBJ_FLAG_TRIANGULATE
|
||||
);
|
||||
|
||||
if (success != TINYOBJ_SUCCESS || num_shapes <= 0) {
|
||||
print("Failed to load obj from '%s'! Success %i\n", obj_file, success);
|
||||
return result;
|
||||
}
|
||||
|
||||
MeshIndexList indices = PushFullList(temp.arena, MeshIndexList, attrib.num_faces);
|
||||
MeshValueList vertices = PushFullList(temp.arena, MeshValueList, 3*attrib.num_faces);
|
||||
MeshValueList normals = PushFullList(temp.arena, MeshValueList, 3*attrib.num_faces);
|
||||
MeshValueList texcoords = PushFullList(temp.arena, MeshValueList, 2*attrib.num_faces);
|
||||
|
||||
for (int i = 0; i < attrib.num_faces; i++) {
|
||||
tinyobj_vertex_index_t vertex_data = attrib.faces[i];
|
||||
|
||||
vertices.data[3*i] = attrib.vertices[3*vertex_data.v_idx];
|
||||
vertices.data[3*i+1] = attrib.vertices[3*vertex_data.v_idx + 1];
|
||||
vertices.data[3*i+2] = attrib.vertices[3*vertex_data.v_idx + 2];
|
||||
|
||||
normals.data[3*i] = attrib.normals[3*vertex_data.vn_idx];
|
||||
normals.data[3*i+1] = attrib.normals[3*vertex_data.vn_idx + 1];
|
||||
normals.data[3*i+2] = attrib.normals[3*vertex_data.vn_idx + 2];
|
||||
|
||||
texcoords.data[2*i] = attrib.texcoords[2*vertex_data.vt_idx];
|
||||
texcoords.data[2*i+1] = attrib.texcoords[2*vertex_data.vt_idx + 1];
|
||||
|
||||
indices.data[i] = i;
|
||||
}
|
||||
|
||||
result.num_indices = attrib.num_faces*3;
|
||||
glGenVertexArrays(1, &result.vao);
|
||||
glGenBuffers(3, result.vbos);
|
||||
|
||||
glBindVertexArray(result.vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.buffers.xyz);
|
||||
glBufferData(GL_ARRAY_BUFFER, vertices.length * sizeof(real32), vertices.data, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(real32), (void*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.buffers.uv);
|
||||
glBufferData(GL_ARRAY_BUFFER, texcoords.length * sizeof(real32), texcoords.data, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(real32), (void*)0);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.buffers.normals);
|
||||
glBufferData(GL_ARRAY_BUFFER, normals.length * sizeof(real32), normals.data, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(real32), (void*)0);
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
//glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
|
||||
|
||||
scratchEnd(temp);
|
||||
return result;
|
||||
}
|
||||
|
||||
Mesh createMeshFromShape(const Shape* shape) {
|
||||
Mesh result = {0};
|
||||
|
||||
result.num_indices = shape->indices.length;
|
||||
glGenVertexArrays(1, &result.vao);
|
||||
glGenBuffers(3, result.vbos);
|
||||
|
||||
glBindVertexArray(result.vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.buffers.xyz);
|
||||
glBufferData(GL_ARRAY_BUFFER, shape->xyz.length * sizeof(float), shape->xyz.data, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.buffers.uv);
|
||||
glBufferData(GL_ARRAY_BUFFER, shape->uv.length * sizeof(real32), shape->uv.data, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(real32), (void*)0);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.buffers.elements);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape->indices.length * sizeof(uint32), shape->indices.data, GL_STATIC_DRAW);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
#include <iostream>
|
||||
#include "Mesh.h"
|
||||
#include "loaders/tinyobj.h"
|
||||
|
||||
auto Mesh::init(const char* obj_file) -> void {
|
||||
auto reader = tinyobj::ObjReader();
|
||||
auto success = reader.ParseFromFile(obj_file);
|
||||
std::cout << reader.Error() << std::endl;
|
||||
|
||||
auto attrib = reader.GetAttrib();
|
||||
|
||||
auto indices_t = reader.GetShapes().at(0).mesh.indices;
|
||||
auto indices = std::vector<unsigned int>(indices_t.size());
|
||||
|
||||
auto vertices = std::vector<float>(3*indices_t.size());
|
||||
auto normals = std::vector<float>(3*indices_t.size());
|
||||
auto texcoords = std::vector<float>(2*indices_t.size());
|
||||
|
||||
for (int i = 0; i < indices_t.size(); i++) {
|
||||
auto vertex_data = indices_t[i];
|
||||
vertices[3*i] = attrib.vertices[3*vertex_data.vertex_index];
|
||||
vertices[3*i+1] = attrib.vertices[3*vertex_data.vertex_index + 1];
|
||||
vertices[3*i+2] = attrib.vertices[3*vertex_data.vertex_index + 2];
|
||||
|
||||
normals[3*i] = attrib.normals[3*vertex_data.normal_index];
|
||||
normals[3*i+1] = attrib.normals[3*vertex_data.normal_index + 1];
|
||||
normals[3*i+2] = attrib.normals[3*vertex_data.normal_index + 2];
|
||||
|
||||
texcoords[2*i] = attrib.texcoords[2*vertex_data.texcoord_index];
|
||||
texcoords[2*i+1] = attrib.texcoords[2*vertex_data.texcoord_index + 1];
|
||||
|
||||
indices[i] = i;
|
||||
}
|
||||
|
||||
num_indices = indices_t.size();
|
||||
glGenVertexArrays(1, &vao);
|
||||
glGenBuffers(1, &vbo_xyz);
|
||||
glGenBuffers(1, &vbo_uv);
|
||||
glGenBuffers(1, &vbo_norm);
|
||||
//glGenBuffers(1, &ebo);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_xyz);
|
||||
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_uv);
|
||||
glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(float), texcoords.data(), GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_norm);
|
||||
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(float), normals.data(), GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
//glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
auto Mesh::init(const LeddaGeometry::Shape* shape) -> void {
|
||||
num_indices = shape->indices_size;
|
||||
glGenVertexArrays(1, &vao);
|
||||
glGenBuffers(1, &vbo_xyz);
|
||||
glGenBuffers(1, &vbo_uv);
|
||||
glGenBuffers(1, &ebo);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_xyz);
|
||||
glBufferData(GL_ARRAY_BUFFER, shape->xyz_size * sizeof(float), shape->xyz, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_uv);
|
||||
glBufferData(GL_ARRAY_BUFFER, shape->uv_size * sizeof(float), shape->uv, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape->indices_size * sizeof(unsigned int), shape->indices, GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
#ifndef LEDDA_MESH_H
|
||||
#define LEDDA_MESH_H
|
||||
|
||||
#include "glad/glad.h"
|
||||
#include "../lib/glad/glad.h"
|
||||
#include "../lib/djstdlib/core.h"
|
||||
#include "geometry.h"
|
||||
|
||||
struct Mesh {
|
||||
unsigned int vao;
|
||||
unsigned int vbo_xyz;
|
||||
unsigned int vbo_uv;
|
||||
unsigned int vbo_norm;
|
||||
unsigned int ebo;
|
||||
unsigned int num_indices;
|
||||
auto init(const char* obj_file) -> void;
|
||||
auto init(const LeddaGeometry::Shape* shape) -> void;
|
||||
};
|
||||
typedef struct {
|
||||
uint32 vao;
|
||||
union {
|
||||
struct {
|
||||
uint32 xyz;
|
||||
uint32 uv;
|
||||
uint32 normals;
|
||||
uint32 elements;
|
||||
} buffers;
|
||||
uint32 vbos[4];
|
||||
};
|
||||
uint64 num_indices;
|
||||
} Mesh;
|
||||
|
||||
Mesh createMesh(const char* obj_file);
|
||||
Mesh createMeshFromShape(const Shape* shape);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
const std = @import("std");
|
||||
const c = @import("../c.zig");
|
||||
const djleddaGeom = @import("djleddaGeom.zig");
|
||||
|
||||
pub const Mesh = struct {
|
||||
vao: c_uint,
|
||||
vbo_xyz: c_uint,
|
||||
vbo_uv: c_uint,
|
||||
vbo_norm: c_uint,
|
||||
ebo: c_uint,
|
||||
num_indices: c_uint,
|
||||
|
||||
pub fn from_shape(shape: *const djleddaGeom.Shape) void {
|
||||
const mesh = Mesh{};
|
||||
mesh.num_indices = shape.indices.len;
|
||||
c.glGenVertexArrays(1, &mesh.vao);
|
||||
c.glGenBuffers(1, &mesh.vbo_xyz);
|
||||
c.glGenBuffers(1, &mesh.vbo_uv);
|
||||
c.glGenBuffers(1, &mesh.ebo);
|
||||
|
||||
c.glBindVertexArray(mesh.vao);
|
||||
|
||||
c.glBindBuffer(c.GL_ARRAY_BUFFER, mesh.vbo_xyz);
|
||||
c.glBufferData(c.GL_ARRAY_BUFFER, shape.xyz.ptr * @sizeOf(float), shape.xyz, c.GL_STATIC_DRAW);
|
||||
c.glVertexAttribPointer(0, 3, c.GL_FLOAT, c.GL_FALSE, 3 * @sizeOf(f32), @as(*void, 0));
|
||||
c.glEnableVertexAttribArray(0);
|
||||
|
||||
c.glBindBuffer(c.GL_ARRAY_BUFFER, mesh.vbo_uv);
|
||||
c.glBufferData(c.GL_ARRAY_BUFFER, shape.uv.ptr * @sizeOf(f32), shape.uv, c.GL_STATIC_DRAW);
|
||||
c.glVertexAttribPointer(1, 2, c.GL_FLOAT, c.GL_FALSE, 2 * @sizeOf(f32), @as(*void, 0));
|
||||
c.glEnableVertexAttribArray(1);
|
||||
|
||||
c.glBindBuffer(c.GL_ELEMENT_ARRAY_BUFFER, mesh.ebo);
|
||||
c.glBufferData(c.GL_ELEMENT_ARRAY_BUFFER, shape.indices.len * @sizeOf(c_uint), shape.indices.ptr, c.GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
// pub fn init(obj_file: *[]const u8) void {
|
||||
// const reader = c.tinyobj.ObjReader();
|
||||
// const success = reader.ParseFromFile(obj_file);
|
||||
// std.debug.print("{}\n", .{reader.Error()});
|
||||
//
|
||||
// const attrib = reader.GetAttrib();
|
||||
//
|
||||
// const indices_t = reader.GetShapes().at(0).mesh.indices;
|
||||
// const indices = ArrayList(c_uint)(indices_t.size());
|
||||
//
|
||||
// const vertices = ArrayList()(3*indices_t.size());
|
||||
// const normals = ArrayList()(3*indices_t.size());
|
||||
// const texcoords = ArrayList()(2*indices_t.size());
|
||||
//
|
||||
// for (int i = 0; i < indices_t.size(); i++) {
|
||||
// const vertex_data = indices_t[i];
|
||||
// vertices[3*i] = attrib.vertices[3*vertex_data.vertex_index];
|
||||
// vertices[3*i+1] = attrib.vertices[3*vertex_data.vertex_index + 1];
|
||||
// vertices[3*i+2] = attrib.vertices[3*vertex_data.vertex_index + 2];
|
||||
//
|
||||
// normals[3*i] = attrib.normals[3*vertex_data.normal_index];
|
||||
// normals[3*i+1] = attrib.normals[3*vertex_data.normal_index + 1];
|
||||
// normals[3*i+2] = attrib.normals[3*vertex_data.normal_index + 2];
|
||||
//
|
||||
// texcoords[2*i] = attrib.texcoords[2*vertex_data.texcoord_index];
|
||||
// texcoords[2*i+1] = attrib.texcoords[2*vertex_data.texcoord_index + 1];
|
||||
//
|
||||
// indices[i] = i;
|
||||
// }
|
||||
//
|
||||
// num_indices = indices_t.size();
|
||||
// glGenVertexArrays(1, &vao);
|
||||
// glGenBuffers(1, &vbo_xyz);
|
||||
// glGenBuffers(1, &vbo_uv);
|
||||
// glGenBuffers(1, &vbo_norm);
|
||||
// //glGenBuffers(1, &ebo);
|
||||
//
|
||||
// glBindVertexArray(vao);
|
||||
//
|
||||
// glBindBuffer(GL_ARRAY_BUFFER, vbo_xyz);
|
||||
// glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
|
||||
// glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
// glEnableVertexAttribArray(0);
|
||||
//
|
||||
// glBindBuffer(GL_ARRAY_BUFFER, vbo_uv);
|
||||
// glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(float), texcoords.data(), GL_STATIC_DRAW);
|
||||
// glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
|
||||
// glEnableVertexAttribArray(1);
|
||||
//
|
||||
// glBindBuffer(GL_ARRAY_BUFFER, vbo_norm);
|
||||
// glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(float), normals.data(), GL_STATIC_DRAW);
|
||||
// glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
// glEnableVertexAttribArray(2);
|
||||
//
|
||||
// //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
// //glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
|
||||
// }
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
#ifndef ORBIT_CONTROLS_H
|
||||
#define ORBIT_CONTROLS_H
|
||||
|
||||
#include "glad/glad.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "loaders/stb_image.h"
|
||||
|
||||
constexpr auto ROTATION_FACTOR = 1.0f / 200.0f;
|
||||
|
||||
struct Point {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
class OrbitControls {
|
||||
private:
|
||||
bool dragging;
|
||||
bool hovered;
|
||||
bool scrolling;
|
||||
bool flyingEnabled;
|
||||
float lastX;
|
||||
float lastY;
|
||||
Point lastScroll1;
|
||||
Point lastScroll2;
|
||||
glm::vec3 y_axis;
|
||||
glm::vec3 x_axis;
|
||||
glm::vec3 start;
|
||||
Entity* orbited_object;
|
||||
|
||||
OrbitControls(Entity* orbited, Camera* camera) {
|
||||
camera = camera;
|
||||
orbited_object = orbited;
|
||||
y_axis = orbited_object.worldToLocal(camera.up);
|
||||
x_axis = orbited_object.position.sub(camera.position);
|
||||
x_axis /= sqrt(pow(x_axis.x) + pow(x_axis.y, 2) + pow(x_axis.z, 2));
|
||||
x_axis = glm::cross(x_axis, y_axis);
|
||||
start = orbited_object.rotation;
|
||||
|
||||
this.element.addEventListener('wheel', (ev) => this.handleScroll(ev));
|
||||
this.element.addEventListener('mouseover', () => this.hovered = true);
|
||||
this.element.addEventListener('mouseout', () => this.hovered = false);
|
||||
this.element.addEventListener('mousedown', (ev) => this.handleMouseDown(ev));
|
||||
window.addEventListener('mousemove', (ev) => this.handleMove(ev));
|
||||
window.addEventListener('mouseup', () => this.dragging = false);
|
||||
}
|
||||
|
||||
on_mouse_down(event) {
|
||||
if (event.button === 1) {
|
||||
this.object.setRotationFromEuler(this.start);
|
||||
}
|
||||
if (!this.dragging) {
|
||||
this.lastX = event.x;
|
||||
this.lastY = event.y;
|
||||
this.dragging = true;
|
||||
}
|
||||
}
|
||||
|
||||
on_mouse_move(event) {
|
||||
if (dragging) {
|
||||
auto x_diff = event.movementX * ROTATION_FACTOR;
|
||||
auto y_diff = event.movementY * ROTATION_FACTOR;
|
||||
glm::rotate(&orbited_object, x_diff, &y_axis);
|
||||
//rotate on world axis ???
|
||||
glm::rotate(&orbited_object, y_diff &x_axis);
|
||||
}
|
||||
}
|
||||
|
||||
on_scroll(event) {
|
||||
if (this.flyingEnabled && this.hovered) {
|
||||
for (const fliable of this.fliables) {
|
||||
const direction = event.deltaY / Math.abs(event.deltaY);
|
||||
fliable.flyBy(direction / 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
100
src/gfx/Shader.c
Normal file
100
src/gfx/Shader.c
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "Shader.h"
|
||||
#include "../lib/djstdlib/core.h"
|
||||
#include "../lib/djstdlib/os.h"
|
||||
#include "../lib/glad/glad.h"
|
||||
#include "../lib/raymath.h"
|
||||
|
||||
typedef enum {
|
||||
ShaderType_fragment=GL_FRAGMENT_SHADER,
|
||||
ShaderType_vertex=GL_VERTEX_SHADER,
|
||||
} ShaderType;
|
||||
|
||||
uint32 createGlShader(string file_path, ShaderType shader_type) {
|
||||
GLuint shader;
|
||||
WithScratch(scratch) {
|
||||
string shader_code = os_readEntireFile(scratch.arena, file_path);
|
||||
shader = glCreateShader(shader_type);
|
||||
const char *shader_code_cstr = cstring(scratch.arena, shader_code);
|
||||
glShaderSource(shader, 1, &shader_code_cstr, NULL);
|
||||
glCompileShader(shader);
|
||||
int success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
GLint info_log_length;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||
string info_log = PushString(scratch.arena, (size_t)info_log_length + 1);
|
||||
glGetShaderInfoLog(shader, info_log_length, NULL, info_log.str);
|
||||
string shader_type_name = shader_type == ShaderType_fragment ? s("FRAGMENT") : s("VERTEX");
|
||||
print("%S shader compilation error (%S):\n%S", shader_type_name, file_path, info_log);
|
||||
}
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
Shader createShader(string vertex_path, string fragment_path) {
|
||||
Shader result = {0};
|
||||
WithScratch(temp) {
|
||||
uint32 vertex_shader = createGlShader(vertex_path, ShaderType_vertex);
|
||||
uint32 fragment_shader = createGlShader(fragment_path, ShaderType_fragment);
|
||||
|
||||
result.progId = glCreateProgram();
|
||||
glAttachShader(result.progId, vertex_shader);
|
||||
glAttachShader(result.progId, fragment_shader);
|
||||
glLinkProgram(result.progId);
|
||||
|
||||
int success;
|
||||
glGetProgramiv(result.progId, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
GLint info_log_length;
|
||||
glGetShaderiv(result.progId, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||
string info_log_prog = PushString(temp.arena, (size_t)info_log_length + 1);
|
||||
glGetProgramInfoLog(result.progId, info_log_length, NULL, info_log_prog.str);
|
||||
print("Shader program link error:\n%S", info_log_prog);
|
||||
}
|
||||
|
||||
glDeleteShader(vertex_shader);
|
||||
glDeleteShader(fragment_shader);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void setUniformMat4fv(Shader *s, const char *uniformName, Matrix *matrix) {
|
||||
glUniformMatrix4fv(
|
||||
glGetUniformLocation(s->progId, uniformName),
|
||||
1, GL_FALSE, MatrixToFloat(*matrix));
|
||||
}
|
||||
void setUniformMat4fvByLoc(int uniformLocation, Matrix *matrix) {
|
||||
glUniformMatrix4fv(uniformLocation, 1, GL_FALSE, MatrixToFloat(*matrix));
|
||||
}
|
||||
|
||||
void setUniform4fv(Shader *s, const char *uniformName, RLVector4 *vector) {
|
||||
glUniform4fv(glGetUniformLocation(s->progId, uniformName), 1, (const GLfloat *)vector);
|
||||
}
|
||||
void setUniform4fvByLoc(int uniformLocation, RLVector4 *vector) {
|
||||
glUniform4fv(uniformLocation, 1, (const GLfloat *)vector);
|
||||
}
|
||||
|
||||
void setUniform3fv(Shader *s, const char *uniformName, RLVector3 *vector) {
|
||||
glUniform3fv(glGetUniformLocation(s->progId, uniformName), 1, Vector3ToFloat(*vector));
|
||||
}
|
||||
void setUniform3fvByLoc(int uniformLocation, RLVector3 *vector) {
|
||||
glUniform3fv(uniformLocation, 1, Vector3ToFloat(*vector));
|
||||
}
|
||||
|
||||
void setUniform1i(Shader *s, const char *uniformName, int32 i) {
|
||||
glUniform1i(glGetUniformLocation(s->progId, uniformName), (GLint)i);
|
||||
}
|
||||
void setUniform1iByLoc(int uniformLocation, int32 i) {
|
||||
glUniform1i(uniformLocation, (GLint)i);
|
||||
}
|
||||
|
||||
void setUniform2fv(Shader *s, const char *uniformName, RLVector2 *vector) {
|
||||
glUniform2fv(glGetUniformLocation(s->progId, uniformName), 1, (const GLfloat *)vector);
|
||||
}
|
||||
void setUniform2fvByLoc(int uniformLocation, RLVector2 *vector) {
|
||||
glUniform2fv(uniformLocation, 1, (const GLfloat *)vector);
|
||||
}
|
||||
|
||||
int getUniformLocation(Shader *s, const char *uniformName) {
|
||||
return glGetUniformLocation(s->progId, uniformName);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
#include "glad/glad.h"
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include "Shader.h"
|
||||
|
||||
enum ShaderType {
|
||||
fragment=GL_FRAGMENT_SHADER,
|
||||
vertex=GL_VERTEX_SHADER,
|
||||
};
|
||||
|
||||
auto create_shader(const char* file_path, ShaderType shader_type, char* info_log) -> unsigned int {
|
||||
std::stringstream shader_stream;
|
||||
std::ifstream shader_file;
|
||||
shader_file.open(file_path);
|
||||
shader_stream << shader_file.rdbuf();
|
||||
shader_file.close();
|
||||
auto shader_string = shader_stream.str();
|
||||
const auto shader_code = shader_string.c_str();
|
||||
|
||||
auto vertex_shader = glCreateShader(shader_type);
|
||||
glShaderSource(vertex_shader, 1, &shader_code, NULL);
|
||||
glCompileShader(vertex_shader);
|
||||
int success;
|
||||
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
|
||||
auto shader_type_name = shader_type == ShaderType::fragment ? "FRAGMENT" : "VERTEX";
|
||||
std::cout << "ERROR::SHADER::" << shader_type_name << "::COMPILATION_FAILED\n" << info_log << std::endl;
|
||||
}
|
||||
|
||||
return vertex_shader;
|
||||
}
|
||||
|
||||
auto Shader::init(const char* vertex_path, const char* fragment_path) -> void {
|
||||
auto info_log = std::array<char, 512>();
|
||||
auto vertex_shader = create_shader(vertex_path, ShaderType::vertex, info_log.data());
|
||||
auto fragment_shader = create_shader(fragment_path, ShaderType::fragment, info_log.data());
|
||||
|
||||
prog_id = glCreateProgram();
|
||||
glAttachShader(prog_id, vertex_shader);
|
||||
glAttachShader(prog_id, fragment_shader);
|
||||
glLinkProgram(prog_id);
|
||||
|
||||
int success;
|
||||
glGetProgramiv(prog_id, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
glGetProgramInfoLog(prog_id, 512, NULL, info_log.data());
|
||||
std::cout << "ERROR::SHADER::PROGRAM::LINK_FAILED\n" << info_log.data() << std::endl;
|
||||
}
|
||||
|
||||
glDeleteShader(vertex_shader);
|
||||
glDeleteShader(fragment_shader);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
#ifndef LEDDA_SHADER_H
|
||||
#define LEDDA_SHADER_H
|
||||
|
||||
struct Shader {
|
||||
unsigned int prog_id;
|
||||
auto init(const char* vertex_path, const char* fragment_path) -> void;
|
||||
};
|
||||
#include "../lib/raymath.h"
|
||||
#include "../lib/djstdlib/core.h"
|
||||
|
||||
typedef struct {
|
||||
uint32 progId;
|
||||
} Shader;
|
||||
|
||||
Shader createShader(string vertex_path, string fragment_path);
|
||||
|
||||
void setUniformMat4fv(Shader *s, const char *uniformName, Matrix *matrix);
|
||||
void setUniformMat4fvByLoc(int uniformLocation, Matrix *matrix);
|
||||
|
||||
void setUniform4fv(Shader *s, const char *uniformName, RLVector4 *vector);
|
||||
void setUniform4fvByLoc(int uniformLocation, RLVector4 *vector);
|
||||
|
||||
void setUniform3fv(Shader *s, const char *uniformName, RLVector3 *vector);
|
||||
void setUniform3fvByLoc(int uniformLocation, RLVector3 *vector);
|
||||
|
||||
void setUniform2fv(Shader *s, const char *uniformName, RLVector2 *vector);
|
||||
void setUniform2fvByLoc(int uniformLocation, RLVector2 *vector);
|
||||
|
||||
void setUniform1i(Shader *s, const char *uniformName, int32 i);
|
||||
void setUniform1iByLoc(int uniformLocation, int32 i);
|
||||
|
||||
int getUniformLocation(Shader *s, const char *uniformName);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
const c = @import("../c.zig");
|
||||
const std = @import("std");
|
||||
|
||||
const ShaderType = enum(u32) {
|
||||
fragment = c.GL_FRAGMENT_SHADER,
|
||||
vertex = c.GL_VERTEX_SHADER,
|
||||
};
|
||||
|
||||
fn create_shader(file_path: []const u8, shader_type: ShaderType, info_log: *[]const u8, allocator: *std.mem.Allocator) c_uint {
|
||||
const file = try std.fs.openFileAbsolute(file_path);
|
||||
|
||||
const file_reader = file.reader(file);
|
||||
const shader_code = std.ArrayList(u8);
|
||||
shader_code.initCapacity(allocator, 1024);
|
||||
defer allocator.free(shader_code);
|
||||
|
||||
file_reader.readAllArrayList(shader_code, 1024 * 1024);
|
||||
|
||||
const vertex_shader = c.glCreateShader(shader_type);
|
||||
c.glShaderSource(vertex_shader, 1, &shader_code.items, c.NULL);
|
||||
c.glCompileShader(vertex_shader);
|
||||
const success: i32 = undefined;
|
||||
c.glGetShaderiv(vertex_shader, c.GL_COMPILE_STATUS, &success);
|
||||
if (success != 0) {
|
||||
c.glGetShaderInfoLog(vertex_shader, 512, c.NULL, info_log);
|
||||
const shader_type_name = if (shader_type == ShaderType.fragment) "FRAGMENT" else "VERTEX";
|
||||
std.debug.print("ERROR::SHADER::{}::COMPILATION_FAILED\n{}\n", .{ shader_type_name, info_log });
|
||||
}
|
||||
|
||||
return vertex_shader;
|
||||
}
|
||||
|
||||
const Shader = struct {
|
||||
prog_id: c_uint,
|
||||
|
||||
pub fn init(self: Shader, vertex_path: *[]const u8, fragment_path: *[]const u8, allocator: *std.mem.Allocator) void {
|
||||
const info_log = [512]u8{};
|
||||
const vertex_shader = create_shader(vertex_path, ShaderType.vertex, &info_log, allocator);
|
||||
const fragment_shader = create_shader(fragment_path, ShaderType.fragment, &info_log, allocator);
|
||||
|
||||
self.prog_id = c.glCreateProgram();
|
||||
c.glAttachShader(self.prog_id, vertex_shader);
|
||||
c.glAttachShader(self.prog_id, fragment_shader);
|
||||
c.glLinkProgram(self.prog_id);
|
||||
|
||||
const success: c_uint = undefined;
|
||||
c.glGetProgramiv(self.prog_id, c.GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
c.glGetProgramInfoLog(self.prog_id, 512, c.NULL, &info_log);
|
||||
std.debug.print("ERROR::SHADER::PROGRAM::LINK_FAILED\n{}\n", .{info_log});
|
||||
}
|
||||
|
||||
c.glDeleteShader(vertex_shader);
|
||||
c.glDeleteShader(fragment_shader);
|
||||
}
|
||||
};
|
||||
134
src/gfx/Texture.c
Normal file
134
src/gfx/Texture.c
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "Texture.h"
|
||||
|
||||
Texture createTexture(const char* bitmap, int32 width, int32 height) {
|
||||
Texture result = {0};
|
||||
result.width = width;
|
||||
result.height = height;
|
||||
glGenTextures(1, &result.tex_id);
|
||||
glBindTexture(GL_TEXTURE_2D, result.tex_id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, result.width, result.height, 0, GL_RGB, GL_UNSIGNED_BYTE, bitmap);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DefineList(stbtt_bakedchar, STBBakedChar);
|
||||
|
||||
static void saveGrayscaleTarga(string location, const char *bytes, int width, int height) {
|
||||
char tgaHeader[18] = {
|
||||
// Image ID length
|
||||
0x00,
|
||||
// Color map type
|
||||
0x00,
|
||||
// Image type
|
||||
0x03,
|
||||
// Color map info (unused)
|
||||
0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
// Image spec
|
||||
0x0, 0x0, // x origin
|
||||
0x0, 0x0, // y origin
|
||||
width & 0xFF, (width >> 8) & 0xFF, // width
|
||||
height & 0xFF, (height >> 8) & 0xFF, // width
|
||||
0x8, // bits per px (depth)
|
||||
0b00000000, // image descriptor
|
||||
};
|
||||
os_writeEntireFile(location, tgaHeader, ArrayCount(tgaHeader));
|
||||
os_fileAppend(location, bytes, width * height);
|
||||
}
|
||||
|
||||
Font createFont(Arena *arena, string ttfLocation, real32 lineHeight) {
|
||||
const int CODEPOINT_START = 32;
|
||||
const int CODEPOINT_END = 126;
|
||||
|
||||
int32 atlasWidth = ((int32)lineHeight)*(int32)sqrt(CODEPOINT_END - CODEPOINT_START);
|
||||
int32 atlasHeight = ((int32)lineHeight)*(int32)sqrt(CODEPOINT_END - CODEPOINT_START);
|
||||
|
||||
STBBakedCharList bakedCharlist = PushFullListZero(arena, STBBakedCharList, CODEPOINT_END - CODEPOINT_START);
|
||||
string fontFile = os_readEntireFile(arena, ttfLocation);
|
||||
|
||||
stbtt_fontinfo info;
|
||||
stbtt_InitFont(&info, (unsigned char *)fontFile.str, stbtt_GetFontOffsetForIndex((unsigned char *)fontFile.str, 0));
|
||||
int ascent, descent, lineGap;
|
||||
stbtt_GetFontVMetrics(&info, &ascent, &descent, &lineGap);
|
||||
real32 scale = stbtt_ScaleForPixelHeight(&info, lineHeight);
|
||||
|
||||
char *bakedFontBitmap = PushArrayZero(arena, char, atlasWidth*atlasHeight);
|
||||
int32 bake_result = stbtt_BakeFontBitmap(
|
||||
(unsigned char *)fontFile.str, 0,
|
||||
lineHeight,
|
||||
(unsigned char *)bakedFontBitmap, atlasWidth, atlasHeight,
|
||||
CODEPOINT_START, CODEPOINT_END - CODEPOINT_START,
|
||||
bakedCharlist.data);
|
||||
|
||||
GlyphMetaList glyphMeta = PushFullListZero(arena, GlyphMetaList, bakedCharlist.length);
|
||||
real32 x, y;
|
||||
real32 lastX, lastY;
|
||||
for (EachIn(bakedCharlist, i)) {
|
||||
stbtt_aligned_quad q;
|
||||
stbtt_GetBakedQuad(bakedCharlist.data, atlasWidth, atlasHeight, i, &x, &y, &q, 1);
|
||||
glyphMeta.data[i] = (GlyphMeta){
|
||||
.uv0=(RLVector2){q.s0, q.t1},
|
||||
.uv1=(RLVector2){q.s1, q.t0},
|
||||
.xOffset=q.x0 - lastX,
|
||||
.yOffset=q.y0 + lineHeight + scale * descent,
|
||||
.width=q.x1-q.x0,
|
||||
.height=q.y1-q.y0,
|
||||
};
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
}
|
||||
|
||||
Font result = {0};
|
||||
result.glyphMeta = glyphMeta;
|
||||
result.lineHeight = lineHeight;
|
||||
result.charWidth = bakedCharlist.data[0].xadvance;
|
||||
|
||||
//saveGrayscaleTarga(s("atlas.tga"), bakedFontBitmap, atlasWidth, atlasHeight);
|
||||
|
||||
glGenTextures(1, &result.texId);
|
||||
glBindTexture(GL_TEXTURE_2D, result.texId);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, atlasWidth, atlasHeight, 0, GL_RED, GL_UNSIGNED_BYTE, bakedFontBitmap);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
glGenBuffers(1, &result.glyphTableBufId);
|
||||
glBindBuffer(GL_TEXTURE_BUFFER, result.glyphTableBufId);
|
||||
glBufferData(GL_TEXTURE_BUFFER, sizeof(result.glyphMeta.data[0])*result.glyphMeta.length, result.glyphMeta.data, GL_STATIC_DRAW);
|
||||
|
||||
glGenTextures(1, &result.glyphTableTexId);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, result.glyphTableTexId);
|
||||
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, result.glyphTableBufId);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Texture createTextureFromFile(const char* source_path) {
|
||||
Texture result = {0};
|
||||
glGenTextures(1, &result.tex_id);
|
||||
glBindTexture(GL_TEXTURE_2D, result.tex_id);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
int nr_channels;
|
||||
stbi_uc *data = stbi_load(source_path, &result.width, &result.height, &nr_channels, 0);
|
||||
if (data) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, result.width, result.height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
} else {
|
||||
print("Failed to load texture.");
|
||||
}
|
||||
stbi_image_free(data);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#include "Texture.h"
|
||||
#include <iostream>
|
||||
#include "loaders/stb_image.h"
|
||||
#include "glad/glad.h"
|
||||
|
||||
auto Texture::init(const char* source_path) -> void {
|
||||
glGenTextures(1, &tex_id);
|
||||
glBindTexture(GL_TEXTURE_2D, tex_id);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
int nr_channels;
|
||||
auto data = stbi_load(source_path, &width, &height, &nr_channels, 0);
|
||||
if (data) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
} else {
|
||||
std::cout << "Failed to load texture." << std::endl;
|
||||
}
|
||||
stbi_image_free(data);
|
||||
}
|
||||
@@ -1,11 +1,45 @@
|
||||
#ifndef LEDDA_TEXTURE_H
|
||||
#define LEDDA_TEXTURE_H
|
||||
|
||||
#include "../lib/djstdlib/core.h"
|
||||
#include "../lib/loaders/stb_truetype.h"
|
||||
#include "../lib/loaders/stb_image.h"
|
||||
#include "../lib/glad/glad.h"
|
||||
#include "../lib/djstdlib/core.h"
|
||||
#include "../lib/djstdlib/os.h"
|
||||
#include "../common.h"
|
||||
|
||||
typedef struct Texture Texture;
|
||||
struct Texture {
|
||||
unsigned int tex_id;
|
||||
int width;
|
||||
int height;
|
||||
auto init(const char* source_path) -> void;
|
||||
uint32 tex_id;
|
||||
int32 width;
|
||||
int32 height;
|
||||
};
|
||||
|
||||
typedef struct GlyphMeta GlyphMeta;
|
||||
struct GlyphMeta {
|
||||
RLVector2 uv0;
|
||||
RLVector2 uv1;
|
||||
real32 xOffset;
|
||||
real32 yOffset;
|
||||
real32 width;
|
||||
real32 height;
|
||||
};
|
||||
|
||||
DefineList(GlyphMeta, GlyphMeta);
|
||||
|
||||
typedef struct Font Font;
|
||||
struct Font {
|
||||
GlyphMetaList glyphMeta;
|
||||
real32 lineHeight;
|
||||
real32 charWidth;
|
||||
uint32 texId;
|
||||
uint32 glyphTableTexId;
|
||||
uint32 glyphTableBufId;
|
||||
};
|
||||
|
||||
Texture createTexture(const char* bitmap, int32 width, int32 height);
|
||||
Texture createTextureFromFile(const char* source_path);
|
||||
Font createFont(Arena *arena, string ttfLocation, real32 lineHeight);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
// Buffer layout:
|
||||
// X, Y, Z, U, V
|
||||
|
||||
pub const Shape = struct {
|
||||
indices: []c_uint,
|
||||
uv: []f32,
|
||||
xyz: []f32,
|
||||
};
|
||||
|
||||
const triangle_vertices = []f32{
|
||||
-0.5, -0.5, 0.0, 1.0, 1.0,
|
||||
0.5, -0.5, 0.0, 0.5, 0.5,
|
||||
0.0, 0.5, 0.0, 0.0, 0.0,
|
||||
};
|
||||
|
||||
const triangle_indices = []c_uint{ 0, 1, 2 };
|
||||
|
||||
const cube_vertices = []f32{ -0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 1.0, -0.5, 0.5, 0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, -0.5, 1.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 1.0, 1.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 1.0 };
|
||||
|
||||
const cube_indices = []c_uint{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
|
||||
|
||||
const square_xyz = []f32{
|
||||
0.5, 0.5, 0.0,
|
||||
0.5, -0.5, 0.0,
|
||||
-0.5, -0.5, 0.0,
|
||||
-0.5, 0.5, 0.0,
|
||||
};
|
||||
|
||||
const square_uv = []f32{
|
||||
1.0, 1.0,
|
||||
1.0, 0.0,
|
||||
0.0, 0.0,
|
||||
0.0, 1.0,
|
||||
};
|
||||
|
||||
const square_indices = []c_uint{
|
||||
0, 1, 3,
|
||||
1, 2, 3,
|
||||
};
|
||||
|
||||
pub const TRIANGLE = Shape{
|
||||
.indices = triangle_indices,
|
||||
.uv = triangle_vertices,
|
||||
.xyz = triangle_vertices,
|
||||
};
|
||||
|
||||
pub const SQUARE = Shape{
|
||||
.indices = square_indices,
|
||||
.uv = square_uv,
|
||||
.xyz = square_xyz,
|
||||
};
|
||||
|
||||
pub const CUBE = Shape{
|
||||
.indices = cube_indices,
|
||||
.uv = triangle_vertices,
|
||||
.xyz = triangle_vertices,
|
||||
};
|
||||
@@ -1,20 +1,21 @@
|
||||
#include <array>
|
||||
#include "geometry.h"
|
||||
#include "../common.h"
|
||||
#include "../lib/djstdlib/core.h"
|
||||
|
||||
// Buffer layout:
|
||||
// X, Y, Z, U, V
|
||||
|
||||
auto triangle_vertices = std::to_array<float>({
|
||||
const FloatList triangle_vertices = AsList(FloatList, {
|
||||
-0.5f, -0.5f, 0.0f, 1.0f, 1.0f,
|
||||
0.5f, -0.5f, 0.0f, 0.5f, 0.5f,
|
||||
0.0f, 0.5f, 0.0f, 0.0f, 0.0f,
|
||||
});
|
||||
|
||||
auto triangle_indices = std::to_array<unsigned int>({
|
||||
const UInt32List triangle_indices = AsList(UInt32List, {
|
||||
0, 1, 2
|
||||
});
|
||||
|
||||
auto cube_vertices = std::to_array<float>({
|
||||
const FloatList cube_vertices = AsList(FloatList, {
|
||||
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
|
||||
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
|
||||
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
|
||||
@@ -58,55 +59,44 @@ auto cube_vertices = std::to_array<float>({
|
||||
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
|
||||
});
|
||||
|
||||
auto cube_indices = std::to_array<unsigned int>({
|
||||
const UInt32List cube_indices = AsList(UInt32List, {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
|
||||
});
|
||||
|
||||
auto square_xyz = std::to_array<float>({
|
||||
0.5f, 0.5f, 0.0f,
|
||||
0.5f, -0.5f, 0.0f,
|
||||
-0.5f, -0.5f, 0.0f,
|
||||
-0.5f, 0.5f, 0.0f,
|
||||
const FloatList square_xyz = AsList(FloatList, {
|
||||
200.0f, 200.0f, 0.0f,
|
||||
200.0f, -200.0f, 0.0f,
|
||||
-200.0f, -200.0f, 0.0f,
|
||||
-200.0f, 200.0f, 0.0f,
|
||||
});
|
||||
|
||||
auto square_uv = std::to_array<float>({
|
||||
const FloatList square_uv = AsList(FloatList, {
|
||||
1.0f, 1.0f,
|
||||
1.0f, 0.0f,
|
||||
0.0f, 0.0f,
|
||||
0.0f, 1.0f,
|
||||
});
|
||||
|
||||
auto square_indices = std::to_array<unsigned int>({
|
||||
const UInt32List square_indices = AsList(UInt32List, {
|
||||
0, 1, 3,
|
||||
1, 2, 3,
|
||||
});
|
||||
|
||||
namespace LeddaGeometry {
|
||||
const Shape TRIANGLE = {
|
||||
.indices = triangle_indices.data(),
|
||||
.indices_size = sizeof(triangle_indices),
|
||||
.uv = triangle_vertices.data(),
|
||||
.uv_size = sizeof(triangle_vertices),
|
||||
.xyz = triangle_vertices.data(),
|
||||
.xyz_size = sizeof(triangle_vertices),
|
||||
};
|
||||
const Shape TRIANGLE = {
|
||||
triangle_indices,
|
||||
triangle_vertices,
|
||||
triangle_vertices,
|
||||
};
|
||||
|
||||
const Shape SQUARE = {
|
||||
.indices = square_indices.data(),
|
||||
.indices_size = square_indices.size(),
|
||||
.uv = square_uv.data(),
|
||||
.uv_size = square_uv.size(),
|
||||
.xyz = square_xyz.data(),
|
||||
.xyz_size = square_xyz.size(),
|
||||
};
|
||||
const Shape SQUARE = {
|
||||
square_indices,
|
||||
square_uv,
|
||||
square_xyz,
|
||||
};
|
||||
|
||||
const Shape CUBE = {
|
||||
.indices = cube_indices.data(),
|
||||
.indices_size = cube_indices.size(),
|
||||
.uv = triangle_vertices.data(),
|
||||
.uv_size = triangle_vertices.size(),
|
||||
.xyz = triangle_vertices.data(),
|
||||
.xyz_size = triangle_vertices.size(),
|
||||
};
|
||||
}
|
||||
const Shape CUBE = {
|
||||
cube_indices,
|
||||
triangle_vertices,
|
||||
triangle_vertices,
|
||||
};
|
||||
@@ -1,20 +1,16 @@
|
||||
#ifndef LEDDA_GEOMETRY_H
|
||||
#define LEDDA_GEOMETRY_H
|
||||
|
||||
#include <cstddef>
|
||||
#include "../common.h"
|
||||
|
||||
namespace LeddaGeometry {
|
||||
struct Shape {
|
||||
unsigned int* indices;
|
||||
size_t indices_size;
|
||||
float* uv;
|
||||
size_t uv_size;
|
||||
float* xyz;
|
||||
size_t xyz_size;
|
||||
};
|
||||
extern const Shape TRIANGLE;
|
||||
extern const Shape SQUARE;
|
||||
extern const Shape CUBE;
|
||||
}
|
||||
typedef struct {
|
||||
UInt32List indices;
|
||||
FloatList uv;
|
||||
FloatList xyz;
|
||||
} Shape;
|
||||
|
||||
extern const Shape TRIANGLE;
|
||||
extern const Shape SQUARE;
|
||||
extern const Shape CUBE;
|
||||
|
||||
#endif
|
||||
|
||||
5
src/gfx/gfx.c
Normal file
5
src/gfx/gfx.c
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "Mesh.c"
|
||||
#include "Shader.c"
|
||||
#include "Texture.c"
|
||||
#include "geometry.c"
|
||||
#include "Color.c"
|
||||
10
src/gfx/gfx.h
Normal file
10
src/gfx/gfx.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef GFX_H
|
||||
#define GFX_H
|
||||
|
||||
#include "Mesh.h"
|
||||
#include "Shader.h"
|
||||
#include "Texture.h"
|
||||
#include "geometry.h"
|
||||
#include "Color.h"
|
||||
|
||||
#endif
|
||||
1
src/lib/djstdlib
Submodule
1
src/lib/djstdlib
Submodule
Submodule src/lib/djstdlib added at 49ce94a6ab
@@ -86,7 +86,7 @@ GLAPI int gladLoadGL(void);
|
||||
|
||||
GLAPI int gladLoadGLLoader(GLADloadproc);
|
||||
|
||||
#include <KHR/khrplatform.h>
|
||||
#include "./khrplatform.h"
|
||||
typedef unsigned int GLenum;
|
||||
typedef unsigned char GLboolean;
|
||||
typedef unsigned int GLbitfield;
|
||||
5079
src/lib/loaders/stb_truetype.h
Normal file
5079
src/lib/loaders/stb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -117,7 +117,7 @@ typedef void (*file_reader_callback)(void *ctx, const char *filename, int is_mtl
|
||||
* @param[in] flags combination of TINYOBJ_FLAG_***
|
||||
*
|
||||
* Returns TINYOBJ_SUCCESS if things goes well.
|
||||
* Returns TINYOBJ_ERR_*** when there is an error.
|
||||
* Returns TINYOBJ_ERROR_*** when there is an error.
|
||||
*/
|
||||
extern int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
|
||||
size_t *num_shapes, tinyobj_material_t **materials,
|
||||
@@ -134,7 +134,7 @@ extern int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes,
|
||||
* @param[in[ ctx Context pointer passed to the file_reader callack.
|
||||
|
||||
* Returns TINYOBJ_SUCCESS if things goes well.
|
||||
* Returns TINYOBJ_ERR_*** when there is an error.
|
||||
* Returns TINYOBJ_ERROR_*** when there is an error.
|
||||
*/
|
||||
extern int tinyobj_parse_mtl_file(tinyobj_material_t **materials_out,
|
||||
size_t *num_materials_out,
|
||||
@@ -150,6 +150,7 @@ extern void tinyobj_materials_free(tinyobj_material_t *materials,
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* TINOBJ_LOADER_C_H_ */
|
||||
|
||||
#ifdef TINYOBJ_LOADER_C_IMPLEMENTATION
|
||||
#include <stdio.h>
|
||||
@@ -701,16 +702,13 @@ static hash_table_entry_t* hash_table_find(unsigned long hash, hash_table_t* has
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hash_table_maybe_grow(size_t new_n, hash_table_t* hash_table)
|
||||
static void hash_table_grow(hash_table_t* hash_table)
|
||||
{
|
||||
size_t new_capacity;
|
||||
hash_table_t new_hash_table;
|
||||
size_t i;
|
||||
|
||||
if (new_n <= hash_table->capacity) {
|
||||
return;
|
||||
}
|
||||
new_capacity = 2 * ((2 * hash_table->capacity) > new_n ? hash_table->capacity : new_n);
|
||||
new_capacity = 2 * hash_table->capacity;
|
||||
/* Create a new hash table. We're not calling create_hash_table because we want to realloc the hash array */
|
||||
new_hash_table.hashes = hash_table->hashes = (unsigned long*) TINYOBJ_REALLOC_SIZED(
|
||||
(void*) hash_table->hashes, sizeof(unsigned long) * hash_table->capacity, sizeof(unsigned long) * new_capacity);
|
||||
@@ -721,8 +719,10 @@ static void hash_table_maybe_grow(size_t new_n, hash_table_t* hash_table)
|
||||
/* Rehash */
|
||||
for (i = 0; i < hash_table->capacity; i++)
|
||||
{
|
||||
hash_table_entry_t* entry = hash_table_find(hash_table->hashes[i], hash_table);
|
||||
hash_table_insert_value(hash_table->hashes[i], entry->value, &new_hash_table);
|
||||
hash_table_entry_t* entry = &hash_table->entries[i];
|
||||
if (entry->filled) {
|
||||
hash_table_insert_value(entry->hash, entry->value, &new_hash_table);
|
||||
}
|
||||
}
|
||||
|
||||
TINYOBJ_FREE(hash_table->entries);
|
||||
@@ -749,11 +749,9 @@ static void hash_table_set(const char* name, size_t val, hash_table_t* hash_tabl
|
||||
/* Expand if necessary
|
||||
* Grow until the element has been added
|
||||
*/
|
||||
do
|
||||
{
|
||||
hash_table_maybe_grow(hash_table->n + 1, hash_table);
|
||||
while (hash_table_insert(hash, (long)val, hash_table) != HASH_TABLE_SUCCESS) {
|
||||
hash_table_grow(hash_table);
|
||||
}
|
||||
while (hash_table_insert(hash, (long)val, hash_table) != HASH_TABLE_SUCCESS);
|
||||
}
|
||||
|
||||
static long hash_table_get(const char* name, hash_table_t* hash_table)
|
||||
@@ -810,7 +808,7 @@ static int get_line_infos(const char *buf, size_t buf_len, LineInfo **line_infos
|
||||
* ending character so add an extra line if there
|
||||
* are more characters after the last line ending
|
||||
* that was found. */
|
||||
if (end_idx - last_line_ending > 0) {
|
||||
if (end_idx - last_line_ending > 1) {
|
||||
(*num_lines)++;
|
||||
}
|
||||
|
||||
@@ -827,7 +825,7 @@ static int get_line_infos(const char *buf, size_t buf_len, LineInfo **line_infos
|
||||
line_no++;
|
||||
}
|
||||
}
|
||||
if (end_idx - last_line_ending > 0) {
|
||||
if (end_idx - last_line_ending > 1) {
|
||||
(*line_infos)[line_no].pos = prev_pos;
|
||||
(*line_infos)[line_no].len = end_idx - 1 - last_line_ending;
|
||||
}
|
||||
@@ -1735,5 +1733,3 @@ void tinyobj_materials_free(tinyobj_material_t *materials,
|
||||
TINYOBJ_FREE(materials);
|
||||
}
|
||||
#endif /* TINYOBJ_LOADER_C_IMPLEMENTATION */
|
||||
|
||||
#endif /* TINOBJ_LOADER_C_H_ */
|
||||
2672
src/lib/raymath.h
Normal file
2672
src/lib/raymath.h
Normal file
File diff suppressed because it is too large
Load Diff
708
src/main.c
Normal file
708
src/main.c
Normal file
@@ -0,0 +1,708 @@
|
||||
// Library initialisation
|
||||
#define RAYMATH_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#define TINYOBJ_LOADER_C_IMPLEMENTATION
|
||||
|
||||
// Project
|
||||
#include "lib/djstdlib/core.c"
|
||||
#include "gfx/gfx.c"
|
||||
#include "world/world.c"
|
||||
#include "ui.c"
|
||||
#include "render.c"
|
||||
#include "SomaSolve.c"
|
||||
#include "VoxelSpace.c"
|
||||
#include "./tests.c"
|
||||
#include "debug.c"
|
||||
|
||||
// Graphics bindings and libs
|
||||
#include "lib/raymath.h"
|
||||
#include "lib/glad/glad.c"
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
|
||||
typedef struct Frame Frame;
|
||||
struct Frame {
|
||||
uint32 width;
|
||||
uint32 height;
|
||||
int32 x;
|
||||
int32 y;
|
||||
Camera cam;
|
||||
};
|
||||
|
||||
typedef enum SolveTaskStatus SolveTaskStatus;
|
||||
enum SolveTaskStatus {
|
||||
SolveTaskStatus_Ready,
|
||||
SolveTaskStatus_Solving,
|
||||
SolveTaskStatus_Complete,
|
||||
SolveTaskStatus_Error,
|
||||
};
|
||||
typedef struct SolveTaskCtx SolveTaskCtx;
|
||||
struct SolveTaskCtx {
|
||||
Arena *arena;
|
||||
SomaSolutionList solutions;
|
||||
VoxelSpaceReprList input;
|
||||
IntList dims;
|
||||
SolveTaskStatus taskStatus;
|
||||
};
|
||||
|
||||
typedef struct PolycubeInput PolycubeInput;
|
||||
struct PolycubeInput {
|
||||
VoxelSpace repr;
|
||||
RLVector4 color;
|
||||
};
|
||||
DefineList(PolycubeInput, PolycubeInput);
|
||||
|
||||
typedef struct SomaState SomaState;
|
||||
struct SomaState {
|
||||
bool isInitialState;
|
||||
bool wireframe;
|
||||
bool polycubeDirty;
|
||||
int32 displayedPolycube;
|
||||
size_t displayedSolution;
|
||||
bool displayingSolutions;
|
||||
RLVector3 rotAxisX;
|
||||
RLVector3 rotAxisY;
|
||||
UI_Rect *threedeePaneRect;
|
||||
real64 explosionOffset;
|
||||
};
|
||||
|
||||
typedef struct Soma Soma;
|
||||
struct Soma {
|
||||
Scene *scene;
|
||||
Renderer *renderer;
|
||||
struct {
|
||||
GLFWwindow *handle;
|
||||
int32 width;
|
||||
int32 height;
|
||||
struct {
|
||||
GLFWcursor *pointer;
|
||||
GLFWcursor *arrow;
|
||||
} cursors;
|
||||
} window;
|
||||
|
||||
SomaState prevState;
|
||||
SomaState state;
|
||||
|
||||
UI_Context *ui;
|
||||
PolycubeInputList polycubeInput;
|
||||
HandleList polycubes;
|
||||
SomaSolutionList solutions;
|
||||
SolveTaskCtx solveTaskCtx;
|
||||
int32 puzzleDims[3];
|
||||
int32 solutionNode;
|
||||
};
|
||||
|
||||
void *executeSolve(void *ctx) {
|
||||
SolveTaskCtx *solveTaskCtx = (SolveTaskCtx *)ctx;
|
||||
if (solveTaskCtx->dims.length == 3) {
|
||||
solveTaskCtx->solutions = solveSoma(solveTaskCtx->arena, solveTaskCtx->input, solveTaskCtx->dims.data);
|
||||
}
|
||||
solveTaskCtx->taskStatus = SolveTaskStatus_Complete;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void tryScheduleSolve(Soma *soma) {
|
||||
if (soma->solveTaskCtx.taskStatus != SolveTaskStatus_Ready) {
|
||||
return;
|
||||
}
|
||||
VoxelSpaceReprList mappedInputs = PushList(soma->solveTaskCtx.arena, VoxelSpaceReprList, soma->polycubeInput.length);
|
||||
for (EachEl(soma->polycubeInput, PolycubeInput, polycubeInput)) {
|
||||
ListAppend(mappedInputs, polycubeInput->repr.space);
|
||||
}
|
||||
soma->solveTaskCtx.input = mappedInputs;
|
||||
soma->solveTaskCtx.taskStatus = SolveTaskStatus_Solving;
|
||||
soma->solveTaskCtx.dims = PushFullList(soma->solveTaskCtx.arena, IntList, 3);
|
||||
soma->solveTaskCtx.dims.data[0] = soma->puzzleDims[0];
|
||||
soma->solveTaskCtx.dims.data[1] = soma->puzzleDims[1];
|
||||
soma->solveTaskCtx.dims.data[2] = soma->puzzleDims[2];
|
||||
os_createThread(&executeSolve, &soma->solveTaskCtx);
|
||||
}
|
||||
|
||||
RLVector3 centreFromPolycube(Scene *scene, uint32 p) {
|
||||
RLVector3 centre = (RLVector3){0,0,0};
|
||||
int32 childCount = 0;
|
||||
int32 nextChild = getSceneGraphNode(scene, p)->firstChild;
|
||||
while (nextChild) {
|
||||
SceneGraphNode *node = getSceneGraphNode(scene, nextChild);
|
||||
centre = Vector3Add(centre, node->translation);
|
||||
nextChild = node->nextSibling;
|
||||
childCount++;
|
||||
}
|
||||
centre = Vector3Scale(centre, 1.0f/childCount);
|
||||
return centre;
|
||||
}
|
||||
|
||||
void framebufferSizeCallback(GLFWwindow *window, int width, int height) {
|
||||
Soma *soma = (Soma *)glfwGetWindowUserPointer(window);
|
||||
soma->renderer->width = width;
|
||||
soma->renderer->height = height;
|
||||
}
|
||||
|
||||
GLFWwindow *initWindowAndGL(uint32 windowWidth, uint32 windowHeight) {
|
||||
if (!glfwInit()) {
|
||||
return NULL;
|
||||
}
|
||||
#ifdef GLFW_WAYLAND_APP_ID
|
||||
glfwWindowHintString(GLFW_WAYLAND_APP_ID, "Somaesque");
|
||||
#endif
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_SAMPLES, 4);
|
||||
GLFWwindow *window = glfwCreateWindow(
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
#ifdef DJSTDLIB_DEBUG
|
||||
"Somaesque (djstdlib_debug)",
|
||||
#else
|
||||
"Somaesque",
|
||||
#endif
|
||||
NULL,
|
||||
NULL);
|
||||
if (window == NULL) {
|
||||
print("Failed to create GLFW window\n");
|
||||
glfwTerminate();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent(window);
|
||||
|
||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
||||
print("Failed to initilaize GLAD\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
glViewport(0, 0, windowWidth, windowHeight);
|
||||
glfwSwapInterval(1);
|
||||
glfwSetInputMode(window, GLFW_CURSOR | GLFW_RAW_MOUSE_MOTION, GLFW_CURSOR_NORMAL);
|
||||
glfwSetWindowSize(window, windowWidth, windowHeight);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
Mesh cubeMesh = {0};
|
||||
Texture wallTex = {0};
|
||||
|
||||
Shader solidColorShader;
|
||||
Shader phongShader;
|
||||
|
||||
static void advanceDisplayReverse(Soma *soma) {
|
||||
if (soma->state.displayingSolutions) {
|
||||
if (soma->state.displayedSolution == 0) {
|
||||
soma->state.displayedSolution = soma->solutions.length - 1;
|
||||
} else {
|
||||
soma->state.displayedSolution -= 1;
|
||||
}
|
||||
} else {
|
||||
if (soma->state.displayedPolycube == 0) {
|
||||
soma->state.displayedPolycube = soma->polycubeInput.length - 1;
|
||||
} else {
|
||||
soma->state.displayedPolycube -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void advanceDisplay(Soma *soma) {
|
||||
if (soma->state.displayingSolutions) {
|
||||
soma->state.explosionOffset = 0;
|
||||
soma->state.displayedSolution += 1;
|
||||
soma->state.displayedSolution %= soma->solutions.length;
|
||||
} else {
|
||||
soma->state.displayedPolycube += 1;
|
||||
soma->state.displayedPolycube %= soma->polycubes.length;
|
||||
}
|
||||
}
|
||||
|
||||
static void onMouseScroll(GLFWwindow *window, real64 deltaX, real64 deltaY) {
|
||||
Soma *soma = (Soma *)glfwGetWindowUserPointer(window);
|
||||
soma->ui->input->mouse.scroll.dX += deltaX;
|
||||
soma->ui->input->mouse.scroll.dY += deltaY;
|
||||
}
|
||||
|
||||
void processInput(Soma *soma) {
|
||||
Input *input = soma->ui->input;
|
||||
Input *prevInput = soma->ui->prevInput;
|
||||
|
||||
if (input->keyboard.escape) {
|
||||
glfwSetWindowShouldClose(soma->window.handle, true);
|
||||
}
|
||||
|
||||
if (input->keyboard.w && !prevInput->keyboard.w) {
|
||||
glPolygonMode(GL_FRONT_AND_BACK, !soma->state.wireframe ? GL_LINE : GL_FILL);
|
||||
soma->state.wireframe = !soma->state.wireframe;
|
||||
}
|
||||
|
||||
SceneGraphNode *node = getSceneGraphNode(soma->scene, soma->renderer->light);
|
||||
int shiftMultiplier = input->keyboard.lshift ? -1 : 1;
|
||||
node->translation.x += 1.0 * input->keyboard.x * shiftMultiplier;
|
||||
node->translation.y += 1.0 * input->keyboard.y * shiftMultiplier;
|
||||
node->translation.z += 1.0 * input->keyboard.z * shiftMultiplier;
|
||||
|
||||
if (input->keyboard.space && !prevInput->keyboard.space) {
|
||||
advanceDisplay(soma);
|
||||
}
|
||||
|
||||
if (input->keyboard.enter && !prevInput->keyboard.enter) {
|
||||
if (soma->state.displayingSolutions) {
|
||||
soma->state.displayingSolutions = false;
|
||||
soma->state.displayedSolution = -1;
|
||||
} else {
|
||||
tryScheduleSolve(soma);
|
||||
}
|
||||
}
|
||||
|
||||
if (input->mouse.btnLeft && soma->ui->hotNode == 0) {
|
||||
uint32 currentObject = soma->state.displayingSolutions
|
||||
? soma->solutionNode
|
||||
: soma->polycubes.data[soma->state.displayedPolycube];
|
||||
SceneGraphNode *objectGraphNode = getSceneGraphNode(soma->scene, currentObject);
|
||||
|
||||
real64 deltaX = (input->mouse.x - prevInput->mouse.x) * 0.005;
|
||||
if (deltaX > 0.00000001 || deltaX < -0.00000001) {
|
||||
objectGraphNode->rotation = QuaternionMultiply(objectGraphNode->rotation, QuaternionFromAxisAngle(soma->state.rotAxisY, -(real32)deltaX));
|
||||
}
|
||||
|
||||
real64 deltaY = (input->mouse.y - prevInput->mouse.y) * 0.005;
|
||||
if (deltaY > 0.00000001 || deltaY < -0.00000001) {
|
||||
objectGraphNode->rotation = QuaternionMultiply(QuaternionFromAxisAngle(soma->state.rotAxisX, -(real32)deltaY), objectGraphNode->rotation);
|
||||
}
|
||||
}
|
||||
|
||||
if (soma->state.displayingSolutions) {
|
||||
real64 scrollDelta = soma->ui->prevInput->mouse.scroll.dY;
|
||||
if (scrollDelta > 0.001 || scrollDelta < -0.001) {
|
||||
soma->state.explosionOffset += scrollDelta;
|
||||
if (soma->state.explosionOffset > 0) {
|
||||
scrollDelta -= soma->state.explosionOffset;
|
||||
soma->state.explosionOffset = 0;
|
||||
}
|
||||
SceneGraphNode *rootNode = getSceneGraphNode(soma->scene, soma->solutionNode);
|
||||
int32 nextChildHandle = rootNode->firstChild;
|
||||
while (nextChildHandle) {
|
||||
SceneGraphNode *nextChild = getSceneGraphNode(soma->scene, nextChildHandle);
|
||||
RLVector3 centre = {0};
|
||||
int32 subsegmentHandle = nextChild->firstChild;
|
||||
int32 subsegmentCount = 0;
|
||||
while (subsegmentHandle) {
|
||||
subsegmentCount++;
|
||||
SceneGraphNode *subSegment = getSceneGraphNode(soma->scene, subsegmentHandle);
|
||||
centre = Vector3Add(centre, subSegment->translation);
|
||||
subsegmentHandle = subSegment->nextSibling;
|
||||
}
|
||||
RLVector3 flyDirection = Vector3Normalize(Vector3Scale(Vector3Transform(centre, nextChild->local), -1.f/subsegmentCount));
|
||||
nextChild->translation = Vector3Add(nextChild->translation, Vector3Scale(flyDirection, scrollDelta * 0.1));
|
||||
nextChildHandle = nextChild->nextSibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 createPolycubeFromRepr(Scene *s, VoxelSpace *repr, RLVector4 color) {
|
||||
uint32 mainGraphNode = createSceneGraphNode(s);
|
||||
for (int x = 0; x < repr->dim_x; x++) {
|
||||
for (int y = 0; y < repr->dim_y; y++) {
|
||||
for (int z = 0; z < repr->dim_z; z++) {
|
||||
if (filledAt(repr, x, y, z)) {
|
||||
uint32 segmentEntityHandle = createEntity(s);
|
||||
Entity *polycubeSegment = getEntity(s, segmentEntityHandle);
|
||||
polycubeSegment->color = color;
|
||||
polycubeSegment->mesh = &cubeMesh;
|
||||
polycubeSegment->tex = &wallTex;
|
||||
SceneGraphNode *graphNode = getSceneGraphNodeForEntity(s, segmentEntityHandle);
|
||||
graphNode->translation = (RLVector3){
|
||||
-((repr->dim_z - 1)/2.0f) + z,
|
||||
-((repr->dim_x - 1)/2.0f) + x,
|
||||
((repr->dim_y - 1)/2.0f) - y
|
||||
};
|
||||
sceneNodeAddNode(s, mainGraphNode, polycubeSegment->graphNodeHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SceneGraphNode *graphNode = getSceneGraphNode(s, mainGraphNode);
|
||||
graphNode->rotation = QuaternionMultiply(graphNode->rotation, QuaternionFromAxisAngle((RLVector3){1, 0, 0}, PI / 4));
|
||||
graphNode->rotation = QuaternionMultiply(graphNode->rotation, QuaternionFromAxisAngle((RLVector3){0, 1, 0}, -PI / 4));
|
||||
return mainGraphNode;
|
||||
}
|
||||
|
||||
static void ui_Interaction(UI_Context *ui, Soma *soma) {
|
||||
real32 boxSize = 30;
|
||||
real32 padding = 20;
|
||||
real32 childGap = 5;
|
||||
|
||||
UI(.flags=UI_Flag_Center | UI_Flag_HeightGrow) {
|
||||
UI(.childGap=padding, .flags=UI_Flag_Vertical | UI_Flag_Center) {
|
||||
if (soma->state.displayingSolutions && soma->solutions.length > 0) {
|
||||
SomaSolution *currentSolution = &soma->solutions.data[soma->state.displayedSolution];
|
||||
for (int x = 0; x < soma->puzzleDims[0]; x++) UI(.childGap=childGap, .flags=UI_Flag_Vertical) {
|
||||
for (int y = 0; y < soma->puzzleDims[1]; y++) UI(.childGap=childGap) {
|
||||
for (int z = 0; z < soma->puzzleDims[2]; z++) {
|
||||
for (EachIn(*currentSolution, i)) {
|
||||
uint64 spaceRepr = currentSolution->data[i];
|
||||
bool cellActive = filledAt(&(VoxelSpace){
|
||||
.space = spaceRepr,
|
||||
.dim_x = soma->puzzleDims[0],
|
||||
.dim_y = soma->puzzleDims[1],
|
||||
.dim_z = soma->puzzleDims[2],
|
||||
}, x, y, z);
|
||||
if (cellActive) UI(
|
||||
.width=boxSize,
|
||||
.height=boxSize,
|
||||
.color=soma->polycubeInput.data[i].color,
|
||||
.borderRadius=5,
|
||||
.borderThickness=0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PolycubeInput *currentPolycube = &soma->polycubeInput.data[soma->state.displayedPolycube];
|
||||
for (int x = 0; x < currentPolycube->repr.dim_x; x++) UI(.childGap=childGap, .flags=UI_Flag_Vertical) {
|
||||
for (int y = 0; y < currentPolycube->repr.dim_y; y++) UI(.childGap=childGap) {
|
||||
for (int z = 0; z < currentPolycube->repr.dim_z; z++) {
|
||||
bool cellActive = filledAt(¤tPolycube->repr, x, y, z);
|
||||
if (ui_CheckboxRect(ui, &cellActive, UI_RectAttr(
|
||||
.width = boxSize,
|
||||
.height = boxSize,
|
||||
.borderRadius = 2,
|
||||
.color = currentPolycube->color,
|
||||
))) {
|
||||
soma->state.polycubeDirty = true;
|
||||
spaceSet(¤tPolycube->repr, cellActive, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ui_Soma(UI_Context *ui, Soma *soma) {
|
||||
RLVector4 darkgrey = {0.2, 0.2, 0.2, 1};
|
||||
RLVector4 grey = {0.4, 0.4, 0.4, 1};
|
||||
RLVector4 lightgrey = {0.6, 0.6, 0.6, 1};
|
||||
UI_Padding btnPad = {.left=10, .right=10, .top=5, .bottom=5};
|
||||
RLVector4 blueHighlight = {0.2, 0.2, 0.7, 1};
|
||||
RLVector4 lightblueHighlight = {0.3, 0.3, 0.8, 1};
|
||||
|
||||
UI_Rect btnRect = UI_RectAttr(.padding=btnPad, .color=grey, .borderRadius=5);
|
||||
UI_Rect btnHoverRect = btnRect;
|
||||
btnHoverRect.color = lightgrey;
|
||||
|
||||
UI_Rect btnRectBlue = UI_RectAttr(.padding=btnPad, .color=blueHighlight, .borderRadius=5);
|
||||
UI_Rect btnHoverRectBlue = btnRectBlue;
|
||||
btnHoverRectBlue.color = lightblueHighlight;
|
||||
|
||||
UI(.flags=UI_Flag_HeightGrow | UI_Flag_WidthGrow) {
|
||||
UI_SetTxtAttr(.lineHeight=20, .fontSize=20);
|
||||
UI(.padding={.left=20, .right=20}, .color=darkgrey, .flags=UI_Flag_HeightGrow | UI_Flag_Vertical) {
|
||||
UI(.padding=UI_PadUniform(10)) UI_Txt(s("Somaesque"), .fontSize=26);
|
||||
ui_Interaction(ui, soma);
|
||||
}
|
||||
UI(.flags=UI_Flag_Vertical | UI_Flag_HeightGrow | UI_Flag_WidthGrow) {
|
||||
UI(.padding=UI_PadUniform(10), .color=darkgrey, .flags=UI_Flag_WidthGrow) {
|
||||
UI(.childGap=10) {
|
||||
if (ui_ButtonWithHover(ui, btnRect, btnHoverRect, UI_TxtAttr(s("Previous")))) {
|
||||
advanceDisplayReverse(soma);
|
||||
}
|
||||
|
||||
if (ui_ButtonWithHover(ui, btnRect, btnHoverRect, UI_TxtAttr(s("Next")))) {
|
||||
advanceDisplay(soma);
|
||||
}
|
||||
}
|
||||
UI(.flags=UI_Flag_WidthGrow);
|
||||
UI(.childGap=10) {
|
||||
UI_Rect *rect = soma->state.displayingSolutions ? &btnRect : &btnRectBlue;
|
||||
UI_Rect *hoverRect = soma->state.displayingSolutions ? &btnHoverRect : &btnHoverRectBlue;
|
||||
if (ui_ButtonWithHover(ui, *rect, *hoverRect, UI_TxtAttr(s("Design")))) {
|
||||
soma->state.displayingSolutions = false;
|
||||
}
|
||||
rect = soma->state.displayingSolutions ? &btnRectBlue : &btnRect;
|
||||
hoverRect = soma->state.displayingSolutions ? &btnHoverRectBlue : &btnHoverRect;
|
||||
if (ui_ButtonWithHover(ui, *rect, *hoverRect, UI_TxtAttr(s("Solve")))) {
|
||||
if (!soma->state.displayingSolutions) {
|
||||
tryScheduleSolve(soma);
|
||||
} else {
|
||||
soma->state.displayingSolutions = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UI(.flags=UI_Flag_Vertical | UI_Flag_WidthGrow | UI_Flag_HeightGrow | UI_Flag_3DScene) {
|
||||
UI_SetTxtAttr(.fontSize=26, .alignment=UI_TxtAlign_Right);
|
||||
UI(.flags=UI_Flag_HeightGrow);
|
||||
UI(.padding=UI_PadUniform(10), .flags=UI_Flag_WidthGrow) {
|
||||
if (soma->solveTaskCtx.taskStatus == SolveTaskStatus_Solving) {
|
||||
UI_Txt(s("Solving..."));
|
||||
} else {
|
||||
bool soln = soma->state.displayingSolutions;
|
||||
UI_Txt(
|
||||
strPrintf(ui->arena, "%S #%d (%d total)",
|
||||
soln ? s("Solution") : s("Polycube"),
|
||||
(soln ? soma->state.displayedSolution : soma->state.displayedPolycube) + 1,
|
||||
soln ? soma->solutions.length : soma->polycubeInput.length),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void updatePolycubeDisplay(Soma *soma) {
|
||||
Scene *s = soma->scene;
|
||||
|
||||
if (soma->state.displayingSolutions && soma->state.displayedSolution != soma->prevState.displayedSolution) {
|
||||
removeSceneGraphNode(s, soma->solutionNode);
|
||||
if (soma->state.displayedSolution >= 0 && soma->state.displayedSolution < soma->solutions.length) {
|
||||
SomaSolution soln = soma->solutions.data[soma->state.displayedSolution];
|
||||
soma->solutionNode = createSceneGraphNode(s);
|
||||
for (EachIn(soln, i)) {
|
||||
uint32 polycubeGraphNodeHandle = createPolycubeFromRepr(
|
||||
s,
|
||||
&(VoxelSpace){ soln.data[i], soma->puzzleDims[0], soma->puzzleDims[1], soma->puzzleDims[2] },
|
||||
colorFromIndex(i)
|
||||
);
|
||||
sceneNodeAddNode(s, soma->solutionNode, polycubeGraphNodeHandle);
|
||||
}
|
||||
sceneNodeAddNode(s, s->sceneRoot, soma->solutionNode);
|
||||
}
|
||||
}
|
||||
|
||||
if (soma->state.displayingSolutions) {
|
||||
show(s, soma->solutionNode);
|
||||
if (soma->prevState.displayedPolycube >= 0 && soma->prevState.displayedPolycube < soma->polycubes.length) {
|
||||
hide(s, soma->polycubes.data[soma->prevState.displayedPolycube]);
|
||||
}
|
||||
} else {
|
||||
hide(s, soma->solutionNode);
|
||||
if (soma->state.displayedPolycube >= 0 && soma->state.displayedPolycube < soma->polycubes.length) {
|
||||
show(s, soma->polycubes.data[soma->state.displayedPolycube]);
|
||||
}
|
||||
if (soma->state.displayedPolycube != soma->prevState.displayedPolycube) {
|
||||
if (soma->prevState.displayedPolycube >= 0 && soma->prevState.displayedPolycube < soma->polycubes.length) {
|
||||
hide(s, soma->polycubes.data[soma->prevState.displayedPolycube]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (soma->state.polycubeDirty) {
|
||||
if (soma->state.displayedPolycube >= 0 && soma->state.displayedPolycube < soma->polycubes.length) {
|
||||
uint32 oldHandle = soma->polycubes.data[soma->state.displayedPolycube];
|
||||
Quaternion rot = getSceneGraphNode(s, oldHandle)->rotation;
|
||||
removeSceneGraphNode(s, oldHandle);
|
||||
|
||||
PolycubeInput *newInput = &soma->polycubeInput.data[soma->state.displayedPolycube];
|
||||
VoxelSpace newInputCulled = newInput->repr;
|
||||
cullEmptySpace(&newInputCulled);
|
||||
|
||||
uint32 newHandle = createPolycubeFromRepr(s, &newInputCulled, newInput->color);
|
||||
getSceneGraphNode(s, newHandle)->rotation = rot;
|
||||
|
||||
soma->polycubes.data[soma->state.displayedPolycube] = newHandle;
|
||||
sceneNodeAddNode(s, s->sceneRoot, newHandle);
|
||||
|
||||
soma->state.polycubeDirty = false;
|
||||
show(s, newHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void updateWindow(Soma *soma) {
|
||||
glfwGetWindowSize(soma->window.handle, &soma->window.width, &soma->window.height);
|
||||
soma->renderer->width = soma->window.width;
|
||||
soma->renderer->height = soma->window.height;
|
||||
}
|
||||
|
||||
const int32 MAX_POLYCUBE_INPUT = 64;
|
||||
const int32 TARGET_FPS = 144;
|
||||
|
||||
void APIENTRY glDebugCallback(GLenum source, GLenum type, GLuint id,
|
||||
GLenum severity, GLsizei length,
|
||||
const GLchar* message, const void* userParam)
|
||||
{
|
||||
fprintf(stderr, "GL: %s\n", message);
|
||||
}
|
||||
|
||||
int mainGfx() {
|
||||
Arena *arena = arenaAlloc(Megabytes(128));
|
||||
Arena *solutionsArena = arenaAlloc(Megabytes(128));
|
||||
|
||||
int32 winWidth = 800;
|
||||
int32 winHeight = 600;
|
||||
GLFWwindow *windowHandle = initWindowAndGL(winWidth, winHeight);
|
||||
if (!windowHandle) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(glDebugCallback, NULL);
|
||||
|
||||
Scene mainScene = createScene(arena);
|
||||
Camera cam = createCamera(winWidth, winHeight);
|
||||
Renderer renderer = createRenderer(arena, &mainScene, &cam, createSceneGraphNode(&mainScene));
|
||||
|
||||
solidColorShader = createShader(
|
||||
s("./assets/shaders/2d-solid.vertex.glsl"),
|
||||
s("./assets/shaders/2d-solid.fragment.glsl"));
|
||||
|
||||
phongShader = createShader(
|
||||
s("./assets/shaders/phong-solid.vertex.glsl"),
|
||||
s("./assets/shaders/phong-solid.fragment.glsl"));
|
||||
|
||||
cubeMesh = createMesh("./assets/models/cube.obj");
|
||||
|
||||
renderer.solidShader = &solidColorShader;
|
||||
renderer.phongShader = &phongShader;
|
||||
renderer.cubeMesh = &cubeMesh;
|
||||
|
||||
Arena *uiArena = arenaAlloc(Megabytes(64));
|
||||
UI_Context ui = ui_initContext(uiArena, &renderer);
|
||||
|
||||
Soma soma = {
|
||||
.window = {
|
||||
.width = winWidth,
|
||||
.height = winHeight,
|
||||
.handle = windowHandle,
|
||||
.cursors = {
|
||||
.pointer = glfwCreateStandardCursor(GLFW_HAND_CURSOR),
|
||||
.arrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR),
|
||||
},
|
||||
},
|
||||
.scene = &mainScene,
|
||||
.ui = &ui,
|
||||
.renderer = &renderer,
|
||||
.polycubeInput = PushListZero(arena, PolycubeInputList, MAX_POLYCUBE_INPUT),
|
||||
.polycubes = PushListZero(arena, HandleList, MAX_POLYCUBE_INPUT),
|
||||
.puzzleDims = {3, 3, 3},
|
||||
.solveTaskCtx = (SolveTaskCtx){
|
||||
.arena = arenaAlloc(Megabytes(128)),
|
||||
.solutions = EmptyList(),
|
||||
.input = EmptyList(),
|
||||
.dims = EmptyList(),
|
||||
.taskStatus = SolveTaskStatus_Ready,
|
||||
},
|
||||
.state = {
|
||||
.threedeePaneRect=NULL,
|
||||
.displayedPolycube = 0,
|
||||
.displayingSolutions = false,
|
||||
.displayedSolution = -1,
|
||||
.isInitialState = true,
|
||||
},
|
||||
};
|
||||
|
||||
glfwSetWindowUserPointer(windowHandle, &soma);
|
||||
glfwSetFramebufferSizeCallback(windowHandle, framebufferSizeCallback);
|
||||
glfwSetScrollCallback(windowHandle, onMouseScroll);
|
||||
|
||||
VoxelSpaceReprList stdSoma = AsList(VoxelSpaceReprList, STD_SOMA);
|
||||
for (EachIn(stdSoma, i)) {
|
||||
VoxelSpace voxelSpace = {
|
||||
.space=stdSoma.data[i],
|
||||
.dim_x=soma.puzzleDims[0],
|
||||
.dim_y=soma.puzzleDims[1],
|
||||
.dim_z=soma.puzzleDims[2],
|
||||
};
|
||||
RLVector4 color = colorFromIndex(i);
|
||||
PolycubeInput input = (PolycubeInput){ .repr=voxelSpace, .color=color };
|
||||
ListAppend(soma.polycubeInput, input);
|
||||
cullEmptySpace(&voxelSpace);
|
||||
uint32 polycubeGraphNodeHandle = createPolycubeFromRepr(soma.scene, &voxelSpace, color);
|
||||
ListAppend(soma.polycubes, polycubeGraphNodeHandle);
|
||||
sceneNodeAddNode(renderer.scene, renderer.scene->sceneRoot, polycubeGraphNodeHandle);
|
||||
}
|
||||
|
||||
cam.pos = (RLVector3){0.0f, 0.0f, 8.0f};
|
||||
cameraLookAt(&cam, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
SceneGraphNode *referencePolycube = getSceneGraphNode(soma.scene, soma.polycubes.data[0]);
|
||||
Matrix worldInverse = MatrixInvert(referencePolycube->world);
|
||||
soma.state.rotAxisY = Vector3Normalize((RLVector3){worldInverse.m4, worldInverse.m5, worldInverse.m6});
|
||||
RLVector3 eyes = Vector3Normalize(Vector3Subtract(cam.pos, referencePolycube->translation));
|
||||
soma.state.rotAxisX = Vector3Normalize(Vector3CrossProduct(eyes, soma.state.rotAxisY));
|
||||
|
||||
getSceneGraphNode(&mainScene, renderer.light)->translation = (RLVector3){4.0f, 6.0f, 24.0f};
|
||||
|
||||
Font kodeMono = createFont(arena, s("./assets/fonts/KodeMono.ttf"), 96.0f);
|
||||
renderer.activeFont = &kodeMono;
|
||||
|
||||
Shader textShader = createShader(s("./assets/shaders/text.vertex.glsl"), s("./assets/shaders/text.fragment.glsl"));
|
||||
renderer.textShader = &textShader;
|
||||
|
||||
// Render loop
|
||||
real64 lastFrame = glfwGetTime();
|
||||
real64 timeDelta = 1.0f / TARGET_FPS;
|
||||
real64 frameStart = lastFrame;
|
||||
|
||||
Input lastInput = {0};
|
||||
Input currInput = {0};
|
||||
while (!glfwWindowShouldClose(soma.window.handle)) {
|
||||
glfwPollEvents();
|
||||
|
||||
lastInput = currInput;
|
||||
currInput = getCurrentInput(soma.window.handle);
|
||||
|
||||
ui.prevInput = &lastInput;
|
||||
ui.input = &currInput;
|
||||
|
||||
updateWindow(&soma);
|
||||
|
||||
processInput(&soma);
|
||||
|
||||
if (soma.solveTaskCtx.taskStatus == SolveTaskStatus_Complete) {
|
||||
soma.solutions = PushList(solutionsArena, SomaSolutionList, soma.solveTaskCtx.solutions.length);
|
||||
for (EachIn(soma.solveTaskCtx.solutions, i)) {
|
||||
ListAppend(soma.solutions, PushListCopy(solutionsArena, SomaSolution, soma.solveTaskCtx.solutions.data[i]));
|
||||
}
|
||||
soma.solveTaskCtx.taskStatus = SolveTaskStatus_Ready;
|
||||
soma.state.displayingSolutions = true;
|
||||
soma.state.displayedSolution = 0;
|
||||
arenaFreeFrom(soma.solveTaskCtx.arena, 0);
|
||||
}
|
||||
|
||||
Render(&renderer) {
|
||||
UI_Pass(&ui) {
|
||||
ui_Soma(&ui, &soma);
|
||||
}
|
||||
|
||||
if (ui.cursorIsPointer) {
|
||||
glfwSetCursor(soma.window.handle, soma.window.cursors.pointer);
|
||||
} else {
|
||||
glfwSetCursor(soma.window.handle, soma.window.cursors.arrow);
|
||||
}
|
||||
}
|
||||
|
||||
updatePolycubeDisplay(&soma);
|
||||
|
||||
recalcScene(soma.scene);
|
||||
|
||||
glfwSwapBuffers(soma.window.handle);
|
||||
|
||||
real64 frameEnd = glfwGetTime();
|
||||
real64 frameTime = frameEnd - frameStart;
|
||||
lastFrame = frameStart;
|
||||
|
||||
//println("FPS: %.7f", 1/(frameTime));
|
||||
|
||||
frameStart = glfwGetTime();
|
||||
|
||||
soma.prevState = soma.state;
|
||||
soma.state.isInitialState = false;
|
||||
}
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
initialiseDjStdCore();
|
||||
return mainGfx();
|
||||
}
|
||||
|
||||
410
src/main.cpp
410
src/main.cpp
@@ -1,410 +0,0 @@
|
||||
#include <bitset>
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "glad/glad.h"
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "loaders/stb_image.h"
|
||||
|
||||
#include "gfx/geometry.h"
|
||||
#include "gfx/Texture.h"
|
||||
#include "gfx/Mesh.h"
|
||||
#include "gfx/Shader.h"
|
||||
#include "gfx/Color.h"
|
||||
#include "VoxelSpace.h"
|
||||
#include "SomaSolve.h"
|
||||
|
||||
struct Entity;
|
||||
struct Polycube;
|
||||
struct SceneGraphNode;
|
||||
auto new_entity() -> int;
|
||||
auto get_entity(int id) -> Entity*;
|
||||
auto get_scene_graph_node(int id) -> SceneGraphNode*;
|
||||
auto new_graph_node() -> int;
|
||||
|
||||
auto print_mat(glm::mat4* matrix) -> void {
|
||||
auto mat = *matrix;
|
||||
std::cout << mat[0][0] << mat[0][1] << mat[0][2] << mat[0][3] << std::endl;
|
||||
std::cout << mat[1][0] << mat[1][1] << mat[1][2] << mat[1][3] << std::endl;
|
||||
std::cout << mat[2][0] << mat[2][1] << mat[2][2] << mat[2][3] << std::endl;
|
||||
std::cout << mat[3][0] << mat[3][1] << mat[3][2] << mat[3][3] << std::endl;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
struct Camera {
|
||||
glm::mat4 view;
|
||||
glm::mat4 proj;
|
||||
glm::vec3 pos;
|
||||
glm::vec3 up;
|
||||
glm::vec3 target;
|
||||
|
||||
auto init(float aspect_ratio = 800.0f / 600.0f) -> void {
|
||||
view = glm::mat4();
|
||||
proj = glm::perspective(glm::radians(45.0f), aspect_ratio, 0.1f, 100.0f);
|
||||
pos = glm::vec3(0.0f);
|
||||
up = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
auto look_at(float x, float y, float z) -> void {
|
||||
target = glm::vec3(x, y, z);
|
||||
view = glm::lookAt(pos, target, up);
|
||||
}
|
||||
|
||||
auto set_up(float up_x, float up_y, float up_z) -> void {
|
||||
up = glm::vec3(up_x, up_y, up_z);
|
||||
}
|
||||
};
|
||||
|
||||
struct GlobalAppState {
|
||||
int current_polycube;
|
||||
int last_polycube_visible;
|
||||
Shader* active_shader;
|
||||
std::vector<Polycube> polycubes;
|
||||
};
|
||||
GlobalAppState app_state;
|
||||
|
||||
struct WindowDims {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
};
|
||||
|
||||
struct Entity {
|
||||
Mesh* mesh;
|
||||
Texture* tex;
|
||||
bool visible;
|
||||
int scene_graph_node;
|
||||
};
|
||||
|
||||
struct SceneGraphNode {
|
||||
glm::mat4 local;
|
||||
glm::mat4 world;
|
||||
glm::vec3 translation;
|
||||
glm::quat rotation;
|
||||
glm::vec3 scale;
|
||||
std::vector<int> children;
|
||||
std::optional<int> entity;
|
||||
|
||||
auto reset() -> void {
|
||||
scale = glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
translation = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
rotation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
auto init() -> void {
|
||||
reset();
|
||||
local = glm::mat4(1.0f);
|
||||
world = local;
|
||||
}
|
||||
|
||||
auto update_local() -> void {
|
||||
local = glm::scale(
|
||||
glm::translate(
|
||||
glm::mat4(1.0f),
|
||||
translation
|
||||
) * glm::toMat4(rotation),
|
||||
scale
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct Polycube {
|
||||
int graph_node;
|
||||
glm::vec3 color;
|
||||
|
||||
auto show() -> void {
|
||||
auto node = get_scene_graph_node(graph_node);
|
||||
for (auto &child : node->children) {
|
||||
auto node = get_scene_graph_node(child);
|
||||
if (node->entity) {
|
||||
get_entity(*node->entity)->visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto hide() -> void {
|
||||
auto node = get_scene_graph_node(graph_node);
|
||||
for (auto &child : node->children) {
|
||||
auto node = get_scene_graph_node(child);
|
||||
if (node->entity) {
|
||||
get_entity(*node->entity)->visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto get_centre() -> glm::vec3 {
|
||||
auto centre = glm::vec3(0.0f);
|
||||
for (auto &child : get_scene_graph_node(graph_node)->children) {
|
||||
centre += get_scene_graph_node(child)->translation;
|
||||
}
|
||||
centre /= get_scene_graph_node(graph_node)->children.size();
|
||||
return centre;
|
||||
}
|
||||
};
|
||||
|
||||
struct Frame {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
int x;
|
||||
int y;
|
||||
Camera* cam;
|
||||
|
||||
auto init(Camera* camera) -> void {
|
||||
camera->init((float)width / (float)height);
|
||||
cam = camera;
|
||||
}
|
||||
};
|
||||
|
||||
auto framebuffer_size_callback(GLFWwindow* window, int width, int height) -> void {
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
auto init_window_and_gl(WindowDims* window_dims) -> GLFWwindow* {
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
auto window = glfwCreateWindow(window_dims->width, window_dims->height, "Somaesque", NULL, NULL);
|
||||
if (window == NULL) {
|
||||
std::cout << "Failed to create GLFW window" << std::endl;
|
||||
glfwTerminate();
|
||||
return nullptr;
|
||||
}
|
||||
glfwMakeContextCurrent(window);
|
||||
|
||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
||||
std::cout << "Failed to initilaize GLAD" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glViewport(0, 0, 800, 600);
|
||||
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
return window;
|
||||
}
|
||||
|
||||
auto gl_update_viewport(WindowDims* window_dims, Frame* frame) -> void {
|
||||
glViewport(frame->x, window_dims->height - frame->y - frame->height, frame->width, frame->height);
|
||||
}
|
||||
|
||||
auto cube_mesh = Mesh{};
|
||||
auto wall_tex = Texture{};
|
||||
auto entities = std::vector<Entity>();
|
||||
auto scene_graph_nodes = std::vector<SceneGraphNode>();
|
||||
|
||||
auto process_input(GLFWwindow *window) -> void {
|
||||
static auto wireframe = false;
|
||||
static auto last_frame_state_press_enter = false;
|
||||
static auto last_frame_state_press = false;
|
||||
|
||||
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
|
||||
glfwSetWindowShouldClose(window, true);
|
||||
}
|
||||
|
||||
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS && !last_frame_state_press) {
|
||||
glPolygonMode(GL_FRONT_AND_BACK, !wireframe ? GL_LINE : GL_FILL);
|
||||
wireframe = !wireframe;
|
||||
last_frame_state_press = true;
|
||||
} else if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_RELEASE) {
|
||||
last_frame_state_press = false;
|
||||
}
|
||||
|
||||
if (glfwGetKey(window, GLFW_KEY_ENTER) == GLFW_PRESS && !last_frame_state_press_enter) {
|
||||
if (app_state.current_polycube == 6) {
|
||||
app_state.current_polycube = 0;
|
||||
} else {
|
||||
app_state.current_polycube += 1;
|
||||
}
|
||||
last_frame_state_press_enter = true;
|
||||
} else if (glfwGetKey(window, GLFW_KEY_ENTER) == GLFW_RELEASE) {
|
||||
last_frame_state_press_enter = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
auto new_entity() -> int {
|
||||
entities.emplace_back();
|
||||
scene_graph_nodes.emplace_back();
|
||||
entities.back().scene_graph_node = scene_graph_nodes.size();
|
||||
scene_graph_nodes.back().entity = entities.size();
|
||||
return entities.size();
|
||||
}
|
||||
|
||||
auto get_entity(int id) -> Entity* {
|
||||
return &entities[id - 1];
|
||||
}
|
||||
|
||||
auto get_scene_graph_node(int id) -> SceneGraphNode* {
|
||||
return &scene_graph_nodes[id - 1];
|
||||
}
|
||||
|
||||
auto new_graph_node() -> int {
|
||||
scene_graph_nodes.emplace_back();
|
||||
return scene_graph_nodes.size();
|
||||
}
|
||||
|
||||
auto draw_entity(Entity* entity) -> void {
|
||||
auto modelUniformLoc = glGetUniformLocation(app_state.active_shader->prog_id, "model");
|
||||
glUniformMatrix4fv(modelUniformLoc, 1, GL_FALSE, glm::value_ptr(get_scene_graph_node(entity->scene_graph_node)->world));
|
||||
glBindTexture(GL_TEXTURE_2D, entity->tex->tex_id);
|
||||
glBindVertexArray(entity->mesh->vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, entity->mesh->num_indices);
|
||||
//glDrawElements(GL_TRIANGLES, entity->mesh->num_indices, GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
|
||||
auto create_polycube_from_repr(Voxel::Space* repr) -> Polycube {
|
||||
auto polycube_id = new_graph_node();
|
||||
get_scene_graph_node(polycube_id)->init();
|
||||
for (int x = 0; x < repr->dim_x; x++) {
|
||||
for (int y = 0; y < repr->dim_y; y++) {
|
||||
for (int z = 0; z < repr->dim_z; z++) {
|
||||
if (Voxel::filledAt(repr, x, y, z)) {
|
||||
auto polycube_segment = get_entity(new_entity());
|
||||
polycube_segment->mesh=&cube_mesh,
|
||||
polycube_segment->tex=&wall_tex;
|
||||
auto graph_node = get_scene_graph_node(polycube_segment->scene_graph_node);
|
||||
graph_node->init();
|
||||
graph_node->translation = glm::vec3(
|
||||
-((repr->dim_z - 1)/2.0f) + z,
|
||||
((repr->dim_x - 1)/2.0f) - x,
|
||||
-((repr->dim_y - 1)/2.0f) + y
|
||||
);
|
||||
graph_node->update_local();
|
||||
get_scene_graph_node(polycube_id)->children.push_back(polycube_segment->scene_graph_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
auto result = Polycube{
|
||||
.graph_node=polycube_id,
|
||||
.color=glm::vec3(1.0f),
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
auto recalculate_scene_graph(SceneGraphNode* top) -> void {
|
||||
if (top->children.size() == 0) {
|
||||
return;
|
||||
}
|
||||
for (auto &node_id : top->children) {
|
||||
auto graph_node = get_scene_graph_node(node_id);
|
||||
graph_node->update_local();
|
||||
graph_node->world = top->world * graph_node->local;
|
||||
recalculate_scene_graph(graph_node);
|
||||
}
|
||||
}
|
||||
|
||||
auto main_cmd() -> int {
|
||||
SomaSolve::interactive_cmd_line_solve_soma();
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto main_gfx() -> int {
|
||||
auto window_dims = WindowDims{ 800, 600 };
|
||||
auto window = init_window_and_gl(&window_dims);
|
||||
if (window == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
app_state = GlobalAppState{
|
||||
.current_polycube=0,
|
||||
.last_polycube_visible=6,
|
||||
.active_shader=nullptr,
|
||||
.polycubes={},
|
||||
};
|
||||
|
||||
auto phong_shader = Shader{};
|
||||
phong_shader.init("../assets/shaders/phong-solid.vertex.glsl", "../assets/shaders/phong-solid.fragment.glsl");
|
||||
app_state.active_shader = &phong_shader;
|
||||
|
||||
cube_mesh.init("../assets/models/c000000.obj");
|
||||
wall_tex.init("../assets/textures/brick-wall.jpg");
|
||||
|
||||
auto little_frame = Frame{ .width=80, .height=60, .x=20, .y=20 };
|
||||
auto big_frame = Frame{ .width=800, .height=600, .x=0, .y=0 };
|
||||
auto main_cam = Camera{};
|
||||
auto other_cam = Camera{};
|
||||
little_frame.init(&other_cam);
|
||||
big_frame.init(&main_cam);
|
||||
auto frames = std::vector{ &big_frame, &little_frame };
|
||||
|
||||
auto root_node = SceneGraphNode{};
|
||||
root_node.init();
|
||||
|
||||
for (int i = 0; i < SomaSolve::STD_SOMA.size(); i++) {
|
||||
auto voxel_space = Voxel::Space{ SomaSolve::STD_SOMA[i], 3, 3, 3 };
|
||||
Voxel::cullEmptySpace(&voxel_space);
|
||||
auto polycube = create_polycube_from_repr(&voxel_space);
|
||||
polycube.color = Color::color_from_index(i);
|
||||
app_state.polycubes.push_back(polycube);
|
||||
root_node.children.push_back(app_state.polycubes.back().graph_node);
|
||||
}
|
||||
|
||||
main_cam.pos = glm::vec3(4.0f, 4.0f, 4.0f);
|
||||
main_cam.look_at(0.0f, 0.0f, 0.0f);
|
||||
|
||||
auto light_pos = glm::vec3(6.0f);
|
||||
|
||||
glUseProgram(app_state.active_shader->prog_id);
|
||||
auto view_loc = glGetUniformLocation(app_state.active_shader->prog_id, "view");
|
||||
auto proj_loc = glGetUniformLocation(app_state.active_shader->prog_id, "projection");
|
||||
auto light_pos_loc = glGetUniformLocation(app_state.active_shader->prog_id, "light_pos");
|
||||
glUniform3fv(light_pos_loc, 1, glm::value_ptr(light_pos));
|
||||
glUniformMatrix4fv(proj_loc, 1, GL_FALSE, glm::value_ptr(main_cam.proj));
|
||||
glUniformMatrix4fv(view_loc, 1, GL_FALSE, glm::value_ptr(main_cam.view));
|
||||
|
||||
auto last_frame = glfwGetTime();
|
||||
auto time_delta = 1.0f/60.0f;
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
time_delta = glfwGetTime() - last_frame;
|
||||
process_input(window);
|
||||
|
||||
if (app_state.last_polycube_visible != app_state.current_polycube) {
|
||||
app_state.polycubes[app_state.last_polycube_visible].hide();
|
||||
app_state.polycubes[app_state.current_polycube].show();
|
||||
app_state.last_polycube_visible = app_state.current_polycube;
|
||||
}
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
|
||||
gl_update_viewport(&window_dims, &big_frame);
|
||||
auto current_polycube = &app_state.polycubes[app_state.current_polycube];
|
||||
get_scene_graph_node(current_polycube->graph_node)->rotation = glm::quat(glm::vec3(0, glfwGetTime() / 2, 0));
|
||||
|
||||
glBindVertexArray(cube_mesh.vao);
|
||||
//glBindTexture(GL_TEXTURE_2D, entity.tex->tex_id);
|
||||
recalculate_scene_graph(&root_node);
|
||||
auto model_uniform_loc = glGetUniformLocation(app_state.active_shader->prog_id, "model");
|
||||
auto solid_color_loc = glGetUniformLocation(app_state.active_shader->prog_id, "solid_color");
|
||||
glUniform3fv(solid_color_loc, 1, glm::value_ptr(current_polycube->color));
|
||||
for (auto &entity : entities) {
|
||||
if (entity.visible) {
|
||||
glUniformMatrix4fv(model_uniform_loc, 1, GL_FALSE, glm::value_ptr(get_scene_graph_node(entity.scene_graph_node)->world));
|
||||
glDrawArrays(GL_TRIANGLES, 0, entity.mesh->num_indices);
|
||||
//glDrawElements(GL_TRIANGLES, entity->mesh->num_indices, GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto main() -> int {
|
||||
return main_cmd();
|
||||
}
|
||||
|
||||
402
src/main.zig
402
src/main.zig
@@ -1,402 +0,0 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig");
|
||||
const zm = @import("zm");
|
||||
|
||||
const Mesh = @import("gfx/Mesh.zig").Mesh;
|
||||
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
fn print_mat(matrix: *const zm.Mat) void {
|
||||
std.debug.print("{}, {}, {}, {}\n", .{ matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3] });
|
||||
std.debug.print("{}, {}, {}, {}\n", .{ matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3] });
|
||||
std.debug.print("{}, {}, {}, {}\n", .{ matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3] });
|
||||
std.debug.print("{}, {}, {}, {}\n", .{ matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3] });
|
||||
}
|
||||
|
||||
const Camera = struct {
|
||||
view: zm.Mat = .{ zm.f32x4s(0.0), zm.f32x4s(0.0), zm.f32x4s(0.0), zm.f32x4s(0.0) },
|
||||
proj: zm.Mat,
|
||||
pos: zm.Vec = zm.f32x4s(0.0),
|
||||
up: zm.Vec = zm.f32x4s(0.0),
|
||||
target: zm.Vec,
|
||||
|
||||
pub fn init(self: Camera, aspect_ratio: f32) void {
|
||||
self.proj = zm.perspectiveFovRh(std.math.degreesToRadians(45.0), aspect_ratio, 0.1, 100.0);
|
||||
}
|
||||
|
||||
pub fn new(aspect_ratio: f32) Camera {
|
||||
const cam = Camera{};
|
||||
init(cam, aspect_ratio);
|
||||
return cam;
|
||||
}
|
||||
|
||||
pub fn look_at(self: Camera, x: f32, y: f32, z: f32) void {
|
||||
self.target = zm.f32x4(x, y, z, 0.0);
|
||||
self.view = zm.lookAtRh(self.pos, self.target, self.up);
|
||||
}
|
||||
|
||||
pub fn set_up(self: Camera, up_x: f32, up_y: f32, up_z: f32) void {
|
||||
self.up = zm.f32x4(up_x, up_y, up_z, 0.0);
|
||||
}
|
||||
};
|
||||
|
||||
const GlobalAppState = struct {
|
||||
current_polycube: i32,
|
||||
last_polycube_visible: i32,
|
||||
active_shader: ?*Shader,
|
||||
polycubes: ArrayList(Polycube),
|
||||
};
|
||||
|
||||
const app_state: GlobalAppState = .{};
|
||||
|
||||
const WindowDims = struct {
|
||||
width: u32,
|
||||
height: u32,
|
||||
};
|
||||
|
||||
const Entity = struct {
|
||||
mesh: *Mesh,
|
||||
tex: *Texture,
|
||||
visible: bool,
|
||||
scene_graph_node: i32,
|
||||
};
|
||||
|
||||
const SceneGraphNode = struct {
|
||||
local: zm.Mat,
|
||||
world: zm.Mat,
|
||||
translation: zm.Vec,
|
||||
rotation: zm.Quat,
|
||||
scale: zm.Vec,
|
||||
children: ArrayList(i32),
|
||||
entity: ?i32,
|
||||
|
||||
pub fn reset(self: SceneGraphNode) void {
|
||||
self.scale = zm.f32x4(1.0, 1.0, 1.0, 0.0);
|
||||
self.translation = zm.f32x4s(0.0);
|
||||
self.rotation = zm.f32x4s(0.0);
|
||||
}
|
||||
|
||||
pub fn init(self: SceneGraphNode) void {
|
||||
self.reset();
|
||||
self.local = zm.identity();
|
||||
self.world = self.local;
|
||||
}
|
||||
|
||||
pub fn update_local(self: SceneGraphNode) void {
|
||||
const scaling = zm.scaling(self.scale);
|
||||
const translation = zm.translation(self.translation);
|
||||
const rotation = zm.quatToMat(self.rotation);
|
||||
self.local = zm.mul(zm.mul(translation, rotation), scaling);
|
||||
self.local = scaling(
|
||||
zm.translate(
|
||||
zm.identity(),
|
||||
self.translation
|
||||
) * toMat4(self.rotation),
|
||||
self.scale
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const Polycube = struct {
|
||||
graph_node: i32,
|
||||
color: zm.Vec,
|
||||
|
||||
pub fn show(self: Polycube) void {
|
||||
const node = get_scene_graph_node(self.graph_node);
|
||||
for (node.children.items) |child_id| {
|
||||
const child_node = get_scene_graph_node(child_id);
|
||||
if (child_node.entity) |entity_id| {
|
||||
get_entity(entity_id).visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hide(self: Polycube) void {
|
||||
const node = get_scene_graph_node(self.graph_node);
|
||||
for (node.children.items) |child_id| {
|
||||
const child_node = get_scene_graph_node(child_id);
|
||||
if (child_node.entity) |entity_id| {
|
||||
get_entity(entity_id).visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_centre(self: Polycube) zm.Vec {
|
||||
const centre = zm.Vec(0.0);
|
||||
for (get_scene_graph_node(self.graph_node).children.items) |child_id| {
|
||||
centre += get_scene_graph_node(child_id).translation;
|
||||
}
|
||||
centre /= get_scene_graph_node(self.graph_node).children.size();
|
||||
return centre;
|
||||
}
|
||||
};
|
||||
|
||||
const Frame = struct {
|
||||
width: i32,
|
||||
height: i32,
|
||||
x: i32,
|
||||
y: i32,
|
||||
cam: *Camera,
|
||||
|
||||
pub fn new(camera: *Camera, width: i32, height: i32) Frame {
|
||||
const frame = Frame{};
|
||||
camera.init(@as(f32, width) / @as(f32, height));
|
||||
frame.cam = camera;
|
||||
return frame;
|
||||
}
|
||||
};
|
||||
|
||||
fn framebuffer_size_callback(width: i32, height: i32) void {
|
||||
c.glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
fn init_window_and_gl(window_dims: *WindowDims) ?*c.GLFWwindow {
|
||||
c.glfwInit();
|
||||
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 6);
|
||||
c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE);
|
||||
const window = c.glfwCreateWindow(window_dims.width, window_dims.height, "Somaesque", c.NULL, c.NULL);
|
||||
if (window == c.NULL) {
|
||||
std.debug.print("Failed to create GLFW window");
|
||||
c.glfwTerminate();
|
||||
return null;
|
||||
}
|
||||
c.glfwMakeContextCurrent(window);
|
||||
|
||||
if (!c.gladLoadGLLoader(@as(c.GLADloadproc, c.glfwGetProcAddress))) {
|
||||
std.debug.print("Failed to initialize GLAD");
|
||||
return null;
|
||||
}
|
||||
|
||||
c.glViewport(0, 0, 800, 600);
|
||||
c.glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||
c.glEnable(c.GL_DEPTH_TEST);
|
||||
return window;
|
||||
}
|
||||
|
||||
fn gl_update_viewport(window_dims: *WindowDims, frame: *Frame) void {
|
||||
c.glViewport(frame.x, window_dims.height - frame.y - frame.height, frame.width, frame.height);
|
||||
}
|
||||
|
||||
const cube_mesh = Mesh{};
|
||||
const wall_tex = Texture{};
|
||||
const entities = ArrayList(Entity);
|
||||
const scene_graph_nodes = ArrayList(SceneGraphNode);
|
||||
|
||||
fn process_input(window: *c.GLFWwindow) void {
|
||||
const static = struct {
|
||||
wireframe: bool = false,
|
||||
last_frame_state_press_enter: bool = false,
|
||||
last_frame_state_press: bool = false,
|
||||
};
|
||||
|
||||
if (c.glfwGetKey(window, c.GLFW_KEY_ESCAPE) == c.GLFW_PRESS) {
|
||||
c.glfwSetWindowShouldClose(window, true);
|
||||
}
|
||||
|
||||
if (c.glfwGetKey(window, c.GLFW_KEY_SPACE) == c.GLFW_PRESS and !static.last_frame_state_press) {
|
||||
c.glPolygonMode(c.GL_FRONT_AND_BACK, if (!static.wireframe) c.GL_LINE else c.GL_FILL);
|
||||
static.wireframe = !static.wireframe;
|
||||
static.last_frame_state_press = true;
|
||||
} else if (c.glfwGetKey(window, c.GLFW_KEY_SPACE) == c.GLFW_RELEASE) {
|
||||
static.last_frame_state_press = false;
|
||||
}
|
||||
|
||||
if (c.glfwGetKey(window, c.GLFW_KEY_ENTER) == c.GLFW_PRESS and !static.last_frame_state_press_enter) {
|
||||
if (app_state.current_polycube == 6) {
|
||||
app_state.current_polycube = 0;
|
||||
} else {
|
||||
app_state.current_polycube += 1;
|
||||
}
|
||||
static.last_frame_state_press_enter = true;
|
||||
} else if (c.glfwGetKey(window, c.GLFW_KEY_ENTER) == c.GLFW_RELEASE) {
|
||||
static.last_frame_state_press_enter = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn new_entity() i32 {
|
||||
entities.append(.{});
|
||||
scene_graph_nodes.append(.{});
|
||||
entities.items[entities.items.len - 1].scene_graph_node = scene_graph_nodes.items.len;
|
||||
scene_graph_nodes.items[scene_graph_nodes.items.len - 1].entity = entities.items.len;
|
||||
return entities.items.len;
|
||||
}
|
||||
|
||||
fn get_entity(id: i32) ?*Entity {
|
||||
if (entities.items[id - 1]) {
|
||||
return &entities.items[id - 1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn get_scene_graph_node(id: i32) *SceneGraphNode {
|
||||
if (scene_graph_nodes.items[id - 1]) {
|
||||
return &scene_graph_nodes.items[id - 1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn new_graph_node() i32 {
|
||||
scene_graph_nodes.append(.{});
|
||||
return scene_graph_nodes.items.len;
|
||||
}
|
||||
|
||||
fn draw_entity(entity: *Entity) void {
|
||||
const modelUniformLoc = c.glGetUniformLocation(app_state.active_shader.prog_id, "model");
|
||||
c.glUniformMatrix4fv(modelUniformLoc, 1, c.GL_FALSE, &get_scene_graph_node(entity.scene_graph_node).world);
|
||||
c.glBindTexture(c.GL_TEXTURE_2D, entity.tex.tex_id);
|
||||
c.glBindVertexArray(entity.mesh.vao);
|
||||
c.glDrawArrays(c.GL_TRIANGLES, 0, entity.mesh.num_indices);
|
||||
//c.glDrawElements(c.GL_TRIANGLES, entity.mesh.num_indices, c.GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
|
||||
fn create_polycube_from_repr(repr: *Voxel.Space) Polycube {
|
||||
const polycube_id = new_graph_node();
|
||||
get_scene_graph_node(polycube_id).init();
|
||||
var x: usize = 1;
|
||||
var y: usize = 1;
|
||||
var z: usize = 1;
|
||||
while (x < repr.dim_x) : (x += 1) {
|
||||
while (y < repr.dim_y) : (y += 1) {
|
||||
while (z < repr.dim_z) : (z += 1) {
|
||||
if (Voxel.filledAt(repr, x, y, z)) {
|
||||
const polycube_segment = get_entity(new_entity());
|
||||
polycube_segment.mesh = &cube_mesh;
|
||||
polycube_segment.tex = &wall_tex;
|
||||
const graph_node = get_scene_graph_node(polycube_segment.scene_graph_node);
|
||||
graph_node.init();
|
||||
graph_node.translation = zm.f32x4(
|
||||
-((repr.dim_z - 1)/2.0) + z,
|
||||
((repr.dim_x - 1)/2.0) - x,
|
||||
-((repr.dim_y - 1)/2.0) + y,
|
||||
0.0,
|
||||
);
|
||||
graph_node.update_local();
|
||||
get_scene_graph_node(polycube_id).children.append(polycube_segment.scene_graph_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = Polycube{
|
||||
.graph_node = polycube_id,
|
||||
.color = zm.f32x4s(1.0),
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn recalculate_scene_graph(top: *SceneGraphNode) void {
|
||||
if (top.children.size() == 0) {
|
||||
return;
|
||||
}
|
||||
for (top.children.items) |child_id| {
|
||||
const graph_node = get_scene_graph_node(child_id);
|
||||
graph_node.update_local();
|
||||
graph_node.world = zm.mul(top.world, graph_node.local);
|
||||
recalculate_scene_graph(graph_node);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() void {
|
||||
const window_dims = WindowDims{ 800, 600 };
|
||||
const window = init_window_and_gl(&window_dims);
|
||||
if (window == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
app_state = GlobalAppState{
|
||||
.current_polycube=0,
|
||||
.last_polycube_visible=6,
|
||||
.active_shader=null,
|
||||
.polycubes={},
|
||||
};
|
||||
|
||||
const phong_shader = Shader{};
|
||||
phong_shader.init("../assets/shaders/phong-solid.vertex.glsl", "../assets/shaders/phong-solid.fragment.glsl");
|
||||
app_state.active_shader = &phong_shader;
|
||||
|
||||
cube_mesh.init("../assets/models/c000000.obj");
|
||||
wall_tex.init("../assets/textures/brick-wall.jpg");
|
||||
|
||||
const little_frame = Frame{ .width=80, .height=60, .x=20, .y=20 };
|
||||
const big_frame = Frame{ .width=800, .height=600, .x=0, .y=0 };
|
||||
const main_cam = Camera{};
|
||||
const other_cam = Camera{};
|
||||
little_frame.init(&other_cam);
|
||||
big_frame.init(&main_cam);
|
||||
const frames = [_]*Frame{ &big_frame, &little_frame };
|
||||
|
||||
const root_node = SceneGraphNode{};
|
||||
root_node.init();
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < SomaSolve.STD_SOMA.items.len) : (i += 1) {
|
||||
const voxel_space = voxel.Space{ SomaSolve.STD_SOMA[i], 3, 3, 3 };
|
||||
voxel.cullEmptySpace(&voxel_space);
|
||||
const polycube = create_polycube_from_repr(&voxel_space);
|
||||
polycube.color = color.color_from_index(i);
|
||||
app_state.polycubes.append(polycube);
|
||||
root_node.children.append(app_state.polycubes.items[app_state.polycubes.items.len - 1].graph_node);
|
||||
}
|
||||
|
||||
main_cam.pos = zm.f32x4(4.0, 4.0, 4.0, 0.0);
|
||||
main_cam.look_at(0.0, 0.0, 0.0);
|
||||
|
||||
const light_pos = zm.f32x4(6.0, 6.0, 6.0, 0.0);
|
||||
|
||||
c.glUseProgram(app_state.active_shader.prog_id);
|
||||
const view_loc = c.glGetUniformLocation(app_state.active_shader.prog_id, "view");
|
||||
const proj_loc = c.glGetUniformLocation(app_state.active_shader.prog_id, "projection");
|
||||
const light_pos_loc = c.glGetUniformLocation(app_state.active_shader.prog_id, "light_pos");
|
||||
c.glUniform3fv(light_pos_loc, 1, &light_pos);
|
||||
c.glUniformMatrix4fv(proj_loc, 1, GL_FALSE, &main_cam.proj);
|
||||
c.glUniformMatrix4fv(view_loc, 1, GL_FALSE, &main_cam.view);
|
||||
|
||||
var last_frame = c.glfwGetTime();
|
||||
var time_delta = 1.0/60.0;
|
||||
while (!c.glfwWindowShouldClose(window)) {
|
||||
time_delta = c.glfwGetTime() - last_frame;
|
||||
process_input(window);
|
||||
|
||||
if (app_state.last_polycube_visible != app_state.current_polycube) {
|
||||
app_state.polycubes[app_state.last_polycube_visible].hide();
|
||||
app_state.polycubes[app_state.current_polycube].show();
|
||||
app_state.last_polycube_visible = app_state.current_polycube;
|
||||
}
|
||||
|
||||
c.glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
c.glClear(c.GL_DEPTH_BUFFER_BIT | c.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
c.gl_update_viewport(&window_dims, &big_frame);
|
||||
const current_polycube = &app_state.polycubes[app_state.current_polycube];
|
||||
c.get_scene_graph_node(current_polycube.graph_node).rotation = zm.quatFromRollPitchYaw(0.0, c.glfwGetTime() / 2.0, 0.0);
|
||||
|
||||
c.glBindVertexArray(cube_mesh.vao);
|
||||
//glBindTexture(GL_TEXTURE_2D, entity.tex->tex_id);
|
||||
recalculate_scene_graph(&root_node);
|
||||
const model_uniform_loc = c.glGetUniformLocation(app_state.active_shader.prog_id, "model");
|
||||
const solid_color_loc = c.glGetUniformLocation(app_state.active_shader.prog_id, "solid_color");
|
||||
c.glUniform3fv(solid_color_loc, 1, ¤t_polycube.color);
|
||||
while (entities.items) |entity| {
|
||||
if (entity.visible) {
|
||||
c.glUniformMatrix4fv(model_uniform_loc, 1, c.GL_FALSE, &get_scene_graph_node(entity.scene_graph_node).world);
|
||||
c.glDrawArrays(c.GL_TRIANGLES, 0, entity.mesh.num_indices);
|
||||
//glDrawElements(GL_TRIANGLES, entity->mesh->num_indices, GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
c.glfwSwapBuffers(window);
|
||||
c.glfwPollEvents();
|
||||
}
|
||||
|
||||
c.glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//test "simple test" {
|
||||
// var list = std.ArrayList(i32).init(std.testing.allocator);
|
||||
// defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
|
||||
// try list.append(42);
|
||||
// try std.testing.expectEqual(@as(i32, 42), list.pop());
|
||||
//}
|
||||
273
src/render.c
Normal file
273
src/render.c
Normal file
@@ -0,0 +1,273 @@
|
||||
#include "render.h"
|
||||
#include "debug.h"
|
||||
|
||||
static RenderObjects_Char createCharObjects(Arena *arena, size_t count) {
|
||||
RenderObjects_Char result = {0};
|
||||
result.count = count;
|
||||
|
||||
result.begin.buf = PushFullList(arena, RLVec2List, count);
|
||||
result.glyph.buf = PushFullList(arena, IntList, count);
|
||||
result.fontSize.buf = PushFullList(arena, FloatList, count);
|
||||
result.color.buf = PushFullList(arena, RLVec4List, count);
|
||||
|
||||
glGenVertexArrays(1, &result.vao);
|
||||
|
||||
glGenBuffers(1, &result.begin.bufId);
|
||||
glGenBuffers(1, &result.glyph.bufId);
|
||||
glGenBuffers(1, &result.fontSize.bufId);
|
||||
glGenBuffers(1, &result.color.bufId);
|
||||
|
||||
glBindVertexArray(result.vao);
|
||||
|
||||
int32 bufItemSize = sizeof(result.begin.buf.data[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.begin.bufId);
|
||||
glBufferData(GL_ARRAY_BUFFER, result.begin.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
|
||||
glVertexAttribDivisor(0, 1);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
bufItemSize = sizeof(result.glyph.buf.data[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.glyph.bufId);
|
||||
glBufferData(GL_ARRAY_BUFFER, result.glyph.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
|
||||
glVertexAttribIPointer(1, 1, GL_INT, bufItemSize, NULL);
|
||||
glVertexAttribDivisor(1, 1);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
bufItemSize = sizeof(result.fontSize.buf.data[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.fontSize.bufId);
|
||||
glBufferData(GL_ARRAY_BUFFER, result.fontSize.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
|
||||
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
|
||||
glVertexAttribDivisor(2, 1);
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
bufItemSize = sizeof(result.color.buf.data[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.color.bufId);
|
||||
glBufferData(GL_ARRAY_BUFFER, result.color.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
|
||||
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
|
||||
glVertexAttribDivisor(3, 1);
|
||||
glEnableVertexAttribArray(3);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static RenderObjects_Rect createRectangleObjects(Arena *arena, size_t count) {
|
||||
RenderObjects_Rect result = {0};
|
||||
result.count = count;
|
||||
|
||||
result.p0.buf = PushFullList(arena, RLVec2List, count);
|
||||
result.p1.buf = PushFullList(arena, RLVec2List, count);
|
||||
result.color.buf = PushFullList(arena, RLVec4List, count);
|
||||
result.borderRadius.buf = PushFullList(arena, FloatList, count);
|
||||
result.borderThickness.buf = PushFullList(arena, FloatList, count);
|
||||
result.borderColor.buf = PushFullList(arena, RLVec4List, count);
|
||||
result.edgeSoftness.buf = PushFullList(arena, FloatList, count);
|
||||
|
||||
glGenVertexArrays(1, &result.vao);
|
||||
|
||||
glGenBuffers(1, &result.p0.bufId);
|
||||
glGenBuffers(1, &result.p1.bufId);
|
||||
glGenBuffers(1, &result.color.bufId);
|
||||
glGenBuffers(1, &result.borderRadius.bufId);
|
||||
glGenBuffers(1, &result.borderThickness.bufId);
|
||||
glGenBuffers(1, &result.borderColor.bufId);
|
||||
glGenBuffers(1, &result.edgeSoftness.bufId);
|
||||
|
||||
glBindVertexArray(result.vao);
|
||||
|
||||
int32 bufItemSize = sizeof(result.p0.buf.data[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.p0.bufId);
|
||||
glBufferData(GL_ARRAY_BUFFER, result.p0.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
|
||||
glVertexAttribDivisor(0, 1);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
bufItemSize = sizeof(result.p1.buf.data[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.p1.bufId);
|
||||
glBufferData(GL_ARRAY_BUFFER, result.p1.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
|
||||
glVertexAttribDivisor(1, 1);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
bufItemSize = sizeof(result.color.buf.data[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.color.bufId);
|
||||
glBufferData(GL_ARRAY_BUFFER, result.color.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
|
||||
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
|
||||
glVertexAttribDivisor(2, 1);
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
bufItemSize = sizeof(result.borderRadius.buf.data[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.borderRadius.bufId);
|
||||
glBufferData(GL_ARRAY_BUFFER, result.borderRadius.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
|
||||
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
|
||||
glVertexAttribDivisor(3, 1);
|
||||
glEnableVertexAttribArray(3);
|
||||
|
||||
bufItemSize = sizeof(result.borderThickness.buf.data[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.borderThickness.bufId);
|
||||
glBufferData(GL_ARRAY_BUFFER, result.borderThickness.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
|
||||
glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
|
||||
glVertexAttribDivisor(4, 1);
|
||||
glEnableVertexAttribArray(4);
|
||||
|
||||
bufItemSize = sizeof(result.borderColor.buf.data[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.borderColor.bufId);
|
||||
glBufferData(GL_ARRAY_BUFFER, result.borderColor.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
|
||||
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
|
||||
glVertexAttribDivisor(5, 1);
|
||||
glEnableVertexAttribArray(5);
|
||||
|
||||
bufItemSize = sizeof(result.edgeSoftness.buf.data[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result.edgeSoftness.bufId);
|
||||
glBufferData(GL_ARRAY_BUFFER, result.edgeSoftness.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
|
||||
glVertexAttribPointer(6, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
|
||||
glVertexAttribDivisor(6, 1);
|
||||
glEnableVertexAttribArray(6);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define GL_UpdateBuffer(buffer) \
|
||||
glBindBuffer(GL_ARRAY_BUFFER, (buffer).bufId);\
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, (buffer).buf.length * sizeof((buffer).underlying), (buffer).buf.data);
|
||||
|
||||
static void updateCharObjectBuffers(Renderer *r) {
|
||||
glBindVertexArray(r->chars.vao);
|
||||
GL_UpdateBuffer(r->chars.begin);
|
||||
GL_UpdateBuffer(r->chars.glyph);
|
||||
GL_UpdateBuffer(r->chars.fontSize);
|
||||
GL_UpdateBuffer(r->chars.color);
|
||||
}
|
||||
|
||||
static void updateRectangleObjectBuffers(Renderer *r) {
|
||||
glBindVertexArray(r->rects.vao);
|
||||
GL_UpdateBuffer(r->rects.p0);
|
||||
GL_UpdateBuffer(r->rects.p1);
|
||||
GL_UpdateBuffer(r->rects.color);
|
||||
GL_UpdateBuffer(r->rects.borderRadius);
|
||||
GL_UpdateBuffer(r->rects.borderThickness);
|
||||
GL_UpdateBuffer(r->rects.borderColor);
|
||||
GL_UpdateBuffer(r->rects.edgeSoftness);
|
||||
}
|
||||
|
||||
Renderer createRenderer(Arena *arena, Scene *scene, Camera *cam, int32 light) {
|
||||
return (Renderer){
|
||||
.scene = scene,
|
||||
.light = light,
|
||||
.camera = cam,
|
||||
.rects = createRectangleObjects(arena, 1024),
|
||||
.chars = createCharObjects(arena, 10000),
|
||||
};
|
||||
}
|
||||
|
||||
void renderBegin(Renderer *r) {
|
||||
r->rects.p0.buf.length = 0;
|
||||
r->rects.p1.buf.length = 0;
|
||||
r->rects.color.buf.length = 0;
|
||||
r->rects.borderRadius.buf.length = 0;
|
||||
r->rects.borderThickness.buf.length = 0;
|
||||
r->rects.borderColor.buf.length = 0;
|
||||
r->rects.edgeSoftness.buf.length = 0;
|
||||
|
||||
r->chars.begin.buf.length = 0;
|
||||
r->chars.glyph.buf.length = 0;
|
||||
r->chars.fontSize.buf.length = 0;
|
||||
r->chars.color.buf.length = 0;
|
||||
|
||||
r->sceneWidth = 0;
|
||||
r->sceneHeight = 0;
|
||||
r->sceneX = 0;
|
||||
r->sceneY = 0;
|
||||
}
|
||||
|
||||
#define CHECK() do{ GLenum e=glGetError(); if(e) printf("GL err 0x%x at %s:%d\n",e,__FILE__,__LINE__); }while(0)
|
||||
|
||||
void renderEnd(Renderer *r) {
|
||||
// 3D Scene
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
glViewport(r->sceneX, r->height - r->sceneY - r->sceneHeight, r->sceneWidth, r->sceneHeight);
|
||||
cameraSetAspect(r->camera, r->sceneWidth, r->sceneHeight);
|
||||
|
||||
glUseProgram(r->phongShader->progId);
|
||||
setUniformMat4fv(r->phongShader, "projection", &r->camera->proj);
|
||||
setUniformMat4fv(r->phongShader, "view", &r->camera->view);
|
||||
|
||||
SceneGraphNode *lightGraphNode = getSceneGraphNode(r->scene, r->light);
|
||||
setUniform3fv(r->phongShader, "light_pos", &lightGraphNode->translation);
|
||||
setUniform3fv(r->phongShader, "camera", &r->camera->pos);
|
||||
|
||||
glBindVertexArray(r->cubeMesh->vao);
|
||||
|
||||
int model_uniform = getUniformLocation(r->phongShader, "model");
|
||||
int solid_color_uniform = getUniformLocation(r->phongShader, "solid_color");
|
||||
for (EachIn(r->scene->entities, i)) {
|
||||
Entity *entity = &r->scene->entities.data[i];
|
||||
if (entity->flags & EntityFlags_Render && entity->flags & EntityFlags_Visible) {
|
||||
setUniform4fvByLoc(solid_color_uniform, &entity->color);
|
||||
setUniformMat4fvByLoc(model_uniform, &getSceneGraphNode(r->scene, entity->graphNodeHandle)->world);
|
||||
glBindTexture(GL_TEXTURE_2D, entity->tex->tex_id);
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)entity->mesh->num_indices);
|
||||
entity->flags &= ~EntityFlags_Render;
|
||||
}
|
||||
}
|
||||
|
||||
// 2D overlay
|
||||
glViewport(0, 0, r->width, r->height);
|
||||
glUseProgram(r->solidShader->progId);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
Matrix ortho = MatrixOrtho(0.0, r->width, r->height, 0.0, -1.0, 1.0);
|
||||
|
||||
// 1. Rects
|
||||
updateRectangleObjectBuffers(r);
|
||||
setUniformMat4fv(r->solidShader, "projection", &ortho);
|
||||
glBindVertexArray(r->rects.vao);
|
||||
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->rects.p0.buf.length);
|
||||
|
||||
// 2. Text
|
||||
updateCharObjectBuffers(r);
|
||||
glUseProgram(r->textShader->progId);
|
||||
setUniformMat4fv(r->textShader, "projection", &ortho);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0); CHECK();
|
||||
glBindTexture(GL_TEXTURE_2D, r->activeFont->texId); CHECK();
|
||||
|
||||
glActiveTexture(GL_TEXTURE1); CHECK();
|
||||
glBindTexture(GL_TEXTURE_BUFFER, r->activeFont->glyphTableTexId); CHECK();
|
||||
setUniform1i(r->textShader, "glyph_table", 1); CHECK();
|
||||
|
||||
glBindVertexArray(r->chars.vao); CHECK();
|
||||
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->chars.begin.buf.length); CHECK();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness, RLVector4 borderColor) {
|
||||
x = roundf(x);
|
||||
y = roundf(y);
|
||||
width = roundf(width);
|
||||
height = roundf(height);
|
||||
ListAppend(r->rects.p0.buf, ((RLVector2){ x, y }));
|
||||
ListAppend(r->rects.p1.buf, ((RLVector2){ x + width, y + height }));
|
||||
ListAppend(r->rects.color.buf, color);
|
||||
ListAppend(r->rects.borderRadius.buf, borderRadius);
|
||||
ListAppend(r->rects.borderThickness.buf, borderThickness);
|
||||
ListAppend(r->rects.borderColor.buf, borderColor);
|
||||
ListAppend(r->rects.edgeSoftness.buf, 0.15f);
|
||||
}
|
||||
|
||||
void rendererPlaceString(Renderer *r, string s, real32 x, real32 y, RLVector4 color, real32 fontSize) {
|
||||
real32 ratio = fontSize / r->activeFont->lineHeight;
|
||||
real32 charWidth = ratio * r->activeFont->charWidth;
|
||||
for (int i = 0; i < s.length; i++) {
|
||||
ListAppend(r->chars.begin.buf, ((RLVector2){ .x=x + i*charWidth, .y=y }));
|
||||
ListAppend(r->chars.glyph.buf, s.str[i] - 32);
|
||||
ListAppend(r->chars.fontSize.buf, ratio);
|
||||
ListAppend(r->chars.color.buf, (color));
|
||||
}
|
||||
}
|
||||
99
src/render.h
Normal file
99
src/render.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#ifndef RENDER_H
|
||||
#define RENDER_H
|
||||
|
||||
#include "gfx/Shader.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "world/world.h"
|
||||
#include "common.h"
|
||||
|
||||
typedef struct RenderObject_BufferInt32 RenderObject_BufferInt32;
|
||||
struct RenderObject_BufferInt32 {
|
||||
IntList buf;
|
||||
uint32 bufId;
|
||||
IntList_underlying underlying;
|
||||
};
|
||||
typedef struct RenderObject_BufferFloat RenderObject_BufferFloat;
|
||||
struct RenderObject_BufferFloat {
|
||||
FloatList buf;
|
||||
uint32 bufId;
|
||||
FloatList_underlying underlying;
|
||||
};
|
||||
typedef struct RenderObject_BufferVec2 RenderObject_BufferVec2;
|
||||
struct RenderObject_BufferVec2 {
|
||||
RLVec2List buf;
|
||||
uint32 bufId;
|
||||
RLVec2List_underlying underlying;
|
||||
};
|
||||
typedef struct RenderObject_BufferVec4 RenderObject_BufferVec4;
|
||||
struct RenderObject_BufferVec4 {
|
||||
RLVec4List buf;
|
||||
uint32 bufId;
|
||||
RLVec4List_underlying underlying;
|
||||
};
|
||||
|
||||
|
||||
typedef struct RenderObjects_Rect RenderObjects_Rect;
|
||||
struct RenderObjects_Rect {
|
||||
uint32 vao;
|
||||
uint64 count;
|
||||
RenderObject_BufferVec2 p0;
|
||||
RenderObject_BufferVec2 p1;
|
||||
RenderObject_BufferVec4 color;
|
||||
RenderObject_BufferFloat borderRadius;
|
||||
RenderObject_BufferFloat borderThickness;
|
||||
RenderObject_BufferVec4 borderColor;
|
||||
RenderObject_BufferFloat edgeSoftness;
|
||||
};
|
||||
|
||||
typedef struct RenderObjects_Char RenderObjects_Char;
|
||||
struct RenderObjects_Char {
|
||||
uint32 vao;
|
||||
uint64 count;
|
||||
RenderObject_BufferVec2 begin;
|
||||
RenderObject_BufferInt32 glyph;
|
||||
RenderObject_BufferFloat fontSize;
|
||||
RenderObject_BufferVec4 color;
|
||||
};
|
||||
|
||||
typedef struct GlyphData GlyphData;
|
||||
struct GlyphData {
|
||||
real32 x0;
|
||||
real32 y0;
|
||||
real32 x1;
|
||||
real32 y1;
|
||||
};
|
||||
|
||||
typedef struct Renderer Renderer;
|
||||
struct Renderer {
|
||||
RenderObjects_Rect rects;
|
||||
RenderObjects_Char chars;
|
||||
|
||||
Shader *phongShader;
|
||||
Shader *solidShader;
|
||||
Shader *textShader;
|
||||
|
||||
Font *activeFont;
|
||||
|
||||
int32 width;
|
||||
int32 height;
|
||||
|
||||
int32 sceneWidth;
|
||||
int32 sceneHeight;
|
||||
int32 sceneX;
|
||||
int32 sceneY;
|
||||
|
||||
Scene *scene;
|
||||
Camera *camera;
|
||||
/** SceneGraphNode handle */
|
||||
int32 light;
|
||||
Mesh *cubeMesh;
|
||||
};
|
||||
|
||||
Renderer createRenderer(Arena *arena, Scene *scene, Camera *cam, int32 light);
|
||||
void renderBegin(Renderer *r);
|
||||
void renderEnd(Renderer *r);
|
||||
void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness, RLVector4 borderColor);
|
||||
void rendererPlaceString(Renderer *r, string s, real32 x, real32 y, RLVector4 color, real32 fontSize);
|
||||
#define Render(r) DeferLoop(renderBegin((r)), renderEnd((r)))
|
||||
|
||||
#endif
|
||||
329
src/tests.c
Normal file
329
src/tests.c
Normal file
@@ -0,0 +1,329 @@
|
||||
#include "VoxelSpace.h"
|
||||
#include "lib/djstdlib/core.h"
|
||||
|
||||
typedef struct MismatchData MismatchData;
|
||||
struct MismatchData {
|
||||
int i;
|
||||
uint64 actual;
|
||||
uint64 expected;
|
||||
};
|
||||
DefineList(MismatchData, MismatchData);
|
||||
|
||||
void test() {
|
||||
{
|
||||
VoxelSpace space = {};
|
||||
space.space=23ull;
|
||||
space.dim_x=3;
|
||||
space.dim_y=3;
|
||||
space.dim_z=3;
|
||||
|
||||
Assert(filledAt(&space, 0, 0, 1) == true);
|
||||
Assert(filledAt(&space, 1, 0, 0) == false);
|
||||
Assert(filledAt(&space, 2, 1, 2) == false);
|
||||
Assert(filledAt(&space, 1, 2, 1) == false);
|
||||
Assert(filledAt(&space, 0, 0, 0) == true);
|
||||
Assert(filledAt(&space, 2, 2, 1) == false);
|
||||
|
||||
space.space = 30ull;
|
||||
Assert(filledAt(&space, 0, 0, 1) == true);
|
||||
Assert(filledAt(&space, 1, 0, 0) == false);
|
||||
Assert(filledAt(&space, 2, 1, 2) == false);
|
||||
Assert(filledAt(&space, 1, 2, 1) == false);
|
||||
Assert(filledAt(&space, 0, 0, 0) == false);
|
||||
Assert(filledAt(&space, 2, 2, 1) == false);
|
||||
|
||||
space.space = 15ull;
|
||||
Assert(filledAt(&space, 0, 0, 1) == true);
|
||||
Assert(filledAt(&space, 1, 0, 0) == false);
|
||||
Assert(filledAt(&space, 2, 1, 2) == false);
|
||||
Assert(filledAt(&space, 1, 2, 1) == false);
|
||||
Assert(filledAt(&space, 0, 0, 0) == true);
|
||||
Assert(filledAt(&space, 2, 2, 1) == false);
|
||||
|
||||
space.space = 23ull;
|
||||
Assert(filledAt(&space, 0, 0, 1) == true);
|
||||
Assert(filledAt(&space, 1, 0, 0) == false);
|
||||
Assert(filledAt(&space, 2, 1, 2) == false);
|
||||
Assert(filledAt(&space, 1, 2, 1) == false);
|
||||
Assert(filledAt(&space, 0, 0, 0) == true);
|
||||
Assert(filledAt(&space, 2, 2, 1) == false);
|
||||
};
|
||||
|
||||
{
|
||||
VoxelSpace space1 = {};
|
||||
space1.space=172ull;
|
||||
space1.dim_x=3;
|
||||
space1.dim_y=3;
|
||||
space1.dim_z=3;
|
||||
|
||||
Assert(newIndexRotX(&space1, 0, 0, 0) == 6);
|
||||
Assert(newIndexRotX(&space1, 1, 0, 1) == 12);
|
||||
|
||||
Assert(newIndexRotY(&space1, 0, 1, 0) == 5);
|
||||
Assert(newIndexRotY(&space1, 1, 2, 0) == 7);
|
||||
|
||||
Assert(newIndexRotZ(&space1, 1, 0, 2) == 23);
|
||||
Assert(newIndexRotZ(&space1, 0, 0, 0) == 18);
|
||||
}
|
||||
|
||||
{
|
||||
Arena *arena = arenaAlloc(Megabytes(64));
|
||||
VoxelSpace space1 = {};
|
||||
space1.space=30ull;
|
||||
space1.dim_x=3;
|
||||
space1.dim_y=3;
|
||||
space1.dim_z=3;
|
||||
|
||||
VoxelSpaceList rotations = getUniqueRotations(arena, &space1);
|
||||
VoxelSpace expected_rots[] = {
|
||||
{ 30ull, 1, 2, 3 },
|
||||
{ 45ull, 1, 3, 2 },
|
||||
{ 30ull, 3, 2, 1 },
|
||||
{ 30ull, 3, 1, 2 },
|
||||
{ 45ull, 3, 2, 1 },
|
||||
{ 45ull, 3, 1, 2 },
|
||||
{ 51ull, 1, 2, 3 },
|
||||
{ 30ull, 1, 3, 2 },
|
||||
{ 30ull, 2, 3, 1 },
|
||||
{ 30ull, 2, 1, 3 },
|
||||
{ 51ull, 2, 3, 1 },
|
||||
{ 51ull, 2, 1, 3 },
|
||||
};
|
||||
|
||||
Assert(ArrayCount(expected_rots) == rotations.length);
|
||||
|
||||
for (int i = 0; i < rotations.length; i++) {
|
||||
if (i <= ArrayCount(expected_rots)) {
|
||||
Assert(isMatch(&expected_rots[i], &rotations.data[i]) == true);
|
||||
}
|
||||
}
|
||||
|
||||
arenaFree(arena);
|
||||
}
|
||||
|
||||
{
|
||||
Arena *arena = arenaAlloc(Megabytes(64));
|
||||
|
||||
VoxelSpace space1 = {};
|
||||
space1.space=30ul;
|
||||
space1.dim_x=3;
|
||||
space1.dim_y=3;
|
||||
space1.dim_z=3;
|
||||
|
||||
int prism_dims[] = { 3, 3, 3 };
|
||||
VoxelSpaceReprList perms = getAllPermutationsInPrism(arena, &space1, prism_dims);
|
||||
uint64 expected_perms[] = {
|
||||
30ull,
|
||||
240ull,
|
||||
15360ull,
|
||||
122880ull,
|
||||
7864320ull,
|
||||
62914560ull,
|
||||
153ull,
|
||||
306ull,
|
||||
78336ull,
|
||||
156672ull,
|
||||
40108032ull,
|
||||
80216064ull,
|
||||
266760ull,
|
||||
533520ull,
|
||||
1067040ull,
|
||||
2134080ull,
|
||||
4268160ull,
|
||||
8536320ull,
|
||||
263682ull,
|
||||
527364ull,
|
||||
2109456ull,
|
||||
4218912ull,
|
||||
16875648ull,
|
||||
33751296ull,
|
||||
2101761ull,
|
||||
4203522ull,
|
||||
8407044ull,
|
||||
16814088ull,
|
||||
33628176ull,
|
||||
67256352ull,
|
||||
525825ull,
|
||||
1051650ull,
|
||||
4206600ull,
|
||||
8413200ull,
|
||||
33652800ull,
|
||||
67305600ull,
|
||||
51ull,
|
||||
408ull,
|
||||
26112ull,
|
||||
208896ull,
|
||||
13369344ull,
|
||||
106954752ull,
|
||||
90ull,
|
||||
180ull,
|
||||
46080ull,
|
||||
92160ull,
|
||||
23592960ull,
|
||||
47185920ull,
|
||||
4680ull,
|
||||
9360ull,
|
||||
18720ull,
|
||||
2396160ull,
|
||||
4792320ull,
|
||||
9584640ull,
|
||||
1542ull,
|
||||
12336ull,
|
||||
98688ull,
|
||||
789504ull,
|
||||
6316032ull,
|
||||
50528256ull,
|
||||
36873ull,
|
||||
73746ull,
|
||||
147492ull,
|
||||
18878976ull,
|
||||
37757952ull,
|
||||
75515904ull,
|
||||
3075ull,
|
||||
24600ull,
|
||||
196800ull,
|
||||
1574400ull,
|
||||
12595200ull,
|
||||
100761600ull,
|
||||
};
|
||||
|
||||
Assert(ArrayCount(expected_perms) == perms.length);
|
||||
|
||||
for (int i = 0; i < perms.length; i++) {
|
||||
if (i <= ArrayCount(expected_perms)) {
|
||||
Assert(expected_perms[i] == perms.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
arenaFree(arena);
|
||||
}
|
||||
|
||||
{
|
||||
VoxelSpace space1 = {};
|
||||
space1.space=30ull;
|
||||
space1.dim_x=3;
|
||||
space1.dim_y=3;
|
||||
space1.dim_z=3;
|
||||
|
||||
VoxelSpace space2 = {};
|
||||
space2.space=30ull;
|
||||
space2.dim_x=3;
|
||||
space2.dim_y=3;
|
||||
space2.dim_z=3;
|
||||
|
||||
VoxelSpace space3 = {};
|
||||
space3.space=30ull;
|
||||
space3.dim_x=3;
|
||||
space3.dim_y=3;
|
||||
space3.dim_z=3;
|
||||
|
||||
rotate90X(&space1);
|
||||
rotate90Y(&space2);
|
||||
rotate90Z(&space3);
|
||||
Assert(space1.space == 153ull);
|
||||
Assert(space2.space == 1067040ull);
|
||||
Assert(space3.space == 1574400ull);
|
||||
}
|
||||
|
||||
{
|
||||
Arena *arena = arenaAlloc(Megabytes(64));
|
||||
|
||||
int dims[] = {3, 3, 3};
|
||||
|
||||
VoxelSpace space1 = {};
|
||||
space1.space=30ull;
|
||||
space1.dim_x=3;
|
||||
space1.dim_y=3;
|
||||
space1.dim_z=3;
|
||||
|
||||
cullEmptySpace(&space1);
|
||||
uint64 expected_results1[] = {
|
||||
30ull,
|
||||
240ull,
|
||||
15360ull,
|
||||
122880ull,
|
||||
7864320ull,
|
||||
62914560ull,
|
||||
};
|
||||
|
||||
VoxelSpace space2 = {};
|
||||
space2.space=23ul;
|
||||
space2.dim_x=3;
|
||||
space2.dim_y=3;
|
||||
space2.dim_z=3;
|
||||
|
||||
cullEmptySpace(&space2);
|
||||
uint64_t expected_results2[] = {
|
||||
23ull,
|
||||
184ull,
|
||||
11776ull,
|
||||
94208ull,
|
||||
6029312ull,
|
||||
48234496ull,
|
||||
};
|
||||
|
||||
VoxelSpace space3 = {};
|
||||
space3.space=15ull;
|
||||
space3.dim_x=3;
|
||||
space3.dim_y=3;
|
||||
space3.dim_z=3;
|
||||
|
||||
cullEmptySpace(&space3);
|
||||
uint64 expected_results3[] = {
|
||||
15ull,
|
||||
120ull,
|
||||
7680ull,
|
||||
61440ull,
|
||||
3932160ull,
|
||||
31457280ull,
|
||||
};
|
||||
|
||||
VoxelSpaceReprList positions1 = getAllPositionsInPrism(arena, &space1, dims);
|
||||
VoxelSpaceReprList positions2 = getAllPositionsInPrism(arena, &space2, dims);
|
||||
VoxelSpaceReprList positions3 = getAllPositionsInPrism(arena, &space3, dims);
|
||||
|
||||
MismatchDataList mismatches1 = PushList(arena, MismatchDataList, 6);
|
||||
MismatchDataList mismatches2 = PushList(arena, MismatchDataList, 6);
|
||||
MismatchDataList mismatches3 = PushList(arena, MismatchDataList, 6);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (positions1.data[i] != expected_results1[i]) {
|
||||
MismatchData data = { i, positions1.data[i], expected_results1[i] };
|
||||
ListAppend(mismatches1, data);
|
||||
}
|
||||
if (positions2.data[i] != expected_results2[i]) {
|
||||
MismatchData data = { i, positions2.data[i], expected_results2[i] };
|
||||
ListAppend(mismatches2, data);
|
||||
}
|
||||
if (positions3.data[i] != expected_results3[i]) {
|
||||
MismatchData data = { i, positions3.data[i], expected_results3[i] };
|
||||
ListAppend(mismatches3, data);
|
||||
}
|
||||
}
|
||||
Assert(mismatches1.length == 0);
|
||||
if (mismatches1.length > 0) {
|
||||
print("Index - Actual - Expected\n");
|
||||
for (EachIn(mismatches1, i)) {
|
||||
print("%zu - %zu - %zu\n", mismatches1.data[i].i, mismatches1.data[i].actual, mismatches1.data[i].expected);
|
||||
}
|
||||
}
|
||||
Assert(mismatches2.length == 0);
|
||||
if (mismatches2.length > 0) {
|
||||
print("Index - Actual - Expected\n");
|
||||
for (EachIn(mismatches2, i)) {
|
||||
print("%zu - %zu - %zu\n", mismatches2.data[i].i, mismatches2.data[i].actual, mismatches2.data[i].expected);
|
||||
}
|
||||
}
|
||||
Assert(mismatches3.length == 0);
|
||||
if (mismatches3.length > 0) {
|
||||
print("Index - Actual - Expected\n");
|
||||
for (EachIn(mismatches3, i)) {
|
||||
print("%zu - %zu - %zu\n", mismatches3.data[i].i, mismatches3.data[i].actual, mismatches3.data[i].expected);
|
||||
}
|
||||
}
|
||||
|
||||
arenaFree(arena);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
312
src/tests.cpp
312
src/tests.cpp
@@ -1,312 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <istream>
|
||||
#include <tuple>
|
||||
#include "VoxelSpace.h"
|
||||
|
||||
TEST(VoxelSpaces, BasicPositions) {
|
||||
auto space = Voxel::Space{
|
||||
.space=23ul,
|
||||
.dim_x=3,
|
||||
.dim_y=3,
|
||||
.dim_z=3,
|
||||
};
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), true);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false);
|
||||
|
||||
space.space = 30ul;
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false);
|
||||
|
||||
space.space = 15ul;
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), true);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false);
|
||||
|
||||
space.space = 23ul;
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), true);
|
||||
EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false);
|
||||
}
|
||||
|
||||
TEST(VoxelSpaces, RotatedIndices) {
|
||||
auto space1 = Voxel::Space{
|
||||
.space=172ul,
|
||||
.dim_x=3,
|
||||
.dim_y=3,
|
||||
.dim_z=3,
|
||||
};
|
||||
|
||||
EXPECT_EQ(Voxel::newIndexRotX(&space1, 0, 0, 0), 6);
|
||||
EXPECT_EQ(Voxel::newIndexRotX(&space1, 1, 0, 1), 12);
|
||||
|
||||
EXPECT_EQ(Voxel::newIndexRotY(&space1, 0, 1, 0), 5);
|
||||
EXPECT_EQ(Voxel::newIndexRotY(&space1, 1, 2, 0), 7);
|
||||
|
||||
EXPECT_EQ(Voxel::newIndexRotZ(&space1, 1, 0, 2), 23);
|
||||
EXPECT_EQ(Voxel::newIndexRotZ(&space1, 0, 0, 0), 18);
|
||||
}
|
||||
|
||||
TEST(VoxelSpaces, UniqueRotations) {
|
||||
auto space1 = Voxel::Space{
|
||||
.space=30ul,
|
||||
.dim_x=3,
|
||||
.dim_y=3,
|
||||
.dim_z=3,
|
||||
};
|
||||
|
||||
auto rotations = Voxel::getUniqueRotations(&space1);
|
||||
auto expected_rots = std::vector<Voxel::Space>{
|
||||
{ 30ul, 1, 2, 3 },
|
||||
{ 45ul, 1, 3, 2 },
|
||||
{ 30ul, 3, 2, 1 },
|
||||
{ 30ul, 3, 1, 2 },
|
||||
{ 45ul, 3, 2, 1 },
|
||||
{ 45ul, 3, 1, 2 },
|
||||
{ 51ul, 1, 2, 3 },
|
||||
{ 30ul, 1, 3, 2 },
|
||||
{ 30ul, 2, 3, 1 },
|
||||
{ 30ul, 2, 1, 3 },
|
||||
{ 51ul, 2, 3, 1 },
|
||||
{ 51ul, 2, 1, 3 },
|
||||
};
|
||||
|
||||
ASSERT_EQ(expected_rots.size(), rotations.size());
|
||||
|
||||
for (int i = 0; i < rotations.size(); i++) {
|
||||
if (i <= expected_rots.size()) {
|
||||
EXPECT_EQ(Voxel::isMatch(&expected_rots.at(i), &rotations.at(i)), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(VoxelSpaces, AllPermutationsInPrism) {
|
||||
auto space1 = Voxel::Space{
|
||||
.space=30ul,
|
||||
.dim_x=3,
|
||||
.dim_y=3,
|
||||
.dim_z=3,
|
||||
};
|
||||
|
||||
auto prism_dims = std::array<int, 3>{ 3, 3, 3 };
|
||||
auto perms = Voxel::getAllPermutationsInPrism(&space1, prism_dims.begin());
|
||||
auto expected_perms = std::vector<uint64_t>{
|
||||
30ul,
|
||||
240ul,
|
||||
15360ul,
|
||||
122880ul,
|
||||
7864320ul,
|
||||
62914560ul,
|
||||
153ul,
|
||||
306ul,
|
||||
78336ul,
|
||||
156672ul,
|
||||
40108032ul,
|
||||
80216064ul,
|
||||
266760ul,
|
||||
533520ul,
|
||||
1067040ul,
|
||||
2134080ul,
|
||||
4268160ul,
|
||||
8536320ul,
|
||||
263682ul,
|
||||
527364ul,
|
||||
2109456ul,
|
||||
4218912ul,
|
||||
16875648ul,
|
||||
33751296ul,
|
||||
2101761ul,
|
||||
4203522ul,
|
||||
8407044ul,
|
||||
16814088ul,
|
||||
33628176ul,
|
||||
67256352ul,
|
||||
525825ul,
|
||||
1051650ul,
|
||||
4206600ul,
|
||||
8413200ul,
|
||||
33652800ul,
|
||||
67305600ul,
|
||||
51ul,
|
||||
408ul,
|
||||
26112ul,
|
||||
208896ul,
|
||||
13369344ul,
|
||||
106954752ul,
|
||||
90ul,
|
||||
180ul,
|
||||
46080ul,
|
||||
92160ul,
|
||||
23592960ul,
|
||||
47185920ul,
|
||||
4680ul,
|
||||
9360ul,
|
||||
18720ul,
|
||||
2396160ul,
|
||||
4792320ul,
|
||||
9584640ul,
|
||||
1542ul,
|
||||
12336ul,
|
||||
98688ul,
|
||||
789504ul,
|
||||
6316032ul,
|
||||
50528256ul,
|
||||
36873ul,
|
||||
73746ul,
|
||||
147492ul,
|
||||
18878976ul,
|
||||
37757952ul,
|
||||
75515904ul,
|
||||
3075ul,
|
||||
24600ul,
|
||||
196800ul,
|
||||
1574400ul,
|
||||
12595200ul,
|
||||
100761600ul,
|
||||
};
|
||||
|
||||
ASSERT_EQ(expected_perms.size(), perms.size());
|
||||
|
||||
for (int i = 0; i < perms.size(); i++) {
|
||||
if (i <= expected_perms.size()) {
|
||||
EXPECT_EQ(expected_perms.at(i), perms.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(VoxelSpaces, RotateXYZ) {
|
||||
auto space1 = Voxel::Space{
|
||||
.space=30ul,
|
||||
.dim_x=3,
|
||||
.dim_y=3,
|
||||
.dim_z=3,
|
||||
};
|
||||
|
||||
auto space2 = Voxel::Space{
|
||||
.space=30ul,
|
||||
.dim_x=3,
|
||||
.dim_y=3,
|
||||
.dim_z=3,
|
||||
};
|
||||
|
||||
auto space3 = Voxel::Space{
|
||||
.space=30ul,
|
||||
.dim_x=3,
|
||||
.dim_y=3,
|
||||
.dim_z=3,
|
||||
};
|
||||
|
||||
Voxel::rotate90X(&space1);
|
||||
Voxel::rotate90Y(&space2);
|
||||
Voxel::rotate90Z(&space3);
|
||||
EXPECT_EQ(space1.space, 153ul);
|
||||
EXPECT_EQ(space2.space, 1067040ul);
|
||||
EXPECT_EQ(space3.space, 1574400ul);
|
||||
}
|
||||
|
||||
TEST(VoxelSpaces, GetAllPositionsInPrism) {
|
||||
int dims[] = {3, 3, 3};
|
||||
|
||||
auto space1 = Voxel::Space{
|
||||
.space=30ul,
|
||||
.dim_x=3,
|
||||
.dim_y=3,
|
||||
.dim_z=3,
|
||||
};
|
||||
Voxel::cullEmptySpace(&space1);
|
||||
uint64_t expected_results1[] = {
|
||||
30ul,
|
||||
240ul,
|
||||
15360ul,
|
||||
122880ul,
|
||||
7864320ul,
|
||||
62914560ul,
|
||||
};
|
||||
|
||||
auto space2 = Voxel::Space{
|
||||
.space=23ul,
|
||||
.dim_x=3,
|
||||
.dim_y=3,
|
||||
.dim_z=3,
|
||||
};
|
||||
Voxel::cullEmptySpace(&space2);
|
||||
uint64_t expected_results2[] = {
|
||||
23ul,
|
||||
184ul,
|
||||
11776ul,
|
||||
94208ul,
|
||||
6029312ul,
|
||||
48234496ul,
|
||||
};
|
||||
|
||||
auto space3 = Voxel::Space{
|
||||
.space=15ul,
|
||||
.dim_x=3,
|
||||
.dim_y=3,
|
||||
.dim_z=3,
|
||||
};
|
||||
Voxel::cullEmptySpace(&space3);
|
||||
uint64_t expected_results3[] = {
|
||||
15ul,
|
||||
120ul,
|
||||
7680ul,
|
||||
61440ul,
|
||||
3932160ul,
|
||||
31457280ul,
|
||||
};
|
||||
|
||||
auto positions1 = Voxel::getAllPositionsInPrism(&space1, dims);
|
||||
auto positions2 = Voxel::getAllPositionsInPrism(&space2, dims);
|
||||
auto positions3 = Voxel::getAllPositionsInPrism(&space3, dims);
|
||||
auto mismatches1 = std::vector<std::tuple<int, uint64_t, uint64_t>>();
|
||||
auto mismatches2 = std::vector<std::tuple<int, uint64_t, uint64_t>>();
|
||||
auto mismatches3 = std::vector<std::tuple<int, uint64_t, uint64_t>>();
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (positions1[i] != expected_results1[i]) {
|
||||
mismatches1.push_back({ i, positions1[i], expected_results1[i] });
|
||||
}
|
||||
if (positions2[i] != expected_results2[i]) {
|
||||
mismatches2.push_back({ i, positions2[i], expected_results2[i] });
|
||||
}
|
||||
if (positions3[i] != expected_results3[i]) {
|
||||
mismatches3.push_back({ i, positions3[i], expected_results3[i] });
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(mismatches1.size(), 0);
|
||||
if (mismatches1.size() > 0) {
|
||||
std::cout << "Index - Actual - Expected" << std::endl;
|
||||
for (auto &mismatch : mismatches1) {
|
||||
std::cout << std::get<0>(mismatch) << " - " << std::get<1>(mismatch) << " - " << std::get<2>(mismatch) << std::endl;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(mismatches2.size(), 0);
|
||||
if (mismatches2.size() > 0) {
|
||||
std::cout << "Index - Actual - Expected" << std::endl;
|
||||
for (auto &mismatch : mismatches2) {
|
||||
std::cout << std::get<0>(mismatch) << " - " << std::get<1>(mismatch) << " - " << std::get<2>(mismatch) << std::endl;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(mismatches3.size(), 0);
|
||||
if (mismatches3.size() > 0) {
|
||||
std::cout << "Index - Actual - Expected" << std::endl;
|
||||
for (auto &mismatch : mismatches3) {
|
||||
std::cout << "At " << std::get<0>(mismatch) << ": " << std::get<1>(mismatch) << " != " << std::get<2>(mismatch) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
444
src/ui.c
Normal file
444
src/ui.c
Normal file
@@ -0,0 +1,444 @@
|
||||
#include "ui.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
#include "lib/djstdlib/core.h"
|
||||
|
||||
UI_Context *__UI_current_ctx__ = NULL;
|
||||
|
||||
static bool glfwMouse(GLFWwindow *window, int mouseBtnCode) {
|
||||
switch (glfwGetMouseButton(window, mouseBtnCode)) {
|
||||
case GLFW_RELEASE: return false;
|
||||
case GLFW_PRESS: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool glfwKey(GLFWwindow *window, int keyCode) {
|
||||
switch (glfwGetKey(window, keyCode)) {
|
||||
case GLFW_RELEASE: return false;
|
||||
case GLFW_PRESS: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
Input getCurrentInput(GLFWwindow *window) {
|
||||
Input input = {0};
|
||||
|
||||
input.keyboard.escape = glfwKey(window, GLFW_KEY_ESCAPE);
|
||||
input.keyboard.enter = glfwKey(window, GLFW_KEY_ENTER);
|
||||
input.keyboard.space = glfwKey(window, GLFW_KEY_SPACE);
|
||||
input.keyboard.lshift = glfwKey(window, GLFW_KEY_LEFT_SHIFT);
|
||||
input.keyboard.w = glfwKey(window, GLFW_KEY_W);
|
||||
input.keyboard.x = glfwKey(window, GLFW_KEY_X);
|
||||
input.keyboard.y = glfwKey(window, GLFW_KEY_Y);
|
||||
input.keyboard.z = glfwKey(window, GLFW_KEY_Z);
|
||||
|
||||
input.mouse.btnLeft = glfwMouse(window, GLFW_MOUSE_BUTTON_LEFT);
|
||||
input.mouse.btnRight = glfwMouse(window, GLFW_MOUSE_BUTTON_RIGHT);
|
||||
input.mouse.btnMiddle = glfwMouse(window, GLFW_MOUSE_BUTTON_MIDDLE);
|
||||
|
||||
real64 mouseX;
|
||||
real64 mouseY;
|
||||
glfwGetCursorPos(window, &mouseX, &mouseY);
|
||||
input.mouse.point = (RLVector2){(real32)mouseX, (real32)mouseY};
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
UI_Context ui_initContext(Arena *arena, Renderer *renderer) {
|
||||
UI_RectList prevList = PushListZero(arena, UI_RectList, Thousand(10));
|
||||
UI_RectList list = PushListZero(arena, UI_RectList, Thousand(10));
|
||||
ListAppend(list, ((UI_Rect){0})); // empty item
|
||||
return (UI_Context){
|
||||
.arena=arena,
|
||||
.rects=list,
|
||||
.prevRects=prevList,
|
||||
.hotNode=0,
|
||||
.prevHotNode=0,
|
||||
.cursorIsPointer=false,
|
||||
.scene3DHandle=0,
|
||||
.input=NULL,
|
||||
.prevInput=NULL,
|
||||
.renderer=renderer,
|
||||
};
|
||||
}
|
||||
|
||||
void ui_placeText(UI_Context *ui, UI_RectStr strData) {
|
||||
UI_Rect *currRect = &ui->rects.data[ui->currRect];
|
||||
UI_RectStr *strDataCopy = PushStruct(ui->arena, UI_RectStr);
|
||||
*strDataCopy = strData;
|
||||
if (strDataCopy->fontSize == -1) {
|
||||
strDataCopy->fontSize = currRect->inheritedTextAttr->fontSize;
|
||||
if (strDataCopy->fontSize == -1) {
|
||||
strDataCopy->fontSize = ui->renderer->activeFont->lineHeight;
|
||||
}
|
||||
}
|
||||
if (strDataCopy->lineHeight == -1) {
|
||||
strDataCopy->lineHeight = currRect->inheritedTextAttr->lineHeight;
|
||||
if (strDataCopy->lineHeight == -1) {
|
||||
strDataCopy->lineHeight = strDataCopy->fontSize;
|
||||
}
|
||||
if (strDataCopy->fontSize > strDataCopy->lineHeight) {
|
||||
strDataCopy->lineHeight = strDataCopy->fontSize;
|
||||
}
|
||||
}
|
||||
if (strDataCopy->alignment < 0) {
|
||||
if (currRect->inheritedTextAttr) {
|
||||
strDataCopy->alignment = currRect->inheritedTextAttr->alignment;
|
||||
} else {
|
||||
strDataCopy->alignment = 0;
|
||||
}
|
||||
}
|
||||
currRect->inheritedTextAttr = strDataCopy;
|
||||
if (strData.s.str != NULL) {
|
||||
strDataCopy->s = PushString(ui->arena, strData.s.length);
|
||||
memcpy(strDataCopy->s.str, strData.s.str, strData.s.length);
|
||||
currRect->stringData = strDataCopy;
|
||||
} else {
|
||||
// NOTE(djledda): Not real text data if empty string -> just style attributes. Ignore
|
||||
}
|
||||
}
|
||||
|
||||
void ui_sizingPass(UI_Context *ui, bool isXAxis, int32 rectHandle) {
|
||||
UI_Rect *rect = &ui->rects.data[rectHandle];
|
||||
if (!rect->firstChild) return;
|
||||
|
||||
bool isVertical = rect->flags & UI_Flag_Vertical;
|
||||
|
||||
real32 remainingSpace = (isXAxis
|
||||
? rect->resolvedWidth - rect->padding.left - rect->padding.right
|
||||
: rect->resolvedHeight - rect->padding.top - rect->padding.bottom) + rect->childGap;
|
||||
int32 growableChildrenCount = 0;
|
||||
|
||||
UI_Rect *child;
|
||||
|
||||
int32 childHandle = rect->firstChild;
|
||||
if (isVertical != isXAxis) {
|
||||
while (childHandle) {
|
||||
child = &ui->rects.data[childHandle];
|
||||
if (child->flags & UI_Flag_HeightGrow && !isXAxis || child->flags & UI_Flag_WidthGrow && isXAxis) {
|
||||
growableChildrenCount++;
|
||||
}
|
||||
real32 childBreadth = isXAxis ? child->resolvedWidth : child->resolvedHeight;
|
||||
remainingSpace -= childBreadth + rect->childGap;
|
||||
childHandle = child->nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
real32 childBreadthInc = growableChildrenCount > 0 ? remainingSpace / (real32)growableChildrenCount : 0;
|
||||
|
||||
childHandle = rect->firstChild;
|
||||
while (childHandle) {
|
||||
child = &ui->rects.data[childHandle];
|
||||
|
||||
if (isXAxis) {
|
||||
if (child->flags & UI_Flag_WidthGrow) {
|
||||
if (isVertical) {
|
||||
child->resolvedWidth = rect->resolvedWidth - rect->padding.left - rect->padding.right;
|
||||
} else {
|
||||
child->resolvedWidth += childBreadthInc;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (child->flags & UI_Flag_HeightGrow) {
|
||||
if (!isVertical) {
|
||||
child->resolvedHeight = rect->resolvedHeight - rect->padding.top - rect->padding.bottom;
|
||||
} else {
|
||||
child->resolvedHeight += childBreadthInc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui_sizingPass(ui, isXAxis, childHandle);
|
||||
|
||||
childHandle = child->nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_calcLayout(UI_Context *ui, bool isXAxis, int32 rectHandle) {
|
||||
UI_Rect *rect = &ui->rects.data[rectHandle];
|
||||
if (!rect->firstChild) return;
|
||||
|
||||
bool isVertical = (rect->flags & UI_Flag_Vertical);
|
||||
|
||||
real32 coord = isXAxis
|
||||
? rect->x + rect->xOffset + rect->padding.left
|
||||
: rect->y + rect->yOffset + rect->padding.top;
|
||||
|
||||
int32 childHandle = rect->firstChild;
|
||||
UI_Rect *child;
|
||||
while (childHandle) {
|
||||
child = &ui->rects.data[childHandle];
|
||||
|
||||
if (isXAxis) {
|
||||
child->x = child->flags & UI_Flag_Center && isVertical
|
||||
? coord + (rect->resolvedWidth - rect->padding.left - rect->padding.right)/2 - child->resolvedWidth/2
|
||||
: coord;
|
||||
} else {
|
||||
child->y = child->flags & UI_Flag_Center && !isVertical
|
||||
? coord + (rect->resolvedHeight - rect->padding.top - rect->padding.bottom)/2 - child->resolvedHeight/2
|
||||
: coord;
|
||||
}
|
||||
|
||||
ui_calcLayout(ui, isXAxis, childHandle);
|
||||
|
||||
if (!isVertical == isXAxis) {
|
||||
coord += isXAxis ? child->resolvedWidth : child->resolvedHeight;
|
||||
coord += rect->childGap;
|
||||
}
|
||||
|
||||
childHandle = child->nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_begin(UI_Context *ui) {
|
||||
__UI_current_ctx__ = ui;
|
||||
arenaFreeFrom(ui->arena, 0);
|
||||
ClearList(ui->prevRects);
|
||||
ListAppendList(ui->prevRects, ui->rects);
|
||||
ui->cursorIsPointer = false;
|
||||
ui->prevHotNode = ui->hotNode;
|
||||
ui->hotNode = 0;
|
||||
ui->scene3DHandle = 0;
|
||||
ui->rootRect = 0;
|
||||
ui->currRect = 0;
|
||||
ClearList(ui->rects);
|
||||
ListAppend(ui->rects, (UI_Rect){0});
|
||||
|
||||
ui_openElement(ui, (UI_Rect){ .width=ui->renderer->width, .height=ui->renderer->height, .color=(RLVector4){0,0,0,0}});
|
||||
}
|
||||
|
||||
void ui_end(UI_Context *ui) {
|
||||
ui_closeElement(ui);
|
||||
|
||||
// 1) a. Calculate layout
|
||||
ui_sizingPass(ui, true, ui->rootRect); // x
|
||||
ui_sizingPass(ui, false, ui->rootRect); // y
|
||||
|
||||
// 1) b.
|
||||
ui_calcLayout(ui, true, ui->rootRect); // x
|
||||
ui_calcLayout(ui, false, ui->rootRect); // y
|
||||
|
||||
// 2) Create render commands:
|
||||
|
||||
for (EachEl(ui->rects, UI_Rect, rect)) {
|
||||
rendererPlaceRectangle(
|
||||
ui->renderer,
|
||||
rect->x,
|
||||
rect->y,
|
||||
rect->resolvedWidth,
|
||||
rect->resolvedHeight,
|
||||
rect->color,
|
||||
rect->borderRadius,
|
||||
rect->borderThickness,
|
||||
rect->borderColor
|
||||
);
|
||||
if (rect->stringData) {
|
||||
UI_RectStr *str = rect->stringData;
|
||||
real32 alignmentXOffset = 0;
|
||||
real32 alignmentYOffset = rect->padding.top;
|
||||
real32 textWidth = str->s.length * str->fontSize / ui->renderer->activeFont->lineHeight * ui->renderer->activeFont->charWidth;
|
||||
if (str->alignment & UI_TxtAlign_Right) {
|
||||
alignmentXOffset = rect->resolvedWidth - rect->padding.right - textWidth;
|
||||
} else if (str->alignment & UI_TxtAlign_Center) {
|
||||
alignmentXOffset = rect->resolvedWidth/2.0 - textWidth/2.0;
|
||||
} else {
|
||||
alignmentXOffset = rect->padding.left;
|
||||
}
|
||||
rendererPlaceString(
|
||||
ui->renderer,
|
||||
str->s,
|
||||
rect->x + alignmentXOffset + str->xOffset,
|
||||
rect->y + alignmentYOffset + str->yOffset,
|
||||
str->color,
|
||||
str->fontSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (ui->scene3DHandle) {
|
||||
UI_Rect *scene3DRect = &ui->rects.data[ui->scene3DHandle];
|
||||
ui->renderer->sceneX = (int32)scene3DRect->x;
|
||||
ui->renderer->sceneY = (int32)scene3DRect->y;
|
||||
ui->renderer->sceneWidth = (int32)scene3DRect->resolvedWidth;
|
||||
ui->renderer->sceneHeight = (int32)scene3DRect->resolvedHeight;
|
||||
}
|
||||
|
||||
__UI_current_ctx__ = NULL;
|
||||
}
|
||||
|
||||
static bool pointInRect(real32 x, real32 y, UI_Rect rect) {
|
||||
return x > rect.x && y > rect.y && x < (rect.x + rect.resolvedWidth) && y < (rect.y + rect.resolvedHeight);
|
||||
}
|
||||
|
||||
void ui_openElement(UI_Context *ui, UI_Rect rect) {
|
||||
ListAppend(ui->rects, (UI_Rect){0});
|
||||
int32 newHandle = ui->rects.length - 1;
|
||||
UI_Rect *newRect = &ui->rects.data[newHandle];
|
||||
UI_Rect *currRect = &ui->rects.data[ui->currRect];
|
||||
|
||||
*newRect = rect;
|
||||
newRect->parent = ui->currRect;
|
||||
if (currRect->lastChild) {
|
||||
ui->rects.data[currRect->lastChild].nextSibling = newHandle;
|
||||
} else {
|
||||
currRect->firstChild = newHandle;
|
||||
}
|
||||
currRect->lastChild = newHandle;
|
||||
|
||||
if (!ui->rootRect) {
|
||||
ui->rootRect = newHandle;
|
||||
}
|
||||
|
||||
ui->currRect = newHandle;
|
||||
|
||||
if (newRect->flags & UI_Flag_3DScene) {
|
||||
ui->scene3DHandle = newHandle;
|
||||
}
|
||||
|
||||
if (currRect->stringData) {
|
||||
UI_RectStr *inheritedTextAttr = PushStructZero(ui->arena, UI_RectStr);
|
||||
*inheritedTextAttr = *currRect->stringData;
|
||||
inheritedTextAttr->s = s("");
|
||||
newRect->inheritedTextAttr = inheritedTextAttr;
|
||||
} else {
|
||||
newRect->inheritedTextAttr = currRect->inheritedTextAttr;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_closeElement(UI_Context *ui) {
|
||||
UI_Rect *currRect = &ui->rects.data[ui->currRect];
|
||||
UI_Rect *parentRect = &ui->rects.data[currRect->parent];
|
||||
|
||||
if (currRect->width != -1) {
|
||||
currRect->resolvedWidth = currRect->width;
|
||||
} else if (currRect->minWidth == -1) {
|
||||
if (currRect->stringData) {
|
||||
real32 charWidth = currRect->stringData->fontSize / ui->renderer->activeFont->lineHeight * ui->renderer->activeFont->charWidth;
|
||||
currRect->minWidth = charWidth*currRect->stringData->s.length;
|
||||
} else {
|
||||
currRect->minWidth = 0;
|
||||
}
|
||||
}
|
||||
currRect->resolvedWidth = currRect->padding.left + currRect->padding.right + (currRect->resolvedWidth < currRect->minWidth ? currRect->minWidth : currRect->resolvedWidth);
|
||||
|
||||
if (currRect->height != -1) {
|
||||
currRect->resolvedHeight = currRect->height - currRect->padding.top - currRect->padding.bottom;
|
||||
} else if (currRect->minHeight == -1) {
|
||||
if (currRect->stringData) {
|
||||
currRect->minHeight = currRect->stringData->lineHeight;
|
||||
} else {
|
||||
currRect->minHeight = 0;
|
||||
}
|
||||
}
|
||||
currRect->resolvedHeight = currRect->padding.top + currRect->padding.bottom + (currRect->resolvedHeight < currRect->minHeight ? currRect->minHeight : currRect->resolvedHeight);
|
||||
|
||||
bool vertical = parentRect->flags & UI_Flag_Vertical;
|
||||
|
||||
real32 currBreadth = vertical ? currRect->resolvedHeight : currRect->resolvedWidth;
|
||||
real32 currCrossBreadth = vertical ? currRect->resolvedWidth : currRect->resolvedHeight;
|
||||
real32 parentBreadth = vertical ? parentRect->height : parentRect->width;
|
||||
real32 currParentBreadth = vertical ? parentRect->resolvedHeight : parentRect->resolvedWidth;
|
||||
real32 parentCrossBreadth = vertical ? parentRect->width : parentRect->height;
|
||||
real32 currParentCrossBreadth = vertical ? parentRect->resolvedWidth : parentRect->resolvedHeight;
|
||||
real32 gap = parentRect->childGap;
|
||||
|
||||
real32 breadthInc = (parentRect->firstChild == ui->currRect ? 0 : gap) + currBreadth;
|
||||
|
||||
if (parentBreadth == -1) {
|
||||
if (vertical) {
|
||||
parentRect->resolvedHeight += breadthInc;
|
||||
} else {
|
||||
parentRect->resolvedWidth += breadthInc;
|
||||
}
|
||||
}
|
||||
|
||||
real32 newCrossBreadth = currCrossBreadth > currParentCrossBreadth ? currCrossBreadth : currParentCrossBreadth;
|
||||
if (parentCrossBreadth == -1) {
|
||||
if (vertical) {
|
||||
parentRect->resolvedWidth = newCrossBreadth;
|
||||
} else {
|
||||
parentRect->resolvedHeight = newCrossBreadth;
|
||||
}
|
||||
}
|
||||
|
||||
ui->currRect = ui->rects.data[ui->currRect].parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the checkbox was clicked
|
||||
*/
|
||||
bool ui_ButtonWithHover(UI_Context *ui, UI_Rect rect, UI_Rect hovered, UI_RectStr textAttr) {
|
||||
int32 id = UI_NextID();
|
||||
bool clicked = false;
|
||||
|
||||
bool inRect = pointInRect(ui->input->mouse.point.x, ui->input->mouse.point.y, ui->prevRects.data[id]);
|
||||
if (inRect) {
|
||||
ui->cursorIsPointer = true;
|
||||
if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) {
|
||||
clicked = true;
|
||||
} else if (ui->input->mouse.btnLeft && (!ui->prevInput->mouse.btnLeft || ui->prevHotNode == id)) {
|
||||
ui->hotNode = id;
|
||||
}
|
||||
}
|
||||
|
||||
bool useHover = inRect && !(hovered.flags & UI_Flag_Ignore);
|
||||
UI_Rect *rectToRender = useHover ? &hovered : ▭
|
||||
if (rectToRender->borderRadius == -1) {
|
||||
rectToRender->borderRadius = 5;
|
||||
}
|
||||
|
||||
UI_FromRect(*rectToRender) ui_placeText(ui, textAttr);
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the checkbox was clicked
|
||||
*/
|
||||
bool ui_Button(UI_Context *ui, UI_Rect rect, UI_RectStr textAttr) {
|
||||
return ui_ButtonWithHover(ui, rect, UI_RectAttr(.flags=UI_Flag_Ignore), textAttr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the checkbox was clicked
|
||||
*/
|
||||
bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect) {
|
||||
int32 id = UI_NextID();
|
||||
bool clicked = false;
|
||||
bool pointerInRect = pointInRect(ui->input->mouse.point.x, ui->input->mouse.point.y, ui->prevRects.data[id]);
|
||||
if (pointerInRect) {
|
||||
ui->cursorIsPointer = true;
|
||||
if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) {
|
||||
*value = !*value;
|
||||
clicked = true;
|
||||
} else if (ui->input->mouse.btnLeft && (!ui->prevInput->mouse.btnLeft || ui->prevHotNode == id)) {
|
||||
ui->hotNode = id;
|
||||
}
|
||||
}
|
||||
if (*value) {
|
||||
rect.borderRadius = 5;
|
||||
rect.borderThickness = 0;
|
||||
if (pointerInRect) {
|
||||
rect.color.x += 0.5;
|
||||
rect.color.y += 0.5;
|
||||
rect.color.z += 0.5;
|
||||
rect.color.x = rect.color.x <= 1.0 ? rect.color.x : 1.0;
|
||||
rect.color.y = rect.color.y <= 1.0 ? rect.color.y : 1.0;
|
||||
rect.color.z = rect.color.z <= 1.0 ? rect.color.z : 1.0;
|
||||
}
|
||||
UI_FromRect(rect);
|
||||
} else {
|
||||
rect.borderRadius = 5;
|
||||
rect.borderThickness = 2;
|
||||
rect.borderColor = COLOR_WHITE;
|
||||
if (pointerInRect) {
|
||||
rect.color = COLOR_WHITE;
|
||||
rect.color.w = 0.2;
|
||||
} else {
|
||||
rect.color = (RLVector4){0,0,0,0};
|
||||
}
|
||||
UI_FromRect(rect);
|
||||
}
|
||||
|
||||
return clicked;
|
||||
}
|
||||
193
src/ui.h
Normal file
193
src/ui.h
Normal file
@@ -0,0 +1,193 @@
|
||||
#ifndef UI_H
|
||||
#define UI_H
|
||||
|
||||
#include "render.h"
|
||||
#include "lib/djstdlib/core.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
#include "lib/djstdlib/core.h"
|
||||
|
||||
typedef struct Input Input;
|
||||
struct Input {
|
||||
struct {
|
||||
bool escape;
|
||||
bool enter;
|
||||
bool space;
|
||||
bool lshift;
|
||||
bool x;
|
||||
bool y;
|
||||
bool z;
|
||||
bool w;
|
||||
} keyboard;
|
||||
struct {
|
||||
union {
|
||||
struct {
|
||||
real32 x;
|
||||
real32 y;
|
||||
};
|
||||
RLVector2 point;
|
||||
};
|
||||
struct {
|
||||
real64 dX;
|
||||
real64 dY;
|
||||
} scroll;
|
||||
bool btnLeft;
|
||||
bool btnRight;
|
||||
bool btnMiddle;
|
||||
} mouse;
|
||||
};
|
||||
|
||||
enum UI_Flag {
|
||||
UI_Flag_WidthGrow=1<<0, // Default is fixed
|
||||
UI_Flag_HeightGrow=1<<1, // Default is fixed
|
||||
UI_Flag_Vertical=1<<2, // Default is horizontal
|
||||
UI_Flag_3DScene=1<<3,
|
||||
UI_Flag_Center=1<<4,
|
||||
UI_Flag_Ignore=1<<7, // For optional parameters
|
||||
// ..
|
||||
UI_Flag_COUNT,
|
||||
};
|
||||
|
||||
typedef struct UI_Padding UI_Padding;
|
||||
struct UI_Padding {
|
||||
real32 top;
|
||||
real32 right;
|
||||
real32 bottom;
|
||||
real32 left;
|
||||
};
|
||||
|
||||
enum UI_TxtAlign {
|
||||
UI_TxtAlign_Left=1<<0, // Default
|
||||
UI_TxtAlign_Center=1<<1,
|
||||
UI_TxtAlign_Right=1<<2,
|
||||
};
|
||||
|
||||
typedef struct UI_RectStr UI_RectStr;
|
||||
struct UI_RectStr {
|
||||
string s;
|
||||
real32 xOffset;
|
||||
real32 yOffset;
|
||||
real32 lineHeight;
|
||||
real32 fontSize;
|
||||
RLVector4 color;
|
||||
enum UI_TxtAlign alignment;
|
||||
};
|
||||
|
||||
typedef struct UI_Rect UI_Rect;
|
||||
struct UI_Rect {
|
||||
enum UI_Flag flags;
|
||||
|
||||
int32 parent;
|
||||
int32 firstChild;
|
||||
int32 lastChild;
|
||||
int32 nextSibling;
|
||||
|
||||
real32 xOffset;
|
||||
real32 yOffset;
|
||||
|
||||
real32 width;
|
||||
real32 minWidth;
|
||||
real32 maxWidth;
|
||||
|
||||
real32 height;
|
||||
real32 minHeight;
|
||||
real32 maxHeight;
|
||||
|
||||
real32 borderRadius;
|
||||
real32 borderThickness;
|
||||
|
||||
RLVector4 color;
|
||||
RLVector4 borderColor;
|
||||
|
||||
UI_Padding padding;
|
||||
real32 childGap;
|
||||
|
||||
real32 x;
|
||||
real32 y;
|
||||
real32 resolvedWidth;
|
||||
real32 resolvedHeight;
|
||||
|
||||
UI_RectStr *stringData;
|
||||
/**
|
||||
* Inherited from parent to propagate attributes. Actual string is ignored, only used for filling out defaults in
|
||||
* the next stringData pointer that is set.
|
||||
* This pointer is always set, as the global UI context will set it on the root not if it does not present its own
|
||||
* text data.
|
||||
*/
|
||||
UI_RectStr *inheritedTextAttr;
|
||||
};
|
||||
DefineList(UI_Rect, UI_Rect);
|
||||
|
||||
typedef struct UI_Context UI_Context;
|
||||
struct UI_Context {
|
||||
Arena *arena;
|
||||
UI_RectList prevRects;
|
||||
UI_RectList rects;
|
||||
int32 hotNode;
|
||||
int32 scene3DHandle;
|
||||
int32 prevHotNode;
|
||||
Renderer *renderer;
|
||||
|
||||
Input *prevInput;
|
||||
Input *input;
|
||||
bool cursorIsPointer;
|
||||
|
||||
int32 rootRect;
|
||||
int32 currRect;
|
||||
};
|
||||
|
||||
Input getCurrentInput(GLFWwindow *window);
|
||||
|
||||
void ui_begin(UI_Context *ui);
|
||||
void ui_end(UI_Context *ui);
|
||||
UI_Context ui_initContext(Arena *arena, Renderer *renderer);
|
||||
void ui_resolve();
|
||||
void ui_rect(UI_Context *ui, UI_Rect rect);
|
||||
void ui_placeText(UI_Context *ui, UI_RectStr strData);
|
||||
void ui_openElement(UI_Context *ui, UI_Rect rect);
|
||||
void ui_closeElement(UI_Context *ui);
|
||||
|
||||
bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect);
|
||||
bool ui_Button(UI_Context *ui, UI_Rect rect, UI_RectStr textAttr);
|
||||
bool ui_ButtonWithHover(UI_Context *ui, UI_Rect rect, UI_Rect hovered, UI_RectStr textAttr);
|
||||
|
||||
extern UI_Context *__UI_current_ctx__;
|
||||
|
||||
#if 0
|
||||
#define UI_DEBUG .borderColor=COLOR_RED, .borderThickness=1,
|
||||
#else
|
||||
#define UI_DEBUG
|
||||
#endif
|
||||
|
||||
#define UI_Pass(ui) DeferLoop(ui_begin((ui)), ui_end((ui)))
|
||||
#define UI_PadUniform(padding) ((UI_Padding){ (padding), (padding), (padding), (padding) })
|
||||
#define UI_NextID() ((__UI_current_ctx__)->rects.length)
|
||||
|
||||
#define UI(...)\
|
||||
/** Place a UI_Rect defined in-place in the current position in the UI tree */\
|
||||
DeferLoop(ui_openElement((__UI_current_ctx__), UI_RectAttr(__VA_ARGS__)), ui_closeElement((__UI_current_ctx__)))
|
||||
|
||||
#define UI_RectAttr(...)\
|
||||
/** Generate a UI_Rect to be passed as a value */\
|
||||
((UI_Rect){UI_DEBUG .minHeight=-1, .minWidth=-1, .width=-1, .height=-1, __VA_ARGS__})
|
||||
|
||||
#define UI_FromRect(rect)\
|
||||
/** Place a UI_Rect in the current position in the UI tree, as defined by an existing value */\
|
||||
DeferLoop(ui_openElement((__UI_current_ctx__), (rect)), ui_closeElement((__UI_current_ctx__)))
|
||||
|
||||
#define UI_TxtAttr(str, ...)\
|
||||
/** Generate UI text to be used as a value */\
|
||||
((UI_RectStr){ .alignment=-1, .xOffset=0, .yOffset=0, .fontSize=-1, .lineHeight=-1, .s=(str), __VA_ARGS__ })
|
||||
|
||||
#define UI_Txt(str, ...)\
|
||||
/** Place UI text in the current position in the UI tree. Attaches to the current UI_Rect */\
|
||||
(ui_placeText((__UI_current_ctx__), (UI_TxtAttr(str, __VA_ARGS__))))
|
||||
|
||||
#define UI_SetTxtAttr(...)\
|
||||
/** Attach "phantom" text to the current rect in order to set its heritable attributes for subsequent text in the UI subtrees */\
|
||||
(ui_placeText((__UI_current_ctx__), (UI_TxtAttr(((string){.str=NULL,.length=0}), __VA_ARGS__))))
|
||||
|
||||
#define UI_TxtSeg(str, ...)\
|
||||
/** Place a text fragment in its own box (useful for placing many text fragments in a row */\
|
||||
UI() (UI_Txt((str), __VA_ARGS__))
|
||||
|
||||
#endif
|
||||
26
src/world/camera.c
Normal file
26
src/world/camera.c
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "camera.h"
|
||||
#include "../lib/raymath.h"
|
||||
|
||||
Camera createCamera(int32 width, int32 height) {
|
||||
return (Camera){
|
||||
.view = (Matrix){0},
|
||||
.proj = MatrixPerspective(DEG2RAD * 45.0f, (real32)width/(real32)height, 0.1f, 100.0f),
|
||||
.pos = (RLVector3){0},
|
||||
.up = (RLVector3){0,1,0},
|
||||
};
|
||||
}
|
||||
|
||||
void cameraSetAspect(Camera *c, int32 width, int32 height) {
|
||||
real32 aspectRatio = (real32)width/(real32)height;
|
||||
c->proj = MatrixPerspective(DEG2RAD * 45.0f, aspectRatio, 0.1f, 100.0f);
|
||||
}
|
||||
|
||||
void cameraLookAt(Camera *c, float x, float y, float z) {
|
||||
c->target = (RLVector3){x, y, z};
|
||||
c->view = MatrixLookAt(c->pos, c->target, c->up);
|
||||
}
|
||||
|
||||
void cameraSetUp(Camera *c, real32 up_x, real32 up_y, real32 up_z) {
|
||||
c->up = (RLVector3){up_x, up_y, up_z};
|
||||
}
|
||||
|
||||
21
src/world/camera.h
Normal file
21
src/world/camera.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef CAMERA_H
|
||||
#define CAMERA_H
|
||||
|
||||
#include "../lib/djstdlib/core.h"
|
||||
#include "../lib/raymath.h"
|
||||
|
||||
typedef struct Camera Camera;
|
||||
struct Camera {
|
||||
Matrix view;
|
||||
Matrix proj;
|
||||
RLVector3 pos;
|
||||
RLVector3 up;
|
||||
RLVector3 target;
|
||||
};
|
||||
|
||||
Camera createCamera(int32 width, int32 height);
|
||||
void cameraSetAspect(Camera *c, int32 width, int32 height);
|
||||
void cameraLookAt(Camera *c, float x, float y, float z);
|
||||
void cameraSetUp(Camera *c, real32 up_x, real32 up_y, real32 up_z);
|
||||
|
||||
#endif
|
||||
195
src/world/scene.c
Normal file
195
src/world/scene.c
Normal file
@@ -0,0 +1,195 @@
|
||||
#include "scene.h"
|
||||
|
||||
Entity *getEntity(Scene *s, int32 entityHandle) {
|
||||
return &s->entities.data[entityHandle];
|
||||
}
|
||||
|
||||
SceneGraphNode *getSceneGraphNode(Scene *s, int32 sceneGraphNodeHandle) {
|
||||
return &s->graphNodes.data[sceneGraphNodeHandle];
|
||||
}
|
||||
|
||||
SceneGraphNode *getSceneGraphNodeForEntity(Scene *s, int32 entityHandle) {
|
||||
return getSceneGraphNode(s, getEntity(s, entityHandle)->graphNodeHandle);
|
||||
}
|
||||
|
||||
int32 createSceneGraphNode(Scene *s) {
|
||||
SceneGraphNode *newNode;
|
||||
int32 newNodeHandle;
|
||||
if (s->nextFreeNode) {
|
||||
newNodeHandle = s->nextFreeNode;
|
||||
newNode = getSceneGraphNode(s, newNodeHandle);
|
||||
if (newNode->next) {
|
||||
s->nextFreeNode = newNode->next;
|
||||
} else {
|
||||
s->nextFreeNode = 0;
|
||||
}
|
||||
newNode->next = 0;
|
||||
} else {
|
||||
ListAppend(s->graphNodes, (SceneGraphNode){0});
|
||||
newNodeHandle = (int32)s->graphNodes.length - 1;
|
||||
newNode = getSceneGraphNode(s, newNodeHandle);
|
||||
newNode->nextSibling = 0;
|
||||
}
|
||||
initGraphNode(newNode);
|
||||
return newNodeHandle;
|
||||
}
|
||||
|
||||
int32 createEntity(Scene *s) {
|
||||
Entity *newEntity;
|
||||
int32 newEntityHandle;
|
||||
if (s->nextFreeEntity) {
|
||||
newEntityHandle = s->nextFreeEntity;
|
||||
newEntity = getEntity(s, newEntityHandle);
|
||||
if (newEntity->next) {
|
||||
s->nextFreeEntity = newEntity->next;
|
||||
} else {
|
||||
s->nextFreeEntity = 0;
|
||||
}
|
||||
newEntity->next = 0;
|
||||
} else {
|
||||
ListAppend(s->entities, (Entity){0});
|
||||
newEntityHandle = (int32)s->entities.length - 1;
|
||||
newEntity = getEntity(s, newEntityHandle);
|
||||
}
|
||||
newEntity->graphNodeHandle = createSceneGraphNode(s);
|
||||
getSceneGraphNodeForEntity(s, newEntityHandle)->entityHandle = newEntityHandle;
|
||||
return newEntityHandle;
|
||||
}
|
||||
|
||||
void initGraphNode(SceneGraphNode *n) {
|
||||
n->scale = (RLVector3){1.0f, 1.0f, 1.0f};
|
||||
n->translation = (RLVector3){0.0f, 0.0f, 0.0f};
|
||||
n->rotation = (Quaternion){1.0f, 0.0f, 0.0f, 0.0f};
|
||||
n->local = MatrixIdentity();
|
||||
n->world = n->local;
|
||||
}
|
||||
|
||||
Scene createScene(Arena *arena) {
|
||||
Scene result = {
|
||||
.entities = PushListZero(arena, EntityList, 100000),
|
||||
.nextFreeEntity = 0,
|
||||
.graphNodes = PushListZero(arena, SceneGraphNodeList, 100000),
|
||||
.nextFreeNode = 0,
|
||||
};
|
||||
int32 handle = createEntity(&result); // Intialise the "zero" nodes
|
||||
getEntity(&result, handle)->flags = EntityFlags_None | EntityFlags_Dead;
|
||||
|
||||
result.sceneRoot = createSceneGraphNode(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void recalcSceneGraphNode(Scene *s, int32 parentHandle) {
|
||||
if (!parentHandle) return;
|
||||
SceneGraphNode *parentNode = getSceneGraphNode(s, parentHandle);
|
||||
getEntity(s, parentNode->entityHandle)->flags |= EntityFlags_Render;
|
||||
int32 nextChild = parentNode->firstChild;
|
||||
while (nextChild) {
|
||||
SceneGraphNode *childNode = getSceneGraphNode(s, nextChild);
|
||||
childNode->parentHandle = parentHandle;
|
||||
childNode->local = MatrixCompose(childNode->translation, childNode->rotation, childNode->scale);
|
||||
childNode->world = MatrixMultiply(childNode->local, parentNode->world);
|
||||
recalcSceneGraphNode(s, nextChild);
|
||||
nextChild = childNode->nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
void recalcScene(Scene *s) {
|
||||
recalcSceneGraphNode(s, s->sceneRoot);
|
||||
}
|
||||
|
||||
static void removeSceneGraphNodeRecursive(Scene *s, int32 deletedNodeHandle, bool deletingParent) {
|
||||
if (!deletedNodeHandle) return;
|
||||
SceneGraphNode *deletedNode = getSceneGraphNode(s, deletedNodeHandle);
|
||||
|
||||
int32 nextChild = deletedNode->firstChild;
|
||||
while (nextChild) {
|
||||
int32 sibling = getSceneGraphNode(s, nextChild)->nextSibling;
|
||||
removeSceneGraphNodeRecursive(s, nextChild, true);
|
||||
nextChild = sibling;
|
||||
}
|
||||
|
||||
if (deletedNode->entityHandle) {
|
||||
Entity *entity = getEntity(s, deletedNode->entityHandle);
|
||||
*entity = (Entity){0};
|
||||
if (s->nextFreeEntity) {
|
||||
entity->next = s->nextFreeEntity;
|
||||
}
|
||||
s->nextFreeEntity = deletedNode->entityHandle;
|
||||
}
|
||||
|
||||
if (s->nextFreeNode) {
|
||||
deletedNode->next = s->nextFreeNode;
|
||||
}
|
||||
s->nextFreeNode = deletedNodeHandle;
|
||||
|
||||
if (!deletingParent && deletedNode->parentHandle) {
|
||||
SceneGraphNode *parentNode = getSceneGraphNode(s, deletedNode->parentHandle);
|
||||
if (parentNode->firstChild == deletedNodeHandle) {
|
||||
if (deletedNode->nextSibling) {
|
||||
parentNode->firstChild = deletedNode->nextSibling;
|
||||
} else {
|
||||
parentNode->firstChild = 0;
|
||||
}
|
||||
} else {
|
||||
int32 prevSibling = parentNode->firstChild;
|
||||
int32 nextSibling = getSceneGraphNode(s, parentNode->firstChild)->nextSibling;
|
||||
while (nextSibling) {
|
||||
SceneGraphNode *siblingNode = getSceneGraphNode(s, nextSibling);
|
||||
if (nextSibling == deletedNodeHandle) {
|
||||
SceneGraphNode *prevSiblingNode = getSceneGraphNode(s, prevSibling);
|
||||
prevSiblingNode->nextSibling = deletedNode->nextSibling;
|
||||
break;
|
||||
}
|
||||
prevSibling = nextSibling;
|
||||
nextSibling = siblingNode->nextSibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deletedNode->firstChild = 0;
|
||||
deletedNode->parentHandle = 0;
|
||||
deletedNode->entityHandle = 0;
|
||||
deletedNode->nextSibling = 0;
|
||||
}
|
||||
|
||||
void removeSceneGraphNode(Scene *s, int32 graphNodeHandle) {
|
||||
removeSceneGraphNodeRecursive(s, graphNodeHandle, false);
|
||||
}
|
||||
|
||||
void removeEntity(Scene *s, int32 entityHandle) {
|
||||
if (!entityHandle) return;
|
||||
removeSceneGraphNode(s, getEntity(s, entityHandle)->graphNodeHandle);
|
||||
}
|
||||
|
||||
void sceneNodeAddNode(Scene *s, int32 parentHandle, int32 childHandle) {
|
||||
SceneGraphNode *parentNode = getSceneGraphNode(s, parentHandle);
|
||||
SceneGraphNode *childNode = getSceneGraphNode(s, childHandle);
|
||||
childNode->nextSibling = parentNode->firstChild;
|
||||
parentNode->firstChild = childHandle;
|
||||
childNode->parentHandle = parentHandle;
|
||||
}
|
||||
|
||||
void show(Scene *s, uint32 graphNodeHandle) {
|
||||
SceneGraphNode *node = getSceneGraphNode(s, graphNodeHandle);
|
||||
if (node->entityHandle) {
|
||||
getEntity(s, node->entityHandle)->flags |= EntityFlags_Visible;
|
||||
}
|
||||
int32 next = node->firstChild;
|
||||
while (next) {
|
||||
show(s, next);
|
||||
next = getSceneGraphNode(s, next)->nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
void hide(Scene *s, uint32 graphNodeHandle) {
|
||||
SceneGraphNode *node = getSceneGraphNode(s, graphNodeHandle);
|
||||
if (node->entityHandle) {
|
||||
getEntity(s, node->entityHandle)->flags &= ~EntityFlags_Visible;
|
||||
}
|
||||
int32 next = node->firstChild;
|
||||
while (next) {
|
||||
hide(s, next);
|
||||
next = getSceneGraphNode(s, next)->nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
73
src/world/scene.h
Normal file
73
src/world/scene.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef SCENE_H
|
||||
#define SCENE_H
|
||||
|
||||
#include "../gfx/gfx.h"
|
||||
#include "../lib/raymath.h"
|
||||
|
||||
DefineList(int32, Handle);
|
||||
|
||||
enum EntityFlags {
|
||||
EntityFlags_None=0,
|
||||
EntityFlags_Visible=1<<0,
|
||||
EntityFlags_Dead=1<<1,
|
||||
EntityFlags_Render=1<<2,
|
||||
};
|
||||
|
||||
typedef struct Entity Entity;
|
||||
struct Entity {
|
||||
int32 graphNodeHandle;
|
||||
uint64 flags;
|
||||
RLVector4 color;
|
||||
Mesh *mesh;
|
||||
Texture *tex;
|
||||
|
||||
// Free list
|
||||
int32 next;
|
||||
};
|
||||
DefineList(Entity, Entity);
|
||||
|
||||
typedef struct SceneGraphNode SceneGraphNode;
|
||||
struct SceneGraphNode {
|
||||
Matrix local;
|
||||
Matrix world;
|
||||
RLVector3 translation;
|
||||
Quaternion rotation;
|
||||
RLVector3 scale;
|
||||
int32 entityHandle;
|
||||
int32 parentHandle;
|
||||
|
||||
/** Next free in the free list */
|
||||
int32 next;
|
||||
|
||||
/** Next child in child list in scene hierarchy */
|
||||
int32 nextSibling;
|
||||
int32 firstChild;
|
||||
};
|
||||
DefineList(SceneGraphNode, SceneGraphNode);
|
||||
|
||||
typedef struct Scene Scene;
|
||||
struct Scene {
|
||||
int32 sceneRoot;
|
||||
EntityList entities;
|
||||
int32 nextFreeEntity;
|
||||
/** @internal */
|
||||
SceneGraphNodeList graphNodes;
|
||||
int32 nextFreeNode;
|
||||
};
|
||||
|
||||
int32 createEntity(Scene *s);
|
||||
Entity *getEntity(Scene *s, int32 id);
|
||||
SceneGraphNode *getSceneGraphNode(Scene *s, int32 id);
|
||||
int32 createSceneGraphNode(Scene *s);
|
||||
Scene createScene(Arena *arena);
|
||||
void initGraphNode(SceneGraphNode *n);
|
||||
void recalcSceneGraphNode(Scene *s, int32 parentHandle);
|
||||
void recalcScene(Scene *s);
|
||||
void removeEntity(Scene *s, int32 entityHandle);
|
||||
void removeSceneGraphNode(Scene *s, int32 graphNodeHandle);
|
||||
void sceneNodeAddNode(Scene *s, int32 recipientNodeHandle, int32 graphNodeHandle);
|
||||
SceneGraphNode *getSceneGraphNodeForEntity(Scene *s, int32 entityHandle);
|
||||
void show(Scene *s, uint32 graphNodeHandle);
|
||||
void hide(Scene *s, uint32 graphNodeHandle);
|
||||
|
||||
#endif
|
||||
2
src/world/world.c
Normal file
2
src/world/world.c
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "scene.c"
|
||||
#include "camera.c"
|
||||
8
src/world/world.h
Normal file
8
src/world/world.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef WORLD_H
|
||||
#define WORLD_H
|
||||
|
||||
#include "scene.h"
|
||||
#include "camera.h"
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user