2.50: svn merge https://svn.blender.org/svnroot/bf-blender/trunk/blender -r19323...
[blender.git] / source / blender / blenkernel / intern / constraint.c
index dd8fd88..c055a0c 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "DNA_armature_types.h"
 #include "DNA_constraint_types.h"
+#include "DNA_modifier_types.h"
 #include "DNA_object_types.h"
 #include "DNA_action_types.h"
 #include "DNA_curve_types.h"
@@ -65,6 +66,8 @@
 #include "BKE_global.h"
 #include "BKE_library.h"
 #include "BKE_idprop.h"
+#include "BKE_shrinkwrap.h"
+#include "BKE_mesh.h"
 
 #ifndef DISABLE_PYTHON
 #include "BPY_extern.h"
@@ -387,7 +390,7 @@ void constraint_mat_convertspace (Object *ob, bPoseChannel *pchan, float mat[][4
 /* ------------ General Target Matrix Tools ---------- */
 
 /* function that sets the given matrix based on given vertex group in mesh */
-static void contarget_get_mesh_mat (Object *ob, char *substring, float mat[][4])
+static void contarget_get_mesh_mat (Scene *scene, Object *ob, char *substring, float mat[][4])
 {
        DerivedMesh *dm;
        Mesh *me= ob->data;
@@ -396,6 +399,7 @@ static void contarget_get_mesh_mat (Object *ob, char *substring, float mat[][4])
        float normal[3] = {0.0f, 0.0f, 0.0f}, plane[3];
        float imat[3][3], tmat[3][3];
        int dgroup;
+       short freeDM = 0;
        
        /* initialize target matrix using target matrix */
        Mat4CpyMat4(mat, ob->obmat);
@@ -408,10 +412,19 @@ static void contarget_get_mesh_mat (Object *ob, char *substring, float mat[][4])
        if (em) {
                /* target is in editmode, so get a special derived mesh */
                dm = CDDM_from_editmesh(em, ob->data);
+               freeDM= 1;
        }
        else {
-               /* when not in EditMode, this should exist */
-               dm = (DerivedMesh *)ob->derivedFinal;
+               /* when not in EditMode, use the 'final' derived mesh 
+                *      - check if the custom data masks for derivedFinal mean that we can just use that
+                *        (this is more effficient + sufficient for most cases)
+                */
+               if (ob->lastDataMask != CD_MASK_DERIVEDMESH) {
+                       dm = mesh_get_derived_final(scene, ob, CD_MASK_DERIVEDMESH);
+                       freeDM= 1;
+               }
+               else 
+                       dm = (DerivedMesh *)ob->derivedFinal;
        }
        
        /* only continue if there's a valid DerivedMesh */
@@ -477,10 +490,10 @@ static void contarget_get_mesh_mat (Object *ob, char *substring, float mat[][4])
        }
        
        /* free temporary DerivedMesh created (in EditMode case) */
-       if (em) {
-               if (dm) dm->release(dm);
+       if (dm && freeDM)
+               dm->release(dm);
+       if (em)
                BKE_mesh_end_editmesh(me, em);
-       }
 }
 
 /* function that sets the given matrix based on given vertex group in lattice */
@@ -542,7 +555,7 @@ static void contarget_get_lattice_mat (Object *ob, char *substring, float mat[][
 
 /* generic function to get the appropriate matrix for most target cases */
 /* The cases where the target can be object data have not been implemented */
-static void constraint_target_to_mat4 (Object *ob, char *substring, float mat[][4], short from, short to, float headtail)
+static void constraint_target_to_mat4 (Scene *scene, Object *ob, char *substring, float mat[][4], short from, short to, float headtail)
 {
        /*      Case OBJECT */
        if (!strlen(substring)) {
@@ -559,7 +572,7 @@ static void constraint_target_to_mat4 (Object *ob, char *substring, float mat[][
         *              way as constraints can only really affect things on object/bone level.
         */
        else if (ob->type == OB_MESH) {
-               contarget_get_mesh_mat(ob, substring, mat);
+               contarget_get_mesh_mat(scene, ob, substring, mat);
                constraint_mat_convertspace(ob, NULL, mat, from, to);
        }
        else if (ob->type == OB_LATTICE) {
@@ -641,7 +654,7 @@ static bConstraintTypeInfo CTI_CONSTRNAME = {
 static void default_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintTarget *ct, float ctime)
 {
        if (VALID_CONS_TARGET(ct))
-               constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail);
+               constraint_target_to_mat4(cob->scene, ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail);
        else if (ct)
                Mat4One(ct->matrix);
 }
@@ -1056,7 +1069,7 @@ static void kinematic_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstra
        bKinematicConstraint *data= con->data;
        
        if (VALID_CONS_TARGET(ct)) 
-               constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail);
+               constraint_target_to_mat4(cob->scene, ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail);
        else if (ct) {
                if (data->flag & CONSTRAINT_IK_AUTO) {
                        Object *ob= cob->ob;
@@ -1746,7 +1759,7 @@ static void pycon_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintT
                /* firstly calculate the matrix the normal way, then let the py-function override
                 * this matrix if it needs to do so
                 */
-               constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail);
+               constraint_target_to_mat4(cob->scene, ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail);
                
                /* only execute target calculation if allowed */
 #ifndef DISABLE_PYTHON
@@ -1853,7 +1866,7 @@ static void actcon_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraint
                Mat4One(ct->matrix);
                
                /* get the transform matrix of the target */
-               constraint_target_to_mat4(ct->tar, ct->subtarget, tempmat, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail);
+               constraint_target_to_mat4(cob->scene, ct->tar, ct->subtarget, tempmat, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail);
                
                /* determine where in transform range target is */
                /* data->type is mapped as follows for backwards compatability:
@@ -3108,6 +3121,165 @@ static bConstraintTypeInfo CTI_TRANSFORM = {
        transform_evaluate /* evaluate */
 };
 
+/* ---------- Shrinkwrap Constraint ----------- */
+
+static int shrinkwrap_get_tars (bConstraint *con, ListBase *list)
+{
+       if (con && list) {
+               bShrinkwrapConstraint *data = con->data;
+               bConstraintTarget *ct;
+               
+               SINGLETARGETNS_GET_TARS(con, data->target, ct, list)
+               
+               return 1;
+       }
+       
+       return 0;
+}
+
+
+static void shrinkwrap_flush_tars (bConstraint *con, ListBase *list, short nocopy)
+{
+       if (con && list) {
+               bShrinkwrapConstraint *data = con->data;
+               bConstraintTarget *ct= list->first;
+               
+               SINGLETARGETNS_FLUSH_TARS(con, data->target, ct, list, nocopy)
+       }
+}
+
+
+static void shrinkwrap_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintTarget *ct, float ctime)
+{
+       bShrinkwrapConstraint *scon = (bShrinkwrapConstraint *) con->data;
+       
+       if( VALID_CONS_TARGET(ct) && (ct->tar->type == OB_MESH) )
+       {
+               int fail = FALSE;
+               float co[3] = {0.0f, 0.0f, 0.0f};
+               float no[3] = {0.0f, 0.0f, 0.0f};
+               float dist;
+
+               SpaceTransform transform;
+               DerivedMesh *target = object_get_derived_final(cob->scene, ct->tar, CD_MASK_BAREMESH);
+               BVHTreeRayHit hit;
+               BVHTreeNearest nearest;
+
+               BVHTreeFromMesh treeData;
+               memset( &treeData, 0, sizeof(treeData) );
+
+               nearest.index = -1;
+               nearest.dist = FLT_MAX;
+
+               hit.index = -1;
+               hit.dist = 100000.0f;  //TODO should use FLT_MAX.. but normal projection doenst yet supports it
+
+               Mat4One(ct->matrix);
+
+               if(target != NULL)
+               {
+                       space_transform_from_matrixs(&transform, cob->matrix, ct->tar->obmat);
+
+                       switch(scon->shrinkType)
+                       {
+                               case MOD_SHRINKWRAP_NEAREST_SURFACE:
+                               case MOD_SHRINKWRAP_NEAREST_VERTEX:
+
+                                       if(scon->shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX)
+                                               bvhtree_from_mesh_verts(&treeData, target, 0.0, 2, 6);
+                                       else
+                                               bvhtree_from_mesh_faces(&treeData, target, 0.0, 2, 6);
+
+                                       if(treeData.tree == NULL)
+                                       {
+                                               fail = TRUE;
+                                               break;
+                                       }
+
+                                       space_transform_apply(&transform, co);
+
+                                       BLI_bvhtree_find_nearest(treeData.tree, co, &nearest, treeData.nearest_callback, &treeData);
+                                       
+                                       dist = VecLenf(co, nearest.co);
+                                       VecLerpf(co, co, nearest.co, (dist - scon->dist)/dist); /* linear interpolation */
+                                       space_transform_invert(&transform, co);
+                               break;
+
+                               case MOD_SHRINKWRAP_PROJECT:
+                                       if(scon->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) no[0] = 1.0f;
+                                       if(scon->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) no[1] = 1.0f;
+                                       if(scon->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) no[2] = 1.0f;
+
+                                       if(INPR(no,no) < FLT_EPSILON)
+                                       {
+                                               fail = TRUE;
+                                               break;
+                                       }
+
+                                       Normalize(no);
+
+
+                                       bvhtree_from_mesh_faces(&treeData, target, scon->dist, 4, 6);
+                                       if(treeData.tree == NULL)
+                                       {
+                                               fail = TRUE;
+                                               break;
+                                       }
+
+                                       if(normal_projection_project_vertex(0, co, no, &transform, treeData.tree, &hit, treeData.raycast_callback, &treeData) == FALSE)
+                                       {
+                                               fail = TRUE;
+                                               break;
+                                       }
+                                       VECCOPY(co, hit.co);
+                               break;
+                       }
+
+                       free_bvhtree_from_mesh(&treeData);
+
+                       target->release(target);
+
+                       if(fail == TRUE)
+                       {
+                               /* Don't move the point */
+                               co[0] = co[1] = co[2] = 0.0f;
+                       }
+
+                       /* co is in local object coordinates, change it to global and update target position */
+                       VecMat4MulVecfl(co, cob->matrix, co);
+                       VECCOPY(ct->matrix[3], co);
+               }
+       }
+}
+
+static void shrinkwrap_evaluate (bConstraint *con, bConstraintOb *cob, ListBase *targets)
+{
+       bConstraintTarget *ct= targets->first;
+       
+       /* only evaluate if there is a target */
+       if (VALID_CONS_TARGET(ct))
+       {
+               VECCOPY(cob->matrix[3], ct->matrix[3]);
+       }
+}
+
+static bConstraintTypeInfo CTI_SHRINKWRAP = {
+       CONSTRAINT_TYPE_SHRINKWRAP, /* type */
+       sizeof(bShrinkwrapConstraint), /* size */
+       "Shrinkwrap", /* name */
+       "bShrinkwrapConstraint", /* struct name */
+       NULL, /* free data */
+       NULL, /* relink data */
+       NULL, /* copy data */
+       NULL, /* new data */
+       shrinkwrap_get_tars, /* get constraint targets */
+       shrinkwrap_flush_tars, /* flush constraint targets */
+       shrinkwrap_get_tarmat, /* get a target matrix */
+       shrinkwrap_evaluate /* evaluate */
+};
+
+
+
 /* ************************* Constraints Type-Info *************************** */
 /* All of the constraints api functions use bConstraintTypeInfo structs to carry out
  * and operations that involve constraint specific code.
@@ -3139,6 +3311,7 @@ static void constraints_init_typeinfo () {
        constraintsTypeInfo[17]= &CTI_RIGIDBODYJOINT;   /* RigidBody Constraint */
        constraintsTypeInfo[18]= &CTI_CLAMPTO;                  /* ClampTo Constraint */        
        constraintsTypeInfo[19]= &CTI_TRANSFORM;                /* Transformation Constraint */
+       constraintsTypeInfo[20]= &CTI_SHRINKWRAP;               /* Shrinkwrap Constraint */
 }
 
 /* This function should be used for getting the appropriate type-info when only