2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): none yet.
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/editors/uvedit/uvedit_unwrap_ops.c
37 #include "MEM_guardedalloc.h"
39 #include "DNA_camera_types.h"
40 #include "DNA_mesh_types.h"
41 #include "DNA_meshdata_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_scene_types.h"
44 #include "DNA_modifier_types.h"
46 #include "BLI_utildefines.h"
48 #include "BLI_edgehash.h"
49 #include "BLI_uvproject.h"
50 #include "BLI_utildefines.h"
52 #include "BLI_string.h"
54 #include "BKE_cdderivedmesh.h"
55 #include "BKE_subsurf.h"
56 #include "BKE_context.h"
57 #include "BKE_customdata.h"
58 #include "BKE_depsgraph.h"
59 #include "BKE_image.h"
62 #include "BKE_report.h"
63 #include "BKE_tessmesh.h"
66 #include "BLI_edgehash.h"
67 #include "BLI_scanfill.h"
68 #include "BLI_array.h"
69 #include "BLI_uvproject.h"
75 #include "ED_screen.h"
76 #include "ED_uvedit.h"
77 #include "ED_view3d.h"
79 #include "RNA_access.h"
80 #include "RNA_define.h"
86 #include "uvedit_intern.h"
87 #include "uvedit_parametrizer.h"
89 static int ED_uvedit_ensure_uvs(bContext *C, Scene *scene, Object *obedit)
91 Main *bmain = CTX_data_main(C);
92 BMEditMesh *em = BMEdit_FromObject(obedit);
101 if (ED_uvedit_test(obedit)) {
105 if (em && em->bm->totface && !CustomData_has_layer(&em->bm->pdata, CD_MTEXPOLY)) {
106 BM_data_layer_add(em->bm, &em->bm->pdata, CD_MTEXPOLY);
107 BM_data_layer_add(em->bm, &em->bm->ldata, CD_MLOOPUV);
108 ED_mesh_uv_loop_reset_ex(C, obedit->data, 0);
111 if (!ED_uvedit_test(obedit)) {
115 ima = CTX_data_edit_image(C);
118 /* no image in context in the 3d view, we find first image window .. */
119 sc = CTX_wm_screen(C);
121 for (sa = sc->areabase.first; sa; sa = sa->next) {
122 slink = sa->spacedata.first;
123 if (slink->spacetype == SPACE_IMAGE) {
124 sima = (SpaceImage *)slink;
128 if (ima->type == IMA_TYPE_R_RESULT || ima->type == IMA_TYPE_COMPOSITE)
138 ED_uvedit_assign_image(bmain, scene, obedit, ima, NULL);
140 /* select new UV's */
141 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
142 uvedit_face_select_enable(scene, em, efa, FALSE);
148 /****************** Parametrizer Conversion ***************/
150 static int uvedit_have_selection(Scene *scene, BMEditMesh *em, short implicit)
157 /* verify if we have any selected uv's before unwrapping,
158 * so we can cancel the operator early */
159 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
160 if (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) {
161 if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
164 else if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || !BM_elem_flag_test(efa, BM_ELEM_SELECT))
167 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
168 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
172 if (uvedit_uv_select_test(em, scene, l))
185 static ParamHandle *construct_param_handle(Scene *scene, BMEditMesh *em,
186 short implicit, short fill, short sel,
187 short correct_aspect)
189 ScanFillContext sf_ctx;
197 handle = param_construct_begin();
199 if (correct_aspect) {
200 efa = BM_active_face_get(em->bm, TRUE);
204 tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
206 ED_image_uv_aspect(tf->tpage, &aspx, &aspy);
209 param_aspect_ratio(handle, aspx, aspy);
213 /* we need the vert indices */
214 BM_mesh_elem_index_ensure(em->bm, BM_VERT);
218 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
219 ScanFillVert *sf_vert, *sf_vert_last, *sf_vert_first;
220 ScanFillFace *sf_tri;
221 ParamKey key, vkeys[4];
222 ParamBool pin[4], select[4];
228 if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || (sel && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0))
233 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
234 if (uvedit_uv_select_test(em, scene, l)) {
240 if (implicit && !lsel)
246 if (efa->len == 3 || efa->len == 4) {
247 /* for quads let parametrize split, it can make better decisions
248 * about which split is best for unwrapping than scanfill */
250 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
251 MLoopUV *luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
252 vkeys[i] = (ParamKey)BM_elem_index_get(l->v);
255 pin[i] = (luv->flag & MLOOPUV_PINNED) != 0;
256 select[i] = uvedit_uv_select_test(em, scene, l) != 0;
261 param_face_add(handle, key, i, vkeys, co, uv, pin, select);
264 /* ngon - scanfill time! */
265 BLI_scanfill_begin(&sf_ctx);
267 sf_vert_first = sf_vert_last = NULL;
268 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
271 sf_vert = BLI_scanfill_vert_add(&sf_ctx, l->v->co);
273 /* add small random offset */
274 for (i = 0; i < 3; i++) {
275 sf_vert->co[i] += (BLI_frand() - 0.5f) * FLT_EPSILON * 50;
281 BLI_scanfill_edge_add(&sf_ctx, sf_vert_last, sf_vert);
284 sf_vert_last = sf_vert;
286 sf_vert_first = sf_vert;
289 BLI_scanfill_edge_add(&sf_ctx, sf_vert_first, sf_vert);
291 BLI_scanfill_calc_ex(&sf_ctx, TRUE, efa->no);
292 for (sf_tri = sf_ctx.fillfacebase.first; sf_tri; sf_tri = sf_tri->next) {
293 ls[0] = sf_tri->v1->tmp.p;
294 ls[1] = sf_tri->v2->tmp.p;
295 ls[2] = sf_tri->v3->tmp.p;
297 for (i = 0; i < 3; i++) {
298 MLoopUV *luv = CustomData_bmesh_get(&em->bm->ldata, ls[i]->head.data, CD_MLOOPUV);
299 vkeys[i] = (ParamKey)BM_elem_index_get(ls[i]->v);
300 co[i] = ls[i]->v->co;
302 pin[i] = (luv->flag & MLOOPUV_PINNED) != 0;
303 select[i] = uvedit_uv_select_test(em, scene, ls[i]) != 0;
306 param_face_add(handle, key, 3, vkeys, co, uv, pin, select);
309 BLI_scanfill_end(&sf_ctx);
314 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
315 if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
317 vkeys[0] = (ParamKey)BM_elem_index_get(eed->v1);
318 vkeys[1] = (ParamKey)BM_elem_index_get(eed->v2);
319 param_edge_set_seam(handle, vkeys);
324 param_construct_end(handle, fill, implicit);
330 static void texface_from_original_index(BMFace *efa, int index, float **uv, ParamBool *pin, ParamBool *select, Scene *scene, BMEditMesh *em)
340 if (index == ORIGINDEX_NONE)
343 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
344 if (BM_elem_index_get(l->v) == index) {
345 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
347 *pin = (luv->flag & MLOOPUV_PINNED) ? 1 : 0;
348 *select = (uvedit_uv_select_test(em, scene, l) != 0);
353 /* unwrap handle initialization for subsurf aware-unwrapper. The many modifications required to make the original function(see above)
354 * work justified the existence of a new function. */
355 static ParamHandle *construct_param_handle_subsurfed(Scene *scene, BMEditMesh *em, short fill, short sel, short correct_aspect)
364 /* modifier initialization data, will control what type of subdivision will happen*/
365 SubsurfModifierData smd = {{0}};
366 /* Used to hold subsurfed Mesh */
367 DerivedMesh *derivedMesh, *initialDerived;
368 /* holds original indices for subsurfed mesh */
369 int *origVertIndices, *origFaceIndices, *origEdgeIndices;
370 /* Holds vertices of subsurfed mesh */
371 MVert *subsurfedVerts;
372 MEdge *subsurfedEdges;
373 MFace *subsurfedFaces;
374 /* number of vertices and faces for subsurfed mesh*/
375 int numOfEdges, numOfFaces;
377 /* holds a map to editfaces for every subsurfed MFace. These will be used to get hidden/ selected flags etc. */
379 /* similar to the above, we need a way to map edges to their original ones */
382 handle = param_construct_begin();
384 if (correct_aspect) {
385 editFace = BM_active_face_get(em->bm, TRUE);
390 tf = CustomData_bmesh_get(&em->bm->pdata, editFace->head.data, CD_MTEXPOLY);
392 ED_image_uv_aspect(tf->tpage, &aspx, &aspy);
395 param_aspect_ratio(handle, aspx, aspy);
399 /* number of subdivisions to perform */
400 smd.levels = scene->toolsettings->uv_subsurf_level;
401 smd.subdivType = ME_CC_SUBSURF;
403 initialDerived = CDDM_from_BMEditMesh(em, NULL, 0, 0);
404 derivedMesh = subsurf_make_derived_from_derived(initialDerived, &smd,
405 NULL, SUBSURF_IN_EDIT_MODE);
407 initialDerived->release(initialDerived);
409 /* get the derived data */
410 subsurfedVerts = derivedMesh->getVertArray(derivedMesh);
411 subsurfedEdges = derivedMesh->getEdgeArray(derivedMesh);
412 subsurfedFaces = derivedMesh->getTessFaceArray(derivedMesh);
414 origVertIndices = derivedMesh->getVertDataArray(derivedMesh, CD_ORIGINDEX);
415 origEdgeIndices = derivedMesh->getEdgeDataArray(derivedMesh, CD_ORIGINDEX);
416 origFaceIndices = derivedMesh->getTessFaceDataArray(derivedMesh, CD_ORIGINDEX);
418 numOfEdges = derivedMesh->getNumEdges(derivedMesh);
419 numOfFaces = derivedMesh->getNumTessFaces(derivedMesh);
421 faceMap = MEM_mallocN(numOfFaces * sizeof(BMFace *), "unwrap_edit_face_map");
423 BM_mesh_elem_index_ensure(em->bm, BM_VERT);
424 EDBM_index_arrays_init(em, 0, 1, 1);
426 /* map subsurfed faces to original editFaces */
427 for (i = 0; i < numOfFaces; i++)
428 faceMap[i] = EDBM_face_at_index(em, origFaceIndices[i]);
430 edgeMap = MEM_mallocN(numOfEdges * sizeof(BMEdge *), "unwrap_edit_edge_map");
432 /* map subsurfed edges to original editEdges */
433 for (i = 0; i < numOfEdges; i++) {
434 /* not all edges correspond to an old edge */
435 edgeMap[i] = (origEdgeIndices[i] != -1) ?
436 EDBM_edge_at_index(em, origEdgeIndices[i]) : NULL;
439 EDBM_index_arrays_free(em);
441 /* Prepare and feed faces to the solver */
442 for (i = 0; i < numOfFaces; i++) {
443 ParamKey key, vkeys[4];
444 ParamBool pin[4], select[4];
447 BMFace *origFace = faceMap[i];
449 face = subsurfedFaces + i;
451 if (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) {
452 if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN))
456 if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN) || (sel && !BM_elem_flag_test(origFace, BM_ELEM_SELECT)))
460 /* We will not check for v4 here. Subsurfed mfaces always have 4 vertices. */
461 key = (ParamKey)face;
462 vkeys[0] = (ParamKey)face->v1;
463 vkeys[1] = (ParamKey)face->v2;
464 vkeys[2] = (ParamKey)face->v3;
465 vkeys[3] = (ParamKey)face->v4;
467 co[0] = subsurfedVerts[face->v1].co;
468 co[1] = subsurfedVerts[face->v2].co;
469 co[2] = subsurfedVerts[face->v3].co;
470 co[3] = subsurfedVerts[face->v4].co;
472 /* This is where all the magic is done. If the vertex exists in the, we pass the original uv pointer to the solver, thus
473 * flushing the solution to the edit mesh. */
474 texface_from_original_index(origFace, origVertIndices[face->v1], &uv[0], &pin[0], &select[0], scene, em);
475 texface_from_original_index(origFace, origVertIndices[face->v2], &uv[1], &pin[1], &select[1], scene, em);
476 texface_from_original_index(origFace, origVertIndices[face->v3], &uv[2], &pin[2], &select[2], scene, em);
477 texface_from_original_index(origFace, origVertIndices[face->v4], &uv[3], &pin[3], &select[3], scene, em);
479 param_face_add(handle, key, 4, vkeys, co, uv, pin, select);
482 /* these are calculated from original mesh too */
483 for (edge = subsurfedEdges, i = 0; i < numOfEdges; i++, edge++) {
484 if ((edgeMap[i] != NULL) && BM_elem_flag_test(edgeMap[i], BM_ELEM_SEAM)) {
486 vkeys[0] = (ParamKey)edge->v1;
487 vkeys[1] = (ParamKey)edge->v2;
488 param_edge_set_seam(handle, vkeys);
492 param_construct_end(handle, fill, 0);
497 derivedMesh->release(derivedMesh);
502 /* ******************** Minimize Stretch operator **************** */
504 typedef struct MinStretch {
515 static int minimize_stretch_init(bContext *C, wmOperator *op)
517 Scene *scene = CTX_data_scene(C);
518 Object *obedit = CTX_data_edit_object(C);
519 BMEditMesh *em = BMEdit_FromObject(obedit);
521 int fill_holes = RNA_boolean_get(op->ptr, "fill_holes");
524 if (!uvedit_have_selection(scene, em, implicit)) {
528 ms = MEM_callocN(sizeof(MinStretch), "MinStretch");
532 ms->blend = RNA_float_get(op->ptr, "blend");
533 ms->iterations = RNA_int_get(op->ptr, "iterations");
535 ms->handle = construct_param_handle(scene, em, implicit, fill_holes, 1, 1);
536 ms->lasttime = PIL_check_seconds_timer();
538 param_stretch_begin(ms->handle);
539 if (ms->blend != 0.0f)
540 param_stretch_blend(ms->handle, ms->blend);
547 static void minimize_stretch_iteration(bContext *C, wmOperator *op, int interactive)
549 MinStretch *ms = op->customdata;
550 ScrArea *sa = CTX_wm_area(C);
552 param_stretch_blend(ms->handle, ms->blend);
553 param_stretch_iter(ms->handle);
556 RNA_int_set(op->ptr, "iterations", ms->i);
558 if (interactive && (PIL_check_seconds_timer() - ms->lasttime > 0.5)) {
561 param_flush(ms->handle);
564 BLI_snprintf(str, sizeof(str), "Minimize Stretch. Blend %.2f", ms->blend);
565 ED_area_headerprint(sa, str);
568 ms->lasttime = PIL_check_seconds_timer();
570 DAG_id_tag_update(ms->obedit->data, 0);
571 WM_event_add_notifier(C, NC_GEOM | ND_DATA, ms->obedit->data);
575 static void minimize_stretch_exit(bContext *C, wmOperator *op, int cancel)
577 MinStretch *ms = op->customdata;
578 ScrArea *sa = CTX_wm_area(C);
581 ED_area_headerprint(sa, NULL);
583 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), ms->timer);
586 param_flush_restore(ms->handle);
588 param_flush(ms->handle);
590 param_stretch_end(ms->handle);
591 param_delete(ms->handle);
593 DAG_id_tag_update(ms->obedit->data, 0);
594 WM_event_add_notifier(C, NC_GEOM | ND_DATA, ms->obedit->data);
597 op->customdata = NULL;
600 static int minimize_stretch_exec(bContext *C, wmOperator *op)
604 if (!minimize_stretch_init(C, op))
605 return OPERATOR_CANCELLED;
607 iterations = RNA_int_get(op->ptr, "iterations");
608 for (i = 0; i < iterations; i++)
609 minimize_stretch_iteration(C, op, 0);
610 minimize_stretch_exit(C, op, 0);
612 return OPERATOR_FINISHED;
615 static int minimize_stretch_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
619 if (!minimize_stretch_init(C, op))
620 return OPERATOR_CANCELLED;
622 minimize_stretch_iteration(C, op, 1);
625 WM_event_add_modal_handler(C, op);
626 ms->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
628 return OPERATOR_RUNNING_MODAL;
631 static int minimize_stretch_modal(bContext *C, wmOperator *op, wmEvent *event)
633 MinStretch *ms = op->customdata;
635 switch (event->type) {
638 minimize_stretch_exit(C, op, 1);
639 return OPERATOR_CANCELLED;
643 minimize_stretch_exit(C, op, 0);
644 return OPERATOR_FINISHED;
647 if (ms->blend < 0.95f) {
650 RNA_float_set(op->ptr, "blend", ms->blend);
651 minimize_stretch_iteration(C, op, 1);
656 if (ms->blend > 0.05f) {
659 RNA_float_set(op->ptr, "blend", ms->blend);
660 minimize_stretch_iteration(C, op, 1);
664 if (ms->timer == event->customdata) {
665 double start = PIL_check_seconds_timer();
668 minimize_stretch_iteration(C, op, 1);
669 } while (PIL_check_seconds_timer() - start < 0.01);
674 if (ms->iterations && ms->i >= ms->iterations) {
675 minimize_stretch_exit(C, op, 0);
676 return OPERATOR_FINISHED;
679 return OPERATOR_RUNNING_MODAL;
682 static int minimize_stretch_cancel(bContext *C, wmOperator *op)
684 minimize_stretch_exit(C, op, 1);
686 return OPERATOR_CANCELLED;
689 void UV_OT_minimize_stretch(wmOperatorType *ot)
692 ot->name = "Minimize Stretch";
693 ot->idname = "UV_OT_minimize_stretch";
694 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_POINTER | OPTYPE_BLOCKING;
695 ot->description = "Reduce UV stretching by relaxing angles";
698 ot->exec = minimize_stretch_exec;
699 ot->invoke = minimize_stretch_invoke;
700 ot->modal = minimize_stretch_modal;
701 ot->cancel = minimize_stretch_cancel;
702 ot->poll = ED_operator_uvedit;
705 RNA_def_boolean(ot->srna, "fill_holes", 1, "Fill Holes", "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry");
706 RNA_def_float_factor(ot->srna, "blend", 0.0f, 0.0f, 1.0f, "Blend", "Blend factor between stretch minimized and original", 0.0f, 1.0f);
707 RNA_def_int(ot->srna, "iterations", 0, 0, INT_MAX, "Iterations", "Number of iterations to run, 0 is unlimited when run interactively", 0, 100);
710 /* ******************** Pack Islands operator **************** */
712 static int pack_islands_exec(bContext *C, wmOperator *op)
714 Scene *scene = CTX_data_scene(C);
715 Object *obedit = CTX_data_edit_object(C);
716 BMEditMesh *em = BMEdit_FromObject(obedit);
720 if (!uvedit_have_selection(scene, em, implicit)) {
721 return OPERATOR_CANCELLED;
724 if (RNA_struct_property_is_set(op->ptr, "margin")) {
725 scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin");
728 RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
731 handle = construct_param_handle(scene, em, implicit, 0, 1, 1);
732 param_pack(handle, scene->toolsettings->uvcalc_margin);
734 param_delete(handle);
736 DAG_id_tag_update(obedit->data, 0);
737 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
739 return OPERATOR_FINISHED;
742 void UV_OT_pack_islands(wmOperatorType *ot)
745 ot->name = "Pack Islands";
746 ot->idname = "UV_OT_pack_islands";
747 ot->description = "Transform all islands so that they fill up the UV space as much as possible";
749 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
752 ot->exec = pack_islands_exec;
753 ot->poll = ED_operator_uvedit;
756 RNA_def_float_factor(ot->srna, "margin", 0.0f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
759 /* ******************** Average Islands Scale operator **************** */
761 static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
763 Scene *scene = CTX_data_scene(C);
764 Object *obedit = CTX_data_edit_object(C);
765 BMEditMesh *em = BMEdit_FromObject(obedit);
769 if (!uvedit_have_selection(scene, em, implicit)) {
770 return OPERATOR_CANCELLED;
773 handle = construct_param_handle(scene, em, implicit, 0, 1, 1);
774 param_average(handle);
776 param_delete(handle);
778 DAG_id_tag_update(obedit->data, 0);
779 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
781 return OPERATOR_FINISHED;
784 void UV_OT_average_islands_scale(wmOperatorType *ot)
787 ot->name = "Average Islands Scale";
788 ot->idname = "UV_OT_average_islands_scale";
789 ot->description = "Average the size of separate UV islands, based on their area in 3D space";
791 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
794 ot->exec = average_islands_scale_exec;
795 ot->poll = ED_operator_uvedit;
798 /**************** Live Unwrap *****************/
800 static ParamHandle *liveHandle = NULL;
802 void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
804 BMEditMesh *em = BMEdit_FromObject(obedit);
805 short abf = scene->toolsettings->unwrapper == 0;
806 short fillholes = scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES;
807 short use_subsurf = scene->toolsettings->uvcalc_flag & UVCALC_USESUBSURF;
809 if (!ED_uvedit_test(obedit)) {
814 liveHandle = construct_param_handle_subsurfed(scene, em, fillholes, 0, 1);
816 liveHandle = construct_param_handle(scene, em, 0, fillholes, 0, 1);
818 param_lscm_begin(liveHandle, PARAM_TRUE, abf);
821 void ED_uvedit_live_unwrap_re_solve(void)
824 param_lscm_solve(liveHandle);
825 param_flush(liveHandle);
829 void ED_uvedit_live_unwrap_end(short cancel)
832 param_lscm_end(liveHandle);
834 param_flush_restore(liveHandle);
835 param_delete(liveHandle);
840 void ED_uvedit_live_unwrap(Scene *scene, Object *obedit)
842 BMEditMesh *em = BMEdit_FromObject(obedit);
844 if (scene->toolsettings->edge_mode_live_unwrap &&
845 CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV))
847 ED_unwrap_lscm(scene, obedit, FALSE); /* unwrap all not just sel */
851 /*************** UV Map Common Transforms *****************/
853 #define VIEW_ON_EQUATOR 0
854 #define VIEW_ON_POLES 1
855 #define ALIGN_TO_OBJECT 2
860 static void uv_map_transform_center(Scene *scene, View3D *v3d, float *result,
861 Object *ob, BMEditMesh *em)
866 float min[3], max[3], *cursx;
867 int around = (v3d) ? v3d->around : V3D_CENTER;
869 /* only operates on the edit object - this is all that's needed now */
872 case V3D_CENTER: /* bounding box center */
873 INIT_MINMAX(min, max);
875 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
876 if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
877 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
878 minmax_v3v3_v3(min, max, l->v->co);
882 mid_v3_v3v3(result, min, max);
885 case V3D_CURSOR: /*cursor center*/
886 cursx = give_cursor(scene, v3d);
887 /* shift to objects world */
888 sub_v3_v3v3(result, cursx, ob->obmat[3]);
891 case V3D_LOCAL: /*object center*/
892 case V3D_CENTROID: /* multiple objects centers, only one object here*/
894 result[0] = result[1] = result[2] = 0.0;
899 static void uv_map_rotation_matrix(float result[][4], RegionView3D *rv3d, Object *ob,
900 float upangledeg, float sideangledeg, float radius)
902 float rotup[4][4], rotside[4][4], viewmatrix[4][4], rotobj[4][4];
903 float sideangle = 0.0f, upangle = 0.0f;
906 /* get rotation of the current view matrix */
908 copy_m4_m4(viewmatrix, rv3d->viewmat);
913 for (k = 0; k < 4; k++)
914 viewmatrix[3][k] = 0.0f;
916 /* get rotation of the current object matrix */
917 copy_m4_m4(rotobj, ob->obmat);
920 for (k = 0; k < 4; k++)
926 /* compensate front/side.. against opengl x,y,z world definition */
927 /* this is "kanonen gegen spatzen", a few plus minus 1 will do here */
928 /* i wanted to keep the reason here, so we're rotating*/
929 sideangle = (float)M_PI * (sideangledeg + 180.0f) / 180.0f;
930 rotside[0][0] = (float)cos(sideangle);
931 rotside[0][1] = -(float)sin(sideangle);
932 rotside[1][0] = (float)sin(sideangle);
933 rotside[1][1] = (float)cos(sideangle);
934 rotside[2][2] = 1.0f;
936 upangle = (float)M_PI * upangledeg / 180.0f;
937 rotup[1][1] = (float)cos(upangle) / radius;
938 rotup[1][2] = -(float)sin(upangle) / radius;
939 rotup[2][1] = (float)sin(upangle) / radius;
940 rotup[2][2] = (float)cos(upangle) / radius;
941 rotup[0][0] = (float)1.0f / radius;
943 /* calculate transforms*/
944 mul_serie_m4(result, rotup, rotside, viewmatrix, rotobj, NULL, NULL, NULL, NULL);
947 static void uv_map_transform(bContext *C, wmOperator *op, float center[3], float rotmat[4][4])
949 /* context checks are messy here, making it work in both 3d view and uv editor */
950 Scene *scene = CTX_data_scene(C);
951 Object *obedit = CTX_data_edit_object(C);
952 BMEditMesh *em = BMEdit_FromObject(obedit);
953 View3D *v3d = CTX_wm_view3d(C);
954 RegionView3D *rv3d = CTX_wm_region_view3d(C);
955 /* common operator properties */
956 int align = RNA_enum_get(op->ptr, "align");
957 int direction = RNA_enum_get(op->ptr, "direction");
958 float radius = RNA_struct_find_property(op->ptr, "radius") ? RNA_float_get(op->ptr, "radius") : 1.0f;
959 float upangledeg, sideangledeg;
961 uv_map_transform_center(scene, v3d, center, obedit, em);
963 if (direction == VIEW_ON_EQUATOR) {
969 if (align == POLAR_ZY) sideangledeg = 0.0f;
970 else sideangledeg = 90.0f;
973 /* be compatible to the "old" sphere/cylinder mode */
974 if (direction == ALIGN_TO_OBJECT)
977 uv_map_rotation_matrix(rotmat, rv3d, obedit, upangledeg, sideangledeg, radius);
981 static void uv_transform_properties(wmOperatorType *ot, int radius)
983 static EnumPropertyItem direction_items[] = {
984 {VIEW_ON_EQUATOR, "VIEW_ON_EQUATOR", 0, "View on Equator", "3D view is on the equator"},
985 {VIEW_ON_POLES, "VIEW_ON_POLES", 0, "View on Poles", "3D view is on the poles"},
986 {ALIGN_TO_OBJECT, "ALIGN_TO_OBJECT", 0, "Align to Object", "Align according to object transform"},
987 {0, NULL, 0, NULL, NULL}
989 static EnumPropertyItem align_items[] = {
990 {POLAR_ZX, "POLAR_ZX", 0, "Polar ZX", "Polar 0 is X"},
991 {POLAR_ZY, "POLAR_ZY", 0, "Polar ZY", "Polar 0 is Y"},
992 {0, NULL, 0, NULL, NULL}
995 RNA_def_enum(ot->srna, "direction", direction_items, VIEW_ON_EQUATOR, "Direction",
996 "Direction of the sphere or cylinder");
997 RNA_def_enum(ot->srna, "align", align_items, VIEW_ON_EQUATOR, "Align",
998 "How to determine rotation around the pole");
1000 RNA_def_float(ot->srna, "radius", 1.0f, 0.0f, FLT_MAX, "Radius",
1001 "Radius of the sphere or cylinder", 0.0001f, 100.0f);
1004 static void correct_uv_aspect(BMEditMesh *em)
1006 BMFace *efa = BM_active_face_get(em->bm, TRUE);
1010 float scale, aspx = 1.0f, aspy = 1.0f;
1015 tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
1016 ED_image_uv_aspect(tf->tpage, &aspx, &aspy);
1023 scale = aspy / aspx;
1025 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1026 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT) || BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
1029 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1030 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1031 luv->uv[0] = ((luv->uv[0] - 0.5f) * scale) + 0.5f;
1036 scale = aspx / aspy;
1038 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1039 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT) || BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
1042 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1043 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1044 luv->uv[1] = ((luv->uv[1] - 0.5f) * scale) + 0.5f;
1050 /******************** Map Clip & Correct ******************/
1052 static void uv_map_clip_correct_properties(wmOperatorType *ot)
1054 RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect",
1055 "Map UVs taking image aspect ratio into account");
1056 RNA_def_boolean(ot->srna, "clip_to_bounds", 0, "Clip to Bounds",
1057 "Clip UV coordinates to bounds after unwrapping");
1058 RNA_def_boolean(ot->srna, "scale_to_bounds", 0, "Scale to Bounds",
1059 "Scale UV coordinates to bounds after unwrapping");
1062 static void uv_map_clip_correct(BMEditMesh *em, wmOperator *op)
1068 float dx, dy, min[2], max[2];
1069 int correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
1070 int clip_to_bounds = RNA_boolean_get(op->ptr, "clip_to_bounds");
1071 int scale_to_bounds = RNA_boolean_get(op->ptr, "scale_to_bounds");
1073 /* correct for image aspect ratio */
1075 correct_uv_aspect(em);
1077 if (scale_to_bounds) {
1078 INIT_MINMAX2(min, max);
1080 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1081 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1084 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1085 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1086 DO_MINMAX2(luv->uv, min, max);
1090 /* rescale UV to be in 1/1 */
1091 dx = (max[0] - min[0]);
1092 dy = (max[1] - min[1]);
1099 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1100 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1103 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1104 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1106 luv->uv[0] = (luv->uv[0] - min[0]) * dx;
1107 luv->uv[1] = (luv->uv[1] - min[1]) * dy;
1111 else if (clip_to_bounds) {
1112 /* clipping and wrapping */
1113 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1114 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1117 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1118 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1119 CLAMP(luv->uv[0], 0.0f, 1.0f);
1120 CLAMP(luv->uv[1], 0.0f, 1.0f);
1126 /* ******************** Unwrap operator **************** */
1128 /* assumes UV Map is checked, doesn't run update funcs */
1129 void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel)
1131 BMEditMesh *em = BMEdit_FromObject(obedit);
1132 ParamHandle *handle;
1134 const short fill_holes = scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES;
1135 const short correct_aspect = !(scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT);
1136 const short use_subsurf = scene->toolsettings->uvcalc_flag & UVCALC_USESUBSURF;
1139 handle = construct_param_handle_subsurfed(scene, em, fill_holes, sel, correct_aspect);
1141 handle = construct_param_handle(scene, em, 0, fill_holes, sel, correct_aspect);
1143 param_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0);
1144 param_lscm_solve(handle);
1145 param_lscm_end(handle);
1147 param_average(handle);
1148 param_pack(handle, scene->toolsettings->uvcalc_margin);
1150 param_flush(handle);
1152 param_delete(handle);
1155 static int unwrap_exec(bContext *C, wmOperator *op)
1157 Scene *scene = CTX_data_scene(C);
1158 Object *obedit = CTX_data_edit_object(C);
1159 BMEditMesh *em = BMEdit_FromObject(obedit);
1160 int method = RNA_enum_get(op->ptr, "method");
1161 int fill_holes = RNA_boolean_get(op->ptr, "fill_holes");
1162 int correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
1163 int use_subsurf = RNA_boolean_get(op->ptr, "use_subsurf_data");
1164 int subsurf_level = RNA_int_get(op->ptr, "uv_subsurf_level");
1165 float obsize[3], unitsize[3] = {1.0f, 1.0f, 1.0f};
1168 if (!uvedit_have_selection(scene, em, implicit)) {
1169 return OPERATOR_CANCELLED;
1172 /* add uvs if they don't exist yet */
1173 if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1174 return OPERATOR_CANCELLED;
1177 mat4_to_size(obsize, obedit->obmat);
1178 if (!compare_v3v3(obsize, unitsize, 1e-4f))
1179 BKE_report(op->reports, RPT_INFO, "Object scale is not 1.0. Unwrap will operate on a non-scaled version of the mesh.");
1181 /* remember last method for live unwrap */
1182 if (RNA_struct_property_is_set(op->ptr, "method"))
1183 scene->toolsettings->unwrapper = method;
1185 RNA_enum_set(op->ptr, "method", scene->toolsettings->unwrapper);
1187 scene->toolsettings->uv_subsurf_level = subsurf_level;
1189 if (fill_holes) scene->toolsettings->uvcalc_flag |= UVCALC_FILLHOLES;
1190 else scene->toolsettings->uvcalc_flag &= ~UVCALC_FILLHOLES;
1192 if (correct_aspect) scene->toolsettings->uvcalc_flag &= ~UVCALC_NO_ASPECT_CORRECT;
1193 else scene->toolsettings->uvcalc_flag |= UVCALC_NO_ASPECT_CORRECT;
1195 if (use_subsurf) scene->toolsettings->uvcalc_flag |= UVCALC_USESUBSURF;
1196 else scene->toolsettings->uvcalc_flag &= ~UVCALC_USESUBSURF;
1198 /* execute unwrap */
1199 ED_unwrap_lscm(scene, obedit, TRUE);
1201 DAG_id_tag_update(obedit->data, 0);
1202 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1204 return OPERATOR_FINISHED;
1207 void UV_OT_unwrap(wmOperatorType *ot)
1209 static EnumPropertyItem method_items[] = {
1210 {0, "ANGLE_BASED", 0, "Angle Based", ""},
1211 {1, "CONFORMAL", 0, "Conformal", ""},
1212 {0, NULL, 0, NULL, NULL}
1216 ot->name = "Unwrap";
1217 ot->description = "Unwrap the mesh of the object being edited";
1218 ot->idname = "UV_OT_unwrap";
1219 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1222 ot->exec = unwrap_exec;
1223 ot->poll = ED_operator_uvmap;
1226 RNA_def_enum(ot->srna, "method", method_items, 0, "Method",
1227 "Unwrapping method (Angle Based usually gives better results than Conformal, while being somewhat slower)");
1228 RNA_def_boolean(ot->srna, "fill_holes", 1, "Fill Holes",
1229 "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry");
1230 RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect",
1231 "Map UVs taking image aspect ratio into account");
1232 RNA_def_boolean(ot->srna, "use_subsurf_data", 0, "Use Subsurf Data",
1233 "Map UVs taking vertex position after subsurf into account");
1234 RNA_def_int(ot->srna, "uv_subsurf_level", 1, 1, 6, "SubSurf Target",
1235 "Number of times to subdivide before calculating UVs", 1, 6);
1238 /**************** Project From View operator **************/
1239 static int uv_from_view_exec(bContext *C, wmOperator *op)
1241 Scene *scene = CTX_data_scene(C);
1242 Object *obedit = CTX_data_edit_object(C);
1243 Camera *camera = NULL;
1244 BMEditMesh *em = BMEdit_FromObject(obedit);
1245 ARegion *ar = CTX_wm_region(C);
1246 View3D *v3d = CTX_wm_view3d(C);
1247 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1254 /* add uvs if they don't exist yet */
1255 if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1256 return OPERATOR_CANCELLED;
1259 /* establish the camera object, so we can default to view mapping if anything is wrong with it */
1260 if ((rv3d->persp == RV3D_CAMOB) && (v3d->camera) && (v3d->camera->type == OB_CAMERA)) {
1261 camera = v3d->camera->data;
1264 if (RNA_boolean_get(op->ptr, "orthographic")) {
1265 uv_map_rotation_matrix(rotmat, rv3d, obedit, 90.0f, 0.0f, 1.0f);
1267 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1268 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1271 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1272 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1273 BLI_uvproject_from_view_ortho(luv->uv, l->v->co, rotmat);
1278 struct ProjCameraInfo *uci = BLI_uvproject_camera_info(v3d->camera, obedit->obmat, scene->r.xsch, scene->r.ysch);
1281 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1282 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1285 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1286 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1287 BLI_uvproject_from_camera(luv->uv, l->v->co, uci);
1295 copy_m4_m4(rotmat, obedit->obmat);
1297 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1298 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1301 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1302 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1303 BLI_uvproject_from_view(luv->uv, l->v->co, rv3d->persmat, rotmat, ar->winx, ar->winy);
1308 uv_map_clip_correct(em, op);
1310 DAG_id_tag_update(obedit->data, 0);
1311 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1313 return OPERATOR_FINISHED;
1316 static int uv_from_view_poll(bContext *C)
1318 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1320 if (!ED_operator_uvmap(C))
1323 return (rv3d != NULL);
1326 void UV_OT_from_view(wmOperatorType *ot)
1329 ot->name = "Project From View";
1330 ot->idname = "UV_OT_project_from_view";
1331 ot->description = "Project the UV vertices of the mesh as seen in current 3D view";
1333 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1336 ot->exec = uv_from_view_exec;
1337 ot->poll = uv_from_view_poll;
1340 RNA_def_boolean(ot->srna, "orthographic", 0, "Orthographic", "Use orthographic projection");
1341 uv_map_clip_correct_properties(ot);
1344 /********************** Reset operator ********************/
1346 static int reset_exec(bContext *C, wmOperator *UNUSED(op))
1348 Scene *scene = CTX_data_scene(C);
1349 Object *obedit = CTX_data_edit_object(C);
1350 Mesh *me = (Mesh *)obedit->data;
1352 /* add uvs if they don't exist yet */
1353 if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1354 return OPERATOR_CANCELLED;
1357 if (!ED_mesh_uv_loop_reset(C, me)) {
1358 return OPERATOR_CANCELLED;
1361 DAG_id_tag_update(obedit->data, 0);
1362 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1364 return OPERATOR_FINISHED;
1367 void UV_OT_reset(wmOperatorType *ot)
1371 ot->idname = "UV_OT_reset";
1372 ot->description = "Reset UV projection";
1374 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1377 ot->exec = reset_exec;
1378 ot->poll = ED_operator_uvmap;
1381 /****************** Sphere Project operator ***************/
1383 static void uv_sphere_project(float target[2], float source[3], float center[3], float rotmat[4][4])
1387 sub_v3_v3v3(pv, source, center);
1388 mul_m4_v3(rotmat, pv);
1390 map_to_sphere(&target[0], &target[1], pv[0], pv[1], pv[2]);
1392 /* split line is always zero */
1393 if (target[0] >= 1.0f)
1397 static void uv_map_mirror(BMEditMesh *em, BMFace *efa, MTexPoly *UNUSED(tf))
1403 BLI_array_fixedstack_declare(uvs, BM_NGON_STACK_SIZE, efa->len, __func__);
1408 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1409 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1415 for (i = 1; i < efa->len; i++)
1416 if (uvs[i][0] > uvs[mi][0])
1419 for (i = 0; i < efa->len; i++) {
1421 dx = uvs[mi][0] - uvs[i][0];
1422 if (dx > 0.5f) uvs[i][0] += 1.0f;
1426 BLI_array_fixedstack_free(uvs);
1429 static int sphere_project_exec(bContext *C, wmOperator *op)
1431 Scene *scene = CTX_data_scene(C);
1432 Object *obedit = CTX_data_edit_object(C);
1433 BMEditMesh *em = BMEdit_FromObject(obedit);
1439 float center[3], rotmat[4][4];
1441 /* add uvs if they don't exist yet */
1442 if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1443 return OPERATOR_CANCELLED;
1446 uv_map_transform(C, op, center, rotmat);
1448 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1449 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1452 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1453 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1455 uv_sphere_project(luv->uv, l->v->co, center, rotmat);
1458 tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
1459 uv_map_mirror(em, efa, tf);
1462 uv_map_clip_correct(em, op);
1464 DAG_id_tag_update(obedit->data, 0);
1465 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1467 return OPERATOR_FINISHED;
1470 void UV_OT_sphere_project(wmOperatorType *ot)
1473 ot->name = "Sphere Projection";
1474 ot->idname = "UV_OT_sphere_project";
1475 ot->description = "Project the UV vertices of the mesh over the curved surface of a sphere";
1477 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1480 ot->exec = sphere_project_exec;
1481 ot->poll = ED_operator_uvmap;
1484 uv_transform_properties(ot, 0);
1485 uv_map_clip_correct_properties(ot);
1488 /***************** Cylinder Project operator **************/
1490 static void uv_cylinder_project(float target[2], float source[3], float center[3], float rotmat[4][4])
1494 sub_v3_v3v3(pv, source, center);
1495 mul_m4_v3(rotmat, pv);
1497 map_to_tube(&target[0], &target[1], pv[0], pv[1], pv[2]);
1499 /* split line is always zero */
1500 if (target[0] >= 1.0f)
1504 static int cylinder_project_exec(bContext *C, wmOperator *op)
1506 Scene *scene = CTX_data_scene(C);
1507 Object *obedit = CTX_data_edit_object(C);
1508 BMEditMesh *em = BMEdit_FromObject(obedit);
1514 float center[3], rotmat[4][4];
1516 /* add uvs if they don't exist yet */
1517 if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1518 return OPERATOR_CANCELLED;
1521 uv_map_transform(C, op, center, rotmat);
1523 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1524 tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
1525 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1528 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1529 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1531 uv_cylinder_project(luv->uv, l->v->co, center, rotmat);
1534 uv_map_mirror(em, efa, tf);
1537 uv_map_clip_correct(em, op);
1539 DAG_id_tag_update(obedit->data, 0);
1540 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1542 return OPERATOR_FINISHED;
1545 void UV_OT_cylinder_project(wmOperatorType *ot)
1548 ot->name = "Cylinder Projection";
1549 ot->idname = "UV_OT_cylinder_project";
1550 ot->description = "Project the UV vertices of the mesh over the curved wall of a cylinder";
1552 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1555 ot->exec = cylinder_project_exec;
1556 ot->poll = ED_operator_uvmap;
1559 uv_transform_properties(ot, 1);
1560 uv_map_clip_correct_properties(ot);
1563 /******************* Cube Project operator ****************/
1565 static int cube_project_exec(bContext *C, wmOperator *op)
1567 Scene *scene = CTX_data_scene(C);
1568 Object *obedit = CTX_data_edit_object(C);
1569 BMEditMesh *em = BMEdit_FromObject(obedit);
1573 /* MTexPoly *tf; */ /* UNUSED */
1575 float cube_size, *loc, dx, dy;
1578 /* add uvs if they don't exist yet */
1579 if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1580 return OPERATOR_CANCELLED;
1583 loc = obedit->obmat[3];
1584 cube_size = RNA_float_get(op->ptr, "cube_size");
1586 /* choose x,y,z axis for projection depending on the largest normal
1587 * component, but clusters all together around the center of map. */
1589 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1592 /* tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); */ /* UNUSED */
1593 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1596 axis_dominant_v3(&cox, &coy, efa->no);
1599 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1600 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1602 luv->uv[0] = 0.5f + 0.5f * cube_size * (loc[cox] + l->v->co[cox]);
1603 luv->uv[1] = 0.5f + 0.5f * cube_size * (loc[coy] + l->v->co[coy]);
1606 dx = floor(luv->uv[0]);
1607 dy = floor(luv->uv[1]);
1617 uv_map_clip_correct(em, op);
1619 DAG_id_tag_update(obedit->data, 0);
1620 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1622 return OPERATOR_FINISHED;
1625 void UV_OT_cube_project(wmOperatorType *ot)
1628 ot->name = "Cube Projection";
1629 ot->idname = "UV_OT_cube_project";
1630 ot->description = "Project the UV vertices of the mesh over the six faces of a cube";
1632 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1635 ot->exec = cube_project_exec;
1636 ot->poll = ED_operator_uvmap;
1639 RNA_def_float(ot->srna, "cube_size", 1.0f, 0.0f, FLT_MAX, "Cube Size", "Size of the cube to project on", 0.001f, 100.0f);
1640 uv_map_clip_correct_properties(ot);