paint: merge text layers
This commit is contained in:
@@ -73,6 +73,7 @@ slot_layer_t *layers_new_layer(bool clear, i32 position, slot_layer_
|
||||
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);
|
||||
slot_layer_t *layers_new_text_layer();
|
||||
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);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
char *manifest_title = "ArmorPaint";
|
||||
char *manifest_version = "1.0 rc2";
|
||||
char *manifest_version_project = "13";
|
||||
char *manifest_version_project = "14";
|
||||
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";
|
||||
|
||||
@@ -258,6 +258,7 @@ void export_arm_run_project() {
|
||||
.path_points_parent = l->path_points_parent,
|
||||
.path_tool = l->path_tool,
|
||||
.path_curved = l->path_curved,
|
||||
.path_text = l->path_text,
|
||||
.path_material = l->path_material != NULL ? array_index_of(g_project->_->materials, l->path_material) : -1});
|
||||
any_array_push(ld, d);
|
||||
}
|
||||
|
||||
@@ -585,6 +585,7 @@ void import_arm_run_project(char *path) {
|
||||
l->path_points_parent = ld->path_points_parent;
|
||||
l->path_tool = ld->path_tool;
|
||||
l->path_curved = ld->path_curved;
|
||||
l->path_text = ld->path_text;
|
||||
|
||||
gpu_delete_texture(_texpaint);
|
||||
if (_texpaint_nor != NULL) {
|
||||
|
||||
@@ -210,6 +210,14 @@ project_t *import_arm_from_map_to_arm(any_map_t *old) {
|
||||
ld->paint_emis = armpack_map_get_i32(old, "paint_emis") > 0;
|
||||
ld->paint_subs = armpack_map_get_i32(old, "paint_subs") > 0;
|
||||
ld->uv_map = armpack_map_get_i32(old, "uv_map");
|
||||
ld->path_points = any_map_get(old, "path_points");
|
||||
ld->path_points_world = any_map_get(old, "path_points_world");
|
||||
ld->path_points_camera = any_map_get(old, "path_points_camera");
|
||||
ld->path_points_parent = any_map_get(old, "path_points_parent");
|
||||
ld->path_tool = armpack_map_get_i32(old, "path_tool");
|
||||
ld->path_curved = armpack_map_get_i32(old, "path_curved") > 0;
|
||||
ld->path_material = ld->path_points != NULL ? armpack_map_get_i32(old, "path_material") : -1;
|
||||
ld->path_text = armpack_map_get_i32(old, "path_text") > 0;
|
||||
any_array_push(project->layer_datas, ld);
|
||||
}
|
||||
}
|
||||
@@ -303,9 +311,20 @@ project_t *import_arm_from_map_to_arm(any_map_t *old) {
|
||||
return project;
|
||||
}
|
||||
|
||||
project_t *import_arm_from_version_13(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, "path_text", 0);
|
||||
}
|
||||
}
|
||||
return import_arm_from_map_to_arm(old);
|
||||
}
|
||||
|
||||
project_t *import_arm_from_version_12(any_map_t *old) {
|
||||
any_map_set(old, "mesh_skins", NULL);
|
||||
return import_arm_from_map_to_arm(old);
|
||||
return import_arm_from_version_13(old);
|
||||
}
|
||||
|
||||
project_t *import_arm_from_version_11(any_map_t *old) {
|
||||
@@ -434,7 +453,7 @@ project_t *import_arm_from_old(buffer_t *b) {
|
||||
project_t *(*fns[])(any_map_t *) = {
|
||||
import_arm_from_version_0, import_arm_from_version_1, import_arm_from_version_2, import_arm_from_version_3, import_arm_from_version_4,
|
||||
import_arm_from_version_5, import_arm_from_version_6, import_arm_from_version_7, import_arm_from_version_8, import_arm_from_version_9,
|
||||
import_arm_from_version_10, import_arm_from_version_11, import_arm_from_version_12,
|
||||
import_arm_from_version_10, import_arm_from_version_11, import_arm_from_version_12, import_arm_from_version_13,
|
||||
};
|
||||
for (i32 v = sizeof(fns) / sizeof(fns[0]) - 1; v >= 0; --v) {
|
||||
if (import_arm_is_version(b, i32_to_string(v))) {
|
||||
|
||||
@@ -372,6 +372,7 @@ slot_layer_t *slot_layer_duplicate(slot_layer_t *raw) {
|
||||
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_text = raw->path_text;
|
||||
l->path_material = raw->path_material;
|
||||
}
|
||||
|
||||
@@ -488,6 +489,7 @@ void slot_layer_to_paint_layer(slot_layer_t *raw) {
|
||||
if (raw->path_material != NULL) {
|
||||
raw->path_material = NULL;
|
||||
raw->path_points = NULL;
|
||||
raw->path_text = false;
|
||||
util_layer_update_path();
|
||||
}
|
||||
make_material_parse_paint_material(true);
|
||||
|
||||
@@ -52,6 +52,7 @@ typedef struct slot_layer {
|
||||
i32_array_t *path_points_parent;
|
||||
i32 path_tool;
|
||||
bool path_curved;
|
||||
bool path_text;
|
||||
struct slot_material *path_material;
|
||||
} slot_layer_t;
|
||||
|
||||
@@ -645,6 +646,7 @@ typedef struct layer_data {
|
||||
i32 path_tool;
|
||||
bool path_curved;
|
||||
i32 path_material;
|
||||
bool path_text;
|
||||
} layer_data_t;
|
||||
|
||||
typedef struct timeline_layer_keyframe_data {
|
||||
|
||||
@@ -18,6 +18,12 @@ void tab_layers_button_2d_view() {
|
||||
}
|
||||
}
|
||||
|
||||
void tab_layers_repaint_text_layer(slot_layer_t *l) {
|
||||
util_layer_repaint_path(l);
|
||||
g_context->layer_preview_dirty = true;
|
||||
g_context->layers_preview_dirty = true;
|
||||
}
|
||||
|
||||
void tab_layers_set_drag_layer(slot_layer_t *layer, f32 off_x, f32 off_y) {
|
||||
base_drag_off_x = off_x;
|
||||
base_drag_off_y = off_y;
|
||||
@@ -409,6 +415,9 @@ void tab_layers_draw_layer_slot_full(slot_layer_t *l, i32 i) {
|
||||
tab_layers_layer_name_handle->text = string_copy(l->name);
|
||||
char *new_name = string_copy(ui_text_input(tab_layers_layer_name_handle, "", UI_ALIGN_LEFT, true, false));
|
||||
tab_stages_rename_layer(l->name, new_name);
|
||||
if (l->path_text && slot_layer_is_path(l) && !string_equals(l->name, new_name)) {
|
||||
sys_notify_on_next_frame(&tab_layers_repaint_text_layer, l);
|
||||
}
|
||||
l->name = new_name;
|
||||
if (g_ui->text_selected_handle != tab_layers_layer_name_handle) {
|
||||
tab_layers_layer_name_edit = -1;
|
||||
@@ -1058,6 +1067,10 @@ 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("Text"), "", ICON_FONT)) {
|
||||
layers_new_text_layer();
|
||||
history_new_layer();
|
||||
}
|
||||
if (ui_menu_button(tr("Path"), "", ICON_PATH)) {
|
||||
layers_new_path_layer(false);
|
||||
history_new_layer();
|
||||
|
||||
@@ -366,7 +366,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(36);
|
||||
armpack_encode_map(37);
|
||||
armpack_encode_string("name");
|
||||
armpack_encode_string(raw->layer_datas->buffer[i]->name);
|
||||
armpack_encode_string("res");
|
||||
@@ -439,6 +439,8 @@ buffer_t *util_encode_project(project_t *raw) {
|
||||
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);
|
||||
armpack_encode_string("path_text");
|
||||
armpack_encode_bool(raw->layer_datas->buffer[i]->path_text);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -684,6 +684,17 @@ slot_layer_t *layers_new_path_layer(bool curved) {
|
||||
return l;
|
||||
}
|
||||
|
||||
slot_layer_t *layers_new_text_layer() {
|
||||
slot_layer_t *l = layers_new_path_layer(true);
|
||||
if (l == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
l->path_text = true;
|
||||
l->path_tool = TOOL_TYPE_TEXT;
|
||||
l->name = string("Text %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();
|
||||
|
||||
+174
-12
@@ -275,6 +275,159 @@ static void path_paint_straight(f32_array_t *points, f32_array_t *points_world,
|
||||
render_path_paint_dilate(true, true);
|
||||
}
|
||||
|
||||
static void path_text_stamp(char *letter, f32 px, f32 py, f32 angle, f32 *prev_px, f32 *prev_py) {
|
||||
g_context->text_tool_text = string_copy(letter);
|
||||
util_render_make_text_preview();
|
||||
g_context->brush_angle = angle;
|
||||
g_context->prev_paint_vec_x = px;
|
||||
g_context->prev_paint_vec_y = py;
|
||||
path_paint(px, py, prev_px, prev_py, true);
|
||||
}
|
||||
|
||||
static void path_paint_text(slot_layer_t *l) {
|
||||
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;
|
||||
i32 num_world = points_world->length / 3;
|
||||
i32 num_camera = points_camera->length / 9;
|
||||
i32 num_parent = points_parent->length;
|
||||
|
||||
char *text = l->name;
|
||||
i32 n = 0; // Codepoint count
|
||||
for (i32 i = 0; text[i] != '\0'; i++) {
|
||||
if ((text[i] & 0xc0) != 0x80) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *_text = g_context->text_tool_text;
|
||||
gpu_texture_t *_image = g_context->text_tool_image;
|
||||
g_context->text_tool_image = NULL;
|
||||
|
||||
f32 prev_px = 0.5f;
|
||||
f32 prev_py = 0.5f;
|
||||
|
||||
if (num_world < 3) {
|
||||
// Single anchor
|
||||
path_set_camera(points_camera, num_camera, 0);
|
||||
f32 px = points->length >= 2 ? points->buffer[0] : 0.5f;
|
||||
f32 py = points->length >= 2 ? points->buffer[1] : 0.5f;
|
||||
path_text_stamp(text, px, py, 0.0f, &prev_px, &prev_py);
|
||||
}
|
||||
else {
|
||||
// Sample the bezier segments
|
||||
i32 num_segments = (num_world - 1) / 2;
|
||||
i32 num_samples = num_segments * 32 + 1;
|
||||
|
||||
f32_array_t *spos = f32_array_create(num_samples * 3);
|
||||
f32_array_t *slen = f32_array_create(num_samples);
|
||||
i32_array_t *scam = i32_array_create(num_samples);
|
||||
|
||||
i32 si = 0;
|
||||
for (i32 j = 2; j < num_world; j += 2) {
|
||||
i32 p = (j < num_parent) ? points_parent->buffer[j] : j - 2;
|
||||
f32 wx0 = points_world->buffer[p * 3];
|
||||
f32 wy0 = points_world->buffer[p * 3 + 1];
|
||||
f32 wz0 = points_world->buffer[p * 3 + 2];
|
||||
f32 wcx = points_world->buffer[(j - 1) * 3];
|
||||
f32 wcy = points_world->buffer[(j - 1) * 3 + 1];
|
||||
f32 wcz = points_world->buffer[(j - 1) * 3 + 2];
|
||||
f32 wx1 = points_world->buffer[j * 3];
|
||||
f32 wy1 = points_world->buffer[j * 3 + 1];
|
||||
f32 wz1 = points_world->buffer[j * 3 + 2];
|
||||
|
||||
if (si == 0) {
|
||||
spos->buffer[0] = wx0;
|
||||
spos->buffer[1] = wy0;
|
||||
spos->buffer[2] = wz0;
|
||||
slen->buffer[0] = 0.0f;
|
||||
scam->buffer[0] = p;
|
||||
si = 1;
|
||||
}
|
||||
for (i32 k = 1; k <= 32; k++) {
|
||||
f32 kt = k / 32.0f;
|
||||
f32 bwx = bezier_eval(wx0, wcx, wx1, kt);
|
||||
f32 bwy = bezier_eval(wy0, wcy, wy1, kt);
|
||||
f32 bwz = bezier_eval(wz0, wcz, wz1, kt);
|
||||
f32 dx = bwx - spos->buffer[(si - 1) * 3];
|
||||
f32 dy = bwy - spos->buffer[(si - 1) * 3 + 1];
|
||||
f32 dz = bwz - spos->buffer[(si - 1) * 3 + 2];
|
||||
spos->buffer[si * 3] = bwx;
|
||||
spos->buffer[si * 3 + 1] = bwy;
|
||||
spos->buffer[si * 3 + 2] = bwz;
|
||||
slen->buffer[si] = slen->buffer[si - 1] + sqrtf(dx * dx + dy * dy + dz * dz);
|
||||
scam->buffer[si] = (k == 32 && j + 2 >= num_world) ? j : p;
|
||||
si++;
|
||||
}
|
||||
}
|
||||
|
||||
f32 total = slen->buffer[num_samples - 1];
|
||||
f32 aspect = sys_w() / (f32)sys_h();
|
||||
i32 cur_cam = -1;
|
||||
i32 byte_i = 0;
|
||||
for (i32 i = 0; i < n; i++) {
|
||||
// Extract letter
|
||||
i32 start = byte_i;
|
||||
byte_i++;
|
||||
while (text[byte_i] != '\0' && (text[byte_i] & 0xc0) == 0x80) {
|
||||
byte_i++;
|
||||
}
|
||||
char letter[8];
|
||||
i32 len_b = byte_i - start > 7 ? 7 : byte_i - start;
|
||||
memcpy(letter, text + start, len_b);
|
||||
letter[len_b] = '\0';
|
||||
if (letter[0] == ' ' || letter[0] == '\t') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Evenly spaced along the arc
|
||||
f32 target = n == 1 ? 0.0f : total * i / (f32)(n - 1);
|
||||
i32 k = 1;
|
||||
while (k < num_samples - 1 && slen->buffer[k] < target) {
|
||||
k++;
|
||||
}
|
||||
f32 d = slen->buffer[k] - slen->buffer[k - 1];
|
||||
f32 a = d > 0.0f ? (target - slen->buffer[k - 1]) / d : 1.0f;
|
||||
f32 wx = spos->buffer[(k - 1) * 3] + a * (spos->buffer[k * 3] - spos->buffer[(k - 1) * 3]);
|
||||
f32 wy = spos->buffer[(k - 1) * 3 + 1] + a * (spos->buffer[k * 3 + 1] - spos->buffer[(k - 1) * 3 + 1]);
|
||||
f32 wz = spos->buffer[(k - 1) * 3 + 2] + a * (spos->buffer[k * 3 + 2] - spos->buffer[(k - 1) * 3 + 2]);
|
||||
|
||||
i32 cam = scam->buffer[k];
|
||||
if (cam != cur_cam) {
|
||||
path_set_camera(points_camera, num_camera, cam);
|
||||
cur_cam = cam;
|
||||
}
|
||||
|
||||
f32 px, py;
|
||||
if (!project_to_screen((vec4_t){wx, wy, wz, 1.0f}, &px, &py)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Rotate the letter
|
||||
f32 ax, ay, bx, by;
|
||||
f32 angle = 0.0f;
|
||||
if (project_to_screen((vec4_t){spos->buffer[(k - 1) * 3], spos->buffer[(k - 1) * 3 + 1], spos->buffer[(k - 1) * 3 + 2], 1.0f}, &ax, &ay) &&
|
||||
project_to_screen((vec4_t){spos->buffer[k * 3], spos->buffer[k * 3 + 1], spos->buffer[k * 3 + 2], 1.0f}, &bx, &by)) {
|
||||
angle = -atan2f(-(by - ay), (bx - ax) * aspect) * (180.0f / math_pi());
|
||||
}
|
||||
|
||||
path_text_stamp(letter, px, py, angle, &prev_px, &prev_py);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_context->text_tool_image != NULL) {
|
||||
gpu_delete_texture(g_context->text_tool_image);
|
||||
}
|
||||
g_context->text_tool_text = _text;
|
||||
g_context->text_tool_image = _image;
|
||||
|
||||
render_path_paint_dilate(true, true);
|
||||
}
|
||||
|
||||
static void path_repaint(slot_layer_t *l) {
|
||||
if (l->path_material == NULL) {
|
||||
return;
|
||||
@@ -284,15 +437,20 @@ static void path_repaint(slot_layer_t *l) {
|
||||
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;
|
||||
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;
|
||||
f32 _brush_angle = g_context->brush_angle;
|
||||
g_context->layer = l;
|
||||
g_context->material = l->path_material;
|
||||
g_context->tool = l->path_tool;
|
||||
if (l->path_text) {
|
||||
// Add rotation uniform to paint shader
|
||||
g_context->brush_angle = 0.001f;
|
||||
}
|
||||
|
||||
make_material_save_paint_material();
|
||||
make_material_parse_paint_material(false);
|
||||
@@ -320,7 +478,10 @@ static void path_repaint(slot_layer_t *l) {
|
||||
f32 dot_spacing = sphere_mode ? g_context->brush_lazy_radius * g_context->brush_lazy_step * r_world * 3.0 : 0.0f;
|
||||
|
||||
if (num_world >= 1) {
|
||||
if (l->path_curved) {
|
||||
if (l->path_text) {
|
||||
path_paint_text(l);
|
||||
}
|
||||
else if (l->path_curved) {
|
||||
path_paint_curved(points, points_world, points_camera, points_parent, num_world, num_camera, num_parent, sphere_mode, dot_spacing);
|
||||
}
|
||||
else {
|
||||
@@ -343,6 +504,7 @@ static void path_repaint(slot_layer_t *l) {
|
||||
g_context->last_paint_vec_x = _last_x;
|
||||
g_context->last_paint_vec_y = _last_y;
|
||||
g_context->paint_vec = _paint_vec;
|
||||
g_context->brush_angle = _brush_angle;
|
||||
make_material_restore_paint_material();
|
||||
}
|
||||
|
||||
@@ -354,7 +516,7 @@ void util_layer_clear_path_points(slot_layer_t *l) {
|
||||
l->path_points_world->length = 0;
|
||||
l->path_points_camera->length = 0;
|
||||
l->path_points_parent->length = 0;
|
||||
l->path_tool = -1;
|
||||
l->path_tool = l->path_text ? TOOL_TYPE_TEXT : -1;
|
||||
path_layer_last_active = -1;
|
||||
}
|
||||
|
||||
@@ -463,7 +625,7 @@ 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) {
|
||||
if (!l->path_text && (i32)g_context->tool != l->path_tool) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user