paint: improve particles

This commit is contained in:
luboslenco
2026-05-12 13:58:45 +02:00
parent b5a9629574
commit 8f4b497ae6
10 changed files with 123 additions and 150 deletions
+7
View File
@@ -1485,6 +1485,10 @@ void ui_base_update(void *_) {
}
}
if (mouse_released("left")) {
g_context->layer_preview_dirty = true;
}
#ifdef arm_physics
if (!particle_just_fired) {
for (i32 i = 0; i < 32; ++i) {
@@ -1506,6 +1510,9 @@ void ui_base_update(void *_) {
g_context->particles[i].contact_time += sys_delta();
g_context->pdirty = 1;
}
else {
g_context->particles[i].hit_x = 0.0;
}
}
}
#endif
+4 -5
View File
@@ -102,6 +102,9 @@ context_t *context_create() {
c->last_particle_hit_z = 0.0;
c->particle_friction = 0.1;
c->particle_bounciness = 0.0;
c->particle_gravity_x = 0.0;
c->particle_gravity_y = 0.0;
c->particle_gravity_z = -9.81;
c->particle_lifetime = 5.0;
c->particle_mass = 1.0;
c->particle_random = 0.1;
@@ -301,16 +304,12 @@ void context_select_tool(i32 i) {
}
void context_init_tool() {
bool decal = context_is_decal();
if (decal) {
if (context_is_decal()) {
if (g_context->tool == TOOL_TYPE_TEXT) {
util_render_make_text_preview();
}
util_render_make_decal_preview();
}
else if (g_context->tool == TOOL_TYPE_PARTICLE) {
util_particle_init();
}
else if (g_context->tool == TOOL_TYPE_BAKE) {
// Bake in lit mode for now
if (g_context->viewport_mode == VIEWPORT_MODE_PATH_TRACE) {
-1
View File
@@ -565,7 +565,6 @@ void ui_box_show_custom(void (*commands)(void), i32 mw,
void ui_box_hide();
void tab_meshes_draw(ui_handle_t *htab);
void tab_meshes_reset_preview_map();
void util_particle_init();
void util_particle_init_physics();
void tab_layers_draw(ui_handle_t *htab);
void tab_layers_remap_layer_pointers(ui_node_t_array_t *nodes, i32_imap_t *pointer_map);
+11 -9
View File
@@ -224,11 +224,11 @@ void import_arm_run_project(char *path) {
buffer_t *b = data_get_blob(path);
project_t *project;
bool import_as_mesh = false;
#ifdef IRON_WINDOWS
#ifdef IRON_WINDOWS
bool is_cloud = string_index_of(path, "\\cloud\\") >= 0;
#else
#else
bool is_cloud = string_index_of(path, "/cloud/") >= 0;
#endif
#endif
if (import_arm_is_old(b) && !is_cloud) {
project = import_arm_from_old(b);
}
@@ -434,15 +434,17 @@ void import_arm_run_project(char *path) {
render_target_t *blend0 = any_map_get(rts, "texpaint_blend0");
gpu_texture_t *_texpaint_blend0 = blend0->_image;
gpu_delete_texture(_texpaint_blend0);
blend0->width = config_get_texture_res_x();
blend0->height = config_get_texture_res_y();
blend0->_image = gpu_create_render_target(config_get_texture_res_x(), config_get_texture_res_y(), GPU_TEXTURE_FORMAT_R8);
blend0->width = config_get_texture_res_x();
blend0->height = config_get_texture_res_y();
blend0->_image = gpu_create_render_target(config_get_texture_res_x(), config_get_texture_res_y(), GPU_TEXTURE_FORMAT_R8);
render_target_t *blend1 = any_map_get(rts, "texpaint_blend1");
gpu_texture_t *_texpaint_blend1 = blend1->_image;
gpu_delete_texture(_texpaint_blend1);
blend1->width = config_get_texture_res_x();
blend1->height = config_get_texture_res_y();
blend1->_image = gpu_create_render_target(config_get_texture_res_x(), config_get_texture_res_y(), GPU_TEXTURE_FORMAT_R8);
blend1->width = config_get_texture_res_x();
blend1->height = config_get_texture_res_y();
blend1->_image = gpu_create_render_target(config_get_texture_res_x(), config_get_texture_res_y(), GPU_TEXTURE_FORMAT_R8);
g_context->brush_blend_dirty = true;
}
+6 -14
View File
@@ -27,26 +27,18 @@ void physics_body_init(physics_body_t *body, object_t *obj) {
mesh_data_t *data = mo->data;
vec4_t scale = obj->transform->scale;
if (obj->parent != NULL) {
scale.x *= obj->parent->transform->scale.x;
scale.y *= obj->parent->transform->scale.y;
scale.z *= obj->parent->transform->scale.z;
}
i16_array_t *positions = mesh_data_get_vertex_array(data, "pos")->values;
u32_array_t *indices0 = data->index_array;
scale_pos = scale.x * data->scale_pos;
posa = positions;
inda = indices0;
// triangles = f32_array_create(indices0.length * 3);
// let ar: u32_array_t = indices[0];
// for (let i: i32 = 0; i < math_floor(ar.length / 3); ++i) {
// triangles[i * 9 ] = positions[ar[i * 3 ] * 4 ] * sx;
// triangles[i * 9 + 1] = positions[ar[i * 3 ] * 4 + 1] * sy;
// triangles[i * 9 + 2] = positions[ar[i * 3 ] * 4 + 2] * sz;
// triangles[i * 9 + 3] = positions[ar[i * 3 + 1] * 4 ] * sx;
// triangles[i * 9 + 4] = positions[ar[i * 3 + 1] * 4 + 1] * sy;
// triangles[i * 9 + 5] = positions[ar[i * 3 + 1] * 4 + 2] * sz;
// triangles[i * 9 + 6] = positions[ar[i * 3 + 2] * 4 ] * sx;
// triangles[i * 9 + 7] = positions[ar[i * 3 + 2] * 4 + 1] * sy;
// triangles[i * 9 + 8] = positions[ar[i * 3 + 2] * 4 + 2] * sz;
// }
}
vec4_t loc = obj->transform->loc;
+12 -17
View File
@@ -124,31 +124,26 @@ void render_path_paint_commands_particle(i32 tid, char *texpaint, bool is_mask)
if (g_context->particles[_pi].timer == NULL || g_context->particles[_pi].hit_x == 0) {
continue;
}
g_context->particle_index = _pi;
g_context->particle_hit_x = g_context->particles[_pi].hit_x;
g_context->particle_hit_y = g_context->particles[_pi].hit_y;
g_context->particle_hit_z = g_context->particles[_pi].hit_z;
g_context->last_particle_hit_x = g_context->particles[_pi].hit_last_x;
g_context->last_particle_hit_y = g_context->particles[_pi].hit_last_y;
g_context->last_particle_hit_z = g_context->particles[_pi].hit_last_z;
g_context->particle_index = _pi;
g_context->particle_hit_x = g_context->particles[_pi].hit_x;
g_context->particle_hit_y = g_context->particles[_pi].hit_y;
g_context->particle_hit_z = g_context->particles[_pi].hit_z;
g_context->last_particle_hit_x = g_context->particles[_pi].hit_last_x;
g_context->last_particle_hit_y = g_context->particles[_pi].hit_last_y;
g_context->last_particle_hit_z = g_context->particles[_pi].hit_last_z;
if (is_mask) {
i32 ptid = g_context->layer->parent->id;
tid = g_context->layer->parent->id;
if (slot_layer_is_group(g_context->layer->parent)) {
for (i32 i = 0; i < slot_layer_get_children(g_context->layer->parent)->length; ++i) {
slot_layer_t *c = slot_layer_get_children(g_context->layer->parent)->buffer[i];
ptid = c->id;
tid = c->id;
break;
}
}
string_array_t *additional =
any_array_create_from_raw((void *[]){string("texpaint_nor%d", ptid), string("texpaint_pack%d", ptid), "texpaint_blend0"}, 3);
render_path_set_target(texpaint, additional, NULL, GPU_CLEAR_NONE, 0, 0.0);
}
else {
string_array_t *additional =
any_array_create_from_raw((void *[]){string("texpaint_nor%d", tid), string("texpaint_pack%d", tid), "texpaint_blend0"}, 3);
render_path_set_target(texpaint, additional, NULL, GPU_CLEAR_NONE, 0, 0.0);
}
string_array_t *additional = any_array_create_from_raw((void *[]){string("texpaint_nor%d", tid), string("texpaint_pack%d", tid), "texpaint_blend0"}, 3);
render_path_set_target(texpaint, additional, NULL, GPU_CLEAR_NONE, 0, 0.0);
render_path_bind_target("main", "gbufferD");
if (g_context->xray || g_config->brush_angle_reject) {
render_path_bind_target("gbuffer0", "gbuffer0");
+3 -1
View File
@@ -284,7 +284,6 @@ typedef struct context {
bool write_icon_on_export;
struct gpu_texture *text_tool_image;
char *text_tool_text;
struct material_data *particle_material;
i32 layer_filter;
struct brush_output_node *brush_output_node_inst;
void (*run_brush)(void *, i32);
@@ -401,6 +400,9 @@ typedef struct context {
i32 particle_index;
f32 particle_friction;
f32 particle_bounciness;
f32 particle_gravity_x;
f32 particle_gravity_y;
f32 particle_gravity_z;
f32 particle_lifetime;
f32 particle_mass;
f32 particle_random;
+77 -40
View File
@@ -37,6 +37,81 @@ void ui_header_render_ui() {
}
}
void ui_header_particle_menu_draw() {
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 *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 *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;
}
g_context->particle_friction = ui_slider(hfriction, tr("Friction"), 0.0, 1.0, true, 100.0, true, UI_ALIGN_RIGHT, true);
if (hfriction->changed) {
asim_set_friction(g_context->particle_friction);
}
ui_handle_t *hbounciness = ui_handle(__ID__);
if (hbounciness->init) {
hbounciness->f = g_context->particle_bounciness;
}
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);
}
ui_handle_t *hgravx = ui_handle(__ID__);
if (hgravx->init) {
hgravx->f = g_context->particle_gravity_x;
}
g_context->particle_gravity_x = ui_slider(hgravx, tr("Gravity X"), -10.0, 10.0, true, 100.0, true, UI_ALIGN_RIGHT, true);
if (hgravx->changed) {
asim_set_gravity(g_context->particle_gravity_x, g_context->particle_gravity_y, g_context->particle_gravity_z);
}
ui_handle_t *hgravy = ui_handle(__ID__);
if (hgravy->init) {
hgravy->f = g_context->particle_gravity_y;
}
g_context->particle_gravity_y = ui_slider(hgravy, tr("Gravity Y"), -10.0, 10.0, true, 100.0, true, UI_ALIGN_RIGHT, true);
if (hgravy->changed) {
asim_set_gravity(g_context->particle_gravity_x, g_context->particle_gravity_y, g_context->particle_gravity_z);
}
ui_handle_t *hgravz = ui_handle(__ID__);
if (hgravz->init) {
hgravz->f = g_context->particle_gravity_z;
}
g_context->particle_gravity_z = ui_slider(hgravz, tr("Gravity Z"), -10.0, 10.0, true, 100.0, true, UI_ALIGN_RIGHT, true);
if (hgravz->changed) {
asim_set_gravity(g_context->particle_gravity_x, g_context->particle_gravity_y, g_context->particle_gravity_z);
}
if (ui->changed || ui->is_typing) {
ui_menu_keep_open = true;
}
}
void ui_header_draw_tool_properties_layer_preview_dirty(void *_) {
g_context->layer_preview_dirty = true;
}
@@ -551,46 +626,8 @@ void ui_header_draw_tool_properties() {
}
if (g_context->tool == TOOL_TYPE_PARTICLE) {
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 *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 *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;
}
g_context->particle_friction = ui_slider(hfriction, tr("Friction"), 0.0, 1.0, true, 100.0, true, UI_ALIGN_RIGHT, true);
if (hfriction->changed) {
asim_set_friction(g_context->particle_friction);
}
ui_handle_t *hbounciness = ui_handle(__ID__);
if (hbounciness->init) {
hbounciness->f = g_context->particle_bounciness;
}
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);
if (ui_button(tr("Particle"), UI_ALIGN_CENTER, "")) {
ui_menu_draw(&ui_header_particle_menu_draw, -1, -1);
}
}
-4
View File
@@ -334,10 +334,6 @@ gpu_texture_t *uniforms_ext_tex_link(object_t *object, material_data_t *mat, cha
else if (string_equals(link, "_texbrushstencil")) {
return g_context->brush_stencil_image;
}
else if (string_equals(link, "_texparticle")) {
render_target_t *rt = any_map_get(render_path_render_targets, "texparticle");
return rt->_image;
}
else if (string_equals(link, "_texuvmap")) {
if (!util_uv_uvmap_cached) {
sys_notify_on_next_frame(&uniforms_ext_cache_uv_map, NULL);
+3 -59
View File
@@ -1,78 +1,22 @@
#include "global.h"
void util_particle_init() {
if (g_context->particle_material != NULL) {
return;
}
{
render_target_t *t = render_target_create();
t->name = "texparticle";
t->width = 0;
t->height = 0;
t->format = "R8";
t->scale = render_path_base_get_super_sampling();
render_path_create_render_target(t);
}
for (i32 i = 0; i < _scene_raw->material_datas->length; ++i) {
material_data_t *mat = _scene_raw->material_datas->buffer[i];
if (string_equals(mat->name, "Material2")) {
material_data_t *m = util_clone_material_data(mat);
m->name = "MaterialParticle";
any_array_push(_scene_raw->material_datas, m);
break;
}
}
material_data_t *md = data_get_material("Scene", "MaterialParticle");
g_context->particle_material = md;
for (i32 i = 0; i < _scene_raw->objects->length; ++i) {
obj_t *obj = _scene_raw->objects->buffer[i];
if (string_equals(obj->name, ".Sphere")) {
obj_t *particle = util_clone_obj(obj);
particle->name = ".Particle";
particle->material_ref = "MaterialParticle";
any_array_push(_scene_raw->objects, particle);
for (i32 i = 0; i < 16; ++i) {
particle->transform->buffer[i] *= 0.01;
}
break;
}
}
object_t *o = scene_spawn_object(".Sphere", NULL, true);
mesh_object_t *mo = o->ext;
mo->base->name = ".ParticleEmitter";
mo->base->raw = util_clone_obj(mo->base->raw);
}
void util_particle_init_mesh() {
if (g_context->paint_body != NULL) {
return;
}
if (g_context->merged_object == NULL) {
util_mesh_merge(NULL);
}
mesh_object_t *po = g_context->merged_object;
po->base->transform->scale.x = po->base->parent->transform->scale.x;
po->base->transform->scale.y = po->base->parent->transform->scale.y;
po->base->transform->scale.z = po->base->parent->transform->scale.z;
g_context->paint_body = physics_body_create();
g_context->paint_body->shape = PHYSICS_SHAPE_MESH;
physics_body_init(g_context->paint_body, po->base);
physics_body_init(g_context->paint_body, g_context->merged_object->base);
}
void util_particle_init_physics() {
if (physics_world_active != NULL) {
util_particle_init_mesh();
return;
if (physics_world_active == NULL) {
physics_world_create();
}
physics_world_create();
util_particle_init_mesh();
}