sculpt: export fixes

This commit is contained in:
luboslenco
2026-06-13 21:27:16 +02:00
parent 5807ad3360
commit 1f825fdba4
4 changed files with 131 additions and 26 deletions
+4 -1
View File
@@ -10,7 +10,10 @@ void export_mesh_run(char *path, mesh_object_t_array_t *paint_objects, bool appl
paint_objects = g_project->_->paint_objects;
}
if (g_context->export_mesh_format == MESH_FORMAT_OBJ) {
if (merge_vertices) {
if (g_config->workflow == WORKFLOW_SCULPT) {
export_obj_run_sculpt(path, paint_objects);
}
else if (merge_vertices) {
export_obj_run(path, paint_objects, apply_disp);
}
else {
+125 -23
View File
@@ -275,15 +275,63 @@ void export_obj_run_fast(char *path, mesh_object_t_array_t *paint_objects) {
iron_file_save_bytes(path, o, 0);
}
void export_obj_run_sculpt(char *path, mesh_object_t_array_t *paint_objects) {
slot_layer_t *l = NULL;
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
if (g_project->_->layers->buffer[i]->texpaint_sculpt != NULL) {
l = g_project->_->layers->buffer[i];
break;
static bool export_obj_sculpt_layer_mask(slot_layer_t *l, f32_array_t *pmask, i32 len, i16_array_t *texa, u32_array_t *inda, f32 inv) {
slot_layer_t_array_t *masks = slot_layer_get_masks(l, true);
if (masks == NULL) {
return false;
}
#ifdef IRON_BGRA
i32 r_off = 2;
#else
i32 r_off = 0;
#endif
bool any = false;
for (i32 mi = 0; mi < masks->length; ++mi) {
slot_layer_t *m = masks->buffer[mi];
if (!slot_layer_is_visible(m) || m->texpaint == NULL) {
continue;
}
if (!any) {
for (i32 i = 0; i < len; ++i) {
pmask->buffer[i] = 1.0;
}
any = true;
}
buffer_t *mp = gpu_get_texture_pixels(m->texpaint);
i32 mw = m->texpaint->width;
i32 mh = m->texpaint->height;
f32 opac = slot_layer_get_opacity(m);
for (i32 i = 0; i < len; ++i) {
i32 vid = inda->buffer[i];
f32 u = texa->buffer[vid * 2] * inv;
f32 v = texa->buffer[vid * 2 + 1] * inv;
i32 x = math_floor(u * mw);
i32 y = math_floor(v * mh);
x = x < 0 ? 0 : (x >= mw ? mw - 1 : x);
y = y < 0 ? 0 : (y >= mh ? mh - 1 : y);
f32 r = buffer_get_u8(mp, (y * mw + x) * 4 + r_off) / 255.0;
pmask->buffer[i] *= (1.0 - opac) + r * opac;
}
}
if (l == NULL) {
if (any) {
for (i32 i = 0; i < len; ++i) {
f32 c = pmask->buffer[i];
pmask->buffer[i] = c < 0.0 ? 0.0 : (c > 1.0 ? 1.0 : c);
}
}
return any;
}
void export_obj_run_sculpt(char *path, mesh_object_t_array_t *paint_objects) {
slot_layer_t_array_t *sculpt_layers = any_array_create_from_raw((void *[]){}, 0);
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
slot_layer_t *l = g_project->_->layers->buffer[i];
if (l->texpaint_sculpt != NULL && slot_layer_is_visible(l)) {
any_array_push(sculpt_layers, l);
}
}
i32 count = sculpt_layers->length;
if (count == 0) {
return;
}
@@ -294,18 +342,71 @@ void export_obj_run_sculpt(char *path, mesh_object_t_array_t *paint_objects) {
mesh_data_t *mesh = p->data;
f32 sc = mesh->scale_pos;
i16_array_t *texa = mesh->vertex_arrays->buffer[2]->values;
i32 len = math_floor(mesh->index_array->length);
u32_array_t *inda = mesh->index_array;
i32 len = math_floor(inda->length);
i32 tris = math_floor(len / 3.0);
f32 inv = 1.0 / 32767.0;
buffer_t *pixels = gpu_get_texture_pixels(l->texpaint_sculpt);
// The base render target holds the rest-pose positions
buffer_t *base_pixels = NULL;
render_target_t *base_rt = any_map_get(render_path_render_targets, "texpaint_sculpt_base");
if (base_rt != NULL && base_rt->_image != NULL) {
base_pixels = gpu_get_texture_pixels(base_rt->_image);
}
// Accumulate the combined position of every vertex across all layers and masks
f32_array_t *cpos = f32_array_create(len * 3);
f32_array_t *pmask = f32_array_create(len);
buffer_t *l0 = gpu_get_texture_pixels(sculpt_layers->buffer[0]->texpaint_sculpt);
for (i32 i = 0; i < len; ++i) {
cpos->buffer[i * 3] = buffer_get_f32(l0, i * 16);
cpos->buffer[i * 3 + 1] = buffer_get_f32(l0, i * 16 + 4);
cpos->buffer[i * 3 + 2] = buffer_get_f32(l0, i * 16 + 8);
}
// Blend the base layer back toward the rest pose where its mask is dark
if (export_obj_sculpt_layer_mask(sculpt_layers->buffer[0], pmask, len, texa, inda, inv) && base_pixels != NULL) {
for (i32 i = 0; i < len; ++i) {
f32 bx = buffer_get_f32(base_pixels, i * 16);
f32 by = buffer_get_f32(base_pixels, i * 16 + 4);
f32 bz = buffer_get_f32(base_pixels, i * 16 + 8);
f32 w = pmask->buffer[i];
cpos->buffer[i * 3] = bx + (cpos->buffer[i * 3] - bx) * w;
cpos->buffer[i * 3 + 1] = by + (cpos->buffer[i * 3 + 1] - by) * w;
cpos->buffer[i * 3 + 2] = bz + (cpos->buffer[i * 3 + 2] - bz) * w;
}
}
// Add each additional layers displacement relative to the rest pose
for (i32 k = 1; k < count; ++k) {
buffer_t *lk = gpu_get_texture_pixels(sculpt_layers->buffer[k]->texpaint_sculpt);
bool masked = export_obj_sculpt_layer_mask(sculpt_layers->buffer[k], pmask, len, texa, inda, inv);
for (i32 i = 0; i < len; ++i) {
f32 dx = buffer_get_f32(lk, i * 16);
f32 dy = buffer_get_f32(lk, i * 16 + 4);
f32 dz = buffer_get_f32(lk, i * 16 + 8);
if (base_pixels != NULL) {
dx -= buffer_get_f32(base_pixels, i * 16);
dy -= buffer_get_f32(base_pixels, i * 16 + 4);
dz -= buffer_get_f32(base_pixels, i * 16 + 8);
}
if (masked) {
f32 w = pmask->buffer[i];
dx *= w;
dy *= w;
dz *= w;
}
cpos->buffer[i * 3] += dx;
cpos->buffer[i * 3 + 1] += dy;
cpos->buffer[i * 3 + 2] += dz;
}
}
export_obj_write_string(o, string("o %s\n", p->base->name));
for (i32 i = 0; i < len; ++i) {
f32 x = buffer_get_f32(pixels, i * 16) * sc;
f32 y = buffer_get_f32(pixels, i * 16 + 4) * sc;
f32 z = buffer_get_f32(pixels, i * 16 + 8) * sc;
f32 x = cpos->buffer[i * 3] * sc;
f32 y = cpos->buffer[i * 3 + 1] * sc;
f32 z = cpos->buffer[i * 3 + 2] * sc;
export_obj_write_string(o, "v ");
export_obj_write_string(o, f32_to_string(x));
export_obj_write_string(o, " ");
@@ -317,15 +418,15 @@ void export_obj_run_sculpt(char *path, mesh_object_t_array_t *paint_objects) {
for (i32 t = 0; t < tris; ++t) {
i32 i0 = t * 3, i1 = t * 3 + 1, i2 = t * 3 + 2;
f32 x0 = buffer_get_f32(pixels, i0 * 16) * sc;
f32 y0 = buffer_get_f32(pixels, i0 * 16 + 4) * sc;
f32 z0 = buffer_get_f32(pixels, i0 * 16 + 8) * sc;
f32 x1 = buffer_get_f32(pixels, i1 * 16) * sc;
f32 y1 = buffer_get_f32(pixels, i1 * 16 + 4) * sc;
f32 z1 = buffer_get_f32(pixels, i1 * 16 + 8) * sc;
f32 x2 = buffer_get_f32(pixels, i2 * 16) * sc;
f32 y2 = buffer_get_f32(pixels, i2 * 16 + 4) * sc;
f32 z2 = buffer_get_f32(pixels, i2 * 16 + 8) * sc;
f32 x0 = cpos->buffer[i0 * 3] * sc;
f32 y0 = cpos->buffer[i0 * 3 + 1] * sc;
f32 z0 = cpos->buffer[i0 * 3 + 2] * sc;
f32 x1 = cpos->buffer[i1 * 3] * sc;
f32 y1 = cpos->buffer[i1 * 3 + 1] * sc;
f32 z1 = cpos->buffer[i1 * 3 + 2] * sc;
f32 x2 = cpos->buffer[i2 * 3] * sc;
f32 y2 = cpos->buffer[i2 * 3 + 1] * sc;
f32 z2 = cpos->buffer[i2 * 3 + 2] * sc;
f32 e1x = x1 - x0, e1y = y1 - y0, e1z = z1 - z0;
f32 e2x = x2 - x0, e2y = y2 - y0, e2z = z2 - z0;
f32 nx = e1y * e2z - e1z * e2y;
@@ -347,8 +448,9 @@ void export_obj_run_sculpt(char *path, mesh_object_t_array_t *paint_objects) {
}
for (i32 i = 0; i < len; ++i) {
f32 u = texa->buffer[i * 2] * inv;
f32 v = 1.0 - texa->buffer[i * 2 + 1] * inv;
i32 vid = inda->buffer[i];
f32 u = texa->buffer[vid * 2] * inv;
f32 v = 1.0 - texa->buffer[vid * 2 + 1] * inv;
export_obj_write_string(o, "vt ");
export_obj_write_string(o, f32_to_string(u));
export_obj_write_string(o, " ");