diff --git a/src/filters/wrap.c b/src/filters/wrap.c index 187766c0a..79dd13256 100644 --- a/src/filters/wrap.c +++ b/src/filters/wrap.c @@ -141,7 +141,7 @@ static int gui(filter_t *filter) bool should_wrap; layer_t *layer; - memcpy(box, goxel.selection, sizeof(box)); + memcpy(box, goxel.image->selection_box, sizeof(box)); if (box_is_null(box)) memcpy(box, goxel.image->active_layer->box, sizeof(box)); diff --git a/src/goxel.c b/src/goxel.c index 598270c3e..0c288b9a9 100644 --- a/src/goxel.c +++ b/src/goxel.c @@ -322,11 +322,11 @@ int goxel_unproject(const float viewport[4], } if ((1 << i) == SNAP_SELECTION_IN) r = goxel_unproject_on_box(viewport, pos, - goxel.selection, true, + goxel.image->selection_box, true, p, n, NULL); if ((1 << i) == SNAP_SELECTION_OUT) r = goxel_unproject_on_box(viewport, pos, - goxel.selection, false, + goxel.image->selection_box, false, p, n, NULL); if ((1 << i) == SNAP_SHAPE_BOX) { r = goxel_unproject_on_box(viewport, pos, snap_shape, false, @@ -1059,10 +1059,13 @@ void goxel_render_view(const float viewport[4], bool render_mode) render_img(rend, layer->image, layer->mat, EFFECT_NO_SHADING); } - render_box(rend, goxel.selection, NULL, EFFECT_STRIP | EFFECT_WIREFRAME); + render_box(rend, goxel.image->selection_box, NULL, + EFFECT_STRIP | EFFECT_WIREFRAME); - if (goxel.tool->flags & TOOL_SHOW_MASK) - render_volume(rend, goxel.mask, NULL, EFFECT_GRID_ONLY); + if (goxel.tool->flags & TOOL_SHOW_MASK) { + render_volume(rend, goxel.image->selection_mask, NULL, + EFFECT_GRID_ONLY); + } // Debug: show the current layer volume tiles. if ((0)) { @@ -1424,19 +1427,20 @@ void goxel_apply_color_filter( int p[3]; uint8_t color[4]; painter_t painter; + image_t *img = goxel.image; /* Compute the volume where we want to apply the filter. Mask, rect * selection, or the whole layer. */ volume = volume_copy(layer->volume); - if (!volume_is_empty(goxel.mask)) { - volume_merge(volume, goxel.mask, MODE_INTERSECT, NULL); - } else if (!box_is_null(goxel.selection)) { + if (!volume_is_empty(img->selection_mask)) { + volume_merge(volume, img->selection_mask, MODE_INTERSECT, NULL); + } else if (!box_is_null(img->selection_box)) { painter = (painter_t) { .shape = &shape_cube, .mode = MODE_INTERSECT, .color = {255, 255, 255, 255}, }; - volume_op(volume, &painter, goxel.selection); + volume_op(volume, &painter, img->selection_box); } iter = volume_get_iterator(volume, VOLUME_ITER_VOXELS | VOLUME_ITER_SKIP_EMPTY); @@ -1455,17 +1459,17 @@ static void a_cut_as_new_layer(void) { layer_t *new_layer; painter_t painter; - image_t *img = goxel.image; layer_t *layer = img->active_layer; - const float (*box)[4][4] = &goxel.selection; + const float (*box)[4][4] = &img->selection_box; new_layer = image_duplicate_layer(img, layer); // Use the mask in priority. - if (!volume_is_empty(goxel.mask)) { - volume_merge(new_layer->volume, goxel.mask, MODE_INTERSECT, NULL); - volume_merge(layer->volume, goxel.mask , MODE_SUB, NULL); + if (!volume_is_empty(img->selection_mask)) { + volume_merge(new_layer->volume, img->selection_mask, + MODE_INTERSECT, NULL); + volume_merge(layer->volume, img->selection_mask , MODE_SUB, NULL); return; } @@ -1487,11 +1491,12 @@ ACTION_REGISTER(cut_as_new_layer, static void a_reset_selection(void) { - if (!volume_is_empty(goxel.mask)) { - volume_delete(goxel.mask); - goxel.mask = NULL; + image_t *img = goxel.image; + if (!volume_is_empty(img->selection_mask)) { + volume_delete(img->selection_mask); + img->selection_mask = NULL; } - mat4_copy(mat4_zero, goxel.selection); + mat4_copy(mat4_zero, img->selection_box); } ACTION_REGISTER(reset_selection, @@ -1500,9 +1505,10 @@ ACTION_REGISTER(reset_selection, static void a_fill_selection_box(void) { - layer_t *layer = goxel.image->active_layer; - if (box_is_null(goxel.selection)) return; - volume_op(layer->volume, &goxel.painter, goxel.selection); + image_t *img = goxel.image; + layer_t *layer = img->active_layer; + if (box_is_null(img->selection_box)) return; + volume_op(layer->volume, &goxel.painter, img->selection_box); } ACTION_REGISTER(fill_selection_box, @@ -1513,9 +1519,11 @@ ACTION_REGISTER(fill_selection_box, static void a_paint_selection(void) { - layer_t *layer = goxel.image->active_layer; - if (volume_is_empty(goxel.mask)) return; - volume_merge(layer->volume, goxel.mask, MODE_OVER, goxel.painter.color); + image_t *img = goxel.image; + layer_t *layer = img->active_layer; + if (volume_is_empty(img->selection_mask)) return; + volume_merge(layer->volume, img->selection_mask, MODE_OVER, + goxel.painter.color); } ACTION_REGISTER(paint_selection, @@ -1528,17 +1536,18 @@ static void a_add_selection(void) { volume_t *tmp; painter_t painter; + image_t *img = goxel.image; - if (box_is_null(goxel.selection)) return; + if (box_is_null(img->selection_box)) return; painter = (painter_t) { .shape = &shape_cube, .mode = MODE_INTERSECT_FILL, .color = {255, 255, 255, 255}, }; tmp = volume_copy(goxel.image->active_layer->volume); - volume_op(tmp, &painter, goxel.selection); - if (goxel.mask == NULL) goxel.mask = volume_new(); - volume_merge(goxel.mask, tmp, MODE_OVER, painter.color); + volume_op(tmp, &painter, img->selection_box); + if (img->selection_mask == NULL) img->selection_mask = volume_new(); + volume_merge(img->selection_mask, tmp, MODE_OVER, painter.color); volume_delete(tmp); } @@ -1550,13 +1559,16 @@ ACTION_REGISTER(add_selection, static void a_sub_selection(void) { painter_t painter; - if (goxel.mask == NULL || box_is_null(goxel.selection)) return; + image_t *img = goxel.image; + + if (img->selection_mask == NULL || box_is_null(img->selection_box)) + return; painter = (painter_t) { .shape = &shape_cube, .mode = MODE_SUB, .color = {255, 255, 255, 255}, }; - volume_op(goxel.mask, &painter, goxel.selection); + volume_op(img->selection_mask, &painter, img->selection_box); } ACTION_REGISTER(sub_selection, @@ -1567,22 +1579,25 @@ ACTION_REGISTER(sub_selection, static void copy_action(void) { painter_t painter; + image_t *img = goxel.image; + volume_delete(goxel.clipboard.volume); - mat4_copy(goxel.selection, goxel.clipboard.box); + mat4_copy(img->selection_box, goxel.clipboard.box); goxel.clipboard.volume = volume_copy(goxel.image->active_layer->volume); - if (!box_is_null(goxel.selection)) { + if (!box_is_null(img->selection_box)) { painter = (painter_t) { .shape = &shape_cube, .mode = MODE_INTERSECT, .color = {255, 255, 255, 255}, }; - volume_op(goxel.clipboard.volume, &painter, goxel.selection); + volume_op(goxel.clipboard.volume, &painter, img->selection_box); } } static void paste_action(void) { - volume_t *volume = goxel.image->active_layer->volume; + image_t *img = goxel.image; + volume_t *volume = img->active_layer->volume; volume_t *tmp; float p1[3], p2[3], mat[4][4]; @@ -1590,9 +1605,9 @@ static void paste_action(void) if (!goxel.clipboard.volume) return; tmp = volume_copy(goxel.clipboard.volume); - if ( !box_is_null(goxel.selection) && + if ( !box_is_null(img->selection_box) && !box_is_null(goxel.clipboard.box)) { - vec3_copy(goxel.selection[3], p1); + vec3_copy(img->selection_box[3], p1); vec3_copy(goxel.clipboard.box[3], p2); mat4_itranslate(mat, +p1[0], +p1[1], +p1[2]); mat4_itranslate(mat, -p2[0], -p2[1], -p2[2]); diff --git a/src/goxel.h b/src/goxel.h index 94a03dc53..020042dfd 100644 --- a/src/goxel.h +++ b/src/goxel.h @@ -497,9 +497,6 @@ typedef struct goxel float tool_radius; bool pathtrace; // Render pathtraced mode. - float selection[4][4]; // The selection box. - volume_t *mask; // Global selection mask volume. - struct { float rotation[4]; float pos[2]; diff --git a/src/gui/snap_panel.c b/src/gui/snap_panel.c index 00891cc66..2ea7e791b 100644 --- a/src/gui/snap_panel.c +++ b/src/gui/snap_panel.c @@ -36,7 +36,7 @@ void gui_snap_panel(void) snap_button(_(VOLUME), SNAP_VOLUME); snap_button(_(PLANE), SNAP_PLANE); gui_row_end(); - if (!box_is_null(goxel.selection)) { + if (!box_is_null(goxel.image->selection_box)) { snap_button(_(SELECTION_IN), SNAP_SELECTION_IN); snap_button(_(SELECTION_OUT), SNAP_SELECTION_OUT); } diff --git a/src/image.c b/src/image.c index 42dd178b6..0136a6b68 100644 --- a/src/image.c +++ b/src/image.c @@ -188,7 +188,6 @@ static void image_restore(image_t *img, const image_t *snap) DL_DELETE(img->materials, material); material_delete(material); } - mat4_copy(snap->box, img->box); // Set copy from the other. img->layers = NULL; @@ -222,6 +221,15 @@ static void image_restore(image_t *img, const image_t *snap) layer->material = material; } } + + // Copy other attributes. + mat4_copy(snap->box, img->box); + mat4_copy(snap->selection_box, img->selection_box); + + volume_delete(img->selection_mask); + img->selection_mask = NULL; + if (snap->selection_mask) + img->selection_mask = volume_copy(snap->selection_mask); } /* @@ -235,7 +243,7 @@ static image_t *image_snap(const image_t *other) material_t *material, *other_material; img = calloc(1, sizeof(*img)); - *img = *other; + *img = *other; // XXX: better to copy manually I think! img->layers = NULL; img->active_layer = NULL; @@ -269,6 +277,10 @@ static image_t *image_snap(const image_t *other) } } + img->selection_mask = NULL; + if (other->selection_mask) + img->selection_mask = volume_copy(other->selection_mask); + img->history = img->history_next = img->history_prev = NULL; img->history_pos = NULL; return img; @@ -340,8 +352,8 @@ layer_t *image_add_shape_layer(image_t *img) layer->shape = &shape_sphere; vec4_copy(goxel.painter.color, layer->color); // If the selection is on use it, otherwise center it in the image. - if (!box_is_null(goxel.selection)) { - mat4_copy(goxel.selection, layer->mat); + if (!box_is_null(img->selection_box)) { + mat4_copy(img->selection_box, layer->mat); } else { vec3_copy(img->box[3], layer->mat[3]); mat4_iscale(layer->mat, 4, 4, 4); @@ -683,15 +695,17 @@ void image_redo(image_t *img) static void image_clear_layer(void) { painter_t painter; - layer_t *layer = goxel.image->active_layer; - if (box_is_null(goxel.selection) && volume_is_empty(goxel.mask)) { + image_t *img = goxel.image; + layer_t *layer = img->active_layer; + if ( box_is_null(img->selection_box) && + volume_is_empty(img->selection_mask)) { volume_clear(layer->volume); return; } // Use the mask in priority if it exists. - if (!volume_is_empty(goxel.mask)) { - volume_merge(layer->volume, goxel.mask, MODE_SUB, NULL); + if (!volume_is_empty(img->selection_mask)) { + volume_merge(layer->volume, img->selection_mask, MODE_SUB, NULL); return; } @@ -700,7 +714,7 @@ static void image_clear_layer(void) .mode = MODE_SUB, .color = {255, 255, 255, 255}, }; - volume_op(layer->volume, &painter, goxel.selection); + volume_op(layer->volume, &painter, img->selection_box); } bool image_layer_can_edit(const image_t *img, const layer_t *layer) @@ -731,6 +745,9 @@ uint32_t image_get_key(const image_t *img) k = material_get_hash(material); key = XXH32(&k, sizeof(k), key); } + key = XXH32(img->selection_box, sizeof(img->selection_box), key); + k = volume_get_key(img->selection_mask); + key = XXH32(&k, sizeof(k), key); return key; } diff --git a/src/image.h b/src/image.h index 0043a6815..71ac6b2e3 100644 --- a/src/image.h +++ b/src/image.h @@ -42,6 +42,9 @@ struct image { float box[4][4]; + float selection_box[4][4]; + volume_t *selection_mask; + // For saving. // XXX: I think those should be persistend data of export code instead. char *path; // Path to save the gox file. diff --git a/src/script.c b/src/script.c index 8e79437ec..9a74897af 100644 --- a/src/script.c +++ b/src/script.c @@ -494,6 +494,7 @@ static klass_t image_klass = { {"addLayer", .fn=js_image_addLayer}, {"activeLayer", .klass=&layer_klass, MEMBER(image_t, active_layer)}, {"getLayersVolume", .fn=js_image_getLayersVolume}, + {"selectionBox", .klass=&box_klass, MEMBER(image_t, selection_box)}, {} } }; @@ -625,7 +626,6 @@ static klass_t goxel_klass = { .def.class_name = "Goxel", .attributes = { {"image", .klass=&image_klass, MEMBER(goxel_t, image)}, - {"selection", .klass=&box_klass, MEMBER(goxel_t, selection)}, {"registerFormat", .fn=js_goxel_registerFormat}, {"registerScript", .fn=js_goxel_registerScript}, {} diff --git a/src/tools/fuzzy_select.c b/src/tools/fuzzy_select.c index 0f634d943..a3a0ad262 100644 --- a/src/tools/fuzzy_select.c +++ b/src/tools/fuzzy_select.c @@ -43,10 +43,11 @@ static int select_cond(void *user, const volume_t *volume, static int on_click(gesture3d_t *gest) { - volume_t *volume = goxel.image->active_layer->volume; + tool_fuzzy_select_t *tool = gest->user; + image_t *img = goxel.image; + volume_t *volume = img->active_layer->volume; volume_t *sel; int pi[3]; - tool_fuzzy_select_t *tool = gest->user; int mode = tool->mode; if (gest->flags & GESTURE3D_FLAG_SHIFT) @@ -59,8 +60,8 @@ static int on_click(gesture3d_t *gest) pi[2] = floor(gest->pos[2]); sel = volume_new(); volume_select(volume, pi, select_cond, tool, sel); - if (goxel.mask == NULL) goxel.mask = volume_new(); - volume_merge(goxel.mask, sel, mode, NULL); + if (img->selection_mask == NULL) img->selection_mask = volume_new(); + volume_merge(img->selection_mask, sel, mode, NULL); volume_delete(sel); return 0; } @@ -100,6 +101,7 @@ static int gui(tool_t *tool_) { tool_fuzzy_select_t *tool = (void*)tool_; bool use_color = tool->threshold < 255; + image_t *img = goxel.image; tool_gui_mask_mode(&tool->mode); @@ -110,23 +112,24 @@ static int gui(tool_t *tool_) gui_input_int(_(THRESHOLD), &tool->threshold, 1, 254); } - if (volume_is_empty(goxel.mask)) + if (volume_is_empty(img->selection_mask)) return 0; - volume_t *volume = goxel.image->active_layer->volume; + volume_t *volume = img->active_layer->volume; gui_group_begin(NULL); + // XXX: should use actions here? if (gui_button(_(CLEAR), 1, 0)) { - volume_merge(volume, goxel.mask, MODE_SUB, NULL); + volume_merge(volume, img->selection_mask, MODE_SUB, NULL); image_history_push(goxel.image); } if (gui_button(_(FILL), 1, 0)) { - volume_merge(volume, goxel.mask, MODE_OVER, goxel.painter.color); + volume_merge(volume, img->selection_mask, MODE_OVER, + goxel.painter.color); image_history_push(goxel.image); } if (gui_button(_(CUT_TO_NEW_LAYER), 1, 0)) { - cut_as_new_layer(goxel.image, goxel.image->active_layer, - goxel.mask); + cut_as_new_layer(img, img->active_layer, img->selection_mask); image_history_push(goxel.image); } gui_group_end(); diff --git a/src/tools/move.c b/src/tools/move.c index f881d77ae..7001e2cab 100644 --- a/src/tools/move.c +++ b/src/tools/move.c @@ -101,12 +101,14 @@ static int iter_selection(tool_move_t *tool, const painter_t *painter, float transf[4][4]; float transf_tot[4][4]; int box_edit_state; - layer_t *layer = goxel.image->active_layer; + image_t *img = goxel.image; + layer_t *layer = img->active_layer; + volume_t *mask = img->selection_mask; volume_t *tmp; - if (tool->start_mask_key != volume_get_key(goxel.mask)) { - volume_get_box(goxel.mask, true, tool->box); - tool->start_mask_key = volume_get_key(goxel.mask); + if (tool->start_mask_key != volume_get_key(mask)) { + volume_get_box(mask, true, tool->box); + tool->start_mask_key = volume_get_key(mask); } box_edit_state = box_edit(tool->box, DRAG_MOVE, transf); @@ -116,9 +118,9 @@ static int iter_selection(tool_move_t *tool, const painter_t *painter, assert(!tool->start_volume && !tool->start_selection); mat4_copy(tool->box, tool->start_box); tool->start_volume = volume_copy(layer->volume); - volume_merge(tool->start_volume, goxel.mask, MODE_SUB, NULL); + volume_merge(tool->start_volume, mask, MODE_SUB, NULL); tool->start_selection = volume_copy(layer->volume); - volume_merge(tool->start_selection, goxel.mask, MODE_INTERSECT, NULL); + volume_merge(tool->start_selection, mask, MODE_INTERSECT, NULL); } mat4_mul(transf, tool->box, tool->box); @@ -135,7 +137,7 @@ static int iter_selection(tool_move_t *tool, const painter_t *painter, tool->start_volume = NULL; tool->start_selection = NULL; tool->box[3][3] = 0; - volume_move(goxel.mask, transf_tot); + volume_move(mask, transf_tot); image_history_push(goxel.image); } @@ -152,10 +154,10 @@ static int iter(tool_t *tool_, const painter_t *painter, float box[4][4]; int drag_mode = DRAG_MOVE; int box_edit_state; + image_t *img = goxel.image; + layer_t *layer = img->active_layer; - layer_t *layer = goxel.image->active_layer; - - if (goxel.mask && !volume_is_empty(goxel.mask)) { + if (img->selection_mask && !volume_is_empty(img->selection_mask)) { return iter_selection(tool, painter, viewport); } diff --git a/src/tools/rect_select.c b/src/tools/rect_select.c index 8dbac6535..8c244ddd4 100644 --- a/src/tools/rect_select.c +++ b/src/tools/rect_select.c @@ -19,9 +19,10 @@ static void apply(const float rect_[4], int mode) uint8_t value[4]; uint8_t color[4] = {255, 255, 255, 255}; float view_proj_mat[4][4]; - volume_t *volume = goxel.image->active_layer->volume; + image_t *img = goxel.image; + volume_t *volume = img->active_layer->volume; volume_iterator_t iter; - const camera_t *cam = goxel.image->active_camera; + const camera_t *cam = img->active_camera; rect[0] = min(rect_[0], rect_[2]); rect[1] = min(rect_[1], rect_[3]); @@ -29,12 +30,12 @@ static void apply(const float rect_[4], int mode) rect[3] = max(rect_[1], rect_[3]); mat4_mul(cam->proj_mat, cam->view_mat, view_proj_mat); - if (goxel.mask == NULL) goxel.mask = volume_new(); + if (img->selection_mask == NULL) img->selection_mask = volume_new(); if (mode == MODE_SUB) memset(color, 0, sizeof(color)); if (mode == MODE_REPLACE) - volume_clear(goxel.mask); + volume_clear(img->selection_mask); // XXX: very slow implementation! @@ -49,7 +50,7 @@ static void apply(const float rect_[4], int mode) if ( p[0] < rect[0] || p[0] > rect[2] || p[1] < rect[1] || p[1] > rect[3]) continue; - volume_set_at(goxel.mask, NULL, vp, color); + volume_set_at(img->selection_mask, NULL, vp, color); } } diff --git a/src/tools/selection.c b/src/tools/selection.c index cd144a1a8..0a6c1b495 100644 --- a/src/tools/selection.c +++ b/src/tools/selection.c @@ -42,8 +42,9 @@ static void update_mask(tool_selection_t *tool) { volume_t *tmp; painter_t painter; + image_t *img = goxel.image; - if (box_is_null(goxel.selection)) return; + if (box_is_null(img->selection_box)) return; // Compute interesection of volume and selection rect. painter = (painter_t) { .shape = &shape_cube, @@ -51,13 +52,14 @@ static void update_mask(tool_selection_t *tool) .color = {255, 255, 255, 255}, }; tmp = volume_copy(goxel.image->active_layer->volume); - volume_op(tmp, &painter, goxel.selection); + volume_op(tmp, &painter, img->selection_box); // Apply the new volume. - if (goxel.mask == NULL) goxel.mask = volume_new(); - volume_set(goxel.mask, tool->start_mask); - volume_merge(goxel.mask, tmp, tool->current_mode, painter.color); + if (img->selection_mask == NULL) img->selection_mask = volume_new(); + volume_set(img->selection_mask, tool->start_mask); + volume_merge(img->selection_mask, tmp, tool->current_mode, painter.color); volume_delete(tmp); + image_history_push(goxel.image); } static int on_hover(gesture3d_t *gest) @@ -86,6 +88,7 @@ static int on_drag(gesture3d_t *gest) float rect[4][4]; float p[3]; int dir; + image_t *img = goxel.image; goxel_set_help_text("Drag."); @@ -94,8 +97,8 @@ static int on_drag(gesture3d_t *gest) get_rect(gest->start_pos, gest->start_normal, tool->start_rect); if (tool->start_mask == NULL) tool->start_mask = volume_new(); - if (goxel.mask == NULL) goxel.mask = volume_new(); - volume_set(tool->start_mask, goxel.mask); + if (img->selection_mask == NULL) img->selection_mask = volume_new(); + volume_set(tool->start_mask, img->selection_mask); tool->current_mode = tool->mode; if (gest->flags & GESTURE3D_FLAG_SHIFT) @@ -104,12 +107,13 @@ static int on_drag(gesture3d_t *gest) tool->current_mode = MODE_SUB; } - box_union(tool->start_rect, rect, goxel.selection); + box_union(tool->start_rect, rect, img->selection_box); // If the selection is flat, we grow it one voxel. - if (box_get_volume(goxel.selection) == 0) { + if (box_get_volume(img->selection_box) == 0) { dir = gest->snaped == SNAP_VOLUME ? -1 : 1; vec3_addk(gest->pos, gest->normal, dir, p); - bbox_extends_from_points(goxel.selection, 1, &p, goxel.selection); + bbox_extends_from_points(img->selection_box, 1, &p, + img->selection_box); } if (gest->state == GESTURE3D_STATE_END) { @@ -133,14 +137,15 @@ static int iter(tool_t *tool, const painter_t *painter, tool_selection_t *selection = (tool_selection_t*)tool; int snap_mask = goxel.snap_mask; int box_edit_state; + image_t *img = goxel.image; // To cleanup. snap_mask |= SNAP_ROUNDED; snap_mask &= ~(SNAP_SELECTION_IN | SNAP_SELECTION_OUT); - box_edit_state = box_edit(goxel.selection, 1, transf); + box_edit_state = box_edit(img->selection_box, 1, transf); if (box_edit_state) { - mat4_mul(transf, goxel.selection, goxel.selection); + mat4_mul(transf, img->selection_box, img->selection_box); if (box_edit_state == GESTURE3D_STATE_END) { update_mask(selection); } @@ -186,7 +191,7 @@ static int gui(tool_t *tool_) tool_selection_t *tool = (void*)tool_; float x_mag, y_mag, z_mag; int x, y, z, w, h, d; - float (*box)[4][4] = &goxel.selection; + float (*box)[4][4] = &goxel.image->selection_box; bool deactivated = false; tool_gui_mask_mode(&tool->mode);