static void particle_system_minmax(Scene *scene, Object *object, ParticleSystem *psys, float radius, float min[3], float max[3]) { const float size[3] = {radius, radius, radius}; const float cfra = BKE_scene_frame_get(scene); ParticleSettings *part = psys->part; ParticleSimulationData sim = {NULL}; ParticleData *pa = NULL; int i; int total_particles; float mat[4][4], imat[4][4]; INIT_MINMAX(min, max); if (part->type == PART_HAIR) { /* TOOD(sergey): Not supported currently. */ return; } unit_m4(mat); psys_render_set(object, psys, mat, mat, 1, 1, 0); sim.scene = scene; sim.ob = object; sim.psys = psys; sim.psmd = psys_get_modifier(object, psys); invert_m4_m4(imat, object->obmat); total_particles = psys->totpart + psys->totchild; psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); for (i = 0, pa = psys->particles; i < total_particles; i++, pa++) { float co_object[3], co_min[3], co_max[3]; ParticleKey state; state.time = cfra; if (!psys_get_particle_state(&sim, i, &state, 0)) { continue; } mul_v3_m4v3(co_object, imat, state.co); sub_v3_v3v3(co_min, co_object, size); add_v3_v3v3(co_max, co_object, size); minmax_v3v3_v3(min, max, co_min); minmax_v3v3_v3(min, max, co_max); } if (psys->lattice_deform_data) { end_latt_deform(psys->lattice_deform_data); psys->lattice_deform_data = NULL; } psys_render_restore(object, psys); }
bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) { const Mesh *me; const MPoly *mp; const MLoop *ml; const MVert *mvert; int a, b; bool ok = false; float vec[3], bmat[3][3]; me = BKE_mesh_from_object(ob); if (!me || !me->mloopuv) { return ok; } copy_m3_m4(bmat, ob->obmat); mvert = me->mvert; mp = me->mpoly; for (a = me->totpoly; a > 0; a--, mp++) { if (mp->flag & ME_HIDE || !(mp->flag & ME_FACE_SEL)) continue; ml = me->mloop + mp->totloop; for (b = 0; b < mp->totloop; b++, ml++) { mul_v3_m3v3(vec, bmat, mvert[ml->v].co); add_v3_v3v3(vec, vec, ob->obmat[3]); minmax_v3v3_v3(r_min, r_max, vec); } ok = true; } return ok; }
bool BKE_mball_minmax_ex(MetaBall *mb, float min[3], float max[3], float obmat[4][4], const short flag) { const float scale = obmat ? mat4_to_scale(obmat) : 1.0f; MetaElem *ml; bool changed = false; float centroid[3], vec[3]; INIT_MINMAX(min, max); for (ml = mb->elems.first; ml; ml = ml->next) { if ((ml->flag & flag) == flag) { const float scale_mb = (ml->rad * 0.5f) * scale; int i; if (obmat) { mul_v3_m4v3(centroid, obmat, &ml->x); } else { copy_v3_v3(centroid, &ml->x); } /* TODO, non circle shapes cubes etc, probably nobody notices - campbell */ for (i = -1; i != 3; i += 2) { copy_v3_v3(vec, centroid); add_v3_fl(vec, scale_mb * i); minmax_v3v3_v3(min, max, vec); } changed = true; } } return changed; }
/* find the bounding box of an objectinstance in global space */ void global_bounds_obi(Render *re, ObjectInstanceRen *obi, float bbmin[3], float bbmax[3]) { ObjectRen *obr = obi->obr; VolumePrecache *vp = obi->volume_precache; VertRen *ver= NULL; float co[3]; int a; if (vp->bbmin != NULL && vp->bbmax != NULL) { copy_v3_v3(bbmin, vp->bbmin); copy_v3_v3(bbmax, vp->bbmax); return; } vp->bbmin = MEM_callocN(sizeof(float)*3, "volume precache min boundbox corner"); vp->bbmax = MEM_callocN(sizeof(float)*3, "volume precache max boundbox corner"); INIT_MINMAX(bbmin, bbmax); for (a=0; a<obr->totvert; a++) { if ((a & 255)==0) ver= obr->vertnodes[a>>8].vert; else ver++; copy_v3_v3(co, ver->co); /* transformed object instance in camera space */ if (obi->flag & R_TRANSFORMED) mul_m4_v3(obi->mat, co); /* convert to global space */ mul_m4_v3(re->viewinv, co); minmax_v3v3_v3(vp->bbmin, vp->bbmax, co); }
void strand_minmax(StrandRen *strand, float min[3], float max[3], const float width) { StrandVert *svert; const float width2 = width * 2.0f; float vec[3]; int a; for (a=0, svert=strand->vert; a<strand->totvert; a++, svert++) { copy_v3_v3(vec, svert->co); minmax_v3v3_v3(min, max, vec); if (width!=0.0f) { add_v3_fl(vec, width); minmax_v3v3_v3(min, max, vec); add_v3_fl(vec, -width2); minmax_v3v3_v3(min, max, vec); } } }
void BKE_lattice_minmax(Lattice *lt, float min[3], float max[3]) { int i, numVerts; if (lt->editlatt) lt = lt->editlatt->latt; numVerts = lt->pntsu * lt->pntsv * lt->pntsw; for (i = 0; i < numVerts; i++) minmax_v3v3_v3(min, max, lt->def[i].vec); }
/* basic vertex data functions */ bool BKE_mball_minmax(MetaBall *mb, float min[3], float max[3]) { MetaElem *ml; INIT_MINMAX(min, max); for (ml = mb->elems.first; ml; ml = ml->next) { minmax_v3v3_v3(min, max, &ml->x); } return (BLI_listbase_is_empty(&mb->elems) == false); }
static void bundle_midpoint(Scene *scene, Object *ob, float vec[3]) { MovieClip *clip = BKE_object_movieclip_get(scene, ob, false); MovieTracking *tracking; MovieTrackingObject *object; bool ok = false; float min[3], max[3], mat[4][4], pos[3], cammat[4][4]; if (!clip) return; tracking = &clip->tracking; copy_m4_m4(cammat, ob->obmat); BKE_tracking_get_camera_object_matrix(scene, ob, mat); INIT_MINMAX(min, max); for (object = tracking->objects.first; object; object = object->next) { ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, object); MovieTrackingTrack *track = tracksbase->first; float obmat[4][4]; if (object->flag & TRACKING_OBJECT_CAMERA) { copy_m4_m4(obmat, mat); } else { float imat[4][4]; BKE_tracking_camera_get_reconstructed_interpolate(tracking, object, scene->r.cfra, imat); invert_m4(imat); mul_m4_m4m4(obmat, cammat, imat); } while (track) { if ((track->flag & TRACK_HAS_BUNDLE) && TRACK_SELECTED(track)) { ok = 1; mul_v3_m4v3(pos, obmat, track->bundle_pos); minmax_v3v3_v3(min, max, pos); } track = track->next; } } if (ok) { mid_v3_v3v3(vec, min, max); } }
/* Raytracing for vertex to bone/vertex visibility */ static void heat_ray_tree_create(LaplacianSystem *sys) { const MLoopTri *looptri = sys->heat.mlooptri; const MLoop *mloop = sys->heat.mloop; float (*verts)[3] = sys->heat.verts; int tottri = sys->heat.tottri; int totvert = sys->heat.totvert; int a; sys->heat.bvhtree = BLI_bvhtree_new(tottri, 0.0f, 4, 6); sys->heat.vltree = MEM_callocN(sizeof(MLoopTri *) * totvert, "HeatVFaces"); for (a = 0; a < tottri; a++) { const MLoopTri *lt = &looptri[a]; float bb[6]; int vtri[3]; vtri[0] = mloop[lt->tri[0]].v; vtri[1] = mloop[lt->tri[1]].v; vtri[2] = mloop[lt->tri[2]].v; INIT_MINMAX(bb, bb + 3); minmax_v3v3_v3(bb, bb + 3, verts[vtri[0]]); minmax_v3v3_v3(bb, bb + 3, verts[vtri[1]]); minmax_v3v3_v3(bb, bb + 3, verts[vtri[2]]); BLI_bvhtree_insert(sys->heat.bvhtree, a, bb, 2); //Setup inverse pointers to use on isect.orig sys->heat.vltree[vtri[0]] = lt; sys->heat.vltree[vtri[1]] = lt; sys->heat.vltree[vtri[2]] = lt; } BLI_bvhtree_balance(sys->heat.bvhtree); }
/* ******************************************************** */ bool ED_gpencil_stroke_minmax( const bGPDstroke *gps, const bool use_select, float r_min[3], float r_max[3]) { const bGPDspoint *pt; int i; bool changed = false; for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) {; minmax_v3v3_v3(r_min, r_max, &pt->x); changed = true; } } return changed; }
void BKE_lattice_minmax_dl(Object *ob, Lattice *lt, float min[3], float max[3]) { DispList *dl = ob->curve_cache ? BKE_displist_find(&ob->curve_cache->disp, DL_VERTS) : NULL; if (!dl) { BKE_lattice_minmax(lt, min, max); } else { int i, numVerts; if (lt->editlatt) lt = lt->editlatt->latt; numVerts = lt->pntsu * lt->pntsv * lt->pntsw; for (i = 0; i < numVerts; i++) minmax_v3v3_v3(min, max, &dl->verts[i * 3]); } }
/** Compute bounding box of all MetaElems/MetaBalls. * * Bounding box is computed from polygonized surface. Object *ob is * basic MetaBall (usually with name Meta). All other MetaBalls (with * names Meta.001, Meta.002, etc) are included in this Bounding Box. */ void BKE_mball_texspace_calc(Object *ob) { DispList *dl; BoundBox *bb; float *data, min[3], max[3] /*, loc[3], size[3] */; int tot; bool do_it = false; if (ob->bb == NULL) ob->bb = MEM_callocN(sizeof(BoundBox), "mb boundbox"); bb = ob->bb; /* Weird one, this. */ /* INIT_MINMAX(min, max); */ (min)[0] = (min)[1] = (min)[2] = 1.0e30f; (max)[0] = (max)[1] = (max)[2] = -1.0e30f; dl = ob->curve_cache->disp.first; while (dl) { tot = dl->nr; if (tot) do_it = true; data = dl->verts; while (tot--) { /* Also weird... but longer. From utildefines. */ minmax_v3v3_v3(min, max, data); data += 3; } dl = dl->next; } if (!do_it) { min[0] = min[1] = min[2] = -1.0f; max[0] = max[1] = max[2] = 1.0f; } #if 0 loc[0] = (min[0] + max[0]) / 2.0f; loc[1] = (min[1] + max[1]) / 2.0f; loc[2] = (min[2] + max[2]) / 2.0f; size[0] = (max[0] - min[0]) / 2.0f; size[1] = (max[1] - min[1]) / 2.0f; size[2] = (max[2] - min[2]) / 2.0f; #endif BKE_boundbox_init_from_minmax(bb, min, max); }
ScatterTree *scatter_tree_new(ScatterSettings *ss[3], float scale, float error, float (*co)[3], float (*color)[3], float *area, int totpoint) { ScatterTree *tree; ScatterPoint *points, **refpoints; int i; /* allocate tree */ tree= MEM_callocN(sizeof(ScatterTree), "ScatterTree"); tree->scale= scale; tree->error= error; tree->totpoint= totpoint; tree->ss[0]= ss[0]; tree->ss[1]= ss[1]; tree->ss[2]= ss[2]; points = MEM_callocN(sizeof(ScatterPoint) * totpoint, "ScatterPoints"); refpoints = MEM_callocN(sizeof(ScatterPoint *) * totpoint, "ScatterRefPoints"); tree->points= points; tree->refpoints= refpoints; /* build points */ INIT_MINMAX(tree->min, tree->max); for (i=0; i<totpoint; i++) { copy_v3_v3(points[i].co, co[i]); copy_v3_v3(points[i].rad, color[i]); points[i].area= fabsf(area[i])/(tree->scale*tree->scale); points[i].back= (area[i] < 0.0f); mul_v3_fl(points[i].co, 1.0f / tree->scale); minmax_v3v3_v3(tree->min, tree->max, points[i].co); refpoints[i]= points + i; } return tree; }
static int occ_find_bbox_axis(OcclusionTree *tree, int begin, int end, float *min, float *max) { float len, maxlen = -1.0f; int a, axis = 0; INIT_MINMAX(min, max); for (a = begin; a < end; a++) { minmax_v3v3_v3(min, max, tree->co[a]); } for (a = 0; a < 3; a++) { len = max[a] - min[a]; if (len > maxlen) { maxlen = len; axis = a; } } return axis; }
void curve_deform_verts(Scene *scene, Object *cuOb, Object *target, DerivedMesh *dm, float (*vertexCos)[3], int numVerts, const char *vgroup, short defaxis) { Curve *cu; int a, flag; CurveDeform cd; int use_vgroups; const int is_neg_axis = (defaxis > 2); if (cuOb->type != OB_CURVE) return; cu = cuOb->data; flag = cu->flag; cu->flag |= (CU_PATH | CU_FOLLOW); // needed for path & bevlist init_curve_deform(cuOb, target, &cd); /* dummy bounds, keep if CU_DEFORM_BOUNDS_OFF is set */ if (is_neg_axis == FALSE) { cd.dmin[0] = cd.dmin[1] = cd.dmin[2] = 0.0f; cd.dmax[0] = cd.dmax[1] = cd.dmax[2] = 1.0f; } else { /* negative, these bounds give a good rest position */ cd.dmin[0] = cd.dmin[1] = cd.dmin[2] = -1.0f; cd.dmax[0] = cd.dmax[1] = cd.dmax[2] = 0.0f; } /* check whether to use vertex groups (only possible if target is a Mesh) * we want either a Mesh with no derived data, or derived data with * deformverts */ if (target && target->type == OB_MESH) { /* if there's derived data without deformverts, don't use vgroups */ if (dm) { use_vgroups = (dm->getVertData(dm, 0, CD_MDEFORMVERT) != NULL); } else { Mesh *me = target->data; use_vgroups = (me->dvert != NULL); } } else { use_vgroups = FALSE; } if (vgroup && vgroup[0] && use_vgroups) { Mesh *me = target->data; int index = defgroup_name_index(target, vgroup); if (index != -1 && (me->dvert || dm)) { MDeformVert *dvert = me->dvert; float vec[3]; float weight; if (cu->flag & CU_DEFORM_BOUNDS_OFF) { dvert = me->dvert; for (a = 0; a < numVerts; a++, dvert++) { if (dm) dvert = dm->getVertData(dm, a, CD_MDEFORMVERT); weight = defvert_find_weight(dvert, index); if (weight > 0.0f) { mul_m4_v3(cd.curvespace, vertexCos[a]); copy_v3_v3(vec, vertexCos[a]); calc_curve_deform(scene, cuOb, vec, defaxis, &cd, NULL); interp_v3_v3v3(vertexCos[a], vertexCos[a], vec, weight); mul_m4_v3(cd.objectspace, vertexCos[a]); } } } else { /* set mesh min/max bounds */ INIT_MINMAX(cd.dmin, cd.dmax); for (a = 0; a < numVerts; a++, dvert++) { if (dm) dvert = dm->getVertData(dm, a, CD_MDEFORMVERT); if (defvert_find_weight(dvert, index) > 0.0f) { mul_m4_v3(cd.curvespace, vertexCos[a]); minmax_v3v3_v3(cd.dmin, cd.dmax, vertexCos[a]); } } dvert = me->dvert; for (a = 0; a < numVerts; a++, dvert++) { if (dm) dvert = dm->getVertData(dm, a, CD_MDEFORMVERT); weight = defvert_find_weight(dvert, index); if (weight > 0.0f) { /* already in 'cd.curvespace', prev for loop */ copy_v3_v3(vec, vertexCos[a]); calc_curve_deform(scene, cuOb, vec, defaxis, &cd, NULL); interp_v3_v3v3(vertexCos[a], vertexCos[a], vec, weight); mul_m4_v3(cd.objectspace, vertexCos[a]); } } } } } else { if (cu->flag & CU_DEFORM_BOUNDS_OFF) { for (a = 0; a < numVerts; a++) { mul_m4_v3(cd.curvespace, vertexCos[a]); calc_curve_deform(scene, cuOb, vertexCos[a], defaxis, &cd, NULL); mul_m4_v3(cd.objectspace, vertexCos[a]); } } else { /* set mesh min max bounds */ INIT_MINMAX(cd.dmin, cd.dmax); for (a = 0; a < numVerts; a++) { mul_m4_v3(cd.curvespace, vertexCos[a]); minmax_v3v3_v3(cd.dmin, cd.dmax, vertexCos[a]); } for (a = 0; a < numVerts; a++) { /* already in 'cd.curvespace', prev for loop */ calc_curve_deform(scene, cuOb, vertexCos[a], defaxis, &cd, NULL); mul_m4_v3(cd.objectspace, vertexCos[a]); } } } cu->flag = flag; }
static void scopes_update_cb(void *userdata, void *userdata_chunk, const int y, const int UNUSED(threadid)) { const ScopesUpdateData *data = userdata; Scopes *scopes = data->scopes; const ImBuf *ibuf = data->ibuf; struct ColormanageProcessor *cm_processor = data->cm_processor; const unsigned char *display_buffer = data->display_buffer; const int ycc_mode = data->ycc_mode; ScopesUpdateDataChunk *data_chunk = userdata_chunk; unsigned int *bin_lum = data_chunk->bin_lum; unsigned int *bin_r = data_chunk->bin_r; unsigned int *bin_g = data_chunk->bin_g; unsigned int *bin_b = data_chunk->bin_b; unsigned int *bin_a = data_chunk->bin_a; float *min = data_chunk->min; float *max = data_chunk->max; const float *rf = NULL; const unsigned char *rc = NULL; const int rows_per_sample_line = ibuf->y / scopes->sample_lines; const int savedlines = y / rows_per_sample_line; const bool do_sample_line = (savedlines < scopes->sample_lines) && (y % rows_per_sample_line) == 0; const bool is_float = (ibuf->rect_float != NULL); if (is_float) rf = ibuf->rect_float + ((size_t)y) * ibuf->x * ibuf->channels; else { rc = display_buffer + ((size_t)y) * ibuf->x * ibuf->channels; } for (int x = 0; x < ibuf->x; x++) { float rgba[4], ycc[3], luma; if (is_float) { switch (ibuf->channels) { case 4: copy_v4_v4(rgba, rf); IMB_colormanagement_processor_apply_v4(cm_processor, rgba); break; case 3: copy_v3_v3(rgba, rf); IMB_colormanagement_processor_apply_v3(cm_processor, rgba); rgba[3] = 1.0f; break; case 2: copy_v3_fl(rgba, rf[0]); rgba[3] = rf[1]; break; case 1: copy_v3_fl(rgba, rf[0]); rgba[3] = 1.0f; break; default: BLI_assert(0); } } else { for (int c = 4; c--;) rgba[c] = rc[c] * INV_255; } /* we still need luma for histogram */ luma = IMB_colormanagement_get_luminance(rgba); /* check for min max */ if (ycc_mode == -1) { minmax_v3v3_v3(min, max, rgba); } else { rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode); mul_v3_fl(ycc, INV_255); minmax_v3v3_v3(min, max, ycc); } /* increment count for histo*/ bin_lum[get_bin_float(luma)]++; bin_r[get_bin_float(rgba[0])]++; bin_g[get_bin_float(rgba[1])]++; bin_b[get_bin_float(rgba[2])]++; bin_a[get_bin_float(rgba[3])]++; /* save sample if needed */ if (do_sample_line) { const float fx = (float)x / (float)ibuf->x; const int idx = 2 * (ibuf->x * savedlines + x); save_sample_line(scopes, idx, fx, rgba, ycc); } rf += ibuf->channels; rc += ibuf->channels; } }
static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]) { Object *obedit = CTX_data_edit_object(C); Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); TransVertStore tvs = {NULL}; TransVert *tv; float bmat[3][3], vec[3], min[3], max[3], centroid[3]; int count, a; count = 0; INIT_MINMAX(min, max); zero_v3(centroid); if (obedit) { if (ED_transverts_check_obedit(obedit)) ED_transverts_create_from_obedit(&tvs, obedit, TM_ALL_JOINTS | TM_SKIP_HANDLES); if (tvs.transverts_tot == 0) { return false; } copy_m3_m4(bmat, obedit->obmat); tv = tvs.transverts; for (a = 0; a < tvs.transverts_tot; a++, tv++) { copy_v3_v3(vec, tv->loc); mul_m3_v3(bmat, vec); add_v3_v3(vec, obedit->obmat[3]); add_v3_v3(centroid, vec); minmax_v3v3_v3(min, max, vec); } if (v3d->around == V3D_AROUND_CENTER_MEAN) { mul_v3_fl(centroid, 1.0f / (float)tvs.transverts_tot); copy_v3_v3(cursor, centroid); } else { mid_v3_v3v3(cursor, min, max); } ED_transverts_free(&tvs); } else { Object *obact = CTX_data_active_object(C); if (obact && (obact->mode & OB_MODE_POSE)) { bArmature *arm = obact->data; bPoseChannel *pchan; for (pchan = obact->pose->chanbase.first; pchan; pchan = pchan->next) { if (arm->layer & pchan->bone->layer) { if (pchan->bone->flag & BONE_SELECTED) { copy_v3_v3(vec, pchan->pose_head); mul_m4_v3(obact->obmat, vec); add_v3_v3(centroid, vec); minmax_v3v3_v3(min, max, vec); count++; } } } } else { CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { copy_v3_v3(vec, ob->obmat[3]); /* special case for camera -- snap to bundles */ if (ob->type == OB_CAMERA) { /* snap to bundles should happen only when bundles are visible */ if (v3d->flag2 & V3D_SHOW_RECONSTRUCTION) { bundle_midpoint(scene, ob, vec); } } add_v3_v3(centroid, vec); minmax_v3v3_v3(min, max, vec); count++; } CTX_DATA_END; } if (count == 0) { return false; } if (v3d->around == V3D_AROUND_CENTER_MEAN) { mul_v3_fl(centroid, 1.0f / (float)count); copy_v3_v3(cursor, centroid); } else { mid_v3_v3v3(cursor, min, max); } }
void curve_deform_verts(Object *cuOb, Object *target, float (*vertexCos)[3], int numVerts, MDeformVert *dvert, const int defgrp_index, short defaxis) { Curve *cu; int a; CurveDeform cd; const bool is_neg_axis = (defaxis > 2); if (cuOb->type != OB_CURVE) { return; } cu = cuOb->data; init_curve_deform(cuOb, target, &cd); /* dummy bounds, keep if CU_DEFORM_BOUNDS_OFF is set */ if (is_neg_axis == false) { cd.dmin[0] = cd.dmin[1] = cd.dmin[2] = 0.0f; cd.dmax[0] = cd.dmax[1] = cd.dmax[2] = 1.0f; } else { /* negative, these bounds give a good rest position */ cd.dmin[0] = cd.dmin[1] = cd.dmin[2] = -1.0f; cd.dmax[0] = cd.dmax[1] = cd.dmax[2] = 0.0f; } if (dvert) { MDeformVert *dvert_iter; float vec[3]; if (cu->flag & CU_DEFORM_BOUNDS_OFF) { for (a = 0, dvert_iter = dvert; a < numVerts; a++, dvert_iter++) { const float weight = defvert_find_weight(dvert_iter, defgrp_index); if (weight > 0.0f) { mul_m4_v3(cd.curvespace, vertexCos[a]); copy_v3_v3(vec, vertexCos[a]); calc_curve_deform(cuOb, vec, defaxis, &cd, NULL); interp_v3_v3v3(vertexCos[a], vertexCos[a], vec, weight); mul_m4_v3(cd.objectspace, vertexCos[a]); } } } else { /* set mesh min/max bounds */ INIT_MINMAX(cd.dmin, cd.dmax); for (a = 0, dvert_iter = dvert; a < numVerts; a++, dvert_iter++) { if (defvert_find_weight(dvert_iter, defgrp_index) > 0.0f) { mul_m4_v3(cd.curvespace, vertexCos[a]); minmax_v3v3_v3(cd.dmin, cd.dmax, vertexCos[a]); } } for (a = 0, dvert_iter = dvert; a < numVerts; a++, dvert_iter++) { const float weight = defvert_find_weight(dvert_iter, defgrp_index); if (weight > 0.0f) { /* already in 'cd.curvespace', prev for loop */ copy_v3_v3(vec, vertexCos[a]); calc_curve_deform(cuOb, vec, defaxis, &cd, NULL); interp_v3_v3v3(vertexCos[a], vertexCos[a], vec, weight); mul_m4_v3(cd.objectspace, vertexCos[a]); } } } } else { if (cu->flag & CU_DEFORM_BOUNDS_OFF) { for (a = 0; a < numVerts; a++) { mul_m4_v3(cd.curvespace, vertexCos[a]); calc_curve_deform(cuOb, vertexCos[a], defaxis, &cd, NULL); mul_m4_v3(cd.objectspace, vertexCos[a]); } } else { /* set mesh min max bounds */ INIT_MINMAX(cd.dmin, cd.dmax); for (a = 0; a < numVerts; a++) { mul_m4_v3(cd.curvespace, vertexCos[a]); minmax_v3v3_v3(cd.dmin, cd.dmax, vertexCos[a]); } for (a = 0; a < numVerts; a++) { /* already in 'cd.curvespace', prev for loop */ calc_curve_deform(cuOb, vertexCos[a], defaxis, &cd, NULL); mul_m4_v3(cd.objectspace, vertexCos[a]); } } } }
static void distribute_grid(DerivedMesh *dm, ParticleSystem *psys) { ParticleData *pa=NULL; float min[3], max[3], delta[3], d; MVert *mv, *mvert = dm->getVertDataArray(dm,0); int totvert=dm->getNumVerts(dm), from=psys->part->from; int i, j, k, p, res=psys->part->grid_res, size[3], axis; /* find bounding box of dm */ if (totvert > 0) { mv=mvert; copy_v3_v3(min, mv->co); copy_v3_v3(max, mv->co); mv++; for (i = 1; i < totvert; i++, mv++) { minmax_v3v3_v3(min, max, mv->co); } } else { zero_v3(min); zero_v3(max); } sub_v3_v3v3(delta, max, min); /* determine major axis */ axis = axis_dominant_v3_single(delta); d = delta[axis]/(float)res; size[axis] = res; size[(axis+1)%3] = (int)ceil(delta[(axis+1)%3]/d); size[(axis+2)%3] = (int)ceil(delta[(axis+2)%3]/d); /* float errors grrr.. */ size[(axis+1)%3] = MIN2(size[(axis+1)%3],res); size[(axis+2)%3] = MIN2(size[(axis+2)%3],res); size[0] = MAX2(size[0], 1); size[1] = MAX2(size[1], 1); size[2] = MAX2(size[2], 1); /* no full offset for flat/thin objects */ min[0]+= d < delta[0] ? d/2.f : delta[0]/2.f; min[1]+= d < delta[1] ? d/2.f : delta[1]/2.f; min[2]+= d < delta[2] ? d/2.f : delta[2]/2.f; for (i=0,p=0,pa=psys->particles; i<res; i++) { for (j=0; j<res; j++) { for (k=0; k<res; k++,p++,pa++) { pa->fuv[0] = min[0] + (float)i*d; pa->fuv[1] = min[1] + (float)j*d; pa->fuv[2] = min[2] + (float)k*d; pa->flag |= PARS_UNEXIST; pa->hair_index = 0; /* abused in volume calculation */ } } } /* enable particles near verts/edges/faces/inside surface */ if (from==PART_FROM_VERT) { float vec[3]; pa=psys->particles; min[0] -= d/2.0f; min[1] -= d/2.0f; min[2] -= d/2.0f; for (i=0,mv=mvert; i<totvert; i++,mv++) { sub_v3_v3v3(vec,mv->co,min); vec[0]/=delta[0]; vec[1]/=delta[1]; vec[2]/=delta[2]; pa[((int)(vec[0] * (size[0] - 1)) * res + (int)(vec[1] * (size[1] - 1))) * res + (int)(vec[2] * (size[2] - 1))].flag &= ~PARS_UNEXIST; } } else if (ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) { float co1[3], co2[3]; MFace *mface= NULL, *mface_array; float v1[3], v2[3], v3[3], v4[4], lambda; int a, a1, a2, a0mul, a1mul, a2mul, totface; int amax= from==PART_FROM_FACE ? 3 : 1; totface=dm->getNumTessFaces(dm); mface=mface_array=dm->getTessFaceDataArray(dm,CD_MFACE); for (a=0; a<amax; a++) { if (a==0) { a0mul=res*res; a1mul=res; a2mul=1; } else if (a==1) { a0mul=res; a1mul=1; a2mul=res*res; } else { a0mul=1; a1mul=res*res; a2mul=res; } for (a1=0; a1<size[(a+1)%3]; a1++) { for (a2=0; a2<size[(a+2)%3]; a2++) { mface= mface_array; pa = psys->particles + a1*a1mul + a2*a2mul; copy_v3_v3(co1, pa->fuv); co1[a] -= d < delta[a] ? d/2.f : delta[a]/2.f; copy_v3_v3(co2, co1); co2[a] += delta[a] + 0.001f*d; co1[a] -= 0.001f*d; struct IsectRayPrecalc isect_precalc; float ray_direction[3]; sub_v3_v3v3(ray_direction, co2, co1); isect_ray_tri_watertight_v3_precalc(&isect_precalc, ray_direction); /* lets intersect the faces */ for (i=0; i<totface; i++,mface++) { copy_v3_v3(v1, mvert[mface->v1].co); copy_v3_v3(v2, mvert[mface->v2].co); copy_v3_v3(v3, mvert[mface->v3].co); bool intersects_tri = isect_ray_tri_watertight_v3(co1, &isect_precalc, v1, v2, v3, &lambda, NULL); if (intersects_tri) { if (from==PART_FROM_FACE) (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; else /* store number of intersections */ (pa+(int)(lambda*size[a])*a0mul)->hair_index++; } if (mface->v4 && (!intersects_tri || from==PART_FROM_VOLUME)) { copy_v3_v3(v4, mvert[mface->v4].co); if (isect_ray_tri_watertight_v3( co1, &isect_precalc, v1, v3, v4, &lambda, NULL)) { if (from==PART_FROM_FACE) (pa+(int)(lambda*size[a])*a0mul)->flag &= ~PARS_UNEXIST; else (pa+(int)(lambda*size[a])*a0mul)->hair_index++; } } } if (from==PART_FROM_VOLUME) { int in=pa->hair_index%2; if (in) pa->hair_index++; for (i=0; i<size[0]; i++) { if (in || (pa+i*a0mul)->hair_index%2) (pa+i*a0mul)->flag &= ~PARS_UNEXIST; /* odd intersections == in->out / out->in */ /* even intersections -> in stays same */ in=(in + (pa+i*a0mul)->hair_index) % 2; } } } } } } if (psys->part->flag & PART_GRID_HEXAGONAL) { for (i=0,p=0,pa=psys->particles; i<res; i++) { for (j=0; j<res; j++) { for (k=0; k<res; k++,p++,pa++) { if (j%2) pa->fuv[0] += d/2.f; if (k%2) { pa->fuv[0] += d/2.f; pa->fuv[1] += d/2.f; } } } } } if (psys->part->flag & PART_GRID_INVERT) { for (i=0; i<size[0]; i++) { for (j=0; j<size[1]; j++) { pa=psys->particles + res*(i*res + j); for (k=0; k<size[2]; k++, pa++) { pa->flag ^= PARS_UNEXIST; } } } } if (psys->part->grid_rand > 0.f) { float rfac = d * psys->part->grid_rand; for (p=0,pa=psys->particles; p<psys->totpart; p++,pa++) { if (pa->flag & PARS_UNEXIST) continue; pa->fuv[0] += rfac * (psys_frand(psys, p + 31) - 0.5f); pa->fuv[1] += rfac * (psys_frand(psys, p + 32) - 0.5f); pa->fuv[2] += rfac * (psys_frand(psys, p + 33) - 0.5f); } } }
static void cuboid_do( CastModifierData *cmd, Object *ob, DerivedMesh *dm, float (*vertexCos)[3], int numVerts) { MDeformVert *dvert = NULL; Object *ctrl_ob = NULL; int i, defgrp_index; bool has_radius = false; short flag; float fac = cmd->fac; float facm = 1.0f - fac; const float fac_orig = fac; float min[3], max[3], bb[8][3]; float center[3] = {0.0f, 0.0f, 0.0f}; float mat[4][4], imat[4][4]; flag = cmd->flag; ctrl_ob = cmd->object; /* now we check which options the user wants */ /* 1) (flag was checked in the "if (ctrl_ob)" block above) */ /* 2) cmd->radius > 0.0f: only the vertices within this radius from * the center of the effect should be deformed */ if (cmd->radius > FLT_EPSILON) has_radius = 1; /* 3) if we were given a vertex group name, * only those vertices should be affected */ modifier_get_vgroup(ob, dm, cmd->defgrp_name, &dvert, &defgrp_index); if (ctrl_ob) { if (flag & MOD_CAST_USE_OB_TRANSFORM) { invert_m4_m4(imat, ctrl_ob->obmat); mul_m4_m4m4(mat, imat, ob->obmat); invert_m4_m4(imat, mat); } invert_m4_m4(ob->imat, ob->obmat); mul_v3_m4v3(center, ob->imat, ctrl_ob->obmat[3]); } if ((flag & MOD_CAST_SIZE_FROM_RADIUS) && has_radius) { for (i = 0; i < 3; i++) { min[i] = -cmd->radius; max[i] = cmd->radius; } } else if (!(flag & MOD_CAST_SIZE_FROM_RADIUS) && cmd->size > 0) { for (i = 0; i < 3; i++) { min[i] = -cmd->size; max[i] = cmd->size; } } else { /* get bound box */ /* We can't use the object's bound box because other modifiers * may have changed the vertex data. */ INIT_MINMAX(min, max); /* Cast's center is the ob's own center in its local space, * by default, but if the user defined a control object, we use * its location, transformed to ob's local space. */ if (ctrl_ob) { float vec[3]; /* let the center of the ctrl_ob be part of the bound box: */ minmax_v3v3_v3(min, max, center); for (i = 0; i < numVerts; i++) { sub_v3_v3v3(vec, vertexCos[i], center); minmax_v3v3_v3(min, max, vec); } } else { for (i = 0; i < numVerts; i++) { minmax_v3v3_v3(min, max, vertexCos[i]); } } /* we want a symmetric bound box around the origin */ if (fabsf(min[0]) > fabsf(max[0])) max[0] = fabsf(min[0]); if (fabsf(min[1]) > fabsf(max[1])) max[1] = fabsf(min[1]); if (fabsf(min[2]) > fabsf(max[2])) max[2] = fabsf(min[2]); min[0] = -max[0]; min[1] = -max[1]; min[2] = -max[2]; } /* building our custom bounding box */ bb[0][0] = bb[2][0] = bb[4][0] = bb[6][0] = min[0]; bb[1][0] = bb[3][0] = bb[5][0] = bb[7][0] = max[0]; bb[0][1] = bb[1][1] = bb[4][1] = bb[5][1] = min[1]; bb[2][1] = bb[3][1] = bb[6][1] = bb[7][1] = max[1]; bb[0][2] = bb[1][2] = bb[2][2] = bb[3][2] = min[2]; bb[4][2] = bb[5][2] = bb[6][2] = bb[7][2] = max[2]; /* ready to apply the effect, one vertex at a time */ for (i = 0; i < numVerts; i++) { int octant, coord; float d[3], dmax, apex[3], fbb; float tmp_co[3]; copy_v3_v3(tmp_co, vertexCos[i]); if (ctrl_ob) { if (flag & MOD_CAST_USE_OB_TRANSFORM) { mul_m4_v3(mat, tmp_co); } else { sub_v3_v3(tmp_co, center); } } if (has_radius) { if (fabsf(tmp_co[0]) > cmd->radius || fabsf(tmp_co[1]) > cmd->radius || fabsf(tmp_co[2]) > cmd->radius) { continue; } } if (dvert) { const float weight = defvert_find_weight(&dvert[i], defgrp_index); if (weight == 0.0f) { continue; } fac = fac_orig * weight; facm = 1.0f - fac; } /* The algo used to project the vertices to their * bounding box (bb) is pretty simple: * for each vertex v: * 1) find in which octant v is in; * 2) find which outer "wall" of that octant is closer to v; * 3) calculate factor (var fbb) to project v to that wall; * 4) project. */ /* find in which octant this vertex is in */ octant = 0; if (tmp_co[0] > 0.0f) octant += 1; if (tmp_co[1] > 0.0f) octant += 2; if (tmp_co[2] > 0.0f) octant += 4; /* apex is the bb's vertex at the chosen octant */ copy_v3_v3(apex, bb[octant]); /* find which bb plane is closest to this vertex ... */ d[0] = tmp_co[0] / apex[0]; d[1] = tmp_co[1] / apex[1]; d[2] = tmp_co[2] / apex[2]; /* ... (the closest has the higher (closer to 1) d value) */ dmax = d[0]; coord = 0; if (d[1] > dmax) { dmax = d[1]; coord = 1; } if (d[2] > dmax) { /* dmax = d[2]; */ /* commented, we don't need it */ coord = 2; } /* ok, now we know which coordinate of the vertex to use */ if (fabsf(tmp_co[coord]) < FLT_EPSILON) /* avoid division by zero */ continue; /* finally, this is the factor we wanted, to project the vertex * to its bounding box (bb) */ fbb = apex[coord] / tmp_co[coord]; /* calculate the new vertex position */ if (flag & MOD_CAST_X) tmp_co[0] = facm * tmp_co[0] + fac * tmp_co[0] * fbb; if (flag & MOD_CAST_Y) tmp_co[1] = facm * tmp_co[1] + fac * tmp_co[1] * fbb; if (flag & MOD_CAST_Z) tmp_co[2] = facm * tmp_co[2] + fac * tmp_co[2] * fbb; if (ctrl_ob) { if (flag & MOD_CAST_USE_OB_TRANSFORM) { mul_m4_v3(imat, tmp_co); } else { add_v3_v3(tmp_co, center); } } copy_v3_v3(vertexCos[i], tmp_co); } }
/* 0 == do center, 1 == center new, 2 == center cursor */ void ED_armature_origin_set(Scene *scene, Object *ob, float cursor[3], int centermode, int around) { Object *obedit = scene->obedit; // XXX get from context EditBone *ebone; bArmature *arm = ob->data; float cent[3]; /* Put the armature into editmode */ if (ob != obedit) { ED_armature_to_edit(ob); obedit = NULL; /* we cant use this so behave as if there is no obedit */ } /* Find the centerpoint */ if (centermode == 2) { copy_v3_v3(cent, cursor); invert_m4_m4(ob->imat, ob->obmat); mul_m4_v3(ob->imat, cent); } else { if (around == V3D_CENTROID) { int total = 0; zero_v3(cent); for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { total += 2; add_v3_v3(cent, ebone->head); add_v3_v3(cent, ebone->tail); } if (total) { mul_v3_fl(cent, 1.0f / (float)total); } } else { float min[3], max[3]; INIT_MINMAX(min, max); for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { minmax_v3v3_v3(min, max, ebone->head); minmax_v3v3_v3(min, max, ebone->tail); } mid_v3_v3v3(cent, min, max); } } /* Do the adjustments */ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { sub_v3_v3(ebone->head, cent); sub_v3_v3(ebone->tail, cent); } /* Turn the list into an armature */ if (obedit == NULL) { ED_armature_from_edit(ob); ED_armature_edit_free(ob); } /* Adjust object location for new centerpoint */ if (centermode && obedit == NULL) { mul_mat3_m4_v3(ob->obmat, cent); /* ommit translation part */ add_v3_v3(ob->loc, cent); } }
static void harmonic_coordinates_bind(Scene *UNUSED(scene), MeshDeformModifierData *mmd, MeshDeformBind *mdb) { MDefBindInfluence *inf; MDefInfluence *mdinf; MDefCell *cell; float center[3], vec[3], maxwidth, totweight; int a, b, x, y, z, totinside, offset; /* compute bounding box of the cage mesh */ INIT_MINMAX(mdb->min, mdb->max); for (a = 0; a < mdb->totcagevert; a++) minmax_v3v3_v3(mdb->min, mdb->max, mdb->cagecos[a]); /* allocate memory */ mdb->size = (2 << (mmd->gridsize - 1)) + 2; mdb->size3 = mdb->size * mdb->size * mdb->size; mdb->tag = MEM_callocN(sizeof(int) * mdb->size3, "MeshDeformBindTag"); mdb->phi = MEM_callocN(sizeof(float) * mdb->size3, "MeshDeformBindPhi"); mdb->totalphi = MEM_callocN(sizeof(float) * mdb->size3, "MeshDeformBindTotalPhi"); mdb->boundisect = MEM_callocN(sizeof(*mdb->boundisect) * mdb->size3, "MDefBoundIsect"); mdb->semibound = MEM_callocN(sizeof(int) * mdb->size3, "MDefSemiBound"); mdb->bvhtree = bvhtree_from_mesh_looptri(&mdb->bvhdata, mdb->cagedm, FLT_EPSILON * 100, 4, 6); mdb->inside = MEM_callocN(sizeof(int) * mdb->totvert, "MDefInside"); if (mmd->flag & MOD_MDEF_DYNAMIC_BIND) mdb->dyngrid = MEM_callocN(sizeof(MDefBindInfluence *) * mdb->size3, "MDefDynGrid"); else mdb->weights = MEM_callocN(sizeof(float) * mdb->totvert * mdb->totcagevert, "MDefWeights"); mdb->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "harmonic coords arena"); BLI_memarena_use_calloc(mdb->memarena); /* initialize data from 'cagedm' for reuse */ { DerivedMesh *dm = mdb->cagedm; mdb->cagedm_cache.mpoly = dm->getPolyArray(dm); mdb->cagedm_cache.mloop = dm->getLoopArray(dm); mdb->cagedm_cache.looptri = dm->getLoopTriArray(dm); mdb->cagedm_cache.poly_nors = dm->getPolyDataArray(dm, CD_NORMAL); /* can be NULL */ } /* make bounding box equal size in all directions, add padding, and compute * width of the cells */ maxwidth = -1.0f; for (a = 0; a < 3; a++) if (mdb->max[a] - mdb->min[a] > maxwidth) maxwidth = mdb->max[a] - mdb->min[a]; for (a = 0; a < 3; a++) { center[a] = (mdb->min[a] + mdb->max[a]) * 0.5f; mdb->min[a] = center[a] - maxwidth * 0.5f; mdb->max[a] = center[a] + maxwidth * 0.5f; mdb->width[a] = (mdb->max[a] - mdb->min[a]) / (mdb->size - 4); mdb->min[a] -= 2.1f * mdb->width[a]; mdb->max[a] += 2.1f * mdb->width[a]; mdb->width[a] = (mdb->max[a] - mdb->min[a]) / mdb->size; mdb->halfwidth[a] = mdb->width[a] * 0.5f; } progress_bar(0, "Setting up mesh deform system"); totinside = 0; for (a = 0; a < mdb->totvert; a++) { copy_v3_v3(vec, mdb->vertexcos[a]); mdb->inside[a] = meshdeform_inside_cage(mdb, vec); if (mdb->inside[a]) totinside++; } /* free temporary MDefBoundIsects */ BLI_memarena_free(mdb->memarena); mdb->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "harmonic coords arena"); /* start with all cells untyped */ for (a = 0; a < mdb->size3; a++) mdb->tag[a] = MESHDEFORM_TAG_UNTYPED; /* detect intersections and tag boundary cells */ for (z = 0; z < mdb->size; z++) for (y = 0; y < mdb->size; y++) for (x = 0; x < mdb->size; x++) meshdeform_add_intersections(mdb, x, y, z); /* compute exterior and interior tags */ meshdeform_bind_floodfill(mdb); for (z = 0; z < mdb->size; z++) for (y = 0; y < mdb->size; y++) for (x = 0; x < mdb->size; x++) meshdeform_check_semibound(mdb, x, y, z); /* solve */ meshdeform_matrix_solve(mmd, mdb); /* assign results */ if (mmd->flag & MOD_MDEF_DYNAMIC_BIND) { mmd->totinfluence = 0; for (a = 0; a < mdb->size3; a++) for (inf = mdb->dyngrid[a]; inf; inf = inf->next) mmd->totinfluence++; /* convert MDefBindInfluences to smaller MDefInfluences */ mmd->dyngrid = MEM_callocN(sizeof(MDefCell) * mdb->size3, "MDefDynGrid"); mmd->dyninfluences = MEM_callocN(sizeof(MDefInfluence) * mmd->totinfluence, "MDefInfluence"); offset = 0; for (a = 0; a < mdb->size3; a++) { cell = &mmd->dyngrid[a]; cell->offset = offset; totweight = 0.0f; mdinf = mmd->dyninfluences + cell->offset; for (inf = mdb->dyngrid[a]; inf; inf = inf->next, mdinf++) { mdinf->weight = inf->weight; mdinf->vertex = inf->vertex; totweight += mdinf->weight; cell->totinfluence++; } if (totweight > 0.0f) { mdinf = mmd->dyninfluences + cell->offset; for (b = 0; b < cell->totinfluence; b++, mdinf++) mdinf->weight /= totweight; } offset += cell->totinfluence; } mmd->dynverts = mdb->inside; mmd->dyngridsize = mdb->size; copy_v3_v3(mmd->dyncellmin, mdb->min); mmd->dyncellwidth = mdb->width[0]; MEM_freeN(mdb->dyngrid); } else { mmd->bindweights = mdb->weights; MEM_freeN(mdb->inside); } MEM_freeN(mdb->tag); MEM_freeN(mdb->phi); MEM_freeN(mdb->totalphi); MEM_freeN(mdb->boundisect); MEM_freeN(mdb->semibound); BLI_memarena_free(mdb->memarena); free_bvhtree_from_mesh(&mdb->bvhdata); }
void minmax_v3v3_v3_array(float r_min[3], float r_max[3], const float (*vec_arr)[3], int nbr) { while (nbr--) { minmax_v3v3_v3(r_min, r_max, *vec_arr++); } }
void curve_deform_verts( Scene *scene, Object *cuOb, Object *target, DerivedMesh *dm, float (*vertexCos)[3], int numVerts, const char *vgroup, short defaxis) { Curve *cu; int a; CurveDeform cd; MDeformVert *dvert = NULL; int defgrp_index = -1; const bool is_neg_axis = (defaxis > 2); if (cuOb->type != OB_CURVE) return; cu = cuOb->data; init_curve_deform(cuOb, target, &cd); /* dummy bounds, keep if CU_DEFORM_BOUNDS_OFF is set */ if (is_neg_axis == false) { cd.dmin[0] = cd.dmin[1] = cd.dmin[2] = 0.0f; cd.dmax[0] = cd.dmax[1] = cd.dmax[2] = 1.0f; } else { /* negative, these bounds give a good rest position */ cd.dmin[0] = cd.dmin[1] = cd.dmin[2] = -1.0f; cd.dmax[0] = cd.dmax[1] = cd.dmax[2] = 0.0f; } /* Check whether to use vertex groups (only possible if target is a Mesh or Lattice). * We want either a Mesh/Lattice with no derived data, or derived data with deformverts. */ if (vgroup && vgroup[0] && ELEM(target->type, OB_MESH, OB_LATTICE)) { defgrp_index = defgroup_name_index(target, vgroup); if (defgrp_index != -1) { /* if there's derived data without deformverts, don't use vgroups */ if (dm) { dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); } else if (target->type == OB_LATTICE) { dvert = ((Lattice *)target->data)->dvert; } else { dvert = ((Mesh *)target->data)->dvert; } } } if (dvert) { MDeformVert *dvert_iter; float vec[3]; if (cu->flag & CU_DEFORM_BOUNDS_OFF) { for (a = 0, dvert_iter = dvert; a < numVerts; a++, dvert_iter++) { const float weight = defvert_find_weight(dvert_iter, defgrp_index); if (weight > 0.0f) { mul_m4_v3(cd.curvespace, vertexCos[a]); copy_v3_v3(vec, vertexCos[a]); calc_curve_deform(scene, cuOb, vec, defaxis, &cd, NULL); interp_v3_v3v3(vertexCos[a], vertexCos[a], vec, weight); mul_m4_v3(cd.objectspace, vertexCos[a]); } } } else { /* set mesh min/max bounds */ INIT_MINMAX(cd.dmin, cd.dmax); for (a = 0, dvert_iter = dvert; a < numVerts; a++, dvert_iter++) { if (defvert_find_weight(dvert_iter, defgrp_index) > 0.0f) { mul_m4_v3(cd.curvespace, vertexCos[a]); minmax_v3v3_v3(cd.dmin, cd.dmax, vertexCos[a]); } } for (a = 0, dvert_iter = dvert; a < numVerts; a++, dvert_iter++) { const float weight = defvert_find_weight(dvert_iter, defgrp_index); if (weight > 0.0f) { /* already in 'cd.curvespace', prev for loop */ copy_v3_v3(vec, vertexCos[a]); calc_curve_deform(scene, cuOb, vec, defaxis, &cd, NULL); interp_v3_v3v3(vertexCos[a], vertexCos[a], vec, weight); mul_m4_v3(cd.objectspace, vertexCos[a]); } } } } else { if (cu->flag & CU_DEFORM_BOUNDS_OFF) { for (a = 0; a < numVerts; a++) { mul_m4_v3(cd.curvespace, vertexCos[a]); calc_curve_deform(scene, cuOb, vertexCos[a], defaxis, &cd, NULL); mul_m4_v3(cd.objectspace, vertexCos[a]); } } else { /* set mesh min max bounds */ INIT_MINMAX(cd.dmin, cd.dmax); for (a = 0; a < numVerts; a++) { mul_m4_v3(cd.curvespace, vertexCos[a]); minmax_v3v3_v3(cd.dmin, cd.dmax, vertexCos[a]); } for (a = 0; a < numVerts; a++) { /* already in 'cd.curvespace', prev for loop */ calc_curve_deform(scene, cuOb, vertexCos[a], defaxis, &cd, NULL); mul_m4_v3(cd.objectspace, vertexCos[a]); } } } }
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; }