From 9d9f958152473af084c9acd868ffe7ecefbb3191 Mon Sep 17 00:00:00 2001 From: luboslenco Date: Mon, 11 May 2026 21:57:42 +0200 Subject: [PATCH] paint: improve particles --- paint/sources/base.c | 45 ++++++++++++++++++++++++++++++--------- paint/sources/context.c | 2 ++ paint/sources/types.h | 2 ++ paint/sources/ui_header.c | 29 ++++++++++++++++++------- 4 files changed, 60 insertions(+), 18 deletions(-) diff --git a/paint/sources/base.c b/paint/sources/base.c index 74c41a5e..14800b44 100644 --- a/paint/sources/base.c +++ b/paint/sources/base.c @@ -1418,9 +1418,8 @@ void ui_base_update(void *_) { } static f64 particle_last_spawn_time = 0.0; - bool particle_just_fired = false; - if (mouse_down("left") && context_in_paint_area() && !g_context->paint2d && - (mouse_started("left") || sys_time() - particle_last_spawn_time >= 0.2)) { + bool particle_just_fired = false; + if (mouse_down("left") && context_in_paint_area() && !g_context->paint2d && (mouse_started("left") || sys_time() - particle_last_spawn_time >= 0.2)) { particle_last_spawn_time = sys_time(); i32 slot = -1; @@ -1431,17 +1430,24 @@ void ui_base_update(void *_) { } } if (slot >= 0) { - history_push_undo = true; - g_context->brush_blend_dirty = true; + if (mouse_started("left")) { + history_push_undo = true; + g_context->brush_blend_dirty = true; + } object_t *o = scene_spawn_object(".Sphere", NULL, true); mesh_object_t *mo = o->ext; mo->base->name = ".Bullet"; mo->base->visible = true; - camera_object_t *camera = scene_camera; - transform_t *ct = camera->base->transform; - mo->base->transform->loc = (vec4_t){transform_world_x(ct), transform_world_y(ct), transform_world_z(ct), 1.0}; + util_render_pick_pos_nor_tex(); + f32 nx = g_context->norx_picked; + f32 ny = g_context->nory_picked; + f32 nz = g_context->norz_picked; + f32 sphere_radius = g_context->brush_radius * 0.1f; + f32 spawn_h = -(sphere_radius + g_context->particle_spawn_distance) + 0.01; + mo->base->transform->loc = + (vec4_t){g_context->posx_picked + nx * spawn_h, g_context->posy_picked + ny * spawn_h, g_context->posz_picked + nz * spawn_h, 1.0}; mo->base->transform->scale = (vec4_t){g_context->brush_radius * 0.2, g_context->brush_radius * 0.2, g_context->brush_radius * 0.2, 1.0}; transform_build_matrix(mo->base->transform); @@ -1450,8 +1456,27 @@ void ui_base_update(void *_) { body->mass = g_context->particle_mass; physics_body_init(body, mo->base); - ray_t *ray = raycast_get_ray(mouse_view_x(), mouse_view_y(), camera); - physics_body_apply_impulse(body, vec4_mult(ray->dir, 0.12)); + // Random direction + f32 rx = math_random() * 2.0f - 1.0f; + f32 ry = math_random() * 2.0f - 1.0f; + f32 rz = math_random() * 2.0f - 1.0f; + f32 rlen = sqrtf(rx * rx + ry * ry + rz * rz); + if (rlen < 0.0001f) + rlen = 0.0001f; + rx /= rlen; + ry /= rlen; + rz /= rlen; + if (rx * nx + ry * ny + rz * nz < 0.0f) { + rx = -rx; + ry = -ry; + rz = -rz; + } + f32 r = g_context->particle_random; + f32 dx = nx + (rx - nx) * r; + f32 dy = ny + (ry - ny) * r; + f32 dz = nz + (rz - nz) * r; + f32 impulse = g_context->particle_spawn_distance * 30.0f; + physics_body_apply_impulse(body, (vec4_t){dx * impulse, dy * impulse, dz * impulse, 0.0}); g_context->particles[slot].body = body; g_context->particles[slot].bullet = mo->base; diff --git a/paint/sources/context.c b/paint/sources/context.c index d99c60e3..ff02a1c3 100644 --- a/paint/sources/context.c +++ b/paint/sources/context.c @@ -104,6 +104,8 @@ context_t *context_create() { c->particle_bounciness = 0.0; c->particle_lifetime = 5.0; c->particle_mass = 1.0; + c->particle_random = 0.1; + c->particle_spawn_distance = 0.3; c->layer_filter = 0; c->gizmo_started = false; c->gizmo_offset = 0.0; diff --git a/paint/sources/types.h b/paint/sources/types.h index a71a6d75..6463b634 100644 --- a/paint/sources/types.h +++ b/paint/sources/types.h @@ -403,6 +403,8 @@ typedef struct context { f32 particle_bounciness; f32 particle_lifetime; f32 particle_mass; + f32 particle_random; + f32 particle_spawn_distance; } context_t; typedef struct node_shader { diff --git a/paint/sources/ui_header.c b/paint/sources/ui_header.c index 519bdbc4..17f12075 100644 --- a/paint/sources/ui_header.c +++ b/paint/sources/ui_header.c @@ -417,8 +417,8 @@ void ui_header_draw_tool_properties() { } else { ui_handle_t *brush_radius_handle = ui_handle(__ID__); - brush_radius_handle->f = g_context->brush_radius; - g_context->brush_radius = ui_slider(brush_radius_handle, tr("Radius"), 0.01, 2.0, true, 100.0, true, UI_ALIGN_RIGHT, true); + brush_radius_handle->f = g_context->brush_radius; + g_context->brush_radius = ui_slider(brush_radius_handle, tr("Radius"), 0.01, 2.0, true, 100.0, true, UI_ALIGN_RIGHT, true); if (ui->is_hovered) { any_map_t *vars = any_map_create(); any_map_set(vars, "brush_radius", any_map_get(config_keymap, "brush_radius")); @@ -475,7 +475,8 @@ void ui_header_draw_tool_properties() { vars)); } - if (g_context->tool == TOOL_TYPE_BRUSH || g_context->tool == TOOL_TYPE_ERASER || g_context->tool == TOOL_TYPE_CLONE || decal_mask || g_context->tool == TOOL_TYPE_PARTICLE) { + if (g_context->tool == TOOL_TYPE_BRUSH || g_context->tool == TOOL_TYPE_ERASER || g_context->tool == TOOL_TYPE_CLONE || decal_mask || + g_context->tool == TOOL_TYPE_PARTICLE) { ui_handle_t *h = ui_handle(__ID__); if (h->init) { h->f = g_context->brush_hardness; @@ -550,19 +551,31 @@ void ui_header_draw_tool_properties() { } if (g_context->tool == TOOL_TYPE_PARTICLE) { - ui_handle_t *hlifetime = ui_handle(__ID__); + ui_handle_t *hlifetime = ui_handle(__ID__); if (hlifetime->init) { hlifetime->f = g_context->particle_lifetime; } g_context->particle_lifetime = ui_slider(hlifetime, tr("Lifetime"), 0.0, 10.0, true, 1.0, true, UI_ALIGN_RIGHT, true); - ui_handle_t *hmass = ui_handle(__ID__); + ui_handle_t *hspawn_distance = ui_handle(__ID__); + if (hspawn_distance->init) { + hspawn_distance->f = g_context->particle_spawn_distance; + } + g_context->particle_spawn_distance = ui_slider(hspawn_distance, tr("Distance"), 0.0, 1.0, true, 100.0, true, UI_ALIGN_RIGHT, true); + + ui_handle_t *hmass = ui_handle(__ID__); if (hmass->init) { hmass->f = g_context->particle_mass; } g_context->particle_mass = ui_slider(hmass, tr("Mass"), 0.0, 3.0, true, 100.0, true, UI_ALIGN_RIGHT, true); - ui_handle_t *hfriction = ui_handle(__ID__); + ui_handle_t *hrandom = ui_handle(__ID__); + if (hrandom->init) { + hrandom->f = g_context->particle_random; + } + g_context->particle_random = ui_slider(hrandom, tr("Random"), 0.0, 1.0, true, 100.0, true, UI_ALIGN_RIGHT, true); + + ui_handle_t *hfriction = ui_handle(__ID__); if (hfriction->init) { hfriction->f = g_context->particle_friction; } @@ -571,11 +584,11 @@ void ui_header_draw_tool_properties() { asim_set_friction(g_context->particle_friction); } - ui_handle_t *hbounciness = ui_handle(__ID__); + ui_handle_t *hbounciness = ui_handle(__ID__); if (hbounciness->init) { hbounciness->f = g_context->particle_bounciness; } - g_context->particle_bounciness = ui_slider(hbounciness, tr("Bounciness"), 0.0, 1.0, true, 100.0, true, UI_ALIGN_RIGHT, true); + g_context->particle_bounciness = ui_slider(hbounciness, tr("Bounce"), 0.0, 1.0, true, 100.0, true, UI_ALIGN_RIGHT, true); if (hbounciness->changed) { asim_set_bounciness(g_context->particle_bounciness); }