diff --git a/.gitignore b/.gitignore index 4306bd3..1fa5686 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ vgcore.* /gen/ *.pdf *.r8g8b8a8 -*.r8b8g8 +*.r8g8b8 *.r8 +*.a8 *.xcf +*_NORMAL.png +*_TEMPLATE.png \ No newline at end of file diff --git a/src/l1/core/util.h b/src/l1/core/util.h index 383d54d..886cd44 100644 --- a/src/l1/core/util.h +++ b/src/l1/core/util.h @@ -310,4 +310,8 @@ T OptionT##_expect(OptionT self){ \ #define OptionT_struct_Definition(T) OptionT_struct_Definition_custom_name(T, Option##T) #define OptionT_method_Definition(T) OptionT_method_Definition_custom_name(T, Option##T) +float pow2f(float x) { + return x * x; +} + #endif diff --git a/src/l1/gen/geom_and_textures.h b/src/l1/gen/geom_and_textures.h index 18a7082..a4c036c 100644 --- a/src/l1/gen/geom_and_textures.h +++ b/src/l1/gen/geom_and_textures.h @@ -214,6 +214,43 @@ NODISCARD VecU8 generate_xvecy_method_and_one(ConstSpanU8 xvec, int n) { return res; } +NODISCARD VecU8 generate_xvecy_method_dot(ConstSpanU8 xvec, ConstSpanU8 member, int n) { + VecU8 res = VecU8_from_span(member); + VecU8_append(&res, ' '); + string_append_xvecy(&res, xvec, n); + VecU8_append_span(&res, cstr("_dot(")); + string_append_xvecy(&res, xvec, n); + VecU8_append_span(&res, cstr(" A, ")); + string_append_xvecy(&res, xvec, n); + VecU8_append_span(&res, cstr(" B) {\n" SPACE4 "return ")); + for (int i = 0; i < n; i++) { + if (i) + VecU8_append_span(&res, cstr(" + ")); + VecU8_append_span(&res, cstr("A.")); + string_append_vec_field_name(&res, i); + VecU8_append_span(&res, cstr(" * B.")); + string_append_vec_field_name(&res, i); + } + VecU8_append_span(&res, cstr(";\n}\n\n")); + return res; +} + +NODISCARD VecU8 generate_xvec3_method_cross(ConstSpanU8 xvec) { + VecU8 res = VecU8_new(); + string_append_xvecy(&res, xvec, 3); + VecU8_append(&res, ' '); + string_append_xvecy(&res, xvec, 3); + VecU8_append_span(&res, cstr("_cross(")); + string_append_xvecy(&res, xvec, 3); + VecU8_append_span(&res, cstr(" A, ")); + string_append_xvecy(&res, xvec, 3); + VecU8_append_span(&res, cstr(" B) {\n" SPACE4 "return (")); + string_append_xvecy(&res, xvec, 3); + VecU8_append_span(&res, cstr("){A.y * B.z - A.z * B.y, -A.x * B.z + A.z * B.x, A.x * B.y - A.y * B.x};\n}\n\n")); + return res; +} + + void string_append_xmatnm(VecU8* str, ConstSpanU8 xmat, int cols, int rows) { VecU8_append_span(str, xmat); VecU8_append(str, '0' + cols); @@ -439,29 +476,6 @@ NODISCARD VecU8 generate_xmatnm_method_div_by_scal(ConstSpanU8 xmat, ConstSpanU8 return res; } -NODISCARD VecU8 generate_xvecn_method_dot_xvecn(ConstSpanU8 xvec, ConstSpanU8 member, int n) { - VecU8 res = VecU8_from_span(member); - VecU8_append(&res, ' '); - string_append_xvecy(&res, xvec, n); - VecU8_append_span(&res, cstr("_dot_")); - string_append_xvecy(&res, xvec, n); - VecU8_append(&res, '('); - string_append_xvecy(&res, xvec, n); - VecU8_append_span(&res, cstr(" A, ")); - string_append_xvecy(&res, xvec, n); - VecU8_append_span(&res, cstr(" B) {\n" SPACE4 "return ")); - for (int i = 0; i < n; i++) { - if (i) - VecU8_append_span(&res, cstr(" + ")); - VecU8_append_span(&res, cstr("A.")); - string_append_vec_field_name(&res, i); - VecU8_append_span(&res, cstr(" * B.")); - string_append_vec_field_name(&res, i); - } - VecU8_append_span(&res, cstr(";\n}\n\n")); - return res; -} - NODISCARD VecU8 generate_xmatnm_method_mul_xvecn(ConstSpanU8 xmat, ConstSpanU8 xvec, int n, int m) { VecU8 res = VecU8_new(); string_append_xvecy(&res, xvec, m); @@ -575,9 +589,11 @@ NODISCARD VecU8 generate_xvec234_structs_and_methods(ConstSpanU8 xvec, ConstSpan VecU8_append_vec(&res, generate_xvecy_method_mul_scal(xvec, member, cc)); VecU8_append_vec(&res, generate_xvecy_method_div_by_scal(xvec, member, cc)); VecU8_append_vec(&res, generate_xvecy_method_mul_xvecy(xvec, member, cc)); + VecU8_append_vec(&res, generate_xvecy_method_dot(xvec, member, cc)); } for (int n = 2; n <= 3; n++) VecU8_append_vec(&res, generate_xvecy_method_and_one(xvec, n)); + VecU8_append_vec(&res, generate_xvec3_method_cross(xvec)); return res; } diff --git a/src/l2/margaret/margaret.h b/src/l2/margaret/margaret.h index a274929..2069a1c 100644 --- a/src/l2/margaret/margaret.h +++ b/src/l2/margaret/margaret.h @@ -848,7 +848,7 @@ VkSwapchainKHR MargaretSwapchainBundle_pop_swapchain_drop_rest(VkDevice device, // Not a regular _drop method, because it requires a bundled VkDevice void MargaretSwapchainBundle_drop_with_device(VkDevice device, MargaretSwapchainBundle swfb) { VkSwapchainKHR swapchain = MargaretSwapchainBundle_pop_swapchain_drop_rest(device, swfb); - vkDestroySwapchainKHR(device, swfb.swapchain, NULL); + vkDestroySwapchainKHR(device, swapchain, NULL); // Now swapchain bundle is 100% dropped } @@ -1114,11 +1114,16 @@ MargaretBufferInMemoryInfo margaret_prep_buffer_mem_info_of_small_local_ubo(size return (MargaretBufferInMemoryInfo){ .sz = struct_sz, .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT }; } -MargaretImageInMemoryInfo margaret_prep_image_mem_info_of_gpu_texture_rgba(uint32_t w, uint32_t h) { +MargaretImageInMemoryInfo margaret_prep_image_mem_info_of_gpu_texture_srgba(uint32_t w, uint32_t h) { return (MargaretImageInMemoryInfo){ .width = w, .height = h, .format = VK_FORMAT_R8G8B8A8_SRGB, .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT }; } +MargaretImageInMemoryInfo margaret_prep_image_mem_info_of_gpu_texture_unorm_32(uint32_t w, uint32_t h) { + return (MargaretImageInMemoryInfo){ .width = w, .height = h, .format = VK_FORMAT_R8G8B8A8_UNORM, + .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT }; +} + MargaretImageInMemoryInfo margaret_prep_image_mem_info_of_zbuffer(uint32_t max_width, uint32_t max_height, VkFormat zbuf_format) { return (MargaretImageInMemoryInfo){ .width = max_width, .height = max_height, .format = zbuf_format, .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT }; @@ -1303,16 +1308,16 @@ VkImageView margaret_create_view_for_image ( } // For texture -VkSampler margaret_create_sampler(VkPhysicalDevice physical_device, VkDevice device) { +VkSampler margaret_create_sampler(VkPhysicalDevice physical_device, VkDevice device, bool make_linear) { VkPhysicalDeviceProperties physical_device_properties; vkGetPhysicalDeviceProperties(physical_device, &physical_device_properties); VkPhysicalDeviceFeatures physical_device_features; vkGetPhysicalDeviceFeatures(physical_device, &physical_device_features); VkSamplerCreateInfo crinfo = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .magFilter = make_linear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST, + .minFilter = make_linear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST, + .mipmapMode = make_linear? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST, .addressModeU = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, .addressModeV = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, .addressModeW = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, diff --git a/src/l2/marie/graphics_geom.h b/src/l2/marie/graphics_geom.h index 36fcd00..5711d7a 100644 --- a/src/l2/marie/graphics_geom.h +++ b/src/l2/marie/graphics_geom.h @@ -74,4 +74,13 @@ mat3 marie_simple_camera_rot_m_basis_in_cols(float yaw, float pitch, float roll) }; } +vec2 marie_trigonom_circle(float angle) { + return (vec2){cosf(angle), sinf(angle)}; +} + +vec3 marie_normal_from_tang_space_gradient(float delt_x, float delta_z) { + float N = 1 / sqrtf(delt_x * delt_x + delta_z * delta_z + 1); + return (vec3){-delt_x * N, N, -delta_z * N}; +} + #endif diff --git a/src/l2/tests/r0.c b/src/l2/tests/r0.c index 87c052e..4c867bb 100644 --- a/src/l2/tests/r0.c +++ b/src/l2/tests/r0.c @@ -205,7 +205,6 @@ PipelineHands create_graphics_pipeline_0( VkDescriptorSetLayoutBinding bindings_for_my_descr_set_layout[] = { { - // Binding in shader .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, // our shader variable is not an array of descriptors, so this stays 1 @@ -218,6 +217,12 @@ PipelineHands create_graphics_pipeline_0( .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, }, + { + .binding = 2, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + }, }; VkDescriptorSetLayoutCreateInfo descriptor_set_layout_crinfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, @@ -715,14 +720,19 @@ int main() { ModelTopology cylinder_1 = generate_one_fourth_of_a_cylinder(10, 2, 6); ModelTopology cylinder_2 = generate_one_fourth_of_a_cylinder(5, 5, 10); // TextureDataR8G8B8A8 wood_texture_data = generate_texture_for_one_fourth_of_a_cylinder(20, 10, 2, 6); - TextureDataR8G8B8A8 wood_texture_data = TextureDataR8G8B8A8_read_from_file("test_textures/log_10_2_6.r8g8b8a8"); + // todo: learn how to use libpng + TextureDataR8G8B8A8 cyl_1_diffuse_tex = TextureDataR8G8B8A8_read_from_file("test_textures/log_10_2_6.r8g8b8a8"); + // todo: learn how to write texture immediately from file to mapped host memory buffer + // todo: and at the same time I need to add methods to convert between these formats + TextureDataR8G8B8A8 cyl_1_normal_tex = TextureDataR8G8B8A8_read_from_file("test_textures/log_10_2_6_NORMAL.r8g8b8a8"); // We have only one staging buffer in host memory (because we don't really need more) MargaretBufferInMemoryInfo host_mem_buffer = (MargaretBufferInMemoryInfo){ .sz = MAX_U64(ModelTopology_get_space_needed_for_staging_buffer(&cylinder_1), MAX_U64(ModelTopology_get_space_needed_for_staging_buffer(&cylinder_2), MAX_U64(sizeof(Pipeline0UBO), - MAX_U64(TextureDataR8G8B8A8_get_size_in_bytes(&wood_texture_data), 0)))) + MAX_U64(TextureDataR8G8B8A8_get_size_in_bytes(&cyl_1_diffuse_tex), + MAX_U64(TextureDataR8G8B8A8_get_size_in_bytes(&cyl_1_normal_tex), 0))))) , .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT }; VkDeviceMemory host_mem = margaret_initialize_buffers_and_images(physical_device, device, (SpanMargaretBufferInMemoryInfo){.data = &host_mem_buffer, .len = 1}, (SpanMargaretImageInMemoryInfo){ 0 }, @@ -736,11 +746,14 @@ int main() { margaret_prep_buffer_mem_info_of_gpu_ubo(sizeof(Pipeline0UBO)), }; MargaretImageInMemoryInfo device_mem_images[] = { - margaret_prep_image_mem_info_of_gpu_texture_rgba(wood_texture_data.width, - TextureDataR8G8B8A8_get_height(&wood_texture_data)), margaret_prep_image_mem_info_of_colorbuffer(MAX_WIN_WIDTH, MAX_WIN_HEIGHT, IT1_format.some), margaret_prep_image_mem_info_of_zbuffer(MAX_WIN_WIDTH, MAX_WIN_HEIGHT, zbuffer_format.some), + margaret_prep_image_mem_info_of_gpu_texture_srgba(cyl_1_diffuse_tex.width, + TextureDataR8G8B8A8_get_height(&cyl_1_diffuse_tex)), + margaret_prep_image_mem_info_of_gpu_texture_srgba(cyl_1_normal_tex.width, + TextureDataR8G8B8A8_get_height(&cyl_1_normal_tex)), }; + // todo: replace this ugly garbage with pointer spans. Seriously, this is fucking digusting VkDeviceMemory device_mem = margaret_initialize_buffers_and_images(physical_device, device, (SpanMargaretBufferInMemoryInfo){ .data = device_mem_buffers, .len = ARRAY_SIZE(device_mem_buffers)}, (SpanMargaretImageInMemoryInfo){ .data = device_mem_images, .len = ARRAY_SIZE(device_mem_images) }, @@ -750,9 +763,10 @@ int main() { MargaretBufferInMemoryInfo device_vbo_2_buffer = device_mem_buffers[2]; MargaretBufferInMemoryInfo device_ebo_2_buffer = device_mem_buffers[3]; MargaretBufferInMemoryInfo device_ubo_my_buffer = device_mem_buffers[4]; - MargaretImageInMemoryInfo device_wood_texture = device_mem_images[0]; - MargaretImageInMemoryInfo device_IT1_image = device_mem_images[1]; - MargaretImageInMemoryInfo device_zbuffer_image = device_mem_images[2]; + MargaretImageInMemoryInfo device_IT1_image = device_mem_images[0]; + MargaretImageInMemoryInfo device_zbuffer_image = device_mem_images[1]; + MargaretImageInMemoryInfo device_cyl_1_diffuse_texture = device_mem_images[2]; + MargaretImageInMemoryInfo device_cyl_1_normal_texture = device_mem_images[3]; VkCommandPool command_pool = margaret_create_resettable_command_pool(device, queue_fam.for_graphics); VkCommandBuffer rendering_command_buffer_0 = margaret_allocate_command_buffer(device, command_pool); @@ -790,14 +804,20 @@ int main() { margaret_copy_buffer_imm(device, command_pool, graphics_queue, device_ebo_2_buffer.buffer, host_mem_buffer.buffer, size); } - memcpy(host_mem_buffer_mem, wood_texture_data.pixels.buf, - TextureDataR8G8B8A8_get_size_in_bytes(&wood_texture_data)); - margaret_copy_buffer_to_texture_for_frag_shader_imm(device, command_pool, graphics_queue, - &device_wood_texture, host_mem_buffer.buffer); + { + memcpy(host_mem_buffer_mem, cyl_1_diffuse_tex.pixels.buf, + TextureDataR8G8B8A8_get_size_in_bytes(&cyl_1_diffuse_tex)); + margaret_copy_buffer_to_texture_for_frag_shader_imm(device, command_pool, graphics_queue, + &device_cyl_1_diffuse_texture, host_mem_buffer.buffer); + } + { + memcpy(host_mem_buffer_mem, cyl_1_normal_tex.pixels.buf, TextureDataR8G8B8A8_get_size_in_bytes(&cyl_1_normal_tex)); + margaret_copy_buffer_to_texture_for_frag_shader_imm(device, command_pool, graphics_queue, + &device_cyl_1_normal_texture, host_mem_buffer.buffer); + } + // We sent everything we needed. but host_mem_buffer_mem may be used later - // My wood texture needs VkImageView - VkImageView wood_texture_view = margaret_create_view_for_image(device, &device_wood_texture, VK_IMAGE_ASPECT_COLOR_BIT); // My zbuffer also needs a view VkImageView zbuffer_view = margaret_create_view_for_image(device, &device_zbuffer_image, VK_IMAGE_ASPECT_DEPTH_BIT); /* Here we create an image view into a temporary IT1 texture and a framebuffer for scene rendering */ @@ -805,17 +825,23 @@ int main() { VkFramebuffer IT1_framebuffer = create_IT1_framebuffer(device, IT1_view, zbuffer_view, render_pass_0, MAX_WIN_WIDTH, MAX_WIN_HEIGHT); + // My cylinder 1 texture needs VkImageView + VkImageView cyl_1_diffuse_texture_view = margaret_create_view_for_image(device, &device_cyl_1_diffuse_texture, VK_IMAGE_ASPECT_COLOR_BIT); + // My cylinder 1 normal texture also needs NkImageView + VkImageView cyl_1_normal_texture_view = margaret_create_view_for_image(device, &device_cyl_1_normal_texture, VK_IMAGE_ASPECT_COLOR_BIT); + Scene scene = Scene_new(); VecUsedModelOnScene_append(&scene.models, (UsedModelOnScene){.model = { .vbo = device_vbo_1_buffer.buffer, .ebo = device_ebo_1_buffer.buffer, .indexes = cylinder_1.indexes.len }, .model_t = marie_translation_mat4((vec3){1, -1, 5}) }); VecUsedModelOnScene_append(&scene.models, (UsedModelOnScene){.model = { .vbo = device_vbo_2_buffer.buffer, .ebo = device_ebo_2_buffer.buffer, .indexes = cylinder_2.indexes.len - }, .model_t = marie_translation_mat4((vec3){6, -3, 7}) }); + }, .model_t = marie_translation_mat4((vec3){6, -3, 16}) }); - // Sampler is global for a lot of my future textures - VkSampler my_texture_sampler = margaret_create_sampler(physical_device, device); - VkDescriptorPool descriptor_pool = margaret_create_descriptor_set_pool(device, 1, 2, 2); + // These samplers are global for a lot of my future textures + VkSampler linear_sampler = margaret_create_sampler(physical_device, device, true); + VkSampler nearest_sampler = margaret_create_sampler(physical_device, device, false); + VkDescriptorPool descriptor_pool = margaret_create_descriptor_set_pool(device, 1, 3, 2); VkDescriptorSet descriptor_set_for_pipeline_0 = margaret_allocate_descriptor_set(device, descriptor_pool, pipeline_hands_0.descriptor_set_layout); VkDescriptorSet descriptor_set_for_pipeline_1 = margaret_allocate_descriptor_set(device, descriptor_pool, pipeline_hands_1.descriptor_set_layout); @@ -827,12 +853,17 @@ int main() { .range = sizeof(Pipeline0UBO), }; VkDescriptorImageInfo image_info_for_descriptor_1_in_set_0 = { - .sampler = my_texture_sampler, - .imageView = wood_texture_view, + .sampler = linear_sampler, + .imageView = cyl_1_diffuse_texture_view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; + VkDescriptorImageInfo image_info_for_descriptor_2_in_set_0 = { + .sampler = nearest_sampler, + .imageView = cyl_1_normal_texture_view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; VkDescriptorImageInfo image_info_for_descriptor_0_in_set_1 = { - .sampler = my_texture_sampler, + .sampler = nearest_sampler, .imageView = IT1_view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; @@ -855,6 +886,15 @@ int main() { .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .pImageInfo = &image_info_for_descriptor_1_in_set_0, }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_set_for_pipeline_0, + .dstBinding = 2, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &image_info_for_descriptor_2_in_set_0, + }, { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, @@ -887,7 +927,6 @@ int main() { break; for (size_t i = 0; i < events.len; i++) { Xlib_Event* ev = VecXlib_Event_at(&events, i); - // printf("%lu %lu\n", ev->xany.window, wep.win); if (ev->xany.window != wep.win) continue; Margaret_WEP_update_with_new_event(&wep, ev); @@ -1086,16 +1125,20 @@ int main() { ModelTopology_drop(cylinder_2); // destroying vulkan objects vkDestroyDescriptorPool(device, descriptor_pool, NULL); - vkDestroySampler(device, my_texture_sampler, NULL); - vkDestroyImageView(device, wood_texture_view, NULL); + vkDestroySampler(device, linear_sampler, NULL); + vkDestroySampler(device, nearest_sampler, NULL); + vkDestroyImageView(device, cyl_1_diffuse_texture_view, NULL); + vkDestroyImageView(device, cyl_1_normal_texture_view, NULL); vkDestroyBuffer(device, device_vbo_1_buffer.buffer, NULL); vkDestroyBuffer(device, device_ebo_1_buffer.buffer, NULL); vkDestroyBuffer(device, device_vbo_2_buffer.buffer, NULL); vkDestroyBuffer(device, device_ebo_2_buffer.buffer, NULL); vkDestroyBuffer(device, device_ubo_my_buffer.buffer, NULL); - vkDestroyImage(device, device_wood_texture.image, NULL); + vkDestroyImage(device, device_cyl_1_diffuse_texture.image, NULL); + vkDestroyImage(device, device_cyl_1_normal_texture.image, NULL); vkDestroyImage(device, device_IT1_image.image, NULL); + vkDestroyImage(device, device_zbuffer_image.image, NULL); vkDestroyBuffer(device, host_mem_buffer.buffer, NULL); vkFreeMemory(device, device_mem, NULL); diff --git a/src/l2/tests/r0_assets.h b/src/l2/tests/r0_assets.h index 6487494..14995c3 100644 --- a/src/l2/tests/r0_assets.h +++ b/src/l2/tests/r0_assets.h @@ -7,6 +7,7 @@ #include "../../l1/system/fileio.h" #include #include "../../../gen/pixel_masses.h" +#include "../marie/rasterization.h" typedef struct { vec3 pos; @@ -76,8 +77,8 @@ VecT_trivmove_struct_Definition(Pipeline0PointLight) VecT_trivmove_method_Definition(Pipeline0PointLight) VecT_primitive_zeroinit_method_Definition(Pipeline0PointLight) -const size_t pipeline_0_ubo_point_light_max_count = 20; -const size_t pipeline_0_ubo_spotlight_max_count = 120; +#define pipeline_0_ubo_point_light_max_count 20 +#define pipeline_0_ubo_spotlight_max_count 120 typedef struct { int spotlight_count; @@ -165,20 +166,20 @@ void TextureDataR8_draw_vertical_inner_line_maxing(TextureDataR8* self, } void TextureDataR8_draw_inner_line_maxing(TextureDataR8* self, - float x1, float y1, float x2, float y2, float r_cut, float r_decay) { - float dx = fabsf(x2 - x1); - float dy = fabsf(y2 - y1); + vec2 v1, vec2 v2, float r_cut, float r_decay) { + float dx = fabsf(v2.x - v1.x); + float dy = fabsf(v2.y - v1.y); if (dx > dy) { - if (x1 < x2) { - TextureDataR8_draw_horizontal_inner_line_maxing(self, x1, y1, x2, y2, r_cut, r_decay); + if (v1.x < v2.x) { + TextureDataR8_draw_horizontal_inner_line_maxing(self, v1.x, v1.y, v2.x, v2.y, r_cut, r_decay); } else { - TextureDataR8_draw_horizontal_inner_line_maxing(self, x2, y2, x1, y1, r_cut, r_decay); + TextureDataR8_draw_horizontal_inner_line_maxing(self, v2.x, v2.y, v1.x, v1.y, r_cut, r_decay); } } else { - if (y1 < y2) { - TextureDataR8_draw_vertical_inner_line_maxing(self, x1, y1, x2, y2, r_cut, r_decay); + if (v1.y < v2.y) { + TextureDataR8_draw_vertical_inner_line_maxing(self, v1.x, v1.y, v2.x, v2.y, r_cut, r_decay); } else { - TextureDataR8_draw_vertical_inner_line_maxing(self, x2, y2, x1, y1, r_cut, r_decay); + TextureDataR8_draw_vertical_inner_line_maxing(self, v2.x, v2.y, v1.x, v1.y, r_cut, r_decay); } } } @@ -201,35 +202,35 @@ void TextureDataR8_draw_one_segment_maxing(TextureDataR8* self, vec2 v1, vec2 v2, float r_cut, float r_decay) { TextureDataR8_draw_spot_maxing(self, v1, r_cut, r_decay); TextureDataR8_draw_spot_maxing(self, v2, r_cut, r_decay); - TextureDataR8_draw_inner_line_maxing(self, v1.x, v1.y, v2.x, v2.y, r_cut, r_decay); + TextureDataR8_draw_inner_line_maxing(self, v1, v2, r_cut, r_decay); } -TextureDataR8G8B8A8 generate_wood_texture() { - const U32 width = 100; - const U32 height = 100; - TextureDataR8G8B8A8 res = TextureDataR8G8B8A8_new(width, height); - for (U32 y = 0; y < width; y++) { - for (U32 col = 0; col < height; col++) { - *TextureDataR8G8B8A8_at(&res, col, y) = (cvec4){150, 30, 50, 255}; - } - } - for (U32 i = 0; i < 10; i++) { - for (U32 y = 0; y < height; y++) { - U32 col = 3 + i * 10 + ((30 < y + 3 * i && y - i < 60) ? 1 : 0); - - *TextureDataR8G8B8A8_at(&res, col, y) = (cvec4){130, 25, 40, 255}; - *TextureDataR8G8B8A8_at(&res, col + 1, y) = (cvec4){80, 10, 15, 255}; - *TextureDataR8G8B8A8_at(&res, col + 2, y) = (cvec4){70, 11, 12, 255}; - *TextureDataR8G8B8A8_at(&res, col + 3, y) = (cvec4){125, 20, 20, 255}; - } - } - for (U32 y = 0; y < 10; y++) { - for (U32 col = 0; col < 10; col++) { - *TextureDataR8G8B8A8_at(&res, col + 4, y + 13) = (cvec4){60, 8, 6, 255}; - } - } - return res; -} +// TextureDataR8G8B8A8 generate_wood_texture() { +// const U32 width = 100; +// const U32 height = 100; +// TextureDataR8G8B8A8 res = TextureDataR8G8B8A8_new(width, height); +// for (U32 y = 0; y < width; y++) { +// for (U32 col = 0; col < height; col++) { +// *TextureDataR8G8B8A8_at(&res, col, y) = (cvec4){150, 30, 50, 255}; +// } +// } +// for (U32 i = 0; i < 10; i++) { +// for (U32 y = 0; y < height; y++) { +// U32 col = 3 + i * 10 + ((30 < y + 3 * i && y - i < 60) ? 1 : 0); +// +// *TextureDataR8G8B8A8_at(&res, col, y) = (cvec4){130, 25, 40, 255}; +// *TextureDataR8G8B8A8_at(&res, col + 1, y) = (cvec4){80, 10, 15, 255}; +// *TextureDataR8G8B8A8_at(&res, col + 2, y) = (cvec4){70, 11, 12, 255}; +// *TextureDataR8G8B8A8_at(&res, col + 3, y) = (cvec4){125, 20, 20, 255}; +// } +// } +// for (U32 y = 0; y < 10; y++) { +// for (U32 col = 0; col < 10; col++) { +// *TextureDataR8G8B8A8_at(&res, col + 4, y + 13) = (cvec4){60, 8, 6, 255}; +// } +// } +// return res; +// } ModelTopology generate_one_fourth_of_a_cylinder(float w, float r, U32 k) { assert(k >= 1); @@ -244,49 +245,73 @@ ModelTopology generate_one_fourth_of_a_cylinder(float w, float r, U32 k) { VecVertex_append(&vertices, (Vertex){.pos = {w, 0, 0}, .tex = v1tex}); VecVertex_append(&vertices, (Vertex){.pos = {0, r, 0}, .tex = v2tex}); VecVertex_append(&vertices, (Vertex){.pos = {w, r, 0}, .tex = v3tex}); - VecVertex_append(&vertices, (Vertex){.pos = {0, 0, r}, .tex = {r / (2 * r + w), 0}}); - VecVertex_append(&vertices, (Vertex){.pos = {w, 0, r}, .tex = {(r + w) / (2 * r + w), 0}}); + VecVertex_append(&vertices, (Vertex){.pos = {0, 0, -r}, .tex = {r / (2 * r + w), 0}}); + VecVertex_append(&vertices, (Vertex){.pos = {w, 0, -r}, .tex = {(r + w) / (2 * r + w), 0}}); for (U32 i = 1; i <= k; i++) { VecVertex_append(&vertices, (Vertex){ - .pos = {0, cosf(a * i) * r, sinf(a * i) * r}, + .pos = {0, cosf(a * i) * r, -sinf(a * i) * r}, .tex = vec2_add_vec2(v0tex, (vec2){r / (2 * r + w) * -sinf(a * i), r / (2 * r + k * l) * cos(a * i)}) }); } for (U32 i = 1; i <= k; i++) { VecVertex_append(&vertices, (Vertex){ - .pos = {w, cosf(a * i) * r, sinf(a * i) * r}, + .pos = {w, cosf(a * i) * r, -sinf(a * i) * r}, .tex = vec2_add_vec2(v1tex, (vec2){r / (2 * r + w) * sinf(a * i), r / (2*r + k * l) * cos(a * i)}) }); } for (U32 i = 1; i <= k; i++) { VecVertex_append(&vertices, (Vertex){ - .pos = {0, cosf(a * i) * r, sinf(a * i) * r}, + .pos = {0, cosf(a * i) * r, -sinf(a * i) * r}, .tex = {v2tex.x, v2tex.y + i * l / (2*r + k * l)} }); } for (U32 i = 1; i <= k; i++) { VecVertex_append(&vertices, (Vertex){ - .pos = {w, cosf(a * i) * r, sinf(a * i) * r}, + .pos = {w, cosf(a * i) * r, -sinf(a * i) * r}, .tex = {v3tex.x, v3tex.y + i * l / (2*r + k * l)} }); } VecU32 indexes = VecU32_new(); // todo: reserve 3 * (2+2+2*k+2*k)< { - U32 _span_0[] = { 5, 0, 1, 5, 4, 0, 1, 0, 3, 3, 0, 2 }; + U32 _span_0[] = {5, 1, 0, 5, 0, 4, 1, 3, 0, 3, 2, 0}; VecU32_append_span(&indexes, (ConstSpanU32){.data = _span_0, .len = ARRAY_SIZE(_span_0)}); } for (U32 i = 1; i <= k; i++) { U32 _span_1[] = { - 0, 5 + i, i > 1 ? 5 + i - 1 : 2, - 1, i > 1 ? 5 + k + i - 1 : 3, 5 + k + i, - i > 1 ? 5 + 2 * k + i - 1 : 2, 5 + 2 * k + i, i > 1 ? 5 + 3 * k + i - 1 : 3, - 5 + 3 * k + i, i > 1 ? 5 + 3 * k + i - 1 : 3, 5 + 2 * k + i + 0, i > 1 ? 5 + i - 1 : 2, 5 + i, + 1, 5 + k + i, i > 1 ? 5 + k + i - 1 : 3, + i > 1 ? 5 + 2 * k + i - 1 : 2, i > 1 ? 5 + 3 * k + i - 1 : 3, 5 + 2 * k + i, + 5 + 3 * k + i, 5 + 2 * k + i, i > 1 ? 5 + 3 * k + i - 1 : 3, }; VecU32_append_span(&indexes, (ConstSpanU32){.data = _span_1, .len = ARRAY_SIZE(_span_1)}); } return (ModelTopology){.vertices = vertices, .indexes = indexes}; } +#define vec2_drop(x) {} +#define vec2_clone(x) (*(x)) + +VecT_trivmove_struct_Definition(vec2) +VecT_trivmove_method_Definition(vec2) +VecT_primitive_zeroinit_method_Definition(vec2) +SpanT_struct_Definition(vec2) +SpanT_method_Definition(vec2) +SpanT_VecT_method_Definition(vec2) + +void TextureDataR8_draw_perimeter_maxing(TextureDataR8* self, ConstSpanvec2 P) { + float r_cut = 2; + float r_decay = 1; + size_t S = P.len; + for (size_t i = 0; i < S; i++) { + TextureDataR8_draw_spot_maxing(self, *ConstSpanvec2_at(P, i), r_cut, r_decay); + } + for (size_t i = 1; i < S; i++) { + TextureDataR8_draw_inner_line_maxing(self, *ConstSpanvec2_at(P, i - 1), *ConstSpanvec2_at(P, i), r_cut, r_decay); + } + if (S > 2) + TextureDataR8_draw_inner_line_maxing(self, *ConstSpanvec2_at(P, S - 1), *ConstSpanvec2_at(P, 0), r_cut, r_decay); +} + typedef struct { vec2 bl; vec2 tr; @@ -303,7 +328,7 @@ VecT_primitive_zeroinit_method_Definition(Wimbzle) typedef struct { vec2 center; - vec2 rad; + float rad; float hc; } Nibzle; @@ -315,22 +340,22 @@ VecT_trivmove_method_Definition(Nibzle) VecT_primitive_zeroinit_method_Definition(Nibzle) typedef struct { - VecWimbzle wibmzles; + VecWimbzle wimbzles; VecNibzle nibzles; } Bublazhuzhka; Bublazhuzhka Bublazhuzhka_new() { - return (Bublazhuzhka){.wibmzles = VecWimbzle_new(), .nibzles = VecNibzle_new()}; + return (Bublazhuzhka){.wimbzles = VecWimbzle_new(), .nibzles = VecNibzle_new()}; } void Bublazhuzhka_drop(Bublazhuzhka self) { - VecWimbzle_drop(self.wibmzles); + VecWimbzle_drop(self.wimbzles); VecNibzle_drop(self.nibzles); } -void Bublazhuzhka_TextureDataR8_draw_maxing(const Bublazhuzhka* self, TextureDataR8* canvas, vec2) { - for (size_t i = 0; i < self->wibmzles.len; i++) { - Wimbzle rect = *VecWimbzle_cat(&self->wibmzles, i); +void Bublazhuzhka_TextureDataR8_draw_maxing(const Bublazhuzhka* self, TextureDataR8* canvas, mat3x2 trop) { + for (size_t i = 0; i < self->wimbzles.len; i++) { + Wimbzle rect = *VecWimbzle_cat(&self->wimbzles, i); vec2 B = {rect.tr.x + rect.brd, rect.tr.y + rect.brd}; vec2 C = {rect.bl.x - rect.brd, rect.bl.y - rect.brd}; vec2 A = {C.x, B.y}; @@ -340,16 +365,199 @@ void Bublazhuzhka_TextureDataR8_draw_maxing(const Bublazhuzhka* self, TextureDat vec2 E = {G.x, F.y}; vec2 H = {F.x, G.y}; - TextureDataR8_draw_one_segment_maxing(canvas, A, B, 1.5, 1); + vec2 p1[4] = {mat3x2_mul_vec3(trop, vec2_and_one(A)), mat3x2_mul_vec3(trop, vec2_and_one(B)), + mat3x2_mul_vec3(trop, vec2_and_one(D)), mat3x2_mul_vec3(trop, vec2_and_one(C))}; + TextureDataR8_draw_perimeter_maxing(canvas, (ConstSpanvec2){.data = p1, ARRAY_SIZE(p1)}); + vec2 p2[4] = {mat3x2_mul_vec3(trop, vec2_and_one(E)), mat3x2_mul_vec3(trop, vec2_and_one(F)), + mat3x2_mul_vec3(trop, vec2_and_one(H)), mat3x2_mul_vec3(trop, vec2_and_one(G))}; + TextureDataR8_draw_perimeter_maxing(canvas, (ConstSpanvec2){.data = p2, ARRAY_SIZE(p2)}); + } + + for (size_t i = 0; i < self->nibzles.len; i++) { + Nibzle sphere = *VecNibzle_cat(&self->nibzles, i); + Vecvec2 p = Vecvec2_new_zeroinit(13); + for (int j = 0; j < 13; j++) { + float a = (float)j * 2 * M_PI / 13; + *Vecvec2_at(&p, j) = vec2_add_vec2(sphere.center, vec2_mul_scal(marie_trigonom_circle(a), sphere.rad)); + } + TextureDataR8_draw_perimeter_maxing(canvas, Vecvec2_to_ConstSpanvec2(&p)); + Vecvec2_drop(p); + TextureDataR8_draw_spot_maxing(canvas, mat3x2_mul_vec3(trop, vec2_and_one(sphere.center)), 3, 1); } } -#define vec2_drop(x) {} -#define vec2_clone(x) (*(x)) +Bublazhuzhka fill_rectangle_with_crap(float w, float h) { + // Bublazhuzhka res = Bublazhuzhka_new(); + S32 k = MAX_S32(0, (S32)floorf((w/h + 0.1f) / 0.7f)); + VecWimbzle wimbzles = VecWimbzle_new_zeroinit(k); + VecNibzle nibzles = VecNibzle_new_zeroinit(2 * k); + float start = k != 1 ? h * 0.2f : ((w - 0.2f * h) / 2); + float d = k > 1 ? ((w - h * 0.4f - h * 0.2f * k) / (float)(k - 1)) : 0; + for (S32 i = 0; i < k; i++) { + float x = start + (d + h * 0.2) * i; + *VecWimbzle_at(&wimbzles, i) = (Wimbzle){.bl = {x + 0.02 * h, 0.27 * h}, .tr = {x + 0.18 * h, h * 0.73}, .height = h * 0.03, .brd = h * 0.03}; + /* hc is a height coefficient*/ + *VecNibzle_at(&nibzles, 2 * i) = (Nibzle){.center = {x + 0.10 * h, 0.11 * h}, .rad = h * 0.05, .hc = 0.75}; + *VecNibzle_at(&nibzles, 2 * i + 1) = (Nibzle){.center = {x + 0.10 * h, 0.89 * h}, .rad = h * 0.05, .hc = 0.75}; + } + return (Bublazhuzhka){.wimbzles = wimbzles, .nibzles = nibzles}; +} + +vec2 Bublazhuzhka_get_derivative(const Bublazhuzhka* self, vec2 v) { + vec2 sum = { 0 }; + for (size_t i = 0; i < self->wimbzles.len; i++) { + Wimbzle rect = *VecWimbzle_cat(&self->wimbzles, i); + vec2 B = {rect.tr.x + rect.brd, rect.tr.y + rect.brd}; + vec2 C = {rect.bl.x - rect.brd, rect.bl.y - rect.brd}; + vec2 A = {C.x, B.y}; + vec2 D = {B.x, C.y}; + vec2 F = rect.tr; + vec2 G = rect.bl; + vec2 E = {G.x, F.y}; + vec2 H = {F.x, G.y}; + float slp = rect.height / rect.brd; + if (A.x < v.x && v.x < E.x && marie_surface(E, A, v) > 0 && marie_surface(C, G, v) > 0) { + sum.x += slp; + } else if (F.x < v.x && v.x < B.x && marie_surface(B, F, v) > 0 && marie_surface(H, D, v) > 0) { + sum.x -= slp; + } else if (C.y < v.y && v.y < G.y && marie_surface(G, C, v) > 0 && marie_surface(D, H, v) > 0) { + sum.y += slp; + } else if (F.y < v.y && v.y < B.y && marie_surface(A, E, v) > 0 && marie_surface(F, B, v) > 0) { + sum.y -= slp; + } + } + for (size_t i = 0; i < self->nibzles.len; i++) { + Nibzle sphere = *VecNibzle_cat(&self->nibzles, i); + float sq_h = pow2f(sphere.rad) - pow2f(v.x - sphere.center.x) - pow2f(v.y - sphere.center.y); + if (sq_h <= 0) + continue; + float w = sphere.hc / sqrtf(sq_h); + sum = vec2_add_vec2(sum, vec2_mul_scal(vec2_minus_vec2(sphere.center, v), w)); + } + return sum; +} + +cvec3 compress_normal_vec_into_norm_texel(vec3 n) { + return (cvec3){(U32)roundf(255 * (n.x + 1) / 2), (U32)roundf(255 * (n.y + 1) / 2), (U32)roundf(255 * (n.z + 1) / 2)}; +} + + +typedef struct { + /* (guest, param) -> normal vector in model space */ + vec3 (*fn)(void*, vec2); + void* guest; +} FnNormalVectorGenCallback; + +typedef struct { + TextureDataR8G8B8* tex; + FnNormalVectorGenCallback my_client; +} draw_polygon_on_normal_texture_smooth_param_surf_H_DrawGuest; + +void draw_polygon_on_normal_texture_smooth_param_surf_h_draw_cb(void* ug, S32 x, S32 y, vec4 attr) { + draw_polygon_on_normal_texture_smooth_param_surf_H_DrawGuest* g = ug; + vec3 normal = g->my_client.fn(g->my_client.guest, (vec2){attr.x, attr.y}); + *TextureDataR8G8B8_at(g->tex, x, y) = compress_normal_vec_into_norm_texel(normal); +} + +void draw_polygon_on_normal_texture_smooth_param_surf( + TextureDataR8G8B8* tex, vec2 pa, vec2 pb, vec2 pc, mat3x2 trop, FnNormalVectorGenCallback cb + ) { + draw_polygon_on_normal_texture_smooth_param_surf_H_DrawGuest aboba = {.tex = tex, .my_client = cb}; + marie_rasterize_triangle_with_attr( + (MariePlaneVertAttr){.pos = mat3x2_mul_vec3(trop, vec2_and_one(pa)), .attr = {pa.x, pa.y, 0, 0} }, + (MariePlaneVertAttr){.pos = mat3x2_mul_vec3(trop, vec2_and_one(pb)), .attr = {pb.x, pb.y, 0, 0} }, + (MariePlaneVertAttr){.pos = mat3x2_mul_vec3(trop, vec2_and_one(pc)), .attr = {pc.x, pc.y, 0, 0} }, + (FnMarieRasterizerCallback){draw_polygon_on_normal_texture_smooth_param_surf_h_draw_cb, (void*)&aboba}); +} + + +typedef struct { + /* (guest, param) -> normal vector in model space */ + vec3 (*fn)(void*, vec3); + void* guest; +} FnNormalVectorGenExaggParamCallback; + +typedef struct { + TextureDataR8G8B8* tex; + FnNormalVectorGenExaggParamCallback my_client; +} draw_polygon_on_normal_texture_exaggerated_param_surf_H_DrawGuest; + +void draw_polygon_on_normal_texture_exaggerated_param_surf_draw_cb(void* ug, S32 x, S32 y, vec4 attr) { + draw_polygon_on_normal_texture_exaggerated_param_surf_H_DrawGuest* g = ug; + vec3 normal = g->my_client.fn(g->my_client.guest, (vec3){attr.x, attr.y, attr.z}); + *TextureDataR8G8B8_at(g->tex, x, y) = compress_normal_vec_into_norm_texel(normal); +} + +/* We can't derive texture coordinates from parameter space coordinates, you have to do it yourself */ +void draw_polygon_on_normal_texture_nat_cords_exaggerated_param_surf( + TextureDataR8G8B8* tex, vec2 ta, vec2 tb, vec2 tc, vec3 pa, vec3 pb, vec3 pc, FnNormalVectorGenExaggParamCallback cb + ) { + draw_polygon_on_normal_texture_exaggerated_param_surf_H_DrawGuest aboba = {.tex = tex, .my_client = cb}; + marie_rasterize_triangle_with_attr( + (MariePlaneVertAttr){.pos = ta, .attr = {pa.x, pa.y, pa.z, 0} }, + (MariePlaneVertAttr){.pos = tb, .attr = {pb.x, pb.y, pb.z, 0} }, + (MariePlaneVertAttr){.pos = tc, .attr = {pc.x, pc.y, pc.z, 0} }, + (FnMarieRasterizerCallback){draw_polygon_on_normal_texture_exaggerated_param_surf_draw_cb, (void*)&aboba}); +} +// todo: add a version for that function with non-native coordinate system (on vertex) (like I did with absolutely flat surface) + + +typedef struct { + TextureDataR8G8B8* tex; + cvec3 normal_compr; +} draw_polygon_on_normal_texture_absolutely_flat_H_DrawGuest; + +void draw_polygon_on_normal_texture_absolutely_flat_h_draw_cb(void* ug, S32 x, S32 y, vec4) { + draw_polygon_on_normal_texture_absolutely_flat_H_DrawGuest* g = ug; + *TextureDataR8G8B8_at(g->tex, x, y) = g->normal_compr; +} + +void draw_polygon_on_normal_texture_nat_cords_absolutely_flat(TextureDataR8G8B8* tex, + vec2 ta, vec2 tb, vec2 tc, vec3 c_normal + ) { + draw_polygon_on_normal_texture_absolutely_flat_H_DrawGuest aboba = {tex, compress_normal_vec_into_norm_texel(c_normal)}; + marie_rasterize_triangle_with_attr((MariePlaneVertAttr){.pos = ta}, (MariePlaneVertAttr){.pos = tb}, + (MariePlaneVertAttr){.pos = tc}, (FnMarieRasterizerCallback){ + .fn = draw_polygon_on_normal_texture_absolutely_flat_h_draw_cb, .guest = (void*)&aboba}); +} + +void draw_polygon_on_normal_texture_absolutely_flat(TextureDataR8G8B8* tex, + vec2 pa, vec2 pb, vec2 pc, mat3x2 trop, vec3 c_normal + ) { + draw_polygon_on_normal_texture_nat_cords_absolutely_flat(tex, mat3x2_mul_vec3(trop, vec2_and_one(pa)), + mat3x2_mul_vec3(trop, vec2_and_one(pb)), mat3x2_mul_vec3(trop, vec2_and_one(pc)), c_normal); +} + + +typedef struct { + /* (guest, param) -> height gradient */ + vec2 (*fn)(void*, vec2); + void* guest; +} FnHeightMapGradFlatSurfCallback; + +typedef struct { + mat3 surf_orient; + FnHeightMapGradFlatSurfCallback my_client; +} draw_polygon_on_normal_texture_flat_param_surf_H_DrawGuest; + +vec3 draw_polygon_on_normal_texture_flat_param_surf_h_draw_cb(void* ug, vec2 p) { + draw_polygon_on_normal_texture_flat_param_surf_H_DrawGuest* g = ug; + vec2 grad = g->my_client.fn(g->my_client.guest, p); + return mat3_mul_vec3(g->surf_orient, marie_normal_from_tang_space_gradient(grad.x, grad.y)); +} + +/* The simplest case of normal texture generation: for a smooth flat surface of a polygon */ +void draw_polygon_on_normal_texture_flat_param_surf(TextureDataR8G8B8* tex, vec2 pa, vec2 pb, vec2 pc, mat3x2 trop, + mat3 surf_orient, FnHeightMapGradFlatSurfCallback height_map_cb + ) { + draw_polygon_on_normal_texture_flat_param_surf_H_DrawGuest aboba = {surf_orient, height_map_cb}; + draw_polygon_on_normal_texture_smooth_param_surf(tex, pa, pb, pc, trop, (FnNormalVectorGenCallback){ + .fn = draw_polygon_on_normal_texture_flat_param_surf_h_draw_cb, .guest = (void*)&aboba}); +} + + + -VecT_trivmove_struct_Definition(vec2) -VecT_trivmove_method_Definition(vec2) -VecT_primitive_zeroinit_method_Definition(vec2) TextureDataR8 generate_tex_template_for_one_fourth_of_a_cylinder(float s_resol, float w, float r, U32 k) { assert(k >= 1); @@ -382,22 +590,24 @@ TextureDataR8 generate_tex_template_for_one_fourth_of_a_cylinder(float s_resol, for (size_t i = 1; i <= k; i++) { Vecvec2_append(&P, (vec2){r - r * sinf(a * i), r + r * cos(a * i)}); } - size_t S = 6 + 4 * k; - assert(P.len == S); - float r_cut = 2; - float r_decay = 1; - for (size_t i = 0; i < S; i++) { - vec2 p = vec2_mul_vec2(*Vecvec2_at(&P, i), cord_resol); - TextureDataR8_draw_spot_maxing(&res, p, r_cut, r_decay); - } - for (size_t i = 0; i < S; i++) { - vec2 pp = vec2_mul_vec2(*Vecvec2_at(&P, i ? i - 1 : S - 1), cord_resol); - vec2 p = vec2_mul_vec2(*Vecvec2_at(&P, i), cord_resol); - TextureDataR8_draw_inner_line_maxing(&res, pp.x, pp.y, p.x, p.y, r_cut, r_decay); + for (size_t i = 0; i < P.len; i++) { + *Vecvec2_at(&P, i) = vec2_mul_vec2(*Vecvec2_at(&P, i), cord_resol); } + TextureDataR8_draw_perimeter_maxing(&res, Vecvec2_to_ConstSpanvec2(&P)); + + Bublazhuzhka crap_on_back_side = fill_rectangle_with_crap(w, r); + Bublazhuzhka_TextureDataR8_draw_maxing(&crap_on_back_side, &res, + (mat3x2){.x.x = cord_resol.x, .y.y = cord_resol.y, .z = vec2_mul_vec2((vec2){r, r}, cord_resol)}); return res; } +/* Use it as a callback in normal map drawing functions that work with smooth (smooth / flat / cylindrical) + * height maps. Guest pointer is of type Bublazhuzhka* */ +vec2 height_map_cb_that_uses_bublazhuzhka(void* ug, vec2 v) { + Bublazhuzhka* bzh = ug; + return Bublazhuzhka_get_derivative(bzh, v); +} + TextureDataR8G8B8 generate_normal_tex_for_one_fourth_of_a_cylinder(float s_resol, float w, float r, U32 k) { assert(k >= 1); const float a = M_PI_2f / (float)k; @@ -409,35 +619,40 @@ TextureDataR8G8B8 generate_normal_tex_for_one_fourth_of_a_cylinder(float s_resol 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}; TextureDataR8G8B8 res = TextureDataR8G8B8_new(width_pix, height_pix); - // Vecvec2 P = Vecvec2_new(); // todo: reserve 6 + k * 4 - // for (size_t i = k; i > 0; i--) { - // Vecvec2_append(&P, (vec2){r + w + r * sinf(a * i), r + r * cos(a * i)}); - // } - // Vecvec2_append(&P, v3tex); - // for (size_t i = 1; i <= k; i++) { - // Vecvec2_append(&P, (vec2){r + w, 2 * r + i * l}); - // } - // for (size_t i = k; i > 0; i--) { - // Vecvec2_append(&P, (vec2){r, 2 * r + i * l}); - // } - // Vecvec2_append(&P, v2tex); - // for (size_t i = 1; i <= k; i++) { - // Vecvec2_append(&P, (vec2){r - r * sinf(a * i), r + r * cos(a * i)}); - // } - // size_t S = 6 + 4 * k; - // assert(P.len == S); - // float r_cut = 2; - // float r_decay = 1; - // for (size_t i = 0; i < S; i++) { - // vec2 p = vec2_mul_vec2(*Vecvec2_at(&P, i), cord_resol); - // TextureDataR8_draw_spot_maxing(&res, p.x, p.y, r_cut, r_decay); - // } - // for (size_t i = 0; i < S; i++) { - // vec2 pp = vec2_mul_vec2(*Vecvec2_at(&P, i ? i - 1 : S - 1), cord_resol); - // vec2 p = vec2_mul_vec2(*Vecvec2_at(&P, i), cord_resol); - // TextureDataR8_draw_inner_line_maxing(&res, pp.x, pp.y, p.x, p.y, r_cut, r_decay); - // } + + Bublazhuzhka crap_on_the_back_side = fill_rectangle_with_crap(w, r); + mat3x2 trop_back_side = {.x.x = cord_resol.x, .y.y = cord_resol.y, .z = vec2_mul_vec2((vec2){r, r}, cord_resol)}; + mat3 orient_back_side = {.x = {1, 0, 0}, .y = {0, 0, 1}, {0, 1, 0}}; + draw_polygon_on_normal_texture_flat_param_surf(&res, (vec2){0, 0}, (vec2){w, 0}, (vec2){w, r}, trop_back_side, orient_back_side, + (FnHeightMapGradFlatSurfCallback){.fn = height_map_cb_that_uses_bublazhuzhka, .guest = &crap_on_the_back_side}); + draw_polygon_on_normal_texture_flat_param_surf(&res, (vec2){0, 0}, (vec2){0, r}, (vec2){w, r}, trop_back_side, orient_back_side, + (FnHeightMapGradFlatSurfCallback){.fn = height_map_cb_that_uses_bublazhuzhka, .guest = &crap_on_the_back_side}); + + mat3x2 str = {.x.x = cord_resol.x, .y.y = cord_resol.y}; + draw_polygon_on_normal_texture_absolutely_flat(&res, v0tex, v1tex, v4tex, str, (vec3){0, -1, 0}); + draw_polygon_on_normal_texture_absolutely_flat(&res, v1tex, v4tex, v5tex, str, (vec3){0, -1, 0}); + for (size_t i = 0; i < k; i++) { + vec2 A = {r - sinf(i * a) * r, r + cosf(i * a) * r}; + vec2 B = {r - sinf((i + 1) * a) * r, r + cosf((i + 1) * a) * r}; + draw_polygon_on_normal_texture_absolutely_flat(&res, A, B, (vec2){r, r}, str, (vec3){-1, 0, 0}); + } + for (size_t i = 0; i < k; i++) { + vec2 A = {r + w + sinf(i * a) * r, r + cos(i * a) * r}; + vec2 B = {r + w + sinf((i + 1) * a) * r, r + cos((i + 1) * a) * r}; + draw_polygon_on_normal_texture_absolutely_flat(&res, A, B, (vec2){r + w, r}, str, (vec3){1, 0, 0}); + } + for (size_t i = 0; i < k; i++) { + vec2 A = {r, 2 * r + i * l}; + vec2 B = {r + w, 2 * r + i * l}; + vec2 C = {r, 2 * r + i * l + l}; + vec2 D = {r + w, 2 * r + i * l + l}; + vec3 n = {0, cos(a / 2 + a * i), -sin(a / 2 + a * i)}; + draw_polygon_on_normal_texture_absolutely_flat(&res, A, B, C, str, n); + draw_polygon_on_normal_texture_absolutely_flat(&res, D, B, C, str, n); + } return res; } diff --git a/src/l2/tests/r0_tex_init_prep.c b/src/l2/tests/r0_tex_init_prep.c index 5edfbf4..ea06da8 100644 --- a/src/l2/tests/r0_tex_init_prep.c +++ b/src/l2/tests/r0_tex_init_prep.c @@ -16,19 +16,29 @@ void draw_cool_triangle(TextureDataR8G8B8* tex, vec2 A, vec2 B, vec2 C) { } int main() { - // TextureDataR8 tex_1 = generate_tex_template_for_one_fourth_of_a_cylinder(20, 10, 2, 6); - // TextureDataR8_write_to_file(&tex_1, "log_10_2_6_TEMPLATE.r8"); - // TextureDataR8_drop(tex_1); - TextureDataR8G8B8 t = TextureDataR8G8B8_new(1000, 1000);\ - vec2 center = {500, 500}; - for (int i = 0; i < 70; i++) { - float a1 = (float)i * 2 * M_PIf / 70; - float a2 = (float)(i + 1) * 2 * M_PIf / 70; - vec2 A = vec2_add_vec2(center, vec2_mul_scal((vec2){cosf(a1), sinf(a1)}, 480)); - vec2 B = vec2_add_vec2(center, vec2_mul_scal((vec2){cosf(a2), sinf(a2)}, 480)); - draw_cool_triangle(&t, B, A, center); + TextureDataR8 tex_1 = generate_tex_template_for_one_fourth_of_a_cylinder(120, 10, 2, 6); + TextureDataR8_write_to_file(&tex_1, "log_10_2_6_TEMPLATE.a8"); + TextureDataR8_drop(tex_1); + TextureDataR8G8B8 tex_2 = generate_normal_tex_for_one_fourth_of_a_cylinder(120, 10, 2, 6); + TextureDataR8G8B8A8 tex_2_big = TextureDataR8G8B8A8_new(tex_2.width, TextureDataR8G8B8_get_height(&tex_2)); + for (size_t i = 0; i < tex_2.pixels.len; i++) { + cvec3 rgb = *Veccvec3_at(&tex_2.pixels, i); + *Veccvec4_at(&tex_2_big.pixels, i) = (cvec4){rgb.x, rgb.y, rgb.z, 255}; } - TextureDataR8G8B8_write_to_file(&t, "experiment.r8g8b8"); - TextureDataR8G8B8_drop(t); + TextureDataR8G8B8A8_write_to_file(&tex_2_big, "log_10_2_6_NORMAL.r8g8b8a8"); + TextureDataR8G8B8A8_drop(tex_2_big); + TextureDataR8G8B8_drop(tex_2); + + // TextureDataR8G8B8 t = TextureDataR8G8B8_new(1000, 1000);\ + // vec2 center = {500, 500}; + // for (int i = 0; i < 70; i++) { + // float a1 = (float)i * 2 * M_PIf / 70; + // float a2 = (float)(i + 1) * 2 * M_PIf / 70; + // vec2 A = vec2_add_vec2(center, vec2_mul_scal((vec2){cosf(a1), sinf(a1)}, 480)); + // vec2 B = vec2_add_vec2(center, vec2_mul_scal((vec2){cosf(a2), sinf(a2)}, 480)); + // draw_cool_triangle(&t, B, A, center); + // } + // TextureDataR8G8B8_write_to_file(&t, "experiment.r8g8b8"); + // TextureDataR8G8B8_drop(t); return 0; } diff --git a/src/l2/tests/test_shaders/glsl/0/0.frag b/src/l2/tests/test_shaders/glsl/0/0.frag index 1ce099b..3bd7fd2 100644 --- a/src/l2/tests/test_shaders/glsl/0/0.frag +++ b/src/l2/tests/test_shaders/glsl/0/0.frag @@ -6,6 +6,7 @@ layout(location = 0) out vec4 fin_color; layout(binding = 1) uniform sampler2D color_tex; +layout(binding = 2) uniform sampler2D normal_map; struct Pipeline0Spotlight { @@ -31,5 +32,5 @@ layout(std140, binding = 0) uniform Pipeline0UBO }; void main(){ - fin_color = texture(color_tex, fsin_tex); + fin_color = texture(normal_map, fsin_tex); } diff --git a/src/l2/tests/test_textures/bitmap_converter.py b/src/l2/tests/test_textures/bitmap_converter.py index 8cc0983..3c8ab66 100755 --- a/src/l2/tests/test_textures/bitmap_converter.py +++ b/src/l2/tests/test_textures/bitmap_converter.py @@ -1,9 +1,7 @@ #!/usr/bin/env python3 -""" -raw_png_conv.py -=============== -Convert between custom bottom‑up raw files (.r8g8b8a8 / .r8b8g8 / .r8) +""" +Convert between custom bottom‑up raw files (.r8g8b8a8 / .r8b8g8 / .r8 / .a8) and normal PNG using Pillow. Format @@ -14,6 +12,7 @@ pixel data rows bottom‑first: * .r8g8b8a8 : R G B A (4× uint8) * .r8b8g8 : R G B (3× uint8) * .r8 : R (1× uint8 <- grayscale) + * .a8 : A (can convert from it, but can't convert to it) CLI --- @@ -29,10 +28,12 @@ from PIL import Image # Helpers # --------------------------------------------------------------------- # + RAW_FORMATS = { ".r8g8b8a8": {"pix_size": 4, "mode": "RGBA"}, ".r8g8b8": {"pix_size": 3, "mode": "RGB"}, ".r8": {"pix_size": 1, "mode": "L"}, + ".a8": {"pix_size": 1, "mode": None}, # special-cased (alpha-only) } @@ -43,37 +44,52 @@ def get_fmt(path: Path): return fmt -def read_raw(path: Path) -> Image.Image: - """Load any supported raw file -> Pillow Image.""" - spec = get_fmt(path) +def read_header_and_data(path: Path, pix_size: int): with path.open("rb") as f: header = f.read(8) if len(header) != 8: raise ValueError("File too short for header") w, h = struct.unpack(" Image.Image: + """Load any supported raw file -> Pillow Image.""" + spec = get_fmt(path) + w, h, data = read_header_and_data(path, spec["pix_size"]) + row_len = w * spec["pix_size"] - rows = [data[i : i + row_len] for i in range(0, expected, row_len)] - img_bytes = b"".join(reversed(rows)) # flip bottom‑up -> top‑down - return Image.frombytes(spec["mode"], (w, h), img_bytes) + rows = [data[i : i + row_len] for i in range(0, len(data), row_len)] + top_down = b"".join(reversed(rows)) # flip bottom‑up -> top‑down + + # Special handling for .a8 (alpha-only -> LA with black color) + if path.suffix.lower() == ".a8": + big = bytearray(4 * len(top_down)) + big[3::4] = top_down + big = bytes(big) + return Image.frombytes("RGBA", (w, h), big) + + # Normal cases can be constructed directly + return Image.frombytes(spec["mode"], (w, h), top_down) def write_raw(img: Image.Image, path: Path) -> None: """Write Pillow Image -> raw file chosen by path suffix.""" + ext = path.suffix.lower() spec = get_fmt(path) - # Convert to required mode - if img.mode != spec["mode"]: - if spec["mode"] == "L": - img = img.convert("L") - elif spec["mode"] == "RGB": - img = img.convert("RGB") - else: # RGBA - img = img.convert("RGBA") + + if ext == ".a8": + raise ValueError("Don't convert to .a8 format") + + target_mode = spec["mode"] + if img.mode != target_mode: + img = img.convert(target_mode) w, h = img.size data = img.tobytes() diff --git a/src/l2/tests/test_textures/log_10_2_6_TEMPLATE.png b/src/l2/tests/test_textures/log_10_2_6_TEMPLATE.png deleted file mode 100644 index fed645a..0000000 Binary files a/src/l2/tests/test_textures/log_10_2_6_TEMPLATE.png and /dev/null differ diff --git a/src/l2/tests/test_textures/log_10_2_6_v2.png b/src/l2/tests/test_textures/log_10_2_6_v2.png deleted file mode 100644 index 615b820..0000000 Binary files a/src/l2/tests/test_textures/log_10_2_6_v2.png and /dev/null differ