Fix T37879: Default UV generation for mesh primitives.
authorBastien Montagne <montagne29@wanadoo.fr>
Fri, 4 Dec 2015 22:49:55 +0000 (23:49 +0100)
committerBastien Montagne <montagne29@wanadoo.fr>
Fri, 4 Dec 2015 22:49:55 +0000 (23:49 +0100)
Adds default-generated UVs to mesh primitives (cone, cylinder, icosphere, uvsphere, cube, circle, grid)
when they are added to the scene, since some of them can be pretty awkward to unwrap manually.

Original patch: Liam Mitchell (CommanderCorianderSalamander).
Main review work: Campbell Barton (campbellbarton).
Finalization, fixes and cleanup: Bastien Montagne (mont29).

Reviewers: mont29, #mesh_modeling, campbellbarton

Reviewed By: mont29, campbellbarton

Subscribers: lkruel, campbellbarton, michaelknubben, kevindietrich

Maniphest Tasks: T37879

Differential Revision: https://developer.blender.org/D481

source/blender/bmesh/intern/bmesh_opdefines.c
source/blender/bmesh/intern/bmesh_operators.h
source/blender/bmesh/operators/bmo_primitive.c
source/blender/editors/include/ED_object.h
source/blender/editors/mesh/editmesh_add.c
source/blender/editors/object/object_add.c

index d92bdc6ea69ae2b9f96a3382d2d5f9662f14fd56..72a8dac534aecd793c49a395f49d72c04832cc31 100644 (file)
@@ -1567,6 +1567,7 @@ static BMOpDefine bmo_create_grid_def = {
         {"y_segments",      BMO_OP_SLOT_INT},  /* number of y segments */
         {"size",            BMO_OP_SLOT_FLT},  /* size of the grid */
         {"matrix",          BMO_OP_SLOT_MAT},  /* matrix to multiply the new geometry with */
+        {"calc_uvs",        BMO_OP_SLOT_BOOL}, /* calculate default UVs */
         {{'\0'}},
        },
        /* slots_out */
@@ -1590,6 +1591,7 @@ static BMOpDefine bmo_create_uvsphere_def = {
         {"v_segments",      BMO_OP_SLOT_INT}, /* number of v segment */
         {"diameter",        BMO_OP_SLOT_FLT}, /* diameter */
         {"matrix",          BMO_OP_SLOT_MAT}, /* matrix to multiply the new geometry with */
+        {"calc_uvs",        BMO_OP_SLOT_BOOL}, /* calculate default UVs */
         {{'\0'}},
        },
        /* slots_out */
@@ -1612,6 +1614,7 @@ static BMOpDefine bmo_create_icosphere_def = {
        {{"subdivisions",    BMO_OP_SLOT_INT}, /* how many times to recursively subdivide the sphere */
         {"diameter",        BMO_OP_SLOT_FLT}, /* diameter */
         {"matrix",          BMO_OP_SLOT_MAT}, /* matrix to multiply the new geometry with */
+        {"calc_uvs",        BMO_OP_SLOT_BOOL}, /* calculate default UVs */
         {{'\0'}},
        },
        /* slots_out */
@@ -1658,6 +1661,7 @@ static BMOpDefine bmo_create_cone_def = {
         {"diameter2",       BMO_OP_SLOT_FLT},  /* diameter of the opposite */
         {"depth",           BMO_OP_SLOT_FLT},  /* distance between ends */
         {"matrix",          BMO_OP_SLOT_MAT},  /* matrix to multiply the new geometry with */
+        {"calc_uvs",        BMO_OP_SLOT_BOOL}, /* calculate default UVs */
         {{'\0'}},
        },
        /* slots_out */
@@ -1680,6 +1684,7 @@ static BMOpDefine bmo_create_circle_def = {
         {"segments",        BMO_OP_SLOT_INT},
         {"diameter",        BMO_OP_SLOT_FLT},  /* diameter of one end */
         {"matrix",          BMO_OP_SLOT_MAT},  /* matrix to multiply the new geometry with */
+        {"calc_uvs",        BMO_OP_SLOT_BOOL}, /* calculate default UVs */
         {{'\0'}},
        },
        /* slots_out */
@@ -1701,6 +1706,7 @@ static BMOpDefine bmo_create_cube_def = {
        /* slots_in */
        {{"size",            BMO_OP_SLOT_FLT},  /* size of the cube */
         {"matrix",          BMO_OP_SLOT_MAT},  /* matrix to multiply the new geometry with */
+        {"calc_uvs",        BMO_OP_SLOT_BOOL}, /* calculate default UVs */
         {{'\0'}},
        },
        /* slots_out */
index d9961e589da8c3a312465c64d46cf0e1264b2efd..0a4fb1d56a45a6e03374433f18b1760ad818e545 100644 (file)
@@ -141,6 +141,14 @@ void BM_mesh_esubdivide(
         const short use_only_quads,
         const int seed);
 
+void BM_mesh_calc_uvs_grid(BMesh *bm, const unsigned int x_segments, const unsigned int y_segments, const short oflag);
+void BM_mesh_calc_uvs_sphere(BMesh *bm, const short oflag);
+void BM_mesh_calc_uvs_circle(BMesh *bm, float mat[4][4], const float radius, const short oflag);
+void BM_mesh_calc_uvs_cone(
+        BMesh *bm, float mat[4][4],
+        const float radius_top, const float radius_bottom, const int segments, const bool cap_ends, const short oflag);
+void BM_mesh_calc_uvs_cube(BMesh *bm, const short oflag);
+
 #include "intern/bmesh_operator_api_inline.h"
 
 #endif /* __BMESH_OPERATORS_H__ */
index 2108a2c05897b3bcf2b4231c0439d1a75e64eeda..944f26eb131147a5be7a43c124374a8d863ade6f 100644 (file)
 
 #include "BLI_math.h"
 
+#include "BKE_customdata.h"
+
+#include "DNA_meshdata_types.h"
+
 #include "bmesh.h"
 #include "intern/bmesh_operators_private.h"
 
@@ -235,6 +239,7 @@ void bmo_create_grid_exec(BMesh *bm, BMOperator *op)
        const unsigned int ytot = max_ii(2, BMO_slot_int_get(op->slots_in, "y_segments"));
        const float xtot_inv2 = 2.0f / (xtot - 1);
        const float ytot_inv2 = 2.0f / (ytot - 1);
+       const bool calc_uvs = BMO_slot_bool_get(op->slots_in, "calc_uvs");
 
        BMVert **varr;
        BMVert *vquad[4];
@@ -265,17 +270,86 @@ void bmo_create_grid_exec(BMesh *bm, BMOperator *op)
 
        for (y = 1; y < ytot; y++) {
                for (x = 1; x < xtot; x++) {
+                       BMFace *f;
+
                        vquad[0] = varr[XY(x - 1, y - 1)];
                        vquad[1] = varr[XY(x,     y - 1)];
                        vquad[2] = varr[XY(x,         y)];
                        vquad[3] = varr[XY(x - 1,     y)];
 
-                       BM_face_create_verts(bm, vquad, 4, NULL, BM_CREATE_NOP, true);
+                       f = BM_face_create_verts(bm, vquad, 4, NULL, BM_CREATE_NOP, true);
+                       if (calc_uvs) {
+                               BMO_elem_flag_enable(bm, f, FACE_MARK);
+                       }
                }
        }
 
 #undef XY
 
+       if (calc_uvs) {
+               BM_mesh_calc_uvs_grid(bm, xtot, ytot, FACE_MARK);
+       }
+}
+
+/**
+ * Fills first available UVmap with grid-like UVs for all faces OpFlag-ged by given flag.
+ *
+ * \param bm The BMesh to operate on
+ * \param x_segments The x-resolution of the grid
+ * \param y_segments The y-resolution of the grid
+ * \param oflag The flag to check faces with.
+ */
+void BM_mesh_calc_uvs_grid(BMesh *bm, const unsigned int x_segments, const unsigned int y_segments, const short oflag)
+{
+       BMFace *f;
+       BMLoop *l;
+       BMIter iter, liter;
+
+       const float dx = 1.0f / (float)(x_segments - 1);
+       const float dy = 1.0f / (float)(y_segments - 1);
+       float x = 0.0f;
+       float y = 0.0f;
+
+       const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+       int loop_index;
+
+       BLI_assert(cd_loop_uv_offset != -1);
+
+       BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+               if (!BMO_elem_flag_test(bm, f, oflag))
+                       continue;
+
+               BM_ITER_ELEM_INDEX (l, &liter, f, BM_LOOPS_OF_FACE, loop_index) {
+                       MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+
+                       switch (loop_index) {
+                               case 0:
+                                       x += dx;
+                                       break;
+                               case 1:
+                                       y += dy;
+                                       break;
+                               case 2:
+                                       x -= dx;
+                                       break;
+                               case 3:
+                                       y -= dy;
+                                       break;
+                               default:
+                                       break;
+                       }
+
+                       luv->uv[0] = x;
+                       luv->uv[1] = y;
+               }
+
+               x += dx;
+               if (x >= 1.0f) {
+                       x = 0.0f;
+                       y += dy;
+               }
+       }
 }
 
 void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op)
@@ -283,6 +357,7 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op)
        const float dia = BMO_slot_float_get(op->slots_in, "diameter");
        const int seg = BMO_slot_int_get(op->slots_in, "u_segments");
        const int tot = BMO_slot_int_get(op->slots_in, "v_segments");
+       const bool calc_uvs = BMO_slot_bool_get(op->slots_in, "calc_uvs");
 
        BMOperator bmop, prevop;
        BMVert *eve, *preveve;
@@ -358,6 +433,31 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op)
                BMO_op_callf(bm, op->flag, "remove_doubles verts=%fv dist=%f", VERT_MARK, min_ff(len, len2) / 3.0f);
        }
 
+       if (calc_uvs) {
+               BMFace *f;
+               BMLoop *l;
+               BMIter fiter, liter;
+
+               /* We cannot tag faces for UVs computing above, so we have to do it now, based on all its vertices
+                * being tagged. */
+               BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+                       bool valid = true;
+
+                       BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+                               if (!BMO_elem_flag_test(bm, l->v, VERT_MARK)) {
+                                       valid = false;
+                                       break;
+                               }
+                       }
+
+                       if (valid) {
+                               BMO_elem_flag_enable(bm, f, FACE_MARK);
+                       }
+               }
+
+               BM_mesh_calc_uvs_sphere(bm, FACE_MARK);
+       }
+
        /* and now do imat */
        BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
                if (BMO_elem_flag_test(bm, eve, VERT_MARK)) {
@@ -373,6 +473,7 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op)
        const float dia = BMO_slot_float_get(op->slots_in, "diameter");
        const float dia_div = dia / 200.0f;
        const int subdiv = BMO_slot_int_get(op->slots_in, "subdivisions");
+       const bool calc_uvs = BMO_slot_bool_get(op->slots_in, "calc_uvs");
 
        BMVert *eva[12];
        BMVert *v;
@@ -431,6 +532,30 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op)
                BMO_op_finish(bm, &bmop);
        }
 
+       if (calc_uvs) {
+               BMFace *f;
+               BMIter fiter;
+
+               /* We cannot tag faces for UVs computing above, so we have to do it now, based on all its vertices
+                * being tagged. */
+               BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+                       bool valid = true;
+
+                       BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+                               if (!BMO_elem_flag_test(bm, l->v, VERT_MARK)) {
+                                       valid = false;
+                                       break;
+                               }
+                       }
+
+                       if (valid) {
+                               BMO_elem_flag_enable(bm, f, FACE_MARK);
+                       }
+               }
+
+               BM_mesh_calc_uvs_sphere(bm, FACE_MARK);
+       }
+
        /* must transform after because of sphere subdivision */
        BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
                if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
@@ -441,6 +566,75 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op)
        BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, VERT_MARK);
 }
 
+static void bm_mesh_calc_uvs_sphere_face(BMFace *f, float mat_rot[3][3], const int cd_loop_uv_offset)
+{
+       float *uvs[4];
+       BMLoop *l;
+       BMIter iter;
+       float dx;
+       int loop_index, loop_index_max_x;
+
+       BLI_assert(f->len <= 4);
+
+       BM_ITER_ELEM_INDEX (l, &iter, f, BM_LOOPS_OF_FACE, loop_index) {
+               MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+               float vco[3];
+
+               mul_v3_m3v3(vco, mat_rot, l->v->co);
+               map_to_sphere(&luv->uv[0], &luv->uv[1], vco[0], vco[1], vco[2]);
+
+               uvs[loop_index] = luv->uv;
+       }
+
+       /* Fix awkwardly-wrapping UVs */
+       loop_index_max_x = 0;
+       for (loop_index = 1; loop_index < f->len; loop_index++) {
+               if (uvs[loop_index][0] > uvs[loop_index_max_x][0]) {
+                       loop_index_max_x = loop_index;
+               }
+       }
+
+       for (loop_index = 0; loop_index < f->len; loop_index++) {
+               if (loop_index != loop_index_max_x) {
+                       dx = uvs[loop_index_max_x][0] - uvs[loop_index][0];
+                       if (dx > 0.5f) {
+                               uvs[loop_index][0] += 1.0f;
+                       }
+               }
+       }
+}
+
+/**
+ * Fills first available UVmap with spherical projected UVs for all faces OpFlag-ged by given flag.
+ *
+ * \param bm The BMesh to operate on
+ * \param oflag The flag to check faces with.
+ */
+void BM_mesh_calc_uvs_sphere(BMesh *bm, const short oflag)
+{
+       BMFace *f;
+       BMIter iter;
+
+       const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+       /* We apply a 'magic' rotationto vcos before mapping them to sphere,
+        * those values seem to give best results for both ico and uv sphere projections. */
+       float mat_rot[3][3];
+       const float axis[3] = {0.806f, 0.329f, 0.491f};
+       const float angle = DEG2RADF(120.0f);
+
+       axis_angle_to_mat3(mat_rot, axis, angle);
+
+       BLI_assert(cd_loop_uv_offset != -1); /* caller is responsible for giving us UVs */
+
+       BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+               if (!BMO_elem_flag_test(bm, f, oflag))
+                       continue;
+
+               bm_mesh_calc_uvs_sphere_face(f, mat_rot, cd_loop_uv_offset);
+       }
+}
+
 void bmo_create_monkey_exec(BMesh *bm, BMOperator *op)
 {
        BMVert *eve;
@@ -498,6 +692,7 @@ void bmo_create_circle_exec(BMesh *bm, BMOperator *op)
        const int segs = BMO_slot_int_get(op->slots_in, "segments");
        const bool cap_ends = BMO_slot_bool_get(op->slots_in, "cap_ends");
        const bool cap_tris = BMO_slot_bool_get(op->slots_in, "cap_tris");
+       const bool calc_uvs = BMO_slot_bool_get(op->slots_in, "calc_uvs");
 
        BMVert *v1, *lastv1 = NULL, *cent1, *firstv1 = NULL;
        float vec[3], mat[4][4], phi, phid;
@@ -555,6 +750,10 @@ void bmo_create_circle_exec(BMesh *bm, BMOperator *op)
                
                f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, BM_CREATE_NOP);
                BMO_elem_flag_enable(bm, f, FACE_NEW);
+
+               if (calc_uvs) {
+                       BM_mesh_calc_uvs_circle(bm, mat, dia, FACE_NEW);
+               }
        }
        
        if (!cap_tris) {
@@ -564,9 +763,54 @@ void bmo_create_circle_exec(BMesh *bm, BMOperator *op)
        BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, VERT_MARK);
 }
 
+/**
+ * Fills first available UVmap with 2D projected UVs for all faces OpFlag-ged by given flag.
+ *
+ * \param bm The BMesh to operate on.
+ * \param mat The transform matrix applied to the created circle.
+ * \param radius The size of the circle.
+ * \param oflag The flag to check faces with.
+ */
+void BM_mesh_calc_uvs_circle(BMesh *bm, float mat[4][4], const float radius, const short oflag)
+{
+       BMFace *f;
+       BMLoop *l;
+       BMIter fiter, liter;
+
+       const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+       const float uv_scale = 0.5f / radius;
+       const float uv_center = 0.5f;
+
+       float inv_mat[4][4];
+
+       BLI_assert(cd_loop_uv_offset != -1);  /* caller must ensure we have UVs already */
+
+       invert_m4_m4(inv_mat, mat);
+
+       BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+               if (!BMO_elem_flag_test(bm, f, oflag))
+                       continue;
+
+               BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+                       MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+
+                       float uv_vco[3];
+                       copy_v3_v3(uv_vco, l->v->co);
+                       /* transform back into the unit circle flat on the Z-axis */
+                       mul_m4_v3(inv_mat, uv_vco);
+
+                       /* then just take those coords for UVs */
+                       luv->uv[0] = uv_center + uv_scale * uv_vco[0];
+                       luv->uv[1] = uv_center + uv_scale * uv_vco[1];
+               }
+       }
+}
+
 void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
 {
        BMVert *v1, *v2, *lastv1 = NULL, *lastv2 = NULL, *cent1, *cent2, *firstv1, *firstv2;
+       BMFace *f;
        float vec[3], mat[4][4], phi, phid;
        float dia1 = BMO_slot_float_get(op->slots_in, "diameter1");
        float dia2 = BMO_slot_float_get(op->slots_in, "diameter2");
@@ -574,6 +818,7 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
        int segs = BMO_slot_int_get(op->slots_in, "segments");
        const bool cap_ends = BMO_slot_bool_get(op->slots_in, "cap_ends");
        const bool cap_tris = BMO_slot_bool_get(op->slots_in, "cap_tris");
+       const bool calc_uvs = BMO_slot_bool_get(op->slots_in, "calc_uvs");
        int a;
        
        if (!segs)
@@ -620,14 +865,23 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
 
                if (a) {
                        if (cap_ends) {
-                               BMFace *f;
-                               
                                f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, NULL, NULL, BM_CREATE_NOP);
+                               if (calc_uvs) {
+                                       BMO_elem_flag_enable(bm, f, FACE_MARK);
+                               }
                                BMO_elem_flag_enable(bm, f, FACE_NEW);
+
                                f = BM_face_create_quad_tri(bm, cent2, v2, lastv2, NULL, NULL, BM_CREATE_NOP);
+                               if (calc_uvs) {
+                                       BMO_elem_flag_enable(bm, f, FACE_MARK);
+                               }
                                BMO_elem_flag_enable(bm, f, FACE_NEW);
                        }
-                       BM_face_create_quad_tri(bm, lastv1, lastv2, v2, v1, NULL, BM_CREATE_NOP);
+
+                       f = BM_face_create_quad_tri(bm, lastv1, lastv2, v2, v1, NULL, BM_CREATE_NOP);
+                       if (calc_uvs) {
+                               BMO_elem_flag_enable(bm, f, FACE_MARK);
+                       }
                }
                else {
                        firstv1 = v1;
@@ -642,29 +896,149 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
                return;
 
        if (cap_ends) {
-               BMFace *f;
-               
                f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, BM_CREATE_NOP);
+               if (calc_uvs) {
+                       BMO_elem_flag_enable(bm, f, FACE_MARK);
+               }
                BMO_elem_flag_enable(bm, f, FACE_NEW);
+
                f = BM_face_create_quad_tri(bm, cent2, firstv2, v2, NULL, NULL, BM_CREATE_NOP);
+               if (calc_uvs) {
+                       BMO_elem_flag_enable(bm, f, FACE_MARK);
+               }
                BMO_elem_flag_enable(bm, f, FACE_NEW);
        }
-       
+
+       f = BM_face_create_quad_tri(bm, v1, v2, firstv2, firstv1, NULL, BM_CREATE_NOP);
+       if (calc_uvs) {
+               BMO_elem_flag_enable(bm, f, FACE_MARK);
+       }
+
+       if (calc_uvs) {
+               BM_mesh_calc_uvs_cone(bm, mat, dia2, dia1, segs, cap_ends, FACE_MARK);
+       }
+
        if (!cap_tris) {
                BMO_op_callf(bm, op->flag, "dissolve_faces faces=%ff", FACE_NEW);
        }
        
-       BM_face_create_quad_tri(bm, v1, v2, firstv2, firstv1, NULL, BM_CREATE_NOP);
-
        BMO_op_callf(bm, op->flag, "remove_doubles verts=%fv dist=%f", VERT_MARK, 0.000001);
        BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, VERT_MARK);
 }
 
+/**
+ * Fills first available UVmap with cylinder/cone-like UVs for all faces OpFlag-ged by given flag.
+ *
+ * \param bm The BMesh to operate on.
+ * \param mat The transform matrix applied to the created cone/cylinder.
+ * \param radius_top The size of the top end of the cone/cylynder.
+ * \param radius_bottom The size of the bottom end of the cone/cylynder.
+ * \param segments The number of subdivisions in the sides of the cone/cylinder.
+ * \param cap_ends Whether the ends of the cone/cylinder are filled or not.
+ * \param oflag The flag to check faces with.
+ */
+void BM_mesh_calc_uvs_cone(
+        BMesh *bm, float mat[4][4],
+        const float radius_top, const float radius_bottom, const int segments, const bool cap_ends, const short oflag)
+{
+       BMFace *f;
+       BMLoop *l;
+       BMIter fiter, liter;
+       const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+       const float uv_width = 1.0f / (float)segments;
+       const float uv_height = cap_ends ? 0.5f : 1.0f;
+
+       /* Note that all this allows us to handle all cases (real cone, truncated cone, with or without ends capped)
+        * with a single common code. */
+       const float uv_center_y = cap_ends ? 0.25f : 0.5f;
+       const float uv_center_x_top = cap_ends ? 0.25f : 0.5f;
+       const float uv_center_x_bottom = cap_ends ? 0.75f : 0.5f;
+       const float uv_radius = cap_ends ? 0.24f : 0.5f;
+
+       /* Using the opposite's end uv_scale as fallback allows us to handle 'real cone' case. */
+       const float uv_scale_top = (radius_top != 0.0f) ? (uv_radius / radius_top) :
+                                                         ((radius_bottom != 0.0f) ? (uv_radius / radius_bottom) : uv_radius);
+       const float uv_scale_bottom = (radius_bottom != 0.0f) ? (uv_radius / radius_bottom) :
+                                                               uv_scale_top;
+
+       float local_up[3] = {0.0f, 0.0f, 1.0f};
+
+       float x, y;
+       float inv_mat[4][4];
+       int loop_index;
+
+       mul_mat3_m4_v3(mat, local_up);  /* transform the upvector like we did the cone itself, without location. */
+       normalize_v3(local_up);  /* remove global scaling... */
+
+       invert_m4_m4(inv_mat, mat);
+
+       BLI_assert(cd_loop_uv_offset != -1); /* caller is responsible for ensuring the mesh has UVs */
+
+       x = 0.0f;
+       y = 1.0f - uv_height;
+
+       BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+               if (!BMO_elem_flag_test(bm, f, oflag))
+                       continue;
+
+               if (f->len == 4 && radius_top && radius_bottom) {
+                       /* side face - so unwrap it in a rectangle */
+                       BM_ITER_ELEM_INDEX (l, &liter, f, BM_LOOPS_OF_FACE, loop_index) {
+                               MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+
+                               switch (loop_index) {
+                                       case 0:
+                                               x += uv_width;
+                                               break;
+                                       case 1:
+                                               y += uv_height;
+                                               break;
+                                       case 2:
+                                               x -= uv_width;
+                                               break;
+                                       case 3:
+                                               y -= uv_height;
+                                               break;
+                                       default:
+                                               break;
+                               }
+
+                               luv->uv[0] = x;
+                               luv->uv[1] = y;
+                       }
+
+                       x += uv_width;
+               }
+               else {
+                       /* top or bottom face - so unwrap it by transforming back to a circle and using the X/Y coords */
+                       BM_face_normal_update(f);
+
+                       BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+                               MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+                               float uv_vco[3];
+
+                               mul_v3_m4v3(uv_vco, inv_mat, l->v->co);
+
+                               if (dot_v3v3(f->no, local_up) > 0.0f) { /* if this is a top face of the cone */
+                                       luv->uv[0] = uv_center_x_top + uv_vco[0] * uv_scale_top;
+                                       luv->uv[1] = uv_center_y + uv_vco[1] * uv_scale_top;
+                               }
+                               else {
+                                       luv->uv[0] = uv_center_x_bottom + uv_vco[0] * uv_scale_bottom;
+                                       luv->uv[1] = uv_center_y + uv_vco[1] * uv_scale_bottom;
+                               }
+                       }
+               }
+       }
+}
+
 void bmo_create_cube_exec(BMesh *bm, BMOperator *op)
 {
        BMVert *verts[8];
        float mat[4][4];
        float off = BMO_slot_float_get(op->slots_in, "size") / 2.0f;
+       const bool calc_uvs = BMO_slot_bool_get(op->slots_in, "calc_uvs");
        int i, x, y, z;
        const char faces[6][4] = {
                {1, 3, 2, 0},
@@ -693,6 +1067,7 @@ void bmo_create_cube_exec(BMesh *bm, BMOperator *op)
        }
 
        for (i = 0; i < ARRAY_SIZE(faces); i++) {
+               BMFace *f;
                BMVert *quad[4] = {
                    verts[faces[i][0]],
                    verts[faces[i][1]],
@@ -700,8 +1075,82 @@ void bmo_create_cube_exec(BMesh *bm, BMOperator *op)
                    verts[faces[i][3]],
                };
 
-               BM_face_create_verts(bm, quad, 4, NULL, BM_CREATE_NOP, true);
+               f = BM_face_create_verts(bm, quad, 4, NULL, BM_CREATE_NOP, true);
+               if (calc_uvs) {
+                       BMO_elem_flag_enable(bm, f, FACE_MARK);
+               }
+       }
+
+       if (calc_uvs) {
+               BM_mesh_calc_uvs_cube(bm, FACE_MARK);
        }
 
        BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, VERT_MARK);
 }
+
+/**
+ * Fills first available UVmap with cube-like UVs for all faces OpFlag-ged by given flag.
+ *
+ * \note Expects tagged faces to be six quads...
+ *
+ * \param bm The BMesh to operate on.
+ * \param oflag The flag to check faces with.
+ */
+void BM_mesh_calc_uvs_cube(BMesh *bm, const short oflag)
+{
+       BMFace *f;
+       BMLoop *l;
+       BMIter fiter, liter;
+       const float width = 0.25f;
+
+       const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+       float x = 0.375f;
+       float y = 0.0f;
+
+       int loop_index;
+
+       BLI_assert(cd_loop_uv_offset != -1); /* the caller can ensure that we have UVs */
+
+       BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+               if (!BMO_elem_flag_test(bm, f, oflag)) {
+                       continue;
+               }
+
+               BM_ITER_ELEM_INDEX (l, &liter, f, BM_LOOPS_OF_FACE, loop_index) {
+                       MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+
+                       luv->uv[0] = x;
+                       luv->uv[1] = y;
+
+                       switch (loop_index) {
+                               case 0:
+                                       x += width;
+                                       break;
+                               case 1:
+                                       y += width;
+                                       break;
+                               case 2:
+                                       x -= width;
+                                       break;
+                               case 3:
+                                       y -= width;
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+
+               if (y >= 0.75f && x > 0.125f) {
+                       x = 0.125f;
+                       y = 0.5f;
+               }
+               else if (x <= 0.125f) {
+                       x = 0.625f;
+                       y = 0.5f;
+               }
+               else {
+                       y += 0.25f;
+               }
+       }
+}
index 9fd7ec32279d16a9472106e314f66c9a211dca4b..c9b7875aef064d6f8b749a01dbbb842949981907 100644 (file)
@@ -137,6 +137,7 @@ float ED_object_new_primitive_matrix(
 
 void ED_object_add_unit_props(struct wmOperatorType *ot);
 void ED_object_add_generic_props(struct wmOperatorType *ot, bool do_editmode);
+void ED_object_add_mesh_props(struct wmOperatorType *ot);
 bool ED_object_add_generic_get_opts(struct bContext *C, struct wmOperator *op, const char view_align_axis,
                                     float loc[3], float rot[3],
                                     bool *enter_editmode, unsigned int *layer, bool *is_view_aligned);
index 49383356b758c853e7edd9d1ef88058a4b083a36..b0369c6f5ad39ba093d946f175fb80f8e0e4f028 100644 (file)
@@ -29,6 +29,7 @@
  *  \ingroup edmesh
  */
 
+#include "DNA_meshdata_types.h"
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 
@@ -49,6 +50,7 @@
 #include "ED_mesh.h"
 #include "ED_screen.h"
 #include "ED_object.h"
+#include "ED_uvedit.h"
 
 #include "mesh_intern.h"  /* own include */
 
@@ -105,16 +107,21 @@ static int add_primitive_plane_exec(bContext *C, wmOperator *op)
        bool enter_editmode;
        bool was_editmode;
        unsigned int layer;
+       const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs");
 
        WM_operator_view3d_unit_defaults(C, op);
        ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL);
        obedit = make_prim_init(C, CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Plane"), &dia, mat, &was_editmode, loc, rot, layer);
        em = BKE_editmesh_from_object(obedit);
 
+       if (calc_uvs && !ED_uvedit_test(obedit)) {
+               ED_mesh_uv_texture_add(obedit->data, NULL, true);
+       }
+
        if (!EDBM_op_call_and_selectf(
                em, op, "verts.out", false,
-               "create_grid x_segments=%i y_segments=%i size=%f matrix=%m4",
-               1, 1, RNA_float_get(op->ptr, "radius"), mat))
+               "create_grid x_segments=%i y_segments=%i size=%f matrix=%m4 calc_uvs=%b",
+               1, 1, RNA_float_get(op->ptr, "radius"), mat, calc_uvs))
        {
                return OPERATOR_CANCELLED;
        }
@@ -139,6 +146,7 @@ void MESH_OT_primitive_plane_add(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 
        ED_object_add_unit_props(ot);
+       ED_object_add_mesh_props(ot);
        ED_object_add_generic_props(ot, true);
 }
 
@@ -150,16 +158,21 @@ static int add_primitive_cube_exec(bContext *C, wmOperator *op)
        bool enter_editmode;
        bool was_editmode;
        unsigned int layer;
+       const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs");
 
        WM_operator_view3d_unit_defaults(C, op);
        ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL);
        obedit = make_prim_init(C, CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Cube"), &dia, mat, &was_editmode, loc, rot, layer);
        em = BKE_editmesh_from_object(obedit);
 
+       if (calc_uvs && !ED_uvedit_test(obedit)) {
+               ED_mesh_uv_texture_add(obedit->data, NULL, true);
+       }
+
        if (!EDBM_op_call_and_selectf(
                em, op, "verts.out", false,
-               "create_cube matrix=%m4 size=%f",
-               mat, RNA_float_get(op->ptr, "radius") * 2.0f))
+               "create_cube matrix=%m4 size=%f calc_uvs=%b",
+               mat, RNA_float_get(op->ptr, "radius") * 2.0f, calc_uvs))
        {
                return OPERATOR_CANCELLED;
        }
@@ -185,6 +198,7 @@ void MESH_OT_primitive_cube_add(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 
        ED_object_add_unit_props(ot);
+       ED_object_add_mesh_props(ot);
        ED_object_add_generic_props(ot, true);
 }
 
@@ -203,6 +217,7 @@ static int add_primitive_circle_exec(bContext *C, wmOperator *op)
        int cap_end, cap_tri;
        unsigned int layer;
        bool was_editmode;
+       const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs");
 
        cap_end = RNA_enum_get(op->ptr, "fill_type");
        cap_tri = (cap_end == 2);
@@ -212,11 +227,15 @@ static int add_primitive_circle_exec(bContext *C, wmOperator *op)
        obedit = make_prim_init(C, CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Circle"), &dia, mat, &was_editmode, loc, rot, layer);
        em = BKE_editmesh_from_object(obedit);
 
+       if (calc_uvs && !ED_uvedit_test(obedit)) {
+               ED_mesh_uv_texture_add(obedit->data, NULL, true);
+       }
+
        if (!EDBM_op_call_and_selectf(
                em, op, "verts.out", false,
-               "create_circle segments=%i diameter=%f cap_ends=%b cap_tris=%b matrix=%m4",
+               "create_circle segments=%i diameter=%f cap_ends=%b cap_tris=%b matrix=%m4 calc_uvs=%b",
                RNA_int_get(op->ptr, "vertices"), RNA_float_get(op->ptr, "radius"),
-               cap_end, cap_tri, mat))
+               cap_end, cap_tri, mat, calc_uvs))
        {
                return OPERATOR_CANCELLED;
        }
@@ -245,6 +264,7 @@ void MESH_OT_primitive_circle_add(wmOperatorType *ot)
        ED_object_add_unit_props(ot);
        RNA_def_enum(ot->srna, "fill_type", fill_type_items, 0, "Fill Type", "");
 
+       ED_object_add_mesh_props(ot);
        ED_object_add_generic_props(ot, true);
 }
 
@@ -259,20 +279,25 @@ static int add_primitive_cylinder_exec(bContext *C, wmOperator *op)
        const int end_fill_type = RNA_enum_get(op->ptr, "end_fill_type");
        const bool cap_end = (end_fill_type != 0);
        const bool cap_tri = (end_fill_type == 2);
+       const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs");
 
        WM_operator_view3d_unit_defaults(C, op);
        ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL);
        obedit = make_prim_init(C, CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Cylinder"), &dia, mat, &was_editmode, loc, rot, layer);
        em = BKE_editmesh_from_object(obedit);
 
+       if (calc_uvs && !ED_uvedit_test(obedit)) {
+               ED_mesh_uv_texture_add(obedit->data, NULL, true);
+       }
+
        if (!EDBM_op_call_and_selectf(
                em, op, "verts.out", false,
-               "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b cap_tris=%b depth=%f matrix=%m4",
+               "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
                RNA_int_get(op->ptr, "vertices"),
                RNA_float_get(op->ptr, "radius"),
                RNA_float_get(op->ptr, "radius"),
                cap_end, cap_tri,
-               RNA_float_get(op->ptr, "depth"), mat))
+               RNA_float_get(op->ptr, "depth"), mat, calc_uvs))
        {
                return OPERATOR_CANCELLED;
        }
@@ -302,6 +327,7 @@ void MESH_OT_primitive_cylinder_add(wmOperatorType *ot)
        RNA_def_float_distance(ot->srna, "depth", 2.0f, 0.0, OBJECT_ADD_SIZE_MAXF, "Depth", "", 0.001, 100.00);
        RNA_def_enum(ot->srna, "end_fill_type", fill_type_items, 1, "Cap Fill Type", "");
 
+       ED_object_add_mesh_props(ot);
        ED_object_add_generic_props(ot, true);
 }
 
@@ -316,17 +342,22 @@ static int add_primitive_cone_exec(bContext *C, wmOperator *op)
        const int end_fill_type = RNA_enum_get(op->ptr, "end_fill_type");
        const bool cap_end = (end_fill_type != 0);
        const bool cap_tri = (end_fill_type == 2);
+       const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs");
 
        WM_operator_view3d_unit_defaults(C, op);
        ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL);
        obedit = make_prim_init(C, CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Cone"), &dia, mat, &was_editmode, loc, rot, layer);
        em = BKE_editmesh_from_object(obedit);
 
+       if (calc_uvs && !ED_uvedit_test(obedit)) {
+               ED_mesh_uv_texture_add(obedit->data, NULL, true);
+       }
+
        if (!EDBM_op_call_and_selectf(
                em, op, "verts.out", false,
-               "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b cap_tris=%b depth=%f matrix=%m4",
+               "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
                RNA_int_get(op->ptr, "vertices"), RNA_float_get(op->ptr, "radius1"),
-               RNA_float_get(op->ptr, "radius2"), cap_end, cap_tri, RNA_float_get(op->ptr, "depth"), mat))
+               RNA_float_get(op->ptr, "radius2"), cap_end, cap_tri, RNA_float_get(op->ptr, "depth"), mat, calc_uvs))
        {
                return OPERATOR_CANCELLED;
        }
@@ -357,6 +388,7 @@ void MESH_OT_primitive_cone_add(wmOperatorType *ot)
        RNA_def_float_distance(ot->srna, "depth", 2.0f, 0.0, OBJECT_ADD_SIZE_MAXF, "Depth", "", 0.001, 100.00);
        RNA_def_enum(ot->srna, "end_fill_type", fill_type_items, 1, "Base Fill Type", "");
 
+       ED_object_add_mesh_props(ot);
        ED_object_add_generic_props(ot, true);
 }
 
@@ -368,18 +400,23 @@ static int add_primitive_grid_exec(bContext *C, wmOperator *op)
        bool enter_editmode;
        bool was_editmode;
        unsigned int layer;
+       const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs");
 
        WM_operator_view3d_unit_defaults(C, op);
        ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL);
        obedit = make_prim_init(C, CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Grid"), &dia, mat, &was_editmode, loc, rot, layer);
        em = BKE_editmesh_from_object(obedit);
 
+       if (calc_uvs && !ED_uvedit_test(obedit)) {
+               ED_mesh_uv_texture_add(obedit->data, NULL, true);
+       }
+
        if (!EDBM_op_call_and_selectf(
                em, op, "verts.out", false,
-               "create_grid x_segments=%i y_segments=%i size=%f matrix=%m4",
+               "create_grid x_segments=%i y_segments=%i size=%f matrix=%m4 calc_uvs=%b",
                RNA_int_get(op->ptr, "x_subdivisions"),
                RNA_int_get(op->ptr, "y_subdivisions"),
-               RNA_float_get(op->ptr, "radius"), mat))
+               RNA_float_get(op->ptr, "radius"), mat, calc_uvs))
        {
                return OPERATOR_CANCELLED;
        }
@@ -410,6 +447,7 @@ void MESH_OT_primitive_grid_add(wmOperatorType *ot)
        RNA_def_int(ot->srna, "y_subdivisions", 10, 2, MESH_ADD_VERTS_MAXI, "Y Subdivisions", "", 2, 1000);
        ED_object_add_unit_props(ot);
 
+       ED_object_add_mesh_props(ot);
        ED_object_add_generic_props(ot, true);
 }
 
@@ -471,17 +509,22 @@ static int add_primitive_uvsphere_exec(bContext *C, wmOperator *op)
        bool enter_editmode;
        bool was_editmode;
        unsigned int layer;
+       const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs");
 
        WM_operator_view3d_unit_defaults(C, op);
        ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL);
        obedit = make_prim_init(C, CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Sphere"), &dia, mat, &was_editmode, loc, rot, layer);
        em = BKE_editmesh_from_object(obedit);
 
+       if (calc_uvs && !ED_uvedit_test(obedit)) {
+               ED_mesh_uv_texture_add(obedit->data, NULL, true);
+       }
+
        if (!EDBM_op_call_and_selectf(
                em, op, "verts.out", false,
-               "create_uvsphere u_segments=%i v_segments=%i diameter=%f matrix=%m4",
+               "create_uvsphere u_segments=%i v_segments=%i diameter=%f matrix=%m4 calc_uvs=%b",
                RNA_int_get(op->ptr, "segments"), RNA_int_get(op->ptr, "ring_count"),
-               RNA_float_get(op->ptr, "size"), mat))
+               RNA_float_get(op->ptr, "size"), mat, calc_uvs))
        {
                return OPERATOR_CANCELLED;
        }
@@ -510,6 +553,7 @@ void MESH_OT_primitive_uv_sphere_add(wmOperatorType *ot)
        RNA_def_int(ot->srna, "ring_count", 16, 3, MESH_ADD_VERTS_MAXI / 100, "Rings", "", 3, 500);
        RNA_def_float_distance(ot->srna, "size", 1.0f, 0.0, OBJECT_ADD_SIZE_MAXF, "Size", "", 0.001, 100.00);
 
+       ED_object_add_mesh_props(ot);
        ED_object_add_generic_props(ot, true);
 }
 
@@ -521,17 +565,22 @@ static int add_primitive_icosphere_exec(bContext *C, wmOperator *op)
        bool enter_editmode;
        bool was_editmode;
        unsigned int layer;
+       const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs");
 
        WM_operator_view3d_unit_defaults(C, op);
        ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL);
        obedit = make_prim_init(C, CTX_DATA_(BLT_I18NCONTEXT_ID_MESH, "Icosphere"), &dia, mat, &was_editmode, loc, rot, layer);
        em = BKE_editmesh_from_object(obedit);
 
+       if (calc_uvs && !ED_uvedit_test(obedit)) {
+               ED_mesh_uv_texture_add(obedit->data, NULL, true);
+       }
+
        if (!EDBM_op_call_and_selectf(
                em, op, "verts.out", false,
-               "create_icosphere subdivisions=%i diameter=%f matrix=%m4",
+               "create_icosphere subdivisions=%i diameter=%f matrix=%m4 calc_uvs=%b",
                RNA_int_get(op->ptr, "subdivisions"),
-               RNA_float_get(op->ptr, "size"), mat))
+               RNA_float_get(op->ptr, "size"), mat, calc_uvs))
        {
                return OPERATOR_CANCELLED;
        }
@@ -559,5 +608,6 @@ void MESH_OT_primitive_ico_sphere_add(wmOperatorType *ot)
        RNA_def_int(ot->srna, "subdivisions", 2, 1, 10, "Subdivisions", "", 1, 8);
        RNA_def_float_distance(ot->srna, "size", 1.0f, 0.0f, OBJECT_ADD_SIZE_MAXF, "Size", "", 0.001f, 100.00);
 
+       ED_object_add_mesh_props(ot);
        ED_object_add_generic_props(ot, true);
 }
index 2888077faf29aa6cb7e8d5c020635b1588508da2..7650be941d4b1dc4f23d1e0c36766095669a0d41 100644 (file)
@@ -290,6 +290,11 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode)
        RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
 }
 
+void ED_object_add_mesh_props(wmOperatorType *ot)
+{
+       RNA_def_boolean(ot->srna, "calc_uvs", false, "Generate UVs", "Generate a default UV map");
+}
+
 bool ED_object_add_generic_get_opts(bContext *C, wmOperator *op, const char view_align_axis,
                                     float loc[3], float rot[3],
                                     bool *enter_editmode, unsigned int *layer, bool *is_view_aligned)