diff options
-rw-r--r-- | source/blender/bmesh/intern/bmesh_opdefines.c | 6 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_operators.h | 8 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_primitive.c | 469 | ||||
-rw-r--r-- | source/blender/editors/include/ED_object.h | 1 | ||||
-rw-r--r-- | source/blender/editors/mesh/editmesh_add.c | 82 | ||||
-rw-r--r-- | source/blender/editors/object/object_add.c | 5 |
6 files changed, 545 insertions, 26 deletions
diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index d92bdc6ea69..72a8dac534a 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -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 */ diff --git a/source/blender/bmesh/intern/bmesh_operators.h b/source/blender/bmesh/intern/bmesh_operators.h index d9961e589da..0a4fb1d56a4 100644 --- a/source/blender/bmesh/intern/bmesh_operators.h +++ b/source/blender/bmesh/intern/bmesh_operators.h @@ -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__ */ diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c index 2108a2c0589..944f26eb131 100644 --- a/source/blender/bmesh/operators/bmo_primitive.c +++ b/source/blender/bmesh/operators/bmo_primitive.c @@ -30,6 +30,10 @@ #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; + } + } +} diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 9fd7ec32279..c9b7875aef0 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -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); diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c index 49383356b75..b0369c6f5ad 100644 --- a/source/blender/editors/mesh/editmesh_add.c +++ b/source/blender/editors/mesh/editmesh_add.c @@ -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); } diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 2888077faf2..7650be941d4 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -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) |