Fix T86358: Use per face aspect correction for primitive UV projections
During UV unwrapping, Cube Projection, Sphere Projection, Cylinder Projection and Project From View (in the 3D Viewport), when "Correct Aspect" toggle is active, it now uses a query cache to perform a per-face aspect ratio ("per_face_aspect") correction for the active image of each face. Reviewed By: campbellbarton Ref D14852
This commit is contained in:
parent
a4c2060b91
commit
1c1e842879
Notes:
blender-bot
2023-02-14 01:52:41 +01:00
Referenced by issue #86358, Cube UV projection correct aspect option does not work correctly with multiple assigned materials
|
@ -268,6 +268,10 @@ struct BMLoop **ED_uvedit_selected_verts(const struct Scene *scene,
|
|||
int *r_verts_len);
|
||||
|
||||
void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy);
|
||||
void ED_uvedit_get_aspect_from_material(Object *ob,
|
||||
const int material_index,
|
||||
float *r_aspx,
|
||||
float *r_aspy);
|
||||
|
||||
void ED_uvedit_active_vert_loop_set(struct BMesh *bm, struct BMLoop *l);
|
||||
struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm);
|
||||
|
|
|
@ -267,26 +267,35 @@ static bool uvedit_have_selection_multi(const Scene *scene,
|
|||
return have_select;
|
||||
}
|
||||
|
||||
void ED_uvedit_get_aspect_from_material(Object *ob,
|
||||
const int material_index,
|
||||
float *r_aspx,
|
||||
float *r_aspy)
|
||||
{
|
||||
if (UNLIKELY(material_index < 0 || material_index >= ob->totcol)) {
|
||||
*r_aspx = 1.0f;
|
||||
*r_aspy = 1.0f;
|
||||
return;
|
||||
}
|
||||
Image *ima;
|
||||
ED_object_get_active_image(ob, material_index + 1, &ima, NULL, NULL, NULL);
|
||||
ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy);
|
||||
}
|
||||
|
||||
void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy)
|
||||
{
|
||||
BMEditMesh *em = BKE_editmesh_from_object(ob);
|
||||
BLI_assert(em != NULL);
|
||||
bool sloppy = true;
|
||||
bool selected = false;
|
||||
BMFace *efa;
|
||||
Image *ima;
|
||||
|
||||
efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
|
||||
|
||||
if (efa) {
|
||||
ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL);
|
||||
|
||||
ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy);
|
||||
}
|
||||
else {
|
||||
BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
|
||||
if (!efa) {
|
||||
*r_aspx = 1.0f;
|
||||
*r_aspy = 1.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
ED_uvedit_get_aspect_from_material(ob, efa->mat_nr, r_aspx, r_aspy);
|
||||
}
|
||||
|
||||
static void construct_param_handle_face_add(ParamHandle *handle,
|
||||
|
@ -1527,49 +1536,88 @@ static void uv_transform_properties(wmOperatorType *ot, int radius)
|
|||
}
|
||||
}
|
||||
|
||||
static void shrink_loop_uv_by_aspect_ratio(BMFace *efa,
|
||||
const int cd_loop_uv_offset,
|
||||
const float aspect_y)
|
||||
{
|
||||
BLI_assert(aspect_y != 1.0f); /* Nothing to do, should be handled by caller. */
|
||||
BLI_assert(aspect_y > 0.0f); /* Negative aspect ratios are not supported. */
|
||||
|
||||
BMLoop *l;
|
||||
BMIter iter;
|
||||
BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
|
||||
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
|
||||
if (aspect_y > 1.0f) {
|
||||
/* Reduce round-off error, i.e. `u = (u - 0.5) / aspect_y + 0.5`. */
|
||||
luv->uv[0] = luv->uv[0] / aspect_y + (0.5f - 0.5f / aspect_y);
|
||||
}
|
||||
else {
|
||||
/* Reduce round-off error, i.e. `v = (v - 0.5) * aspect_y + 0.5`. */
|
||||
luv->uv[1] = luv->uv[1] * aspect_y + (0.5f - 0.5f * aspect_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void correct_uv_aspect(Object *ob, BMEditMesh *em)
|
||||
{
|
||||
BMLoop *l;
|
||||
BMIter iter, liter;
|
||||
MLoopUV *luv;
|
||||
BMFace *efa;
|
||||
float scale, aspx, aspy;
|
||||
|
||||
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
|
||||
|
||||
float aspx, aspy;
|
||||
ED_uvedit_get_aspect(ob, &aspx, &aspy);
|
||||
const float aspect_y = aspx / aspy;
|
||||
if (aspect_y == 1.0f) {
|
||||
/* Scaling by 1.0 has no effect. */
|
||||
return;
|
||||
}
|
||||
BMFace *efa;
|
||||
BMIter iter;
|
||||
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
||||
if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
||||
shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aspx == aspy) {
|
||||
static void correct_uv_aspect_per_face(Object *ob, BMEditMesh *em)
|
||||
{
|
||||
const int materials_num = ob->totcol;
|
||||
if (materials_num == 0) {
|
||||
/* Without any materials, there is no aspect_y information and nothing to do. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (aspx > aspy) {
|
||||
scale = aspy / aspx;
|
||||
float *material_aspect_y = BLI_array_alloca(material_aspect_y, materials_num);
|
||||
/* Lazily initialize aspect ratio for materials. */
|
||||
copy_vn_fl(material_aspect_y, materials_num, -1.0f);
|
||||
|
||||
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
||||
if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
||||
continue;
|
||||
}
|
||||
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
|
||||
|
||||
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
|
||||
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
|
||||
luv->uv[0] = ((luv->uv[0] - 0.5f) * scale) + 0.5f;
|
||||
}
|
||||
BMFace *efa;
|
||||
BMIter iter;
|
||||
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
||||
if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
scale = aspx / aspy;
|
||||
|
||||
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
||||
if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
|
||||
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
|
||||
luv->uv[1] = ((luv->uv[1] - 0.5f) * scale) + 0.5f;
|
||||
}
|
||||
const int material_index = efa->mat_nr;
|
||||
if (UNLIKELY(material_index < 0 || material_index >= materials_num)) {
|
||||
/* The index might be for a material slot which is not currently setup. */
|
||||
continue;
|
||||
}
|
||||
|
||||
float aspect_y = material_aspect_y[material_index];
|
||||
if (aspect_y == -1.0f) {
|
||||
/* Lazily initialize aspect ratio for materials. */
|
||||
float aspx, aspy;
|
||||
ED_uvedit_get_aspect_from_material(ob, material_index, &aspx, &aspy);
|
||||
aspect_y = aspx / aspy;
|
||||
material_aspect_y[material_index] = aspect_y;
|
||||
}
|
||||
|
||||
if (aspect_y == 1.0f) {
|
||||
/* Scaling by 1.0 has no effect. */
|
||||
continue;
|
||||
}
|
||||
shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1613,7 +1661,17 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot)
|
|||
uv_map_clip_correct_properties_ex(ot, true);
|
||||
}
|
||||
|
||||
static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op)
|
||||
/**
|
||||
* \param per_face_aspect: Calculate the aspect ratio per-face,
|
||||
* otherwise use a single aspect for all UV's based on the material of the active face.
|
||||
* TODO: using per-face aspect may split UV islands so more advanced UV projection methods
|
||||
* such as "Unwrap" & "Smart UV Projections" will need to handle aspect correction themselves.
|
||||
* For now keep using a single aspect for all faces in this case.
|
||||
*/
|
||||
static void uv_map_clip_correct_multi(Object **objects,
|
||||
uint objects_len,
|
||||
wmOperator *op,
|
||||
bool per_face_aspect)
|
||||
{
|
||||
BMFace *efa;
|
||||
BMLoop *l;
|
||||
|
@ -1633,9 +1691,14 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
|
|||
BMEditMesh *em = BKE_editmesh_from_object(ob);
|
||||
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
|
||||
|
||||
/* correct for image aspect ratio */
|
||||
/* Correct for image aspect ratio. */
|
||||
if (correct_aspect) {
|
||||
correct_uv_aspect(ob, em);
|
||||
if (per_face_aspect) {
|
||||
correct_uv_aspect_per_face(ob, em);
|
||||
}
|
||||
else {
|
||||
correct_uv_aspect(ob, em);
|
||||
}
|
||||
}
|
||||
|
||||
if (scale_to_bounds) {
|
||||
|
@ -1678,6 +1741,11 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
|
|||
dy = 1.0f / dy;
|
||||
}
|
||||
|
||||
if (dx == 1.0f && dy == 1.0f) {
|
||||
/* Scaling by 1.0 has no effect. */
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
||||
Object *ob = objects[ob_index];
|
||||
|
||||
|
@ -1702,7 +1770,7 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
|
|||
|
||||
static void uv_map_clip_correct(Object *ob, wmOperator *op)
|
||||
{
|
||||
uv_map_clip_correct_multi(&ob, 1, op);
|
||||
uv_map_clip_correct_multi(&ob, 1, op, true);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -2283,7 +2351,9 @@ static int smart_project_exec(bContext *C, wmOperator *op)
|
|||
.use_seams = true,
|
||||
});
|
||||
|
||||
uv_map_clip_correct_multi(objects_changed, object_changed_len, op);
|
||||
/* #ED_uvedit_pack_islands_multi only supports `per_face_aspect = false`. */
|
||||
const bool per_face_aspect = false;
|
||||
uv_map_clip_correct_multi(objects_changed, object_changed_len, op, per_face_aspect);
|
||||
}
|
||||
|
||||
MEM_freeN(objects_changed);
|
||||
|
@ -2485,7 +2555,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
|
||||
if (changed_multi) {
|
||||
uv_map_clip_correct_multi(objects, objects_len, op);
|
||||
uv_map_clip_correct_multi(objects, objects_len, op, true);
|
||||
}
|
||||
|
||||
MEM_freeN(objects);
|
||||
|
|
Loading…
Reference in New Issue