diff --git a/paint/sources/functions.h b/paint/sources/functions.h index c3c1b13d..7a5af5c6 100644 --- a/paint/sources/functions.h +++ b/paint/sources/functions.h @@ -87,6 +87,7 @@ bool operator_shortcut(char *s, shortcut_type_t type); void render_compass(); void render_compass_update(); void render_envsphere(); +void render_pathsphere(); void config_load(); void config_save(); void config_init(); diff --git a/paint/sources/globals.h b/paint/sources/globals.h index 35551528..c57be8ea 100644 --- a/paint/sources/globals.h +++ b/paint/sources/globals.h @@ -247,6 +247,8 @@ ui_handle_t *tab_browser_hpath; ui_handle_t *tab_browser_hsearch; bool tab_browser_refresh = false; extern i32 ui_files_selected; +extern i32 path_point_dragging; +extern i32 path_layer_last_active; any_map_t *util_mesh_unwrappers; i32 ui_header_default_h = 30; i32 ui_header_h; diff --git a/paint/sources/main.c b/paint/sources/main.c index baeb04a7..576e7e09 100644 --- a/paint/sources/main.c +++ b/paint/sources/main.c @@ -159,6 +159,7 @@ #include "project.c" #include "render_compass.c" #include "render_envsphere.c" +#include "render_pathsphere.c" #include "render_gizmo.c" #include "render_path_base.c" #include "render_path_deferred.c" diff --git a/paint/sources/render_path_raytrace.c b/paint/sources/render_path_raytrace.c index e9183f19..5c4b9d9c 100644 --- a/paint/sources/render_path_raytrace.c +++ b/paint/sources/render_path_raytrace.c @@ -204,6 +204,7 @@ void render_path_raytrace_draw(bool use_live_layer) { render_path_base_draw_bloom("buf", "last"); render_path_set_target("last", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); render_envsphere(); + render_pathsphere(); render_path_set_target("", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); render_path_bind_target("last", "tex"); render_path_draw_shader("Scene/copy_pass/copy_pass"); diff --git a/paint/sources/render_pathsphere.c b/paint/sources/render_pathsphere.c new file mode 100644 index 00000000..535a5384 --- /dev/null +++ b/paint/sources/render_pathsphere.c @@ -0,0 +1,170 @@ + +#include "global.h" + +extern char *str_sh_irradiance; +extern char *str_envmap_equirect; +extern char *str_envmap_sample; +extern char *str_env_brdf_approx; + +static mesh_object_t *render_pathsphere_obj = NULL; + +static node_shader_context_t *make_pathsphere_shader(char *name) { + material_t *mat = GC_ALLOC_INIT(material_t, {.name = name, .canvas = NULL}); + shader_context_t *props = GC_ALLOC_INIT(shader_context_t, { + .name = "overlay", + .depth_write = false, + .compare_mode = "always", + .cull_mode = "clockwise", + .vertex_elements = any_array_create_from_raw( + (void *[]){ + GC_ALLOC_INIT(vertex_element_t, {.name = "pos", .data = "short4norm"}), + GC_ALLOC_INIT(vertex_element_t, {.name = "nor", .data = "short2norm"}), + GC_ALLOC_INIT(vertex_element_t, {.name = "tex", .data = "short2norm"}), + }, + 3), + .color_attachments = any_array_create_from_raw((void *[]){"RGBA64"}, 1), + .depth_attachment = "NONE", + }); + node_shader_context_t *con = node_shader_context_create(mat, props); + node_shader_t *kong = node_shader_context_make_kong(con); + + kong->frag_n = true; + + node_shader_add_constant(kong, "WVP: float4x4", "_world_view_proj_matrix"); + node_shader_write_vert(kong, "output.pos = constants.WVP * float4(input.pos.xyz, 1.0);"); + + node_shader_add_constant(kong, "cam_look: float3", "_camera_look"); + node_shader_write_attrib_frag(kong, "var vvec: float3 = -constants.cam_look;"); + + node_shader_add_texture(kong, "senvmap_radiance", "_envmap_radiance"); + node_shader_add_texture(kong, "senvmap_radiance0", "_envmap_radiance0"); + node_shader_add_texture(kong, "senvmap_radiance1", "_envmap_radiance1"); + node_shader_add_texture(kong, "senvmap_radiance2", "_envmap_radiance2"); + node_shader_add_texture(kong, "senvmap_radiance3", "_envmap_radiance3"); + node_shader_add_texture(kong, "senvmap_radiance4", "_envmap_radiance4"); + + node_shader_add_constant(kong, "envmap_data: float4", "_envmap_data"); // (angle, sin, cos, strength) + node_shader_add_constant(kong, "shirr0: float4", "_envmap_irradiance0"); + node_shader_add_constant(kong, "shirr1: float4", "_envmap_irradiance1"); + node_shader_add_constant(kong, "shirr2: float4", "_envmap_irradiance2"); + node_shader_add_constant(kong, "shirr3: float4", "_envmap_irradiance3"); + node_shader_add_constant(kong, "shirr4: float4", "_envmap_irradiance4"); + node_shader_add_constant(kong, "shirr5: float4", "_envmap_irradiance5"); + node_shader_add_constant(kong, "shirr6: float4", "_envmap_irradiance6"); + + node_shader_add_function(kong, str_sh_irradiance); + node_shader_add_function(kong, str_envmap_equirect); + node_shader_add_function(kong, str_envmap_sample); + node_shader_add_function(kong, str_env_brdf_approx); + + node_shader_write_frag(kong, "var basecol: float3 = float3(0.1, 0.1, 0.1);"); + node_shader_write_frag(kong, "var roughness: float = 0.3;"); + node_shader_write_frag(kong, "var metallic: float = 0.0;"); + + // Indirect lighting + node_shader_write_frag(kong, "var albedo: float3 = lerp3(basecol, float3(0.0, 0.0, 0.0), metallic);"); + node_shader_write_frag(kong, "var f0: float3 = lerp3(float3(0.04, 0.04, 0.04), basecol, metallic);"); + node_shader_write_frag(kong, "var dotnv: float = max(0.0, dot(n, vvec));"); + node_shader_write_frag(kong, "var wreflect: float3 = reflect(-vvec, n);"); + node_shader_write_frag(kong, "var envlod: float = roughness * 5.0;"); + node_shader_write_frag(kong, "var lod0: float = floor(envlod);"); + node_shader_write_frag(kong, "var lod1: float = ceil(envlod);"); + node_shader_write_frag(kong, "var lodf: float = envlod - lod0;"); + node_shader_write_frag(kong, "var envmap_coord: float2 = envmap_equirect(wreflect, constants.envmap_data.x);"); + node_shader_write_frag(kong, "var lodc0: float3 = envmap_sample(lod0, envmap_coord);"); + node_shader_write_frag(kong, "var lodc1: float3 = envmap_sample(lod1, envmap_coord);"); + node_shader_write_frag(kong, "var prefiltered_color: float3 = lerp3(lodc0, lodc1, lodf);"); + // Rotate normal by envmap angle for irradiance + node_shader_write_frag(kong, "var indirect: float3 = albedo * (sh_irradiance(float3(n.x * constants.envmap_data.z - n.y * constants.envmap_data.y, n.x * " + "constants.envmap_data.y + n.y * constants.envmap_data.z, n.z)) / 3.14159265);"); + node_shader_write_frag(kong, "indirect = indirect + prefiltered_color * env_brdf_approx(f0, roughness, dotnv) * 0.5;"); + node_shader_write_frag(kong, "indirect = indirect * constants.envmap_data.w;"); + node_shader_write_frag(kong, "indirect = max3(indirect, float3(0.0, 0.0, 0.0));"); + + // Filmic tone-mapping (matches compositor_pass) + node_shader_write_frag(kong, "var tc: float3 = max3(indirect - float3(0.004, 0.004, 0.004), float3(0.0, 0.0, 0.0));"); + node_shader_write_frag(kong, + "indirect = (tc * (tc * 6.2 + float3(0.5, 0.5, 0.5))) / (tc * (tc * 6.2 + float3(1.7, 1.7, 1.7)) + float3(0.06, 0.06, 0.06));"); + node_shader_write_frag(kong, "output = float4(indirect, 1.0);"); + + parser_material_finalize(con); + con->data->shader_from_source = true; + gpu_create_shaders_from_kong(node_shader_get(kong), &con->data->vertex_shader, &con->data->fragment_shader, &con->data->_->vertex_shader_size, + &con->data->_->fragment_shader_size); + return con; +} + +static void create_pathsphere_object() { + mesh_data_t *md = ((mesh_object_t *)scene_get_child(".Sphere")->ext)->data; + + node_shader_context_t *con = make_pathsphere_shader("render_pathsphere"); + shader_context_load(con->data); + + shader_data_t *sd = GC_ALLOC_INIT(shader_data_t, { + .name = "render_pathsphere", + .contexts = any_array_create_from_raw((void *[]){con->data}, 1), + }); + material_context_t *mcon = GC_ALLOC_INIT(material_context_t, { + .name = "overlay", + .bind_constants = any_array_create_from_raw((void *[]){}, 0), + .bind_textures = any_array_create_from_raw((void *[]){}, 0), + }); + material_context_load(mcon); + material_data_t *mat = GC_ALLOC_INIT(material_data_t, { + .name = "render_pathsphere", + .shader = "", + .contexts = any_array_create_from_raw((void *[]){mcon}, 1), + ._ = GC_ALLOC_INIT(material_data_runtime_t, {.uid = 0.0, .shader = sd}), + }); + + render_pathsphere_obj = mesh_object_create(md, mat); + render_pathsphere_obj->base->name = "render_pathsphere"; + render_pathsphere_obj->base->visible = false; + render_pathsphere_obj->frustum_culling = false; + gc_root(render_pathsphere_obj); +} + +void render_pathsphere() { + if (g_config->workspace == WORKSPACE_PLAYER || g_context->capturing_screenshot || g_context->paint2d) { + return; + } + + slot_layer_t *l = g_context->layer; + if (l == NULL || !slot_layer_is_path(l)) { + return; + } + + f32_array_t *points_world = l->path_points_world; + if (points_world == NULL || points_world->length == 0) { + return; + } + + if (path_point_dragging >= 0) { + return; + } + + if (render_pathsphere_obj == NULL) { + create_pathsphere_object(); + } + + i32 num_points = points_world->length / 3; + i32 active_index = path_layer_last_active; + + render_pathsphere_obj->base->visible = true; + + for (i32 i = 0; i < num_points; i++) { + f32 wx = points_world->buffer[i * 3]; + f32 wy = points_world->buffer[i * 3 + 1]; + f32 wz = points_world->buffer[i * 3 + 2]; + f32 dist = vec4_dist(scene_camera->base->transform->loc, (vec4_t){wx, wy, wz, 1.0}); + f32 fov = scene_camera->data->fov; + f32 scale = dist / 8.0f * fov * 0.1f * (i == active_index ? 1.5f : 1.0f); + + render_pathsphere_obj->base->transform->loc = (vec4_t){wx, wy, wz, 1.0}; + render_pathsphere_obj->base->transform->scale = (vec4_t){scale, scale, scale, 1.0}; + transform_build_matrix(render_pathsphere_obj->base->transform); + mesh_object_render(render_pathsphere_obj, "overlay", NULL); + } + + render_pathsphere_obj->base->visible = false; +} diff --git a/paint/sources/util_layer.c b/paint/sources/util_layer.c index 7ff1c999..4ab91c29 100644 --- a/paint/sources/util_layer.c +++ b/paint/sources/util_layer.c @@ -4,8 +4,8 @@ static slot_layer_t *path_layer_current = NULL; static object_t **path_point_spheres = NULL; static i32 path_point_sphere_count = 0; -static i32 path_point_dragging = -1; -static i32 path_layer_last_active = -1; +i32 path_point_dragging = -1; +i32 path_layer_last_active = -1; bool util_layer_is_path_point_dragging() { return path_point_dragging >= 0; @@ -111,10 +111,10 @@ static void path_paint_curved(f32_array_t *points, f32_array_t *points_world, f3 project_to_screen((vec4_t){wx0, wy0, wz0, 1.0f}, &prev_px, &prev_py); } - bool have_j = j < num_camera; - f32 prev_bwx = wx0; - f32 prev_bwy = wy0; - f32 prev_bwz = wz0; + bool have_j = j < num_camera; + f32 prev_bwx = wx0; + f32 prev_bwy = wy0; + f32 prev_bwz = wz0; g_context->prev_paint_vec_x = prev_px; g_context->prev_paint_vec_y = prev_py; if (sphere_mode) { @@ -217,13 +217,13 @@ static void path_paint_straight(f32_array_t *points, f32_array_t *points_world, f32 cwz = points_world->buffer[i * 3 + 2]; f32 ppx = cur_px, ppy = cur_py; project_to_screen((vec4_t){pwx, pwy, pwz, 1.0f}, &ppx, &ppy); - prev_px = ppx; - prev_py = ppy; - f32 dx = (cur_px - ppx) * (f32)sys_w(); - f32 dy = (cur_py - ppy) * (f32)sys_h(); - f32 len = sqrtf(dx * dx + dy * dy); - i32 n = dot_spacing > 0.5f ? (i32)ceilf(len / dot_spacing) : 17; - n = n < 1 ? 1 : n > 64 ? 64 : n; + prev_px = ppx; + prev_py = ppy; + f32 dx = (cur_px - ppx) * (f32)sys_w(); + f32 dy = (cur_py - ppy) * (f32)sys_h(); + f32 len = sqrtf(dx * dx + dy * dy); + i32 n = dot_spacing > 0.5f ? (i32)ceilf(len / dot_spacing) : 17; + n = n < 1 ? 1 : n > 64 ? 64 : n; g_context->prev_paint_vec_x = ppx; g_context->prev_paint_vec_y = ppy; for (i32 s = 1; s <= n; s++) {