paint: path and curve layers

This commit is contained in:
luboslenco
2026-05-13 17:11:52 +02:00
parent cf743de29b
commit d7116f2288
24 changed files with 657 additions and 86 deletions
+6 -5
View File
@@ -365,19 +365,19 @@ gpu_texture_t *base_get_drag_image() {
base_drag_tint = base_darker(ui->ops->theme->LABEL_COL, 0x00202020);
return icons;
}
if (base_drag_layer != NULL && slot_layer_is_mask(base_drag_layer) && base_drag_layer->fill_layer == NULL) {
if (base_drag_layer != NULL && slot_layer_is_mask(base_drag_layer) && base_drag_layer->fill_material == NULL) {
tab_layers_make_mask_preview_rgba32(base_drag_layer);
return g_context->mask_preview_rgba32;
}
if (base_drag_layer != NULL) {
return base_drag_layer->fill_layer != NULL ? base_drag_layer->fill_layer->image_icon : base_drag_layer->texpaint_preview;
return base_drag_layer->fill_material != NULL ? base_drag_layer->fill_material->image_icon : base_drag_layer->texpaint_preview;
}
return NULL;
}
rect_t *base_get_drag_background() {
gpu_texture_t *icons = resource_get("icons.k");
if (base_drag_layer != NULL && !slot_layer_is_group(base_drag_layer) && base_drag_layer->fill_layer == NULL) {
if (base_drag_layer != NULL && !slot_layer_is_group(base_drag_layer) && base_drag_layer->fill_material == NULL) {
return resource_tile50(icons, ICON_CHECKER);
}
return NULL;
@@ -766,6 +766,7 @@ void ui_base_update_ui() {
}
gizmo_update();
util_layer_update_path();
// Same mapping for paint and rotate (predefined in touch keymap)
if (context_in_3d_view()) {
@@ -932,7 +933,7 @@ void ui_base_update_ui() {
g_context->layer_preview_dirty = true; // Update layer preview
// New color id picked, update fill layer
if (g_context->tool == TOOL_TYPE_COLORID && g_context->layer->fill_layer != NULL) {
if (g_context->tool == TOOL_TYPE_COLORID && g_context->layer->fill_material != NULL) {
sys_notify_on_next_frame(&ui_base_update_ui_on_next_frame, NULL);
}
}
@@ -2066,7 +2067,7 @@ void base_toggle_fullscreen() {
bool base_is_decal_layer() {
bool is_painting = g_context->tool != TOOL_TYPE_MATERIAL && g_context->tool != TOOL_TYPE_BAKE;
return is_painting && g_context->layer->fill_layer != NULL && g_context->layer->uv_type == UV_TYPE_PROJECT;
return is_painting && g_context->layer->fill_material != NULL && g_context->layer->uv_type == UV_TYPE_PROJECT;
}
void base_redraw_status() {
+19 -2
View File
@@ -64,7 +64,8 @@ void brush_output_node_parse_inputs(brush_output_node_t *self) {
}
void brush_output_paint(brush_output_node_t *self) {
bool down = mouse_down("left") || pen_down("tip");
bool down = mouse_down("left") || pen_down("tip");
bool started = mouse_started("left") || pen_started("tip");
// Set color pick
if (down && g_context->tool == TOOL_TYPE_COLORID && project_assets->length > 0) {
@@ -72,6 +73,16 @@ void brush_output_paint(brush_output_node_t *self) {
ui_toolbar_handle->redraws = 1;
}
// Path layer - add path points only
if (slot_layer_is_path(g_context->layer) && !started) {
return;
}
// Path layer - dragging existing path point
if (slot_layer_is_path(g_context->layer) && util_layer_is_path_point_dragging()) {
return;
}
// Prevent painting the same spot
bool same_spot = g_context->paint_vec.x == g_context->last_paint_x && g_context->paint_vec.y == g_context->last_paint_y;
bool lazy = g_context->tool == TOOL_TYPE_BRUSH && g_context->brush_lazy_radius > 0;
@@ -92,6 +103,12 @@ void brush_output_paint(brush_output_node_t *self) {
brush_output_node_parse_inputs(self);
}
// Path layer - add point and repaint
if (slot_layer_is_path(g_context->layer)) {
util_layer_add_path_point(g_context->layer, g_context->paint_vec.x, g_context->paint_vec.y);
return;
}
if (g_context->painted <= 1) {
g_context->pdirty = 1;
g_context->rdirty = 2;
@@ -135,7 +152,7 @@ void brush_output_node_run(brush_output_node_t *self, i32 from) {
}
// Do not paint over fill layer
if (g_context->layer->fill_layer != NULL && g_context->tool != TOOL_TYPE_PICKER && g_context->tool != TOOL_TYPE_MATERIAL &&
if (g_context->layer->fill_material != NULL && g_context->tool != TOOL_TYPE_PICKER && g_context->tool != TOOL_TYPE_MATERIAL &&
g_context->tool != TOOL_TYPE_COLORID) {
return;
}
+12 -5
View File
@@ -113,7 +113,7 @@ buffer_t *export_arm_rgba64_to_rgba32(buffer_t *buffer) {
void export_arm_run_project() {
workflow_t _workflow = g_config->workflow;
g_config->workflow = WORKFLOW_PBR;
g_config->workflow = WORKFLOW_PBR;
base_update_workflow();
ui_node_canvas_t_array_t *mnodes = any_array_create_from_raw((void *[]){}, 0);
@@ -182,7 +182,7 @@ void export_arm_run_project() {
.uv_map = l->uv_map,
.decal_mat = l->uv_type == UV_TYPE_PROJECT ? mat4_to_f32_array(l->decal_mat) : NULL,
.opacity_mask = l->mask_opacity,
.fill_layer = l->fill_layer != NULL ? array_index_of(project_materials, l->fill_layer) : -1,
.fill_material = l->fill_material != NULL ? array_index_of(project_materials, l->fill_material) : -1,
.object_mask = l->object_mask,
.blending = l->blending,
.parent = l->parent != NULL ? array_index_of(project_layers, l->parent) : -1,
@@ -199,7 +199,14 @@ void export_arm_run_project() {
.paint_height = l->paint_height,
.paint_height_blend = l->paint_height_blend,
.paint_emis = l->paint_emis,
.paint_subs = l->paint_subs});
.paint_subs = l->paint_subs,
.path_points = l->path_points,
.path_points_world = l->path_points_world,
.path_points_camera = l->path_points_camera,
.path_points_parent = l->path_points_parent,
.path_tool = l->path_tool,
.path_curved = l->path_curved,
.path_material = l->path_material != NULL ? array_index_of(project_materials, l->path_material) : -1});
any_array_push(ld, d);
}
@@ -236,8 +243,8 @@ void export_arm_run_project() {
f32_array_t_array_t *mesh_transforms = any_array_create_from_raw((void *[]){}, 0);
for (i32 i = 0; i < project_paint_objects->length; ++i) {
mesh_object_t *p = project_paint_objects->buffer[i];
f32_array_t *ar = f32_array_create_from_raw(p->base->transform->local.m, 16);
mesh_object_t *p = project_paint_objects->buffer[i];
f32_array_t *ar = f32_array_create_from_raw(p->base->transform->local.m, 16);
any_array_push(mesh_transforms, ar);
}
g_project->mesh_transforms = mesh_transforms;
+9
View File
@@ -38,6 +38,7 @@ slot_layer_t_array_t *slot_layer_get_filters(slot_layer_t *raw, bool include_gro
f32 slot_layer_get_opacity(slot_layer_t *raw);
i32 slot_layer_get_object_mask(slot_layer_t *raw);
bool slot_layer_is_layer(slot_layer_t *raw);
bool slot_layer_is_path(slot_layer_t *raw);
bool slot_layer_is_group(slot_layer_t *raw);
slot_layer_t *slot_layer_get_containing_group(slot_layer_t *raw);
bool slot_layer_is_filter(slot_layer_t *raw);
@@ -57,12 +58,15 @@ void layers_commands_merge_pack(gpu_pipeline_t *pipe, gpu_texture_t *i0, gpu_tex
i32 i1blending);
bool layers_is_fill_material();
void layers_update_fill_layers();
bool layers_is_path_material();
void layers_update_path_layers();
void layers_update_fill_layer(bool parse_paint);
void layers_update_linked_layers();
void layers_set_object_mask();
slot_layer_t *layers_new_layer(bool clear, i32 position, slot_layer_t *parent);
slot_layer_t *layers_new_mask(bool clear, slot_layer_t *parent, i32 position);
slot_layer_t *layers_new_group();
slot_layer_t *layers_new_path_layer(bool curved);
void layers_create_fill_layer(uv_type_t uv_type, mat4_t decal_mat, i32 position);
void layers_create_image_mask(asset_t *asset);
void layers_create_color_layer(i32 base_color, f32 occlusion, f32 roughness, f32 metallic, i32 position);
@@ -172,6 +176,11 @@ void ui_base_toggle_browser();
void ui_base_set_icon_scale();
void ui_base_set_viewport_col(i32 col);
void gizmo_update();
void util_layer_update_path();
bool util_layer_is_path_point_dragging();
void util_layer_add_path_point(slot_layer_t *l, f32 screen_x, f32 screen_y);
void util_layer_repaint_path(slot_layer_t *l);
void util_layer_clear_path_points(slot_layer_t *l);
void make_texcoord_run(node_shader_t *kong);
void context_init();
void context_select_material(i32 i);
+2 -2
View File
@@ -165,8 +165,8 @@ void gizmo_update() {
}
g_context->gizmo_drag_last = g_context->gizmo_drag;
if (g_context->material != g_context->layer->fill_layer) {
context_set_material(g_context->layer->fill_layer);
if (g_context->material != g_context->layer->fill_material) {
context_set_material(g_context->layer->fill_material);
}
layers_update_fill_layer(g_context->gizmo_started);
}
+1 -1
View File
@@ -7,7 +7,7 @@
char *manifest_title = "ArmorPaint";
char *manifest_version = "1.0 alpha";
char *manifest_version_project = "6";
char *manifest_version_project = "7";
char *manifest_version_config = "1";
char *manifest_url = "https://armorpaint.org";
char *manifest_url_android = "https://play.google.com/store/apps/details?id=org.armorpaint";
+2 -2
View File
@@ -264,7 +264,7 @@ void history_undo() {
history_undo_i = history_undo_i - 1 < 0 ? g_config->undo_steps - 1 : history_undo_i - 1;
slot_layer_t *lay = history_undo_layers->buffer[history_undo_i];
slot_layer_swap(g_context->layer, lay);
g_context->layer->fill_layer = project_materials->buffer[step->material];
g_context->layer->fill_material = project_materials->buffer[step->material];
}
else if (step->action == HISTORY_ACTION_LAYER_OPACITY) {
context_set_layer(project_layers->buffer[step->layer]);
@@ -536,7 +536,7 @@ void history_redo() {
else if (step->action == HISTORY_ACTION_TO_FILL_LAYER || step->action == HISTORY_ACTION_TO_FILL_MASK) {
slot_layer_t *lay = history_undo_layers->buffer[history_undo_i];
slot_layer_swap(g_context->layer, lay);
g_context->layer->fill_layer = project_materials->buffer[step->material];
g_context->layer->fill_material = project_materials->buffer[step->material];
history_undo_i = (history_undo_i + 1) % g_config->undo_steps;
}
else if (step->action == HISTORY_ACTION_TO_PAINT_LAYER || step->action == HISTORY_ACTION_TO_PAINT_MASK) {
+9 -2
View File
@@ -526,6 +526,12 @@ void import_arm_run_project(char *path) {
l->paint_height_blend = ld->paint_height_blend;
l->paint_emis = ld->paint_emis;
l->paint_subs = ld->paint_subs;
l->path_points = ld->path_points;
l->path_points_world = ld->path_points_world;
l->path_points_camera = ld->path_points_camera;
l->path_points_parent = ld->path_points_parent;
l->path_tool = ld->path_tool;
l->path_curved = ld->path_curved;
gpu_delete_texture(_texpaint);
if (_texpaint_nor != NULL) {
@@ -594,13 +600,14 @@ void import_arm_run_project(char *path) {
util_render_make_brush_preview();
}
// Fill layers
// Fill layers and path layers materials
for (i32 i = 0; i < project->layer_datas->length; ++i) {
layer_data_t *ld = project->layer_datas->buffer[i];
slot_layer_t *l = project_layers->buffer[i];
bool is_group = ld->texpaint == NULL;
if (!is_group) {
l->fill_layer = ld->fill_layer > -1 ? project_materials->buffer[ld->fill_layer] : NULL;
l->fill_material = ld->fill_material > -1 ? project_materials->buffer[ld->fill_material] : NULL;
l->path_material = ld->path_material > -1 ? project_materials->buffer[ld->path_material] : NULL;
}
}
+23 -2
View File
@@ -159,7 +159,7 @@ project_t *import_arm_from_map_to_arm(any_map_t *old) {
ld->uv_type = armpack_map_get_i32(old, "uv_type");
ld->decal_mat = any_map_get(old, "decal_mat");
ld->opacity_mask = armpack_map_get_f32(old, "opacity_mask");
ld->fill_layer = armpack_map_get_i32(old, "fill_layer");
ld->fill_material = armpack_map_get_i32(old, "fill_material");
ld->object_mask = armpack_map_get_i32(old, "object_mask");
ld->blending = armpack_map_get_i32(old, "blending");
ld->parent = armpack_map_get_i32(old, "parent");
@@ -215,6 +215,24 @@ project_t *import_arm_from_map_to_arm(any_map_t *old) {
return project;
}
project_t *import_arm_from_version_6(any_map_t *old) {
any_array_t *lds = any_map_get(old, "layer_datas");
if (lds != NULL) {
for (i32 i = 0; i < lds->length; ++i) {
any_map_t *ld = lds->buffer[i];
armpack_map_set_i32(ld, "fill_material", armpack_map_get_i32(ld, "fill_layer"));
any_map_set(ld, "path_points", NULL);
any_map_set(ld, "path_points_world", NULL);
any_map_set(ld, "path_points_camera", NULL);
any_map_set(ld, "path_points_parent", NULL);
armpack_map_set_i32(ld, "path_tool", 0);
armpack_map_set_i32(ld, "path_curved", 0);
armpack_map_set_i32(ld, "path_material", -1);
}
}
return import_arm_from_map_to_arm(old);
}
project_t *import_arm_from_version_5(any_map_t *old) {
any_array_t *mds = any_map_get(old, "mesh_datas");
any_array_t *mts = any_array_create_from_raw((void *[]){}, 0);
@@ -222,7 +240,7 @@ project_t *import_arm_from_version_5(any_map_t *old) {
any_array_push(mts, mat4_to_f32_array(mat4_identity()));
}
any_map_set(old, "mesh_transforms", mts);
return import_arm_from_map_to_arm(old);
return import_arm_from_version_6(old);
}
project_t *import_arm_from_version_4(any_map_t *old) {
@@ -255,6 +273,9 @@ project_t *import_arm_from_version_2(any_map_t *old) {
project_t *import_arm_from_old(buffer_t *b) {
any_map_t *old = armpack_decode_to_map(b);
if (import_arm_is_version(b, '6')) {
return import_arm_from_version_6(old);
}
if (import_arm_is_version(b, '5')) {
return import_arm_from_version_5(old);
}
+2 -1
View File
@@ -193,9 +193,10 @@
#include "ui_statusbar.c"
#include "ui_toolbar.c"
#include "ui_view2d.c"
#include "uniforms_ext.c"
#include "uniforms.c"
#include "util_clone.c"
#include "util_encode.c"
#include "util_layer.c"
#include "util_mesh.c"
#include "util_particle.c"
#include "util_render.c"
+1 -1
View File
@@ -9,7 +9,7 @@ void make_brush_run(node_shader_t *kong) {
return;
}
bool fill_layer = g_context->layer->fill_layer != NULL;
bool fill_layer = g_context->layer->fill_material != NULL;
bool decal = context_is_decal();
if (decal && !fill_layer) {
node_shader_write_frag(kong, "if (constants.decal_mask.z > 0.0) {");
+5 -5
View File
@@ -190,7 +190,7 @@ node_shader_context_t *make_paint_run(material_t *data, material_context_t *matc
node_shader_write_vert(kong, "output.pos = float4(tpos, 0.0, 1.0);");
bool decal_layer = g_context->layer->fill_layer != NULL && g_context->layer->uv_type == UV_TYPE_PROJECT;
bool decal_layer = g_context->layer->fill_material != NULL && g_context->layer->uv_type == UV_TYPE_PROJECT;
if (decal_layer) {
node_shader_add_constant(kong, "WVP: float4x4", "_decal_layer_matrix");
}
@@ -207,7 +207,7 @@ node_shader_context_t *make_paint_run(material_t *data, material_context_t *matc
node_shader_write_attrib_frag(kong, "sp.y = 1.0 - sp.y;");
node_shader_write_attrib_frag(kong, "sp.z -= 0.0001;"); // small bias
uv_type_t uv_type = g_context->layer->fill_layer != NULL ? g_context->layer->uv_type : g_context->brush_paint;
uv_type_t uv_type = g_context->layer->fill_material != NULL ? g_context->layer->uv_type : g_context->brush_paint;
if (uv_type == UV_TYPE_PROJECT) {
kong->frag_ndcpos = true;
}
@@ -311,7 +311,7 @@ node_shader_context_t *make_paint_run(material_t *data, material_context_t *matc
parser_material_parse_subsurface = g_context->material->paint_subs;
parser_material_parse_height = g_context->material->paint_height;
parser_material_parse_height_as_channel = true;
uv_type_t uv_type = g_context->layer->fill_layer != NULL ? g_context->layer->uv_type : g_context->brush_paint;
uv_type_t uv_type = g_context->layer->fill_material != NULL ? g_context->layer->uv_type : g_context->brush_paint;
parser_material_triplanar = uv_type == UV_TYPE_TRIPLANAR && !decal;
parser_material_sample_keep_aspect = decal;
gc_unroot(parser_material_sample_uv_scale);
@@ -346,7 +346,7 @@ node_shader_context_t *make_paint_run(material_t *data, material_context_t *matc
node_shader_write_frag(kong, "var opacity: float = mat_opacity;");
}
if (g_context->layer->fill_layer == NULL) {
if (g_context->layer->fill_material == NULL) {
node_shader_write_frag(kong, "opacity *= constants.brush_opacity;");
}
@@ -512,7 +512,7 @@ node_shader_context_t *make_paint_run(material_t *data, material_context_t *matc
node_shader_write_frag(kong,
"output[0] = float4((basecol * t_blur + sample_undo.rgb * sample_undo.a * (1.0 - t_blur)) / max(out_a, 0.0000001), out_a);");
}
else if (g_context->material->paint_opac_mode == OPACITY_MODE_TRANSLUC || g_context->layer->fill_layer != NULL) {
else if (g_context->material->paint_opac_mode == OPACITY_MODE_TRANSLUC || g_context->layer->fill_material != NULL) {
node_shader_write_frag(kong, string("output[0] = float4(%s, %s);",
make_material_blend_mode(kong, g_context->brush_blending, "sample_undo.rgb", "basecol", "str"), "mat_opacity"));
}
+1 -1
View File
@@ -183,7 +183,7 @@ node_shader_context_t *sculpt_make_sculpt_run(material_t *data, material_context
node_shader_write_frag(kong, "var sample_undo: float4 = sample_lod(texpaint_sculpt_undo, sampler_linear, sculpt_uv, 0.0);");
node_shader_write_frag(kong, "var raw_undo: float4 = texpaint_sculpt_undo[uint2(uint(tex_coord.x * 2048.0), uint(tex_coord.y * 2048.0))];");
if (g_context->layer->fill_layer != NULL) {
if (g_context->layer->fill_material != NULL) {
node_shader_add_function(kong, str_octahedron_wrap);
node_shader_write_frag(kong, "var nor_v: float = floor(raw_undo.a) / 255.0;");
node_shader_write_frag(kong, "var nor_oct: float2 = float2(raw_undo.a - floor(raw_undo.a), nor_v) * 2.0 - 1.0;");
+1 -1
View File
@@ -3,7 +3,7 @@
void make_texcoord_run(node_shader_t *kong) {
bool fill_layer = g_context->layer->fill_layer != NULL;
bool fill_layer = g_context->layer->fill_material != NULL;
uv_type_t uv_type = fill_layer ? g_context->layer->uv_type : g_context->brush_paint;
bool decal = context_is_decal();
f32 angle = g_context->brush_angle + g_context->brush_nodes_angle;
+2 -2
View File
@@ -662,7 +662,7 @@ void render_path_paint_commands_cursor() {
return;
}
bool fill_layer = g_context->layer->fill_layer != NULL;
bool fill_layer = g_context->layer->fill_material != NULL;
bool group_layer = slot_layer_is_group(g_context->layer);
if (!base_ui_enabled || base_is_dragging || fill_layer || group_layer) {
return;
@@ -676,7 +676,7 @@ void render_path_paint_commands_cursor() {
}
bool render_path_paint_paint_enabled() {
bool fill_layer = g_context->layer->fill_layer != NULL && g_context->tool != TOOL_TYPE_PICKER && g_context->tool != TOOL_TYPE_MATERIAL &&
bool fill_layer = g_context->layer->fill_material != NULL && g_context->tool != TOOL_TYPE_PICKER && g_context->tool != TOOL_TYPE_MATERIAL &&
g_context->tool != TOOL_TYPE_COLORID;
bool group_layer = slot_layer_is_group(g_context->layer);
+52 -12
View File
@@ -280,7 +280,7 @@ void layers_apply_mask(slot_layer_t *l, slot_layer_t *m) {
}
void slot_layer_apply_mask(slot_layer_t *raw) {
if (raw->parent->fill_layer != NULL) {
if (raw->parent->fill_material != NULL) {
slot_layer_to_paint_layer(raw->parent);
}
if (slot_layer_is_group(raw->parent)) {
@@ -346,7 +346,7 @@ slot_layer_t *slot_layer_duplicate(slot_layer_t *raw) {
l->visible = raw->visible;
l->mask_opacity = raw->mask_opacity;
l->fill_layer = raw->fill_layer;
l->fill_material = raw->fill_material;
l->object_mask = raw->object_mask;
l->blending = raw->blending;
l->uv_type = raw->uv_type;
@@ -453,14 +453,14 @@ void slot_layer_to_fill_layer_on_next_frame(void *_) {
void slot_layer_to_fill_layer(slot_layer_t *raw) {
context_set_layer(raw);
raw->fill_layer = g_context->material;
raw->fill_material = g_context->material;
layers_update_fill_layer(true);
sys_notify_on_next_frame(&slot_layer_to_fill_layer_on_next_frame, NULL);
}
void slot_layer_to_paint_layer(slot_layer_t *raw) {
context_set_layer(raw);
raw->fill_layer = NULL;
raw->fill_material = NULL;
make_material_parse_paint_material(true);
g_context->layer_preview_dirty = true;
ui_base_hwnds->buffer[TAB_AREA_SIDEBAR0]->redraws = 2;
@@ -607,6 +607,10 @@ bool slot_layer_is_layer(slot_layer_t *raw) {
return raw->texpaint != NULL && raw->texpaint_nor != NULL;
}
bool slot_layer_is_path(slot_layer_t *raw) {
return raw->path_points != NULL;
}
bool slot_layer_is_group(slot_layer_t *raw) {
return raw->texpaint == NULL;
}
@@ -1054,7 +1058,7 @@ bool layers_is_fill_material() {
slot_material_t *m = g_context->material;
for (i32 i = 0; i < project_layers->length; ++i) {
slot_layer_t *l = project_layers->buffer[i];
if (l->fill_layer == m) {
if (l->fill_material == m) {
return true;
}
}
@@ -1105,13 +1109,13 @@ void layers_update_fill_layers() {
bool has_fill_mask = false;
for (i32 i = 0; i < project_layers->length; ++i) {
slot_layer_t *l = project_layers->buffer[i];
if (slot_layer_is_layer(l) && l->fill_layer == g_context->material) {
if (slot_layer_is_layer(l) && l->fill_material == g_context->material) {
has_fill_layer = true;
}
}
for (i32 i = 0; i < project_layers->length; ++i) {
slot_layer_t *l = project_layers->buffer[i];
if (slot_layer_is_mask(l) && l->fill_layer == g_context->material) {
if (slot_layer_is_mask(l) && l->fill_material == g_context->material) {
has_fill_mask = true;
}
}
@@ -1129,7 +1133,7 @@ void layers_update_fill_layers() {
bool first = true;
for (i32 i = 0; i < project_layers->length; ++i) {
slot_layer_t *l = project_layers->buffer[i];
if (slot_layer_is_layer(l) && l->fill_layer == g_context->material) {
if (slot_layer_is_layer(l) && l->fill_material == g_context->material) {
g_context->layer = l;
if (first) {
first = false;
@@ -1157,7 +1161,7 @@ void layers_update_fill_layers() {
bool first = true;
for (i32 i = 0; i < project_layers->length; ++i) {
slot_layer_t *l = project_layers->buffer[i];
if (slot_layer_is_mask(l) && l->fill_layer == g_context->material) {
if (slot_layer_is_mask(l) && l->fill_material == g_context->material) {
g_context->layer = l;
if (first) {
first = false;
@@ -1184,6 +1188,26 @@ void layers_update_fill_layers() {
}
}
bool layers_is_path_material() {
slot_material_t *m = g_context->material;
for (i32 i = 0; i < project_layers->length; ++i) {
slot_layer_t *l = project_layers->buffer[i];
if (slot_layer_is_path(l) && l->path_material == m) {
return true;
}
}
return false;
}
void layers_update_path_layers() {
for (i32 i = 0; i < project_layers->length; ++i) {
slot_layer_t *l = project_layers->buffer[i];
if (slot_layer_is_path(l) && l->path_material == g_context->material) {
util_layer_repaint_path(l);
}
}
}
void layers_update_fill_layer(bool parse_paint) {
gpu_texture_t *current = _draw_current;
bool in_use = gpu_in_use;
@@ -1366,6 +1390,22 @@ slot_layer_t *layers_new_group() {
return l;
}
slot_layer_t *layers_new_path_layer(bool curved) {
slot_layer_t *l = layers_new_layer(true, -1, NULL);
if (l == NULL) {
return NULL;
}
l->path_points = f32_array_create(0);
l->path_points_world = f32_array_create(0);
l->path_points_camera = f32_array_create(0);
l->path_points_parent = i32_array_create(0);
l->path_tool = -1;
l->path_curved = curved;
l->path_material = g_context->material;
l->name = string(curved ? "Curve %d" : "Path %d", l->id + 1);
return l;
}
void layers_create_fill_layer_on_next_frame(void *_) {
slot_layer_t *l = layers_new_layer(false, _layers_position, NULL);
history_new_layer();
@@ -1608,7 +1648,7 @@ slot_layer_t *layers_merge_group(slot_layer_t *l) {
children->buffer[0]->parent = NULL;
children->buffer[0]->name = l->name;
if (children->buffer[0]->fill_layer != NULL) {
if (children->buffer[0]->fill_material != NULL) {
slot_layer_to_paint_layer(children->buffer[0]);
}
slot_layer_delete(l);
@@ -1883,9 +1923,9 @@ void layers_on_resized_on_next_frame(void *_) {
slot_material_t *_material = g_context->material;
for (i32 i = 0; i < project_layers->length; ++i) {
slot_layer_t *l = project_layers->buffer[i];
if (l->fill_layer != NULL) {
if (l->fill_material != NULL) {
g_context->layer = l;
g_context->material = l->fill_layer;
g_context->material = l->fill_material;
layers_update_fill_layer(true);
}
}
+33 -23
View File
@@ -63,8 +63,11 @@ void tab_layers_handle_layer_icon_state(slot_layer_t *l, i32 i, ui_state_t state
if (sys_time() - g_context->select_time > 0.2) {
g_context->select_time = sys_time();
}
if (l->fill_layer != NULL) {
context_set_material(l->fill_layer);
if (l->fill_material != NULL) {
context_set_material(l->fill_material);
}
else if (l->path_material != NULL) {
context_set_material(l->path_material);
}
}
}
@@ -90,17 +93,17 @@ ui_state_t tab_layers_draw_layer_icon(slot_layer_t *l, i32 i, f32 uix, f32 uiy,
gpu_texture_t *texpaint_preview = l->texpaint_preview;
gpu_texture_t *icon;
if (l->fill_layer == NULL) {
icon = texpaint_preview;
if (l->fill_material != NULL) {
icon = g_config->window_scale > 1 ? l->fill_material->image : l->fill_material->image_icon;
}
else if (g_config->window_scale > 1) {
icon = l->fill_layer->image;
else if (l->path_material != NULL) {
icon = g_config->window_scale > 1 ? l->path_material->image : l->path_material->image_icon;
}
else {
icon = l->fill_layer->image_icon;
icon = texpaint_preview;
}
if (l->fill_layer == NULL) {
if (l->fill_material == NULL && l->path_material == NULL) {
// Checker
rect_t *r = resource_tile50(icons, ICON_CHECKER);
f32 _x = ui->_x;
@@ -111,14 +114,14 @@ ui_state_t tab_layers_draw_layer_icon(slot_layer_t *l, i32 i, f32 uix, f32 uiy,
ui->_y = _y;
ui->_w = _w;
}
if (l->fill_layer == NULL && slot_layer_is_mask(l)) {
if (l->fill_material == NULL && slot_layer_is_mask(l)) {
draw_set_pipeline(ui_view2d_pipe);
gpu_set_int(ui_view2d_channel_loc, 1);
}
ui_state_t state = ui_image(icon, 0xffffffff, icon_h);
if (l->fill_layer == NULL && slot_layer_is_mask(l)) {
if (l->fill_material == NULL && slot_layer_is_mask(l)) {
draw_set_pipeline(NULL);
}
@@ -231,7 +234,7 @@ void tab_layers_draw_layer_slot_full_delete_layer(void *_) {
}
void tab_layers_combo_object_layer_clear(slot_layer_t *l) {
g_context->material = l->fill_layer;
g_context->material = l->fill_material;
slot_layer_clear(l, 0x00000000, NULL, 1.0, layers_default_rough, 0.0);
layers_update_fill_layers();
}
@@ -259,7 +262,7 @@ ui_handle_t *tab_layers_combo_object(slot_layer_t *l, bool label) {
if (object_handle->changed) {
context_set_layer(l);
make_material_parse_mesh_material();
if (l->fill_layer != NULL) {
if (l->fill_material != NULL) {
sys_notify_on_next_frame(&tab_layers_combo_object_layer_clear, l);
}
else {
@@ -574,7 +577,7 @@ void tab_layers_draw_layer_context_menu_merge_down(void *_) {
context_set_layer(l);
history_merge_layers();
layers_merge_down();
if (g_context->layer->fill_layer != NULL)
if (g_context->layer->fill_material != NULL)
slot_layer_to_paint_layer(g_context->layer);
}
@@ -611,6 +614,7 @@ void tab_layers_draw_layer_context_menu_clear(void *_) {
if (!slot_layer_is_group(l)) {
history_clear_layer();
slot_layer_clear(l, 0x00000000, NULL, 1.0, layers_default_rough, 0.0);
util_layer_clear_path_points(l);
}
else {
for (i32 i = 0; i < slot_layer_get_children(l)->length; ++i) {
@@ -663,15 +667,15 @@ void tab_layers_draw_layer_context_menu_draw() {
char *to_fill_string = slot_layer_is_layer(l) ? tr("To Fill Layer") : tr("To Fill Mask");
char *to_paint_string = slot_layer_is_layer(l) ? tr("To Paint Layer") : tr("To Paint Mask");
if (l->fill_layer == NULL && ui_menu_button(to_fill_string, "", ICON_SPHERE)) {
if (l->fill_material == NULL && ui_menu_button(to_fill_string, "", ICON_SPHERE)) {
sys_notify_on_next_frame(&tab_layers_draw_layer_context_menu_to_fill_layer, NULL);
}
if (l->fill_layer != NULL && ui_menu_button(to_paint_string, "", ICON_PAINT)) {
if (l->fill_material != NULL && ui_menu_button(to_paint_string, "", ICON_PAINT)) {
sys_notify_on_next_frame(&tab_layers_draw_layer_context_menu_to_paint_layer, NULL);
}
}
if (slot_layer_is_layer(l) && l->fill_layer != NULL && l->texpaint_sculpt != NULL && ui_menu_button(tr("Apply"), "", ICON_CHECK)) {
if (slot_layer_is_layer(l) && l->fill_material != NULL && l->texpaint_sculpt != NULL && ui_menu_button(tr("Apply"), "", ICON_CHECK)) {
sys_notify_on_next_frame(&tab_layers_draw_layer_context_menu_apply_sculpt, NULL);
}
@@ -681,11 +685,11 @@ void tab_layers_draw_layer_context_menu_draw() {
}
ui->enabled = true;
if (l->fill_layer == NULL && ui_menu_button(tr("Clear"), "", ICON_ERASE)) {
if (l->fill_material == NULL && ui_menu_button(tr("Clear"), "", ICON_ERASE)) {
context_set_layer(l);
sys_notify_on_next_frame(&tab_layers_draw_layer_context_menu_clear, NULL);
}
if (slot_layer_is_mask(l) && l->fill_layer == NULL && ui_menu_button(tr("Invert"), "", ICON_INVERT)) {
if (slot_layer_is_mask(l) && l->fill_material == NULL && ui_menu_button(tr("Invert"), "", ICON_INVERT)) {
sys_notify_on_next_frame(&tab_layers_draw_layer_context_menu_invert, NULL);
}
if (slot_layer_is_mask(l) && ui_menu_button(tr("Apply"), "", ICON_CHECK)) {
@@ -798,13 +802,13 @@ void tab_layers_draw_layer_context_menu_draw() {
ui_menu_keep_open = true;
}
}
if (l->fill_layer != NULL) {
if (l->fill_material != NULL) {
ui_menu_align();
ui_handle_t *scale_handle = ui_nest(ui_handle(__ID__), l->id);
scale_handle->f = l->scale;
l->scale = ui_slider(scale_handle, tr("UV Scale"), 0.0, 5.0, true, 100.0, true, UI_ALIGN_RIGHT, true);
if (scale_handle->changed) {
context_set_material(l->fill_layer);
context_set_material(l->fill_material);
context_set_layer(l);
sys_notify_on_next_frame(&tab_layers_draw_layer_context_menu_update_fill_layers, NULL);
ui_menu_keep_open = true;
@@ -815,7 +819,7 @@ void tab_layers_draw_layer_context_menu_draw() {
angle_handle->f = l->angle;
l->angle = ui_slider(angle_handle, tr("Angle"), 0.0, 360, true, 1, true, UI_ALIGN_RIGHT, true);
if (angle_handle->changed) {
context_set_material(l->fill_layer);
context_set_material(l->fill_material);
context_set_layer(l);
make_material_parse_paint_material(true);
sys_notify_on_next_frame(&tab_layers_draw_layer_context_menu_update_fill_layers, NULL);
@@ -834,7 +838,7 @@ void tab_layers_draw_layer_context_menu_draw() {
3);
l->uv_type = ui_inline_radio(uv_type_handle, uv_type_items, UI_ALIGN_LEFT);
if (uv_type_handle->changed) {
context_set_material(l->fill_layer);
context_set_material(l->fill_material);
context_set_layer(l);
make_material_parse_paint_material(true);
sys_notify_on_next_frame(&tab_layers_draw_layer_context_menu_update_fill_layers, NULL);
@@ -1029,7 +1033,13 @@ void tab_layers_button_new_menu() {
if (ui_menu_button(tr("Decal"), "", ICON_DECAL)) {
layers_create_fill_layer(UV_TYPE_PROJECT, mat4_nan(), -1);
}
if (ui_menu_button(tr("Path"), "", ICON_RULER)) {
if (ui_menu_button(tr("Path"), "", ICON_PATH)) {
layers_new_path_layer(false);
history_new_layer();
}
if (ui_menu_button(tr("Curve"), "", ICON_CURVE)) {
layers_new_path_layer(true);
history_new_layer();
}
if (ui_menu_button(tr("Black Mask"), "", ICON_MASK)) {
if (slot_layer_is_mask(l) || slot_layer_is_filter(l)) {
+2 -2
View File
@@ -60,8 +60,8 @@ void tab_materials_delete_material(slot_material_t *m) {
i32 i = array_index_of(project_materials, m);
for (i32 i = 0; i < project_layers->length; ++i) {
slot_layer_t *l = project_layers->buffer[i];
if (l->fill_layer == m) {
l->fill_layer = NULL;
if (l->fill_material == m) {
l->fill_material = NULL;
}
}
history_delete_material();
+16 -2
View File
@@ -24,7 +24,7 @@ typedef struct slot_layer {
struct gpu_texture *texpaint_pack;
struct gpu_texture *texpaint_preview; // Layer preview
f32 mask_opacity; // Opacity mask
struct slot_material *fill_layer;
struct slot_material *fill_material;
bool show_panel;
blend_type_t blending;
i32 object_mask;
@@ -45,6 +45,13 @@ typedef struct slot_layer {
bool paint_subs;
mat4_t decal_mat; // Decal layer
struct gpu_texture *texpaint_sculpt;
f32_array_t *path_points; // uv-space [x, y, ..]
f32_array_t *path_points_world; // world-space [x, y, z, ..]
f32_array_t *path_points_camera; // [loc.xyzw, rot.xyzw, aspect, ..] at time of point placement
i32_array_t *path_points_parent;
i32 path_tool;
bool path_curved;
struct slot_material *path_material;
} slot_layer_t;
typedef struct slot_font {
@@ -572,7 +579,7 @@ typedef struct layer_data {
i32 uv_type;
struct f32_array *decal_mat;
f32 opacity_mask;
i32 fill_layer;
i32 fill_material;
i32 object_mask;
i32 blending;
i32 parent;
@@ -591,6 +598,13 @@ typedef struct layer_data {
bool paint_emis;
bool paint_subs;
i32 uv_map;
f32_array_t *path_points;
f32_array_t *path_points_world;
f32_array_t *path_points_camera;
i32_array_t *path_points_parent;
i32 path_tool;
bool path_curved;
i32 path_material;
} layer_data_t;
typedef struct shader_out {
+19 -4
View File
@@ -787,7 +787,16 @@ void ui_nodes_recompile() {
if (ui_nodes_is_tab_selected()) {
g_context->material = ui_nodes_tabs->buffer[ui_nodes_tab_index()];
}
layers_is_fill_material() ? layers_update_fill_layers() : util_render_make_material_preview();
if (layers_is_fill_material()) {
layers_update_fill_layers();
}
else if (layers_is_path_material()) {
layers_update_path_layers();
}
else {
util_render_make_material_preview();
}
g_context->material = _material;
if (ui_view2d_show && ui_view2d_type == VIEW_2D_TYPE_NODE) {
@@ -805,9 +814,15 @@ void ui_nodes_recompile() {
else if (ui_nodes_recompile_mat_final) {
make_material_parse_paint_material(true);
if (ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL && layers_is_fill_material()) {
layers_update_fill_layers();
util_render_make_material_preview();
if (ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL) {
if (layers_is_fill_material()) {
layers_update_fill_layers();
util_render_make_material_preview();
}
if (layers_is_path_material()) {
layers_update_path_layers();
util_render_make_material_preview();
}
}
bool decal = context_is_decal();
@@ -79,7 +79,7 @@ f32 uniforms_ext_f32_link(object_t *object, material_data_t *mat, char *link) {
// i32 atlas_stride = atlas_w / (float)item_w;
// return atlas_stride;
// }
bool fill = g_context->layer->fill_layer != NULL;
bool fill = g_context->layer->fill_material != NULL;
f32 val = (fill ? g_context->layer->scale : g_context->brush_scale) * g_context->brush_nodes_scale;
return val;
}
@@ -153,7 +153,7 @@ vec2_t uniforms_ext_vec2_link(object_t *object, material_data_t *mat, char *link
}
else if (string_equals(link, "_brush_angle")) {
f32 brush_angle = g_context->brush_angle + g_context->brush_nodes_angle;
f32 angle = g_context->layer->fill_layer != NULL ? g_context->layer->angle : brush_angle;
f32 angle = g_context->layer->fill_material != NULL ? g_context->layer->angle : brush_angle;
angle *= (math_pi() / 180.0);
if (g_config->pressure_angle && pen_down("tip")) {
angle *= pen_pressure * g_config->pressure_sensitivity;
+17 -3
View File
@@ -275,7 +275,7 @@ buffer_t *util_encode_project(project_t *raw) {
if (raw->layer_datas != NULL) {
armpack_encode_array(raw->layer_datas->length);
for (i32 i = 0; i < raw->layer_datas->length; ++i) {
armpack_encode_map(28);
armpack_encode_map(35);
armpack_encode_string("name");
armpack_encode_string(raw->layer_datas->buffer[i]->name);
armpack_encode_string("res");
@@ -294,8 +294,8 @@ buffer_t *util_encode_project(project_t *raw) {
armpack_encode_array_f32(raw->layer_datas->buffer[i]->decal_mat);
armpack_encode_string("opacity_mask");
armpack_encode_f32(raw->layer_datas->buffer[i]->opacity_mask);
armpack_encode_string("fill_layer");
armpack_encode_i32(raw->layer_datas->buffer[i]->fill_layer);
armpack_encode_string("fill_material");
armpack_encode_i32(raw->layer_datas->buffer[i]->fill_material);
armpack_encode_string("object_mask");
armpack_encode_i32(raw->layer_datas->buffer[i]->object_mask);
armpack_encode_string("blending");
@@ -332,6 +332,20 @@ buffer_t *util_encode_project(project_t *raw) {
armpack_encode_bool(raw->layer_datas->buffer[i]->paint_subs);
armpack_encode_string("uv_map");
armpack_encode_i32(raw->layer_datas->buffer[i]->uv_map);
armpack_encode_string("path_points");
armpack_encode_array_f32(raw->layer_datas->buffer[i]->path_points);
armpack_encode_string("path_points_world");
armpack_encode_array_f32(raw->layer_datas->buffer[i]->path_points_world);
armpack_encode_string("path_points_camera");
armpack_encode_array_f32(raw->layer_datas->buffer[i]->path_points_camera);
armpack_encode_string("path_points_parent");
armpack_encode_array_i32(raw->layer_datas->buffer[i]->path_points_parent);
armpack_encode_string("path_tool");
armpack_encode_i32(raw->layer_datas->buffer[i]->path_tool);
armpack_encode_string("path_curved");
armpack_encode_bool(raw->layer_datas->buffer[i]->path_curved);
armpack_encode_string("path_material");
armpack_encode_i32(raw->layer_datas->buffer[i]->path_material);
}
}
else {
+415
View File
@@ -0,0 +1,415 @@
#include "global.h"
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;
bool util_layer_is_path_point_dragging() {
return path_point_dragging >= 0;
}
static void path_layer_destroy_spheres() {
for (i32 i = 0; i < path_point_sphere_count; i++) {
mesh_object_remove((mesh_object_t *)path_point_spheres[i]->ext);
}
gc_free(path_point_spheres);
path_point_spheres = NULL;
path_point_sphere_count = 0;
}
static void path_layer_push_camera(f32_array_t *ar) {
vec4_t loc = scene_camera->base->transform->loc;
quat_t rot = scene_camera->base->transform->rot;
f32_array_push(ar, loc.x);
f32_array_push(ar, loc.y);
f32_array_push(ar, loc.z);
f32_array_push(ar, loc.w);
f32_array_push(ar, rot.x);
f32_array_push(ar, rot.y);
f32_array_push(ar, rot.z);
f32_array_push(ar, rot.w);
f32_array_push(ar, sys_w() / (f32)sys_h());
}
static void path_set_camera(f32_array_t *points_camera, i32 num_camera, i32 ci) {
if (ci < num_camera) {
scene_camera->base->transform->loc = (vec4_t){points_camera->buffer[ci * 9 + 0], points_camera->buffer[ci * 9 + 1], points_camera->buffer[ci * 9 + 2],
points_camera->buffer[ci * 9 + 3]};
scene_camera->base->transform->rot = (quat_t){points_camera->buffer[ci * 9 + 4], points_camera->buffer[ci * 9 + 5], points_camera->buffer[ci * 9 + 6],
points_camera->buffer[ci * 9 + 7]};
camera_object_build_proj(scene_camera, points_camera->buffer[ci * 9 + 8]);
camera_object_build_mat(scene_camera);
render_path_base_draw_gbuffer();
}
}
static void path_paint(f32 px, f32 py, bool is_decal, f32 *prev_px, f32 *prev_py) {
if (is_decal) {
g_context->decal_x = px;
g_context->decal_y = py;
}
g_context->paint_vec.x = px;
g_context->paint_vec.y = py;
g_context->last_paint_vec_x = *prev_px;
g_context->last_paint_vec_y = *prev_py;
g_context->pdirty = 1;
render_path_paint_commands_paint(false);
*prev_px = px;
*prev_py = py;
}
static void layers_repaint_path_layer(slot_layer_t *l) {
if (l->path_material == NULL) {
return;
}
f32_array_t *points_world = l->path_points_world;
if (points_world == NULL || points_world->length == 0) {
return;
}
slot_layer_t *_layer = g_context->layer;
slot_material_t *_material = g_context->material;
tool_type_t _tool = g_context->tool;
f32 _last_x = g_context->last_paint_vec_x;
f32 _last_y = g_context->last_paint_vec_y;
vec4_t _paint_vec = g_context->paint_vec;
g_context->layer = l;
g_context->material = l->path_material;
g_context->tool = l->path_tool;
bool _depth_reject = g_config->brush_depth_reject;
bool _angle_reject = g_config->brush_angle_reject;
g_config->brush_depth_reject = false;
g_config->brush_angle_reject = false;
make_material_parse_paint_material(false);
layers_set_object_mask();
slot_layer_clear(l, 0x00000000, NULL, 1.0, layers_default_rough, 0.0);
vec4_t _camera_loc = scene_camera->base->transform->loc;
quat_t _camera_rot = scene_camera->base->transform->rot;
f32_array_t *points = l->path_points;
f32_array_t *points_camera = l->path_points_camera;
i32_array_t *points_parent = l->path_points_parent;
i32 num_world = points_world->length / 3;
i32 num_camera = points_camera->length / 9;
i32 num_parent = points_parent->length;
bool is_decal = context_is_decal();
if (l->path_curved && num_world >= 1) {
f32 prev_px = 0.5f;
f32 prev_py = 0.5f;
// Paint the first anchor
f32 pt0x = points->length >= 2 ? points->buffer[0] : 0.5f;
f32 pt0y = points->length >= 2 ? points->buffer[1] : 0.5f;
path_set_camera(points_camera, num_camera, 0);
prev_px = pt0x;
prev_py = pt0y;
path_paint(pt0x, pt0y, is_decal, &prev_px, &prev_py);
// Paint curve - anchor[k], control[k+1], anchor[k+2]
for (i32 k = 0; k + 2 < num_world; k += 2) {
f32 ax0 = k * 2 + 1 < (i32)points->length ? points->buffer[k * 2] : 0.5f;
f32 ay0 = k * 2 + 1 < (i32)points->length ? points->buffer[k * 2 + 1] : 0.5f;
f32 cx = (k + 1) * 2 + 1 < (i32)points->length ? points->buffer[(k + 1) * 2] : 0.5f;
f32 cy = (k + 1) * 2 + 1 < (i32)points->length ? points->buffer[(k + 1) * 2 + 1] : 0.5f;
f32 ax1 = (k + 2) * 2 + 1 < (i32)points->length ? points->buffer[(k + 2) * 2] : 0.5f;
f32 ay1 = (k + 2) * 2 + 1 < (i32)points->length ? points->buffer[(k + 2) * 2 + 1] : 0.5f;
// 16 intermediate points + final anchor
bool have_k2 = k + 2 < num_camera;
for (i32 s = 1; s <= 17; s++) {
f32 t = s / 17.0f;
f32 t1 = 1.0f - t;
f32 bx = t1 * t1 * t1 * ax0 + 3.0f * t * t1 * cx + t * t * t * ax1;
f32 by = t1 * t1 * t1 * ay0 + 3.0f * t * t1 * cy + t * t * t * ay1;
// f32 bx = t1 * t1 * ax0 + 2.0f * t * t1 * cx + t * t * ax1;
// f32 by = t1 * t1 * ay0 + 2.0f * t * t1 * cy + t * t * ay1;
if (s == 17 && have_k2) {
path_set_camera(points_camera, num_camera, k + 2);
}
path_paint(bx, by, is_decal, &prev_px, &prev_py);
}
}
render_path_paint_dilate(true, true);
}
else {
for (i32 i = 0; i < num_world; i++) {
path_set_camera(points_camera, num_camera, i);
f32 cur_px = points->buffer[i * 2];
f32 cur_py = points->buffer[i * 2 + 1];
// Project parent world point for stroke connection
f32 prev_px = cur_px;
f32 prev_py = cur_py;
if (!is_decal) {
i32 parent = points_parent->buffer[i];
if (parent >= 0) {
f32 pwx = points_world->buffer[parent * 3];
f32 pwy = points_world->buffer[parent * 3 + 1];
f32 pwz = points_world->buffer[parent * 3 + 2];
vec4_t clip = vec4_apply_mat4((vec4_t){pwx, pwy, pwz, 1.0f}, scene_camera->vp);
if (clip.w > 0.0f) {
prev_px = (clip.x / clip.w + 1.0f) * 0.5f;
prev_py = (-clip.y / clip.w + 1.0f) * 0.5f;
}
}
}
path_paint(cur_px, cur_py, is_decal, &prev_px, &prev_py);
}
render_path_paint_dilate(true, true);
}
g_config->brush_depth_reject = _depth_reject;
g_config->brush_angle_reject = _angle_reject;
g_context->tool = _tool;
scene_camera->base->transform->loc = _camera_loc;
scene_camera->base->transform->rot = _camera_rot;
camera_object_build_proj(scene_camera, -1.0);
camera_object_build_mat(scene_camera);
render_path_base_draw_gbuffer();
g_context->pdirty = 0;
g_context->rdirty = 2;
g_context->layer = _layer;
g_context->material = _material;
make_material_parse_paint_material(false);
g_context->last_paint_vec_x = _last_x;
g_context->last_paint_vec_y = _last_y;
g_context->paint_vec = _paint_vec;
}
void util_layer_clear_path_points(slot_layer_t *l) {
if (l->path_points == NULL) {
return;
}
l->path_points->length = 0;
l->path_points_world->length = 0;
l->path_points_camera->length = 0;
l->path_points_parent->length = 0;
l->path_tool = -1;
path_layer_last_active = -1;
}
void util_layer_add_path_point(slot_layer_t *l, f32 screen_x, f32 screen_y) {
if (l->path_tool == -1) {
l->path_tool = g_context->tool;
}
if ((i32)g_context->tool != l->path_tool) {
return;
}
for (i32 j = 0; j < path_point_sphere_count; j++) {
path_point_spheres[j]->visible = false;
}
// Re-render gbuffer without spheres so gbufferD is clean
render_path_base_draw_gbuffer();
f32_array_t *points = l->path_points;
f32_array_t *points_world = l->path_points_world;
f32_array_t *points_camera = l->path_points_camera;
i32_array_t *points_parent = l->path_points_parent;
f32_array_push(points, screen_x);
f32_array_push(points, screen_y);
f32 saved_pvx = g_context->paint_vec.x;
f32 saved_pvy = g_context->paint_vec.y;
g_context->paint_vec.x = screen_x;
g_context->paint_vec.y = screen_y;
util_render_pick_pos_nor_tex();
g_context->paint_vec.x = saved_pvx;
g_context->paint_vec.y = saved_pvy;
f32_array_push(points_world, g_context->posx_picked);
f32_array_push(points_world, g_context->posy_picked);
f32_array_push(points_world, g_context->posz_picked);
path_layer_push_camera(points_camera);
i32_array_push(points_parent, path_layer_last_active);
// New point becomes the parent for the next stroke
i32 new_index = points->length / 2 - 1;
path_layer_last_active = new_index;
layers_repaint_path_layer(l);
for (i32 j = 0; j < path_point_sphere_count; j++) {
path_point_spheres[j]->visible = true;
}
}
void util_layer_repaint_path(slot_layer_t *l) {
for (i32 j = 0; j < path_point_sphere_count; j++) {
path_point_spheres[j]->visible = false;
}
layers_repaint_path_layer(l);
for (i32 j = 0; j < path_point_sphere_count; j++) {
path_point_spheres[j]->visible = true;
}
}
void util_layer_update_path() {
if (g_config->workspace == WORKSPACE_PLAYER || g_context->paint2d) {
return;
}
slot_layer_t *l = g_context->layer;
bool is_path = slot_layer_is_path(l);
// Clear spheres when switching away from path layer
if (path_layer_current != l) {
path_layer_destroy_spheres();
path_point_dragging = -1;
path_layer_current = is_path ? l : NULL;
path_layer_last_active = -1;
if (is_path) {
// Resume from the last point when switching to a path layer
i32 n = l->path_points->length / 2;
path_layer_last_active = n > 0 ? n - 1 : -1;
}
}
if (!is_path) {
return;
}
f32_array_t *points = l->path_points;
f32_array_t *points_world = l->path_points_world;
i32 num_points = points->length / 2;
i32 num_world = points_world->length / 3;
i32 vis_points = num_points;
// Sync sphere count with number of visible path points
if (vis_points < path_point_sphere_count) {
while (path_point_sphere_count > vis_points) {
path_point_sphere_count--;
mesh_object_remove((mesh_object_t *)path_point_spheres[path_point_sphere_count]->ext);
path_point_spheres[path_point_sphere_count] = NULL;
}
}
else if (vis_points > path_point_sphere_count) {
path_point_spheres = (object_t **)gc_realloc(path_point_spheres, vis_points * sizeof(object_t *));
gc_root(path_point_spheres);
for (i32 i = path_point_sphere_count; i < vis_points; i++) {
object_t *o = scene_spawn_object(".Sphere", NULL, true);
o->visible = true;
path_point_spheres[i] = o;
}
path_point_sphere_count = vis_points;
}
// Update sphere transforms
for (i32 i = 0; i < vis_points; i++) {
if (path_point_spheres[i] == NULL) {
continue;
}
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 == path_layer_last_active ? 1.5f : 1.0f);
path_point_spheres[i]->transform->loc = (vec4_t){wx, wy, wz, 1.0};
path_point_spheres[i]->transform->scale = (vec4_t){scale, scale, scale, 1.0};
transform_build_matrix(path_point_spheres[i]->transform);
}
// Release drag - the dragged point becomes the parent for the next new point
if (mouse_released("left") && path_point_dragging >= 0) {
for (i32 j = 0; j < path_point_sphere_count; j++) {
path_point_spheres[j]->visible = true;
}
path_layer_last_active = path_point_dragging;
path_point_dragging = -1;
g_context->layer_preview_dirty = true;
g_context->layers_preview_dirty = true;
}
// Drag point
if (mouse_down("left") && !mouse_started("left") && path_point_dragging >= 0) {
f32 new_x = g_context->paint_vec.x;
f32 new_y = g_context->paint_vec.y;
f32 old_x = points->buffer[path_point_dragging * 2];
f32 old_y = points->buffer[path_point_dragging * 2 + 1];
if (math_abs(new_x - old_x) > 0.0005f || math_abs(new_y - old_y) > 0.0005f) {
points->buffer[path_point_dragging * 2] = new_x;
points->buffer[path_point_dragging * 2 + 1] = new_y;
// Pick world position at new screen location
f32 saved_pvx = g_context->paint_vec.x;
f32 saved_pvy = g_context->paint_vec.y;
g_context->paint_vec.x = new_x;
g_context->paint_vec.y = new_y;
util_render_pick_pos_nor_tex();
g_context->paint_vec.x = saved_pvx;
g_context->paint_vec.y = saved_pvy;
if (math_abs(g_context->posx_picked) < 50.0f) {
points_world->buffer[path_point_dragging * 3] = g_context->posx_picked;
points_world->buffer[path_point_dragging * 3 + 1] = g_context->posy_picked;
points_world->buffer[path_point_dragging * 3 + 2] = g_context->posz_picked;
f32_array_t *points_camera = l->path_points_camera;
if (points_camera->length >= (path_point_dragging + 1) * 9) {
vec4_t loc = scene_camera->base->transform->loc;
quat_t rot = scene_camera->base->transform->rot;
points_camera->buffer[path_point_dragging * 9 + 0] = loc.x;
points_camera->buffer[path_point_dragging * 9 + 1] = loc.y;
points_camera->buffer[path_point_dragging * 9 + 2] = loc.z;
points_camera->buffer[path_point_dragging * 9 + 3] = loc.w;
points_camera->buffer[path_point_dragging * 9 + 4] = rot.x;
points_camera->buffer[path_point_dragging * 9 + 5] = rot.y;
points_camera->buffer[path_point_dragging * 9 + 6] = rot.z;
points_camera->buffer[path_point_dragging * 9 + 7] = rot.w;
points_camera->buffer[path_point_dragging * 9 + 8] = sys_w() / (f32)sys_h();
}
}
util_layer_repaint_path(l);
for (i32 j = 0; j < path_point_sphere_count; j++) {
path_point_spheres[j]->visible = false;
}
}
return;
}
// Check if mouse ray hits any point sphere
if (mouse_started("left") && !ui->is_hovered && !base_is_dragging && vis_points > 0) {
f32 min_dist = 1e10f;
i32 closest = -1;
for (i32 i = 0; i < vis_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 hit_radius = dist / 8.0f * fov * 0.25f;
ray_t *ray = raycast_get_ray(mouse_view_x(), mouse_view_y(), scene_camera);
if (ray_intersects_sphere(ray, (vec4_t){wx, wy, wz, 1.0}, hit_radius) && dist < min_dist) {
min_dist = dist;
closest = i;
}
}
if (closest >= 0) {
path_point_dragging = closest;
for (i32 j = 0; j < path_point_sphere_count; j++) {
if (path_point_spheres[j] != NULL) {
path_point_spheres[j]->visible = false;
}
}
}
}
}
+6 -6
View File
@@ -264,8 +264,8 @@ void util_render_make_brush_preview() {
g_context->layer = g_context->layer->parent;
}
slot_material_t *_fill_layer = g_context->layer->fill_layer;
g_context->layer->fill_layer = NULL;
slot_material_t *_fill_material = g_context->layer->fill_material;
g_context->layer->fill_material = NULL;
render_path_paint_use_live_layer(true);
make_material_parse_paint_material(false);
@@ -375,10 +375,10 @@ void util_render_make_brush_preview() {
g_context->prev_paint_vec_y = -1;
g_context->pdirty = _pdirty;
render_path_paint_use_live_layer(false);
g_context->layer->fill_layer = _fill_layer;
g_context->layer = _layer;
g_context->material = _material;
g_context->tool = _tool;
g_context->layer->fill_material = _fill_material;
g_context->layer = _layer;
g_context->material = _material;
g_context->tool = _tool;
sys_notify_on_next_frame(&util_render_make_brush_preview_parse_paint_material, NULL);
// Restore paint mesh