Files
armorpaint/paint/sources/util/util_particle.c
T
2026-06-11 20:01:25 +02:00

156 lines
5.0 KiB
C

#include "../global.h"
void util_particle_init_mesh() {
if (g_context->paint_body != NULL) {
return;
}
if (g_context->merged_object == NULL) {
util_mesh_merge(NULL);
}
g_context->paint_body = physics_body_create();
g_context->paint_body->shape = PHYSICS_SHAPE_MESH;
physics_body_init(g_context->paint_body, g_context->merged_object->base);
}
void util_particle_init_physics() {
if (physics_world_active == NULL) {
physics_world_create();
}
util_particle_init_mesh();
}
void util_particle_update() {
if (g_context->tool != TOOL_TYPE_PARTICLE) {
return;
}
util_particle_init_physics();
make_particle_get_bullet_material();
physics_world_t *world = physics_world_active;
physics_world_update(world);
for (i32 i = 0; i < 32; ++i) {
if (g_context->particles[i].timer != NULL && g_context->particles[i].timer->delay <= 0) {
physics_body_remove(g_context->particles[i].body);
mesh_object_remove((mesh_object_t *)g_context->particles[i].bullet->ext);
memset(&g_context->particles[i], 0, sizeof(g_context->particles[i]));
}
}
bool any_active = false;
for (i32 i = 0; i < 32; ++i) {
if (g_context->particles[i].timer != NULL) {
any_active = true;
break;
}
}
if (any_active) {
g_context->ddirty = 2;
g_context->rdirty = 2;
iron_delay_idle_sleep();
}
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)) {
particle_last_spawn_time = sys_time();
i32 slot = -1;
for (i32 i = 0; i < 32; ++i) {
if (g_context->particles[i].timer == NULL) {
slot = i;
break;
}
}
if (slot >= 0) {
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;
mo->material = make_particle_get_bullet_material();
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);
physics_body_t *body = physics_body_create();
body->shape = PHYSICS_SHAPE_SPHERE;
body->mass = g_context->particle_mass;
physics_body_init(body, mo->base);
// 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;
g_context->particles[slot].timer = tween_timer(g_context->particle_lifetime, NULL, NULL);
particle_just_fired = true;
}
}
if (mouse_released("left")) {
g_context->layer_preview_dirty = true;
}
#ifdef WITH_PHYSICS
if (!particle_just_fired) {
for (i32 i = 0; i < 32; ++i) {
if (g_context->particles[i].timer == NULL) {
continue;
}
physics_pair_t_array_t *pairs = physics_world_get_contact_pairs(world, g_context->particles[i].body);
if (pairs != NULL && pairs->length > 0) {
physics_pair_t *p = pairs->buffer[0];
g_context->particles[i].hit_last_x = g_context->particles[i].hit_x != 0 ? g_context->particles[i].hit_x : p->pos_a_x;
g_context->particles[i].hit_last_y = g_context->particles[i].hit_y != 0 ? g_context->particles[i].hit_y : p->pos_a_y;
g_context->particles[i].hit_last_z = g_context->particles[i].hit_z != 0 ? g_context->particles[i].hit_z : p->pos_a_z;
g_context->particles[i].hit_x = p->pos_a_x;
g_context->particles[i].hit_y = p->pos_a_y;
g_context->particles[i].hit_z = p->pos_a_z;
g_context->particles[i].hit_nor_x = p->nor_x;
g_context->particles[i].hit_nor_y = p->nor_y;
g_context->particles[i].hit_nor_z = p->nor_z;
g_context->particles[i].contact_time += sys_delta();
g_context->pdirty = 1;
}
else {
g_context->particles[i].hit_x = 0.0;
}
}
}
#endif
}