/* turns Mesh into editmesh */ void make_editMesh(Scene *scene, Object *ob) { Mesh *me= ob->data; MFace *mface; MVert *mvert; MSelect *mselect; KeyBlock *actkey; EditMesh *em; EditVert *eve, **evlist, *eve1, *eve2, *eve3, *eve4; EditFace *efa, *efa_last_sel= NULL; EditEdge *eed; EditSelection *ese; float *co, (*keyco)[3]= NULL; int tot, a, eekadoodle= 0; const short is_paint_face_sel= paint_facesel_test(ob); const short is_paint_vert_sel= is_paint_face_sel ? FALSE : paint_vertsel_test(ob); if(me->edit_mesh==NULL) me->edit_mesh= MEM_callocN(sizeof(EditMesh), "editmesh"); else /* because of reload */ free_editMesh(me->edit_mesh); em= me->edit_mesh; em->selectmode= scene->toolsettings->selectmode; // warning needs to be synced em->act_face = NULL; em->totvert= tot= me->totvert; em->totedge= me->totedge; em->totface= me->totface; if(tot==0) { return; } if(ob->actcol > 0) em->mat_nr= ob->actcol-1; /* initialize fastmalloc for editmesh */ init_editmesh_fastmalloc(em, me->totvert, me->totedge, me->totface); actkey = ob_get_keyblock(ob); if(actkey) { /* undo-ing in past for previous editmode sessions gives corrupt 'keyindex' values */ undo_editmode_clear(); keyco= actkey->data; em->shapenr= ob->shapenr; } /* make editverts */ CustomData_copy(&me->vdata, &em->vdata, CD_MASK_EDITMESH, CD_CALLOC, 0); mvert= me->mvert; evlist= (EditVert **)MEM_mallocN(tot*sizeof(void *),"evlist"); for(a=0; a<tot; a++, mvert++) { co= mvert->co; /* edit the shape key coordinate if available */ if(keyco && a < actkey->totelem) co= keyco[a]; eve= addvertlist(em, co, NULL); evlist[a]= eve; /* face select sets selection in next loop */ if(!is_paint_face_sel) eve->f |= (mvert->flag & SELECT); if (mvert->flag & ME_HIDE) eve->h= 1; normal_short_to_float_v3(eve->no, mvert->no); eve->bweight= ((float)mvert->bweight)/255.0f; /* lets overwrite the keyindex of the editvert * with the order it used to be in before * editmode */ eve->keyindex = a; CustomData_to_em_block(&me->vdata, &em->vdata, a, &eve->data); } if(actkey && actkey->totelem!=me->totvert); else { MEdge *medge= me->medge; CustomData_copy(&me->edata, &em->edata, CD_MASK_EDITMESH, CD_CALLOC, 0); /* make edges */ for(a=0; a<me->totedge; a++, medge++) { eed= addedgelist(em, evlist[medge->v1], evlist[medge->v2], NULL); /* eed can be zero when v1 and v2 are identical, dxf import does this... */ if(eed) { int is_sel; if (is_paint_vert_sel) { /* when from vertex select, flush flags to edges, * allow selection, code below handles editmode selection conversion */ is_sel= (eed->v1->f & SELECT) && (eed->v2->f & SELECT); } else { is_sel= (medge->flag & SELECT); } eed->crease= ((float)medge->crease)/255.0f; eed->bweight= ((float)medge->bweight)/255.0f; if(medge->flag & ME_SEAM) eed->seam= 1; if(medge->flag & ME_SHARP) eed->sharp = 1; if(medge->flag & ME_FGON) eed->h= EM_FGON; // 2 different defines! if(medge->flag & ME_HIDE) eed->h |= 1; if(is_sel) eed->f |= SELECT; if(em->selectmode==SCE_SELECT_EDGE) EM_select_edge(eed, eed->f & SELECT); // force edge selection to vertices, seems to be needed ... CustomData_to_em_block(&me->edata,&em->edata, a, &eed->data); } } CustomData_copy(&me->fdata, &em->fdata, CD_MASK_EDITMESH, CD_CALLOC, 0); /* make faces */ mface= me->mface; for(a=0; a<me->totface; a++, mface++) { eve1= evlist[mface->v1]; eve2= evlist[mface->v2]; if(!mface->v3) eekadoodle= 1; eve3= evlist[mface->v3]; if(mface->v4) eve4= evlist[mface->v4]; else eve4= NULL; efa= addfacelist(em, eve1, eve2, eve3, eve4, NULL, NULL); if(efa) { CustomData_to_em_block(&me->fdata, &em->fdata, a, &efa->data); efa->mat_nr= mface->mat_nr; efa->flag= mface->flag & ~ME_HIDE; /* select and hide face flag */ if(mface->flag & ME_HIDE) { efa->h= 1; } else { int is_sel; if (!is_paint_vert_sel) { is_sel= (mface->flag & ME_FACE_SEL); } else { /* when from vertex select, flush flags to edges, * allow selection, code below handles editmode selection conversion */ is_sel= ( (efa->v1->f & SELECT) && (efa->v2->f & SELECT) && (efa->v3->f & SELECT) && (efa->v4 == NULL || efa->v4->f & SELECT) ); } if (a==me->act_face) { EM_set_actFace(em, efa); } /* dont allow hidden and selected */ if(is_sel) { efa->f |= SELECT; if(is_paint_face_sel) { EM_select_face(efa, 1); /* flush down */ } efa_last_sel= efa; } } } } } if(EM_get_actFace(em, 0)==NULL && efa_last_sel) { EM_set_actFace(em, efa_last_sel); } if(eekadoodle) error("This Mesh has old style edgecodes, please put it in the bugtracker!"); MEM_freeN(evlist); end_editmesh_fastmalloc(); // resets global function pointers if(me->mselect){ //restore editselections EM_init_index_arrays(em, 1,1,1); mselect = me->mselect; for(a=0; a<me->totselect; a++, mselect++){ /*check if recorded selection is still valid, if so copy into editmesh*/ if ( (mselect->type == EDITVERT && me->mvert[mselect->index].flag & SELECT) || (mselect->type == EDITEDGE && me->medge[mselect->index].flag & SELECT) || (mselect->type == EDITFACE && me->mface[mselect->index].flag & ME_FACE_SEL) ) { ese = MEM_callocN(sizeof(EditSelection), "Edit Selection"); ese->type = mselect->type; if(ese->type == EDITVERT) ese->data = EM_get_vert_for_index(mselect->index); else if(ese->type == EDITEDGE) ese->data = EM_get_edge_for_index(mselect->index); else if(ese->type == EDITFACE) ese->data = EM_get_face_for_index(mselect->index); BLI_addtail(&(em->selected),ese); } } EM_free_index_arrays(); } /* this creates coherent selections. also needed for older files */ EM_selectmode_set(em); /* paranoia check to enforce hide rules */ EM_hide_reset(em); /* sets helper flags which arent saved */ EM_fgon_flags(em); if (EM_get_actFace(em, 0)==NULL) { EM_set_actFace(em, em->faces.first ); /* will use the first face, this is so we alwats have an active face */ } }
/** * This function returns the coordinate and normal of a barycentric u,v for a face defined by the primitive_id index. * The returned coordinate is extruded along the normal by cage_extrusion */ static void calc_point_from_barycentric_extrusion( TriTessFace *triangles, float mat[4][4], float imat[4][4], int primitive_id, float u, float v, float cage_extrusion, float r_co[3], float r_dir[3], const bool is_cage) { float data[3][3]; float coord[3]; float dir[3]; float cage[3]; bool is_smooth; TriTessFace *triangle = &triangles[primitive_id]; is_smooth = triangle->is_smooth || is_cage; copy_v3_v3(data[0], triangle->mverts[0]->co); copy_v3_v3(data[1], triangle->mverts[1]->co); copy_v3_v3(data[2], triangle->mverts[2]->co); interp_barycentric_tri_v3(data, u, v, coord); if (is_smooth) { normal_short_to_float_v3(data[0], triangle->mverts[0]->no); normal_short_to_float_v3(data[1], triangle->mverts[1]->no); normal_short_to_float_v3(data[2], triangle->mverts[2]->no); interp_barycentric_tri_v3(data, u, v, dir); normalize_v3(dir); } else { copy_v3_v3(dir, triangle->normal); } mul_v3_v3fl(cage, dir, cage_extrusion); add_v3_v3(coord, cage); normalize_v3(dir); negate_v3(dir); /* convert from local to world space */ mul_m4_v3(mat, coord); mul_transposed_mat3_m4_v3(imat, dir); normalize_v3(dir); copy_v3_v3(r_co, coord); copy_v3_v3(r_dir, dir); }
static void multiresbake_get_normal(const MResolvePixelData *data, float norm[], const int face_num, const int vert_index) { unsigned int indices[]= {data->mface[face_num].v1, data->mface[face_num].v2, data->mface[face_num].v3, data->mface[face_num].v4}; const int smoothnormal= (data->mface[face_num].flag & ME_SMOOTH); if(!smoothnormal) { /* flat */ if(data->precomputed_normals) { copy_v3_v3(norm, &data->precomputed_normals[3*face_num]); } else { float nor[3]; float *p0, *p1, *p2; const int iGetNrVerts= data->mface[face_num].v4!=0 ? 4 : 3; p0= data->mvert[indices[0]].co; p1= data->mvert[indices[1]].co; p2= data->mvert[indices[2]].co; if(iGetNrVerts==4) { float *p3= data->mvert[indices[3]].co; normal_quad_v3(nor, p0, p1, p2, p3); } else { normal_tri_v3(nor, p0, p1, p2); } copy_v3_v3(norm, nor); } } else { short *no= data->mvert[indices[vert_index]].no; normal_short_to_float_v3(norm, no); normalize_v3(norm); } }
static void GPU_buffer_copy_normal(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *UNUSED(user)) { int i, totface; int start; float f_no[3]; float *nors= dm->getFaceDataArray(dm, CD_NORMAL); MVert *mvert = dm->getVertArray(dm); MFace *f = dm->getFaceArray(dm); totface= dm->getNumFaces(dm); for(i = 0; i < totface; i++, f++) { const int smoothnormal = (f->flag & ME_SMOOTH); start = index[mat_orig_to_new[f->mat_nr]]; index[mat_orig_to_new[f->mat_nr]] += f->v4 ? 18 : 9; if(smoothnormal) { /* copy vertex normal */ normal_short_to_float_v3(&varray[start], mvert[f->v1].no); normal_short_to_float_v3(&varray[start+3], mvert[f->v2].no); normal_short_to_float_v3(&varray[start+6], mvert[f->v3].no); if(f->v4) { normal_short_to_float_v3(&varray[start+9], mvert[f->v3].no); normal_short_to_float_v3(&varray[start+12], mvert[f->v4].no); normal_short_to_float_v3(&varray[start+15], mvert[f->v1].no); } } else if(nors) { /* copy cached face normal */ copy_v3_v3(&varray[start], &nors[i*3]); copy_v3_v3(&varray[start+3], &nors[i*3]); copy_v3_v3(&varray[start+6], &nors[i*3]); if(f->v4) { copy_v3_v3(&varray[start+9], &nors[i*3]); copy_v3_v3(&varray[start+12], &nors[i*3]); copy_v3_v3(&varray[start+15], &nors[i*3]); } } else { /* calculate face normal */ if(f->v4) normal_quad_v3(f_no, mvert[f->v1].co, mvert[f->v2].co, mvert[f->v3].co, mvert[f->v4].co); else normal_tri_v3(f_no, mvert[f->v1].co, mvert[f->v2].co, mvert[f->v3].co); copy_v3_v3(&varray[start], f_no); copy_v3_v3(&varray[start+3], f_no); copy_v3_v3(&varray[start+6], f_no); if(f->v4) { copy_v3_v3(&varray[start+9], f_no); copy_v3_v3(&varray[start+12], f_no); copy_v3_v3(&varray[start+15], f_no); } } } }
static void vpaint_proj_dm_map_cosnos_update__map_cb( void *userData, int index, const float co[3], const float no_f[3], const short no_s[3]) { struct VertProjUpdate *vp_update = userData; struct VertProjHandle *vp_handle = vp_update->vp_handle; DMCoNo *co_no = &vp_handle->vcosnos[index]; /* find closest vertex */ { /* first find distance to this vertex */ float co_ss[2]; /* screenspace */ if (ED_view3d_project_float_object( vp_update->ar, co, co_ss, V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) { const float dist_sq = len_squared_v2v2(vp_update->mval_fl, co_ss); if (dist_sq > vp_handle->dists_sq[index]) { /* bail out! */ return; } vp_handle->dists_sq[index] = dist_sq; } else if (vp_handle->dists_sq[index] != FLT_MAX) { /* already initialized & couldn't project this 'co' */ return; } } /* continue with regular functionality */ copy_v3_v3(co_no->co, co); if (no_f) { copy_v3_v3(co_no->no, no_f); } else { normal_short_to_float_v3(co_no->no, no_s); } }
bool BL_SkinDeformer::UpdateInternal(bool shape_applied) { /* See if the armature has been updated for this frame */ if (PoseUpdated()) { if (!shape_applied) { /* store verts locally */ VerifyStorage(); /* duplicate */ for (int v =0; v<m_bmesh->totvert; v++) { copy_v3_v3(m_transverts[v], m_bmesh->mvert[v].co); normal_short_to_float_v3(m_transnors[v], m_bmesh->mvert[v].no); } } m_armobj->ApplyPose(); switch (m_armobj->GetVertDeformType()) { case ARM_VDEF_BGE_CPU: BGEDeformVerts(); break; case ARM_VDEF_BLENDER: default: BlenderDeformVerts(); } /* Update the current frame */ m_lastArmaUpdate=m_armobj->GetLastFrame(); m_armobj->RestorePose(); /* dynamic vertex, cannot use display list */ m_bDynamic = true; /* indicate that the m_transverts and normals are up to date */ return true; } return false; }
static void vpaint_proj_dm_map_cosnos_init__map_cb( void *userData, int index, const float co[3], const float no_f[3], const short no_s[3]) { struct VertProjHandle *vp_handle = userData; CoNo *co_no = &vp_handle->vcosnos[index]; /* check if we've been here before (normal should not be 0) */ if (!is_zero_v3(co_no->no)) { /* remember that multiple dm verts share the same source vert */ vp_handle->use_update = true; return; } copy_v3_v3(co_no->co, co); if (no_f) { copy_v3_v3(co_no->no, no_f); } else { normal_short_to_float_v3(co_no->no, no_s); } }
static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for_render) { int i; /* Options about projection direction */ const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit; float proj_axis[3] = {0.0f, 0.0f, 0.0f}; /* Raycast and tree stuff */ /** \note 'hit.dist' is kept in the targets space, this is only used * for finding the best hit, to get the real dist, * measure the len_v3v3() from the input coord to hit.co */ BVHTreeRayHit hit; BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; /* auxiliary target */ DerivedMesh *auxMesh = NULL; BVHTreeFromMesh auxData = NULL_BVHTreeFromMesh; SpaceTransform local2aux; /* If the user doesn't allows to project in any direction of projection axis * then theres nothing todo. */ if ((calc->smd->shrinkOpts & (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 0) return; /* Prepare data to retrieve the direction in which we should project each vertex */ if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { if (calc->vert == NULL) return; } else { /* The code supports any axis that is a combination of X,Y,Z * although currently UI only allows to set the 3 different axis */ if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) proj_axis[0] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) proj_axis[1] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) proj_axis[2] = 1.0f; normalize_v3(proj_axis); /* Invalid projection direction */ if (len_squared_v3(proj_axis) < FLT_EPSILON) { return; } } if (calc->smd->auxTarget) { auxMesh = object_get_derived_final(calc->smd->auxTarget, for_render); if (!auxMesh) return; SPACE_TRANSFORM_SETUP(&local2aux, calc->ob, calc->smd->auxTarget); } /* After sucessufuly build the trees, start projection vertexs */ if (bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 4, 6) && (auxMesh == NULL || bvhtree_from_mesh_faces(&auxData, auxMesh, 0.0, 4, 6))) { #ifndef __APPLE__ #pragma omp parallel for private(i, hit) schedule(static) #endif for (i = 0; i < calc->numVerts; ++i) { float *co = calc->vertexCos[i]; float tmp_co[3], tmp_no[3]; const float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); if (weight == 0.0f) { continue; } if (calc->vert) { /* calc->vert contains verts from derivedMesh */ /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { copy_v3_v3(tmp_co, calc->vert[i].co); normal_short_to_float_v3(tmp_no, calc->vert[i].no); } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } hit.index = -1; hit.dist = 10000.0f; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ /* Project over positive direction of axis */ if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { if (auxData.tree) { BKE_shrinkwrap_project_normal(0, tmp_co, tmp_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); } BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, tmp_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } /* Project over negative direction of axis */ if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) { float inv_no[3]; negate_v3_v3(inv_no, tmp_no); if (auxData.tree) { BKE_shrinkwrap_project_normal(0, tmp_co, inv_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); } BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, inv_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } /* don't set the initial dist (which is more efficient), * because its calculated in the targets space, we want the dist in our own space */ if (proj_limit_squared != 0.0f) { if (len_squared_v3v3(hit.co, co) > proj_limit_squared) { hit.index = -1; } } if (hit.index != -1) { madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist); interp_v3_v3v3(co, co, hit.co, weight); } } } /* free data structures */ free_bvhtree_from_mesh(&treeData); free_bvhtree_from_mesh(&auxData); }
/** * \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. */ 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; MPoly *mp; KeyBlock *actkey, *block; BMVert *v, **vtable = NULL; BMEdge *e, **etable = NULL; BMFace *f, **ftable = NULL; float (*keyco)[3] = NULL; int totuv, totloops, i; 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); CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_ASSIGN, 0); CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_ASSIGN, 0); CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_ASSIGN, 0); 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); } return; /* sanity check */ } 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); /* 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); } else { actkey = NULL; } 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) { 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++; } } } if (actkey && actkey->totelem == me->totvert) { keyco = params->use_shapekey ? actkey->data : NULL; if (is_new) { bm->shapenr = params->active_shapekey; } } 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; } } 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); } 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 = 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 ? keyco[i] : mvert->co, NULL, BM_CREATE_SKIP_CD); BM_elem_index_set(v, i); /* set_ok */ /* transfer flag */ v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT); /* this is necessary for selection counts to work properly */ if (mvert->flag & SELECT) { BM_vert_select_set(bm, v, true); } normal_short_to_float_v3(v->no, mvert->no); /* Copy Custom Data */ CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true); if (cd_vert_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert->bweight / 255.0f); /* set shape key original index */ if (cd_shape_keyindex_offset != -1) BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, i); /* set shapekey data */ if (tot_shape_keys) { float (*co_dst)[3] = BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset); for (int j = 0; j < tot_shape_keys; j++, co_dst++) { copy_v3_v3(*co_dst, shape_key_table[j][i]); } } } if (is_new) { bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */ } etable = MEM_mallocN(sizeof(BMEdge **) * me->totedge, __func__); medge = me->medge; for (i = 0; i < me->totedge; i++, medge++) { e = etable[i] = BM_edge_create(bm, vtable[medge->v1], vtable[medge->v2], NULL, BM_CREATE_SKIP_CD); BM_elem_index_set(e, i); /* set_ok */ /* transfer flags */ e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT); /* this is necessary for selection counts to work properly */ if (medge->flag & SELECT) { BM_edge_select_set(bm, e, true); } /* Copy Custom Data */ CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data, true); if (cd_edge_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge->bweight / 255.0f); 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 */ } /* only needed for selection. */ if (me->mselect && me->totselect != 0) { ftable = MEM_mallocN(sizeof(BMFace **) * me->totpoly, __func__); } mloop = me->mloop; mp = me->mpoly; for (i = 0, totloops = 0; i < me->totpoly; i++, mp++) { BMLoop *l_iter; BMLoop *l_first; 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" " \"%s\" at index %d!, skipping\n", __func__, me->id.name + 2, i); continue; } /* don't use 'i' since we may have skipped the face */ BM_elem_index_set(f, bm->totface - 1); /* set_ok */ /* transfer flag */ f->head.hflag = BM_face_flag_from_mflag(mp->flag & ~ME_FACE_SEL); /* this is necessary for selection counts to work properly */ if (mp->flag & ME_FACE_SEL) { BM_face_select_set(bm, f, true); } f->mat_nr = mp->mat_nr; if (i == me->act_face) bm->act_face = f; 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. */ BM_elem_index_set(l_iter, totloops++); /* set_ok */ /* Save index of correspsonding MLoop */ CustomData_to_bmesh_block(&me->ldata, &bm->ldata, j++, &l_iter->head.data, true); } while ((l_iter = l_iter->next) != l_first); /* Copy Custom Data */ CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data, true); if (params->calc_face_normal) { BM_face_normal_update(f); } } 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). * * Take care to keep this last and not use (v/e/ftable) after this. */ if (me->mselect && me->totselect != 0) { MSelect *msel; for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) { BMElem **ele_p; switch (msel->type) { case ME_VSEL: ele_p = (BMElem **)&vtable[msel->index]; break; case ME_ESEL: ele_p = (BMElem **)&etable[msel->index]; break; case ME_FSEL: ele_p = (BMElem **)&ftable[msel->index]; break; default: continue; } if (*ele_p != NULL) { BM_select_history_store_notest(bm, *ele_p); *ele_p = NULL; } } } else { BM_select_history_clear(bm); } MEM_freeN(vtable); MEM_freeN(etable); if (ftable) { MEM_freeN(ftable); } }
/** * \brief Mesh -> BMesh * * \warning This function doesn't calculate face normals. */ void BM_mesh_bm_from_me(BMesh *bm, Mesh *me, const bool calc_face_normal, const bool set_key, int act_key_nr) { MVert *mvert; MEdge *medge; MLoop *mloop; MPoly *mp; KeyBlock *actkey, *block; BMVert *v, **vtable = NULL; BMEdge *e, **etable = NULL; BMFace *f; float (*keyco)[3] = NULL; int *keyi; int totuv, i, j; int cd_vert_bweight_offset; int cd_edge_bweight_offset; int cd_edge_crease_offset; /* 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*/ 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); CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_ASSIGN, 0); 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); } return; /* sanity check */ } vtable = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable"); 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); } if ((act_key_nr != 0) && (me->key != NULL)) { actkey = BLI_findlink(&me->key->block, act_key_nr - 1); } else { actkey = NULL; } if (me->key) { CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); /* 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++; } } if (actkey && actkey->totelem == me->totvert) { keyco = actkey->data; bm->shapenr = act_key_nr; } 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; } } 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); cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) { v = vtable[i] = BM_vert_create(bm, keyco && set_key ? keyco[i] : mvert->co, NULL, BM_CREATE_SKIP_CD); BM_elem_index_set(v, i); /* set_ok */ /* transfer flag */ v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT); /* this is necessary for selection counts to work properly */ if (mvert->flag & SELECT) { BM_vert_select_set(bm, v, true); } normal_short_to_float_v3(v->no, mvert->no); /* Copy Custom Data */ CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true); if (cd_vert_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert->bweight / 255.0f); /* set shapekey data */ if (me->key) { /* set shape key original index */ keyi = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_SHAPE_KEYINDEX); if (keyi) { *keyi = i; } for (block = me->key->block.first, j = 0; block; block = block->next, j++) { float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j); if (co) { copy_v3_v3(co, ((float *)block->data) + 3 * i); } } } } bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */ if (!me->totedge) { MEM_freeN(vtable); return; } etable = MEM_mallocN(sizeof(void **) * me->totedge, "mesh to bmesh etable"); medge = me->medge; for (i = 0; i < me->totedge; i++, medge++) { e = etable[i] = BM_edge_create(bm, vtable[medge->v1], vtable[medge->v2], NULL, BM_CREATE_SKIP_CD); BM_elem_index_set(e, i); /* set_ok */ /* transfer flags */ e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT); /* this is necessary for selection counts to work properly */ if (medge->flag & SELECT) { BM_edge_select_set(bm, e, true); } /* Copy Custom Data */ CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data, true); if (cd_edge_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge->bweight / 255.0f); if (cd_edge_crease_offset != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset, (float)medge->crease / 255.0f); } bm->elem_index_dirty &= ~BM_EDGE; /* added in order, clear dirty flag */ mloop = me->mloop; mp = me->mpoly; for (i = 0; i < me->totpoly; i++, mp++) { BMLoop *l_iter; BMLoop *l_first; f = bm_face_create_from_mpoly(mp, mloop + mp->loopstart, bm, vtable, etable); if (UNLIKELY(f == NULL)) { printf("%s: Warning! Bad face in mesh" " \"%s\" at index %d!, skipping\n", __func__, me->id.name + 2, i); continue; } /* don't use 'i' since we may have skipped the face */ BM_elem_index_set(f, bm->totface - 1); /* set_ok */ /* transfer flag */ f->head.hflag = BM_face_flag_from_mflag(mp->flag & ~ME_FACE_SEL); /* this is necessary for selection counts to work properly */ if (mp->flag & ME_FACE_SEL) { BM_face_select_set(bm, f, true); } f->mat_nr = mp->mat_nr; if (i == me->act_face) bm->act_face = f; j = mp->loopstart; l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { /* Save index of correspsonding MLoop */ CustomData_to_bmesh_block(&me->ldata, &bm->ldata, j++, &l_iter->head.data, true); } while ((l_iter = l_iter->next) != l_first); /* Copy Custom Data */ CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data, true); if (calc_face_normal) { BM_face_normal_update(f); } } bm->elem_index_dirty &= ~BM_FACE; /* added in order, clear dirty flag */ if (me->mselect && me->totselect != 0) { BMVert **vert_array = MEM_mallocN(sizeof(BMVert *) * bm->totvert, "VSelConv"); BMEdge **edge_array = MEM_mallocN(sizeof(BMEdge *) * bm->totedge, "ESelConv"); BMFace **face_array = MEM_mallocN(sizeof(BMFace *) * bm->totface, "FSelConv"); MSelect *msel; #pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT) { #pragma omp section { BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)vert_array, bm->totvert); } #pragma omp section { BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)edge_array, bm->totedge); } #pragma omp section { BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)face_array, bm->totface); } } for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) { switch (msel->type) { case ME_VSEL: BM_select_history_store(bm, (BMElem *)vert_array[msel->index]); break; case ME_ESEL: BM_select_history_store(bm, (BMElem *)edge_array[msel->index]); break; case ME_FSEL: BM_select_history_store(bm, (BMElem *)face_array[msel->index]); break; } } MEM_freeN(vert_array); MEM_freeN(edge_array); MEM_freeN(face_array); } else { me->totselect = 0; if (me->mselect) { MEM_freeN(me->mselect); me->mselect = NULL; } } MEM_freeN(vtable); MEM_freeN(etable); }
static DerivedMesh *arrayModifier_doArray( ArrayModifierData *amd, Scene *scene, Object *ob, DerivedMesh *dm, ModifierApplyFlag flag) { const float eps = 1e-6f; const MVert *src_mvert; MVert *mv, *mv_prev, *result_dm_verts; MEdge *me; MLoop *ml; MPoly *mp; int i, j, c, count; float length = amd->length; /* offset matrix */ float offset[4][4]; float scale[3]; bool offset_has_scale; float current_offset[4][4]; float final_offset[4][4]; int *full_doubles_map = NULL; int tot_doubles; const bool use_merge = (amd->flags & MOD_ARR_MERGE) != 0; const bool use_recalc_normals = (dm->dirty & DM_DIRTY_NORMALS) || use_merge; const bool use_offset_ob = ((amd->offset_type & MOD_ARR_OFF_OBJ) && amd->offset_ob); /* allow pole vertices to be used by many faces */ const bool with_follow = use_offset_ob; int start_cap_nverts = 0, start_cap_nedges = 0, start_cap_npolys = 0, start_cap_nloops = 0; int end_cap_nverts = 0, end_cap_nedges = 0, end_cap_npolys = 0, end_cap_nloops = 0; int result_nverts = 0, result_nedges = 0, result_npolys = 0, result_nloops = 0; int chunk_nverts, chunk_nedges, chunk_nloops, chunk_npolys; int first_chunk_start, first_chunk_nverts, last_chunk_start, last_chunk_nverts; DerivedMesh *result, *start_cap_dm = NULL, *end_cap_dm = NULL; chunk_nverts = dm->getNumVerts(dm); chunk_nedges = dm->getNumEdges(dm); chunk_nloops = dm->getNumLoops(dm); chunk_npolys = dm->getNumPolys(dm); count = amd->count; if (amd->start_cap && amd->start_cap != ob && amd->start_cap->type == OB_MESH) { start_cap_dm = get_dm_for_modifier(amd->start_cap, flag); if (start_cap_dm) { start_cap_nverts = start_cap_dm->getNumVerts(start_cap_dm); start_cap_nedges = start_cap_dm->getNumEdges(start_cap_dm); start_cap_nloops = start_cap_dm->getNumLoops(start_cap_dm); start_cap_npolys = start_cap_dm->getNumPolys(start_cap_dm); } } if (amd->end_cap && amd->end_cap != ob && amd->end_cap->type == OB_MESH) { end_cap_dm = get_dm_for_modifier(amd->end_cap, flag); if (end_cap_dm) { end_cap_nverts = end_cap_dm->getNumVerts(end_cap_dm); end_cap_nedges = end_cap_dm->getNumEdges(end_cap_dm); end_cap_nloops = end_cap_dm->getNumLoops(end_cap_dm); end_cap_npolys = end_cap_dm->getNumPolys(end_cap_dm); } } /* Build up offset array, cumulating all settings options */ unit_m4(offset); src_mvert = dm->getVertArray(dm); if (amd->offset_type & MOD_ARR_OFF_CONST) add_v3_v3v3(offset[3], offset[3], amd->offset); if (amd->offset_type & MOD_ARR_OFF_RELATIVE) { for (j = 0; j < 3; j++) offset[3][j] += amd->scale[j] * vertarray_size(src_mvert, chunk_nverts, j); } if (use_offset_ob) { float obinv[4][4]; float result_mat[4][4]; if (ob) invert_m4_m4(obinv, ob->obmat); else unit_m4(obinv); mul_m4_series(result_mat, offset, obinv, amd->offset_ob->obmat); copy_m4_m4(offset, result_mat); } /* Check if there is some scaling. If scaling, then we will not translate mapping */ mat4_to_size(scale, offset); offset_has_scale = !is_one_v3(scale); if (amd->fit_type == MOD_ARR_FITCURVE && amd->curve_ob) { Curve *cu = amd->curve_ob->data; if (cu) { #ifdef CYCLIC_DEPENDENCY_WORKAROUND if (amd->curve_ob->curve_cache == NULL) { BKE_displist_make_curveTypes(scene, amd->curve_ob, false); } #endif if (amd->curve_ob->curve_cache && amd->curve_ob->curve_cache->path) { float scale = mat4_to_scale(amd->curve_ob->obmat); length = scale * amd->curve_ob->curve_cache->path->totdist; } } } /* calculate the maximum number of copies which will fit within the * prescribed length */ if (amd->fit_type == MOD_ARR_FITLENGTH || amd->fit_type == MOD_ARR_FITCURVE) { float dist = len_v3(offset[3]); if (dist > eps) { /* this gives length = first copy start to last copy end * add a tiny offset for floating point rounding errors */ count = (length + eps) / dist; } else { /* if the offset has no translation, just make one copy */ count = 1; } } if (count < 1) count = 1; /* The number of verts, edges, loops, polys, before eventually merging doubles */ result_nverts = chunk_nverts * count + start_cap_nverts + end_cap_nverts; result_nedges = chunk_nedges * count + start_cap_nedges + end_cap_nedges; result_nloops = chunk_nloops * count + start_cap_nloops + end_cap_nloops; result_npolys = chunk_npolys * count + start_cap_npolys + end_cap_npolys; /* Initialize a result dm */ result = CDDM_from_template(dm, result_nverts, result_nedges, 0, result_nloops, result_npolys); result_dm_verts = CDDM_get_verts(result); if (use_merge) { /* Will need full_doubles_map for handling merge */ full_doubles_map = MEM_mallocN(sizeof(int) * result_nverts, "mod array doubles map"); fill_vn_i(full_doubles_map, result_nverts, -1); } /* copy customdata to original geometry */ DM_copy_vert_data(dm, result, 0, 0, chunk_nverts); DM_copy_edge_data(dm, result, 0, 0, chunk_nedges); DM_copy_loop_data(dm, result, 0, 0, chunk_nloops); DM_copy_poly_data(dm, result, 0, 0, chunk_npolys); /* subsurf for eg wont have mesh data in the * now add mvert/medge/mface layers */ if (!CustomData_has_layer(&dm->vertData, CD_MVERT)) { dm->copyVertArray(dm, result_dm_verts); } if (!CustomData_has_layer(&dm->edgeData, CD_MEDGE)) { dm->copyEdgeArray(dm, CDDM_get_edges(result)); } if (!CustomData_has_layer(&dm->polyData, CD_MPOLY)) { dm->copyLoopArray(dm, CDDM_get_loops(result)); dm->copyPolyArray(dm, CDDM_get_polys(result)); } /* Remember first chunk, in case of cap merge */ first_chunk_start = 0; first_chunk_nverts = chunk_nverts; unit_m4(current_offset); for (c = 1; c < count; c++) { /* copy customdata to new geometry */ DM_copy_vert_data(result, result, 0, c * chunk_nverts, chunk_nverts); DM_copy_edge_data(result, result, 0, c * chunk_nedges, chunk_nedges); DM_copy_loop_data(result, result, 0, c * chunk_nloops, chunk_nloops); DM_copy_poly_data(result, result, 0, c * chunk_npolys, chunk_npolys); mv_prev = result_dm_verts; mv = mv_prev + c * chunk_nverts; /* recalculate cumulative offset here */ mul_m4_m4m4(current_offset, current_offset, offset); /* apply offset to all new verts */ for (i = 0; i < chunk_nverts; i++, mv++, mv_prev++) { mul_m4_v3(current_offset, mv->co); /* We have to correct normals too, if we do not tag them as dirty! */ if (!use_recalc_normals) { float no[3]; normal_short_to_float_v3(no, mv->no); mul_mat3_m4_v3(current_offset, no); normalize_v3(no); normal_float_to_short_v3(mv->no, no); } } /* adjust edge vertex indices */ me = CDDM_get_edges(result) + c * chunk_nedges; for (i = 0; i < chunk_nedges; i++, me++) { me->v1 += c * chunk_nverts; me->v2 += c * chunk_nverts; } mp = CDDM_get_polys(result) + c * chunk_npolys; for (i = 0; i < chunk_npolys; i++, mp++) { mp->loopstart += c * chunk_nloops; } /* adjust loop vertex and edge indices */ ml = CDDM_get_loops(result) + c * chunk_nloops; for (i = 0; i < chunk_nloops; i++, ml++) { ml->v += c * chunk_nverts; ml->e += c * chunk_nedges; } /* Handle merge between chunk n and n-1 */ if (use_merge && (c >= 1)) { if (!offset_has_scale && (c >= 2)) { /* Mapping chunk 3 to chunk 2 is a translation of mapping 2 to 1 * ... that is except if scaling makes the distance grow */ int k; int this_chunk_index = c * chunk_nverts; int prev_chunk_index = (c - 1) * chunk_nverts; for (k = 0; k < chunk_nverts; k++, this_chunk_index++, prev_chunk_index++) { int target = full_doubles_map[prev_chunk_index]; if (target != -1) { target += chunk_nverts; /* translate mapping */ if (full_doubles_map[target] != -1) { if (with_follow) { target = full_doubles_map[target]; } else { /* The rule here is to not follow mapping to chunk N-2, which could be too far * so if target vertex was itself mapped, then this vertex is not mapped */ target = -1; } } } full_doubles_map[this_chunk_index] = target; } } else { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, (c - 1) * chunk_nverts, chunk_nverts, c * chunk_nverts, chunk_nverts, amd->merge_dist, with_follow); } } } last_chunk_start = (count - 1) * chunk_nverts; last_chunk_nverts = chunk_nverts; copy_m4_m4(final_offset, current_offset); if (use_merge && (amd->flags & MOD_ARR_MERGEFINAL) && (count > 1)) { /* Merge first and last copies */ dm_mvert_map_doubles( full_doubles_map, result_dm_verts, last_chunk_start, last_chunk_nverts, first_chunk_start, first_chunk_nverts, amd->merge_dist, with_follow); } /* start capping */ if (start_cap_dm) { float start_offset[4][4]; int start_cap_start = result_nverts - start_cap_nverts - end_cap_nverts; invert_m4_m4(start_offset, offset); dm_merge_transform( result, start_cap_dm, start_offset, result_nverts - start_cap_nverts - end_cap_nverts, result_nedges - start_cap_nedges - end_cap_nedges, result_nloops - start_cap_nloops - end_cap_nloops, result_npolys - start_cap_npolys - end_cap_npolys, start_cap_nverts, start_cap_nedges, start_cap_nloops, start_cap_npolys); /* Identify doubles with first chunk */ if (use_merge) { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, first_chunk_start, first_chunk_nverts, start_cap_start, start_cap_nverts, amd->merge_dist, false); } } if (end_cap_dm) { float end_offset[4][4]; int end_cap_start = result_nverts - end_cap_nverts; mul_m4_m4m4(end_offset, current_offset, offset); dm_merge_transform( result, end_cap_dm, end_offset, result_nverts - end_cap_nverts, result_nedges - end_cap_nedges, result_nloops - end_cap_nloops, result_npolys - end_cap_npolys, end_cap_nverts, end_cap_nedges, end_cap_nloops, end_cap_npolys); /* Identify doubles with last chunk */ if (use_merge) { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, last_chunk_start, last_chunk_nverts, end_cap_start, end_cap_nverts, amd->merge_dist, false); } } /* done capping */ /* Handle merging */ tot_doubles = 0; if (use_merge) { for (i = 0; i < result_nverts; i++) { if (full_doubles_map[i] != -1) { if (i == full_doubles_map[i]) { full_doubles_map[i] = -1; } else { tot_doubles++; } } } if (tot_doubles > 0) { result = CDDM_merge_verts(result, full_doubles_map, tot_doubles, CDDM_MERGE_VERTS_DUMP_IF_EQUAL); } MEM_freeN(full_doubles_map); } /* In case org dm has dirty normals, or we made some merging, mark normals as dirty in new dm! * TODO: we may need to set other dirty flags as well? */ if (use_recalc_normals) { result->dirty |= DM_DIRTY_NORMALS; } return result; }
static DerivedMesh *arrayModifier_doArray( ArrayModifierData *amd, Scene *scene, Object *ob, DerivedMesh *dm, ModifierApplyFlag flag) { const float eps = 1e-6f; const MVert *src_mvert; MVert *mv, *mv_prev, *result_dm_verts; MEdge *me; MLoop *ml; MPoly *mp; int i, j, c, count; float length = amd->length; /* offset matrix */ float offset[4][4]; float scale[3]; bool offset_has_scale; float current_offset[4][4]; float final_offset[4][4]; int *full_doubles_map = NULL; int tot_doubles; const bool use_merge = (amd->flags & MOD_ARR_MERGE) != 0; const bool use_recalc_normals = (dm->dirty & DM_DIRTY_NORMALS) || use_merge; const bool use_offset_ob = ((amd->offset_type & MOD_ARR_OFF_OBJ) && amd->offset_ob); int start_cap_nverts = 0, start_cap_nedges = 0, start_cap_npolys = 0, start_cap_nloops = 0; int end_cap_nverts = 0, end_cap_nedges = 0, end_cap_npolys = 0, end_cap_nloops = 0; int result_nverts = 0, result_nedges = 0, result_npolys = 0, result_nloops = 0; int chunk_nverts, chunk_nedges, chunk_nloops, chunk_npolys; int first_chunk_start, first_chunk_nverts, last_chunk_start, last_chunk_nverts; DerivedMesh *result, *start_cap_dm = NULL, *end_cap_dm = NULL; int *vgroup_start_cap_remap = NULL; int vgroup_start_cap_remap_len = 0; int *vgroup_end_cap_remap = NULL; int vgroup_end_cap_remap_len = 0; chunk_nverts = dm->getNumVerts(dm); chunk_nedges = dm->getNumEdges(dm); chunk_nloops = dm->getNumLoops(dm); chunk_npolys = dm->getNumPolys(dm); count = amd->count; if (amd->start_cap && amd->start_cap != ob && amd->start_cap->type == OB_MESH) { vgroup_start_cap_remap = BKE_object_defgroup_index_map_create(amd->start_cap, ob, &vgroup_start_cap_remap_len); start_cap_dm = get_dm_for_modifier(amd->start_cap, flag); if (start_cap_dm) { start_cap_nverts = start_cap_dm->getNumVerts(start_cap_dm); start_cap_nedges = start_cap_dm->getNumEdges(start_cap_dm); start_cap_nloops = start_cap_dm->getNumLoops(start_cap_dm); start_cap_npolys = start_cap_dm->getNumPolys(start_cap_dm); } } if (amd->end_cap && amd->end_cap != ob && amd->end_cap->type == OB_MESH) { vgroup_end_cap_remap = BKE_object_defgroup_index_map_create(amd->end_cap, ob, &vgroup_end_cap_remap_len); end_cap_dm = get_dm_for_modifier(amd->end_cap, flag); if (end_cap_dm) { end_cap_nverts = end_cap_dm->getNumVerts(end_cap_dm); end_cap_nedges = end_cap_dm->getNumEdges(end_cap_dm); end_cap_nloops = end_cap_dm->getNumLoops(end_cap_dm); end_cap_npolys = end_cap_dm->getNumPolys(end_cap_dm); } } /* Build up offset array, cumulating all settings options */ unit_m4(offset); src_mvert = dm->getVertArray(dm); if (amd->offset_type & MOD_ARR_OFF_CONST) { add_v3_v3(offset[3], amd->offset); } if (amd->offset_type & MOD_ARR_OFF_RELATIVE) { float min[3], max[3]; const MVert *src_mv; INIT_MINMAX(min, max); for (src_mv = src_mvert, j = chunk_nverts; j--; src_mv++) { minmax_v3v3_v3(min, max, src_mv->co); } for (j = 3; j--; ) { offset[3][j] += amd->scale[j] * (max[j] - min[j]); } } if (use_offset_ob) { float obinv[4][4]; float result_mat[4][4]; if (ob) invert_m4_m4(obinv, ob->obmat); else unit_m4(obinv); mul_m4_series(result_mat, offset, obinv, amd->offset_ob->obmat); copy_m4_m4(offset, result_mat); } /* Check if there is some scaling. If scaling, then we will not translate mapping */ mat4_to_size(scale, offset); offset_has_scale = !is_one_v3(scale); if (amd->fit_type == MOD_ARR_FITCURVE && amd->curve_ob) { Curve *cu = amd->curve_ob->data; if (cu) { #ifdef CYCLIC_DEPENDENCY_WORKAROUND if (amd->curve_ob->curve_cache == NULL) { BKE_displist_make_curveTypes(scene, amd->curve_ob, false); } #endif if (amd->curve_ob->curve_cache && amd->curve_ob->curve_cache->path) { float scale_fac = mat4_to_scale(amd->curve_ob->obmat); length = scale_fac * amd->curve_ob->curve_cache->path->totdist; } } } /* calculate the maximum number of copies which will fit within the * prescribed length */ if (amd->fit_type == MOD_ARR_FITLENGTH || amd->fit_type == MOD_ARR_FITCURVE) { float dist = len_v3(offset[3]); if (dist > eps) { /* this gives length = first copy start to last copy end * add a tiny offset for floating point rounding errors */ count = (length + eps) / dist + 1; } else { /* if the offset has no translation, just make one copy */ count = 1; } } if (count < 1) count = 1; /* The number of verts, edges, loops, polys, before eventually merging doubles */ result_nverts = chunk_nverts * count + start_cap_nverts + end_cap_nverts; result_nedges = chunk_nedges * count + start_cap_nedges + end_cap_nedges; result_nloops = chunk_nloops * count + start_cap_nloops + end_cap_nloops; result_npolys = chunk_npolys * count + start_cap_npolys + end_cap_npolys; /* Initialize a result dm */ result = CDDM_from_template(dm, result_nverts, result_nedges, 0, result_nloops, result_npolys); result_dm_verts = CDDM_get_verts(result); if (use_merge) { /* Will need full_doubles_map for handling merge */ full_doubles_map = MEM_malloc_arrayN(result_nverts, sizeof(int), "mod array doubles map"); copy_vn_i(full_doubles_map, result_nverts, -1); } /* copy customdata to original geometry */ DM_copy_vert_data(dm, result, 0, 0, chunk_nverts); DM_copy_edge_data(dm, result, 0, 0, chunk_nedges); DM_copy_loop_data(dm, result, 0, 0, chunk_nloops); DM_copy_poly_data(dm, result, 0, 0, chunk_npolys); /* Subsurf for eg won't have mesh data in the custom data arrays. * now add mvert/medge/mpoly layers. */ if (!CustomData_has_layer(&dm->vertData, CD_MVERT)) { dm->copyVertArray(dm, result_dm_verts); } if (!CustomData_has_layer(&dm->edgeData, CD_MEDGE)) { dm->copyEdgeArray(dm, CDDM_get_edges(result)); } if (!CustomData_has_layer(&dm->polyData, CD_MPOLY)) { dm->copyLoopArray(dm, CDDM_get_loops(result)); dm->copyPolyArray(dm, CDDM_get_polys(result)); } /* Remember first chunk, in case of cap merge */ first_chunk_start = 0; first_chunk_nverts = chunk_nverts; unit_m4(current_offset); for (c = 1; c < count; c++) { /* copy customdata to new geometry */ DM_copy_vert_data(result, result, 0, c * chunk_nverts, chunk_nverts); DM_copy_edge_data(result, result, 0, c * chunk_nedges, chunk_nedges); DM_copy_loop_data(result, result, 0, c * chunk_nloops, chunk_nloops); DM_copy_poly_data(result, result, 0, c * chunk_npolys, chunk_npolys); mv_prev = result_dm_verts; mv = mv_prev + c * chunk_nverts; /* recalculate cumulative offset here */ mul_m4_m4m4(current_offset, current_offset, offset); /* apply offset to all new verts */ for (i = 0; i < chunk_nverts; i++, mv++, mv_prev++) { mul_m4_v3(current_offset, mv->co); /* We have to correct normals too, if we do not tag them as dirty! */ if (!use_recalc_normals) { float no[3]; normal_short_to_float_v3(no, mv->no); mul_mat3_m4_v3(current_offset, no); normalize_v3(no); normal_float_to_short_v3(mv->no, no); } } /* adjust edge vertex indices */ me = CDDM_get_edges(result) + c * chunk_nedges; for (i = 0; i < chunk_nedges; i++, me++) { me->v1 += c * chunk_nverts; me->v2 += c * chunk_nverts; } mp = CDDM_get_polys(result) + c * chunk_npolys; for (i = 0; i < chunk_npolys; i++, mp++) { mp->loopstart += c * chunk_nloops; } /* adjust loop vertex and edge indices */ ml = CDDM_get_loops(result) + c * chunk_nloops; for (i = 0; i < chunk_nloops; i++, ml++) { ml->v += c * chunk_nverts; ml->e += c * chunk_nedges; } /* Handle merge between chunk n and n-1 */ if (use_merge && (c >= 1)) { if (!offset_has_scale && (c >= 2)) { /* Mapping chunk 3 to chunk 2 is a translation of mapping 2 to 1 * ... that is except if scaling makes the distance grow */ int k; int this_chunk_index = c * chunk_nverts; int prev_chunk_index = (c - 1) * chunk_nverts; for (k = 0; k < chunk_nverts; k++, this_chunk_index++, prev_chunk_index++) { int target = full_doubles_map[prev_chunk_index]; if (target != -1) { target += chunk_nverts; /* translate mapping */ while (target != -1 && !ELEM(full_doubles_map[target], -1, target)) { /* If target is already mapped, we only follow that mapping if final target remains * close enough from current vert (otherwise no mapping at all). */ if (compare_len_v3v3(result_dm_verts[this_chunk_index].co, result_dm_verts[full_doubles_map[target]].co, amd->merge_dist)) { target = full_doubles_map[target]; } else { target = -1; } } } full_doubles_map[this_chunk_index] = target; } } else { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, (c - 1) * chunk_nverts, chunk_nverts, c * chunk_nverts, chunk_nverts, amd->merge_dist); } } } /* handle UVs */ if (chunk_nloops > 0 && is_zero_v2(amd->uv_offset) == false) { const int totuv = CustomData_number_of_layers(&result->loopData, CD_MLOOPUV); for (i = 0; i < totuv; i++) { MLoopUV *dmloopuv = CustomData_get_layer_n(&result->loopData, CD_MLOOPUV, i); dmloopuv += chunk_nloops; for (c = 1; c < count; c++) { const float uv_offset[2] = { amd->uv_offset[0] * (float)c, amd->uv_offset[1] * (float)c, }; int l_index = chunk_nloops; for (; l_index-- != 0; dmloopuv++) { dmloopuv->uv[0] += uv_offset[0]; dmloopuv->uv[1] += uv_offset[1]; } } } } last_chunk_start = (count - 1) * chunk_nverts; last_chunk_nverts = chunk_nverts; copy_m4_m4(final_offset, current_offset); if (use_merge && (amd->flags & MOD_ARR_MERGEFINAL) && (count > 1)) { /* Merge first and last copies */ dm_mvert_map_doubles( full_doubles_map, result_dm_verts, last_chunk_start, last_chunk_nverts, first_chunk_start, first_chunk_nverts, amd->merge_dist); } /* start capping */ if (start_cap_dm) { float start_offset[4][4]; int start_cap_start = result_nverts - start_cap_nverts - end_cap_nverts; invert_m4_m4(start_offset, offset); dm_merge_transform( result, start_cap_dm, start_offset, result_nverts - start_cap_nverts - end_cap_nverts, result_nedges - start_cap_nedges - end_cap_nedges, result_nloops - start_cap_nloops - end_cap_nloops, result_npolys - start_cap_npolys - end_cap_npolys, start_cap_nverts, start_cap_nedges, start_cap_nloops, start_cap_npolys, vgroup_start_cap_remap, vgroup_start_cap_remap_len); /* Identify doubles with first chunk */ if (use_merge) { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, first_chunk_start, first_chunk_nverts, start_cap_start, start_cap_nverts, amd->merge_dist); } } if (end_cap_dm) { float end_offset[4][4]; int end_cap_start = result_nverts - end_cap_nverts; mul_m4_m4m4(end_offset, current_offset, offset); dm_merge_transform( result, end_cap_dm, end_offset, result_nverts - end_cap_nverts, result_nedges - end_cap_nedges, result_nloops - end_cap_nloops, result_npolys - end_cap_npolys, end_cap_nverts, end_cap_nedges, end_cap_nloops, end_cap_npolys, vgroup_end_cap_remap, vgroup_end_cap_remap_len); /* Identify doubles with last chunk */ if (use_merge) { dm_mvert_map_doubles( full_doubles_map, result_dm_verts, last_chunk_start, last_chunk_nverts, end_cap_start, end_cap_nverts, amd->merge_dist); } } /* done capping */ /* Handle merging */ tot_doubles = 0; if (use_merge) { for (i = 0; i < result_nverts; i++) { int new_i = full_doubles_map[i]; if (new_i != -1) { /* We have to follow chains of doubles (merge start/end especially is likely to create some), * those are not supported at all by CDDM_merge_verts! */ while (!ELEM(full_doubles_map[new_i], -1, new_i)) { new_i = full_doubles_map[new_i]; } if (i == new_i) { full_doubles_map[i] = -1; } else { full_doubles_map[i] = new_i; tot_doubles++; } } } if (tot_doubles > 0) { result = CDDM_merge_verts(result, full_doubles_map, tot_doubles, CDDM_MERGE_VERTS_DUMP_IF_EQUAL); } MEM_freeN(full_doubles_map); } /* In case org dm has dirty normals, or we made some merging, mark normals as dirty in new dm! * TODO: we may need to set other dirty flags as well? */ if (use_recalc_normals) { result->dirty |= DM_DIRTY_NORMALS; } if (vgroup_start_cap_remap) { MEM_freeN(vgroup_start_cap_remap); } if (vgroup_end_cap_remap) { MEM_freeN(vgroup_end_cap_remap); } return result; }
/** * \param dm Mesh to calculate normals for. * \param face_nors Precalculated face normals. * \param r_vert_nors Return vert normals. */ static void dm_calc_normal(DerivedMesh *dm, float (*face_nors)[3], float (*r_vert_nors)[3]) { int i, numVerts, numEdges, numFaces; MPoly *mpoly, *mp; MLoop *mloop, *ml; MEdge *medge, *ed; MVert *mvert, *mv; numVerts = dm->getNumVerts(dm); numEdges = dm->getNumEdges(dm); numFaces = dm->getNumPolys(dm); mpoly = dm->getPolyArray(dm); medge = dm->getEdgeArray(dm); mvert = dm->getVertArray(dm); mloop = dm->getLoopArray(dm); /* we don't want to overwrite any referenced layers */ /* Doesn't work here! */ #if 0 mv = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, numVerts); cddm->mvert = mv; #endif mv = mvert; mp = mpoly; { EdgeFaceRef *edge_ref_array = MEM_callocN(sizeof(EdgeFaceRef) * (size_t)numEdges, "Edge Connectivity"); EdgeFaceRef *edge_ref; float edge_normal[3]; /* This loop adds an edge hash if its not there, and adds the face index */ for (i = 0; i < numFaces; i++, mp++) { int j; ml = mloop + mp->loopstart; for (j = 0; j < mp->totloop; j++, ml++) { /* --- add edge ref to face --- */ edge_ref = &edge_ref_array[ml->e]; if (!edgeref_is_init(edge_ref)) { edge_ref->f1 = i; edge_ref->f2 = -1; } else if ((edge_ref->f1 != -1) && (edge_ref->f2 == -1)) { edge_ref->f2 = i; } else { /* 3+ faces using an edge, we can't handle this usefully */ edge_ref->f1 = edge_ref->f2 = -1; #ifdef USE_NONMANIFOLD_WORKAROUND medge[ml->e].flag |= ME_EDGE_TMP_TAG; #endif } /* --- done --- */ } } for (i = 0, ed = medge, edge_ref = edge_ref_array; i < numEdges; i++, ed++, edge_ref++) { /* Get the edge vert indices, and edge value (the face indices that use it) */ if (edgeref_is_init(edge_ref) && (edge_ref->f1 != -1)) { if (edge_ref->f2 != -1) { /* We have 2 faces using this edge, calculate the edges normal * using the angle between the 2 faces as a weighting */ #if 0 add_v3_v3v3(edge_normal, face_nors[edge_ref->f1], face_nors[edge_ref->f2]); normalize_v3(edge_normal); mul_v3_fl(edge_normal, angle_normalized_v3v3(face_nors[edge_ref->f1], face_nors[edge_ref->f2])); #else mid_v3_v3v3_angle_weighted(edge_normal, face_nors[edge_ref->f1], face_nors[edge_ref->f2]); #endif } else { /* only one face attached to that edge */ /* an edge without another attached- the weight on this is undefined */ copy_v3_v3(edge_normal, face_nors[edge_ref->f1]); } add_v3_v3(r_vert_nors[ed->v1], edge_normal); add_v3_v3(r_vert_nors[ed->v2], edge_normal); } } MEM_freeN(edge_ref_array); } /* normalize vertex normals and assign */ for (i = 0; i < numVerts; i++, mv++) { if (normalize_v3(r_vert_nors[i]) == 0.0f) { normal_short_to_float_v3(r_vert_nors[i], mv->no); } } }
static DerivedMesh *applyModifier( ModifierData *md, Object *ob, DerivedMesh *dm, ModifierApplyFlag UNUSED(flag)) { DerivedMesh *result; const SolidifyModifierData *smd = (SolidifyModifierData *) md; MVert *mv, *mvert, *orig_mvert; MEdge *ed, *medge, *orig_medge; MLoop *ml, *mloop, *orig_mloop; MPoly *mp, *mpoly, *orig_mpoly; const unsigned int numVerts = (unsigned int)dm->getNumVerts(dm); const unsigned int numEdges = (unsigned int)dm->getNumEdges(dm); const unsigned int numFaces = (unsigned int)dm->getNumPolys(dm); const unsigned int numLoops = (unsigned int)dm->getNumLoops(dm); unsigned int newLoops = 0, newFaces = 0, newEdges = 0, newVerts = 0, rimVerts = 0; /* only use material offsets if we have 2 or more materials */ const short mat_nr_max = ob->totcol > 1 ? ob->totcol - 1 : 0; const short mat_ofs = mat_nr_max ? smd->mat_ofs : 0; const short mat_ofs_rim = mat_nr_max ? smd->mat_ofs_rim : 0; /* use for edges */ /* over-alloc new_vert_arr, old_vert_arr */ unsigned int *new_vert_arr = NULL; STACK_DECLARE(new_vert_arr); unsigned int *new_edge_arr = NULL; STACK_DECLARE(new_edge_arr); unsigned int *old_vert_arr = MEM_callocN(sizeof(*old_vert_arr) * (size_t)numVerts, "old_vert_arr in solidify"); unsigned int *edge_users = NULL; char *edge_order = NULL; float (*vert_nors)[3] = NULL; float (*face_nors)[3] = NULL; const bool need_face_normals = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) || (smd->flag & MOD_SOLIDIFY_EVEN); const float ofs_orig = -(((-smd->offset_fac + 1.0f) * 0.5f) * smd->offset); const float ofs_new = smd->offset + ofs_orig; const float offset_fac_vg = smd->offset_fac_vg; const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg; const bool do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0; const bool do_clamp = (smd->offset_clamp != 0.0f); const bool do_shell = ((smd->flag & MOD_SOLIDIFY_RIM) && (smd->flag & MOD_SOLIDIFY_NOSHELL)) == 0; /* weights */ MDeformVert *dvert; const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0; int defgrp_index; /* array size is doubled in case of using a shell */ const unsigned int stride = do_shell ? 2 : 1; modifier_get_vgroup(ob, dm, smd->defgrp_name, &dvert, &defgrp_index); orig_mvert = dm->getVertArray(dm); orig_medge = dm->getEdgeArray(dm); orig_mloop = dm->getLoopArray(dm); orig_mpoly = dm->getPolyArray(dm); if (need_face_normals) { /* calculate only face normals */ face_nors = MEM_mallocN(sizeof(*face_nors) * (size_t)numFaces, __func__); BKE_mesh_calc_normals_poly( orig_mvert, NULL, (int)numVerts, orig_mloop, orig_mpoly, (int)numLoops, (int)numFaces, face_nors, true); } STACK_INIT(new_vert_arr, numVerts * 2); STACK_INIT(new_edge_arr, numEdges * 2); if (smd->flag & MOD_SOLIDIFY_RIM) { BLI_bitmap *orig_mvert_tag = BLI_BITMAP_NEW(numVerts, __func__); unsigned int eidx; unsigned int i; #define INVALID_UNUSED ((unsigned int)-1) #define INVALID_PAIR ((unsigned int)-2) new_vert_arr = MEM_mallocN(sizeof(*new_vert_arr) * (size_t)(numVerts * 2), __func__); new_edge_arr = MEM_mallocN(sizeof(*new_edge_arr) * (size_t)((numEdges * 2) + numVerts), __func__); edge_users = MEM_mallocN(sizeof(*edge_users) * (size_t)numEdges, "solid_mod edges"); edge_order = MEM_mallocN(sizeof(*edge_order) * (size_t)numEdges, "solid_mod eorder"); /* save doing 2 loops here... */ #if 0 copy_vn_i(edge_users, numEdges, INVALID_UNUSED); #endif for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) { edge_users[eidx] = INVALID_UNUSED; } for (i = 0, mp = orig_mpoly; i < numFaces; i++, mp++) { MLoop *ml_prev; int j; ml = orig_mloop + mp->loopstart; ml_prev = ml + (mp->totloop - 1); for (j = 0; j < mp->totloop; j++, ml++) { /* add edge user */ eidx = ml_prev->e; if (edge_users[eidx] == INVALID_UNUSED) { ed = orig_medge + eidx; BLI_assert(ELEM(ml_prev->v, ed->v1, ed->v2) && ELEM(ml->v, ed->v1, ed->v2)); edge_users[eidx] = (ml_prev->v > ml->v) == (ed->v1 < ed->v2) ? i : (i + numFaces); edge_order[eidx] = j; } else { edge_users[eidx] = INVALID_PAIR; } ml_prev = ml; } } for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) { if (!ELEM(edge_users[eidx], INVALID_UNUSED, INVALID_PAIR)) { BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v1); BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v2); STACK_PUSH(new_edge_arr, eidx); newFaces++; newLoops += 4; } } for (i = 0; i < numVerts; i++) { if (BLI_BITMAP_TEST(orig_mvert_tag, i)) { old_vert_arr[i] = STACK_SIZE(new_vert_arr); STACK_PUSH(new_vert_arr, i); rimVerts++; } else { old_vert_arr[i] = INVALID_UNUSED; } } MEM_freeN(orig_mvert_tag); } if (do_shell == false) { /* only add rim vertices */ newVerts = rimVerts; /* each extruded face needs an opposite edge */ newEdges = newFaces; } else { /* (stride == 2) in this case, so no need to add newVerts/newEdges */ BLI_assert(newVerts == 0); BLI_assert(newEdges == 0); } if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) { vert_nors = MEM_callocN(sizeof(float) * (size_t)numVerts * 3, "mod_solid_vno_hq"); dm_calc_normal(dm, face_nors, vert_nors); } result = CDDM_from_template(dm, (int)((numVerts * stride) + newVerts), (int)((numEdges * stride) + newEdges + rimVerts), 0, (int)((numLoops * stride) + newLoops), (int)((numFaces * stride) + newFaces)); mpoly = CDDM_get_polys(result); mloop = CDDM_get_loops(result); medge = CDDM_get_edges(result); mvert = CDDM_get_verts(result); if (do_shell) { DM_copy_vert_data(dm, result, 0, 0, (int)numVerts); DM_copy_vert_data(dm, result, 0, (int)numVerts, (int)numVerts); DM_copy_edge_data(dm, result, 0, 0, (int)numEdges); DM_copy_edge_data(dm, result, 0, (int)numEdges, (int)numEdges); DM_copy_loop_data(dm, result, 0, 0, (int)numLoops); DM_copy_loop_data(dm, result, 0, (int)numLoops, (int)numLoops); DM_copy_poly_data(dm, result, 0, 0, (int)numFaces); DM_copy_poly_data(dm, result, 0, (int)numFaces, (int)numFaces); } else { int i, j; DM_copy_vert_data(dm, result, 0, 0, (int)numVerts); for (i = 0, j = (int)numVerts; i < numVerts; i++) { if (old_vert_arr[i] != INVALID_UNUSED) { DM_copy_vert_data(dm, result, i, j, 1); j++; } } DM_copy_edge_data(dm, result, 0, 0, (int)numEdges); for (i = 0, j = (int)numEdges; i < numEdges; i++) { if (!ELEM(edge_users[i], INVALID_UNUSED, INVALID_PAIR)) { MEdge *ed_src, *ed_dst; DM_copy_edge_data(dm, result, i, j, 1); ed_src = &medge[i]; ed_dst = &medge[j]; ed_dst->v1 = old_vert_arr[ed_src->v1] + numVerts; ed_dst->v2 = old_vert_arr[ed_src->v2] + numVerts; j++; } } /* will be created later */ DM_copy_loop_data(dm, result, 0, 0, (int)numLoops); DM_copy_poly_data(dm, result, 0, 0, (int)numFaces); } #undef INVALID_UNUSED #undef INVALID_PAIR /* initializes: (i_end, do_shell_align, mv) */ #define INIT_VERT_ARRAY_OFFSETS(test) \ if (((ofs_new >= ofs_orig) == do_flip) == test) { \ i_end = numVerts; \ do_shell_align = true; \ mv = mvert; \ } \ else { \ if (do_shell) { \ i_end = numVerts; \ do_shell_align = true; \ } \ else { \ i_end = newVerts ; \ do_shell_align = false; \ } \ mv = &mvert[numVerts]; \ } (void)0 /* flip normals */ if (do_shell) { unsigned int i; mp = mpoly + numFaces; for (i = 0; i < dm->numPolyData; i++, mp++) { MLoop *ml2; unsigned int e; int j; /* reverses the loop direction (MLoop.v as well as custom-data) * MLoop.e also needs to be corrected too, done in a separate loop below. */ ml2 = mloop + mp->loopstart + dm->numLoopData; for (j = 0; j < mp->totloop; j++) { CustomData_copy_data(&dm->loopData, &result->loopData, mp->loopstart + j, mp->loopstart + (mp->totloop - j - 1) + dm->numLoopData, 1); } if (mat_ofs) { mp->mat_nr += mat_ofs; CLAMP(mp->mat_nr, 0, mat_nr_max); } e = ml2[0].e; for (j = 0; j < mp->totloop - 1; j++) { ml2[j].e = ml2[j + 1].e; } ml2[mp->totloop - 1].e = e; mp->loopstart += dm->numLoopData; for (j = 0; j < mp->totloop; j++) { ml2[j].e += numEdges; ml2[j].v += numVerts; } } for (i = 0, ed = medge + numEdges; i < numEdges; i++, ed++) { ed->v1 += numVerts; ed->v2 += numVerts; } } /* note, copied vertex layers don't have flipped normals yet. do this after applying offset */ if ((smd->flag & MOD_SOLIDIFY_EVEN) == 0) { /* no even thickness, very simple */ float scalar_short; float scalar_short_vgroup; /* for clamping */ float *vert_lens = NULL; const float offset = fabsf(smd->offset) * smd->offset_clamp; const float offset_sq = offset * offset; if (do_clamp) { unsigned int i; vert_lens = MEM_mallocN(sizeof(float) * numVerts, "vert_lens"); copy_vn_fl(vert_lens, (int)numVerts, FLT_MAX); for (i = 0; i < numEdges; i++) { const float ed_len_sq = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co); vert_lens[medge[i].v1] = min_ff(vert_lens[medge[i].v1], ed_len_sq); vert_lens[medge[i].v2] = min_ff(vert_lens[medge[i].v2], ed_len_sq); } } if (ofs_new != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; scalar_short = scalar_short_vgroup = ofs_new / 32767.0f; INIT_VERT_ARRAY_OFFSETS(false); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (dvert) { MDeformVert *dv = &dvert[i]; if (defgrp_invert) scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index); else scalar_short_vgroup = defvert_find_weight(dv, defgrp_index); scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * scalar_short; } if (do_clamp) { /* always reset becaise we may have set before */ if (dvert == NULL) { scalar_short_vgroup = scalar_short; } if (vert_lens[i] < offset_sq) { float scalar = sqrtf(vert_lens[i]) / offset; scalar_short_vgroup *= scalar; } } madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); } } if (ofs_orig != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f; /* as above but swapped */ INIT_VERT_ARRAY_OFFSETS(true); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (dvert) { MDeformVert *dv = &dvert[i]; if (defgrp_invert) scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index); else scalar_short_vgroup = defvert_find_weight(dv, defgrp_index); scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * scalar_short; } if (do_clamp) { /* always reset becaise we may have set before */ if (dvert == NULL) { scalar_short_vgroup = scalar_short; } if (vert_lens[i] < offset_sq) { float scalar = sqrtf(vert_lens[i]) / offset; scalar_short_vgroup *= scalar; } } madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); } } if (do_clamp) { MEM_freeN(vert_lens); } } else { #ifdef USE_NONMANIFOLD_WORKAROUND const bool check_non_manifold = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) != 0; #endif /* same as EM_solidify() in editmesh_lib.c */ float *vert_angles = MEM_callocN(sizeof(float) * numVerts * 2, "mod_solid_pair"); /* 2 in 1 */ float *vert_accum = vert_angles + numVerts; unsigned int vidx; unsigned int i; if (vert_nors == NULL) { vert_nors = MEM_mallocN(sizeof(float) * numVerts * 3, "mod_solid_vno"); for (i = 0, mv = mvert; i < numVerts; i++, mv++) { normal_short_to_float_v3(vert_nors[i], mv->no); } } for (i = 0, mp = mpoly; i < numFaces; i++, mp++) { /* #BKE_mesh_calc_poly_angles logic is inlined here */ float nor_prev[3]; float nor_next[3]; int i_curr = mp->totloop - 1; int i_next = 0; ml = &mloop[mp->loopstart]; sub_v3_v3v3(nor_prev, mvert[ml[i_curr - 1].v].co, mvert[ml[i_curr].v].co); normalize_v3(nor_prev); while (i_next < mp->totloop) { float angle; sub_v3_v3v3(nor_next, mvert[ml[i_curr].v].co, mvert[ml[i_next].v].co); normalize_v3(nor_next); angle = angle_normalized_v3v3(nor_prev, nor_next); /* --- not related to angle calc --- */ if (angle < FLT_EPSILON) { angle = FLT_EPSILON; } vidx = ml[i_curr].v; vert_accum[vidx] += angle; #ifdef USE_NONMANIFOLD_WORKAROUND /* skip 3+ face user edges */ if ((check_non_manifold == false) || LIKELY(((orig_medge[ml[i_curr].e].flag & ME_EDGE_TMP_TAG) == 0) && ((orig_medge[ml[i_next].e].flag & ME_EDGE_TMP_TAG) == 0))) { vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_nors[i]) * angle; } else { vert_angles[vidx] += angle; } #else vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_nors[i]) * angle; #endif /* --- end non-angle-calc section --- */ /* step */ copy_v3_v3(nor_prev, nor_next); i_curr = i_next; i_next++; } } /* vertex group support */ if (dvert) { MDeformVert *dv = dvert; float scalar; if (defgrp_invert) { for (i = 0; i < numVerts; i++, dv++) { scalar = 1.0f - defvert_find_weight(dv, defgrp_index); scalar = offset_fac_vg + (scalar * offset_fac_vg_inv); vert_angles[i] *= scalar; } } else { for (i = 0; i < numVerts; i++, dv++) { scalar = defvert_find_weight(dv, defgrp_index); scalar = offset_fac_vg + (scalar * offset_fac_vg_inv); vert_angles[i] *= scalar; } } } if (do_clamp) { float *vert_lens_sq = MEM_mallocN(sizeof(float) * numVerts, "vert_lens"); const float offset = fabsf(smd->offset) * smd->offset_clamp; const float offset_sq = offset * offset; copy_vn_fl(vert_lens_sq, (int)numVerts, FLT_MAX); for (i = 0; i < numEdges; i++) { const float ed_len = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co); vert_lens_sq[medge[i].v1] = min_ff(vert_lens_sq[medge[i].v1], ed_len); vert_lens_sq[medge[i].v2] = min_ff(vert_lens_sq[medge[i].v2], ed_len); } for (i = 0; i < numVerts; i++) { if (vert_lens_sq[i] < offset_sq) { float scalar = sqrtf(vert_lens_sq[i]) / offset; vert_angles[i] *= scalar; } } MEM_freeN(vert_lens_sq); } if (ofs_new != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; INIT_VERT_ARRAY_OFFSETS(false); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i_other = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (vert_accum[i_other]) { /* zero if unselected */ madd_v3_v3fl(mv->co, vert_nors[i_other], ofs_new * (vert_angles[i_other] / vert_accum[i_other])); } } } if (ofs_orig != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; /* same as above but swapped, intentional use of 'ofs_new' */ INIT_VERT_ARRAY_OFFSETS(true); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i_other = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (vert_accum[i_other]) { /* zero if unselected */ madd_v3_v3fl(mv->co, vert_nors[i_other], ofs_orig * (vert_angles[i_other] / vert_accum[i_other])); } } } MEM_freeN(vert_angles); } if (vert_nors) MEM_freeN(vert_nors); /* must recalculate normals with vgroups since they can displace unevenly [#26888] */ if ((dm->dirty & DM_DIRTY_NORMALS) || (smd->flag & MOD_SOLIDIFY_RIM) || dvert) { result->dirty |= DM_DIRTY_NORMALS; } else if (do_shell) { unsigned int i; /* flip vertex normals for copied verts */ mv = mvert + numVerts; for (i = 0; i < numVerts; i++, mv++) { negate_v3_short(mv->no); } } if (smd->flag & MOD_SOLIDIFY_RIM) { unsigned int i; /* bugger, need to re-calculate the normals for the new edge faces. * This could be done in many ways, but probably the quickest way * is to calculate the average normals for side faces only. * Then blend them with the normals of the edge verts. * * at the moment its easiest to allocate an entire array for every vertex, * even though we only need edge verts - campbell */ #define SOLIDIFY_SIDE_NORMALS #ifdef SOLIDIFY_SIDE_NORMALS const bool do_side_normals = !(result->dirty & DM_DIRTY_NORMALS); /* annoying to allocate these since we only need the edge verts, */ float (*edge_vert_nos)[3] = do_side_normals ? MEM_callocN(sizeof(float) * numVerts * 3, __func__) : NULL; float nor[3]; #endif const unsigned char crease_rim = smd->crease_rim * 255.0f; const unsigned char crease_outer = smd->crease_outer * 255.0f; const unsigned char crease_inner = smd->crease_inner * 255.0f; int *origindex_edge; int *orig_ed; unsigned int j; if (crease_rim || crease_outer || crease_inner) { result->cd_flag |= ME_CDFLAG_EDGE_CREASE; } /* add faces & edges */ origindex_edge = result->getEdgeDataArray(result, CD_ORIGINDEX); ed = &medge[(numEdges * stride) + newEdges]; /* start after copied edges */ orig_ed = &origindex_edge[(numEdges * stride) + newEdges]; for (i = 0; i < rimVerts; i++, ed++, orig_ed++) { ed->v1 = new_vert_arr[i]; ed->v2 = (do_shell ? new_vert_arr[i] : i) + numVerts; ed->flag |= ME_EDGEDRAW; *orig_ed = ORIGINDEX_NONE; if (crease_rim) { ed->crease = crease_rim; } } /* faces */ mp = mpoly + (numFaces * stride); ml = mloop + (numLoops * stride); j = 0; for (i = 0; i < newFaces; i++, mp++) { unsigned int eidx = new_edge_arr[i]; unsigned int fidx = edge_users[eidx]; int k1, k2; bool flip; if (fidx >= numFaces) { fidx -= numFaces; flip = true; } else { flip = false; } ed = medge + eidx; /* copy most of the face settings */ DM_copy_poly_data(dm, result, (int)fidx, (int)((numFaces * stride) + i), 1); mp->loopstart = (int)(j + (numLoops * stride)); mp->flag = mpoly[fidx].flag; /* notice we use 'mp->totloop' which is later overwritten, * we could lookup the original face but theres no point since this is a copy * and will have the same value, just take care when changing order of assignment */ k1 = mpoly[fidx].loopstart + (((edge_order[eidx] - 1) + mp->totloop) % mp->totloop); /* prev loop */ k2 = mpoly[fidx].loopstart + (edge_order[eidx]); mp->totloop = 4; CustomData_copy_data(&dm->loopData, &result->loopData, k2, (int)((numLoops * stride) + j + 0), 1); CustomData_copy_data(&dm->loopData, &result->loopData, k1, (int)((numLoops * stride) + j + 1), 1); CustomData_copy_data(&dm->loopData, &result->loopData, k1, (int)((numLoops * stride) + j + 2), 1); CustomData_copy_data(&dm->loopData, &result->loopData, k2, (int)((numLoops * stride) + j + 3), 1); if (flip == false) { ml[j].v = ed->v1; ml[j++].e = eidx; ml[j].v = ed->v2; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges; ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts; ml[j++].e = (do_shell ? eidx : i) + numEdges; ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges; } else { ml[j].v = ed->v2; ml[j++].e = eidx; ml[j].v = ed->v1; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges; ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts; ml[j++].e = (do_shell ? eidx : i) + numEdges; ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges; } origindex_edge[ml[j - 3].e] = ORIGINDEX_NONE; origindex_edge[ml[j - 1].e] = ORIGINDEX_NONE; /* use the next material index if option enabled */ if (mat_ofs_rim) { mp->mat_nr += mat_ofs_rim; CLAMP(mp->mat_nr, 0, mat_nr_max); } if (crease_outer) { /* crease += crease_outer; without wrapping */ char *cr = &(ed->crease); int tcr = *cr + crease_outer; *cr = tcr > 255 ? 255 : tcr; } if (crease_inner) { /* crease += crease_inner; without wrapping */ char *cr = &(medge[numEdges + (do_shell ? eidx : i)].crease); int tcr = *cr + crease_inner; *cr = tcr > 255 ? 255 : tcr; } #ifdef SOLIDIFY_SIDE_NORMALS if (do_side_normals) { normal_quad_v3(nor, mvert[ml[j - 4].v].co, mvert[ml[j - 3].v].co, mvert[ml[j - 2].v].co, mvert[ml[j - 1].v].co); add_v3_v3(edge_vert_nos[ed->v1], nor); add_v3_v3(edge_vert_nos[ed->v2], nor); } #endif } #ifdef SOLIDIFY_SIDE_NORMALS if (do_side_normals) { ed = medge + (numEdges * stride); for (i = 0; i < rimVerts; i++, ed++) { float nor_cpy[3]; short *nor_short; int k; /* note, only the first vertex (lower half of the index) is calculated */ normalize_v3_v3(nor_cpy, edge_vert_nos[ed->v1]); for (k = 0; k < 2; k++) { /* loop over both verts of the edge */ nor_short = mvert[*(&ed->v1 + k)].no; normal_short_to_float_v3(nor, nor_short); add_v3_v3(nor, nor_cpy); normalize_v3(nor); normal_float_to_short_v3(nor_short, nor); } } MEM_freeN(edge_vert_nos); } #endif MEM_freeN(new_vert_arr); MEM_freeN(new_edge_arr); MEM_freeN(edge_users); MEM_freeN(edge_order); } if (old_vert_arr) MEM_freeN(old_vert_arr); if (face_nors) MEM_freeN(face_nors); if (numFaces == 0 && numEdges != 0) { modifier_setError(md, "Faces needed for useful output"); } return result; }
/** * The main function for copying DerivedMesh data into BMesh. * * \note The mesh may already have geometry. see 'is_init' */ void DM_to_bmesh_ex(DerivedMesh *dm, BMesh *bm, const bool calc_face_normal) { MVert *mv, *mvert; MEdge *me, *medge; MPoly /* *mpoly, */ /* UNUSED */ *mp; MLoop *mloop; BMVert *v, **vtable; BMEdge *e, **etable; float (*face_normals)[3]; BMFace *f; int i, j, totvert, totedge /* , totface */ /* UNUSED */ ; bool is_init = (bm->totvert == 0) && (bm->totedge == 0) && (bm->totface == 0); bool is_cddm = (dm->type == DM_TYPE_CDDM); /* duplicate the arrays for non cddm */ char has_orig_hflag = 0; int cd_vert_bweight_offset; int cd_edge_bweight_offset; int cd_edge_crease_offset; if (is_init == FALSE) { /* check if we have an origflag */ has_orig_hflag |= CustomData_has_layer(&bm->vdata, CD_ORIGINDEX) ? BM_VERT : 0; has_orig_hflag |= CustomData_has_layer(&bm->edata, CD_ORIGINDEX) ? BM_EDGE : 0; has_orig_hflag |= CustomData_has_layer(&bm->pdata, CD_ORIGINDEX) ? BM_FACE : 0; } /*merge custom data layout*/ CustomData_bmesh_merge(&dm->vertData, &bm->vdata, CD_MASK_DERIVEDMESH, CD_CALLOC, bm, BM_VERT); CustomData_bmesh_merge(&dm->edgeData, &bm->edata, CD_MASK_DERIVEDMESH, CD_CALLOC, bm, BM_EDGE); CustomData_bmesh_merge(&dm->loopData, &bm->ldata, CD_MASK_DERIVEDMESH, CD_CALLOC, bm, BM_LOOP); CustomData_bmesh_merge(&dm->polyData, &bm->pdata, CD_MASK_DERIVEDMESH, CD_CALLOC, bm, BM_FACE); if (is_init) { BM_mesh_cd_flag_apply(bm, dm->cd_flag); } cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); totvert = dm->getNumVerts(dm); totedge = dm->getNumEdges(dm); /* totface = dm->getNumPolys(dm); */ /* UNUSED */ vtable = MEM_callocN(sizeof(void **) * totvert, __func__); etable = MEM_callocN(sizeof(void **) * totedge, __func__); /*do verts*/ mv = mvert = is_cddm ? dm->getVertArray(dm) : dm->dupVertArray(dm); for (i = 0; i < totvert; i++, mv++) { v = BM_vert_create(bm, mv->co, NULL, BM_CREATE_SKIP_CD); normal_short_to_float_v3(v->no, mv->no); v->head.hflag = BM_vert_flag_from_mflag(mv->flag); BM_elem_index_set(v, i); /* set_inline */ CustomData_to_bmesh_block(&dm->vertData, &bm->vdata, i, &v->head.data, true); vtable[i] = v; /* add bevel weight */ if (cd_vert_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mv->bweight / 255.0f); if (UNLIKELY(has_orig_hflag & BM_VERT)) { int *orig_index = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_ORIGINDEX); *orig_index = ORIGINDEX_NONE; } } if (!is_cddm) MEM_freeN(mvert); if (is_init) bm->elem_index_dirty &= ~BM_VERT; /*do edges*/ me = medge = is_cddm ? dm->getEdgeArray(dm) : dm->dupEdgeArray(dm); for (i = 0; i < totedge; i++, me++) { //BLI_assert(BM_edge_exists(vtable[me->v1], vtable[me->v2]) == NULL); e = BM_edge_create(bm, vtable[me->v1], vtable[me->v2], NULL, BM_CREATE_SKIP_CD); e->head.hflag = BM_edge_flag_from_mflag(me->flag); BM_elem_index_set(e, i); /* set_inline */ CustomData_to_bmesh_block(&dm->edgeData, &bm->edata, i, &e->head.data, true); etable[i] = e; if (cd_edge_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)me->bweight / 255.0f); if (cd_edge_crease_offset != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset, (float)me->crease / 255.0f); if (UNLIKELY(has_orig_hflag & BM_EDGE)) { int *orig_index = CustomData_bmesh_get(&bm->edata, e->head.data, CD_ORIGINDEX); *orig_index = ORIGINDEX_NONE; } } if (!is_cddm) MEM_freeN(medge); if (is_init) bm->elem_index_dirty &= ~BM_EDGE; /* do faces */ /* note: i_alt is aligned with bmesh faces which may not always align with mpolys */ mp = dm->getPolyArray(dm); mloop = dm->getLoopArray(dm); face_normals = (dm->dirty & DM_DIRTY_NORMALS) ? NULL : CustomData_get_layer(&dm->polyData, CD_NORMAL); for (i = 0; i < dm->numPolyData; i++, mp++) { BMLoop *l_iter; BMLoop *l_first; f = bm_face_create_from_mpoly(mp, mloop + mp->loopstart, bm, vtable, etable); if (UNLIKELY(f == NULL)) { continue; } f->head.hflag = BM_face_flag_from_mflag(mp->flag); BM_elem_index_set(f, bm->totface - 1); /* set_inline */ f->mat_nr = mp->mat_nr; j = mp->loopstart; l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { /* Save index of correspsonding MLoop */ CustomData_to_bmesh_block(&dm->loopData, &bm->ldata, j++, &l_iter->head.data, true); } while ((l_iter = l_iter->next) != l_first); CustomData_to_bmesh_block(&dm->polyData, &bm->pdata, i, &f->head.data, true); if (calc_face_normal) { if (face_normals) { copy_v3_v3(f->no, face_normals[i]); } else { BM_face_normal_update(f); } } if (UNLIKELY(has_orig_hflag & BM_FACE)) { int *orig_index = CustomData_bmesh_get(&bm->pdata, f->head.data, CD_ORIGINDEX); *orig_index = ORIGINDEX_NONE; } } if (is_init) bm->elem_index_dirty &= ~BM_FACE; MEM_freeN(vtable); MEM_freeN(etable); }
/** * This function converts an object space normal map to a tangent space normal map for a given low poly mesh */ void RE_bake_normal_world_to_tangent( const BakePixel pixel_array[], const size_t num_pixels, const int depth, float result[], Mesh *me, const BakeNormalSwizzle normal_swizzle[3], float mat[4][4]) { size_t i; TriTessFace *triangles; DerivedMesh *dm = CDDM_from_mesh(me); triangles = MEM_mallocN(sizeof(TriTessFace) * (me->totface * 2), "MVerts Mesh"); mesh_calc_tri_tessface(triangles, me, true, dm); BLI_assert(num_pixels >= 3); for (i = 0; i < num_pixels; i++) { TriTessFace *triangle; float tangents[3][3]; float normals[3][3]; float signs[3]; int j; float tangent[3]; float normal[3]; float binormal[3]; float sign; float u, v, w; float tsm[3][3]; /* tangent space matrix */ float itsm[3][3]; size_t offset; float nor[3]; /* texture normal */ bool is_smooth; int primitive_id = pixel_array[i].primitive_id; offset = i * depth; if (primitive_id == -1) { copy_v3_fl3(&result[offset], 0.5f, 0.5f, 1.0f); continue; } triangle = &triangles[primitive_id]; is_smooth = triangle->is_smooth; for (j = 0; j < 3; j++) { const TSpace *ts; if (is_smooth) normal_short_to_float_v3(normals[j], triangle->mverts[j]->no); else normal[j] = triangle->normal[j]; ts = triangle->tspace[j]; copy_v3_v3(tangents[j], ts->tangent); signs[j] = ts->sign; } u = pixel_array[i].uv[0]; v = pixel_array[i].uv[1]; w = 1.0f - u - v; /* normal */ if (is_smooth) interp_barycentric_tri_v3(normals, u, v, normal); /* tangent */ interp_barycentric_tri_v3(tangents, u, v, tangent); /* sign */ /* The sign is the same at all face vertices for any non degenerate face. * Just in case we clamp the interpolated value though. */ sign = (signs[0] * u + signs[1] * v + signs[2] * w) < 0 ? (-1.0f) : 1.0f; /* binormal */ /* B = sign * cross(N, T) */ cross_v3_v3v3(binormal, normal, tangent); mul_v3_fl(binormal, sign); /* populate tangent space matrix */ copy_v3_v3(tsm[0], tangent); copy_v3_v3(tsm[1], binormal); copy_v3_v3(tsm[2], normal); /* texture values */ normal_uncompress(nor, &result[offset]); /* converts from world space to local space */ mul_transposed_mat3_m4_v3(mat, nor); invert_m3_m3(itsm, tsm); mul_m3_v3(itsm, nor); normalize_v3(nor); /* save back the values */ normal_compress(&result[offset], nor, normal_swizzle); } /* garbage collection */ MEM_freeN(triangles); if (dm) dm->release(dm); }
static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc) { int i; //Options about projection direction const char use_normal = calc->smd->shrinkOpts; float proj_axis[3] = {0.0f, 0.0f, 0.0f}; //Raycast and tree stuff BVHTreeRayHit hit; BVHTreeFromMesh treeData= NULL_BVHTreeFromMesh; //auxiliary target DerivedMesh *auxMesh = NULL; BVHTreeFromMesh auxData = NULL_BVHTreeFromMesh; SpaceTransform local2aux; //If the user doesn't allows to project in any direction of projection axis //then theres nothing todo. if ((use_normal & (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 0) return; //Prepare data to retrieve the direction in which we should project each vertex if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { if (calc->vert == NULL) return; } else { //The code supports any axis that is a combination of X,Y,Z //although currently UI only allows to set the 3 different axis if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) proj_axis[0] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) proj_axis[1] = 1.0f; if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) proj_axis[2] = 1.0f; normalize_v3(proj_axis); //Invalid projection direction if (dot_v3v3(proj_axis, proj_axis) < FLT_EPSILON) return; } if (calc->smd->auxTarget) { auxMesh = object_get_derived_final(calc->smd->auxTarget); if (!auxMesh) return; space_transform_setup(&local2aux, calc->ob, calc->smd->auxTarget); } //After sucessufuly build the trees, start projection vertexs if (bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 4, 6) && (auxMesh == NULL || bvhtree_from_mesh_faces(&auxData, auxMesh, 0.0, 4, 6))) { #ifndef __APPLE__ #pragma omp parallel for private(i,hit) schedule(static) #endif for (i = 0; i<calc->numVerts; ++i) { float *co = calc->vertexCos[i]; float tmp_co[3], tmp_no[3]; float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); if (weight == 0.0f) continue; if (calc->vert) { /* calc->vert contains verts from derivedMesh */ /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { copy_v3_v3(tmp_co, calc->vert[i].co); normal_short_to_float_v3(tmp_no, calc->vert[i].no); } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } } else { copy_v3_v3(tmp_co, co); copy_v3_v3(tmp_no, proj_axis); } hit.index = -1; hit.dist = 10000.0f; //TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that //Project over positive direction of axis if (use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { if (auxData.tree) normal_projection_project_vertex(0, tmp_co, tmp_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, tmp_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } //Project over negative direction of axis if (use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR && hit.index == -1) { float inv_no[3]; negate_v3_v3(inv_no, tmp_no); if (auxData.tree) normal_projection_project_vertex(0, tmp_co, inv_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, inv_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); } if (hit.index != -1) { madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist); interp_v3_v3v3(co, co, hit.co, weight); } } } //free data structures free_bvhtree_from_mesh(&treeData); free_bvhtree_from_mesh(&auxData); }