Blender V2.61 - r43446
|
00001 /* 00002 * ***** BEGIN GPL LICENSE BLOCK ***** 00003 * 00004 * This program is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU General Public License 00006 * as published by the Free Software Foundation; either version 2 00007 * of the License, or (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software Foundation, 00016 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 * 00018 * The Original Code is Copyright (C) Blender Foundation. 00019 * All rights reserved. 00020 * 00021 * The Original Code is: all of this file. 00022 * 00023 * Contributor(s): Andr Pinto 00024 * 00025 * ***** END GPL LICENSE BLOCK ***** 00026 */ 00027 00032 #include <string.h> 00033 #include <float.h> 00034 #include <math.h> 00035 #include <memory.h> 00036 #include <stdio.h> 00037 #include <time.h> 00038 #include <assert.h> 00039 00040 #include "DNA_object_types.h" 00041 #include "DNA_modifier_types.h" 00042 #include "DNA_meshdata_types.h" 00043 #include "DNA_mesh_types.h" 00044 #include "DNA_scene_types.h" 00045 00046 #include "BLI_editVert.h" 00047 #include "BLI_math.h" 00048 #include "BLI_utildefines.h" 00049 00050 #include "BKE_shrinkwrap.h" 00051 #include "BKE_DerivedMesh.h" 00052 #include "BKE_lattice.h" 00053 00054 #include "BKE_deform.h" 00055 #include "BKE_mesh.h" 00056 #include "BKE_subsurf.h" 00057 00058 /* Util macros */ 00059 #define OUT_OF_MEMORY() ((void)printf("Shrinkwrap: Out of memory\n")) 00060 00061 /* Benchmark macros */ 00062 #if !defined(_WIN32) && 0 00063 00064 #include <sys/time.h> 00065 00066 #define BENCH(a) \ 00067 do { \ 00068 double _t1, _t2; \ 00069 struct timeval _tstart, _tend; \ 00070 clock_t _clock_init = clock(); \ 00071 gettimeofday ( &_tstart, NULL); \ 00072 (a); \ 00073 gettimeofday ( &_tend, NULL); \ 00074 _t1 = ( double ) _tstart.tv_sec + ( double ) _tstart.tv_usec/ ( 1000*1000 ); \ 00075 _t2 = ( double ) _tend.tv_sec + ( double ) _tend.tv_usec/ ( 1000*1000 ); \ 00076 printf("%s: %fs (real) %fs (cpu)\n", #a, _t2-_t1, (float)(clock()-_clock_init)/CLOCKS_PER_SEC);\ 00077 } while(0) 00078 00079 #else 00080 00081 #define BENCH(a) (a) 00082 00083 #endif 00084 00085 typedef void ( *Shrinkwrap_ForeachVertexCallback) (DerivedMesh *target, float *co, float *normal); 00086 00087 /* get derived mesh */ 00088 //TODO is anyfunction that does this? returning the derivedFinal witouth we caring if its in edit mode or not? 00089 DerivedMesh *object_get_derived_final(Object *ob) 00090 { 00091 Mesh *me= ob->data; 00092 EditMesh *em = BKE_mesh_get_editmesh(me); 00093 00094 if(em) { 00095 DerivedMesh *dm = em->derivedFinal; 00096 BKE_mesh_end_editmesh(me, em); 00097 return dm; 00098 } 00099 00100 return ob->derivedFinal; 00101 } 00102 00103 /* Space transform */ 00104 void space_transform_from_matrixs(SpaceTransform *data, float local[4][4], float target[4][4]) 00105 { 00106 float itarget[4][4]; 00107 invert_m4_m4(itarget, target); 00108 mul_serie_m4(data->local2target, itarget, local, NULL, NULL, NULL, NULL, NULL, NULL); 00109 invert_m4_m4(data->target2local, data->local2target); 00110 } 00111 00112 void space_transform_apply(const SpaceTransform *data, float *co) 00113 { 00114 mul_v3_m4v3(co, ((SpaceTransform*)data)->local2target, co); 00115 } 00116 00117 void space_transform_invert(const SpaceTransform *data, float *co) 00118 { 00119 mul_v3_m4v3(co, ((SpaceTransform*)data)->target2local, co); 00120 } 00121 00122 static void space_transform_apply_normal(const SpaceTransform *data, float *no) 00123 { 00124 mul_mat3_m4_v3( ((SpaceTransform*)data)->local2target, no); 00125 normalize_v3(no); // TODO: could we just determine de scale value from the matrix? 00126 } 00127 00128 static void space_transform_invert_normal(const SpaceTransform *data, float *no) 00129 { 00130 mul_mat3_m4_v3(((SpaceTransform*)data)->target2local, no); 00131 normalize_v3(no); // TODO: could we just determine de scale value from the matrix? 00132 } 00133 00134 /* 00135 * Shrinkwrap to the nearest vertex 00136 * 00137 * it builds a kdtree of vertexs we can attach to and then 00138 * for each vertex performs a nearest vertex search on the tree 00139 */ 00140 static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) 00141 { 00142 int i; 00143 00144 BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; 00145 BVHTreeNearest nearest = NULL_BVHTreeNearest; 00146 00147 00148 BENCH(bvhtree_from_mesh_verts(&treeData, calc->target, 0.0, 2, 6)); 00149 if(treeData.tree == NULL) 00150 { 00151 OUT_OF_MEMORY(); 00152 return; 00153 } 00154 00155 //Setup nearest 00156 nearest.index = -1; 00157 nearest.dist = FLT_MAX; 00158 #ifndef __APPLE__ 00159 #pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(treeData,calc) schedule(static) 00160 #endif 00161 for(i = 0; i<calc->numVerts; ++i) 00162 { 00163 float *co = calc->vertexCos[i]; 00164 float tmp_co[3]; 00165 float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); 00166 if(weight == 0.0f) continue; 00167 00168 00169 //Convert the vertex to tree coordinates 00170 if(calc->vert) { 00171 copy_v3_v3(tmp_co, calc->vert[i].co); 00172 } 00173 else { 00174 copy_v3_v3(tmp_co, co); 00175 } 00176 space_transform_apply(&calc->local2target, tmp_co); 00177 00178 //Use local proximity heuristics (to reduce the nearest search) 00179 // 00180 //If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex 00181 //so we can initiate the "nearest.dist" with the expected value to that last hit. 00182 //This will lead in prunning of the search tree. 00183 if(nearest.index != -1) 00184 nearest.dist = len_squared_v3v3(tmp_co, nearest.co); 00185 else 00186 nearest.dist = FLT_MAX; 00187 00188 BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); 00189 00190 00191 //Found the nearest vertex 00192 if(nearest.index != -1) 00193 { 00194 //Adjusting the vertex weight, so that after interpolating it keeps a certain distance from the nearest position 00195 float dist = sasqrt(nearest.dist); 00196 if(dist > FLT_EPSILON) weight *= (dist - calc->keepDist)/dist; 00197 00198 //Convert the coordinates back to mesh coordinates 00199 copy_v3_v3(tmp_co, nearest.co); 00200 space_transform_invert(&calc->local2target, tmp_co); 00201 00202 interp_v3_v3v3(co, co, tmp_co, weight); //linear interpolation 00203 } 00204 } 00205 00206 free_bvhtree_from_mesh(&treeData); 00207 } 00208 00209 /* 00210 * This function raycast a single vertex and updates the hit if the "hit" is considered valid. 00211 * Returns TRUE if "hit" was updated. 00212 * Opts control whether an hit is valid or not 00213 * Supported options are: 00214 * MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE (front faces hits are ignored) 00215 * MOD_SHRINKWRAP_CULL_TARGET_BACKFACE (back faces hits are ignored) 00216 */ 00217 int normal_projection_project_vertex(char options, const float *vert, const float *dir, const SpaceTransform *transf, BVHTree *tree, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata) 00218 { 00219 float tmp_co[3], tmp_no[3]; 00220 const float *co, *no; 00221 BVHTreeRayHit hit_tmp; 00222 00223 //Copy from hit (we need to convert hit rays from one space coordinates to the other 00224 memcpy( &hit_tmp, hit, sizeof(hit_tmp) ); 00225 00226 //Apply space transform (TODO readjust dist) 00227 if(transf) 00228 { 00229 copy_v3_v3( tmp_co, vert ); 00230 space_transform_apply( transf, tmp_co ); 00231 co = tmp_co; 00232 00233 copy_v3_v3( tmp_no, dir ); 00234 space_transform_apply_normal( transf, tmp_no ); 00235 no = tmp_no; 00236 00237 hit_tmp.dist *= mat4_to_scale( ((SpaceTransform*)transf)->local2target ); 00238 } 00239 else 00240 { 00241 co = vert; 00242 no = dir; 00243 } 00244 00245 hit_tmp.index = -1; 00246 00247 BLI_bvhtree_ray_cast(tree, co, no, 0.0f, &hit_tmp, callback, userdata); 00248 00249 if(hit_tmp.index != -1) { 00250 /* invert the normal first so face culling works on rotated objects */ 00251 if(transf) { 00252 space_transform_invert_normal(transf, hit_tmp.no); 00253 } 00254 00255 if (options & (MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE|MOD_SHRINKWRAP_CULL_TARGET_BACKFACE)) { 00256 /* apply backface */ 00257 const float dot= dot_v3v3(dir, hit_tmp.no); 00258 if( ((options & MOD_SHRINKWRAP_CULL_TARGET_FRONTFACE) && dot <= 0.0f) || 00259 ((options & MOD_SHRINKWRAP_CULL_TARGET_BACKFACE) && dot >= 0.0f) 00260 ) { 00261 return FALSE; /* Ignore hit */ 00262 } 00263 } 00264 00265 if(transf) { 00266 /* Inverting space transform (TODO make coeherent with the initial dist readjust) */ 00267 space_transform_invert(transf, hit_tmp.co); 00268 hit_tmp.dist = len_v3v3((float *)vert, hit_tmp.co); 00269 } 00270 00271 memcpy(hit, &hit_tmp, sizeof(hit_tmp) ); 00272 return TRUE; 00273 } 00274 return FALSE; 00275 } 00276 00277 00278 static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc) 00279 { 00280 int i; 00281 00282 //Options about projection direction 00283 const char use_normal = calc->smd->shrinkOpts; 00284 float proj_axis[3] = {0.0f, 0.0f, 0.0f}; 00285 00286 //Raycast and tree stuff 00287 BVHTreeRayHit hit; 00288 BVHTreeFromMesh treeData= NULL_BVHTreeFromMesh; 00289 00290 //auxiliar target 00291 DerivedMesh *auxMesh = NULL; 00292 BVHTreeFromMesh auxData = NULL_BVHTreeFromMesh; 00293 SpaceTransform local2aux; 00294 00295 //If the user doesn't allows to project in any direction of projection axis 00296 //then theres nothing todo. 00297 if((use_normal & (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 0) 00298 return; 00299 00300 00301 //Prepare data to retrieve the direction in which we should project each vertex 00302 if(calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) 00303 { 00304 if(calc->vert == NULL) return; 00305 } 00306 else 00307 { 00308 //The code supports any axis that is a combination of X,Y,Z 00309 //altought currently UI only allows to set the 3 diferent axis 00310 if(calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) proj_axis[0] = 1.0f; 00311 if(calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) proj_axis[1] = 1.0f; 00312 if(calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) proj_axis[2] = 1.0f; 00313 00314 normalize_v3(proj_axis); 00315 00316 //Invalid projection direction 00317 if(dot_v3v3(proj_axis, proj_axis) < FLT_EPSILON) 00318 return; 00319 } 00320 00321 if(calc->smd->auxTarget) 00322 { 00323 auxMesh = object_get_derived_final(calc->smd->auxTarget); 00324 if(!auxMesh) 00325 return; 00326 space_transform_setup( &local2aux, calc->ob, calc->smd->auxTarget); 00327 } 00328 00329 //After sucessufuly build the trees, start projection vertexs 00330 if( bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 4, 6) 00331 && (auxMesh == NULL || bvhtree_from_mesh_faces(&auxData, auxMesh, 0.0, 4, 6))) 00332 { 00333 00334 #ifndef __APPLE__ 00335 #pragma omp parallel for private(i,hit) schedule(static) 00336 #endif 00337 for(i = 0; i<calc->numVerts; ++i) 00338 { 00339 float *co = calc->vertexCos[i]; 00340 float tmp_co[3], tmp_no[3]; 00341 float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); 00342 00343 if(weight == 0.0f) continue; 00344 00345 if(calc->vert) 00346 { 00347 /* calc->vert contains verts from derivedMesh */ 00348 /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ 00349 /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ 00350 if(calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { 00351 copy_v3_v3(tmp_co, calc->vert[i].co); 00352 normal_short_to_float_v3(tmp_no, calc->vert[i].no); 00353 } else { 00354 copy_v3_v3(tmp_co, co); 00355 copy_v3_v3(tmp_no, proj_axis); 00356 } 00357 } 00358 else 00359 { 00360 copy_v3_v3(tmp_co, co); 00361 copy_v3_v3(tmp_no, proj_axis); 00362 } 00363 00364 00365 hit.index = -1; 00366 hit.dist = 10000.0f; //TODO: we should use FLT_MAX here, but sweepsphere code isnt prepared for that 00367 00368 //Project over positive direction of axis 00369 if(use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) 00370 { 00371 00372 if(auxData.tree) 00373 normal_projection_project_vertex(0, tmp_co, tmp_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); 00374 00375 normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, tmp_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); 00376 } 00377 00378 //Project over negative direction of axis 00379 if(use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR && hit.index == -1) 00380 { 00381 float inv_no[3]; 00382 negate_v3_v3(inv_no, tmp_no); 00383 00384 if(auxData.tree) 00385 normal_projection_project_vertex(0, tmp_co, inv_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData); 00386 00387 normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, inv_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData); 00388 } 00389 00390 00391 if(hit.index != -1) 00392 { 00393 madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist); 00394 interp_v3_v3v3(co, co, hit.co, weight); 00395 } 00396 } 00397 } 00398 00399 //free data structures 00400 free_bvhtree_from_mesh(&treeData); 00401 free_bvhtree_from_mesh(&auxData); 00402 } 00403 00404 /* 00405 * Shrinkwrap moving vertexs to the nearest surface point on the target 00406 * 00407 * it builds a BVHTree from the target mesh and then performs a 00408 * NN matchs for each vertex 00409 */ 00410 static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) 00411 { 00412 int i; 00413 00414 BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; 00415 BVHTreeNearest nearest = NULL_BVHTreeNearest; 00416 00417 //Create a bvh-tree of the given target 00418 BENCH(bvhtree_from_mesh_faces( &treeData, calc->target, 0.0, 2, 6)); 00419 if(treeData.tree == NULL) 00420 { 00421 OUT_OF_MEMORY(); 00422 return; 00423 } 00424 00425 //Setup nearest 00426 nearest.index = -1; 00427 nearest.dist = FLT_MAX; 00428 00429 00430 //Find the nearest vertex 00431 #ifndef __APPLE__ 00432 #pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(calc,treeData) schedule(static) 00433 #endif 00434 for(i = 0; i<calc->numVerts; ++i) 00435 { 00436 float *co = calc->vertexCos[i]; 00437 float tmp_co[3]; 00438 float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); 00439 if(weight == 0.0f) continue; 00440 00441 //Convert the vertex to tree coordinates 00442 if(calc->vert) 00443 { 00444 copy_v3_v3(tmp_co, calc->vert[i].co); 00445 } 00446 else 00447 { 00448 copy_v3_v3(tmp_co, co); 00449 } 00450 space_transform_apply(&calc->local2target, tmp_co); 00451 00452 //Use local proximity heuristics (to reduce the nearest search) 00453 // 00454 //If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex 00455 //so we can initiate the "nearest.dist" with the expected value to that last hit. 00456 //This will lead in prunning of the search tree. 00457 if(nearest.index != -1) 00458 nearest.dist = len_squared_v3v3(tmp_co, nearest.co); 00459 else 00460 nearest.dist = FLT_MAX; 00461 00462 BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); 00463 00464 //Found the nearest vertex 00465 if(nearest.index != -1) 00466 { 00467 if(calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) 00468 { 00469 //Make the vertex stay on the front side of the face 00470 madd_v3_v3v3fl(tmp_co, nearest.co, nearest.no, calc->keepDist); 00471 } 00472 else 00473 { 00474 //Adjusting the vertex weight, so that after interpolating it keeps a certain distance from the nearest position 00475 float dist = sasqrt( nearest.dist ); 00476 if(dist > FLT_EPSILON) 00477 interp_v3_v3v3(tmp_co, tmp_co, nearest.co, (dist - calc->keepDist)/dist); //linear interpolation 00478 else 00479 copy_v3_v3( tmp_co, nearest.co ); 00480 } 00481 00482 //Convert the coordinates back to mesh coordinates 00483 space_transform_invert(&calc->local2target, tmp_co); 00484 interp_v3_v3v3(co, co, tmp_co, weight); //linear interpolation 00485 } 00486 } 00487 00488 free_bvhtree_from_mesh(&treeData); 00489 } 00490 00491 /* Main shrinkwrap function */ 00492 void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd, Object *ob, DerivedMesh *dm, float (*vertexCos)[3], int numVerts) 00493 { 00494 00495 DerivedMesh *ss_mesh = NULL; 00496 ShrinkwrapCalcData calc = NULL_ShrinkwrapCalcData; 00497 00498 //remove loop dependencies on derived meshs (TODO should this be done elsewhere?) 00499 if(smd->target == ob) smd->target = NULL; 00500 if(smd->auxTarget == ob) smd->auxTarget = NULL; 00501 00502 00503 //Configure Shrinkwrap calc data 00504 calc.smd = smd; 00505 calc.ob = ob; 00506 calc.numVerts = numVerts; 00507 calc.vertexCos = vertexCos; 00508 00509 //DeformVertex 00510 calc.vgroup = defgroup_name_index(calc.ob, calc.smd->vgroup_name); 00511 if(dm) 00512 { 00513 calc.dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); 00514 } 00515 else if(calc.ob->type == OB_LATTICE) 00516 { 00517 calc.dvert = lattice_get_deform_verts(calc.ob); 00518 } 00519 00520 00521 if(smd->target) 00522 { 00523 calc.target = object_get_derived_final(smd->target); 00524 00525 //TODO there might be several "bugs" on non-uniform scales matrixs 00526 //because it will no longer be nearest surface, not sphere projection 00527 //because space has been deformed 00528 space_transform_setup(&calc.local2target, ob, smd->target); 00529 00530 //TODO: smd->keepDist is in global units.. must change to local 00531 calc.keepDist = smd->keepDist; 00532 } 00533 00534 00535 00536 calc.vgroup = defgroup_name_index(calc.ob, smd->vgroup_name); 00537 00538 if(dm != NULL && smd->shrinkType == MOD_SHRINKWRAP_PROJECT) 00539 { 00540 //Setup arrays to get vertexs positions, normals and deform weights 00541 calc.vert = dm->getVertDataArray(dm, CD_MVERT); 00542 calc.dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); 00543 00544 //Using vertexs positions/normals as if a subsurface was applied 00545 if(smd->subsurfLevels) 00546 { 00547 SubsurfModifierData ssmd= {{NULL}}; 00548 ssmd.subdivType = ME_CC_SUBSURF; //catmull clark 00549 ssmd.levels = smd->subsurfLevels; //levels 00550 00551 ss_mesh = subsurf_make_derived_from_derived(dm, &ssmd, FALSE, NULL, 0, 0, (ob->mode & OB_MODE_EDIT)); 00552 00553 if(ss_mesh) 00554 { 00555 calc.vert = ss_mesh->getVertDataArray(ss_mesh, CD_MVERT); 00556 if(calc.vert) 00557 { 00558 //TRICKY: this code assumes subsurface will have the transformed original vertices 00559 //in their original order at the end of the vert array. 00560 calc.vert = calc.vert + ss_mesh->getNumVerts(ss_mesh) - dm->getNumVerts(dm); 00561 } 00562 } 00563 00564 //Just to make sure we are not leaving any memory behind 00565 assert(ssmd.emCache == NULL); 00566 assert(ssmd.mCache == NULL); 00567 } 00568 } 00569 00570 //Projecting target defined - lets work! 00571 if(calc.target) 00572 { 00573 switch(smd->shrinkType) 00574 { 00575 case MOD_SHRINKWRAP_NEAREST_SURFACE: 00576 BENCH(shrinkwrap_calc_nearest_surface_point(&calc)); 00577 break; 00578 00579 case MOD_SHRINKWRAP_PROJECT: 00580 BENCH(shrinkwrap_calc_normal_projection(&calc)); 00581 break; 00582 00583 case MOD_SHRINKWRAP_NEAREST_VERTEX: 00584 BENCH(shrinkwrap_calc_nearest_vertex(&calc)); 00585 break; 00586 } 00587 } 00588 00589 //free memory 00590 if(ss_mesh) 00591 ss_mesh->release(ss_mesh); 00592 } 00593