Start of planned DerivedMesh refactoring. The mface interfaces in DerivedMesh have been renamed to reflect their new status as tesselated face interfaces (rather then the primary ones, which are now stored in mpolys). short review: mpolys store "primary" face data, while mfaces store the tesselated form of the mesh (generally as triangles). mpolys are defined by mloops, and each mpoly defines a range of loops it "owns" in the main mloop array. I've also added basic read-only face iterators, which are implemented for CDDM, ccgsubsurf, and the bmeditmesh derivedmesh. Since faces are now variable-length things, trying to implement the same interface as mfaces would not have worked well (especially since faces are stored as an mpoly + a range of mloops). I figure first we can evaluate these simple read-only face iterators, then decide if a) we like using iterators in DerivedMesh, b) how much of it should use them, and c) if we want write-capable iterators. I plan to write official docs on this design after I get it more stable; I'm committing now because there's a rather lot of changes, and I might do a merge soon.
1773 lines
42 KiB
C
1773 lines
42 KiB
C
/**
|
|
* $Id:
|
|
*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): Martin Poirier
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include <stdio.h>
|
|
|
|
#include "PIL_time.h"
|
|
|
|
#include "DNA_action_types.h"
|
|
#include "DNA_armature_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_meshdata_types.h" // Temporary, for snapping to other unselected meshes
|
|
#include "DNA_space_types.h"
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
#include "DNA_view3d_types.h"
|
|
#include "DNA_windowmanager_types.h"
|
|
|
|
#include "RNA_access.h"
|
|
|
|
#include "BLI_arithb.h"
|
|
#include "BLI_editVert.h"
|
|
#include "BLI_blenlib.h"
|
|
|
|
//#include "BDR_drawobject.h"
|
|
//
|
|
//#include "editmesh.h"
|
|
//#include "BIF_editsima.h"
|
|
#include "BIF_gl.h"
|
|
#include "BIF_glutil.h"
|
|
//#include "BIF_mywindow.h"
|
|
//#include "BIF_screen.h"
|
|
//#include "BIF_editsima.h"
|
|
//#include "BIF_drawimage.h"
|
|
//#include "BIF_editmesh.h"
|
|
|
|
#include "BIF_transform.h"
|
|
|
|
#include "BKE_global.h"
|
|
#include "BKE_utildefines.h"
|
|
#include "BKE_DerivedMesh.h"
|
|
#include "BKE_object.h"
|
|
#include "BKE_anim.h" /* for duplis */
|
|
#include "BKE_context.h"
|
|
#include "BKE_tessmesh.h"
|
|
#include "BKE_mesh.h"
|
|
|
|
#include "ED_armature.h"
|
|
#include "ED_image.h"
|
|
#include "ED_mesh.h"
|
|
#include "ED_uvedit.h"
|
|
#include "ED_view3d.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
#include "UI_resources.h"
|
|
#include "UI_view2d.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "transform.h"
|
|
|
|
//#include "blendef.h" /* for selection modes */
|
|
|
|
/********************* PROTOTYPES ***********************/
|
|
|
|
void setSnappingCallback(TransInfo *t, short snap_target);
|
|
|
|
void ApplySnapTranslation(TransInfo *t, float vec[3]);
|
|
void ApplySnapRotation(TransInfo *t, float *vec);
|
|
void ApplySnapResize(TransInfo *t, float *vec);
|
|
|
|
void CalcSnapGrid(TransInfo *t, float *vec);
|
|
void CalcSnapGeometry(TransInfo *t, float *vec);
|
|
|
|
void TargetSnapMedian(TransInfo *t);
|
|
void TargetSnapCenter(TransInfo *t);
|
|
void TargetSnapClosest(TransInfo *t);
|
|
void TargetSnapActive(TransInfo *t);
|
|
|
|
float RotationBetween(TransInfo *t, float p1[3], float p2[3]);
|
|
float TranslationBetween(TransInfo *t, float p1[3], float p2[3]);
|
|
float ResizeBetween(TransInfo *t, float p1[3], float p2[3]);
|
|
|
|
|
|
/****************** IMPLEMENTATIONS *********************/
|
|
|
|
int BIF_snappingSupported(Object *obedit)
|
|
{
|
|
int status = 0;
|
|
|
|
if (obedit == NULL || ELEM(obedit->type, OB_MESH, OB_ARMATURE)) /* only support object mesh or armature */
|
|
{
|
|
status = 1;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void drawSnapping(const struct bContext *C, TransInfo *t)
|
|
{
|
|
if ((t->tsnap.status & (SNAP_ON|POINT_INIT|TARGET_INIT)) == (SNAP_ON|POINT_INIT|TARGET_INIT) &&
|
|
(t->modifiers & MOD_SNAP_GEARS))
|
|
{
|
|
|
|
char col[4] = {1, 0, 1};
|
|
UI_GetThemeColor3ubv(TH_TRANSFORM, col);
|
|
glColor4ub(col[0], col[1], col[2], 128);
|
|
|
|
if (t->spacetype == SPACE_VIEW3D) {
|
|
View3D *v3d = CTX_wm_view3d(C);
|
|
RegionView3D *rv3d = CTX_wm_region_view3d(C);
|
|
float tmat[4][4], imat[4][4];
|
|
float size;
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
size = get_drawsize(t->ar, t->tsnap.snapPoint);
|
|
|
|
size *= 0.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
|
|
|
|
Mat4CpyMat4(tmat, rv3d->viewmat);
|
|
Mat4Invert(imat, tmat);
|
|
|
|
drawcircball(GL_LINE_LOOP, t->tsnap.snapPoint, size, imat);
|
|
|
|
/* draw normal if needed */
|
|
if (usingSnappingNormal(t) && validSnappingNormal(t))
|
|
{
|
|
glBegin(GL_LINES);
|
|
glVertex3f(t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], t->tsnap.snapPoint[2]);
|
|
glVertex3f( t->tsnap.snapPoint[0] + t->tsnap.snapNormal[0],
|
|
t->tsnap.snapPoint[1] + t->tsnap.snapNormal[1],
|
|
t->tsnap.snapPoint[2] + t->tsnap.snapNormal[2]);
|
|
glEnd();
|
|
}
|
|
|
|
if(v3d->zbuf)
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
else if (t->spacetype==SPACE_IMAGE)
|
|
{
|
|
/*This will not draw, and Im nor sure why - campbell */
|
|
|
|
/*
|
|
float xuser_asp, yuser_asp;
|
|
int wi, hi;
|
|
float w, h;
|
|
|
|
calc_image_view(G.sima, 'f'); // float
|
|
myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax);
|
|
glLoadIdentity();
|
|
|
|
ED_space_image_aspect(t->sa->spacedata.first, &xuser_aspx, &yuser_asp);
|
|
ED_space_image_width(t->sa->spacedata.first, &wi, &hi);
|
|
w = (((float)wi)/256.0f)*G.sima->zoom * xuser_asp;
|
|
h = (((float)hi)/256.0f)*G.sima->zoom * yuser_asp;
|
|
|
|
cpack(0xFFFFFF);
|
|
glTranslatef(t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], 0.0f);
|
|
|
|
//glRectf(0,0,1,1);
|
|
|
|
setlinestyle(0);
|
|
cpack(0x0);
|
|
fdrawline(-0.020/w, 0, -0.1/w, 0);
|
|
fdrawline(0.1/w, 0, .020/w, 0);
|
|
fdrawline(0, -0.020/h, 0, -0.1/h);
|
|
fdrawline(0, 0.1/h, 0, 0.020/h);
|
|
|
|
glTranslatef(-t->tsnap.snapPoint[0], -t->tsnap.snapPoint[1], 0.0f);
|
|
setlinestyle(0);
|
|
*/
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
int handleSnapping(TransInfo *t, wmEvent *event)
|
|
{
|
|
int status = 0;
|
|
|
|
if (BIF_snappingSupported(t->obedit) && event->type == TABKEY && event->shift)
|
|
{
|
|
/* toggle snap and reinit */
|
|
t->scene->snap_flag ^= SCE_SNAP;
|
|
initSnapping(t, NULL);
|
|
status = 1;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void applySnapping(TransInfo *t, float *vec)
|
|
{
|
|
if (t->tsnap.status & SNAP_FORCED)
|
|
{
|
|
t->tsnap.targetSnap(t);
|
|
|
|
t->tsnap.applySnap(t, vec);
|
|
}
|
|
else if ((t->tsnap.status & SNAP_ON) &&
|
|
(t->modifiers & MOD_SNAP_GEARS))
|
|
{
|
|
double current = PIL_check_seconds_timer();
|
|
|
|
// Time base quirky code to go around findnearest slowness
|
|
/* !TODO! add exception for object mode, no need to slow it down then */
|
|
if (current - t->tsnap.last >= 0.1)
|
|
{
|
|
t->tsnap.calcSnap(t, vec);
|
|
t->tsnap.targetSnap(t);
|
|
|
|
t->tsnap.last = current;
|
|
}
|
|
if ((t->tsnap.status & (POINT_INIT|TARGET_INIT)) == (POINT_INIT|TARGET_INIT))
|
|
{
|
|
t->tsnap.applySnap(t, vec);
|
|
}
|
|
}
|
|
}
|
|
|
|
void resetSnapping(TransInfo *t)
|
|
{
|
|
t->tsnap.status = 0;
|
|
t->tsnap.mode = 0;
|
|
t->tsnap.align = 0;
|
|
t->tsnap.modePoint = 0;
|
|
t->tsnap.modeTarget = 0;
|
|
t->tsnap.last = 0;
|
|
t->tsnap.applySnap = NULL;
|
|
|
|
t->tsnap.snapNormal[0] = 0;
|
|
t->tsnap.snapNormal[1] = 0;
|
|
t->tsnap.snapNormal[2] = 0;
|
|
}
|
|
|
|
int usingSnappingNormal(TransInfo *t)
|
|
{
|
|
return t->tsnap.align;
|
|
}
|
|
|
|
int validSnappingNormal(TransInfo *t)
|
|
{
|
|
if ((t->tsnap.status & (POINT_INIT|TARGET_INIT)) == (POINT_INIT|TARGET_INIT))
|
|
{
|
|
if (Inpf(t->tsnap.snapNormal, t->tsnap.snapNormal) > 0)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void initSnapping(TransInfo *t, wmOperator *op)
|
|
{
|
|
Scene *scene = t->scene;
|
|
Object *obedit = t->obedit;
|
|
int snapping = 0;
|
|
short snap_mode = t->scene->snap_target;
|
|
|
|
resetSnapping(t);
|
|
|
|
if (op && RNA_struct_find_property(op->ptr, "snap") && RNA_property_is_set(op->ptr, "snap"))
|
|
{
|
|
if (RNA_boolean_get(op->ptr, "snap"))
|
|
{
|
|
snapping = 1;
|
|
snap_mode = RNA_enum_get(op->ptr, "snap_mode");
|
|
|
|
t->tsnap.status |= SNAP_FORCED|POINT_INIT;
|
|
RNA_float_get_array(op->ptr, "snap_point", t->tsnap.snapPoint);
|
|
|
|
/* snap align only defined in specific cases */
|
|
if (RNA_struct_find_property(op->ptr, "snap_align"))
|
|
{
|
|
t->tsnap.align = RNA_boolean_get(op->ptr, "snap_align");
|
|
RNA_float_get_array(op->ptr, "snap_normal", t->tsnap.snapNormal);
|
|
Normalize(t->tsnap.snapNormal);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snapping = ((scene->snap_flag & SCE_SNAP) == SCE_SNAP);
|
|
t->tsnap.align = ((t->scene->snap_flag & SCE_SNAP_ROTATE) == SCE_SNAP_ROTATE);
|
|
}
|
|
|
|
if ((t->spacetype == SPACE_VIEW3D || t->spacetype == SPACE_IMAGE) && // Only 3D view or UV
|
|
(t->flag & T_CAMERA) == 0) { // Not with camera selected
|
|
setSnappingCallback(t, snap_mode);
|
|
|
|
/* Edit mode */
|
|
if (t->tsnap.applySnap != NULL && // A snapping function actually exist
|
|
(snapping) && // Only if the snap flag is on
|
|
(obedit != NULL && ELEM(obedit->type, OB_MESH, OB_ARMATURE)) ) // Temporary limited to edit mode meshes or armature
|
|
{
|
|
t->tsnap.status |= SNAP_ON;
|
|
t->tsnap.modePoint = SNAP_GEO;
|
|
|
|
if (t->flag & T_PROP_EDIT)
|
|
{
|
|
t->tsnap.mode = SNAP_NOT_OBEDIT;
|
|
}
|
|
else
|
|
{
|
|
t->tsnap.mode = SNAP_ALL;
|
|
}
|
|
}
|
|
/* Object mode */
|
|
else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
|
|
(snapping) && // Only if the snap flag is on
|
|
(obedit == NULL) ) // Object Mode
|
|
{
|
|
t->tsnap.status |= SNAP_ON;
|
|
t->tsnap.modePoint = SNAP_GEO;
|
|
t->tsnap.mode = SNAP_NOT_SELECTED;
|
|
}
|
|
else
|
|
{
|
|
/* Grid if snap is not possible */
|
|
t->tsnap.modePoint = SNAP_GRID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Always grid outside of 3D view */
|
|
t->tsnap.modePoint = SNAP_GRID;
|
|
}
|
|
}
|
|
|
|
void setSnappingCallback(TransInfo *t, short snap_target)
|
|
{
|
|
t->tsnap.calcSnap = CalcSnapGeometry;
|
|
|
|
switch(snap_target)
|
|
{
|
|
case SCE_SNAP_TARGET_CLOSEST:
|
|
t->tsnap.modeTarget = SNAP_CLOSEST;
|
|
t->tsnap.targetSnap = TargetSnapClosest;
|
|
break;
|
|
case SCE_SNAP_TARGET_CENTER:
|
|
t->tsnap.modeTarget = SNAP_CENTER;
|
|
t->tsnap.targetSnap = TargetSnapCenter;
|
|
break;
|
|
case SCE_SNAP_TARGET_MEDIAN:
|
|
t->tsnap.modeTarget = SNAP_MEDIAN;
|
|
t->tsnap.targetSnap = TargetSnapMedian;
|
|
break;
|
|
case SCE_SNAP_TARGET_ACTIVE:
|
|
t->tsnap.modeTarget = SNAP_ACTIVE;
|
|
t->tsnap.targetSnap = TargetSnapActive;
|
|
break;
|
|
|
|
}
|
|
|
|
switch (t->mode)
|
|
{
|
|
case TFM_TRANSLATION:
|
|
t->tsnap.applySnap = ApplySnapTranslation;
|
|
t->tsnap.distance = TranslationBetween;
|
|
break;
|
|
case TFM_ROTATION:
|
|
t->tsnap.applySnap = ApplySnapRotation;
|
|
t->tsnap.distance = RotationBetween;
|
|
|
|
// Can't do TARGET_CENTER with rotation, use TARGET_MEDIAN instead
|
|
if (snap_target == SCE_SNAP_TARGET_CENTER) {
|
|
t->tsnap.modeTarget = SNAP_MEDIAN;
|
|
t->tsnap.targetSnap = TargetSnapMedian;
|
|
}
|
|
break;
|
|
case TFM_RESIZE:
|
|
t->tsnap.applySnap = ApplySnapResize;
|
|
t->tsnap.distance = ResizeBetween;
|
|
|
|
// Can't do TARGET_CENTER with resize, use TARGET_MEDIAN instead
|
|
if (snap_target == SCE_SNAP_TARGET_CENTER) {
|
|
t->tsnap.modeTarget = SNAP_MEDIAN;
|
|
t->tsnap.targetSnap = TargetSnapMedian;
|
|
}
|
|
break;
|
|
default:
|
|
t->tsnap.applySnap = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/********************** APPLY **************************/
|
|
|
|
void ApplySnapTranslation(TransInfo *t, float vec[3])
|
|
{
|
|
VecSubf(vec, t->tsnap.snapPoint, t->tsnap.snapTarget);
|
|
}
|
|
|
|
void ApplySnapRotation(TransInfo *t, float *vec)
|
|
{
|
|
if (t->tsnap.modeTarget == SNAP_CLOSEST) {
|
|
*vec = t->tsnap.dist;
|
|
}
|
|
else {
|
|
*vec = RotationBetween(t, t->tsnap.snapTarget, t->tsnap.snapPoint);
|
|
}
|
|
}
|
|
|
|
void ApplySnapResize(TransInfo *t, float vec[3])
|
|
{
|
|
if (t->tsnap.modeTarget == SNAP_CLOSEST) {
|
|
vec[0] = vec[1] = vec[2] = t->tsnap.dist;
|
|
}
|
|
else {
|
|
vec[0] = vec[1] = vec[2] = ResizeBetween(t, t->tsnap.snapTarget, t->tsnap.snapPoint);
|
|
}
|
|
}
|
|
|
|
/********************** DISTANCE **************************/
|
|
|
|
float TranslationBetween(TransInfo *t, float p1[3], float p2[3])
|
|
{
|
|
return VecLenf(p1, p2);
|
|
}
|
|
|
|
float RotationBetween(TransInfo *t, float p1[3], float p2[3])
|
|
{
|
|
float angle, start[3], end[3], center[3];
|
|
|
|
VECCOPY(center, t->center);
|
|
if(t->flag & (T_EDIT|T_POSE)) {
|
|
Object *ob= t->obedit?t->obedit:t->poseobj;
|
|
Mat4MulVecfl(ob->obmat, center);
|
|
}
|
|
|
|
VecSubf(start, p1, center);
|
|
VecSubf(end, p2, center);
|
|
|
|
// Angle around a constraint axis (error prone, will need debug)
|
|
if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
|
|
float axis[3], tmp[3];
|
|
|
|
t->con.applyRot(t, NULL, axis, NULL);
|
|
|
|
Projf(tmp, end, axis);
|
|
VecSubf(end, end, tmp);
|
|
|
|
Projf(tmp, start, axis);
|
|
VecSubf(start, start, tmp);
|
|
|
|
Normalize(end);
|
|
Normalize(start);
|
|
|
|
Crossf(tmp, start, end);
|
|
|
|
if (Inpf(tmp, axis) < 0.0)
|
|
angle = -acos(Inpf(start, end));
|
|
else
|
|
angle = acos(Inpf(start, end));
|
|
}
|
|
else {
|
|
float mtx[3][3];
|
|
|
|
Mat3CpyMat4(mtx, t->viewmat);
|
|
|
|
Mat3MulVecfl(mtx, end);
|
|
Mat3MulVecfl(mtx, start);
|
|
|
|
angle = atan2(start[1],start[0]) - atan2(end[1],end[0]);
|
|
}
|
|
|
|
if (angle > M_PI) {
|
|
angle = angle - 2 * M_PI;
|
|
}
|
|
else if (angle < -(M_PI)) {
|
|
angle = 2 * M_PI + angle;
|
|
}
|
|
|
|
return angle;
|
|
}
|
|
|
|
float ResizeBetween(TransInfo *t, float p1[3], float p2[3])
|
|
{
|
|
float d1[3], d2[3], center[3];
|
|
|
|
VECCOPY(center, t->center);
|
|
if(t->flag & (T_EDIT|T_POSE)) {
|
|
Object *ob= t->obedit?t->obedit:t->poseobj;
|
|
Mat4MulVecfl(ob->obmat, center);
|
|
}
|
|
|
|
VecSubf(d1, p1, center);
|
|
VecSubf(d2, p2, center);
|
|
|
|
if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
|
|
Mat3MulVecfl(t->con.pmtx, d1);
|
|
Mat3MulVecfl(t->con.pmtx, d2);
|
|
}
|
|
|
|
return VecLength(d2) / VecLength(d1);
|
|
}
|
|
|
|
/********************** CALC **************************/
|
|
|
|
void CalcSnapGrid(TransInfo *t, float *vec)
|
|
{
|
|
snapGridAction(t, t->tsnap.snapPoint, BIG_GEARS);
|
|
}
|
|
|
|
void CalcSnapGeometry(TransInfo *t, float *vec)
|
|
{
|
|
if (t->spacetype == SPACE_VIEW3D)
|
|
{
|
|
float loc[3];
|
|
float no[3];
|
|
int found = 0;
|
|
int dist = SNAP_MIN_DISTANCE; // Use a user defined value here
|
|
SnapMode mode;
|
|
|
|
if (t->scene->snap_mode == SCE_SNAP_MODE_VOLUME)
|
|
{
|
|
ListBase depth_peels;
|
|
DepthPeel *p1, *p2;
|
|
float *last_p = NULL;
|
|
float dist = FLT_MAX;
|
|
float p[3];
|
|
|
|
depth_peels.first = depth_peels.last = NULL;
|
|
|
|
peelObjectsTransForm(t, &depth_peels, t->mval);
|
|
|
|
// if (LAST_SNAP_POINT_VALID)
|
|
// {
|
|
// last_p = LAST_SNAP_POINT;
|
|
// }
|
|
// else
|
|
// {
|
|
last_p = t->tsnap.snapPoint;
|
|
// }
|
|
|
|
|
|
for (p1 = depth_peels.first; p1; p1 = p1->next)
|
|
{
|
|
if (p1->flag == 0)
|
|
{
|
|
float vec[3];
|
|
float new_dist;
|
|
|
|
p2 = NULL;
|
|
p1->flag = 1;
|
|
|
|
/* if peeling objects, take the first and last from each object */
|
|
if (t->scene->snap_flag & SCE_SNAP_PEEL_OBJECT)
|
|
{
|
|
DepthPeel *peel;
|
|
for (peel = p1->next; peel; peel = peel->next)
|
|
{
|
|
if (peel->ob == p1->ob)
|
|
{
|
|
peel->flag = 1;
|
|
p2 = peel;
|
|
}
|
|
}
|
|
}
|
|
/* otherwise, pair first with second and so on */
|
|
else
|
|
{
|
|
for (p2 = p1->next; p2 && p2->ob != p1->ob; p2 = p2->next)
|
|
{
|
|
/* nothing to do here */
|
|
}
|
|
}
|
|
|
|
if (p2)
|
|
{
|
|
p2->flag = 1;
|
|
|
|
VecAddf(vec, p1->p, p2->p);
|
|
VecMulf(vec, 0.5f);
|
|
}
|
|
else
|
|
{
|
|
VECCOPY(vec, p1->p);
|
|
}
|
|
|
|
if (last_p == NULL)
|
|
{
|
|
VECCOPY(p, vec);
|
|
dist = 0;
|
|
break;
|
|
}
|
|
|
|
new_dist = VecLenf(last_p, vec);
|
|
|
|
if (new_dist < dist)
|
|
{
|
|
VECCOPY(p, vec);
|
|
dist = new_dist;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dist != FLT_MAX)
|
|
{
|
|
VECCOPY(loc, p);
|
|
found = 1;
|
|
}
|
|
|
|
BLI_freelistN(&depth_peels);
|
|
}
|
|
else
|
|
{
|
|
if (t->obedit == NULL)
|
|
{
|
|
mode = SNAP_NOT_SELECTED;
|
|
}
|
|
else
|
|
{
|
|
mode = SNAP_NOT_OBEDIT;
|
|
}
|
|
|
|
found = snapObjectsTransform(t, t->mval, &dist, loc, no, mode);
|
|
}
|
|
|
|
if (found == 1)
|
|
{
|
|
float tangent[3];
|
|
|
|
VecSubf(tangent, loc, t->tsnap.snapPoint);
|
|
tangent[2] = 0;
|
|
|
|
if (Inpf(tangent, tangent) > 0)
|
|
{
|
|
VECCOPY(t->tsnap.snapTangent, tangent);
|
|
}
|
|
|
|
VECCOPY(t->tsnap.snapPoint, loc);
|
|
VECCOPY(t->tsnap.snapNormal, no);
|
|
|
|
t->tsnap.status |= POINT_INIT;
|
|
}
|
|
else
|
|
{
|
|
t->tsnap.status &= ~POINT_INIT;
|
|
}
|
|
}
|
|
else if (t->spacetype == SPACE_IMAGE && t->obedit != NULL && t->obedit->type==OB_MESH)
|
|
{ /* same as above but for UV's */
|
|
/* same as above but for UV's */
|
|
Image *ima= ED_space_image(t->sa->spacedata.first);
|
|
float aspx, aspy, co[2];
|
|
|
|
UI_view2d_region_to_view(&t->ar->v2d, t->mval[0], t->mval[1], co, co+1);
|
|
|
|
if(ED_uvedit_nearest_uv(t->scene, t->obedit, ima, co, t->tsnap.snapPoint))
|
|
{
|
|
ED_space_image_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
|
|
t->tsnap.snapPoint[0] *= aspx;
|
|
t->tsnap.snapPoint[1] *= aspy;
|
|
|
|
Mat4MulVecfl(t->obedit->obmat, t->tsnap.snapPoint);
|
|
|
|
t->tsnap.status |= POINT_INIT;
|
|
}
|
|
else
|
|
{
|
|
t->tsnap.status &= ~POINT_INIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
/********************** TARGET **************************/
|
|
|
|
void TargetSnapCenter(TransInfo *t)
|
|
{
|
|
// Only need to calculate once
|
|
if ((t->tsnap.status & TARGET_INIT) == 0)
|
|
{
|
|
VECCOPY(t->tsnap.snapTarget, t->center);
|
|
if(t->flag & (T_EDIT|T_POSE)) {
|
|
Object *ob= t->obedit?t->obedit:t->poseobj;
|
|
Mat4MulVecfl(ob->obmat, t->tsnap.snapTarget);
|
|
}
|
|
|
|
t->tsnap.status |= TARGET_INIT;
|
|
}
|
|
}
|
|
|
|
void TargetSnapActive(TransInfo *t)
|
|
{
|
|
// Only need to calculate once
|
|
if ((t->tsnap.status & TARGET_INIT) == 0)
|
|
{
|
|
TransData *td = NULL;
|
|
TransData *active_td = NULL;
|
|
int i;
|
|
|
|
for(td = t->data, i = 0 ; i < t->total && td->flag & TD_SELECTED ; i++, td++)
|
|
{
|
|
if (td->flag & TD_ACTIVE)
|
|
{
|
|
active_td = td;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (active_td)
|
|
{
|
|
VECCOPY(t->tsnap.snapTarget, active_td->center);
|
|
|
|
if(t->flag & (T_EDIT|T_POSE)) {
|
|
Object *ob= t->obedit?t->obedit:t->poseobj;
|
|
Mat4MulVecfl(ob->obmat, t->tsnap.snapTarget);
|
|
}
|
|
|
|
t->tsnap.status |= TARGET_INIT;
|
|
}
|
|
/* No active, default to median */
|
|
else
|
|
{
|
|
t->tsnap.modeTarget = SNAP_MEDIAN;
|
|
t->tsnap.targetSnap = TargetSnapMedian;
|
|
TargetSnapMedian(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TargetSnapMedian(TransInfo *t)
|
|
{
|
|
// Only need to calculate once
|
|
if ((t->tsnap.status & TARGET_INIT) == 0)
|
|
{
|
|
TransData *td = NULL;
|
|
int i;
|
|
|
|
t->tsnap.snapTarget[0] = 0;
|
|
t->tsnap.snapTarget[1] = 0;
|
|
t->tsnap.snapTarget[2] = 0;
|
|
|
|
for(td = t->data, i = 0 ; i < t->total && td->flag & TD_SELECTED ; i++, td++)
|
|
{
|
|
VecAddf(t->tsnap.snapTarget, t->tsnap.snapTarget, td->center);
|
|
}
|
|
|
|
VecMulf(t->tsnap.snapTarget, 1.0 / i);
|
|
|
|
if(t->flag & (T_EDIT|T_POSE)) {
|
|
Object *ob= t->obedit?t->obedit:t->poseobj;
|
|
Mat4MulVecfl(ob->obmat, t->tsnap.snapTarget);
|
|
}
|
|
|
|
t->tsnap.status |= TARGET_INIT;
|
|
}
|
|
}
|
|
|
|
void TargetSnapClosest(TransInfo *t)
|
|
{
|
|
// Only valid if a snap point has been selected
|
|
if (t->tsnap.status & POINT_INIT)
|
|
{
|
|
TransData *closest = NULL, *td = NULL;
|
|
|
|
/* Object mode */
|
|
if (t->flag & T_OBJECT)
|
|
{
|
|
int i;
|
|
for(td = t->data, i = 0 ; i < t->total && td->flag & TD_SELECTED ; i++, td++)
|
|
{
|
|
struct BoundBox *bb = object_get_boundbox(td->ob);
|
|
|
|
/* use boundbox if possible */
|
|
if (bb)
|
|
{
|
|
int j;
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
float loc[3];
|
|
float dist;
|
|
|
|
VECCOPY(loc, bb->vec[j]);
|
|
Mat4MulVecfl(td->ext->obmat, loc);
|
|
|
|
dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
|
|
|
|
if (closest == NULL || fabs(dist) < fabs(t->tsnap.dist))
|
|
{
|
|
VECCOPY(t->tsnap.snapTarget, loc);
|
|
closest = td;
|
|
t->tsnap.dist = dist;
|
|
}
|
|
}
|
|
}
|
|
/* use element center otherwise */
|
|
else
|
|
{
|
|
float loc[3];
|
|
float dist;
|
|
|
|
VECCOPY(loc, td->center);
|
|
|
|
dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
|
|
|
|
if (closest == NULL || fabs(dist) < fabs(t->tsnap.dist))
|
|
{
|
|
VECCOPY(t->tsnap.snapTarget, loc);
|
|
closest = td;
|
|
t->tsnap.dist = dist;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
for(td = t->data, i = 0 ; i < t->total && td->flag & TD_SELECTED ; i++, td++)
|
|
{
|
|
float loc[3];
|
|
float dist;
|
|
|
|
VECCOPY(loc, td->center);
|
|
|
|
if(t->flag & (T_EDIT|T_POSE)) {
|
|
Object *ob= t->obedit?t->obedit:t->poseobj;
|
|
Mat4MulVecfl(ob->obmat, loc);
|
|
}
|
|
|
|
dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
|
|
|
|
if (closest == NULL || fabs(dist) < fabs(t->tsnap.dist))
|
|
{
|
|
VECCOPY(t->tsnap.snapTarget, loc);
|
|
closest = td;
|
|
t->tsnap.dist = dist;
|
|
}
|
|
}
|
|
}
|
|
|
|
t->tsnap.status |= TARGET_INIT;
|
|
}
|
|
}
|
|
/*================================================================*/
|
|
|
|
int snapFace(ARegion *ar, float v1co[3], float v2co[3], float v3co[3], float *v4co, short mval[2], float ray_start[3], float ray_start_local[3], float ray_normal_local[3], float obmat[][4], float timat[][3], float *loc, float *no, int *dist, float *depth)
|
|
{
|
|
float lambda;
|
|
int result;
|
|
int retval = 0;
|
|
|
|
result = RayIntersectsTriangleThreshold(ray_start_local, ray_normal_local, v1co, v2co, v3co, &lambda, NULL, 0.001);
|
|
|
|
if (result) {
|
|
float location[3], normal[3];
|
|
float intersect[3];
|
|
float new_depth;
|
|
int screen_loc[2];
|
|
int new_dist;
|
|
|
|
VECCOPY(intersect, ray_normal_local);
|
|
VecMulf(intersect, lambda);
|
|
VecAddf(intersect, intersect, ray_start_local);
|
|
|
|
VECCOPY(location, intersect);
|
|
|
|
if (v4co)
|
|
CalcNormFloat4(v1co, v2co, v3co, v4co, normal);
|
|
else
|
|
CalcNormFloat(v1co, v2co, v3co, normal);
|
|
|
|
Mat4MulVecfl(obmat, location);
|
|
|
|
new_depth = VecLenf(location, ray_start);
|
|
|
|
project_int(ar, location, screen_loc);
|
|
new_dist = abs(screen_loc[0] - mval[0]) + abs(screen_loc[1] - mval[1]);
|
|
|
|
if (new_dist <= *dist && new_depth < *depth)
|
|
{
|
|
*depth = new_depth;
|
|
retval = 1;
|
|
|
|
VECCOPY(loc, location);
|
|
VECCOPY(no, normal);
|
|
|
|
Mat3MulVecfl(timat, no);
|
|
Normalize(no);
|
|
|
|
*dist = new_dist;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int snapEdge(ARegion *ar, float v1co[3], short v1no[3], float v2co[3], short v2no[3], short mval[2], float ray_start[3], float ray_start_local[3], float ray_normal_local[3], float obmat[][4], float timat[][3], float *loc, float *no, int *dist, float *depth)
|
|
{
|
|
float intersect[3] = {0, 0, 0}, ray_end[3], dvec[3];
|
|
int result;
|
|
int retval = 0;
|
|
|
|
VECCOPY(ray_end, ray_normal_local);
|
|
VecMulf(ray_end, 2000);
|
|
VecAddf(ray_end, ray_start_local, ray_end);
|
|
|
|
result = LineIntersectLine(v1co, v2co, ray_start_local, ray_end, intersect, dvec); /* dvec used but we don't care about result */
|
|
|
|
if (result)
|
|
{
|
|
float edge_loc[3], vec[3];
|
|
float mul;
|
|
|
|
/* check for behind ray_start */
|
|
VecSubf(dvec, intersect, ray_start_local);
|
|
|
|
VecSubf(edge_loc, v1co, v2co);
|
|
VecSubf(vec, intersect, v2co);
|
|
|
|
mul = Inpf(vec, edge_loc) / Inpf(edge_loc, edge_loc);
|
|
|
|
if (mul > 1) {
|
|
mul = 1;
|
|
VECCOPY(intersect, v1co);
|
|
}
|
|
else if (mul < 0) {
|
|
mul = 0;
|
|
VECCOPY(intersect, v2co);
|
|
}
|
|
|
|
if (Inpf(ray_normal_local, dvec) > 0)
|
|
{
|
|
float location[3];
|
|
float new_depth;
|
|
int screen_loc[2];
|
|
int new_dist;
|
|
|
|
VECCOPY(location, intersect);
|
|
|
|
Mat4MulVecfl(obmat, location);
|
|
|
|
new_depth = VecLenf(location, ray_start);
|
|
|
|
project_int(ar, location, screen_loc);
|
|
new_dist = abs(screen_loc[0] - mval[0]) + abs(screen_loc[1] - mval[1]);
|
|
|
|
/* 10% threshold if edge is closer but a bit further
|
|
* this takes care of series of connected edges a bit slanted w.r.t the viewport
|
|
* otherwise, it would stick to the verts of the closest edge and not slide along merrily
|
|
* */
|
|
if (new_dist <= *dist && new_depth < *depth * 1.001)
|
|
{
|
|
float n1[3], n2[3];
|
|
|
|
*depth = new_depth;
|
|
retval = 1;
|
|
|
|
VecSubf(edge_loc, v1co, v2co);
|
|
VecSubf(vec, intersect, v2co);
|
|
|
|
mul = Inpf(vec, edge_loc) / Inpf(edge_loc, edge_loc);
|
|
|
|
if (no)
|
|
{
|
|
NormalShortToFloat(n1, v1no);
|
|
NormalShortToFloat(n2, v2no);
|
|
VecLerpf(no, n2, n1, mul);
|
|
Mat3MulVecfl(timat, no);
|
|
Normalize(no);
|
|
}
|
|
|
|
VECCOPY(loc, location);
|
|
|
|
*dist = new_dist;
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int snapVertex(ARegion *ar, float vco[3], short vno[3], short mval[2], float ray_start[3], float ray_start_local[3], float ray_normal_local[3], float obmat[][4], float timat[][3], float *loc, float *no, int *dist, float *depth)
|
|
{
|
|
int retval = 0;
|
|
float dvec[3];
|
|
|
|
VecSubf(dvec, vco, ray_start_local);
|
|
|
|
if (Inpf(ray_normal_local, dvec) > 0)
|
|
{
|
|
float location[3];
|
|
float new_depth;
|
|
int screen_loc[2];
|
|
int new_dist;
|
|
|
|
VECCOPY(location, vco);
|
|
|
|
Mat4MulVecfl(obmat, location);
|
|
|
|
new_depth = VecLenf(location, ray_start);
|
|
|
|
project_int(ar, location, screen_loc);
|
|
new_dist = abs(screen_loc[0] - mval[0]) + abs(screen_loc[1] - mval[1]);
|
|
|
|
if (new_dist <= *dist && new_depth < *depth)
|
|
{
|
|
*depth = new_depth;
|
|
retval = 1;
|
|
|
|
VECCOPY(loc, location);
|
|
|
|
if (no)
|
|
{
|
|
NormalShortToFloat(no, vno);
|
|
Mat3MulVecfl(timat, no);
|
|
Normalize(no);
|
|
}
|
|
|
|
*dist = new_dist;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int snapArmature(short snap_mode, ARegion *ar, Object *ob, bArmature *arm, float obmat[][4], float ray_start[3], float ray_normal[3], short mval[2], float *loc, float *no, int *dist, float *depth)
|
|
{
|
|
float imat[4][4];
|
|
float ray_start_local[3], ray_normal_local[3];
|
|
int retval = 0;
|
|
|
|
Mat4Invert(imat, obmat);
|
|
|
|
VECCOPY(ray_start_local, ray_start);
|
|
VECCOPY(ray_normal_local, ray_normal);
|
|
|
|
Mat4MulVecfl(imat, ray_start_local);
|
|
Mat4Mul3Vecfl(imat, ray_normal_local);
|
|
|
|
if(arm->edbo)
|
|
{
|
|
EditBone *eBone;
|
|
|
|
for (eBone=arm->edbo->first; eBone; eBone=eBone->next) {
|
|
if (eBone->layer & arm->layer) {
|
|
/* skip hidden or moving (selected) bones */
|
|
if ((eBone->flag & (BONE_HIDDEN_A|BONE_ROOTSEL|BONE_TIPSEL))==0) {
|
|
switch (snap_mode)
|
|
{
|
|
case SCE_SNAP_MODE_VERTEX:
|
|
retval |= snapVertex(ar, eBone->head, NULL, mval, ray_start, ray_start_local, ray_normal_local, obmat, NULL, loc, NULL, dist, depth);
|
|
retval |= snapVertex(ar, eBone->tail, NULL, mval, ray_start, ray_start_local, ray_normal_local, obmat, NULL, loc, NULL, dist, depth);
|
|
break;
|
|
case SCE_SNAP_MODE_EDGE:
|
|
retval |= snapEdge(ar, eBone->head, NULL, eBone->tail, NULL, mval, ray_start, ray_start_local, ray_normal_local, obmat, NULL, loc, NULL, dist, depth);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ob->pose && ob->pose->chanbase.first)
|
|
{
|
|
bPoseChannel *pchan;
|
|
Bone *bone;
|
|
|
|
for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
|
|
bone= pchan->bone;
|
|
/* skip hidden bones */
|
|
if (bone && !(bone->flag & (BONE_HIDDEN_P|BONE_HIDDEN_PG))) {
|
|
float *head_vec = pchan->pose_head;
|
|
float *tail_vec = pchan->pose_tail;
|
|
|
|
switch (snap_mode)
|
|
{
|
|
case SCE_SNAP_MODE_VERTEX:
|
|
retval |= snapVertex(ar, head_vec, NULL, mval, ray_start, ray_start_local, ray_normal_local, obmat, NULL, loc, NULL, dist, depth);
|
|
retval |= snapVertex(ar, tail_vec, NULL, mval, ray_start, ray_start_local, ray_normal_local, obmat, NULL, loc, NULL, dist, depth);
|
|
break;
|
|
case SCE_SNAP_MODE_EDGE:
|
|
retval |= snapEdge(ar, head_vec, NULL, tail_vec, NULL, mval, ray_start, ray_start_local, ray_normal_local, obmat, NULL, loc, NULL, dist, depth);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int snapDerivedMesh(short snap_mode, ARegion *ar, Object *ob, DerivedMesh *dm, BMEditMesh *em, float obmat[][4], float ray_start[3], float ray_normal[3], short mval[2], float *loc, float *no, int *dist, float *depth)
|
|
{
|
|
int retval = 0;
|
|
int totvert = dm->getNumVerts(dm);
|
|
int totface = dm->getNumTessFaces(dm);
|
|
|
|
if (totvert > 0) {
|
|
float imat[4][4];
|
|
float timat[3][3]; /* transpose inverse matrix for normals */
|
|
float ray_start_local[3], ray_normal_local[3];
|
|
int test = 1;
|
|
|
|
Mat4Invert(imat, obmat);
|
|
|
|
Mat3CpyMat4(timat, imat);
|
|
Mat3Transp(timat);
|
|
|
|
VECCOPY(ray_start_local, ray_start);
|
|
VECCOPY(ray_normal_local, ray_normal);
|
|
|
|
Mat4MulVecfl(imat, ray_start_local);
|
|
Mat4Mul3Vecfl(imat, ray_normal_local);
|
|
|
|
|
|
/* If number of vert is more than an arbitrary limit,
|
|
* test against boundbox first
|
|
* */
|
|
if (totface > 16) {
|
|
struct BoundBox *bb = object_get_boundbox(ob);
|
|
test = ray_hit_boundbox(bb, ray_start_local, ray_normal_local);
|
|
}
|
|
|
|
if (test == 1) {
|
|
|
|
switch (snap_mode)
|
|
{
|
|
case SCE_SNAP_MODE_FACE:
|
|
{
|
|
MVert *verts = dm->getVertArray(dm);
|
|
MFace *faces = dm->getTessFaceArray(dm);
|
|
int *index_array = NULL;
|
|
int index = 0;
|
|
int i;
|
|
|
|
if (em != NULL)
|
|
{
|
|
index_array = dm->getTessFaceDataArray(dm, CD_ORIGINDEX);
|
|
EDBM_init_index_arrays(em, 0, 0, 1);
|
|
}
|
|
|
|
for( i = 0; i < totface; i++) {
|
|
BMFace *efa = NULL;
|
|
MFace *f = faces + i;
|
|
|
|
test = 1; /* reset for every face */
|
|
|
|
if (em != NULL)
|
|
{
|
|
if (index_array)
|
|
{
|
|
index = index_array[i];
|
|
}
|
|
else
|
|
{
|
|
index = i;
|
|
}
|
|
|
|
if (index == ORIGINDEX_NONE)
|
|
{
|
|
test = 0;
|
|
}
|
|
else
|
|
{
|
|
efa = EDBM_get_face_for_index(em, index);
|
|
|
|
if (efa && BM_TestHFlag(efa, BM_HIDDEN))
|
|
{
|
|
test = 0;
|
|
} else if (efa) {
|
|
BMIter iter;
|
|
BMLoop *l;
|
|
|
|
l = BMIter_New(&iter, em->bm, BM_LOOPS_OF_FACE, efa);
|
|
for ( ; l; l=BMIter_Step(&iter)) {
|
|
if (BM_TestHFlag(l->v, BM_SELECT)) {
|
|
test = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (test)
|
|
{
|
|
int result;
|
|
float *v4co = NULL;
|
|
|
|
if (f->v4)
|
|
{
|
|
v4co = verts[f->v4].co;
|
|
}
|
|
|
|
result = snapFace(ar, verts[f->v1].co, verts[f->v2].co, verts[f->v3].co, v4co, mval, ray_start, ray_start_local, ray_normal_local, obmat, timat, loc, no, dist, depth);
|
|
retval |= result;
|
|
|
|
if (f->v4 && result == 0)
|
|
{
|
|
retval |= snapFace(ar, verts[f->v3].co, verts[f->v4].co, verts[f->v1].co, verts[f->v2].co, mval, ray_start, ray_start_local, ray_normal_local, obmat, timat, loc, no, dist, depth);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (em != NULL)
|
|
{
|
|
EDBM_free_index_arrays(em);
|
|
}
|
|
break;
|
|
}
|
|
case SCE_SNAP_MODE_VERTEX:
|
|
{
|
|
MVert *verts = dm->getVertArray(dm);
|
|
int *index_array = NULL;
|
|
int index = 0;
|
|
int i;
|
|
|
|
if (em != NULL)
|
|
{
|
|
index_array = dm->getVertDataArray(dm, CD_ORIGINDEX);
|
|
EDBM_init_index_arrays(em, 1, 0, 0);
|
|
}
|
|
|
|
for( i = 0; i < totvert; i++) {
|
|
BMVert *eve = NULL;
|
|
MVert *v = verts + i;
|
|
|
|
test = 1; /* reset for every vert */
|
|
|
|
if (em != NULL)
|
|
{
|
|
if (index_array)
|
|
{
|
|
index = index_array[i];
|
|
}
|
|
else
|
|
{
|
|
index = i;
|
|
}
|
|
|
|
if (index == ORIGINDEX_NONE)
|
|
{
|
|
test = 0;
|
|
}
|
|
else
|
|
{
|
|
eve = EDBM_get_vert_for_index(em, index);
|
|
|
|
if (eve && (BM_TestHFlag(eve, BM_HIDDEN) || BM_TestHFlag(eve, BM_SELECT)))
|
|
{
|
|
test = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (test)
|
|
{
|
|
retval |= snapVertex(ar, v->co, v->no, mval, ray_start, ray_start_local, ray_normal_local, obmat, timat, loc, no, dist, depth);
|
|
}
|
|
}
|
|
|
|
if (em != NULL)
|
|
{
|
|
EDBM_free_index_arrays(em);
|
|
}
|
|
break;
|
|
}
|
|
case SCE_SNAP_MODE_EDGE:
|
|
{
|
|
MVert *verts = dm->getVertArray(dm);
|
|
MEdge *edges = dm->getEdgeArray(dm);
|
|
int totedge = dm->getNumEdges(dm);
|
|
int *index_array = NULL;
|
|
int index = 0;
|
|
int i;
|
|
|
|
if (em != NULL)
|
|
{
|
|
index_array = dm->getEdgeDataArray(dm, CD_ORIGINDEX);
|
|
EDBM_init_index_arrays(em, 0, 1, 0);
|
|
}
|
|
|
|
for( i = 0; i < totedge; i++) {
|
|
BMEdge *eed = NULL;
|
|
MEdge *e = edges + i;
|
|
|
|
test = 1; /* reset for every vert */
|
|
|
|
if (em != NULL)
|
|
{
|
|
if (index_array)
|
|
{
|
|
index = index_array[i];
|
|
}
|
|
else
|
|
{
|
|
index = i;
|
|
}
|
|
|
|
if (index == ORIGINDEX_NONE)
|
|
{
|
|
test = 0;
|
|
}
|
|
else
|
|
{
|
|
eed = EDBM_get_edge_for_index(em, index);
|
|
|
|
if (eed && (BM_TestHFlag(eed, BM_HIDDEN) ||
|
|
BM_TestHFlag(eed->v1, BM_SELECT) ||
|
|
BM_TestHFlag(eed->v2, BM_SELECT)))
|
|
{
|
|
test = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (test)
|
|
{
|
|
retval |= snapEdge(ar, verts[e->v1].co, verts[e->v1].no, verts[e->v2].co, verts[e->v2].no, mval, ray_start, ray_start_local, ray_normal_local, obmat, timat, loc, no, dist, depth);
|
|
}
|
|
}
|
|
|
|
if (em != NULL)
|
|
{
|
|
EDBM_free_index_arrays(em);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int snapObject(Scene *scene, ARegion *ar, Object *ob, int editobject, float obmat[][4], float ray_start[3], float ray_normal[3], short mval[2], float *loc, float *no, int *dist, float *depth)
|
|
{
|
|
int retval = 0;
|
|
|
|
if (ob->type == OB_MESH) {
|
|
BMEditMesh *em;
|
|
DerivedMesh *dm;
|
|
|
|
if (editobject)
|
|
{
|
|
em = ((Mesh *)ob->data)->edit_btmesh;
|
|
dm = editbmesh_get_derived_cage(scene, ob, em, CD_MASK_BAREMESH);
|
|
}
|
|
else
|
|
{
|
|
em = NULL;
|
|
dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
|
|
}
|
|
|
|
retval = snapDerivedMesh(scene->snap_mode, ar, ob, dm, em, obmat, ray_start, ray_normal, mval, loc, no, dist, depth);
|
|
|
|
dm->release(dm);
|
|
}
|
|
else if (ob->type == OB_ARMATURE)
|
|
{
|
|
retval = snapArmature(scene->snap_mode, ar, ob, ob->data, obmat, ray_start, ray_normal, mval, loc, no, dist, depth);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int snapObjects(Scene *scene, View3D *v3d, ARegion *ar, Object *obedit, short mval[2], int *dist, float *loc, float *no, SnapMode mode) {
|
|
Base *base;
|
|
float depth = FLT_MAX;
|
|
int retval = 0;
|
|
float ray_start[3], ray_normal[3];
|
|
|
|
viewray(ar, v3d, mval, ray_start, ray_normal);
|
|
|
|
if (mode == SNAP_ALL && obedit)
|
|
{
|
|
Object *ob = obedit;
|
|
|
|
retval |= snapObject(scene, ar, ob, 1, ob->obmat, ray_start, ray_normal, mval, loc, no, dist, &depth);
|
|
}
|
|
|
|
base= FIRSTBASE;
|
|
for ( base = FIRSTBASE; base != NULL; base = base->next ) {
|
|
if ( BASE_SELECTABLE(v3d, base) && (base->flag & (BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA)) == 0 && ((mode == SNAP_NOT_SELECTED && (base->flag & (SELECT|BA_WAS_SEL)) == 0) || (mode == SNAP_NOT_OBEDIT && base != BASACT)) ) {
|
|
Object *ob = base->object;
|
|
|
|
if (ob->transflag & OB_DUPLI)
|
|
{
|
|
DupliObject *dupli_ob;
|
|
ListBase *lb = object_duplilist(scene, ob);
|
|
|
|
for(dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next)
|
|
{
|
|
Object *ob = dupli_ob->ob;
|
|
|
|
retval |= snapObject(scene, ar, ob, 0, dupli_ob->mat, ray_start, ray_normal, mval, loc, no, dist, &depth);
|
|
}
|
|
|
|
free_object_duplilist(lb);
|
|
}
|
|
|
|
retval |= snapObject(scene, ar, ob, 0, ob->obmat, ray_start, ray_normal, mval, loc, no, dist, &depth);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int snapObjectsTransform(TransInfo *t, short mval[2], int *dist, float *loc, float *no, SnapMode mode)
|
|
{
|
|
return snapObjects(t->scene, t->view, t->ar, t->obedit, mval, dist, loc, no, mode);
|
|
}
|
|
|
|
int snapObjectsContext(bContext *C, short mval[2], int *dist, float *loc, float *no, SnapMode mode)
|
|
{
|
|
ScrArea *sa = CTX_wm_area(C);
|
|
View3D *v3d = sa->spacedata.first;
|
|
|
|
return snapObjects(CTX_data_scene(C), v3d, CTX_wm_region(C), CTX_data_edit_object(C), mval, dist, loc, no, mode);
|
|
}
|
|
|
|
/******************** PEELING *********************************/
|
|
|
|
|
|
int cmpPeel(void *arg1, void *arg2)
|
|
{
|
|
DepthPeel *p1 = arg1;
|
|
DepthPeel *p2 = arg2;
|
|
int val = 0;
|
|
|
|
if (p1->depth < p2->depth)
|
|
{
|
|
val = -1;
|
|
}
|
|
else if (p1->depth > p2->depth)
|
|
{
|
|
val = 1;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
void removeDoublesPeel(ListBase *depth_peels)
|
|
{
|
|
DepthPeel *peel;
|
|
|
|
for (peel = depth_peels->first; peel; peel = peel->next)
|
|
{
|
|
DepthPeel *next_peel = peel->next;
|
|
|
|
if (peel && next_peel && ABS(peel->depth - next_peel->depth) < 0.0015)
|
|
{
|
|
peel->next = next_peel->next;
|
|
|
|
if (next_peel->next)
|
|
{
|
|
next_peel->next->prev = peel;
|
|
}
|
|
|
|
MEM_freeN(next_peel);
|
|
}
|
|
}
|
|
}
|
|
|
|
void addDepthPeel(ListBase *depth_peels, float depth, float p[3], float no[3], Object *ob)
|
|
{
|
|
DepthPeel *peel = MEM_callocN(sizeof(DepthPeel), "DepthPeel");
|
|
|
|
peel->depth = depth;
|
|
peel->ob = ob;
|
|
VECCOPY(peel->p, p);
|
|
VECCOPY(peel->no, no);
|
|
|
|
BLI_addtail(depth_peels, peel);
|
|
|
|
peel->flag = 0;
|
|
}
|
|
|
|
int peelDerivedMesh(Object *ob, DerivedMesh *dm, float obmat[][4], float ray_start[3], float ray_normal[3], short mval[2], ListBase *depth_peels)
|
|
{
|
|
int retval = 0;
|
|
int totvert = dm->getNumVerts(dm);
|
|
int totface = dm->getNumTessFaces(dm);
|
|
|
|
if (totvert > 0) {
|
|
float imat[4][4];
|
|
float timat[3][3]; /* transpose inverse matrix for normals */
|
|
float ray_start_local[3], ray_normal_local[3];
|
|
int test = 1;
|
|
|
|
Mat4Invert(imat, obmat);
|
|
|
|
Mat3CpyMat4(timat, imat);
|
|
Mat3Transp(timat);
|
|
|
|
VECCOPY(ray_start_local, ray_start);
|
|
VECCOPY(ray_normal_local, ray_normal);
|
|
|
|
Mat4MulVecfl(imat, ray_start_local);
|
|
Mat4Mul3Vecfl(imat, ray_normal_local);
|
|
|
|
|
|
/* If number of vert is more than an arbitrary limit,
|
|
* test against boundbox first
|
|
* */
|
|
if (totface > 16) {
|
|
struct BoundBox *bb = object_get_boundbox(ob);
|
|
test = ray_hit_boundbox(bb, ray_start_local, ray_normal_local);
|
|
}
|
|
|
|
if (test == 1) {
|
|
MVert *verts = dm->getVertArray(dm);
|
|
MFace *faces = dm->getTessFaceArray(dm);
|
|
int i;
|
|
|
|
for( i = 0; i < totface; i++) {
|
|
MFace *f = faces + i;
|
|
float lambda;
|
|
int result;
|
|
|
|
|
|
result = RayIntersectsTriangleThreshold(ray_start_local, ray_normal_local, verts[f->v1].co, verts[f->v2].co, verts[f->v3].co, &lambda, NULL, 0.001);
|
|
|
|
if (result) {
|
|
float location[3], normal[3];
|
|
float intersect[3];
|
|
float new_depth;
|
|
|
|
VECCOPY(intersect, ray_normal_local);
|
|
VecMulf(intersect, lambda);
|
|
VecAddf(intersect, intersect, ray_start_local);
|
|
|
|
VECCOPY(location, intersect);
|
|
|
|
if (f->v4)
|
|
CalcNormFloat4(verts[f->v1].co, verts[f->v2].co, verts[f->v3].co, verts[f->v4].co, normal);
|
|
else
|
|
CalcNormFloat(verts[f->v1].co, verts[f->v2].co, verts[f->v3].co, normal);
|
|
|
|
Mat4MulVecfl(obmat, location);
|
|
|
|
new_depth = VecLenf(location, ray_start);
|
|
|
|
Mat3MulVecfl(timat, normal);
|
|
Normalize(normal);
|
|
|
|
addDepthPeel(depth_peels, new_depth, location, normal, ob);
|
|
}
|
|
|
|
if (f->v4 && result == 0)
|
|
{
|
|
result = RayIntersectsTriangleThreshold(ray_start_local, ray_normal_local, verts[f->v3].co, verts[f->v4].co, verts[f->v1].co, &lambda, NULL, 0.001);
|
|
|
|
if (result) {
|
|
float location[3], normal[3];
|
|
float intersect[3];
|
|
float new_depth;
|
|
|
|
VECCOPY(intersect, ray_normal_local);
|
|
VecMulf(intersect, lambda);
|
|
VecAddf(intersect, intersect, ray_start_local);
|
|
|
|
VECCOPY(location, intersect);
|
|
|
|
if (f->v4)
|
|
CalcNormFloat4(verts[f->v1].co, verts[f->v2].co, verts[f->v3].co, verts[f->v4].co, normal);
|
|
else
|
|
CalcNormFloat(verts[f->v1].co, verts[f->v2].co, verts[f->v3].co, normal);
|
|
|
|
Mat4MulVecfl(obmat, location);
|
|
|
|
new_depth = VecLenf(location, ray_start);
|
|
|
|
Mat3MulVecfl(timat, normal);
|
|
Normalize(normal);
|
|
|
|
addDepthPeel(depth_peels, new_depth, location, normal, ob);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int peelObjects(Scene *scene, View3D *v3d, ARegion *ar, Object *obedit, ListBase *depth_peels, short mval[2])
|
|
{
|
|
Base *base;
|
|
int retval = 0;
|
|
float ray_start[3], ray_normal[3];
|
|
|
|
viewray(ar, v3d, mval, ray_start, ray_normal);
|
|
|
|
for ( base = scene->base.first; base != NULL; base = base->next ) {
|
|
if ( BASE_SELECTABLE(v3d, base) ) {
|
|
Object *ob = base->object;
|
|
|
|
if (ob->transflag & OB_DUPLI)
|
|
{
|
|
DupliObject *dupli_ob;
|
|
ListBase *lb = object_duplilist(scene, ob);
|
|
|
|
for(dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next)
|
|
{
|
|
Object *ob = dupli_ob->ob;
|
|
|
|
if (ob->type == OB_MESH) {
|
|
#if 0 //BMESH_TODO
|
|
EditMesh *em;
|
|
DerivedMesh *dm = NULL;
|
|
int val;
|
|
|
|
if (ob != obedit)
|
|
{
|
|
dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
|
|
|
|
val = peelDerivedMesh(ob, dm, ob->obmat, ray_start, ray_normal, mval, depth_peels);
|
|
}
|
|
else
|
|
{
|
|
em = ((Mesh *)ob->data)->edit_mesh;
|
|
dm = editmesh_get_derived_cage(scene, obedit, em, CD_MASK_BAREMESH);
|
|
|
|
val = peelDerivedMesh(ob, dm, ob->obmat, ray_start, ray_normal, mval, depth_peels);
|
|
}
|
|
|
|
retval = retval || val;
|
|
|
|
dm->release(dm);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
free_object_duplilist(lb);
|
|
}
|
|
|
|
if (ob->type == OB_MESH) {
|
|
BMEditMesh *em;
|
|
DerivedMesh *dm = NULL;
|
|
int val;
|
|
|
|
if (ob != obedit)
|
|
{
|
|
dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
|
|
|
|
val = peelDerivedMesh(ob, dm, ob->obmat, ray_start, ray_normal, mval, depth_peels);
|
|
}
|
|
else
|
|
{
|
|
em = ((Mesh *)ob->data)->edit_btmesh;
|
|
dm = editbmesh_get_derived_cage(scene, obedit, em, CD_MASK_BAREMESH);
|
|
|
|
val = peelDerivedMesh(ob, dm, ob->obmat, ray_start, ray_normal, mval, depth_peels);
|
|
}
|
|
|
|
retval = retval || val;
|
|
|
|
dm->release(dm);
|
|
}
|
|
}
|
|
}
|
|
|
|
BLI_sortlist(depth_peels, cmpPeel);
|
|
removeDoublesPeel(depth_peels);
|
|
|
|
return retval;
|
|
}
|
|
|
|
int peelObjectsTransForm(TransInfo *t, ListBase *depth_peels, short mval[2])
|
|
{
|
|
return peelObjects(t->scene, t->view, t->ar, t->obedit, depth_peels, mval);
|
|
}
|
|
|
|
int peelObjectsContext(bContext *C, ListBase *depth_peels, short mval[2])
|
|
{
|
|
ScrArea *sa = CTX_wm_area(C);
|
|
View3D *v3d = sa->spacedata.first;
|
|
|
|
return peelObjects(CTX_data_scene(C), v3d, CTX_wm_region(C), CTX_data_edit_object(C), depth_peels, mval);
|
|
}
|
|
|
|
/*================================================================*/
|
|
|
|
static void applyGrid(TransInfo *t, float *val, int max_index, float fac[3], GearsType action);
|
|
|
|
|
|
void snapGridAction(TransInfo *t, float *val, GearsType action) {
|
|
float fac[3];
|
|
|
|
fac[NO_GEARS] = t->snap[0];
|
|
fac[BIG_GEARS] = t->snap[1];
|
|
fac[SMALL_GEARS] = t->snap[2];
|
|
|
|
applyGrid(t, val, t->idx_max, fac, action);
|
|
}
|
|
|
|
|
|
void snapGrid(TransInfo *t, float *val) {
|
|
int invert;
|
|
GearsType action;
|
|
|
|
// Only do something if using Snap to Grid
|
|
if (t->tsnap.modePoint != SNAP_GRID)
|
|
return;
|
|
|
|
if(t->mode==TFM_ROTATION || t->mode==TFM_WARP || t->mode==TFM_TILT || t->mode==TFM_TRACKBALL || t->mode==TFM_BONE_ROLL)
|
|
invert = U.flag & USER_AUTOROTGRID;
|
|
else if(t->mode==TFM_RESIZE || t->mode==TFM_SHEAR || t->mode==TFM_BONESIZE || t->mode==TFM_SHRINKFATTEN || t->mode==TFM_CURVE_SHRINKFATTEN)
|
|
invert = U.flag & USER_AUTOSIZEGRID;
|
|
else
|
|
invert = U.flag & USER_AUTOGRABGRID;
|
|
|
|
if(invert) {
|
|
action = (t->modifiers & MOD_SNAP_GEARS) ? NO_GEARS: BIG_GEARS;
|
|
}
|
|
else {
|
|
action = (t->modifiers & MOD_SNAP_GEARS) ? BIG_GEARS : NO_GEARS;
|
|
}
|
|
|
|
if (action == BIG_GEARS && (t->modifiers & MOD_PRECISION)) {
|
|
action = SMALL_GEARS;
|
|
}
|
|
|
|
snapGridAction(t, val, action);
|
|
}
|
|
|
|
|
|
static void applyGrid(TransInfo *t, float *val, int max_index, float fac[3], GearsType action)
|
|
{
|
|
int i;
|
|
float asp[3] = {1.0f, 1.0f, 1.0f}; // TODO: Remove hard coded limit here (3)
|
|
|
|
// Early bailing out if no need to snap
|
|
if (fac[action] == 0.0)
|
|
return;
|
|
|
|
/* evil hack - snapping needs to be adapted for image aspect ratio */
|
|
if((t->spacetype==SPACE_IMAGE) && (t->mode==TFM_TRANSLATION)) {
|
|
ED_space_image_uv_aspect(t->sa->spacedata.first, asp, asp+1);
|
|
}
|
|
|
|
for (i=0; i<=max_index; i++) {
|
|
val[i]= fac[action]*asp[i]*(float)floor(val[i]/(fac[action]*asp[i]) +.5);
|
|
}
|
|
}
|