From e5a7e4e56710a22d3c0d9fe18e4f5a520d190787 Mon Sep 17 00:00:00 2001 From: Andreew Gregory Date: Thu, 25 Dec 2025 02:09:28 +0300 Subject: [PATCH] Moved r0 to r4. Which means now we run tests using HASKELL. YAY --- CMakeLists.txt | 24 +- Makefile | 70 +-- src/l1/anne/geom.h | 2 +- src/l1/anne/pixel_masses.h | 3 +- src/l1/anne/some_tests.h | 6 - src/l2/alice/assets.h | 157 +++++++ src/l2/allie/Allie.hs | 5 + src/l2/{tests/r0/r0.c => allie/allie.c} | 409 +++++++++++++++--- src/l2/anne/codegen.c | 10 + src/l2/{tests/r0/r0_assets.h => anne/r4.h} | 348 +++------------ src/l2/anne/r4_models.h | 156 +++++++ src/l2/lucy/glyph_cache.h | 1 + src/l2/margaret/vulkan_utils.h | 4 +- src/l2/tests/r0/r0_scene.h | 328 -------------- src/l2/tests/r0/r0_tex_init_prep.c | 45 -- .../tests/r0/textures/log_1_10_4_diffuse.png | Bin 57154 -> 0 bytes .../tests/r0/textures/log_1_10_4_specular.png | Bin 57154 -> 0 bytes .../tests/r0/textures/log_2_1_6_diffuse.png | Bin 2894 -> 0 bytes .../tests/r0/textures/log_2_1_6_specular.png | Bin 2894 -> 0 bytes .../tests/r0/textures/log_5_5_10_diffuse.png | Bin 23803 -> 0 bytes .../tests/r0/textures/log_5_5_10_specular.png | Bin 23803 -> 0 bytes .../r0 => l3}/fonts/DMSerifText-Regular.ttf | Bin src/l3/r4/R4.hs | 4 + .../r0 => l3}/textures/log_10_2_6_diffuse.png | Bin .../textures/log_10_2_6_specular.png | Bin .../0/0.frag => l_adele/alice/0gen/0gen.frag} | 0 .../0/0.vert => l_adele/alice/0gen/0gen.vert} | 0 .../0b/0b.frag => l_adele/alice/0sh/0sh.frag} | 0 .../0b/0b.vert => l_adele/alice/0sh/0sh.vert} | 0 .../shaders/glsl => l_adele/alice}/1/1.frag | 0 .../shaders/glsl => l_adele/alice}/1/1.vert | 0 31 files changed, 795 insertions(+), 777 deletions(-) create mode 100644 src/l2/alice/assets.h create mode 100644 src/l2/allie/Allie.hs rename src/l2/{tests/r0/r0.c => allie/allie.c} (84%) create mode 100644 src/l2/anne/codegen.c rename src/l2/{tests/r0/r0_assets.h => anne/r4.h} (69%) create mode 100644 src/l2/anne/r4_models.h delete mode 100644 src/l2/tests/r0/r0_scene.h delete mode 100644 src/l2/tests/r0/r0_tex_init_prep.c delete mode 100644 src/l2/tests/r0/textures/log_1_10_4_diffuse.png delete mode 100644 src/l2/tests/r0/textures/log_1_10_4_specular.png delete mode 100644 src/l2/tests/r0/textures/log_2_1_6_diffuse.png delete mode 100644 src/l2/tests/r0/textures/log_2_1_6_specular.png delete mode 100644 src/l2/tests/r0/textures/log_5_5_10_diffuse.png delete mode 100644 src/l2/tests/r0/textures/log_5_5_10_specular.png rename src/{l2/tests/r0 => l3}/fonts/DMSerifText-Regular.ttf (100%) create mode 100644 src/l3/r4/R4.hs rename src/{l2/tests/r0 => l3}/textures/log_10_2_6_diffuse.png (100%) rename src/{l2/tests/r0 => l3}/textures/log_10_2_6_specular.png (100%) rename src/{l2/tests/r0/shaders/glsl/0/0.frag => l_adele/alice/0gen/0gen.frag} (100%) rename src/{l2/tests/r0/shaders/glsl/0/0.vert => l_adele/alice/0gen/0gen.vert} (100%) rename src/{l2/tests/r0/shaders/glsl/0b/0b.frag => l_adele/alice/0sh/0sh.frag} (100%) rename src/{l2/tests/r0/shaders/glsl/0b/0b.vert => l_adele/alice/0sh/0sh.vert} (100%) rename src/{l2/tests/r0/shaders/glsl => l_adele/alice}/1/1.frag (100%) rename src/{l2/tests/r0/shaders/glsl => l_adele/alice}/1/1.vert (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index e51bf5d..c7df01f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,9 +19,9 @@ execute_process( #add_compile_options("-I/nix/store/2hm4rjvywd00p417y43i9rzx8v793qi0-pipewire-1.4.5-dev/include/pipewire-0.3 -I/nix/store/2hm4rjvywd00p417y43i9rzx8v793qi0-pipewire-1.4.5-dev/include/spa-0.2") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \ - -I /nix/store/2hm4rjvywd00p417y43i9rzx8v793qi0-pipewire-1.4.5-dev/include/pipewire-0.3 \ - -I /nix/store/2hm4rjvywd00p417y43i9rzx8v793qi0-pipewire-1.4.5-dev/include/spa-0.2 ") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \ +# -I /nix/store/2hm4rjvywd00p417y43i9rzx8v793qi0-pipewire-1.4.5-dev/include/pipewire-0.3 \ +# -I /nix/store/2hm4rjvywd00p417y43i9rzx8v793qi0-pipewire-1.4.5-dev/include/spa-0.2 ") add_compile_definitions(_POSIX_C_SOURCE=200112L) add_compile_definitions(_GNU_SOURCE) @@ -40,12 +40,6 @@ target_link_libraries(3_test -lm) add_executable(codegen_l1_5 src/l1_5/anne/codegen.c) -add_executable(0_render_test src/l2/tests/r0/r0.c gen/l_wl_protocols/xdg-shell-private.c) -target_link_libraries(0_render_test -lvulkan -lwayland-client -lm -lxkbcommon -lpng -lfreetype) - -add_executable(0r_tex_init_prep src/l2/tests/r0/r0_tex_init_prep.c) -target_link_libraries(0r_tex_init_prep -lm -lpng) - #add_executable(1_render_test src/l2/tests/r1/r1.c gen/l_wl_protocols/xdg-shell-private.c) #target_link_libraries(1_render_test -lwayland-client -lrt -lm -lxkbcommon) # @@ -56,13 +50,15 @@ target_link_libraries(0r_tex_init_prep -lm -lpng) #target_link_libraries(3_render_test -lwayland-client -lm -lvulkan -lxkbcommon) #add_executable(l2t0_2 src/l2/tests/data_structures/t0_2.c) // todo: I will get back -add_executable(l2t0 src/l2/tests/data_structures/t0.c) -add_executable(l2t0_3 src/l2/tests/data_structures/t0_3.c) +#add_executable(l2t0 src/l2/tests/data_structures/t0.c) +#add_executable(l2t0_3 src/l2/tests/data_structures/t0_3.c) #add_executable(l2t2 src/l2/tests/data_structures/t2.c) #add_executable(l2t0 src/l2/tests/data_structures/t0.c) #add_executable(l2t1 src/l2/tests/data_structures/t1.c) -#add_executable(H src/l2/tests/r_alg/H.c) -#add_executable(I src/l2/tests/r_alg/I.c) -#add_executable(J src/l2/tests/r_alg/J.c) +add_executable(l2_tex_gen src/l2/anne/codegen.c) +target_link_libraries(l2_tex_gen -lm -lpng) + +add_executable(l2_allie_in_r4 src/l2/allie/allie.c gen/l_wl_protocols/xdg-shell-private.c) +target_link_libraries(l2_allie_in_r4 -lvulkan -lwayland-client -lm -lxkbcommon -lpng -lfreetype) diff --git a/Makefile b/Makefile index 0342987..f7d50ee 100644 --- a/Makefile +++ b/Makefile @@ -24,8 +24,15 @@ cc := gcc wl_protocols := $(shell pkg-config --variable=pkgdatadir wayland-protocols) libpipewire_flags := $(shell pkg-config --cflags --libs libpipewire-0.3) -xdg_shell_private := gen/l_wl_protocols/xdg-shell-private.c -l_wl_protocols := gen/l_wl_protocols/xdg-shell-client.h $(xdg_shell_private) +xdg_shell_private_c := gen/l_wl_protocols/xdg-shell-private.c +xdg_shell_client_h := gen/l_wl_protocols/xdg-shell-client.h +xdg_shell_private_o := out/l_wl_protocols/xdg-shell-private.o + +l_wl_protocols := $(xdg_shell_client_h) $(xdg_shell_private_c) + +$(xdg_shell_private_o): $(l_wl_protocols) + mkdir -p out/l_wl_protocols + $(cc) $(cflags) -o $@ -c $(xdg_shell_private_c) -lwayland-client out/l1/codegen: src/l1/anne/codegen.c $(HEADERS_src_l1) mkdir -p out/l1 @@ -63,18 +70,29 @@ gen/l_wl_protocols/xdg-shell-private.c: $(wl_protocols)/stable/xdg-shell/xdg-she .PHONY: gen/l_wl_protocols gen/l_wl_protocols : $(l_wl_protocols) +out/l2/codegen: src/l2/anne/codegen.c $(HEADERS_src_l2) + mkdir -p out/l2 + $(cc) $(cflags) -o $@ $< -lm -lpng -compile_vert_shader = glslc -o gen/l_adele/$(1)/vert.spv src/l_adele/$(1)/$(1).vert -compile_frag_shader = glslc -o gen/l_adele/$(1)/frag.spv src/l_adele/$(1)/$(1).frag +gen/l2/dorothy.txt: out/l2/codegen + mkdir -p gen + cd gen && ../out/l2/codegen + +# First argument is path (relative to src/l_adele), second argument is a name of shader files in shader folder +compile_vert_shader = glslc -o gen/l_adele/$(1)/vert.spv src/l_adele/$(1)/$(2).vert +compile_frag_shader = glslc -o gen/l_adele/$(1)/frag.spv src/l_adele/$(1)/$(2).frag define compile_shader mkdir -p gen/l_adele/$(1) -$(call compile_vert_shader,$(1)) -$(call compile_frag_shader,$(1)) +$(call compile_vert_shader,$(1),$(2)) +$(call compile_frag_shader,$(1),$(2)) endef gen/l_adele/dorothy.txt: $(ASSETS_src_l_adele) - $(call compile_shader,lucy) + $(call compile_shader,lucy,lucy) + $(call compile_shader,alice/0gen,0gen) + $(call compile_shader,alice/0sh,0sh) + $(call compile_shader,alice/1,1) touch gen/l_adele/dorothy.txt out/l2/t0: src/l2/tests/data_structures/t0.c $(HEADERS_gen_l1_5) @@ -85,26 +103,10 @@ out/l2/t0: src/l2/tests/data_structures/t0.c $(HEADERS_gen_l1_5) run_l2_t0: out/l2/t0 cd src/l2/tests/data_structures && ../../../../out/l2/t0 -out/l2/r0: src/l2/tests/r0/r0.c $(HEADERS_src_l2) $(l_wl_protocols) - mkdir -p out/l2 - $(cc) $(cflags) -o $@ $< $(xdg_shell_private) -lvulkan -lm -lxkbcommon -lwayland-client -lpng - -out/l2/r0_tex_init_prep: src/l2/tests/r0/r0_tex_init_prep.c $(HEADERS_src_l2) - mkdir -p out/l2 - $(cc) $(cflags) -o $@ $< -lm -lpng - -.PHONY: run_r0 -run_r0: out/l2/r0 - cd src/l2/tests/r0 && ../../../../out/l2/r0 - -.PHONY: run_r0_tex_init_prep -run_r0_tex_init_prep: out/l2/r0_tex_init_prep - cd src/l2/tests/r0 && ../../../../out/l2/r0_tex_init_prep - out/l2/r1: src/l2/tests/r1/r1.c $(HEADERS_src_l2) $(l_wl_protocols) mkdir -p out/l2 - $(cc) $(cflags) -o $@ $< $(xdg_shell_private) -lwayland-client -lrt -lxkbcommon -lm + $(cc) $(cflags) -o $@ $< $(xdg_shell_private_c) -lwayland-client -lrt -lxkbcommon -lm .PHONY: run_r1 run_r1: out/l2/r1 @@ -113,21 +115,35 @@ run_r1: out/l2/r1 out/l2/r2: src/l2/tests/r2/r2a.c $(HEADERS_src_l2) $(l_wl_protocols) mkdir -p out/l2 - $(cc) $(cflags) -o $@ $< $(xdg_shell_private) -lwayland-client -lrt -lxkbcommon -lm $(libpipewire_flags) + $(cc) $(cflags) -o $@ $< $(xdg_shell_private_c) -lwayland-client -lrt -lxkbcommon -lm $(libpipewire_flags) .PHONY: run_r2 run_r2: out/l2/r2 ./out/l2/r2 - out/l2/r3: src/l2/tests/r3/r3.c $(HEADERS_src_l2) $(l_wl_protocols) mkdir -p out/l2 - $(cc) $(cflags) -o $@ $< $(xdg_shell_private) -lwayland-client -lrt -lxkbcommon -lm -lvulkan + $(cc) $(cflags) -o $@ $< $(xdg_shell_private_c) -lwayland-client -lrt -lxkbcommon -lm -lvulkan .PHONY: run_r3 run_r3: out/l2/r3 ./out/l2/r3 +# Whoever needs this will also need out/l_wl_protocols +out/l2/allie.o: src/l2/allie/allie.c $(HEADERS_src_l2) $(xdg_shell_client_h) gen/l_adele/dorothy.txt $(HEADERS_gen_l2) + mkdir -p out/l2 + $(cc) $(cflags) -o $@ -c $< -lvulkan -lm -lxkbcommon -lwayland-client -lpng -lfreetype + +full_allie_obj := out/l2/allie.o $(xdg_shell_private_o) + +out/l3/r4: src/l3/r4/R4.hs src/l2/allie/Allie.hs $(full_allie_obj) + mkdir -p out/l3 + ghc -isrc/l2/allie -hidir out/l3/ -odir out/l3 -o $@ $< $(full_allie_obj) \ + -lvulkan -lm -lxkbcommon -lwayland-client -lpng -lfreetype + +.PHONY: run_r4 +run_r4: out/l3/r4 + ./out/l3/r4 .PHONY: clean clean: diff --git a/src/l1/anne/geom.h b/src/l1/anne/geom.h index f975586..76c6a9e 100644 --- a/src/l1/anne/geom.h +++ b/src/l1/anne/geom.h @@ -179,7 +179,7 @@ NODISCARD VecU8 generate_xmatnm_struct_and_methods( for (int x = 0; x < cols; x++) { VecU8_append_vec(&res, VecU8_fmt(SPACE "%s %s;\n", xvecm, vec_field_name(x))); if (sv) { - VecU8_append_vec(&res, VecU8_format(SPACE "char _padding_%d[%d];\n", x, 16 - sv)); + VecU8_append_vec(&res, VecU8_fmt(SPACE "char _padding_%u[%u];\n", (U64)x, (U64)(16 - sv))); } } VecU8_append_vec(&res, VecU8_fmt("} %s;\n\n", xmatnm)); diff --git a/src/l1/anne/pixel_masses.h b/src/l1/anne/pixel_masses.h index 307b771..a41ff0a 100644 --- a/src/l1/anne/pixel_masses.h +++ b/src/l1/anne/pixel_masses.h @@ -29,7 +29,8 @@ NODISCARD VecU8 generate_texture_data_struct_and_necc_methods(SpanU8 tex, SpanU8 /* Method _new() */ VecU8_append_vec(&res, VecU8_fmt( "%s %s_new(U32 width, U32 height) {\n" - SPACE "assert(!(SIZE_MAX / width / height < 100 || UINT32_MAX / width < 10 || UINT32_MAX / height < 10));\n" + SPACE "assert(width == 0 || height == 0 ||\n" + SPACE SPACE "!(SIZE_MAX / width / height < 100 || UINT32_MAX / width < 10 || UINT32_MAX / height < 10));\n" SPACE "return (%s){.pixels = %s_new_zeroinit((size_t)width * height), .width = width, .height = height};\n" "}\n\n", tex, tex, tex, pixvec)); /* Method _drop() */ diff --git a/src/l1/anne/some_tests.h b/src/l1/anne/some_tests.h index cfbdc2e..334e75d 100644 --- a/src/l1/anne/some_tests.h +++ b/src/l1/anne/some_tests.h @@ -29,12 +29,6 @@ void generate_headers_for_r0_r1_r2_r3() { .T = cstr("PlayingSound"), .vec_extended = true }); } - mkdir_nofail("l1/eve/r_alg"); - { /* r_alg. Wow! Even these freaks are here! */ - SpanU8 ns = cstr("r_alg"); - generate_eve_span_company_for_primitive(l, ns, cstr("I_FishNode"), true, false); - generate_eve_span_company_for_primitive(l, ns, cstr("J_AlphaVertex"), true, false); - } mkdir_nofail("l1/eve/ds_test"); { /* This structure is needed for testing purposes only */ generate_eve_span_company_for_primitive(l, cstr("ds_test"), cstr("RefRBTreeNode_S64"), true, false); diff --git a/src/l2/alice/assets.h b/src/l2/alice/assets.h new file mode 100644 index 0000000..e7e12ff --- /dev/null +++ b/src/l2/alice/assets.h @@ -0,0 +1,157 @@ +#ifndef prototype1_src_l2_alice_assets_h +#define prototype1_src_l2_alice_assets_h + +#include "../marie/graphics_geom.h" +#include "../../../gen/l1/VecAndSpan_U32.h" +#include "../../../gen/l1/VecAndSpan_U8.h" + +typedef struct { + vec3 pos; + vec2 tex; +} GenericMeshVertexInc; + +#include "../../../gen/l1/eve/r0/VecAndSpan_GenericMeshVertexInc.h" + +typedef struct { + GenericMeshVertexInc base; + vec3 norm; + vec3 tang_U; + vec3 tang_V; +} GenericMeshVertex; + +typedef struct { + VecGenericMeshVertexInc vertices; + VecU32 indexes; +} GenericMeshTopology; + +void GenericMeshTopology_drop(GenericMeshTopology self) { + VecGenericMeshVertexInc_drop(self.vertices); + VecU32_drop(self.indexes); +} + +GenericMeshTopology GenericMeshTopology_clone(const GenericMeshTopology* self) { + return (GenericMeshTopology){.vertices = VecGenericMeshVertexInc_clone(&self->vertices), .indexes = VecU32_clone(&self->indexes)}; +} + +typedef struct { + GenericMeshTopology topology; + VecU8 diffuse_texture_path; + VecU8 normal_texture_path; + VecU8 specular_texture_path; +} GenericMeshInSceneTemplate; + +void GenericMeshInSceneTemplate_drop(GenericMeshInSceneTemplate self) { + GenericMeshTopology_drop(self.topology); + VecU8_drop(self.diffuse_texture_path); + VecU8_drop(self.normal_texture_path); + VecU8_drop(self.specular_texture_path); +} + +GenericMeshInSceneTemplate GenericMeshInSceneTemplate_clone(const GenericMeshInSceneTemplate* self) { + return (GenericMeshInSceneTemplate){.topology = GenericMeshTopology_clone(&self->topology), + .diffuse_texture_path = VecU8_clone(&self->diffuse_texture_path), + .normal_texture_path = VecU8_clone(&self->normal_texture_path), + .specular_texture_path = VecU8_clone(&self->specular_texture_path)}; +} + +#include "../../../gen/l1/eve/r0/VecGenericMeshInSceneTemplate.h" + +typedef struct { + mat4 model_t; +} GenericMeshInstanceInc; + +typedef struct { + GenericMeshInstanceInc base; + mat3 normal_t; +} GenericMeshInstance; + + +typedef struct { + vec3 pos; +} ShinyMeshVertexInc; + +typedef struct { + ShinyMeshVertexInc base; + vec3 normal; +} ShinyMeshVertex; +#include "../../../gen/l1/eve/r0/VecAndSpan_ShinyMeshVertexInc.h" + +typedef struct { + VecShinyMeshVertexInc vertices; + VecU32 indexes; +} ShinyMeshTopology; + +void ShinyMeshTopology_drop(ShinyMeshTopology self) { + VecShinyMeshVertexInc_drop(self.vertices); + VecU32_drop(self.indexes); +} + +ShinyMeshTopology ShinyMeshTopology_clone(const ShinyMeshTopology* self) { + return (ShinyMeshTopology){.vertices = VecShinyMeshVertexInc_clone(&self->vertices), + VecU32_clone(&self->indexes)}; +} + +#include "../../../gen/l1/eve/r0/VecShinyMeshTopology.h" + +typedef struct{ + mat4 model_t; + vec3 color_off; + vec3 color_on; +} ShinyMeshInstanceInc; + +typedef struct { + ShinyMeshInstanceInc base; + mat3 normal_t; +} ShinyMeshInstance; + +typedef struct { + vec2 win_scale; +} Pipeline1PushRangeVertex; + +typedef struct { + float gamma_correction_factor; + float hdr_factor; + float lsd_factor; + float anim_time; +} Pipeline1PushRangeFragment; + +typedef struct { + vec3 pos; + char _padding_0[4]; + vec3 dir; + char _padding_1[4]; + vec3 color; + char _padding_2[4]; + float d; + char _padding_3[12]; +} Pipeline0Spotlight; + +typedef struct { + vec3 pos; + char _padding_0[4]; + vec3 color; + char _padding_1[4]; +} Pipeline0PointLight; + +typedef struct { + VecGenericMeshInSceneTemplate generic_models; + VecShinyMeshTopology shiny_models; +} SceneTemplate; + +void SceneTemplate_drop(SceneTemplate self) { + VecGenericMeshInSceneTemplate_drop(self.generic_models); +} + +#define pipeline_0_ubo_point_light_max_count 120 +#define pipeline_0_ubo_spotlight_max_count 20 + +typedef struct { + int point_light_count; + int spotlight_count; + char _padding_1[8]; + Pipeline0PointLight point_light_arr[pipeline_0_ubo_point_light_max_count]; + Pipeline0Spotlight spotlight_arr[pipeline_0_ubo_spotlight_max_count]; +} Pipeline0UBO; + + +#endif \ No newline at end of file diff --git a/src/l2/allie/Allie.hs b/src/l2/allie/Allie.hs new file mode 100644 index 0000000..a8cb446 --- /dev/null +++ b/src/l2/allie/Allie.hs @@ -0,0 +1,5 @@ +{-# LANGUAGE ForeignFunctionInterface #-} + +module Allie (allieRun) where + +foreign import ccall "allie_run" allieRun :: IO () diff --git a/src/l2/tests/r0/r0.c b/src/l2/allie/allie.c similarity index 84% rename from src/l2/tests/r0/r0.c rename to src/l2/allie/allie.c index 9a85865..2609e1b 100644 --- a/src/l2/tests/r0/r0.c +++ b/src/l2/allie/allie.c @@ -1,17 +1,341 @@ -#include "../../margaret/vulkan_utils.h" -#include "../../../../gen/l1/geom.h" -#include -#include -#include "../../../l1/system/fileio.h" -#include -#include "r0_scene.h" + +#include "../alice/assets.h" +#include "../anne/r4_models.h" // todo: this is very illegal, this is very-very illegal. Please, don't do this +#include "../../l1/marie/geom_alg_utils.h" +#include "../margaret/vulkan_utils.h" +#include "../lucy/glyph_render.h" + +typedef struct { + U64 count; + MargaretSubbuf staging_busy; + MargaretSubbuf staging_updatable; + MargaretSubbuf device_local; + U64 cap; + // todo: delete this crap. This crap turned out to be completely useless. It is another one of my very very dumb ideas + // todo: remove updatable buffer, fill staging buffer in main thread +} PatriciaBuf; + +void PatriciaBuf_swap_staging(PatriciaBuf* self){ + MargaretSubbuf t = self->staging_updatable; + self->staging_updatable = self->staging_busy; + self->staging_busy = t; +} + +typedef struct { + size_t indexes; + + MargaretSubbuf staging_vbo; + MargaretSubbuf staging_ebo; + + // todo: replace TextureDataXXX with MargaretPngPromises + TextureDataR8G8B8A8 pixels_diffuse; + TextureDataR8G8B8A8 pixels_normal; + TextureDataR8 pixels_specular; + + MargaretSubbuf staging_diffuse_tex_buf; + MargaretSubbuf staging_normal_tex_buf; + MargaretSubbuf staging_specular_tex_buf; + + MargaretSubbuf vbo; + MargaretSubbuf ebo; + PatriciaBuf instance_attr; + + // todo: store dimensions of these images + MargaretImg diffuse_texture; + MargaretImg normal_texture; + MargaretImg specular_texture; +} GenericModelOnSceneMem; + +#include "../../../gen/l1/eve/r0/VecGenericModelOnSceneMem.h" + +void GenericModelOnSceneMem_set(GenericModelOnSceneMem* self, size_t instance, GenericMeshInstanceInc uncomp){ + assert(instance < self->instance_attr.count); + GenericMeshInstance* staging = (GenericMeshInstance*)MargaretSubbuf_get_mapped(&self->instance_attr.staging_updatable); + staging[instance].base = uncomp; + mat4 tr_inv = mat4_transpose(mat4_inverse(uncomp.model_t)); + staging[instance].normal_t = mat3_new( + tr_inv.x.x, tr_inv.y.x, tr_inv.z.x, + tr_inv.x.y, tr_inv.y.y, tr_inv.z.y, + tr_inv.x.z, tr_inv.y.z, tr_inv.z.z ); +} + +typedef struct { + size_t indexes; + + MargaretSubbuf staging_vbo; + MargaretSubbuf staging_ebo; + + MargaretSubbuf vbo; + MargaretSubbuf ebo; + PatriciaBuf instance_attr; +} ShinyModelOnSceneMem; + +#include "../../../gen/l1/eve/r0/VecShinyModelOnSceneMem.h" + +void ShinyModelOnSceneMem_set(ShinyModelOnSceneMem* self, size_t instance, ShinyMeshInstanceInc uncomp){ + assert(instance < self->instance_attr.count); + ShinyMeshInstance* staging = (ShinyMeshInstance*)MargaretSubbuf_get_mapped(&self->instance_attr.staging_updatable); + staging[instance].base = uncomp; + mat4 tr_inv = mat4_transpose(mat4_inverse(uncomp.model_t)); + staging[instance].normal_t = mat3_new( + tr_inv.x.x, tr_inv.y.x, tr_inv.z.x, + tr_inv.x.y, tr_inv.y.y, tr_inv.z.y, + tr_inv.x.z, tr_inv.y.z, tr_inv.z.z ); +} + +typedef struct { + float fov; + mat3 cam_basis; + vec3 pos; + + float speed; + float sensitivity; + float pitch_cap; +} CamControlInfo; + +void CamControlInfo_forward(CamControlInfo* self, float fl) { + self->pos = vec3_add_vec3(self->pos, vec3_mul_scal(self->cam_basis.z, -self->speed * fl)); +} + +void CamControlInfo_backward(CamControlInfo* self, float fl) { + self->pos = vec3_add_vec3(self->pos, vec3_mul_scal(self->cam_basis.z, self->speed * fl)); +} + +void CamControlInfo_left(CamControlInfo* self, float fl) { + self->pos = vec3_add_vec3(self->pos, vec3_mul_scal(self->cam_basis.x, -self->speed * fl)); +} + +void CamControlInfo_right(CamControlInfo* self, float fl) { + self->pos = vec3_add_vec3(self->pos, vec3_mul_scal(self->cam_basis.x, self->speed * fl)); +} + +void CamControlInfo_down(CamControlInfo* self, float fl) { + self->pos = vec3_add_vec3(self->pos, vec3_mul_scal((vec3){0, -1, 0}, self->speed * fl)); +} + +void CamControlInfo_up(CamControlInfo* self, float fl) { + self->pos = vec3_add_vec3(self->pos, vec3_mul_scal((vec3){0, 1, 0}, self->speed * fl)); +} + +CamControlInfo CamControlInfo_new() { + return (CamControlInfo){ + .fov = 1.5f, .cam_basis = marie_simple_camera_rot_m_basis_in_cols(0, 0, 0), .pos = {0, 0, 0}, + .speed = 6.7f, .sensitivity = 0.5f * M_PIf / 180, .pitch_cap = M_PIf * 0.49f + }; +} + +void CamControlInfo_update_direction(CamControlInfo* self, int win_width, int win_height, int pointer_x, int pointer_y) { + float yaw = ((float)win_width / 2 - (float)pointer_x) * self->sensitivity; + float pitch = marie_clamp_float( + ((float)win_height / 2 - (float)pointer_y) * self->sensitivity, + -self->pitch_cap, self->pitch_cap + ); + self->cam_basis = marie_simple_camera_rot_m_basis_in_cols(yaw, pitch, 0); +} + +typedef struct { + MargaretSubbuf staging_busy; + MargaretSubbuf staging_updatable; + MargaretSubbuf device_local; +} Pipeline0Transfer; + +// Just for a test in r0 +typedef struct { + mat3 rotation; + vec3 pos; + float scale; + vec3 color_on; +} ObjectInfo; + +#include "../../../gen/l1/eve/r0/VecObjectInfo.h" + +/* Non copyable */ +typedef struct { + VecGenericModelOnSceneMem generic_models; + VecShinyModelOnSceneMem shiny_models; + + VkClearColorValue color; + float gamma_correction_factor; + float hdr_factor; + float lsd_factor; + float anim_time; // A timer, passed to functions that push push constants + + /* point_light_vec_len and spotlight_vec_len are stored in staging (and also device local) buffers */ + Pipeline0Transfer pipeline0_ubo; + + CamControlInfo cam; + + VecObjectInfo smeshnyavka_1; + VecObjectInfo smeshnyavka_3; + + VecU8 text_on_screen; +} Scene; + +ShinyMeshInstanceInc ShinyMeshInstanceInc_from_ObjectInfo(const ObjectInfo* oi){ + return (ShinyMeshInstanceInc){ + .model_t = mat4_mul_mat4(marie_translation_mat4(oi->pos), + mat4_mul_mat4(marie_3d_scal_mat4(oi->scale), marie_mat3_to_mat4(oi->rotation))), + .color_on = oi->color_on, .color_off = {1, 0.4f, 0.5f} + }; +} + +// todo: remove this shit +void Scene_add_smeshnyavka_3(Scene* self, ObjectInfo oi){ + ShinyModelOnSceneMem* model_sh = VecShinyModelOnSceneMem_mat(&self->shiny_models, 0); + size_t ni = self->smeshnyavka_3.len; + assert(ni < model_sh->instance_attr.cap); + VecObjectInfo_append(&self->smeshnyavka_3, oi); + model_sh->instance_attr.count = ni + 1; + ShinyModelOnSceneMem_set(model_sh, ni, ShinyMeshInstanceInc_from_ObjectInfo(&oi)); +} + +// todo: remove this shit (and rewrite everything in haskell) +void Scene_update_smeshnyavka_3(Scene* self, size_t sh_id){ + assert(sh_id < self->smeshnyavka_3.len); + const ObjectInfo* oi = VecObjectInfo_at(&self->smeshnyavka_3, sh_id); + ShinyModelOnSceneMem* model_sh = VecShinyModelOnSceneMem_mat(&self->shiny_models, 0); + ShinyModelOnSceneMem_set(model_sh, sh_id, ShinyMeshInstanceInc_from_ObjectInfo(oi)); +} + +GenericMeshInstanceInc GenericMeshInstanceInc_from_ObjectInfo(const ObjectInfo* oi){ + return (GenericMeshInstanceInc){ + .model_t = mat4_mul_mat4(marie_translation_mat4(oi->pos), + mat4_mul_mat4(marie_3d_scal_mat4(oi->scale), marie_mat3_to_mat4(oi->rotation))), + }; +} + +// todo: remove this shit +void Scene_add_smeshnyavka_1(Scene* self, ObjectInfo oi){ + GenericModelOnSceneMem* model = VecGenericModelOnSceneMem_mat(&self->generic_models, 0); + size_t ni = self->smeshnyavka_1.len; + assert(ni < model->instance_attr.cap); + VecObjectInfo_append(&self->smeshnyavka_1, oi); + model->instance_attr.count = ni + 1; + GenericModelOnSceneMem_set(model, ni, GenericMeshInstanceInc_from_ObjectInfo(&oi)); +} + +// todo: remove this shit +void Scene_update_smeshnyavka_1(Scene* self, size_t sh_id){ + assert(sh_id < self->smeshnyavka_1.len); + const ObjectInfo* oi = VecObjectInfo_at(&self->smeshnyavka_1, sh_id); + GenericModelOnSceneMem* model = VecGenericModelOnSceneMem_mat(&self->generic_models, 0); + GenericModelOnSceneMem_set(model, sh_id, GenericMeshInstanceInc_from_ObjectInfo(oi)); +} + + + +Scene Scene_new(VecGenericModelOnSceneMem generic_models, VecShinyModelOnSceneMem shiny_models, + Pipeline0Transfer pipeline0_ubo) { + return (Scene){.generic_models = generic_models, .shiny_models = shiny_models, + .color = {.float32 = {0, 0, 0, 1}}, + .gamma_correction_factor = 2.2f, .hdr_factor = 1, .lsd_factor = 0, .anim_time = 0, + .pipeline0_ubo = pipeline0_ubo, .cam = CamControlInfo_new(), + .smeshnyavka_1 = VecObjectInfo_new(), + .smeshnyavka_3 = VecObjectInfo_new(), // todo: remove this shit and rewrite everything in haskell + .text_on_screen = VecU8_new(), + }; +} + +void Scene_drop(Scene self) { + VecGenericModelOnSceneMem_drop(self.generic_models); + VecShinyModelOnSceneMem_drop(self.shiny_models); +} + +/* No buffer rerecording, no buffer beginning, no buffer ending */ +void SceneTemplate_copy_initial_model_topology_cmd_buf_recording( + const SceneTemplate* scene_template, const Scene* scene, VkCommandBuffer command_buffer) { + assert(scene_template->generic_models.len == scene->generic_models.len); + assert(scene_template->shiny_models.len == scene->shiny_models.len); + assert(scene_template->generic_models.len == scene->generic_models.len); + + for (size_t mi = 0; mi < scene_template->generic_models.len; mi++) { + const GenericMeshInSceneTemplate* mt = VecGenericMeshInSceneTemplate_at(&scene_template->generic_models, mi); + const GenericModelOnSceneMem *mm = VecGenericModelOnSceneMem_at(&scene->generic_models, mi); + + assert(mm->staging_vbo.len >= mt->topology.vertices.len * sizeof(GenericMeshVertex)); + assert(mm->vbo.len >= mt->topology.vertices.len * sizeof(GenericMeshVertex)); + GenericMeshVertex* staging_vbo = (GenericMeshVertex*)MargaretSubbuf_get_mapped(&mm->staging_vbo); + for (U64 i = 0; i < mt->topology.vertices.len; i++) { + staging_vbo[i].base = mt->topology.vertices.buf[i]; + } + assert(mt->topology.indexes.len % 3 == 0); + for (size_t ti = 0; ti * 3 < mt->topology.indexes.len; ti++) { + U32 v0 = mt->topology.indexes.buf[ti * 3 + 0]; + U32 v1 = mt->topology.indexes.buf[ti * 3 + 1]; + U32 v2 = mt->topology.indexes.buf[ti * 3 + 2]; + const GenericMeshVertexInc* A0 = VecGenericMeshVertexInc_at(&mt->topology.vertices, v0); + const GenericMeshVertexInc* A1 = VecGenericMeshVertexInc_at(&mt->topology.vertices, v1); + const GenericMeshVertexInc* A2 = VecGenericMeshVertexInc_at(&mt->topology.vertices, v2); + vec3 dp1 = vec3_minus_vec3(A1->pos, A0->pos); + vec3 dp2 = vec3_minus_vec3(A2->pos, A0->pos); + float du1 = A1->tex.x - A0->tex.x; + float dv1 = A1->tex.y - A0->tex.y; + float du2 = A2->tex.x - A0->tex.x; + float dv2 = A2->tex.y - A0->tex.y; + vec3 norm = vec3_normalize(vec3_cross(dp1, dp2)); + mat2x3 tang_U_V = mat3x2_transpose(mat2_mul_mat3x2( + mat2_inverse(mat2_new(du1, dv1, du2, dv2)), + mat2x3_transpose((mat2x3){.x = dp1, .y = dp2}) + )); + staging_vbo[v0].norm = staging_vbo[v1].norm = staging_vbo[v2].norm = norm; + staging_vbo[v0].tang_U = staging_vbo[v1].tang_U = staging_vbo[v2].tang_U = tang_U_V.x; + staging_vbo[v0].tang_V = staging_vbo[v1].tang_V = staging_vbo[v2].tang_V = tang_U_V.y; + } + margaret_rec_cmd_copy_buffer_one_to_one(command_buffer, &mm->staging_vbo, &mm->vbo); + + assert(mt->topology.indexes.len == mm->indexes); + size_t ebo_len = mt->topology.indexes.len * sizeof(U32); + assert(mm->ebo.len >= ebo_len); + U32* staging_ebo = (U32*)MargaretSubbuf_get_mapped(&mm->staging_ebo); + memcpy(staging_ebo, mt->topology.indexes.buf, ebo_len); + margaret_rec_cmd_copy_buffer_one_to_one(command_buffer, &mm->staging_ebo, &mm->ebo); + } + + for (size_t mi = 0; mi < scene_template->shiny_models.len; mi++) { + const ShinyMeshTopology* mt = VecShinyMeshTopology_at(&scene_template->shiny_models, mi); + const ShinyModelOnSceneMem *mm = VecShinyModelOnSceneMem_at(&scene->shiny_models, mi); + + assert(mm->staging_vbo.len >= mt->vertices.len * sizeof(ShinyMeshVertex)); + assert(mm->vbo.len >= mt->vertices.len * sizeof(ShinyMeshVertex)); + ShinyMeshVertex* staging_vbo = (ShinyMeshVertex*)MargaretSubbuf_get_mapped(&mm->staging_vbo); + for (U64 i = 0; i < mt->vertices.len; i++) { + staging_vbo[i].base = mt->vertices.buf[i]; + } + assert(mt->indexes.len % 3 == 0); + for (size_t ti = 0; ti * 3 < mt->indexes.len; ti++) { + U32 v0 = mt->indexes.buf[ti * 3 + 0]; + U32 v1 = mt->indexes.buf[ti * 3 + 1]; + U32 v2 = mt->indexes.buf[ti * 3 + 2]; + vec3 p0 = VecShinyMeshVertexInc_at(&mt->vertices, v0)->pos; + vec3 p1 = VecShinyMeshVertexInc_at(&mt->vertices, v1)->pos; + vec3 p2 = VecShinyMeshVertexInc_at(&mt->vertices, v2)->pos; + vec3 norm = vec3_normalize(vec3_cross(vec3_minus_vec3(p1, p0), vec3_minus_vec3(p2, p0))); + staging_vbo[v0].normal = staging_vbo[v1].normal = staging_vbo[v2].normal = norm; + } + + margaret_rec_cmd_copy_buffer_one_to_one(command_buffer, &mm->staging_vbo, &mm->vbo); + + assert(mt->indexes.len == mm->indexes); + size_t ebo_len = mt->indexes.len * sizeof(U32); + assert(mm->ebo.len >= ebo_len); + U32* staging_ebo = (U32*)MargaretSubbuf_get_mapped(&mm->staging_ebo); + memcpy(staging_ebo, mt->indexes.buf, ebo_len); + margaret_rec_cmd_copy_buffer_one_to_one(command_buffer, &mm->staging_ebo, &mm->ebo); + } +} + +/* Here ends the old r0_scene.h file + * And begins old r0.c file */ + #include -#include "../../../l1/system/fsmanip.h" -#include "../../../../gen/l_wl_protocols/xdg-shell-client.h" +#include "../../../gen/l1/margaret/png_pixel_masses.h" +#include "../lucy/glyph_cache.h" +#include +//#include "../../l1/system/fileio.h" +#include #include -#include "../../../l1/system/creating_child_proc.h" -#include "../../../../gen/l1/margaret/png_pixel_masses.h" -#include "../../lucy/glyph_cache.h" +#include "../../../gen/l_wl_protocols/xdg-shell-client.h" + typedef struct { VkPipelineLayout pipeline_layout; @@ -96,7 +420,7 @@ VkRenderPass create_render_pass_0(VkDevice logical_device, VkFormat colorbuffer_ } PipelineHands create_graphics_pipeline_0( - VkDevice device, VkRenderPass render_pass, uint32_t subpass + VkDevice device, SpanU8 root_dir, VkRenderPass render_pass, uint32_t subpass ) { VkDescriptorSetLayoutBinding bindings_for_my_descr_set_layout[] = { { @@ -158,8 +482,8 @@ PipelineHands create_graphics_pipeline_0( abortf("vkCreatePipelineLayout"); - VecU8 vert_bin_code = read_whole_file_or_abort(cstr("shaders/spv/0/vert.spv")); - VecU8 frag_bin_code = read_whole_file_or_abort(cstr("shaders/spv/0/frag.spv")); + VecU8 vert_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/0gen/vert.spv", root_dir)); + VecU8 frag_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/0gen/frag.spv", root_dir)); VkVertexInputBindingDescription vertex_bindings[2] = { { @@ -250,7 +574,7 @@ PipelineHands create_graphics_pipeline_0( } PipelineHands create_graphics_pipeline_0_b( - VkDevice device, VkRenderPass render_pass, uint32_t subpass + VkDevice device, SpanU8 root_dir, VkRenderPass render_pass, uint32_t subpass ) { VkDescriptorSetLayout my_descriptor_set_layout; check(vkCreateDescriptorSetLayout(device, &(VkDescriptorSetLayoutCreateInfo){ @@ -284,8 +608,8 @@ PipelineHands create_graphics_pipeline_0_b( }, NULL, &pipeline_layout) == VK_SUCCESS); - VecU8 vert_bin_code = read_whole_file_or_abort(cstr("shaders/spv/0b/vert.spv")); - VecU8 frag_bin_code = read_whole_file_or_abort(cstr("shaders/spv/0b/frag.spv")); + VecU8 vert_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/0sh/vert.spv", root_dir)); + VecU8 frag_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/0sh/frag.spv", root_dir)); VkVertexInputBindingDescription vertex_bindings[2] = { { @@ -425,7 +749,7 @@ VkRenderPass create_render_pass_1(VkDevice logical_device, VkFormat image_format PipelineHands create_graphics_pipeline_1( - VkDevice device, VkRenderPass render_pass, uint32_t subpass + VkDevice device, SpanU8 root_dir, VkRenderPass render_pass, uint32_t subpass ) { VkDescriptorSetLayoutBinding bindings_for_my_descr_set_layout[] = { @@ -460,8 +784,8 @@ PipelineHands create_graphics_pipeline_1( }, NULL, &pipeline_layout) == VK_SUCCESS); - VecU8 vert_bin_code = read_whole_file_or_abort(cstr("shaders/spv/1/vert.spv")); - VecU8 frag_bin_code = read_whole_file_or_abort(cstr("shaders/spv/1/frag.spv")); + VecU8 vert_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/1/vert.spv", root_dir)); + VecU8 frag_bin_code = read_file_by_path(VecU8_fmt("%s/gen/l_adele/alice/1/frag.spv", root_dir)); VkPipeline pipeline = margaret_create_triangle_pipeline_one_attachment(device, render_pass, subpass, (MargaretMostImportantPipelineOptions){ @@ -542,7 +866,7 @@ typedef struct { * Because I am to lazy to create two set layouts for generic model pipeline */ VkDescriptorSet p_0a_set_0; } GenericModelTexVulkPointers; -#include "../../../../gen/l1/eve/r0/VecGenericModelTexVulkPointers.h" +#include "../../../gen/l1/eve/r0/VecGenericModelTexVulkPointers.h" void reset_and_record_command_buffer_0( VkCommandBuffer command_buffer, VkRenderPass render_pass_0, @@ -1478,32 +1802,8 @@ static const struct wl_callback_listener main_h_wl_surface_frame_listener = { .done = main_h_wl_surface_frame_done, }; -void compile_shader_dir(SpanU8 name, bool have_geom) { - mkdir_nofail("shaders/spv"); - VecU8 spv_shader_dir_name = VecU8_fmt("shaders/spv/%s%c", name, 0); - mkdir_nofail((CSTR)spv_shader_dir_name.buf); - VecU8_drop(spv_shader_dir_name); - // todo: write a function that takes a SpanU8 - VecU8 vert_cmd = VecU8_fmt("glslc -o shaders/spv/%s/vert.spv shaders/glsl/%s/%s.vert%c", name, name, name, 0); - calling_system_func_nofail((CSTR)vert_cmd.buf); - VecU8_drop(vert_cmd); - if (have_geom) { - VecU8 geom_cmd = VecU8_fmt("glslc -o shaders/spv/%s/geom.spv shaders/glsl/%s/%s.geom%c", name, name, name, 0); - calling_system_func_nofail((CSTR)geom_cmd.buf); - VecU8_drop(geom_cmd); - } - VecU8 frag_cmd = VecU8_fmt("glslc -o shaders/spv/%s/frag.spv shaders/glsl/%s/%s.frag%c", name, name, name, 0); - calling_system_func_nofail((CSTR)frag_cmd.buf); - VecU8_drop(frag_cmd); -} - - -int main() { - compile_shader_dir(cstr("0"), false); - compile_shader_dir(cstr("0b"), false); - compile_shader_dir(cstr("1"), false); - - SpanU8 root_dir = cstr("../../../.."); +void allie_run() { + SpanU8 root_dir = cstr("."); SpanU8 GPU = cstr("nvidia"); SpanU8 bugged_GPU = cstr("nothere"); bool ENABLE_VALIDATION_LAYERS = true; @@ -1585,11 +1885,11 @@ int main() { vk->IT1_format = OptionVkFormat_expect(IT1_format_found); vk->render_pass_0 = create_render_pass_0(vk->device, vk->IT1_format, vk->zbuffer_format); - vk->pipeline_hands_0a = create_graphics_pipeline_0(vk->device, vk->render_pass_0, 0); - vk->pipeline_hands_0b = create_graphics_pipeline_0_b(vk->device, vk->render_pass_0, 0); + vk->pipeline_hands_0a = create_graphics_pipeline_0(vk->device, root_dir, vk->render_pass_0, 0); + vk->pipeline_hands_0b = create_graphics_pipeline_0_b(vk->device, root_dir, vk->render_pass_0, 0); vk->render_pass_1 = create_render_pass_1(vk->device, swapchain_details_res.ok.surface_format.format); - vk->pipeline_hands_1 = create_graphics_pipeline_1(vk->device, vk->render_pass_1, 0); + vk->pipeline_hands_1 = create_graphics_pipeline_1(vk->device, root_dir, vk->render_pass_1, 0); // These samplers are global for a lot of my future textures vk->linear_sampler = margaret_create_sampler(vk->physical_device, vk->device, true); @@ -1659,9 +1959,7 @@ int main() { }; VecGenericMeshInSceneTemplate_append(&vk->scene_template.generic_models, - GenericMeshInSceneTemplate_for_log(10, 2, 6)); - // VecGenericMeshInSceneTemplate_append(&vk->scene_template.generic_models, - // GenericMeshInSceneTemplate_for_log(5, 5, 10)); + GenericMeshInSceneTemplate_for_log(root_dir, 10, 2, 6)); VecShinyMeshTopology_append(&vk->scene_template.shiny_models, generate_shiny_cube(0.3f)); VecGenericModelOnSceneMem generic_model_mem = VecGenericModelOnSceneMem_new(); @@ -1770,7 +2068,8 @@ int main() { abortf("Can't init free type library\n"); vk->lucy_cache = LucyGlyphCache_new(engine_reference); - vk->font_face = LucyFace_new(vk->ft_library, &vk->lucy_cache, vcstr("fonts/DMSerifText-Regular.ttf")); + vk->font_face = LucyFace_new(vk->ft_library, &vk->lucy_cache, + VecU8_fmt("%s/src/l3/fonts/DMSerifText-Regular.ttf", root_dir)); vk->font_face_of_size_40 = LucyFace_of_size(&vk->font_face, 40); { VecLucyGlyphCachingRequest lucy_requests = VecLucyGlyphCachingRequest_new(); diff --git a/src/l2/anne/codegen.c b/src/l2/anne/codegen.c new file mode 100644 index 0000000..4ed5c30 --- /dev/null +++ b/src/l2/anne/codegen.c @@ -0,0 +1,10 @@ +/* This file generates l2 stuff, but not code. It generates assets. For example: normal textures for simple models */ + +#include "r4.h" +#include "../../l1/codegen/codegen.h" + +int main(){ + mkdir_nofail("l2"); + gen_assets_for_r4(); + finish_layer(cstr("l2")); +} diff --git a/src/l2/tests/r0/r0_assets.h b/src/l2/anne/r4.h similarity index 69% rename from src/l2/tests/r0/r0_assets.h rename to src/l2/anne/r4.h index 888d27e..bb8afdf 100644 --- a/src/l2/tests/r0/r0_assets.h +++ b/src/l2/anne/r4.h @@ -1,165 +1,16 @@ -#ifndef SPLITTER_DRAFT_SRC_L2_TESTS_R0_ASSETS_H -#define SPLITTER_DRAFT_SRC_L2_TESTS_R0_ASSETS_H +#ifndef prototype1_src_l2_anne_r4_h +#define prototype1_src_l2_anne_r4_h -#include "../../marie/graphics_geom.h" -#include "../../../../gen/l1/VecAndSpan_U32.h" -#include "../../../l1/system/fileio.h" +#include "r4_models.h" +#include "../../../gen/l1/pixel_masses.h" +#include "../marie/rasterization.h" +#include "../marie/texture_processing.h" #include -#include "../../../../gen/l1/VecAndSpan_vec2.h" -#include "../../../../gen/l1/pixel_masses.h" -#include "../../marie/rasterization.h" -#include "../../marie/texture_processing.h" - -typedef struct { - vec3 pos; - vec2 tex; -} GenericMeshVertexInc; - -#include "../../../../gen/l1/eve/r0/VecAndSpan_GenericMeshVertexInc.h" - -typedef struct { - GenericMeshVertexInc base; - vec3 norm; - vec3 tang_U; - vec3 tang_V; -} GenericMeshVertex; - -typedef struct { - VecGenericMeshVertexInc vertices; - VecU32 indexes; -} GenericMeshTopology; - -void GenericMeshTopology_drop(GenericMeshTopology self) { - VecGenericMeshVertexInc_drop(self.vertices); - VecU32_drop(self.indexes); -} - -GenericMeshTopology GenericMeshTopology_clone(const GenericMeshTopology* self) { - return (GenericMeshTopology){.vertices = VecGenericMeshVertexInc_clone(&self->vertices), .indexes = VecU32_clone(&self->indexes)}; -} - -typedef struct { - GenericMeshTopology topology; - VecU8 diffuse_texture_path; - VecU8 normal_texture_path; - VecU8 specular_texture_path; -} GenericMeshInSceneTemplate; - -void GenericMeshInSceneTemplate_drop(GenericMeshInSceneTemplate self) { - GenericMeshTopology_drop(self.topology); - VecU8_drop(self.diffuse_texture_path); - VecU8_drop(self.normal_texture_path); - VecU8_drop(self.specular_texture_path); -} - -GenericMeshInSceneTemplate GenericMeshInSceneTemplate_clone(const GenericMeshInSceneTemplate* self) { - return (GenericMeshInSceneTemplate){.topology = GenericMeshTopology_clone(&self->topology), - .diffuse_texture_path = VecU8_clone(&self->diffuse_texture_path), - .normal_texture_path = VecU8_clone(&self->normal_texture_path), - .specular_texture_path = VecU8_clone(&self->specular_texture_path)}; -} - -#include "../../../../gen/l1/eve/r0/VecGenericMeshInSceneTemplate.h" - -typedef struct { - mat4 model_t; -} GenericMeshInstanceInc; - -typedef struct { - GenericMeshInstanceInc base; - mat3 normal_t; -} GenericMeshInstance; - - -typedef struct { - vec3 pos; -} ShinyMeshVertexInc; - -typedef struct { - ShinyMeshVertexInc base; - vec3 normal; -} ShinyMeshVertex; -#include "../../../../gen/l1/eve/r0/VecAndSpan_ShinyMeshVertexInc.h" - -typedef struct { - VecShinyMeshVertexInc vertices; - VecU32 indexes; -} ShinyMeshTopology; - -void ShinyMeshTopology_drop(ShinyMeshTopology self) { - VecShinyMeshVertexInc_drop(self.vertices); - VecU32_drop(self.indexes); -} - -ShinyMeshTopology ShinyMeshTopology_clone(const ShinyMeshTopology* self) { - return (ShinyMeshTopology){.vertices = VecShinyMeshVertexInc_clone(&self->vertices), - VecU32_clone(&self->indexes)}; -} - -#include "../../../../gen/l1/eve/r0/VecShinyMeshTopology.h" - -typedef struct{ - mat4 model_t; - vec3 color_off; - vec3 color_on; -} ShinyMeshInstanceInc; - -typedef struct { - ShinyMeshInstanceInc base; - mat3 normal_t; -} ShinyMeshInstance; - -typedef struct { - vec2 win_scale; -} Pipeline1PushRangeVertex; - -typedef struct { - float gamma_correction_factor; - float hdr_factor; - float lsd_factor; - float anim_time; -} Pipeline1PushRangeFragment; - -typedef struct { - vec3 pos; - char _padding_0[4]; - vec3 dir; - char _padding_1[4]; - vec3 color; - char _padding_2[4]; - float d; - char _padding_3[12]; -} Pipeline0Spotlight; - -typedef struct { - vec3 pos; - char _padding_0[4]; - vec3 color; - char _padding_1[4]; -} Pipeline0PointLight; - -typedef struct { - VecGenericMeshInSceneTemplate generic_models; - VecShinyMeshTopology shiny_models; -} SceneTemplate; - -void SceneTemplate_drop(SceneTemplate self) { - VecGenericMeshInSceneTemplate_drop(self.generic_models); -} - -#define pipeline_0_ubo_point_light_max_count 120 -#define pipeline_0_ubo_spotlight_max_count 20 - -typedef struct { - int point_light_count; - int spotlight_count; - char _padding_1[8]; - Pipeline0PointLight point_light_arr[pipeline_0_ubo_point_light_max_count]; - Pipeline0Spotlight spotlight_arr[pipeline_0_ubo_spotlight_max_count]; -} Pipeline0UBO; /* generating my cool textures2 */ +#include "../../../gen/l1/VecAndSpan_vec2.h" +// todo: move to marie void TextureDataR8_pixel_maxing(TextureDataR8* self, S32 x, S32 y, U8 val) { if (x < 0 || y < 0 || (size_t)x >= self->width) return; @@ -170,10 +21,12 @@ void TextureDataR8_pixel_maxing(TextureDataR8* self, S32 x, S32 y, U8 val) { *TextureDataR8_mat(self, x, y) = MAX_U8(b, val); } +// todo: move to marie U8 a_small_cute_gradient(float r_cut, float r_decay, float dist) { return dist > r_cut ? 0 : (dist < r_decay) ? 255 : (U8)roundf( 255.f * (r_cut - dist) / (r_cut - r_decay) ); } +// todo: move it to marie void TextureDataR8_draw_spot_maxing(TextureDataR8* self, vec2 v, float r_cut, float r_decay) { S32 sx = (S32)roundf(v.x - .5f); S32 sy = (S32)roundf(v.y - .5f); @@ -188,72 +41,8 @@ void TextureDataR8_draw_spot_maxing(TextureDataR8* self, vec2 v, float r_cut, fl } } -GenericMeshTopology generate_one_fourth_of_a_cylinder(float w, float r, U32 k) { - assert(k >= 1); - const float a = M_PI_2f / (float)k; - const float l = 2 * r * sinf(M_PI_4f / (float)k); - float tex_width = 2 * r + w; - float tex_height = 2 * r + (float)k * l; - - const vec2 v0tex = {r / tex_width, r / tex_height}; - const vec2 v1tex = {(r + w) / tex_width, r / tex_height}; - const vec2 v2tex = {r / tex_width, 2 * r / tex_height}; - const vec2 v3tex = {(r + w) / tex_width, 2 * r / tex_height}; - VecGenericMeshVertexInc vertices = VecGenericMeshVertexInc_new_reserved(8 + 4 * k + (k + 2) * 2); - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {0, 0, 0}, .tex = v0tex}); - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {w, 0, 0}, .tex = v1tex}); - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {0, r, 0}, .tex = v2tex}); - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {w, r, 0}, .tex = v3tex}); - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {0, 0, 0}, .tex = v0tex}); - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {w, 0, 0}, .tex = v1tex}); - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {0, 0, -r}, .tex = {r / tex_width, 0}}); - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {w, 0, -r}, .tex = {(r + w) / tex_width, 0}}); - - for (U32 i = 0; i < k; i++) { - for (int j = 0; j < 2; j++) { - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){ - .pos = {0, cosf(a * (float)(i + j)) * r, -sinf(a * (float)(i + j)) * r}, - .tex = {v2tex.x, v2tex.y + (float)(i + j) * l / tex_height} - }); - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){ - .pos = {w, cosf(a * (float)(i + j)) * r, -sinf(a * (float)(i + j)) * r}, - .tex = {v3tex.x, v3tex.y + (float)(i + j) * l / tex_height} - }); - } - } - assert(vertices.len == 8 + 4 * k); - - for (U32 i = 0; i <= k; i++) { - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){ - .pos = {0, cosf(a * (float)i) * r, -sinf(a * (float)i) * r}, - .tex = (vec2){ (r - r *sinf(a * (float)i)) / tex_width, (r + r * cosf(a * (float)i)) / tex_height}, - }); - } - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {0, 0, 0}, .tex = v0tex}); - for (U32 i = 0; i <= k; i++) { - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){ - .pos = {w, cosf(a * (float)i) * r, -sinf(a * (float)i) * r}, - .tex = (vec2){ (r + w + r * sinf(a * (float)i)) / tex_width, (r + r * cosf(a * (float)i)) / tex_height}, - }); - } - VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {w, 0, 0}, .tex = v1tex}); - assert(vertices.len == 8 + 4 * k + (k + 2) * 2); - - VecU32 indexes = VecU32_new_reserved(3*(4+2*k+2*k)); - U32 _span_0[] = {7, 5, 4, 7, 4, 6, 1, 3, 0, 3, 2, 0}; - VecU32_append_span(&indexes, (SpanU32){.data = _span_0, .len = ARRAY_SIZE(_span_0)}); - for (U32 i = 0; i < k; i++) { - U32 _span_1[] = { - 8 + 4 * k + k + 1, 8 + 4 * k + i, 8 + 4 * k + i + 1, - 8 + 4 * k + 2 * k + 3, 8 + 4 * k + (k + 2) + i + 1, 8 + 4 * k + (k + 2) + i, - 8 + 4 * i + 0, 8 + 4 * i + 1, 8 + 4 * i + 3, - 8 + 4 * i + 0, 8 + 4 * i + 3, 8 + 4 * i + 2, - }; - VecU32_append_span(&indexes, (SpanU32){.data = _span_1, .len = ARRAY_SIZE(_span_1)}); - } - return (GenericMeshTopology){.vertices = vertices, .indexes = indexes}; -} +// todo: move to marie, not used here vec2 perimeter_line_get_thorn_on_vertex(Spanvec2 P, size_t i, float thickness) { assert(P.len >= 3 && i < P.len); vec2 A = *Spanvec2_at(P, i ? i - 1 : P.len - 1); @@ -268,6 +57,7 @@ vec2 perimeter_line_get_thorn_on_vertex(Spanvec2 P, size_t i, float thickness) { return vec2_mul_scal(mat2_mul_vec2(marie_2d_rot_mat2(t), b), thickness / sinf(t)); } +// todo: move to marie /* It is assumed that A != B */ float distance_to_segment(vec2 A, vec2 B, vec2 P) { vec2 seg = vec2_minus_vec2(B, A); @@ -279,6 +69,7 @@ float distance_to_segment(vec2 A, vec2 B, vec2 P) { return len; } +// todo: move to marie typedef struct { TextureDataR8* texture; vec2 A; @@ -287,6 +78,7 @@ typedef struct { float r_decay; } TextureDataR8_draw_perimeter_maxing_H_DrawGuest; +// todo: move to marie void TextureDataR8_draw_perimeter_maxing_h_draw_guest(void* ug, S32 x, S32 y, vec4 attr) { TextureDataR8_draw_perimeter_maxing_H_DrawGuest* g = ug; if (TextureDataR8_is_inside(g->texture, x, y)) { @@ -296,6 +88,7 @@ void TextureDataR8_draw_perimeter_maxing_h_draw_guest(void* ug, S32 x, S32 y, ve } } +// todo: move to marie void TextureDataR8_draw_perimeter_maxing_h_draw_triangle( TextureDataR8_draw_perimeter_maxing_H_DrawGuest* aboba, vec2 a, vec2 b, vec2 c ) { @@ -304,6 +97,8 @@ void TextureDataR8_draw_perimeter_maxing_h_draw_triangle( (FnMarieRasterizerCallback){TextureDataR8_draw_perimeter_maxing_h_draw_guest, aboba}); } + +// todo: move to marie /* It is assumed that P[i] != P[i + 1] foreach i from 0 to P.len - 1 */ void TextureDataR8_draw_perimeter_maxing(TextureDataR8* self, Spanvec2 P) { float r_cut = 5; @@ -331,7 +126,7 @@ typedef struct { float brd; } Wimbzle; -#include "../../../../gen/l1/eve/r0/VecWimbzle.h" +#include "../../../gen/l1/eve/r0/VecWimbzle.h" typedef struct { vec2 center; @@ -339,7 +134,7 @@ typedef struct { float hc; } Nibzle; -#include "../../../../gen/l1/eve/r0/VecNibzle.h" +#include "../../../gen/l1/eve/r0/VecNibzle.h" typedef struct { VecWimbzle wimbzles; @@ -637,8 +432,6 @@ TextureDataR8G8B8A8 generate_tex_template_for_one_fourth_of_a_cylinder(float s_r vec2 cord_resol = {(float)width_pix / (2 * r + w), (float)height_pix / (2 * r + (float)k * l)}; const vec2 v0tex = {r, r}; const vec2 v1tex = {r + w, r}; - const vec2 v2tex = {r, 2 * r}; - const vec2 v3tex = {r + w, 2 * r}; const vec2 v4tex = {r, 0}; const vec2 v5tex = {r + w, 0}; TextureDataR8G8B8A8 res = TextureDataR8G8B8A8_new(width_pix, height_pix); @@ -733,82 +526,41 @@ TextureDataR8G8B8A8 generate_normal_tex_for_one_fourth_of_a_cylinder(float s_res return res; } -U32 quad_to_triangles_conv_arr[6] = {0, 1, 2, 0, 2, 3}; +#include "../../../gen/l1/margaret/png_pixel_masses.h" +#include "../marie/texture_processing.h" +#include "../../l1/system/fsmanip.h" -ShinyMeshTopology generate_shiny_cube(float r) { - ShinyMeshVertexInc vert[24] = { - {{+r, +r, +r}}, - {{+r, -r, +r}}, - {{+r, -r, -r}}, - {{+r, +r, -r}}, - - {{-r, -r, -r}}, - {{-r, -r, +r}}, - {{-r, +r, +r}}, - {{-r, +r, -r}}, - - {{+r, +r, +r}}, - {{+r, +r, -r}}, - {{-r, +r, -r}}, - {{-r, +r, +r}}, - - {{-r, -r, -r}}, - {{+r, -r, -r}}, - {{+r, -r, +r}}, - {{-r, -r, +r}}, - - {{+r, +r, +r}}, - {{-r, +r, +r}}, - {{-r, -r, +r}}, - {{+r, -r, +r}}, - - {{-r, -r, -r}}, - {{-r, +r, -r}}, - {{+r, +r, -r}}, - {{+r, -r, -r}}, - }; - VecShinyMeshVertexInc vertices_vec = VecShinyMeshVertexInc_from_span( - (SpanShinyMeshVertexInc){ .data = vert, .len = ARRAY_SIZE(vert) }); - VecU32 indexes_vec = VecU32_new_reserved(36); - for (U32 f = 0; f < 6; f++) { - for (U32 j = 0; j < 6; j++) - VecU32_append(&indexes_vec, f * 4 + quad_to_triangles_conv_arr[j]); +void for_log(U64 w, U64 r, U64 k) { + { + TextureDataR8G8B8A8 tex = generate_tex_template_for_one_fourth_of_a_cylinder(120, (float)w, (float)r, k); + TextureDataR8G8B8A8 fixed_tex = TextureDataR8G8B8A8_expand_nontransparent_1px(&tex); + VecU8 name = VecU8_fmt("l2/textures/r4/log_%u_%u_%u_TEMPLATE.png", w, r, k); + TextureDataR8G8B8A8_write_to_png_nofail(&fixed_tex, VecU8_to_span(&name)); + VecU8_drop(name); + TextureDataR8G8B8A8_drop(fixed_tex); + TextureDataR8G8B8A8_drop(tex); + } + { + TextureDataR8G8B8A8 tex = generate_normal_tex_for_one_fourth_of_a_cylinder(120, (float)w, (float)r, k); + TextureDataR8G8B8A8 fixed_tex = TextureDataR8G8B8A8_expand_nontransparent_1px(&tex); + VecU8 name = VecU8_fmt("l2/textures/r4/log_%u_%u_%u_NORMAL.png", w, r, k); + TextureDataR8G8B8A8_write_to_png_nofail(&fixed_tex, VecU8_to_span(&name)); + VecU8_drop(name); + TextureDataR8G8B8A8_drop(fixed_tex); + TextureDataR8G8B8A8_drop(tex); } - return (ShinyMeshTopology){ .vertices = vertices_vec, .indexes = indexes_vec}; } -typedef struct { - int face; - /* There is a counterclockwise one-to-one correspondence between vertexes of face and edges of face */ - int vert_on_it; -} CubeVertOfFace; - -CubeVertOfFace CubeVertOfFace_jump(CubeVertOfFace vert) { - const CubeVertOfFace cube_detour_get_neighbour_face[6][4] = { - {{4, 3}, {3, 1}, {5, 2}, {2, 0}}, - {{3, 3}, {4, 1}, {2, 2}, {5, 0}}, - {{0, 3}, {5, 1}, {1, 2}, {4, 0}}, - {{5, 3}, {0, 1}, {4, 2}, {1, 0}}, - {{2, 3}, {1, 1}, {3, 2}, {0, 0}}, - {{1, 3}, {2, 1}, {0, 2}, {3, 0}} - }; - return cube_detour_get_neighbour_face[vert.face][vert.vert_on_it]; +/* We are on l2 */ +int gen_assets_for_r4() { + mkdir_nofail("l2/textures"); + mkdir_nofail("l2/textures/r4"); + for_log(10, 2, 6); + // for_log(5, 5, 10); + // for_log(1, 10, 4); + // for_log(2, 1, 6); + return 0; } -U32 CubeVertOfFace_to_vid(CubeVertOfFace vert) { - return 4 * vert.face + vert.vert_on_it; -} -CubeVertOfFace CubeVertOfFace_next(CubeVertOfFace vert) { - return (CubeVertOfFace){vert.face, (vert.vert_on_it + 1) % 4}; -} - -GenericMeshInSceneTemplate GenericMeshInSceneTemplate_for_log(U32 w, U32 r, U32 k) { - return (GenericMeshInSceneTemplate){.topology = generate_one_fourth_of_a_cylinder((float)w, (float)r, k), - .diffuse_texture_path = VecU8_format("textures/log_%u_%u_%u_diffuse.png", w, r, k), - .normal_texture_path = VecU8_format("textures/log_%u_%u_%u_NORMAL.png", w, r, k), - .specular_texture_path = VecU8_format("textures/log_%u_%u_%u_specular.png", w, r, k), - }; -} - -#endif +#endif \ No newline at end of file diff --git a/src/l2/anne/r4_models.h b/src/l2/anne/r4_models.h new file mode 100644 index 0000000..ae3f945 --- /dev/null +++ b/src/l2/anne/r4_models.h @@ -0,0 +1,156 @@ +#ifndef prototype1_src_l2_anne_r4_models_h +#define prototype1_src_l2_anne_r4_models_h + +#include "../alice/assets.h" +#include "../../l1/core/VecU8_as_str.h" + +GenericMeshTopology generate_one_fourth_of_a_cylinder(float w, float r, U32 k) { + assert(k >= 1); + const float a = M_PI_2f / (float)k; + const float l = 2 * r * sinf(M_PI_4f / (float)k); + float tex_width = 2 * r + w; + float tex_height = 2 * r + (float)k * l; + + const vec2 v0tex = {r / tex_width, r / tex_height}; + const vec2 v1tex = {(r + w) / tex_width, r / tex_height}; + const vec2 v2tex = {r / tex_width, 2 * r / tex_height}; + const vec2 v3tex = {(r + w) / tex_width, 2 * r / tex_height}; + VecGenericMeshVertexInc vertices = VecGenericMeshVertexInc_new_reserved(8 + 4 * k + (k + 2) * 2); + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {0, 0, 0}, .tex = v0tex}); + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {w, 0, 0}, .tex = v1tex}); + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {0, r, 0}, .tex = v2tex}); + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {w, r, 0}, .tex = v3tex}); + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {0, 0, 0}, .tex = v0tex}); + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {w, 0, 0}, .tex = v1tex}); + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {0, 0, -r}, .tex = {r / tex_width, 0}}); + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {w, 0, -r}, .tex = {(r + w) / tex_width, 0}}); + + for (U32 i = 0; i < k; i++) { + for (int j = 0; j < 2; j++) { + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){ + .pos = {0, cosf(a * (float)(i + j)) * r, -sinf(a * (float)(i + j)) * r}, + .tex = {v2tex.x, v2tex.y + (float)(i + j) * l / tex_height} + }); + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){ + .pos = {w, cosf(a * (float)(i + j)) * r, -sinf(a * (float)(i + j)) * r}, + .tex = {v3tex.x, v3tex.y + (float)(i + j) * l / tex_height} + }); + } + } + assert(vertices.len == 8 + 4 * k); + + for (U32 i = 0; i <= k; i++) { + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){ + .pos = {0, cosf(a * (float)i) * r, -sinf(a * (float)i) * r}, + .tex = (vec2){ (r - r *sinf(a * (float)i)) / tex_width, (r + r * cosf(a * (float)i)) / tex_height}, + }); + } + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {0, 0, 0}, .tex = v0tex}); + for (U32 i = 0; i <= k; i++) { + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){ + .pos = {w, cosf(a * (float)i) * r, -sinf(a * (float)i) * r}, + .tex = (vec2){ (r + w + r * sinf(a * (float)i)) / tex_width, (r + r * cosf(a * (float)i)) / tex_height}, + }); + } + VecGenericMeshVertexInc_append(&vertices, (GenericMeshVertexInc){.pos = {w, 0, 0}, .tex = v1tex}); + assert(vertices.len == 8 + 4 * k + (k + 2) * 2); + + VecU32 indexes = VecU32_new_reserved(3*(4+2*k+2*k)); + U32 _span_0[] = {7, 5, 4, 7, 4, 6, 1, 3, 0, 3, 2, 0}; + VecU32_append_span(&indexes, (SpanU32){.data = _span_0, .len = ARRAY_SIZE(_span_0)}); + for (U32 i = 0; i < k; i++) { + U32 _span_1[] = { + 8 + 4 * k + k + 1, 8 + 4 * k + i, 8 + 4 * k + i + 1, + 8 + 4 * k + 2 * k + 3, 8 + 4 * k + (k + 2) + i + 1, 8 + 4 * k + (k + 2) + i, + 8 + 4 * i + 0, 8 + 4 * i + 1, 8 + 4 * i + 3, + 8 + 4 * i + 0, 8 + 4 * i + 3, 8 + 4 * i + 2, + }; + VecU32_append_span(&indexes, (SpanU32){.data = _span_1, .len = ARRAY_SIZE(_span_1)}); + } + return (GenericMeshTopology){.vertices = vertices, .indexes = indexes}; +} + +U32 quad_to_triangles_conv_arr[6] = {0, 1, 2, 0, 2, 3}; + +ShinyMeshTopology generate_shiny_cube(float r) { + ShinyMeshVertexInc vert[24] = { + {{+r, +r, +r}}, + {{+r, -r, +r}}, + {{+r, -r, -r}}, + {{+r, +r, -r}}, + + {{-r, -r, -r}}, + {{-r, -r, +r}}, + {{-r, +r, +r}}, + {{-r, +r, -r}}, + + {{+r, +r, +r}}, + {{+r, +r, -r}}, + {{-r, +r, -r}}, + {{-r, +r, +r}}, + + {{-r, -r, -r}}, + {{+r, -r, -r}}, + {{+r, -r, +r}}, + {{-r, -r, +r}}, + + {{+r, +r, +r}}, + {{-r, +r, +r}}, + {{-r, -r, +r}}, + {{+r, -r, +r}}, + + {{-r, -r, -r}}, + {{-r, +r, -r}}, + {{+r, +r, -r}}, + {{+r, -r, -r}}, + }; + VecShinyMeshVertexInc vertices_vec = VecShinyMeshVertexInc_from_span( + (SpanShinyMeshVertexInc){ .data = vert, .len = ARRAY_SIZE(vert) }); + VecU32 indexes_vec = VecU32_new_reserved(36); + for (U32 f = 0; f < 6; f++) { + for (U32 j = 0; j < 6; j++) + VecU32_append(&indexes_vec, f * 4 + quad_to_triangles_conv_arr[j]); + } + return (ShinyMeshTopology){ .vertices = vertices_vec, .indexes = indexes_vec}; +} + +typedef struct { + int face; + /* There is a counterclockwise one-to-one correspondence between vertexes of face and edges of face */ + int vert_on_it; +} CubeVertOfFace; + +CubeVertOfFace CubeVertOfFace_jump(CubeVertOfFace vert) { + const CubeVertOfFace cube_detour_get_neighbour_face[6][4] = { + {{4, 3}, {3, 1}, {5, 2}, {2, 0}}, + {{3, 3}, {4, 1}, {2, 2}, {5, 0}}, + {{0, 3}, {5, 1}, {1, 2}, {4, 0}}, + {{5, 3}, {0, 1}, {4, 2}, {1, 0}}, + {{2, 3}, {1, 1}, {3, 2}, {0, 0}}, + {{1, 3}, {2, 1}, {0, 2}, {3, 0}} + }; + return cube_detour_get_neighbour_face[vert.face][vert.vert_on_it]; +} + +U32 CubeVertOfFace_to_vid(CubeVertOfFace vert) { + return 4 * vert.face + vert.vert_on_it; +} + +CubeVertOfFace CubeVertOfFace_next(CubeVertOfFace vert) { + return (CubeVertOfFace){vert.face, (vert.vert_on_it + 1) % 4}; +} + +// todo: yes, it is AGAINST THE RULES to take from l3 when being on l2, but we fjsdafklj lI WILL REMOVE THIS OK, MAN CALM DOWN +GenericMeshInSceneTemplate GenericMeshInSceneTemplate_for_log(SpanU8 root_dir, U64 w, U64 r, U64 k) { + return (GenericMeshInSceneTemplate){.topology = generate_one_fourth_of_a_cylinder((float)w, (float)r, k), + .diffuse_texture_path = VecU8_fmt("%s/src/l3/textures/log_%u_%u_%u_diffuse.png", root_dir, w, r, k), + .normal_texture_path = VecU8_fmt("%s/gen/l2/textures/r4/log_%u_%u_%u_NORMAL.png", root_dir, w, r, k), + .specular_texture_path = VecU8_fmt("%s/src/l3/textures/log_%u_%u_%u_specular.png", root_dir, w, r, k), + }; +} + +// todo: separate model data generation and create a function that writes it to .obj file +// todo numero 2: stop generating model data on the fly in r4 and start loading it from generated file + + +#endif diff --git a/src/l2/lucy/glyph_cache.h b/src/l2/lucy/glyph_cache.h index f579162..809abb6 100644 --- a/src/l2/lucy/glyph_cache.h +++ b/src/l2/lucy/glyph_cache.h @@ -6,6 +6,7 @@ #include FT_FREETYPE_H #include "../../../gen/l1/VecAndSpan_U32Segment.h" #include "../../../gen/l1/vulkan/VecVkDescriptorImageInfo.h" +#include "../../../gen/l1/pixel_masses.h" #include "../../l1_5/core/buff_rb_tree_node.h" #include "../../l1_5/core/rb_tree_node.h" diff --git a/src/l2/margaret/vulkan_utils.h b/src/l2/margaret/vulkan_utils.h index 132f7e3..d06c2c3 100644 --- a/src/l2/margaret/vulkan_utils.h +++ b/src/l2/margaret/vulkan_utils.h @@ -269,12 +269,12 @@ NODISCARD VecU8 margaret_stringify_device_memory_properties(VkPhysicalDevice phy vkGetPhysicalDeviceMemoryProperties(physical_device, &properties); VecU8 result = VecU8_new(); for (size_t h = 0; h < properties.memoryHeapCount; h++) { - VecU8_append_vec(&result, VecU8_format("-+ Heap %ld of %lu bytes [ ", h, properties.memoryHeaps[h].size)); + VecU8_append_vec(&result, VecU8_fmt("-+ Heap %u of %u bytes [ ", (U64)h, (U64)properties.memoryHeaps[h].size)); VecU8_append_vec(&result, margaret_stringify_memory_heap_flags(properties.memoryHeaps[h].flags)); VecU8_append_span(&result, cstr(" ], mem types below\n")); for (size_t t = 0; t < properties.memoryTypeCount; t++) { if (properties.memoryTypes->heapIndex == h) { - VecU8_append_vec(&result, VecU8_format("----> Mem type %lu [ ", t)); + VecU8_append_vec(&result, VecU8_fmt("----> Mem type %u [ ", (U64)t)); VecU8_append_vec(&result, margaret_stringify_memory_property_flags(properties.memoryTypes[t].propertyFlags)); VecU8_append_span(&result, cstr(" ]\n")); } diff --git a/src/l2/tests/r0/r0_scene.h b/src/l2/tests/r0/r0_scene.h deleted file mode 100644 index 95c4e56..0000000 --- a/src/l2/tests/r0/r0_scene.h +++ /dev/null @@ -1,328 +0,0 @@ -#ifndef PROTOTYPE1_SRC_L2_TESTS_R0_SCENE_H -#define PROTOTYPE1_SRC_L2_TESTS_R0_SCENE_H - -#include "r0_assets.h" - -#include "../../margaret/vulkan_utils.h" -#include "../../lucy/glyph_render.h" - -typedef struct { - U64 count; - MargaretSubbuf staging_busy; - MargaretSubbuf staging_updatable; - MargaretSubbuf device_local; - U64 cap; - // todo: delete this crap. This crap turned out to be completely useless. It is another one of my very very dumb ideas - // todo: remove updatable buffer, fill staging buffer in main thread -} PatriciaBuf; - -void PatriciaBuf_swap_staging(PatriciaBuf* self){ - MargaretSubbuf t = self->staging_updatable; - self->staging_updatable = self->staging_busy; - self->staging_busy = t; -} - -typedef struct { - size_t indexes; - - MargaretSubbuf staging_vbo; - MargaretSubbuf staging_ebo; - - // todo: replace TextureDataXXX with MargaretPngPromises - TextureDataR8G8B8A8 pixels_diffuse; - TextureDataR8G8B8A8 pixels_normal; - TextureDataR8 pixels_specular; - - MargaretSubbuf staging_diffuse_tex_buf; - MargaretSubbuf staging_normal_tex_buf; - MargaretSubbuf staging_specular_tex_buf; - - MargaretSubbuf vbo; - MargaretSubbuf ebo; - PatriciaBuf instance_attr; - - // todo: store dimensions of these images - MargaretImg diffuse_texture; - MargaretImg normal_texture; - MargaretImg specular_texture; -} GenericModelOnSceneMem; - -#include "../../../../gen/l1/eve/r0/VecGenericModelOnSceneMem.h" - -void GenericModelOnSceneMem_set(GenericModelOnSceneMem* self, size_t instance, GenericMeshInstanceInc uncomp){ - assert(instance < self->instance_attr.count); - GenericMeshInstance* staging = (GenericMeshInstance*)MargaretSubbuf_get_mapped(&self->instance_attr.staging_updatable); - staging[instance].base = uncomp; - mat4 tr_inv = mat4_transpose(mat4_inverse(uncomp.model_t)); - staging[instance].normal_t = mat3_new( - tr_inv.x.x, tr_inv.y.x, tr_inv.z.x, - tr_inv.x.y, tr_inv.y.y, tr_inv.z.y, - tr_inv.x.z, tr_inv.y.z, tr_inv.z.z ); -} - -typedef struct { - size_t indexes; - - MargaretSubbuf staging_vbo; - MargaretSubbuf staging_ebo; - - MargaretSubbuf vbo; - MargaretSubbuf ebo; - PatriciaBuf instance_attr; -} ShinyModelOnSceneMem; - -#include "../../../../gen/l1/eve/r0/VecShinyModelOnSceneMem.h" - -void ShinyModelOnSceneMem_set(ShinyModelOnSceneMem* self, size_t instance, ShinyMeshInstanceInc uncomp){ - assert(instance < self->instance_attr.count); - ShinyMeshInstance* staging = (ShinyMeshInstance*)MargaretSubbuf_get_mapped(&self->instance_attr.staging_updatable); - staging[instance].base = uncomp; - mat4 tr_inv = mat4_transpose(mat4_inverse(uncomp.model_t)); - staging[instance].normal_t = mat3_new( - tr_inv.x.x, tr_inv.y.x, tr_inv.z.x, - tr_inv.x.y, tr_inv.y.y, tr_inv.z.y, - tr_inv.x.z, tr_inv.y.z, tr_inv.z.z ); -} - -typedef struct { - float fov; - mat3 cam_basis; - vec3 pos; - - float speed; - float sensitivity; - float pitch_cap; -} CamControlInfo; - -void CamControlInfo_forward(CamControlInfo* self, float fl) { - self->pos = vec3_add_vec3(self->pos, vec3_mul_scal(self->cam_basis.z, -self->speed * fl)); -} - -void CamControlInfo_backward(CamControlInfo* self, float fl) { - self->pos = vec3_add_vec3(self->pos, vec3_mul_scal(self->cam_basis.z, self->speed * fl)); -} - -void CamControlInfo_left(CamControlInfo* self, float fl) { - self->pos = vec3_add_vec3(self->pos, vec3_mul_scal(self->cam_basis.x, -self->speed * fl)); -} - -void CamControlInfo_right(CamControlInfo* self, float fl) { - self->pos = vec3_add_vec3(self->pos, vec3_mul_scal(self->cam_basis.x, self->speed * fl)); -} - -void CamControlInfo_down(CamControlInfo* self, float fl) { - self->pos = vec3_add_vec3(self->pos, vec3_mul_scal((vec3){0, -1, 0}, self->speed * fl)); -} - -void CamControlInfo_up(CamControlInfo* self, float fl) { - self->pos = vec3_add_vec3(self->pos, vec3_mul_scal((vec3){0, 1, 0}, self->speed * fl)); -} - -CamControlInfo CamControlInfo_new() { - return (CamControlInfo){ - .fov = 1.5f, .cam_basis = marie_simple_camera_rot_m_basis_in_cols(0, 0, 0), .pos = {0, 0, 0}, - .speed = 6.7f, .sensitivity = 0.5f * M_PIf / 180, .pitch_cap = M_PIf * 0.49f - }; -} - -void CamControlInfo_update_direction(CamControlInfo* self, int win_width, int win_height, int pointer_x, int pointer_y) { - float yaw = ((float)win_width / 2 - (float)pointer_x) * self->sensitivity; - float pitch = marie_clamp_float( - ((float)win_height / 2 - (float)pointer_y) * self->sensitivity, - -self->pitch_cap, self->pitch_cap - ); - self->cam_basis = marie_simple_camera_rot_m_basis_in_cols(yaw, pitch, 0); -} - -typedef struct { - MargaretSubbuf staging_busy; - MargaretSubbuf staging_updatable; - MargaretSubbuf device_local; -} Pipeline0Transfer; - -// Just for a test in r0 -typedef struct { - mat3 rotation; - vec3 pos; - float scale; - vec3 color_on; -} ObjectInfo; - -#include "../../../../gen/l1/eve/r0/VecObjectInfo.h" - -/* Non copyable */ -typedef struct { - VecGenericModelOnSceneMem generic_models; - VecShinyModelOnSceneMem shiny_models; - - VkClearColorValue color; - float gamma_correction_factor; - float hdr_factor; - float lsd_factor; - float anim_time; // A timer, passed to functions that push push constants - - /* point_light_vec_len and spotlight_vec_len are stored in staging (and also device local) buffers */ - Pipeline0Transfer pipeline0_ubo; - - CamControlInfo cam; - - VecObjectInfo smeshnyavka_1; - VecObjectInfo smeshnyavka_3; - - VecU8 text_on_screen; -} Scene; - -ShinyMeshInstanceInc ShinyMeshInstanceInc_from_ObjectInfo(const ObjectInfo* oi){ - return (ShinyMeshInstanceInc){ - .model_t = mat4_mul_mat4(marie_translation_mat4(oi->pos), - mat4_mul_mat4(marie_3d_scal_mat4(oi->scale), marie_mat3_to_mat4(oi->rotation))), - .color_on = oi->color_on, .color_off = {1, 0.4f, 0.5f} - }; -} - -// todo: remove this shit -void Scene_add_smeshnyavka_3(Scene* self, ObjectInfo oi){ - ShinyModelOnSceneMem* model_sh = VecShinyModelOnSceneMem_mat(&self->shiny_models, 0); - size_t ni = self->smeshnyavka_3.len; - assert(ni < model_sh->instance_attr.cap); - VecObjectInfo_append(&self->smeshnyavka_3, oi); - model_sh->instance_attr.count = ni + 1; - ShinyModelOnSceneMem_set(model_sh, ni, ShinyMeshInstanceInc_from_ObjectInfo(&oi)); -} - -// todo: remove this shit (and rewrite everything in haskell) -void Scene_update_smeshnyavka_3(Scene* self, size_t sh_id){ - assert(sh_id < self->smeshnyavka_3.len); - const ObjectInfo* oi = VecObjectInfo_at(&self->smeshnyavka_3, sh_id); - ShinyModelOnSceneMem* model_sh = VecShinyModelOnSceneMem_mat(&self->shiny_models, 0); - ShinyModelOnSceneMem_set(model_sh, sh_id, ShinyMeshInstanceInc_from_ObjectInfo(oi)); -} - -GenericMeshInstanceInc GenericMeshInstanceInc_from_ObjectInfo(const ObjectInfo* oi){ - return (GenericMeshInstanceInc){ - .model_t = mat4_mul_mat4(marie_translation_mat4(oi->pos), - mat4_mul_mat4(marie_3d_scal_mat4(oi->scale), marie_mat3_to_mat4(oi->rotation))), - }; -} - -// todo: remove this shit -void Scene_add_smeshnyavka_1(Scene* self, ObjectInfo oi){ - GenericModelOnSceneMem* model = VecGenericModelOnSceneMem_mat(&self->generic_models, 0); - size_t ni = self->smeshnyavka_1.len; - assert(ni < model->instance_attr.cap); - VecObjectInfo_append(&self->smeshnyavka_1, oi); - model->instance_attr.count = ni + 1; - GenericModelOnSceneMem_set(model, ni, GenericMeshInstanceInc_from_ObjectInfo(&oi)); -} - -// todo: remove this shit -void Scene_update_smeshnyavka_1(Scene* self, size_t sh_id){ - assert(sh_id < self->smeshnyavka_1.len); - const ObjectInfo* oi = VecObjectInfo_at(&self->smeshnyavka_1, sh_id); - GenericModelOnSceneMem* model = VecGenericModelOnSceneMem_mat(&self->generic_models, 0); - GenericModelOnSceneMem_set(model, sh_id, GenericMeshInstanceInc_from_ObjectInfo(oi)); -} - - - -Scene Scene_new(VecGenericModelOnSceneMem generic_models, VecShinyModelOnSceneMem shiny_models, - Pipeline0Transfer pipeline0_ubo) { - return (Scene){.generic_models = generic_models, .shiny_models = shiny_models, - .color = {.float32 = {0, 0, 0, 1}}, - .gamma_correction_factor = 2.2f, .hdr_factor = 1, .lsd_factor = 0, .anim_time = 0, - .pipeline0_ubo = pipeline0_ubo, .cam = CamControlInfo_new(), - .smeshnyavka_1 = VecObjectInfo_new(), - .smeshnyavka_3 = VecObjectInfo_new(), // todo: remove this shit and rewrite everything in haskell - .text_on_screen = VecU8_new(), - }; -} - -void Scene_drop(Scene self) { - VecGenericModelOnSceneMem_drop(self.generic_models); - VecShinyModelOnSceneMem_drop(self.shiny_models); -} - -/* No buffer rerecording, no buffer beginning, no buffer ending */ -void SceneTemplate_copy_initial_model_topology_cmd_buf_recording( - const SceneTemplate* scene_template, const Scene* scene, VkCommandBuffer command_buffer) { - assert(scene_template->generic_models.len == scene->generic_models.len); - assert(scene_template->shiny_models.len == scene->shiny_models.len); - assert(scene_template->generic_models.len == scene->generic_models.len); - - for (size_t mi = 0; mi < scene_template->generic_models.len; mi++) { - const GenericMeshInSceneTemplate* mt = VecGenericMeshInSceneTemplate_at(&scene_template->generic_models, mi); - const GenericModelOnSceneMem *mm = VecGenericModelOnSceneMem_at(&scene->generic_models, mi); - - assert(mm->staging_vbo.len >= mt->topology.vertices.len * sizeof(GenericMeshVertex)); - assert(mm->vbo.len >= mt->topology.vertices.len * sizeof(GenericMeshVertex)); - GenericMeshVertex* staging_vbo = (GenericMeshVertex*)MargaretSubbuf_get_mapped(&mm->staging_vbo); - for (U64 i = 0; i < mt->topology.vertices.len; i++) { - staging_vbo[i].base = mt->topology.vertices.buf[i]; - } - assert(mt->topology.indexes.len % 3 == 0); - for (size_t ti = 0; ti * 3 < mt->topology.indexes.len; ti++) { - U32 v0 = mt->topology.indexes.buf[ti * 3 + 0]; - U32 v1 = mt->topology.indexes.buf[ti * 3 + 1]; - U32 v2 = mt->topology.indexes.buf[ti * 3 + 2]; - const GenericMeshVertexInc* A0 = VecGenericMeshVertexInc_at(&mt->topology.vertices, v0); - const GenericMeshVertexInc* A1 = VecGenericMeshVertexInc_at(&mt->topology.vertices, v1); - const GenericMeshVertexInc* A2 = VecGenericMeshVertexInc_at(&mt->topology.vertices, v2); - vec3 dp1 = vec3_minus_vec3(A1->pos, A0->pos); - vec3 dp2 = vec3_minus_vec3(A2->pos, A0->pos); - float du1 = A1->tex.x - A0->tex.x; - float dv1 = A1->tex.y - A0->tex.y; - float du2 = A2->tex.x - A0->tex.x; - float dv2 = A2->tex.y - A0->tex.y; - vec3 norm = vec3_normalize(vec3_cross(dp1, dp2)); - mat2x3 tang_U_V = mat3x2_transpose(mat2_mul_mat3x2( - mat2_inverse(mat2_new(du1, dv1, du2, dv2)), - mat2x3_transpose((mat2x3){.x = dp1, .y = dp2}) - )); - staging_vbo[v0].norm = staging_vbo[v1].norm = staging_vbo[v2].norm = norm; - staging_vbo[v0].tang_U = staging_vbo[v1].tang_U = staging_vbo[v2].tang_U = tang_U_V.x; - staging_vbo[v0].tang_V = staging_vbo[v1].tang_V = staging_vbo[v2].tang_V = tang_U_V.y; - } - margaret_rec_cmd_copy_buffer_one_to_one(command_buffer, &mm->staging_vbo, &mm->vbo); - - assert(mt->topology.indexes.len == mm->indexes); - size_t ebo_len = mt->topology.indexes.len * sizeof(U32); - assert(mm->ebo.len >= ebo_len); - U32* staging_ebo = (U32*)MargaretSubbuf_get_mapped(&mm->staging_ebo); - memcpy(staging_ebo, mt->topology.indexes.buf, ebo_len); - margaret_rec_cmd_copy_buffer_one_to_one(command_buffer, &mm->staging_ebo, &mm->ebo); - } - - for (size_t mi = 0; mi < scene_template->shiny_models.len; mi++) { - const ShinyMeshTopology* mt = VecShinyMeshTopology_at(&scene_template->shiny_models, mi); - const ShinyModelOnSceneMem *mm = VecShinyModelOnSceneMem_at(&scene->shiny_models, mi); - - assert(mm->staging_vbo.len >= mt->vertices.len * sizeof(ShinyMeshVertex)); - assert(mm->vbo.len >= mt->vertices.len * sizeof(ShinyMeshVertex)); - ShinyMeshVertex* staging_vbo = (ShinyMeshVertex*)MargaretSubbuf_get_mapped(&mm->staging_vbo); - for (U64 i = 0; i < mt->vertices.len; i++) { - staging_vbo[i].base = mt->vertices.buf[i]; - } - assert(mt->indexes.len % 3 == 0); - for (size_t ti = 0; ti * 3 < mt->indexes.len; ti++) { - U32 v0 = mt->indexes.buf[ti * 3 + 0]; - U32 v1 = mt->indexes.buf[ti * 3 + 1]; - U32 v2 = mt->indexes.buf[ti * 3 + 2]; - vec3 p0 = VecShinyMeshVertexInc_at(&mt->vertices, v0)->pos; - vec3 p1 = VecShinyMeshVertexInc_at(&mt->vertices, v1)->pos; - vec3 p2 = VecShinyMeshVertexInc_at(&mt->vertices, v2)->pos; - vec3 norm = vec3_normalize(vec3_cross(vec3_minus_vec3(p1, p0), vec3_minus_vec3(p2, p0))); - staging_vbo[v0].normal = staging_vbo[v1].normal = staging_vbo[v2].normal = norm; - } - - margaret_rec_cmd_copy_buffer_one_to_one(command_buffer, &mm->staging_vbo, &mm->vbo); - - assert(mt->indexes.len == mm->indexes); - size_t ebo_len = mt->indexes.len * sizeof(U32); - assert(mm->ebo.len >= ebo_len); - U32* staging_ebo = (U32*)MargaretSubbuf_get_mapped(&mm->staging_ebo); - memcpy(staging_ebo, mt->indexes.buf, ebo_len); - margaret_rec_cmd_copy_buffer_one_to_one(command_buffer, &mm->staging_ebo, &mm->ebo); - } -} - -#endif diff --git a/src/l2/tests/r0/r0_tex_init_prep.c b/src/l2/tests/r0/r0_tex_init_prep.c deleted file mode 100644 index 3d46906..0000000 --- a/src/l2/tests/r0/r0_tex_init_prep.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "r0_assets.h" -#include "../../marie/rasterization.h" -#include "../../../../gen/l1/margaret/png_pixel_masses.h" -#include "../../marie/texture_processing.h" - -void for_log(U32 w, U32 r, U32 k) { - { - TextureDataR8G8B8A8 tex = generate_tex_template_for_one_fourth_of_a_cylinder(120, (float)w, (float)r, k); - TextureDataR8G8B8A8 fixed_tex = TextureDataR8G8B8A8_expand_nontransparent_1px(&tex); - VecU8 name = VecU8_format("textures/log_%u_%u_%u_TEMPLATE.png", w, r, k); - TextureDataR8G8B8A8_write_to_png_nofail(&fixed_tex, VecU8_to_span(&name)); - VecU8_drop(name); - TextureDataR8G8B8A8_drop(fixed_tex); - TextureDataR8G8B8A8_drop(tex); - } - { - TextureDataR8G8B8A8 tex = generate_normal_tex_for_one_fourth_of_a_cylinder(120, (float)w, (float)r, k); - TextureDataR8G8B8A8 fixed_tex = TextureDataR8G8B8A8_expand_nontransparent_1px(&tex); - VecU8 name = VecU8_format("textures/log_%u_%u_%u_NORMAL.png", w, r, k); - TextureDataR8G8B8A8_write_to_png_nofail(&fixed_tex, VecU8_to_span(&name)); - VecU8_drop(name); - TextureDataR8G8B8A8_drop(fixed_tex); - TextureDataR8G8B8A8_drop(tex); - } -} - -int main() { - // TextureDataR8G8B8A8 tex = TextureDataR8G8B8A8_read_from_png_nofail(cstr("/home/gregory/test/basn3p04.png")); - // TextureDataR8G8B8A8_print(&tex); - // TextureDataR8G8B8A8_drop(tex); - // - // TextureDataR8G8B8 TEX = TextureDataR8G8B8_read_from_png_nofail(cstr("/home/gregory/test/basn3p04.png")); - // TextureDataR8G8B8_print(&TEX); - // TextureDataR8G8B8_drop(TEX); - - // TextureDataR8 tex = TextureDataR8_read_from_png_nofail(cstr("textures/log_5_5_10_TEMPLATE.png")); - // TextureDataR8_print(&tex); - // TextureDataR8_drop(tex); - - for_log(10, 2, 6); - for_log(5, 5, 10); - for_log(1, 10, 4); - for_log(2, 1, 6); - return 0; -} diff --git a/src/l2/tests/r0/textures/log_1_10_4_diffuse.png b/src/l2/tests/r0/textures/log_1_10_4_diffuse.png deleted file mode 100644 index 11bf115b5515c05c95821be94335088f233676d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57154 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 HVbOm9$z?Y& diff --git a/src/l2/tests/r0/textures/log_1_10_4_specular.png b/src/l2/tests/r0/textures/log_1_10_4_specular.png deleted file mode 100644 index 11bf115b5515c05c95821be94335088f233676d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57154 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 HVbOm9$z?Y& diff --git a/src/l2/tests/r0/textures/log_2_1_6_diffuse.png b/src/l2/tests/r0/textures/log_2_1_6_diffuse.png deleted file mode 100644 index 06a057594140936ed31ac5507a104a08c0bd479e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2894 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