899 lines
31 KiB
C
899 lines
31 KiB
C
|
|
#include "global.h"
|
|
|
|
slot_layer_t *slot_layer_create(char *ext, layer_slot_type_t type, slot_layer_t *parent) {
|
|
slot_layer_t *raw = GC_ALLOC_INIT(slot_layer_t, {0});
|
|
raw->id = 0;
|
|
raw->ext = "";
|
|
raw->visible = true;
|
|
raw->mask_opacity = 1.0; // Opacity mask
|
|
raw->show_panel = true;
|
|
raw->blending = BLEND_TYPE_MIX;
|
|
raw->object_mask = 0;
|
|
raw->scale = 1.0;
|
|
raw->angle = 0.0;
|
|
raw->uv_type = UV_TYPE_UVMAP;
|
|
raw->paint_base = true;
|
|
raw->paint_opac = true;
|
|
raw->paint_occ = true;
|
|
raw->paint_rough = true;
|
|
raw->paint_met = true;
|
|
raw->paint_nor = true;
|
|
raw->paint_nor_blend = true;
|
|
raw->paint_height = true;
|
|
raw->paint_height_blend = true;
|
|
raw->paint_emis = true;
|
|
raw->paint_subs = true;
|
|
raw->decal_mat = mat4_identity(); // Decal layer
|
|
|
|
if (string_equals(ext, "")) {
|
|
raw->id = 0;
|
|
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
|
|
slot_layer_t *l = g_project->_->layers->buffer[i];
|
|
if (l->id >= raw->id) {
|
|
raw->id = l->id + 1;
|
|
}
|
|
}
|
|
ext = string("%d", raw->id);
|
|
}
|
|
raw->ext = string_copy(ext);
|
|
raw->parent = parent;
|
|
|
|
if (type == LAYER_SLOT_TYPE_GROUP) {
|
|
i32 id = (raw->id + 1);
|
|
raw->name = string("Group %d", id);
|
|
}
|
|
else if (type == LAYER_SLOT_TYPE_LAYER) {
|
|
i32 id = (raw->id + 1);
|
|
raw->name = string("Layer %d", id);
|
|
char *format = base_bits_handle->i == TEXTURE_BITS_BITS8 ? "RGBA32" : base_bits_handle->i == TEXTURE_BITS_BITS16 ? "RGBA64" : "RGBA128";
|
|
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = string("texpaint%s", ext);
|
|
t->width = config_get_texture_res_x();
|
|
t->height = config_get_texture_res_y();
|
|
t->format = string_copy(format);
|
|
raw->texpaint = render_path_create_render_target(t)->_image;
|
|
}
|
|
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = string("texpaint_nor%s", ext);
|
|
t->width = config_get_texture_res_x();
|
|
t->height = config_get_texture_res_y();
|
|
t->format = string_copy(format);
|
|
raw->texpaint_nor = render_path_create_render_target(t)->_image;
|
|
}
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = string("texpaint_pack%s", ext);
|
|
t->width = config_get_texture_res_x();
|
|
t->height = config_get_texture_res_y();
|
|
t->format = string_copy(format);
|
|
raw->texpaint_pack = render_path_create_render_target(t)->_image;
|
|
}
|
|
|
|
raw->texpaint_preview = gpu_create_render_target(util_render_layer_preview_size, util_render_layer_preview_size, GPU_TEXTURE_FORMAT_RGBA32);
|
|
|
|
if (slot_layer_is_filter(raw)) {
|
|
raw->name = string("Filter %d", id);
|
|
}
|
|
}
|
|
|
|
else { // Mask
|
|
i32 id = (raw->id + 1);
|
|
raw->name = string("Mask %d", id);
|
|
char *format = "RGBA32"; // Full bits for undo support, R8 is used
|
|
raw->blending = BLEND_TYPE_MIX;
|
|
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = string("texpaint%s", ext);
|
|
t->width = config_get_texture_res_x();
|
|
t->height = config_get_texture_res_y();
|
|
t->format = string_copy(format);
|
|
raw->texpaint = render_path_create_render_target(t)->_image;
|
|
}
|
|
|
|
raw->texpaint_preview = gpu_create_render_target(util_render_layer_preview_size, util_render_layer_preview_size, GPU_TEXTURE_FORMAT_RGBA32);
|
|
}
|
|
|
|
return raw;
|
|
}
|
|
|
|
void slot_layer_delete(slot_layer_t *raw) {
|
|
slot_layer_unload(raw);
|
|
|
|
if (slot_layer_is_layer(raw)) {
|
|
slot_layer_t_array_t *masks = slot_layer_get_masks(raw, false); // Prevents deleting group masks
|
|
if (masks != NULL) {
|
|
for (i32 i = 0; i < masks->length; ++i) {
|
|
slot_layer_t *m = masks->buffer[i];
|
|
slot_layer_delete(m);
|
|
}
|
|
}
|
|
}
|
|
else if (slot_layer_is_group(raw)) {
|
|
slot_layer_t_array_t *children = slot_layer_get_children(raw);
|
|
if (children != NULL) {
|
|
for (i32 i = 0; i < children->length; ++i) {
|
|
slot_layer_t *c = children->buffer[i];
|
|
slot_layer_delete(c);
|
|
}
|
|
}
|
|
slot_layer_t_array_t *masks = slot_layer_get_masks(raw, true);
|
|
if (masks != NULL) {
|
|
for (i32 i = 0; i < masks->length; ++i) {
|
|
slot_layer_t *m = masks->buffer[i];
|
|
slot_layer_delete(m);
|
|
}
|
|
}
|
|
}
|
|
|
|
i32 lpos = array_index_of(g_project->_->layers, raw);
|
|
array_remove(g_project->_->layers, raw);
|
|
// Undo can remove base layer and then restore it from undo layers
|
|
if (g_project->_->layers->length > 0) {
|
|
context_set_layer(g_project->_->layers->buffer[lpos > 0 ? lpos - 1 : 0]);
|
|
}
|
|
|
|
// Do not remove empty groups if the last layer is deleted as this prevents redo from working properly
|
|
}
|
|
|
|
void slot_layer_unload(slot_layer_t *raw) {
|
|
if (slot_layer_is_group(raw)) {
|
|
return;
|
|
}
|
|
|
|
gpu_texture_t *_texpaint = raw->texpaint;
|
|
gpu_texture_t *_texpaint_nor = raw->texpaint_nor;
|
|
gpu_texture_t *_texpaint_pack = raw->texpaint_pack;
|
|
gpu_texture_t *_texpaint_preview = raw->texpaint_preview;
|
|
|
|
gpu_delete_texture(_texpaint);
|
|
if (_texpaint_nor != NULL) {
|
|
gpu_delete_texture(_texpaint_nor);
|
|
}
|
|
if (_texpaint_pack != NULL) {
|
|
gpu_delete_texture(_texpaint_pack);
|
|
}
|
|
if (_texpaint_preview != NULL) {
|
|
gpu_delete_texture(_texpaint_preview);
|
|
}
|
|
|
|
map_delete(render_path_render_targets, string("texpaint%s", raw->ext));
|
|
if (slot_layer_is_layer(raw)) {
|
|
map_delete(render_path_render_targets, string("texpaint_nor%s", raw->ext));
|
|
map_delete(render_path_render_targets, string("texpaint_pack%s", raw->ext));
|
|
}
|
|
}
|
|
|
|
void slot_layer_swap(slot_layer_t *raw, slot_layer_t *other) {
|
|
if ((slot_layer_is_layer(raw) || slot_layer_is_mask(raw)) && (slot_layer_is_layer(other) || slot_layer_is_mask(other))) {
|
|
render_target_t *rt0 = any_map_get(render_path_render_targets, string("texpaint%s", raw->ext));
|
|
render_target_t *rt1 = any_map_get(render_path_render_targets, string("texpaint%s", other->ext));
|
|
rt0->_image = other->texpaint;
|
|
rt1->_image = raw->texpaint;
|
|
gpu_texture_t *_texpaint = raw->texpaint;
|
|
raw->texpaint = other->texpaint;
|
|
other->texpaint = _texpaint;
|
|
|
|
gpu_texture_t *_texpaint_preview = raw->texpaint_preview;
|
|
raw->texpaint_preview = other->texpaint_preview;
|
|
other->texpaint_preview = _texpaint_preview;
|
|
}
|
|
|
|
if (raw->texpaint_sculpt != NULL && other->texpaint_sculpt != NULL) {
|
|
render_target_t *srt0 = any_map_get(render_path_render_targets, string("texpaint_sculpt%s", raw->ext));
|
|
render_target_t *srt1 = any_map_get(render_path_render_targets, string("texpaint_sculpt%s", other->ext));
|
|
srt0->_image = other->texpaint_sculpt;
|
|
srt1->_image = raw->texpaint_sculpt;
|
|
gpu_texture_t *_texpaint_sculpt = raw->texpaint_sculpt;
|
|
raw->texpaint_sculpt = other->texpaint_sculpt;
|
|
other->texpaint_sculpt = _texpaint_sculpt;
|
|
}
|
|
|
|
if (slot_layer_is_layer(raw) && slot_layer_is_layer(other)) {
|
|
render_target_t *nor0 = any_map_get(render_path_render_targets, string("texpaint_nor%s", raw->ext));
|
|
nor0->_image = other->texpaint_nor;
|
|
render_target_t *pack0 = any_map_get(render_path_render_targets, string("texpaint_pack%s", raw->ext));
|
|
pack0->_image = other->texpaint_pack;
|
|
render_target_t *nor1 = any_map_get(render_path_render_targets, string("texpaint_nor%s", other->ext));
|
|
nor1->_image = raw->texpaint_nor;
|
|
render_target_t *pack1 = any_map_get(render_path_render_targets, string("texpaint_pack%s", other->ext));
|
|
pack1->_image = raw->texpaint_pack;
|
|
gpu_texture_t *_texpaint_nor = raw->texpaint_nor;
|
|
gpu_texture_t *_texpaint_pack = raw->texpaint_pack;
|
|
raw->texpaint_nor = other->texpaint_nor;
|
|
raw->texpaint_pack = other->texpaint_pack;
|
|
other->texpaint_nor = _texpaint_nor;
|
|
other->texpaint_pack = _texpaint_pack;
|
|
}
|
|
}
|
|
|
|
void slot_layer_clear(slot_layer_t *raw, i32 base_color, gpu_texture_t *base_image, f32 occlusion, f32 roughness, f32 metallic) {
|
|
// Base
|
|
_gpu_begin(raw->texpaint, NULL, NULL, GPU_CLEAR_COLOR, base_color, 0.0);
|
|
gpu_end();
|
|
if (base_image != NULL) {
|
|
draw_begin(raw->texpaint, false, 0);
|
|
draw_scaled_image(base_image, 0, 0, raw->texpaint->width, raw->texpaint->height);
|
|
draw_end();
|
|
}
|
|
|
|
if (slot_layer_is_layer(raw)) {
|
|
// Nor
|
|
_gpu_begin(raw->texpaint_nor, NULL, NULL, GPU_CLEAR_COLOR, color_from_floats(0.5, 0.5, 1.0, 0.0), 0.0);
|
|
gpu_end();
|
|
// Occ, rough, met
|
|
_gpu_begin(raw->texpaint_pack, NULL, NULL, GPU_CLEAR_COLOR, color_from_floats(occlusion, roughness, metallic, 0.0), 0.0);
|
|
gpu_end();
|
|
}
|
|
|
|
g_context->layer_preview_dirty = true;
|
|
g_context->ddirty = 3;
|
|
}
|
|
|
|
void slot_layer_invert_mask(slot_layer_t *raw) {
|
|
gpu_texture_t *inverted = gpu_create_render_target(raw->texpaint->width, raw->texpaint->height, GPU_TEXTURE_FORMAT_RGBA32);
|
|
draw_begin(inverted, false, 0);
|
|
draw_set_pipeline(pipes_invert_mask);
|
|
draw_image(raw->texpaint, 0, 0);
|
|
draw_set_pipeline(NULL);
|
|
draw_end();
|
|
gpu_texture_t *_texpaint = raw->texpaint;
|
|
gpu_delete_texture(_texpaint);
|
|
render_target_t *rt = any_map_get(render_path_render_targets, string("texpaint%d", raw->id));
|
|
raw->texpaint = rt->_image = inverted;
|
|
g_context->layer_preview_dirty = true;
|
|
g_context->ddirty = 3;
|
|
}
|
|
|
|
void layers_apply_mask(slot_layer_t *l, slot_layer_t *m) {
|
|
if (!slot_layer_is_layer(l) || !slot_layer_is_mask(m)) {
|
|
return;
|
|
}
|
|
|
|
layers_make_temp_img();
|
|
|
|
// Copy layer to temp
|
|
draw_begin(layers_temp_image, false, 0);
|
|
draw_set_pipeline(pipes_copy);
|
|
draw_image(l->texpaint, 0, 0);
|
|
draw_set_pipeline(NULL);
|
|
draw_end();
|
|
|
|
// Apply mask
|
|
_gpu_begin(l->texpaint, NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
gpu_set_pipeline(pipes_apply_mask);
|
|
gpu_set_texture(pipes_tex0_mask, layers_temp_image);
|
|
gpu_set_texture(pipes_texa_mask, m->texpaint);
|
|
gpu_set_float(pipes_opac_apply_mask, m->mask_opacity);
|
|
gpu_set_vertex_buffer(const_data_screen_aligned_vb);
|
|
gpu_set_index_buffer(const_data_screen_aligned_ib);
|
|
gpu_draw();
|
|
gpu_end();
|
|
}
|
|
|
|
void slot_layer_apply_mask(slot_layer_t *raw) {
|
|
if (raw->parent->fill_material != NULL) {
|
|
slot_layer_to_paint_layer(raw->parent);
|
|
}
|
|
if (slot_layer_is_group(raw->parent)) {
|
|
for (i32 i = 0; i < slot_layer_get_children(raw->parent)->length; ++i) {
|
|
slot_layer_t *c = slot_layer_get_children(raw->parent)->buffer[i];
|
|
layers_apply_mask(c, raw);
|
|
}
|
|
}
|
|
else {
|
|
layers_apply_mask(raw->parent, raw);
|
|
}
|
|
slot_layer_delete(raw);
|
|
}
|
|
|
|
slot_layer_t *slot_layer_duplicate(slot_layer_t *raw) {
|
|
slot_layer_t_array_t *layers = g_project->_->layers;
|
|
i32 i = array_index_of(layers, raw) + 1;
|
|
slot_layer_t *l = slot_layer_create("",
|
|
slot_layer_is_layer(raw) ? LAYER_SLOT_TYPE_LAYER
|
|
: slot_layer_is_mask(raw) ? LAYER_SLOT_TYPE_MASK
|
|
: LAYER_SLOT_TYPE_GROUP,
|
|
raw->parent);
|
|
array_insert(layers, i, l);
|
|
|
|
if (slot_layer_is_layer(raw)) {
|
|
draw_begin(l->texpaint, false, 0);
|
|
draw_set_pipeline(pipes_copy);
|
|
draw_image(raw->texpaint, 0, 0);
|
|
draw_set_pipeline(NULL);
|
|
draw_end();
|
|
|
|
if (l->texpaint_nor != NULL) {
|
|
draw_begin(l->texpaint_nor, false, 0);
|
|
draw_set_pipeline(pipes_copy);
|
|
draw_image(raw->texpaint_nor, 0, 0);
|
|
draw_set_pipeline(NULL);
|
|
draw_end();
|
|
}
|
|
|
|
if (l->texpaint_pack != NULL) {
|
|
draw_begin(l->texpaint_pack, false, 0);
|
|
draw_set_pipeline(pipes_copy);
|
|
draw_image(raw->texpaint_pack, 0, 0);
|
|
draw_set_pipeline(NULL);
|
|
draw_end();
|
|
}
|
|
}
|
|
else if (slot_layer_is_mask(raw)) {
|
|
draw_begin(l->texpaint, false, 0);
|
|
draw_set_pipeline(pipes_copy8);
|
|
draw_image(raw->texpaint, 0, 0);
|
|
draw_set_pipeline(NULL);
|
|
draw_end();
|
|
}
|
|
|
|
if (l->texpaint_preview != NULL) {
|
|
draw_begin(l->texpaint_preview, true, 0x00000000);
|
|
draw_set_pipeline(pipes_copy);
|
|
draw_scaled_image(raw->texpaint_preview, 0, 0, raw->texpaint_preview->width, raw->texpaint_preview->height);
|
|
draw_set_pipeline(NULL);
|
|
draw_end();
|
|
}
|
|
|
|
l->visible = raw->visible;
|
|
l->mask_opacity = raw->mask_opacity;
|
|
l->fill_material = raw->fill_material;
|
|
l->object_mask = raw->object_mask;
|
|
l->blending = raw->blending;
|
|
l->uv_type = raw->uv_type;
|
|
l->decal_mat = raw->decal_mat;
|
|
l->scale = raw->scale;
|
|
l->angle = raw->angle;
|
|
l->paint_base = raw->paint_base;
|
|
l->paint_opac = raw->paint_opac;
|
|
l->paint_occ = raw->paint_occ;
|
|
l->paint_rough = raw->paint_rough;
|
|
l->paint_met = raw->paint_met;
|
|
l->paint_nor = raw->paint_nor;
|
|
l->paint_nor_blend = raw->paint_nor_blend;
|
|
l->paint_height = raw->paint_height;
|
|
l->paint_height_blend = raw->paint_height_blend;
|
|
l->paint_emis = raw->paint_emis;
|
|
l->paint_subs = raw->paint_subs;
|
|
|
|
if (raw->path_points != NULL) {
|
|
l->path_points = f32_array_create_from_raw(raw->path_points->buffer, raw->path_points->length);
|
|
l->path_points_world = f32_array_create_from_raw(raw->path_points_world->buffer, raw->path_points_world->length);
|
|
l->path_points_camera = f32_array_create_from_raw(raw->path_points_camera->buffer, raw->path_points_camera->length);
|
|
l->path_points_parent = i32_array_create_from_raw(raw->path_points_parent->buffer, raw->path_points_parent->length);
|
|
l->path_tool = raw->path_tool;
|
|
l->path_curved = raw->path_curved;
|
|
l->path_material = raw->path_material;
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
void slot_layer_resize_and_set_bits(slot_layer_t *raw) {
|
|
i32 res_x = config_get_texture_res_x();
|
|
i32 res_y = config_get_texture_res_y();
|
|
any_map_t *rts = render_path_render_targets;
|
|
|
|
if (slot_layer_is_layer(raw)) {
|
|
gpu_texture_format_t format = base_bits_handle->i == TEXTURE_BITS_BITS8 ? GPU_TEXTURE_FORMAT_RGBA32
|
|
: base_bits_handle->i == TEXTURE_BITS_BITS16 ? GPU_TEXTURE_FORMAT_RGBA64
|
|
: GPU_TEXTURE_FORMAT_RGBA128;
|
|
|
|
gpu_pipeline_t *pipe = format == GPU_TEXTURE_FORMAT_RGBA32 ? pipes_copy : format == GPU_TEXTURE_FORMAT_RGBA64 ? pipes_copy64 : pipes_copy128;
|
|
|
|
gpu_texture_t *_texpaint = raw->texpaint;
|
|
raw->texpaint = gpu_create_render_target(res_x, res_y, format);
|
|
draw_begin(raw->texpaint, false, 0);
|
|
draw_set_pipeline(pipe);
|
|
draw_scaled_image(_texpaint, 0, 0, res_x, res_y);
|
|
draw_set_pipeline(NULL);
|
|
draw_end();
|
|
|
|
gpu_texture_t *_texpaint_nor = raw->texpaint_nor;
|
|
if (raw->texpaint_nor != NULL) {
|
|
raw->texpaint_nor = gpu_create_render_target(res_x, res_y, format);
|
|
draw_begin(raw->texpaint_nor, false, 0);
|
|
draw_set_pipeline(pipe);
|
|
draw_scaled_image(_texpaint_nor, 0, 0, res_x, res_y);
|
|
draw_set_pipeline(NULL);
|
|
draw_end();
|
|
}
|
|
|
|
gpu_texture_t *_texpaint_pack = raw->texpaint_pack;
|
|
if (raw->texpaint_pack != NULL) {
|
|
raw->texpaint_pack = gpu_create_render_target(res_x, res_y, format);
|
|
draw_begin(raw->texpaint_pack, false, 0);
|
|
draw_set_pipeline(pipe);
|
|
draw_scaled_image(_texpaint_pack, 0, 0, res_x, res_y);
|
|
draw_set_pipeline(NULL);
|
|
draw_end();
|
|
}
|
|
|
|
gpu_delete_texture(_texpaint);
|
|
if (_texpaint_nor != NULL) {
|
|
gpu_delete_texture(_texpaint_nor);
|
|
}
|
|
if (_texpaint_pack != NULL) {
|
|
gpu_delete_texture(_texpaint_pack);
|
|
}
|
|
|
|
render_target_t *rt = any_map_get(rts, string("texpaint%s", raw->ext));
|
|
rt->_image = raw->texpaint;
|
|
|
|
if (raw->texpaint_nor != NULL) {
|
|
render_target_t *rt_nor = any_map_get(rts, string("texpaint_nor%s", raw->ext));
|
|
rt_nor->_image = raw->texpaint_nor;
|
|
}
|
|
|
|
if (raw->texpaint_pack != NULL) {
|
|
render_target_t *rt_pack = any_map_get(rts, string("texpaint_pack%s", raw->ext));
|
|
rt_pack->_image = raw->texpaint_pack;
|
|
}
|
|
}
|
|
else if (slot_layer_is_mask(raw)) {
|
|
gpu_texture_t *_texpaint = raw->texpaint;
|
|
raw->texpaint = gpu_create_render_target(res_x, res_y, GPU_TEXTURE_FORMAT_RGBA32);
|
|
|
|
draw_begin(raw->texpaint, false, 0);
|
|
draw_set_pipeline(pipes_copy8);
|
|
draw_scaled_image(_texpaint, 0, 0, res_x, res_y);
|
|
draw_set_pipeline(NULL);
|
|
draw_end();
|
|
|
|
gpu_delete_texture(_texpaint);
|
|
|
|
render_target_t *rt = any_map_get(rts, string("texpaint%s", raw->ext));
|
|
rt->_image = raw->texpaint;
|
|
}
|
|
|
|
// Clear and repack the base mesh
|
|
if (raw->texpaint_sculpt != NULL) {
|
|
gpu_texture_t *_texpaint_sculpt = raw->texpaint_sculpt;
|
|
raw->texpaint_sculpt = gpu_create_render_target(res_x, res_y, GPU_TEXTURE_FORMAT_RGBA128);
|
|
sculpt_import_mesh_pack_to_texture(raw->texpaint_sculpt);
|
|
gpu_delete_texture(_texpaint_sculpt);
|
|
|
|
render_target_t *srt = any_map_get(rts, string("texpaint_sculpt%s", raw->ext));
|
|
srt->width = res_x;
|
|
srt->height = res_y;
|
|
srt->_image = raw->texpaint_sculpt;
|
|
}
|
|
}
|
|
|
|
void slot_layer_to_fill_layer_on_next_frame(void *_) {
|
|
make_material_parse_paint_material(true);
|
|
g_context->layer_preview_dirty = true;
|
|
ui_base_hwnds->buffer[TAB_AREA_SIDEBAR0]->redraws = 2;
|
|
}
|
|
|
|
void slot_layer_to_fill_layer(slot_layer_t *raw) {
|
|
context_set_layer(raw);
|
|
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_material = NULL;
|
|
if (raw->path_material != NULL) {
|
|
raw->path_material = NULL;
|
|
raw->path_points = NULL;
|
|
util_layer_update_path();
|
|
}
|
|
make_material_parse_paint_material(true);
|
|
g_context->layer_preview_dirty = true;
|
|
ui_base_hwnds->buffer[TAB_AREA_SIDEBAR0]->redraws = 2;
|
|
}
|
|
|
|
bool slot_layer_is_visible(slot_layer_t *raw) {
|
|
return raw->visible && (raw->parent == NULL || raw->parent->visible);
|
|
}
|
|
|
|
slot_layer_t_array_t *slot_layer_get_children(slot_layer_t *raw) {
|
|
slot_layer_t_array_t *children = NULL; // Child layers of a group
|
|
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
|
|
slot_layer_t *l = g_project->_->layers->buffer[i];
|
|
if (l->parent == raw && slot_layer_is_layer(l)) {
|
|
if (children == NULL) {
|
|
children = any_array_create_from_raw((void *[]){}, 0);
|
|
}
|
|
any_array_push(children, l);
|
|
}
|
|
}
|
|
return children;
|
|
}
|
|
|
|
slot_layer_t_array_t *slot_layer_get_recursive_children(slot_layer_t *raw) {
|
|
slot_layer_t_array_t *children = NULL;
|
|
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
|
|
slot_layer_t *l = g_project->_->layers->buffer[i];
|
|
if (l->parent == raw) { // Child layers and group masks
|
|
if (children == NULL) {
|
|
children = any_array_create_from_raw((void *[]){}, 0);
|
|
}
|
|
any_array_push(children, l);
|
|
}
|
|
if (l->parent != NULL && l->parent->parent == raw) { // Layer masks
|
|
if (children == NULL) {
|
|
children = any_array_create_from_raw((void *[]){}, 0);
|
|
}
|
|
any_array_push(children, l);
|
|
}
|
|
}
|
|
return children;
|
|
}
|
|
|
|
slot_layer_t_array_t *slot_layer_get_masks(slot_layer_t *raw, bool include_group_masks) {
|
|
if (slot_layer_is_mask(raw)) {
|
|
return NULL;
|
|
}
|
|
|
|
slot_layer_t_array_t *children = NULL;
|
|
// Child masks of a layer
|
|
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
|
|
slot_layer_t *l = g_project->_->layers->buffer[i];
|
|
if (l->parent == raw && slot_layer_is_mask(l)) {
|
|
if (children == NULL) {
|
|
children = any_array_create_from_raw((void *[]){}, 0);
|
|
}
|
|
any_array_push(children, l);
|
|
}
|
|
}
|
|
// Child masks of a parent group
|
|
if (include_group_masks) {
|
|
if (raw->parent != NULL && slot_layer_is_group(raw->parent)) {
|
|
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
|
|
slot_layer_t *l = g_project->_->layers->buffer[i];
|
|
if (l->parent == raw->parent && slot_layer_is_mask(l)) {
|
|
if (children == NULL) {
|
|
children = any_array_create_from_raw((void *[]){}, 0);
|
|
}
|
|
any_array_push(children, l);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return children;
|
|
}
|
|
|
|
bool slot_layer_has_masks(slot_layer_t *raw, bool include_group_masks) {
|
|
// Layer mask
|
|
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
|
|
slot_layer_t *l = g_project->_->layers->buffer[i];
|
|
if (l->parent == raw && slot_layer_is_mask(l)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Group mask
|
|
if (include_group_masks && raw->parent != NULL && slot_layer_is_group(raw->parent)) {
|
|
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
|
|
slot_layer_t *l = g_project->_->layers->buffer[i];
|
|
if (l->parent == raw->parent && slot_layer_is_mask(l)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
slot_layer_t_array_t *slot_layer_get_filters(slot_layer_t *raw, bool include_group_filters) {
|
|
if (slot_layer_is_filter(raw)) {
|
|
return NULL;
|
|
}
|
|
|
|
slot_layer_t_array_t *children = NULL;
|
|
// Child filters of a layer
|
|
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
|
|
slot_layer_t *l = g_project->_->layers->buffer[i];
|
|
if (l->parent == raw && slot_layer_is_filter(l)) {
|
|
if (children == NULL) {
|
|
children = any_array_create_from_raw((void *[]){}, 0);
|
|
}
|
|
any_array_push(children, l);
|
|
}
|
|
}
|
|
// Child filters of a parent group
|
|
if (include_group_filters) {
|
|
if (raw->parent != NULL && slot_layer_is_group(raw->parent)) {
|
|
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
|
|
slot_layer_t *l = g_project->_->layers->buffer[i];
|
|
if (l->parent == raw->parent && slot_layer_is_filter(l)) {
|
|
if (children == NULL) {
|
|
children = any_array_create_from_raw((void *[]){}, 0);
|
|
}
|
|
any_array_push(children, l);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return children;
|
|
}
|
|
|
|
f32 slot_layer_get_opacity(slot_layer_t *raw) {
|
|
f32 f = raw->mask_opacity;
|
|
if (slot_layer_is_layer(raw) && raw->parent != NULL) {
|
|
f *= raw->parent->mask_opacity;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
i32 slot_layer_get_object_mask(slot_layer_t *raw) {
|
|
return (slot_layer_is_mask(raw) || slot_layer_is_filter(raw)) ? raw->parent->object_mask : raw->object_mask;
|
|
}
|
|
|
|
i32 slot_layer_get_blending(slot_layer_t *raw) {
|
|
return slot_layer_is_filter(raw) ? raw->parent->blending : raw->blending;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
slot_layer_t *slot_layer_get_containing_group(slot_layer_t *raw) {
|
|
if (raw->parent != NULL && slot_layer_is_group(raw->parent)) {
|
|
return raw->parent;
|
|
}
|
|
else if (raw->parent != NULL && raw->parent->parent != NULL && slot_layer_is_group(raw->parent->parent)) {
|
|
return raw->parent->parent;
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
bool slot_layer_is_filter(slot_layer_t *raw) {
|
|
return raw->texpaint != NULL && raw->texpaint_nor != NULL && raw->parent != NULL && slot_layer_is_layer(raw->parent);
|
|
}
|
|
|
|
bool slot_layer_is_mask(slot_layer_t *raw) {
|
|
return raw->texpaint != NULL && raw->texpaint_nor == NULL;
|
|
}
|
|
|
|
bool slot_layer_is_group_mask(slot_layer_t *raw) {
|
|
return slot_layer_is_mask(raw) && slot_layer_is_group(raw->parent);
|
|
}
|
|
|
|
bool slot_layer_is_layer_mask(slot_layer_t *raw) {
|
|
return slot_layer_is_mask(raw) && slot_layer_is_layer(raw->parent);
|
|
}
|
|
|
|
bool slot_layer_is_in_group(slot_layer_t *raw) {
|
|
return raw->parent != NULL && (slot_layer_is_group(raw->parent) || (raw->parent->parent != NULL && slot_layer_is_group(raw->parent->parent)));
|
|
}
|
|
|
|
bool slot_layer_can_move(slot_layer_t *raw, i32 to) {
|
|
i32 old_index = array_index_of(g_project->_->layers, raw);
|
|
|
|
i32 delta = to - old_index; // If delta > 0 the layer is moved up, otherwise down
|
|
if (to < 0 || to > g_project->_->layers->length - 1 || delta == 0) {
|
|
return false;
|
|
}
|
|
|
|
// If the layer is moved up, all layers between the old position and the new one move one down
|
|
// The layers above the new position stay where they are
|
|
// If the new position is on top or on bottom no upper resp. lower layer exists
|
|
slot_layer_t *new_upper_layer =
|
|
delta > 0 ? (to < g_project->_->layers->length - 1 ? g_project->_->layers->buffer[to + 1] : NULL) : g_project->_->layers->buffer[to];
|
|
|
|
// Group or layer is collapsed so we check below and update the upper layer
|
|
if (new_upper_layer != NULL && !new_upper_layer->show_panel) {
|
|
slot_layer_t_array_t *children = slot_layer_get_recursive_children(new_upper_layer);
|
|
to -= children != NULL ? children->length : 0;
|
|
delta = to - old_index;
|
|
new_upper_layer = delta > 0 ? (to < g_project->_->layers->length - 1 ? g_project->_->layers->buffer[to + 1] : NULL) : g_project->_->layers->buffer[to];
|
|
}
|
|
|
|
slot_layer_t *new_lower_layer = delta > 0 ? g_project->_->layers->buffer[to] : (to > 0 ? g_project->_->layers->buffer[to - 1] : NULL);
|
|
|
|
if (slot_layer_is_mask(raw)) {
|
|
// Masks can not be on top
|
|
if (new_upper_layer == NULL) {
|
|
return false;
|
|
}
|
|
// Masks should not be placed below a collapsed group - this condition can be savely removed
|
|
if (slot_layer_is_in_group(new_upper_layer) && !slot_layer_get_containing_group(new_upper_layer)->show_panel) {
|
|
return false;
|
|
}
|
|
// Masks should not be placed below a collapsed layer - this condition can be savely removed
|
|
if (slot_layer_is_mask(new_upper_layer) && !new_upper_layer->parent->show_panel) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (slot_layer_is_filter(raw)) {
|
|
// Filters can not be on top
|
|
if (new_upper_layer == NULL) {
|
|
return false;
|
|
}
|
|
// Filters should not be placed below a collapsed group
|
|
if (slot_layer_is_in_group(new_upper_layer) && !slot_layer_get_containing_group(new_upper_layer)->show_panel) {
|
|
return false;
|
|
}
|
|
// Filters should not be placed below a collapsed layer
|
|
if ((slot_layer_is_filter(new_upper_layer) || slot_layer_is_mask(new_upper_layer)) && !new_upper_layer->parent->show_panel) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (slot_layer_is_layer(raw) && !slot_layer_is_filter(raw)) {
|
|
// Layers can not be moved directly below its own mask(s)
|
|
if (new_upper_layer != NULL && slot_layer_is_mask(new_upper_layer) && new_upper_layer->parent == raw) {
|
|
return false;
|
|
}
|
|
// Layers can not be moved directly below its own filter(s)
|
|
if (new_upper_layer != NULL && slot_layer_is_filter(new_upper_layer) && new_upper_layer->parent == raw) {
|
|
return false;
|
|
}
|
|
// Layers can not be placed above a mask as the mask would be reparented
|
|
if (new_lower_layer != NULL && slot_layer_is_mask(new_lower_layer)) {
|
|
return false;
|
|
}
|
|
// Layers can not be placed above a filter as the filter would be reparented
|
|
if (new_lower_layer != NULL && slot_layer_is_filter(new_lower_layer)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Currently groups can not be nested - thus valid positions for groups are:
|
|
if (slot_layer_is_group(raw)) {
|
|
// At the top
|
|
if (new_upper_layer == NULL) {
|
|
return true;
|
|
}
|
|
// NOT below its own children
|
|
if (slot_layer_get_containing_group(new_upper_layer) == raw) {
|
|
return false;
|
|
}
|
|
// At the bottom
|
|
if (new_lower_layer == NULL) {
|
|
return true;
|
|
}
|
|
// Above a group
|
|
if (slot_layer_is_group(new_lower_layer)) {
|
|
return true;
|
|
}
|
|
// Above a non-grouped layer
|
|
if (slot_layer_is_layer(new_lower_layer) && !slot_layer_is_in_group(new_lower_layer)) {
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void slot_layer_move(slot_layer_t *raw, i32 to) {
|
|
if (!slot_layer_can_move(raw, to)) {
|
|
return;
|
|
}
|
|
|
|
i32_map_t *pointers = tab_layers_init_layer_map();
|
|
i32 old_index = array_index_of(g_project->_->layers, raw);
|
|
i32 delta = to - old_index;
|
|
slot_layer_t *new_upper_layer =
|
|
delta > 0 ? (to < g_project->_->layers->length - 1 ? g_project->_->layers->buffer[to + 1] : NULL) : g_project->_->layers->buffer[to];
|
|
|
|
// Group or layer is collapsed so we check below and update the upper layer
|
|
if (new_upper_layer != NULL && !new_upper_layer->show_panel) {
|
|
slot_layer_t_array_t *children = slot_layer_get_recursive_children(new_upper_layer);
|
|
to -= children != NULL ? children->length : 0;
|
|
delta = to - old_index;
|
|
new_upper_layer = delta > 0 ? (to < g_project->_->layers->length - 1 ? g_project->_->layers->buffer[to + 1] : NULL) : g_project->_->layers->buffer[to];
|
|
}
|
|
|
|
context_set_layer(raw);
|
|
history_order_layers(to);
|
|
ui_base_hwnds->buffer[TAB_AREA_SIDEBAR0]->redraws = 2;
|
|
|
|
array_remove(g_project->_->layers, raw);
|
|
array_insert(g_project->_->layers, to, raw);
|
|
|
|
if (slot_layer_is_filter(raw)) {
|
|
// Precondition new_upper_layer != NULL, ensured in can_move
|
|
if (slot_layer_is_filter(new_upper_layer) || slot_layer_is_mask(new_upper_layer)) {
|
|
raw->parent = new_upper_layer->parent;
|
|
}
|
|
else if (slot_layer_is_layer(new_upper_layer) || slot_layer_is_group(new_upper_layer)) {
|
|
raw->parent = new_upper_layer;
|
|
}
|
|
}
|
|
else if (slot_layer_is_layer(raw)) {
|
|
slot_layer_t *old_parent = raw->parent;
|
|
|
|
if (new_upper_layer == NULL) {
|
|
raw->parent = NULL; // Placed on top
|
|
}
|
|
else if (slot_layer_is_in_group(new_upper_layer) && !slot_layer_get_containing_group(new_upper_layer)->show_panel) {
|
|
raw->parent = NULL; // Placed below a collapsed group
|
|
}
|
|
else if (slot_layer_is_filter(new_upper_layer)) {
|
|
raw->parent = new_upper_layer->parent->parent; // Placed below a filter, use the same level as the filter's parent
|
|
}
|
|
else if (slot_layer_is_layer(new_upper_layer)) {
|
|
raw->parent = new_upper_layer->parent; // Placed below a layer, use the same parent
|
|
}
|
|
else if (slot_layer_is_group(new_upper_layer)) {
|
|
raw->parent = new_upper_layer; // Placed as top layer in a group
|
|
}
|
|
else if (slot_layer_is_group_mask(new_upper_layer)) {
|
|
raw->parent = new_upper_layer->parent; // Placed in a group below the lowest group mask
|
|
}
|
|
else if (slot_layer_is_layer_mask(new_upper_layer)) {
|
|
raw->parent = slot_layer_get_containing_group(new_upper_layer); // Either the group the mask belongs to or NULL
|
|
}
|
|
|
|
// Layers can have masks as children
|
|
// These have to be moved, too
|
|
slot_layer_t_array_t *layer_masks = slot_layer_get_masks(raw, false);
|
|
if (layer_masks != NULL) {
|
|
for (i32 idx = 0; idx < layer_masks->length; ++idx) {
|
|
slot_layer_t *mask = layer_masks->buffer[idx];
|
|
array_remove(g_project->_->layers, mask);
|
|
// If the masks are moved down each step increases the index below the layer by one.
|
|
array_insert(g_project->_->layers, delta > 0 ? old_index + delta - 1 : old_index + delta + idx, mask);
|
|
}
|
|
}
|
|
|
|
// Layers can have filters as children
|
|
// These have to be moved, too
|
|
slot_layer_t_array_t *layer_filters = slot_layer_get_filters(raw, false);
|
|
if (layer_filters != NULL) {
|
|
i32 masks_count = layer_masks != NULL ? layer_masks->length : 0;
|
|
for (i32 idx = 0; idx < layer_filters->length; ++idx) {
|
|
slot_layer_t *filter = layer_filters->buffer[idx];
|
|
array_remove(g_project->_->layers, filter);
|
|
array_insert(g_project->_->layers, delta > 0 ? old_index + delta - 1 : old_index + delta + masks_count + idx, filter);
|
|
}
|
|
}
|
|
|
|
// The layer is the last layer in the group, remove it
|
|
// Notice that this might remove group masks
|
|
if (old_parent != NULL && slot_layer_get_children(old_parent) == NULL) {
|
|
slot_layer_delete(old_parent);
|
|
}
|
|
}
|
|
else if (slot_layer_is_mask(raw)) {
|
|
// Precondition new_upper_layer != NULL, ensured in can_move
|
|
if (slot_layer_is_filter(new_upper_layer) || slot_layer_is_mask(new_upper_layer)) {
|
|
raw->parent = new_upper_layer->parent;
|
|
}
|
|
else if (slot_layer_is_layer(new_upper_layer) || slot_layer_is_group(new_upper_layer)) {
|
|
raw->parent = new_upper_layer;
|
|
}
|
|
}
|
|
else if (slot_layer_is_group(raw)) {
|
|
slot_layer_t_array_t *children = slot_layer_get_recursive_children(raw);
|
|
if (children != NULL) {
|
|
for (i32 idx = 0; idx < children->length; ++idx) {
|
|
slot_layer_t *child = children->buffer[idx];
|
|
array_remove(g_project->_->layers, child);
|
|
// If the children are moved down each step increases the index below the layer by one
|
|
array_insert(g_project->_->layers, delta > 0 ? old_index + delta - 1 : old_index + delta + idx, child);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i32 i = 0; i < g_project->_->materials->length; ++i) {
|
|
slot_material_t *m = g_project->_->materials->buffer[i];
|
|
tab_layers_remap_layer_pointers(m->canvas->nodes, tab_layers_fill_layer_map(pointers));
|
|
}
|
|
}
|