From b6b27b067113184093c3023ac7c9bf9a6ad5914b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 27 Aug 2017 02:38:19 +1000 Subject: [PATCH 1/5] Fix T52515: Crash on BMesh.to_mesh() --- source/blender/bmesh/intern/bmesh_mesh_conv.c | 121 ++++++++++-------- 1 file changed, 68 insertions(+), 53 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.c b/source/blender/bmesh/intern/bmesh_mesh_conv.c index b340bc1a55c..2edc043cb13 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.c +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c @@ -226,6 +226,9 @@ void BM_mesh_bm_from_me( BMesh *bm, Mesh *me, const struct BMeshFromMeshParams *params) { + const bool is_new = + !(bm->totvert || + (bm->vdata.totlayer || bm->edata.totlayer || bm->pdata.totlayer || bm->ldata.totlayer)); MVert *mvert; MEdge *medge; MLoop *mloop; @@ -235,17 +238,12 @@ void BM_mesh_bm_from_me( BMEdge *e, **etable = NULL; BMFace *f, **ftable = NULL; float (*keyco)[3] = NULL; - int totuv, totloops, i, j; + int totuv, totloops, i; /* free custom data */ - /* this isnt needed in most cases but do just incase */ - CustomData_free(&bm->vdata, bm->totvert); - CustomData_free(&bm->edata, bm->totedge); - CustomData_free(&bm->ldata, bm->totloop); - CustomData_free(&bm->pdata, bm->totface); if (!me || !me->totvert) { - if (me) { /*no verts? still copy customdata layout*/ + if (me && is_new) { /*no verts? still copy customdata layout*/ CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_ASSIGN, 0); CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_ASSIGN, 0); CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_ASSIGN, 0); @@ -259,20 +257,28 @@ void BM_mesh_bm_from_me( return; /* sanity check */ } - vtable = MEM_mallocN(sizeof(BMVert **) * me->totvert, __func__); + if (is_new) { + CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); - CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); - CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); - CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0); - CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); - - /* make sure uv layer names are consisten */ - totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); - for (i = 0; i < totuv; i++) { - int li = CustomData_get_layer_index_n(&bm->pdata, CD_MTEXPOLY, i); - CustomData_set_layer_name(&bm->ldata, CD_MLOOPUV, i, bm->pdata.layers[li].name); + /* make sure uv layer names are consisten */ + totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + for (i = 0; i < totuv; i++) { + int li = CustomData_get_layer_index_n(&bm->pdata, CD_MTEXPOLY, i); + CustomData_set_layer_name(&bm->ldata, CD_MLOOPUV, i, bm->pdata.layers[li].name); + } } + /* -------------------------------------------------------------------- */ + /* Shape Key */ + int tot_shape_keys = me->key ? BLI_listbase_count(&me->key->block) : 0; + if (is_new == false) { + tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)); + } + const float (**shape_key_table)[3] = tot_shape_keys ? BLI_array_alloca(shape_key_table, tot_shape_keys) : NULL; + if ((params->active_shapekey != 0) && (me->key != NULL)) { actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1); } @@ -280,11 +286,10 @@ void BM_mesh_bm_from_me( actkey = NULL; } - const int tot_shape_keys = me->key ? BLI_listbase_count(&me->key->block) : 0; - const float (**shape_key_table)[3] = tot_shape_keys ? BLI_array_alloca(shape_key_table, tot_shape_keys) : NULL; - - if (tot_shape_keys || params->add_key_index) { - CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + if (is_new) { + if (tot_shape_keys || params->add_key_index) { + CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + } } if (tot_shape_keys) { @@ -304,39 +309,43 @@ void BM_mesh_bm_from_me( } if (actkey && actkey->totelem == me->totvert) { - keyco = actkey->data; - bm->shapenr = params->active_shapekey; + keyco = params->use_shapekey ? actkey->data : NULL; + if (is_new) { + bm->shapenr = params->active_shapekey; + } } - for (i = 0, block = me->key->block.first; block; block = block->next, i++) { - CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, - CD_ASSIGN, NULL, 0, block->name); - - j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); - bm->vdata.layers[j].uid = block->uid; - + for (i = 0, block = me->key->block.first; i < tot_shape_keys; block = block->next, i++) { + if (is_new) { + CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, + CD_ASSIGN, NULL, 0, block->name); + int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); + bm->vdata.layers[j].uid = block->uid; + } shape_key_table[i] = (const float (*)[3])block->data; } } - CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); - CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); - CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP); - CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE); + if (is_new) { + CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); + CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP); + CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE); - BM_mesh_cd_flag_apply(bm, me->cd_flag); + BM_mesh_cd_flag_apply(bm, me->cd_flag); + } const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); const int cd_shape_key_offset = me->key ? CustomData_get_offset(&bm->vdata, CD_SHAPEKEY) : -1; - const int cd_shape_keyindex_offset = (tot_shape_keys || params->add_key_index) ? + const int cd_shape_keyindex_offset = is_new && (tot_shape_keys || params->add_key_index) ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; + vtable = MEM_mallocN(sizeof(BMVert **) * me->totvert, __func__); + for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) { - v = vtable[i] = BM_vert_create( - bm, keyco && params->use_shapekey ? keyco[i] : mvert->co, NULL, - BM_CREATE_SKIP_CD); + v = vtable[i] = BM_vert_create(bm, keyco ? keyco[i] : mvert->co, NULL, BM_CREATE_SKIP_CD); BM_elem_index_set(v, i); /* set_ok */ /* transfer flag */ @@ -360,13 +369,14 @@ void BM_mesh_bm_from_me( /* set shapekey data */ if (tot_shape_keys) { float (*co_dst)[3] = BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset); - for (j = 0; j < tot_shape_keys; j++, co_dst++) { + for (int j = 0; j < tot_shape_keys; j++, co_dst++) { copy_v3_v3(*co_dst, shape_key_table[j][i]); } } } - - bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */ + if (is_new) { + bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */ + } etable = MEM_mallocN(sizeof(BMEdge **) * me->totedge, __func__); @@ -390,8 +400,14 @@ void BM_mesh_bm_from_me( if (cd_edge_crease_offset != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset, (float)medge->crease / 255.0f); } + if (is_new) { + bm->elem_index_dirty &= ~BM_EDGE; /* added in order, clear dirty flag */ + } - bm->elem_index_dirty &= ~BM_EDGE; /* added in order, clear dirty flag */ + /* only needed for selection. */ + if (me->mselect && me->totselect != 0) { + ftable = MEM_mallocN(sizeof(BMFace **) * me->totpoly, __func__); + } mloop = me->mloop; mp = me->mpoly; @@ -401,6 +417,9 @@ void BM_mesh_bm_from_me( f = bm_face_create_from_mpoly(mp, mloop + mp->loopstart, bm, vtable, etable); + if (ftable != NULL) { + ftable[i] = f; + } if (UNLIKELY(f == NULL)) { printf("%s: Warning! Bad face in mesh" @@ -423,7 +442,7 @@ void BM_mesh_bm_from_me( f->mat_nr = mp->mat_nr; if (i == me->act_face) bm->act_face = f; - j = mp->loopstart; + int j = mp->loopstart; l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { /* don't use 'j' since we may have skipped some faces, hence some loops. */ @@ -440,8 +459,9 @@ void BM_mesh_bm_from_me( BM_face_normal_update(f); } } - - bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); /* added in order, clear dirty flag */ + if (is_new) { + bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); /* added in order, clear dirty flag */ + } /* -------------------------------------------------------------------- */ /* MSelect clears the array elements (avoid adding multiple times). @@ -450,11 +470,6 @@ void BM_mesh_bm_from_me( */ if (me->mselect && me->totselect != 0) { - if (ftable == NULL) { - ftable = MEM_mallocN(sizeof(BMFace **) * bm->totface, __func__); - BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)ftable, bm->totface); - } - MSelect *msel; for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) { BMElem **ele_p; From b07dcb8fb06f622c8521878df6cbef01d98d6cc4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 27 Aug 2017 03:48:18 +1000 Subject: [PATCH 2/5] Missed last commit --- source/blender/bmesh/intern/bmesh_mesh_conv.c | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.c b/source/blender/bmesh/intern/bmesh_mesh_conv.c index 2edc043cb13..7787d704b59 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_conv.c +++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c @@ -219,6 +219,11 @@ static BMFace *bm_face_create_from_mpoly( /** * \brief Mesh -> BMesh + * \param bm: The mesh to write into, while this is typically a newly created BMesh, + * merging into existing data is supported. + * Note the custom-data layout isn't used. + * If more comprehensive merging is needed we should move this into a separate function + * since this should be kept fast for edit-mode switching and storing undo steps. * * \warning This function doesn't calculate face normals. */ @@ -240,8 +245,6 @@ void BM_mesh_bm_from_me( float (*keyco)[3] = NULL; int totuv, totloops, i; - /* free custom data */ - if (!me || !me->totvert) { if (me && is_new) { /*no verts? still copy customdata layout*/ CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_ASSIGN, 0); @@ -293,18 +296,20 @@ void BM_mesh_bm_from_me( } if (tot_shape_keys) { - /* check if we need to generate unique ids for the shapekeys. - * this also exists in the file reading code, but is here for - * a sanity check */ - if (!me->key->uidgen) { - fprintf(stderr, - "%s had to generate shape key uid's in a situation we shouldn't need to! " - "(bmesh internal error)\n", - __func__); + if (is_new) { + /* check if we need to generate unique ids for the shapekeys. + * this also exists in the file reading code, but is here for + * a sanity check */ + if (!me->key->uidgen) { + fprintf(stderr, + "%s had to generate shape key uid's in a situation we shouldn't need to! " + "(bmesh internal error)\n", + __func__); - me->key->uidgen = 1; - for (block = me->key->block.first; block; block = block->next) { - block->uid = me->key->uidgen++; + me->key->uidgen = 1; + for (block = me->key->block.first; block; block = block->next) { + block->uid = me->key->uidgen++; + } } } From 6178cf83537b5b1265867b7bd0d0e0c487a8937a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 27 Aug 2017 15:19:25 +1000 Subject: [PATCH 3/5] Cleanup: use stubs for eigen gtest --- tests/gtests/blenlib/BLI_kdopbvh_test.cc | 2 ++ tests/gtests/blenlib/BLI_math_geom_test.cc | 2 ++ tests/gtests/blenlib/BLI_polyfill2d_test.cc | 2 ++ tests/gtests/blenlib/CMakeLists.txt | 29 ++++++++++++--------- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/tests/gtests/blenlib/BLI_kdopbvh_test.cc b/tests/gtests/blenlib/BLI_kdopbvh_test.cc index e713ddc1695..74db7cf20a0 100644 --- a/tests/gtests/blenlib/BLI_kdopbvh_test.cc +++ b/tests/gtests/blenlib/BLI_kdopbvh_test.cc @@ -12,6 +12,8 @@ extern "C" { #include "MEM_guardedalloc.h" } +#include "stubs/bf_intern_eigen_stubs.h" + /* -------------------------------------------------------------------- */ /* Helper Functions */ diff --git a/tests/gtests/blenlib/BLI_math_geom_test.cc b/tests/gtests/blenlib/BLI_math_geom_test.cc index cd15a4eb8ff..92e2532392e 100644 --- a/tests/gtests/blenlib/BLI_math_geom_test.cc +++ b/tests/gtests/blenlib/BLI_math_geom_test.cc @@ -4,6 +4,8 @@ #include "BLI_math.h" +#include "stubs/bf_intern_eigen_stubs.h" + TEST(math_geom, DistToLine2DSimple) { float p[2] = {5.0f, 1.0f}, diff --git a/tests/gtests/blenlib/BLI_polyfill2d_test.cc b/tests/gtests/blenlib/BLI_polyfill2d_test.cc index 5d112751fa0..eefcdd6e85e 100644 --- a/tests/gtests/blenlib/BLI_polyfill2d_test.cc +++ b/tests/gtests/blenlib/BLI_polyfill2d_test.cc @@ -27,6 +27,8 @@ extern "C" { #endif } +#include "stubs/bf_intern_eigen_stubs.h" + static void polyfill_to_obj( const char *id, const float poly[][2], const unsigned int poly_tot, diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt index 8b013e7a7a6..ffdb8d08d31 100644 --- a/tests/gtests/blenlib/CMakeLists.txt +++ b/tests/gtests/blenlib/CMakeLists.txt @@ -34,24 +34,27 @@ include_directories(${INC}) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}") set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}") +if(WIN32) + set(BLI_path_util_extra_libs "bf_blenlib;bf_intern_utfconv;extern_wcwidth;${ZLIB_LIBRARIES}") +else() + set(BLI_path_util_extra_libs "bf_blenlib;extern_wcwidth;${ZLIB_LIBRARIES}") +endif() BLENDER_TEST(BLI_array_store "bf_blenlib") BLENDER_TEST(BLI_array_utils "bf_blenlib") -BLENDER_TEST(BLI_kdopbvh "bf_blenlib;bf_intern_eigen") -BLENDER_TEST(BLI_stack "bf_blenlib") -BLENDER_TEST(BLI_math_color "bf_blenlib") -BLENDER_TEST(BLI_math_geom "bf_blenlib;bf_intern_eigen") +BLENDER_TEST(BLI_ghash "bf_blenlib") +BLENDER_TEST(BLI_hash_mm2a "bf_blenlib") +BLENDER_TEST(BLI_kdopbvh "bf_blenlib") +BLENDER_TEST(BLI_listbase "bf_blenlib") BLENDER_TEST(BLI_math_base "bf_blenlib") +BLENDER_TEST(BLI_math_color "bf_blenlib") +BLENDER_TEST(BLI_math_geom "bf_blenlib") +BLENDER_TEST(BLI_path_util "${BLI_path_util_extra_libs}") +BLENDER_TEST(BLI_polyfill2d "bf_blenlib") +BLENDER_TEST(BLI_stack "bf_blenlib") BLENDER_TEST(BLI_string "bf_blenlib") BLENDER_TEST(BLI_string_utf8 "bf_blenlib") -if(WIN32) - BLENDER_TEST(BLI_path_util "bf_blenlib;bf_intern_utfconv;extern_wcwidth;${ZLIB_LIBRARIES}") -else() - BLENDER_TEST(BLI_path_util "bf_blenlib;extern_wcwidth;${ZLIB_LIBRARIES}") -endif() -BLENDER_TEST(BLI_polyfill2d "bf_blenlib;bf_intern_eigen") -BLENDER_TEST(BLI_listbase "bf_blenlib") -BLENDER_TEST(BLI_hash_mm2a "bf_blenlib") -BLENDER_TEST(BLI_ghash "bf_blenlib") BLENDER_TEST_PERFORMANCE(BLI_ghash_performance "bf_blenlib") + +unset(BLI_path_util_extra_libs) From b1f2b69884785543190bb4a9377a6f277be290da Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 27 Aug 2017 15:24:41 +1000 Subject: [PATCH 4/5] Missing from last commit --- .../blenlib/stubs/bf_intern_eigen_stubs.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/gtests/blenlib/stubs/bf_intern_eigen_stubs.h diff --git a/tests/gtests/blenlib/stubs/bf_intern_eigen_stubs.h b/tests/gtests/blenlib/stubs/bf_intern_eigen_stubs.h new file mode 100644 index 00000000000..dad77a257d5 --- /dev/null +++ b/tests/gtests/blenlib/stubs/bf_intern_eigen_stubs.h @@ -0,0 +1,18 @@ +/* Apache License, Version 2.0 */ + +extern "C" { + +bool EIG_self_adjoint_eigen_solve(const int size, const float *matrix, float *r_eigen_values, float *r_eigen_vectors) +{ + BLI_assert(0); + UNUSED_VARS(size, matrix, r_eigen_values, r_eigen_vectors); + return false; +} + +void EIG_svd_square_matrix(const int size, const float *matrix, float *r_U, float *r_S, float *r_V) +{ + BLI_assert(0); + UNUSED_VARS(size, matrix, r_U, r_S, r_V); +} + +} From c90452e111abae7817b02ad848b358eb77a44252 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 27 Aug 2017 16:01:06 +1000 Subject: [PATCH 5/5] BLI_rect: Function to calculate a matrix from 2 rctf's --- source/blender/blenlib/BLI_rect.h | 2 ++ source/blender/blenlib/intern/rct.c | 38 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/source/blender/blenlib/BLI_rect.h b/source/blender/blenlib/BLI_rect.h index 21b9c75ac35..471d875c9af 100644 --- a/source/blender/blenlib/BLI_rect.h +++ b/source/blender/blenlib/BLI_rect.h @@ -55,6 +55,8 @@ void BLI_rcti_do_minmax_v(struct rcti *rect, const int xy[2]); void BLI_rctf_do_minmax_v(struct rctf *rect, const float xy[2]); void BLI_rctf_transform_pt_v(const rctf *dst, const rctf *src, float xy_dst[2], const float xy_src[2]); +void BLI_rctf_transform_calc_m4_pivot_min_ex(const rctf *dst, const rctf *src, float matrix[4][4], uint x, uint y); +void BLI_rctf_transform_calc_m4_pivot_min(const rctf *dst, const rctf *src, float matrix[4][4]); void BLI_rctf_translate(struct rctf *rect, float x, float y); void BLI_rcti_translate(struct rcti *rect, int x, int y); diff --git a/source/blender/blenlib/intern/rct.c b/source/blender/blenlib/intern/rct.c index e0c4cbe9990..3adc6b30f6e 100644 --- a/source/blender/blenlib/intern/rct.c +++ b/source/blender/blenlib/intern/rct.c @@ -32,6 +32,7 @@ * A minimalist lib for functions doing stuff with rectangle structs. */ +#include #include #include @@ -41,6 +42,9 @@ #include "DNA_vec_types.h" #include "BLI_rect.h" +/* avoid including BLI_math */ +static void unit_m4(float m[4][4]); + /** * Determine if a rect is empty. An empty * rect is one with a zero (or negative) @@ -405,6 +409,31 @@ void BLI_rctf_transform_pt_v(const rctf *dst, const rctf *src, float xy_dst[2], xy_dst[1] = dst->ymin + ((dst->ymax - dst->ymin) * xy_dst[1]); } +/** + * Calculate a 4x4 matrix representing the transformation between two rectangles. + * + * \note Multiplying a vector by this matrix does *not* give the same value as #BLI_rctf_transform_pt_v. + */ +void BLI_rctf_transform_calc_m4_pivot_min_ex( + const rctf *dst, const rctf *src, float matrix[4][4], + uint x, uint y) +{ + BLI_assert(x < 3 && y < 3); + + unit_m4(matrix); + + matrix[x][x] = BLI_rctf_size_x(src) / BLI_rctf_size_x(dst); + matrix[y][y] = BLI_rctf_size_y(src) / BLI_rctf_size_y(dst); + matrix[3][x] = (src->xmin - dst->xmin) * matrix[x][x]; + matrix[3][y] = (src->ymin - dst->ymin) * matrix[y][y]; +} + +void BLI_rctf_transform_calc_m4_pivot_min( + const rctf *dst, const rctf *src, float matrix[4][4]) +{ + BLI_rctf_transform_calc_m4_pivot_min_ex(dst, src, matrix, 0, 1); +} + void BLI_rcti_translate(rcti *rect, int x, int y) { rect->xmin += x; @@ -763,3 +792,12 @@ void BLI_rctf_rotate_expand(rctf *dst, const rctf *src, const float angle) #undef ROTATE_SINCOS /** \} */ + +static void unit_m4(float m[4][4]) +{ + m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f; + m[0][1] = m[0][2] = m[0][3] = 0.0f; + m[1][0] = m[1][2] = m[1][3] = 0.0f; + m[2][0] = m[2][1] = m[2][3] = 0.0f; + m[3][0] = m[3][1] = m[3][2] = 0.0f; +}