Example #1
0
static float do_clump_level(float result[3], const float co[3], const float par_co[3], float time,
                            float clumpfac, float clumppow, float pa_clump, CurveMapping *clumpcurve)
{
	float clump = 0.0f;

	if (clumpcurve) {
		clump = pa_clump * (1.0f - clamp_f(curvemapping_evaluateF(clumpcurve, 0, time), 0.0f, 1.0f));

		interp_v3_v3v3(result, co, par_co, clump);
	}
	else if (clumpfac != 0.0f) {
		float cpow;

		if (clumppow < 0.0f)
			cpow = 1.0f + clumppow;
		else
			cpow = 1.0f + 9.0f * clumppow;

		if (clumpfac < 0.0f) /* clump roots instead of tips */
			clump = -clumpfac * pa_clump * (float)pow(1.0 - (double)time, (double)cpow);
		else
			clump = clumpfac * pa_clump * (float)pow((double)time, (double)cpow);

		interp_v3_v3v3(result, co, par_co, clump);
	}

	return clump;
}
Example #2
0
static void get_strand_normal(Material *ma, const float surfnor[3], float surfdist, float nor[3])
{
	float cross[3], nstrand[3], vnor[3], blend;

	if (!((ma->mode & MA_STR_SURFDIFF) || (ma->strand_surfnor > 0.0f)))
		return;

	if (ma->mode & MA_STR_SURFDIFF) {
		cross_v3_v3v3(cross, surfnor, nor);
		cross_v3_v3v3(nstrand, nor, cross);

		blend = dot_v3v3(nstrand, surfnor);
		CLAMP(blend, 0.0f, 1.0f);

		interp_v3_v3v3(vnor, nstrand, surfnor, blend);
		normalize_v3(vnor);
	}
	else {
		copy_v3_v3(vnor, nor);
	}

	if (ma->strand_surfnor > 0.0f) {
		if (ma->strand_surfnor > surfdist) {
			blend = (ma->strand_surfnor - surfdist) / ma->strand_surfnor;
			interp_v3_v3v3(vnor, vnor, surfnor, blend);
			normalize_v3(vnor);
		}
	}

	copy_v3_v3(nor, vnor);
}
Example #3
0
static void stroke_elem_interp(
        struct StrokeElem *selem_out,
        const struct StrokeElem *selem_a,  const struct StrokeElem *selem_b, float t)
{
	interp_v2_v2v2(selem_out->mval, selem_a->mval, selem_b->mval, t);
	interp_v3_v3v3(selem_out->location_world, selem_a->location_world, selem_b->location_world, t);
	interp_v3_v3v3(selem_out->location_local, selem_a->location_local, selem_b->location_local, t);
	selem_out->pressure = interpf(selem_a->pressure, selem_b->pressure, t);
}
Example #4
0
static void rotateBevelPiece(Curve *cu, BevPoint *bevp, BevPoint *nbevp, DispList *dlb, float bev_blend, float widfac, float fac, float **r_data)
{
	float *fp, *data = *r_data;
	int b;

	fp = dlb->verts;
	for (b = 0; b < dlb->nr; b++, fp += 3, data += 3) {
		if (cu->flag & CU_3D) {
			float vec[3], quat[4];

			vec[0] = fp[1] + widfac;
			vec[1] = fp[2];
			vec[2] = 0.0;

			if (nbevp == NULL) {
				copy_v3_v3(data, bevp->vec);
				copy_qt_qt(quat, bevp->quat);
			}
			else {
				interp_v3_v3v3(data, bevp->vec, nbevp->vec, bev_blend);
				interp_qt_qtqt(quat, bevp->quat, nbevp->quat, bev_blend);
			}

			mul_qt_v3(quat, vec);

			data[0] += fac * vec[0];
			data[1] += fac * vec[1];
			data[2] += fac * vec[2];
		}
		else {
			float sina, cosa;

			if (nbevp == NULL) {
				copy_v3_v3(data, bevp->vec);
				sina = bevp->sina;
				cosa = bevp->cosa;
			}
			else {
				interp_v3_v3v3(data, bevp->vec, nbevp->vec, bev_blend);

				/* perhaps we need to interpolate angles instead. but the thing is
				 * cosa and sina are not actually sine and cosine
				 */
				sina = nbevp->sina * bev_blend + bevp->sina * (1.0f - bev_blend);
				cosa = nbevp->cosa * bev_blend + bevp->cosa * (1.0f - bev_blend);
			}

			data[0] += fac * (widfac + fp[1]) * sina;
			data[1] += fac * (widfac + fp[1]) * cosa;
			data[2] += fac * fp[2];
		}
	}

	*r_data = data;
}
Example #5
0
static void tri_v3_scale(
        float v1[3], float v2[3], float v3[3],
        const float t)
{
	float p[3];

	mid_v3_v3v3v3(p, v1, v2, v3);

	interp_v3_v3v3(v1, p, v1, t);
	interp_v3_v3v3(v2, p, v2, t);
	interp_v3_v3v3(v3, p, v3, t);
}
Example #6
0
/**
 * Subdivide a stroke once, by adding a point half way between each pair of existing points
 * \param gps: Stroke data
 * \param new_totpoints: Total number of points (after subdividing)
 */
void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints)
{
	/* Move points towards end of enlarged points array to leave space for new points */
	int y = 1;
	for (int i = gps->totpoints - 1; i > 0; i--) {
		gps->points[new_totpoints - y] = gps->points[i];
		y += 2;
	}

	/* Create interpolated points */
	for (int i = 0; i < new_totpoints - 1; i += 2) {
		bGPDspoint *prev  = &gps->points[i];
		bGPDspoint *pt    = &gps->points[i + 1];
		bGPDspoint *next  = &gps->points[i + 2];

		/* Interpolate all values */
		interp_v3_v3v3(&pt->x, &prev->x, &next->x, 0.5f);

		pt->pressure = interpf(prev->pressure, next->pressure, 0.5f);
		pt->strength = interpf(prev->strength, next->strength, 0.5f);
		CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
		pt->time = interpf(prev->time, next->time, 0.5f);
	}

	/* Update to new total number of points */
	gps->totpoints = new_totpoints;
}
Example #7
0
/* results in new vertex with correct coordinate, vertex normal and weight group info */
static BMVert *bm_subdivide_edge_addvert(BMesh *bm, BMEdge *edge, BMEdge *oedge,
                                         const SubDParams *params, float percent,
                                         float percent2,
                                         BMEdge **out, BMVert *vsta, BMVert *vend)
{
	BMVert *ev;
	
	ev = BM_edge_split(bm, edge, edge->v1, out, percent);

	BMO_elem_flag_enable(bm, ev, ELE_INNER);

	/* offset for smooth or sphere or fractal */
	alter_co(ev, oedge, params, percent2, vsta, vend);

#if 0 //BMESH_TODO
	/* clip if needed by mirror modifier */
	if (edge->v1->f2) {
		if (edge->v1->f2 & edge->v2->f2 & 1) {
			co[0] = 0.0f;
		}
		if (edge->v1->f2 & edge->v2->f2 & 2) {
			co[1] = 0.0f;
		}
		if (edge->v1->f2 & edge->v2->f2 & 4) {
			co[2] = 0.0f;
		}
	}
#endif
	
	interp_v3_v3v3(ev->no, vsta->no, vend->no, percent2);
	normalize_v3(ev->no);

	return ev;
}
Example #8
0
static void hook_co_apply(struct HookData_cb *hd, const int j)
{
	float *co = hd->vertexCos[j];
	float fac;

	if (hd->use_falloff) {
		float len_sq;

		if (hd->use_uniform) {
			float co_uniform[3];
			mul_v3_m3v3(co_uniform, hd->mat_uniform, co);
			len_sq = len_squared_v3v3(hd->cent, co_uniform);
		}
		else {
			len_sq = len_squared_v3v3(hd->cent, co);
		}

		fac = hook_falloff(hd, len_sq);
	}
	else {
		fac = hd->fac_orig;
	}

	if (fac) {
		if (hd->dvert) {
			fac *= defvert_find_weight(&hd->dvert[j], hd->defgrp_index);
		}

		if (fac) {
			float co_tmp[3];
			mul_v3_m4v3(co_tmp, hd->mat, co);
			interp_v3_v3v3(co, co, co_tmp, fac);
		}
	}
}
Example #9
0
static void node_shader_exec_curve_rgb(void *UNUSED(data), bNode *node, bNodeStack **in, bNodeStack **out)
{
	float vec[3];
	
	/* stack order input:  vec */
	/* stack order output: vec */
	nodestack_get_vec(vec, SOCK_VECTOR, in[1]);
	curvemapping_evaluateRGBF(node->storage, out[0]->vec, vec);
	if(in[0]->vec[0] != 1.0f) {
		interp_v3_v3v3(out[0]->vec, vec, out[0]->vec, *in[0]->vec);
	}
}
/**
 * Given two corners, computes approximation of surface intersection point between them.
 * In case of small threshold, do bisection.
 */
static void converge(PROCESS *process, const CORNER *c1, const CORNER *c2, float r_p[3])
{
	float tmp, dens;
	unsigned int i;
	float c1_value, c1_co[3];
	float c2_value, c2_co[3];

	if (c1->value < c2->value) {
		c1_value = c2->value;
		copy_v3_v3(c1_co, c2->co);
		c2_value = c1->value;
		copy_v3_v3(c2_co, c1->co);
	}
	else {
		c1_value = c1->value;
		copy_v3_v3(c1_co, c1->co);
		c2_value = c2->value;
		copy_v3_v3(c2_co, c2->co);
	}


	for (i = 0; i < process->converge_res; i++) {
		interp_v3_v3v3(r_p, c1_co, c2_co, 0.5f);
		dens = metaball(process, r_p[0], r_p[1], r_p[2]);

		if (dens > 0.0f) {
			c1_value = dens;
			copy_v3_v3(c1_co, r_p);
		}
		else {
			c2_value = dens;
			copy_v3_v3(c2_co, r_p);
		}
	}

	tmp = -c1_value / (c2_value - c1_value);
	interp_v3_v3v3(r_p, c1_co, c2_co, tmp);
}
static void track_channel_color(MovieTrackingTrack *track, float default_color[3], float color[3])
{
	if (track->flag & TRACK_CUSTOMCOLOR) {
		float bg[3];
		UI_GetThemeColor3fv(TH_HEADER, bg);

		interp_v3_v3v3(color, track->color, bg, 0.5);
	}
	else {
		if (default_color)
			copy_v3_v3(color, default_color);
		else
			UI_GetThemeColor3fv(TH_HEADER, color);
	}
}
Example #12
0
static void blend_pose_strides(bPose *dst, bPose *src, float srcweight, short mode)
{
	float dstweight;
	
	switch (mode){
		case ACTSTRIPMODE_BLEND:
			dstweight = 1.0F - srcweight;
			break;
		case ACTSTRIPMODE_ADD:
			dstweight = 1.0F;
			break;
		default :
			dstweight = 1.0F;
	}
	
	interp_v3_v3v3(dst->stride_offset, dst->stride_offset, src->stride_offset, srcweight);
}
Example #13
0
void InpaintSimpleOperation::pix_step(int x, int y)
{
  const int d = this->mdist(x, y);
  float pix[3] = {0.0f, 0.0f, 0.0f};
  float pix_divider = 0.0f;

  for (int dx = -1; dx <= 1; dx++) {
    for (int dy = -1; dy <= 1; dy++) {
      /* changing to both != 0 gives dithering artifacts */
      if (dx != 0 || dy != 0) {
        int x_ofs = x + dx;
        int y_ofs = y + dy;

        this->clamp_xy(x_ofs, y_ofs);

        if (this->mdist(x_ofs, y_ofs) < d) {

          float weight;

          if (dx == 0 || dy == 0) {
            weight = 1.0f;
          }
          else {
            weight = M_SQRT1_2; /* 1.0f / sqrt(2) */
          }

          madd_v3_v3fl(pix, this->get_pixel(x_ofs, y_ofs), weight);
          pix_divider += weight;
        }
      }
    }
  }

  float *output = this->get_pixel(x, y);
  if (pix_divider != 0.0f) {
    mul_v3_fl(pix, 1.0f / pix_divider);
    /* use existing pixels alpha to blend into */
    interp_v3_v3v3(output, pix, output, output[3]);
    output[3] = 1.0f;
  }
}
Example #14
0
/* results in new vertex with correct coordinate, vertex normal and weight group info */
static BMVert *bm_subdivide_edge_addvert(BMesh *bm,
                                         BMEdge *edge,
                                         BMEdge *e_orig,
                                         const SubDParams *params,
                                         const float factor_edge_split,
                                         const float factor_subd,
                                         BMVert *v_a,
                                         BMVert *v_b,
                                         BMEdge **r_edge)
{
  BMVert *v_new;

  v_new = BM_edge_split(bm, edge, edge->v1, r_edge, factor_edge_split);

  BMO_vert_flag_enable(bm, v_new, ELE_INNER);

  /* offset for smooth or sphere or fractal */
  alter_co(v_new, e_orig, params, factor_subd, v_a, v_b);

#if 0  // BMESH_TODO
  /* clip if needed by mirror modifier */
  if (edge->v1->f2) {
    if (edge->v1->f2 & edge->v2->f2 & 1) {
      co[0] = 0.0f;
    }
    if (edge->v1->f2 & edge->v2->f2 & 2) {
      co[1] = 0.0f;
    }
    if (edge->v1->f2 & edge->v2->f2 & 4) {
      co[2] = 0.0f;
    }
  }
#endif

  interp_v3_v3v3(v_new->no, v_a->no, v_b->no, factor_subd);
  normalize_v3(v_new->no);

  return v_new;
}
Example #15
0
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;
}
Example #16
0
void calc_latt_deform(Object *ob, float co[3], float weight)
{
	Lattice *lt = ob->data;
	float u, v, w, tu[4], tv[4], tw[4];
	float vec[3];
	int idx_w, idx_v, idx_u;
	int ui, vi, wi, uu, vv, ww;

	/* vgroup influence */
	int defgroup_nr = -1;
	float co_prev[3], weight_blend = 0.0f;
	MDeformVert *dvert = BKE_lattice_deform_verts_get(ob);


	if (lt->editlatt) lt = lt->editlatt->latt;
	if (lt->latticedata == NULL) return;

	if (lt->vgroup[0] && dvert) {
		defgroup_nr = defgroup_name_index(ob, lt->vgroup);
		copy_v3_v3(co_prev, co);
	}

	/* co is in local coords, treat with latmat */
	mul_v3_m4v3(vec, lt->latmat, co);

	/* u v w coords */

	if (lt->pntsu > 1) {
		u = (vec[0] - lt->fu) / lt->du;
		ui = (int)floor(u);
		u -= ui;
		key_curve_position_weights(u, tu, lt->typeu);
	}
	else {
		tu[0] = tu[2] = tu[3] = 0.0; tu[1] = 1.0;
		ui = 0;
	}

	if (lt->pntsv > 1) {
		v = (vec[1] - lt->fv) / lt->dv;
		vi = (int)floor(v);
		v -= vi;
		key_curve_position_weights(v, tv, lt->typev);
	}
	else {
		tv[0] = tv[2] = tv[3] = 0.0; tv[1] = 1.0;
		vi = 0;
	}

	if (lt->pntsw > 1) {
		w = (vec[2] - lt->fw) / lt->dw;
		wi = (int)floor(w);
		w -= wi;
		key_curve_position_weights(w, tw, lt->typew);
	}
	else {
		tw[0] = tw[2] = tw[3] = 0.0; tw[1] = 1.0;
		wi = 0;
	}

	for (ww = wi - 1; ww <= wi + 2; ww++) {
		w = tw[ww - wi + 1];

		if (w != 0.0f) {
			if (ww > 0) {
				if (ww < lt->pntsw) idx_w = ww * lt->pntsu * lt->pntsv;
				else idx_w = (lt->pntsw - 1) * lt->pntsu * lt->pntsv;
			}
			else idx_w = 0;

			for (vv = vi - 1; vv <= vi + 2; vv++) {
				v = w * tv[vv - vi + 1];

				if (v != 0.0f) {
					if (vv > 0) {
						if (vv < lt->pntsv) idx_v = idx_w + vv * lt->pntsu;
						else idx_v = idx_w + (lt->pntsv - 1) * lt->pntsu;
					}
					else idx_v = idx_w;

					for (uu = ui - 1; uu <= ui + 2; uu++) {
						u = weight * v * tu[uu - ui + 1];

						if (u != 0.0f) {
							if (uu > 0) {
								if (uu < lt->pntsu) idx_u = idx_v + uu;
								else idx_u = idx_v + (lt->pntsu - 1);
							}
							else idx_u = idx_v;

							madd_v3_v3fl(co, &lt->latticedata[idx_u * 3], u);

							if (defgroup_nr != -1)
								weight_blend += (u * defvert_find_weight(dvert + idx_u, defgroup_nr));
						}
					}
				}
			}
		}
	}

	if (defgroup_nr != -1)
		interp_v3_v3v3(co, co_prev, co, weight_blend);

}
Example #17
0
static int update_reports_display_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
{
	wmWindowManager *wm = CTX_wm_manager(C);
	ReportList *reports = CTX_wm_reports(C);
	Report *report;
	ReportTimerInfo *rti;
	float progress = 0.0, color_progress = 0.0;
	float neutral_col[3] = {0.35, 0.35, 0.35};
	float neutral_gray = 0.6;
	float timeout = 0.0, color_timeout = 0.0;
	int send_note = 0;
	
	/* escape if not our timer */
	if ((reports->reporttimer == NULL) ||
	    (reports->reporttimer != event->customdata) ||
	    ((report = BKE_reports_last_displayable(reports)) == NULL) /* may have been deleted */
	    )
	{
		return OPERATOR_PASS_THROUGH;
	}

	rti = (ReportTimerInfo *)reports->reporttimer->customdata;
	
	timeout = (report->type & RPT_ERROR_ALL) ? ERROR_TIMEOUT : INFO_TIMEOUT;
	color_timeout = (report->type & RPT_ERROR_ALL) ? ERROR_COLOR_TIMEOUT : INFO_COLOR_TIMEOUT;
	
	/* clear the report display after timeout */
	if ((float)reports->reporttimer->duration > timeout) {
		WM_event_remove_timer(wm, NULL, reports->reporttimer);
		reports->reporttimer = NULL;
		
		WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL);
		
		return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
	}

	if (rti->widthfac == 0.0f) {
		/* initialize colors based on report type */
		if (report->type & RPT_ERROR_ALL) {
			rti->col[0] = 1.0;
			rti->col[1] = 0.2;
			rti->col[2] = 0.0;
		}
		else if (report->type & RPT_WARNING_ALL) {
			rti->col[0] = 1.0;
			rti->col[1] = 1.0;
			rti->col[2] = 0.0;
		}
		else if (report->type & RPT_INFO_ALL) {
			rti->col[0] = 0.3;
			rti->col[1] = 0.45;
			rti->col[2] = 0.7;
		}
		rti->grayscale = 0.75;
		rti->widthfac = 1.0;
	}
	
	progress = (float)reports->reporttimer->duration / timeout;
	color_progress = (float)reports->reporttimer->duration / color_timeout;
	
	/* save us from too many draws */
	if (color_progress <= 1.0f) {
		send_note = 1;
		
		/* fade colors out sharply according to progress through fade-out duration */
		interp_v3_v3v3(rti->col, rti->col, neutral_col, color_progress);
		rti->grayscale = interpf(neutral_gray, rti->grayscale, color_progress);
	}

	/* collapse report at end of timeout */
	if (progress * timeout > timeout - COLLAPSE_TIMEOUT) {
		rti->widthfac = (progress * timeout - (timeout - COLLAPSE_TIMEOUT)) / COLLAPSE_TIMEOUT;
		rti->widthfac = 1.0f - rti->widthfac;
		send_note = 1;
	}
	
	if (send_note) {
		WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL);
	}
	
	return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
}
Example #18
0
int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *effectors)
{
	/* Hair currently is a cloth sim in disguise ...
	 * Collision detection and volumetrics work differently then.
	 * Bad design, TODO
	 */
	const bool is_hair = (clmd->hairdata != NULL);
	
	unsigned int i=0;
	float step=0.0f, tf=clmd->sim_parms->timescale;
	Cloth *cloth = clmd->clothObject;
	ClothVertex *verts = cloth->verts/*, *cv*/;
	unsigned int mvert_num = cloth->mvert_num;
	float dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame;
	Implicit_Data *id = cloth->implicit;
	ColliderContacts *contacts = NULL;
	int totcolliders = 0;
	
	BKE_sim_debug_data_clear_category("collision");
	
	if (!clmd->solver_result)
		clmd->solver_result = (ClothSolverResult *)MEM_callocN(sizeof(ClothSolverResult), "cloth solver result");
	cloth_clear_result(clmd);
	
	if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { /* do goal stuff */
		for (i = 0; i < mvert_num; i++) {
			// update velocities with constrained velocities from pinned verts
			if (verts[i].flags & CLOTH_VERT_FLAG_PINNED) {
				float v[3];
				sub_v3_v3v3(v, verts[i].xconst, verts[i].xold);
				// mul_v3_fl(v, clmd->sim_parms->stepsPerFrame);
				/* divide by time_scale to prevent constrained velocities from being multiplied */
				mul_v3_fl(v, 1.0f / clmd->sim_parms->time_scale);
				BPH_mass_spring_set_velocity(id, i, v);
			}
		}
	}
	
	while (step < tf) {
		ImplicitSolverResult result;
		
		/* copy velocities for collision */
		for (i = 0; i < mvert_num; i++) {
			BPH_mass_spring_get_motion_state(id, i, NULL, verts[i].tv);
			copy_v3_v3(verts[i].v, verts[i].tv);
		}
		
		if (is_hair) {
			/* determine contact points */
			if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
				cloth_find_point_contacts(ob, clmd, 0.0f, tf, &contacts, &totcolliders);
			}
			
			/* setup vertex constraints for pinned vertices and contacts */
			cloth_setup_constraints(clmd, contacts, totcolliders, dt);
		}
		else {
			/* setup vertex constraints for pinned vertices */
			cloth_setup_constraints(clmd, NULL, 0, dt);
		}
		
		/* initialize forces to zero */
		BPH_mass_spring_clear_forces(id);
		
		// damping velocity for artistic reasons
		// this is a bad way to do it, should be removed imo - lukas_t
		if (clmd->sim_parms->vel_damping != 1.0f) {
			for (i = 0; i < mvert_num; i++) {
				float v[3];
				BPH_mass_spring_get_motion_state(id, i, NULL, v);
				mul_v3_fl(v, clmd->sim_parms->vel_damping);
				BPH_mass_spring_set_velocity(id, i, v);
			}
		}
		
		// calculate forces
		cloth_calc_force(clmd, frame, effectors, step);
		
		// calculate new velocity and position
		BPH_mass_spring_solve_velocities(id, dt, &result);
		cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
		
		if (is_hair) {
			cloth_continuum_step(clmd, dt);
		}
		
		BPH_mass_spring_solve_positions(id, dt);
		
		if (!is_hair) {
			cloth_collision_solve_extra(ob, clmd, effectors, frame, step, dt);
		}
		
		BPH_mass_spring_apply_result(id);
		
		/* move pinned verts to correct position */
		for (i = 0; i < mvert_num; i++) {
			if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) {
				if (verts[i].flags & CLOTH_VERT_FLAG_PINNED) {
					float x[3];
					/* divide by time_scale to prevent pinned vertices' delta locations from being multiplied */
					interp_v3_v3v3(x, verts[i].xold, verts[i].xconst, (step + dt) / clmd->sim_parms->time_scale);
					BPH_mass_spring_set_position(id, i, x);
				}
			}
			
			BPH_mass_spring_get_motion_state(id, i, verts[i].txold, NULL);
		}
		
		/* free contact points */
		if (contacts) {
			cloth_free_contacts(contacts, totcolliders);
		}
		
		step += dt;
	}
	
	/* copy results back to cloth data */
	for (i = 0; i < mvert_num; i++) {
		BPH_mass_spring_get_motion_state(id, i, verts[i].x, verts[i].v);
		copy_v3_v3(verts[i].txold, verts[i].x);
	}
	
	return 1;
}
Example #19
0
static void cloth_continuum_step(ClothModifierData *clmd, float dt)
{
	ClothSimSettings *parms = clmd->sim_parms;
	Cloth *cloth = clmd->clothObject;
	Implicit_Data *data = cloth->implicit;
	int mvert_num = cloth->mvert_num;
	ClothVertex *vert;
	
	const float fluid_factor = 0.95f; /* blend between PIC and FLIP methods */
	float smoothfac = parms->velocity_smooth;
	/* XXX FIXME arbitrary factor!!! this should be based on some intuitive value instead,
	 * like number of hairs per cell and time decay instead of "strength"
	 */
	float density_target = parms->density_target;
	float density_strength = parms->density_strength;
	float gmin[3], gmax[3];
	int i;
	
	/* clear grid info */
	zero_v3_int(clmd->hair_grid_res);
	zero_v3(clmd->hair_grid_min);
	zero_v3(clmd->hair_grid_max);
	clmd->hair_grid_cellsize = 0.0f;
	
	hair_get_boundbox(clmd, gmin, gmax);
	
	/* gather velocities & density */
	if (smoothfac > 0.0f || density_strength > 0.0f) {
		HairGrid *grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax);
		
		cloth_continuum_fill_grid(grid, cloth);
		
		/* main hair continuum solver */
		BPH_hair_volume_solve_divergence(grid, dt, density_target, density_strength);
		
		for (i = 0, vert = cloth->verts; i < mvert_num; i++, vert++) {
			float x[3], v[3], nv[3];
			
			/* calculate volumetric velocity influence */
			BPH_mass_spring_get_position(data, i, x);
			BPH_mass_spring_get_new_velocity(data, i, v);
			
			BPH_hair_volume_grid_velocity(grid, x, v, fluid_factor, nv);
			
			interp_v3_v3v3(nv, v, nv, smoothfac);
			
			/* apply on hair data */
			BPH_mass_spring_set_new_velocity(data, i, nv);
		}
		
		/* store basic grid info in the modifier data */
		BPH_hair_volume_grid_geometry(grid, &clmd->hair_grid_cellsize, clmd->hair_grid_res, clmd->hair_grid_min, clmd->hair_grid_max);
		
#if 0 /* DEBUG hair velocity vector field */
		{
			const int size = 64;
			int i, j;
			float offset[3], a[3], b[3];
			const int axis = 0;
			const float shift = 0.0f;
			
			copy_v3_v3(offset, clmd->hair_grid_min);
			zero_v3(a);
			zero_v3(b);
			
			offset[axis] = shift * clmd->hair_grid_cellsize;
			a[(axis+1) % 3] = clmd->hair_grid_max[(axis+1) % 3] - clmd->hair_grid_min[(axis+1) % 3];
			b[(axis+2) % 3] = clmd->hair_grid_max[(axis+2) % 3] - clmd->hair_grid_min[(axis+2) % 3];
			
			BKE_sim_debug_data_clear_category(clmd->debug_data, "grid velocity");
			for (j = 0; j < size; ++j) {
				for (i = 0; i < size; ++i) {
					float x[3], v[3], gvel[3], gvel_smooth[3], gdensity;
					
					madd_v3_v3v3fl(x, offset, a, (float)i / (float)(size-1));
					madd_v3_v3fl(x, b, (float)j / (float)(size-1));
					zero_v3(v);
					
					BPH_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL);
					
//					BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity, 0.7, 0.3, 1, "grid density", i, j, 3111);
					if (!is_zero_v3(gvel) || !is_zero_v3(gvel_smooth)) {
						float dvel[3];
						sub_v3_v3v3(dvel, gvel_smooth, gvel);
//						BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel, 0.4, 0, 1, "grid velocity", i, j, 3112);
//						BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel_smooth, 0.6, 1, 1, "grid velocity", i, j, 3113);
						BKE_sim_debug_data_add_vector(clmd->debug_data, x, dvel, 0.4, 1, 0.7, "grid velocity", i, j, 3114);
#if 0
						if (gdensity > 0.0f) {
							float col0[3] = {0.0, 0.0, 0.0};
							float col1[3] = {0.0, 1.0, 0.0};
							float col[3];
							
							interp_v3_v3v3(col, col0, col1, CLAMPIS(gdensity * clmd->sim_parms->density_strength, 0.0, 1.0));
//							BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4, "grid velocity", i, j, 3115);
//							BKE_sim_debug_data_add_dot(clmd->debug_data, x, col[0], col[1], col[2], "grid velocity", i, j, 3115);
							BKE_sim_debug_data_add_circle(clmd->debug_data, x, 0.01f, col[0], col[1], col[2], "grid velocity", i, j, 3115);
						}
#endif
					}
				}
			}
		}
#endif
		
		BPH_hair_volume_free_vertex_grid(grid);
	}
}
Example #20
0
BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, float time)
{
	Cloth *cloth = clmd->clothObject;
	ClothSimSettings *parms = clmd->sim_parms;
	Implicit_Data *data = cloth->implicit;
	ClothVertex *verts = cloth->verts;
	
	bool no_compress = parms->flags & CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS;
	
	zero_v3(s->f);
	zero_m3(s->dfdx);
	zero_m3(s->dfdv);
	
	s->flags &= ~CLOTH_SPRING_FLAG_NEEDED;
	
	// calculate force of structural + shear springs
	if ((s->type & CLOTH_SPRING_TYPE_STRUCTURAL) || (s->type & CLOTH_SPRING_TYPE_SHEAR) || (s->type & CLOTH_SPRING_TYPE_SEWING) ) {
#ifdef CLOTH_FORCE_SPRING_STRUCTURAL
		float k, scaling;
		
		s->flags |= CLOTH_SPRING_FLAG_NEEDED;
		
		scaling = parms->structural + s->stiffness * fabsf(parms->max_struct - parms->structural);
		k = scaling / (parms->avg_spring_len + FLT_EPSILON);
		
		if (s->type & CLOTH_SPRING_TYPE_SEWING) {
			// TODO: verify, half verified (couldn't see error)
			// sewing springs usually have a large distance at first so clamp the force so we don't get tunnelling through colission objects
			BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, parms->max_sewing, s->f, s->dfdx, s->dfdv);
		}
		else {
			BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, 0.0f, s->f, s->dfdx, s->dfdv);
		}
#endif
	}
	else if (s->type & CLOTH_SPRING_TYPE_GOAL) {
#ifdef CLOTH_FORCE_SPRING_GOAL
		float goal_x[3], goal_v[3];
		float k, scaling;
		
		s->flags |= CLOTH_SPRING_FLAG_NEEDED;
		
		// current_position = xold + t * (newposition - xold)
		/* divide by time_scale to prevent goal vertices' delta locations from being multiplied */
		interp_v3_v3v3(goal_x, verts[s->ij].xold, verts[s->ij].xconst, time / parms->time_scale);
		sub_v3_v3v3(goal_v, verts[s->ij].xconst, verts[s->ij].xold); // distance covered over dt==1
		
		scaling = parms->goalspring + s->stiffness * fabsf(parms->max_struct - parms->goalspring);
		k = verts[s->ij].goal * scaling / (parms->avg_spring_len + FLT_EPSILON);
		
		BPH_mass_spring_force_spring_goal(data, s->ij, goal_x, goal_v, k, parms->goalfrict * 0.01f, s->f, s->dfdx, s->dfdv);
#endif
	}
	else if (s->type & CLOTH_SPRING_TYPE_BENDING) {  /* calculate force of bending springs */
#ifdef CLOTH_FORCE_SPRING_BEND
		float kb, cb, scaling;
		
		s->flags |= CLOTH_SPRING_FLAG_NEEDED;
		
		scaling = parms->bending + s->stiffness * fabsf(parms->max_bend - parms->bending);
		kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
		
		// Fix for [#45084] for cloth stiffness must have cb proportional to kb
		cb = kb * parms->bending_damping;
		
		BPH_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb, s->f, s->dfdx, s->dfdv);
#endif
	}
	else if (s->type & CLOTH_SPRING_TYPE_BENDING_ANG) {
#ifdef CLOTH_FORCE_SPRING_BEND
		float kb, cb, scaling;
		
		s->flags |= CLOTH_SPRING_FLAG_NEEDED;
		
		/* XXX WARNING: angular bending springs for hair apply stiffness factor as an overall factor, unlike cloth springs!
		 * this is crap, but needed due to cloth/hair mixing ...
		 * max_bend factor is not even used for hair, so ...
		 */
		scaling = s->stiffness * parms->bending;
		kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
		
		// Fix for [#45084] for cloth stiffness must have cb proportional to kb
		cb = kb * parms->bending_damping;
		
		/* XXX assuming same restlen for ij and jk segments here, this can be done correctly for hair later */
		BPH_mass_spring_force_spring_bending_angular(data, s->ij, s->kl, s->mn, s->target, kb, cb);
		
#if 0
		{
			float x_kl[3], x_mn[3], v[3], d[3];
			
			BPH_mass_spring_get_motion_state(data, s->kl, x_kl, v);
			BPH_mass_spring_get_motion_state(data, s->mn, x_mn, v);
			
			BKE_sim_debug_data_add_dot(clmd->debug_data, x_kl, 0.9, 0.9, 0.9, "target", 7980, s->kl);
			BKE_sim_debug_data_add_line(clmd->debug_data, x_kl, x_mn, 0.8, 0.8, 0.8, "target", 7981, s->kl);
			
			copy_v3_v3(d, s->target);
			BKE_sim_debug_data_add_vector(clmd->debug_data, x_kl, d, 0.8, 0.8, 0.2, "target", 7982, s->kl);
			
//			copy_v3_v3(d, s->target_ij);
//			BKE_sim_debug_data_add_vector(clmd->debug_data, x, d, 1, 0.4, 0.4, "target", 7983, s->kl);
		}
#endif
#endif
	}
}
Example #21
0
/* simple deform modifier */
static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, struct Object *ob, struct DerivedMesh *dm,
                                    float (*vertexCos)[3], int numVerts)
{
	static const float lock_axis[2] = {0.0f, 0.0f};

	int i;
	int limit_axis = 0;
	float smd_limit[2], smd_factor;
	SpaceTransform *transf = NULL, tmp_transf;
	void (*simpleDeform_callback)(const float factor, const float dcut[3], float co[3]) = NULL;  /* Mode callback */
	int vgroup;
	MDeformVert *dvert;

	/* Safe-check */
	if (smd->origin == ob) smd->origin = NULL;  /* No self references */

	if (smd->limit[0] < 0.0f) smd->limit[0] = 0.0f;
	if (smd->limit[0] > 1.0f) smd->limit[0] = 1.0f;

	smd->limit[0] = min_ff(smd->limit[0], smd->limit[1]);  /* Upper limit >= than lower limit */

	/* Calculate matrixs do convert between coordinate spaces */
	if (smd->origin) {
		transf = &tmp_transf;

		if (smd->originOpts & MOD_SIMPLEDEFORM_ORIGIN_LOCAL) {
			space_transform_from_matrixs(transf, ob->obmat, smd->origin->obmat);
		}
		else {
			copy_m4_m4(transf->local2target, smd->origin->obmat);
			invert_m4_m4(transf->target2local, transf->local2target);
		}
	}

	/* Setup vars,
	 * Bend limits on X.. all other modes limit on Z */
	limit_axis  = (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) ? 0 : 2;

	/* Update limits if needed */
	{
		float lower =  FLT_MAX;
		float upper = -FLT_MAX;

		for (i = 0; i < numVerts; i++) {
			float tmp[3];
			copy_v3_v3(tmp, vertexCos[i]);

			if (transf) space_transform_apply(transf, tmp);

			lower = min_ff(lower, tmp[limit_axis]);
			upper = max_ff(upper, tmp[limit_axis]);
		}


		/* SMD values are normalized to the BV, calculate the absolut values */
		smd_limit[1] = lower + (upper - lower) * smd->limit[1];
		smd_limit[0] = lower + (upper - lower) * smd->limit[0];

		smd_factor   = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]);
	}

	modifier_get_vgroup(ob, dm, smd->vgroup_name, &dvert, &vgroup);

	switch (smd->mode) {
		case MOD_SIMPLEDEFORM_MODE_TWIST:   simpleDeform_callback = simpleDeform_twist;     break;
		case MOD_SIMPLEDEFORM_MODE_BEND:    simpleDeform_callback = simpleDeform_bend;      break;
		case MOD_SIMPLEDEFORM_MODE_TAPER:   simpleDeform_callback = simpleDeform_taper;     break;
		case MOD_SIMPLEDEFORM_MODE_STRETCH: simpleDeform_callback = simpleDeform_stretch;   break;
		default:
			return; /* No simpledeform mode? */
	}

	for (i = 0; i < numVerts; i++) {
		float weight = defvert_array_find_weight_safe(dvert, i, vgroup);

		if (weight != 0.0f) {
			float co[3], dcut[3] = {0.0f, 0.0f, 0.0f};

			if (transf) {
				space_transform_apply(transf, vertexCos[i]);
			}

			copy_v3_v3(co, vertexCos[i]);

			/* Apply axis limits */
			if (smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) { /* Bend mode shoulnt have any lock axis */
				if (smd->axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) axis_limit(0, lock_axis, co, dcut);
				if (smd->axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) axis_limit(1, lock_axis, co, dcut);
			}
			axis_limit(limit_axis, smd_limit, co, dcut);

			simpleDeform_callback(smd_factor, dcut, co);  /* apply deform */
			interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight);  /* Use vertex weight has coef of linear interpolation */

			if (transf) {
				space_transform_invert(transf, vertexCos[i]);
			}
		}
	}
}
Example #22
0
/* simple deform modifier */
static void SimpleDeformModifier_do(
        SimpleDeformModifierData *smd, struct Object *ob, struct DerivedMesh *dm,
        float (*vertexCos)[3], int numVerts)
{
	const float base_limit[2] = {0.0f, 0.0f};

	int i;
	float smd_limit[2], smd_factor;
	SpaceTransform *transf = NULL, tmp_transf;
	void (*simpleDeform_callback)(const float factor, const int axis, const float dcut[3], float co[3]) = NULL;  /* Mode callback */
	int vgroup;
	MDeformVert *dvert;

	/* This is historically the lock axis, _not_ the deform axis as the name would imply */
	const int deform_axis = smd->deform_axis;
	int lock_axis = smd->axis;
	if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) { /* Bend mode shouldn't have any lock axis */
		lock_axis = 0;
	}
	else {
		/* Don't lock axis if it is the chosen deform axis, as this flattens
		 * the geometry */
		if (deform_axis == 0) {
			lock_axis &= ~MOD_SIMPLEDEFORM_LOCK_AXIS_X;
		}
		if (deform_axis == 1) {
			lock_axis &= ~MOD_SIMPLEDEFORM_LOCK_AXIS_Y;
		}
		if (deform_axis == 2) {
			lock_axis &= ~MOD_SIMPLEDEFORM_LOCK_AXIS_Z;
		}
	}


	/* Safe-check */
	if (smd->origin == ob) smd->origin = NULL;  /* No self references */

	if (smd->limit[0] < 0.0f) smd->limit[0] = 0.0f;
	if (smd->limit[0] > 1.0f) smd->limit[0] = 1.0f;

	smd->limit[0] = min_ff(smd->limit[0], smd->limit[1]);  /* Upper limit >= than lower limit */

	/* Calculate matrixs do convert between coordinate spaces */
	if (smd->origin) {
		transf = &tmp_transf;
		BLI_SPACE_TRANSFORM_SETUP(transf, ob, smd->origin);
	}

	/* Update limits if needed */
	int limit_axis = deform_axis;
	if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) {
		/* Bend is a special case. */
		switch (deform_axis) {
			case 0:
				ATTR_FALLTHROUGH;
			case 1:
				limit_axis = 2;
				break;
			default:
				limit_axis = 0;
		}
	}

	{
		float lower =  FLT_MAX;
		float upper = -FLT_MAX;

		for (i = 0; i < numVerts; i++) {
			float tmp[3];
			copy_v3_v3(tmp, vertexCos[i]);

			if (transf) {
				BLI_space_transform_apply(transf, tmp);
			}

			lower = min_ff(lower, tmp[limit_axis]);
			upper = max_ff(upper, tmp[limit_axis]);
		}


		/* SMD values are normalized to the BV, calculate the absolute values */
		smd_limit[1] = lower + (upper - lower) * smd->limit[1];
		smd_limit[0] = lower + (upper - lower) * smd->limit[0];

		smd_factor   = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]);
	}

	switch (smd->mode) {
		case MOD_SIMPLEDEFORM_MODE_TWIST:   simpleDeform_callback = simpleDeform_twist;     break;
		case MOD_SIMPLEDEFORM_MODE_BEND:    simpleDeform_callback = simpleDeform_bend;      break;
		case MOD_SIMPLEDEFORM_MODE_TAPER:   simpleDeform_callback = simpleDeform_taper;     break;
		case MOD_SIMPLEDEFORM_MODE_STRETCH: simpleDeform_callback = simpleDeform_stretch;   break;
		default:
			return; /* No simpledeform mode? */
	}

	if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) {
		if (fabsf(smd_factor) < BEND_EPS) {
			return;
		}
	}

	modifier_get_vgroup(ob, dm, smd->vgroup_name, &dvert, &vgroup);
	const bool invert_vgroup = (smd->flag & MOD_SIMPLEDEFORM_FLAG_INVERT_VGROUP) != 0;
	const uint *axis_map = axis_map_table[(smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) ? deform_axis : 2];

	for (i = 0; i < numVerts; i++) {
		float weight = defvert_array_find_weight_safe(dvert, i, vgroup);

		if (invert_vgroup) {
			weight = 1.0f - weight;
		}

		if (weight != 0.0f) {
			float co[3], dcut[3] = {0.0f, 0.0f, 0.0f};

			if (transf) {
				BLI_space_transform_apply(transf, vertexCos[i]);
			}

			copy_v3_v3(co, vertexCos[i]);

			/* Apply axis limits, and axis mappings */
			if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) {
				axis_limit(0, base_limit, co, dcut);
			}
			if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) {
				axis_limit(1, base_limit, co, dcut);
			}
			if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) {
				axis_limit(2, base_limit, co, dcut);
			}
			axis_limit(limit_axis, smd_limit, co, dcut);

			/* apply the deform to a mapped copy of the vertex, and then re-map it back. */
			float co_remap[3];
			float dcut_remap[3];
			copy_v3_v3_map(co_remap, co, axis_map);
			copy_v3_v3_map(dcut_remap, dcut, axis_map);
			simpleDeform_callback(smd_factor, deform_axis, dcut_remap, co_remap);  /* apply deform */
			copy_v3_v3_unmap(co, co_remap, axis_map);

			interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight);  /* Use vertex weight has coef of linear interpolation */

			if (transf) {
				BLI_space_transform_invert(transf, vertexCos[i]);
			}
		}
	}
}
Example #23
0
/*
 * Shrinkwrap moving vertexs to the nearest surface point on the target
 *
 * it builds a BVHTree from the target mesh and then performs a
 * NN matches for each vertex
 */
static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc)
{
	int i;

	BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh;
	BVHTreeNearest nearest  = NULL_BVHTreeNearest;

	/* Create a bvh-tree of the given target */
	bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 2, 6);
	if (treeData.tree == NULL) {
		OUT_OF_MEMORY();
		return;
	}

	/* Setup nearest */
	nearest.index = -1;
	nearest.dist_sq = FLT_MAX;


	/* Find the nearest vertex */
#ifndef __APPLE__
#pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(calc, treeData) schedule(static)
#endif
	for (i = 0; i < calc->numVerts; ++i) {
		float *co = calc->vertexCos[i];
		float tmp_co[3];
		float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);
		if (weight == 0.0f) continue;

		/* Convert the vertex to tree coordinates */
		if (calc->vert) {
			copy_v3_v3(tmp_co, calc->vert[i].co);
		}
		else {
			copy_v3_v3(tmp_co, co);
		}
		space_transform_apply(&calc->local2target, tmp_co);

		/* Use local proximity heuristics (to reduce the nearest search)
		 *
		 * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex
		 * so we can initiate the "nearest.dist" with the expected value to that last hit.
		 * This will lead in pruning of the search tree. */
		if (nearest.index != -1)
			nearest.dist_sq = len_squared_v3v3(tmp_co, nearest.co);
		else
			nearest.dist_sq = FLT_MAX;

		BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData);

		/* Found the nearest vertex */
		if (nearest.index != -1) {
			if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) {
				/* Make the vertex stay on the front side of the face */
				madd_v3_v3v3fl(tmp_co, nearest.co, nearest.no, calc->keepDist);
			}
			else {
				/* Adjusting the vertex weight,
				 * so that after interpolating it keeps a certain distance from the nearest position */
				const float dist = sasqrt(nearest.dist_sq);
				if (dist > FLT_EPSILON) {
					/* linear interpolation */
					interp_v3_v3v3(tmp_co, tmp_co, nearest.co, (dist - calc->keepDist) / dist);
				}
				else {
					copy_v3_v3(tmp_co, nearest.co);
				}
			}

			/* Convert the coordinates back to mesh coordinates */
			space_transform_invert(&calc->local2target, tmp_co);
			interp_v3_v3v3(co, co, tmp_co, weight);  /* linear interpolation */
		}
	}

	free_bvhtree_from_mesh(&treeData);
}
Example #24
0
static void warpModifier_do(WarpModifierData *wmd,
                            const ModifierEvalContext *ctx,
                            Mesh *mesh,
                            float (*vertexCos)[3],
                            int numVerts)
{
  Object *ob = ctx->object;
  float obinv[4][4];
  float mat_from[4][4];
  float mat_from_inv[4][4];
  float mat_to[4][4];
  float mat_unit[4][4];
  float mat_final[4][4];

  float tmat[4][4];

  const float falloff_radius_sq = SQUARE(wmd->falloff_radius);
  float strength = wmd->strength;
  float fac = 1.0f, weight;
  int i;
  int defgrp_index;
  MDeformVert *dvert, *dv = NULL;

  float(*tex_co)[3] = NULL;

  if (!(wmd->object_from && wmd->object_to)) {
    return;
  }

  MOD_get_vgroup(ob, mesh, wmd->defgrp_name, &dvert, &defgrp_index);
  if (dvert == NULL) {
    defgrp_index = -1;
  }

  if (wmd->curfalloff == NULL) { /* should never happen, but bad lib linking could cause it */
    wmd->curfalloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
  }

  if (wmd->curfalloff) {
    curvemapping_initialize(wmd->curfalloff);
  }

  invert_m4_m4(obinv, ob->obmat);

  mul_m4_m4m4(mat_from, obinv, wmd->object_from->obmat);
  mul_m4_m4m4(mat_to, obinv, wmd->object_to->obmat);

  invert_m4_m4(tmat, mat_from);  // swap?
  mul_m4_m4m4(mat_final, tmat, mat_to);

  invert_m4_m4(mat_from_inv, mat_from);

  unit_m4(mat_unit);

  if (strength < 0.0f) {
    float loc[3];
    strength = -strength;

    /* inverted location is not useful, just use the negative */
    copy_v3_v3(loc, mat_final[3]);
    invert_m4(mat_final);
    negate_v3_v3(mat_final[3], loc);
  }
  weight = strength;

  Tex *tex_target = wmd->texture;
  if (mesh != NULL && tex_target != NULL) {
    tex_co = MEM_malloc_arrayN(numVerts, sizeof(*tex_co), "warpModifier_do tex_co");
    MOD_get_texture_coords((MappingInfoModifierData *)wmd, ctx, ob, mesh, vertexCos, tex_co);

    MOD_init_texture((MappingInfoModifierData *)wmd, ctx);
  }

  for (i = 0; i < numVerts; i++) {
    float *co = vertexCos[i];

    if (wmd->falloff_type == eWarp_Falloff_None ||
        ((fac = len_squared_v3v3(co, mat_from[3])) < falloff_radius_sq &&
         (fac = (wmd->falloff_radius - sqrtf(fac)) / wmd->falloff_radius))) {
      /* skip if no vert group found */
      if (defgrp_index != -1) {
        dv = &dvert[i];
        weight = defvert_find_weight(dv, defgrp_index) * strength;
        if (weight <= 0.0f) {
          continue;
        }
      }

      /* closely match PROP_SMOOTH and similar */
      switch (wmd->falloff_type) {
        case eWarp_Falloff_None:
          fac = 1.0f;
          break;
        case eWarp_Falloff_Curve:
          fac = curvemapping_evaluateF(wmd->curfalloff, 0, fac);
          break;
        case eWarp_Falloff_Sharp:
          fac = fac * fac;
          break;
        case eWarp_Falloff_Smooth:
          fac = 3.0f * fac * fac - 2.0f * fac * fac * fac;
          break;
        case eWarp_Falloff_Root:
          fac = sqrtf(fac);
          break;
        case eWarp_Falloff_Linear:
          /* pass */
          break;
        case eWarp_Falloff_Const:
          fac = 1.0f;
          break;
        case eWarp_Falloff_Sphere:
          fac = sqrtf(2 * fac - fac * fac);
          break;
        case eWarp_Falloff_InvSquare:
          fac = fac * (2.0f - fac);
          break;
      }

      fac *= weight;

      if (tex_co) {
        struct Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
        TexResult texres;
        texres.nor = NULL;
        BKE_texture_get_value(scene, tex_target, tex_co[i], &texres, false);
        fac *= texres.tin;
      }

      if (fac != 0.0f) {
        /* into the 'from' objects space */
        mul_m4_v3(mat_from_inv, co);

        if (fac == 1.0f) {
          mul_m4_v3(mat_final, co);
        }
        else {
          if (wmd->flag & MOD_WARP_VOLUME_PRESERVE) {
            /* interpolate the matrix for nicer locations */
            blend_m4_m4m4(tmat, mat_unit, mat_final, fac);
            mul_m4_v3(tmat, co);
          }
          else {
            float tvec[3];
            mul_v3_m4v3(tvec, mat_final, co);
            interp_v3_v3v3(co, co, tvec, fac);
          }
        }

        /* out of the 'from' objects space */
        mul_m4_v3(mat_from, co);
      }
    }
  }

  if (tex_co) {
    MEM_freeN(tex_co);
  }
}
Example #25
0
static void deformVerts(ModifierData *md, Object *ob,
						DerivedMesh *dm,
						float (*vertexCos)[3],
						int numVerts,
						int UNUSED(useRenderParams),
						int UNUSED(isFinalCalc))
{
	HookModifierData *hmd = (HookModifierData*) md;
	bPoseChannel *pchan= get_pose_channel(hmd->object->pose, hmd->subtarget);
	float vec[3], mat[4][4], dmat[4][4];
	int i, *index_pt;
	const float falloff_squared= hmd->falloff * hmd->falloff; /* for faster comparisons */
	
	int max_dvert= 0;
	MDeformVert *dvert= NULL;
	int defgrp_index = -1;
	
	/* get world-space matrix of target, corrected for the space the verts are in */
	if (hmd->subtarget[0] && pchan) {
		/* bone target if there's a matching pose-channel */
		mul_m4_m4m4(dmat, pchan->pose_mat, hmd->object->obmat);
	}
	else {
		/* just object target */
		copy_m4_m4(dmat, hmd->object->obmat);
	}
	invert_m4_m4(ob->imat, ob->obmat);
	mul_serie_m4(mat, ob->imat, dmat, hmd->parentinv,
			 NULL, NULL, NULL, NULL, NULL);

	if((defgrp_index= defgroup_name_index(ob, hmd->name)) != -1) {
		Mesh *me = ob->data;
		if(dm) {
			dvert= dm->getVertDataArray(dm, CD_MDEFORMVERT);
			if(dvert) {
				max_dvert = numVerts;
			}
		}
		else if(me->dvert) {
			dvert= me->dvert;
			if(dvert) {
				max_dvert = me->totvert;
			}
		}
	}

	/* Regarding index range checking below.
	 *
	 * This should always be true and I don't generally like 
	 * "paranoid" style code like this, but old files can have
	 * indices that are out of range because old blender did
	 * not correct them on exit editmode. - zr
	 */
	
	if(hmd->force == 0.0f) {
		/* do nothing, avoid annoying checks in the loop */
	}
	else if(hmd->indexar) { /* vertex indices? */
		const float fac_orig= hmd->force;
		float fac;
		const int *origindex_ar;

		/* if DerivedMesh is present and has original index data,
		* use it
		*/
		if(dm && (origindex_ar= dm->getVertDataArray(dm, CD_ORIGINDEX))) {
			for(i= 0, index_pt= hmd->indexar; i < hmd->totindex; i++, index_pt++) {
				if(*index_pt < numVerts) {
					int j;

					for(j = 0; j < numVerts; j++) {
						if(origindex_ar[j] == *index_pt) {
							float *co = vertexCos[j];
							if((fac= hook_falloff(hmd->cent, co, falloff_squared, fac_orig))) {
								if(dvert)
									fac *= defvert_find_weight(dvert+j, defgrp_index);

								if(fac) {
									mul_v3_m4v3(vec, mat, co);
									interp_v3_v3v3(co, co, vec, fac);
								}
							}
						}
					}
				}
			}
		}
		else { /* missing dm or ORIGINDEX */
			for(i= 0, index_pt= hmd->indexar; i < hmd->totindex; i++, index_pt++) {
				if(*index_pt < numVerts) {
					float *co = vertexCos[*index_pt];
					if((fac= hook_falloff(hmd->cent, co, falloff_squared, fac_orig))) {
						if(dvert)
							fac *= defvert_find_weight(dvert+(*index_pt), defgrp_index);

						if(fac) {
							mul_v3_m4v3(vec, mat, co);
							interp_v3_v3v3(co, co, vec, fac);
						}
					}
				}
			}
		}
	}
	else if(dvert) {	/* vertex group hook */
		int i;
		const float fac_orig= hmd->force;

		for(i = 0; i < max_dvert; i++, dvert++) {
			float fac;
			float *co = vertexCos[i];

			if((fac= hook_falloff(hmd->cent, co, falloff_squared, fac_orig))) {
				fac *= defvert_find_weight(dvert, defgrp_index);
				if(fac) {
					mul_v3_m4v3(vec, mat, co);
					interp_v3_v3v3(co, co, vec, fac);
				}
			}
		}
	}
}
static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu,
                                float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point,
                                const bool add_end_point, tGpTimingData *gtd)
{
	bGPDspoint *pt;
	Nurb *nu = (curnu) ? *curnu : NULL;
	BezTriple *bezt, *prev_bezt = NULL;
	int i, tot, old_nbezt = 0;
	const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0);
	float p3d_cur[3], p3d_prev[3], p3d_next[3], h1[3], h2[3];
	const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE);
	
	/* create new 'nurb' or extend current one within the curve */
	if (nu) {
		old_nbezt = nu->pntsu;
		/* If we do stitch, first point of current stroke is assumed the same as last point of previous stroke,
		 * so no need to add it.
		 * If no stitch, we want to add two additional points to make a "zero-radius" link between both strokes.
		 */
		BKE_nurb_bezierPoints_add(nu, gps->totpoints + ((stitch) ? -1 : 2) + add_start_end_points);
	}
	else {
		nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)");
		
		nu->pntsu = gps->totpoints + add_start_end_points;
		nu->resolu = 12;
		nu->resolv = 12;
		nu->type = CU_BEZIER;
		nu->bezt = (BezTriple *)MEM_callocN(sizeof(BezTriple) * nu->pntsu, "bezts");
		
		stitch = false; /* Security! */
	}
	
	if (do_gtd) {
		gp_timing_data_set_nbr(gtd, nu->pntsu);
	}
	
	tot = gps->totpoints;
	
	/* get initial coordinates */
	pt = gps->points;
	if (tot) {
		gp_strokepoint_convertcoords(C, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect);
		if (tot > 1) {
			gp_strokepoint_convertcoords(C, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect);
		}
		if (stitch && tot > 2) {
			gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect);
		}
	}
	
	/* If needed, make the link between both strokes with two zero-radius additional points */
	if (curnu && old_nbezt) {
		BLI_assert(gps->prev != NULL);
		
		/* Update last point's second handle */
		if (stitch) {
			bezt = &nu->bezt[old_nbezt - 1];
			interp_v3_v3v3(h2, bezt->vec[1], p3d_cur, BEZT_HANDLE_FAC);
			copy_v3_v3(bezt->vec[2], h2);
			pt++;
		}
		
		/* Create "link points" */
		/* About "zero-radius" point interpolations:
		 * - If we have at least two points in current curve (most common case), we linearly extrapolate
		 *   the last segment to get the first point (p1) position and timing.
		 * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point
		 *   with the first point of the current stroke.
		 * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated
		 * if it exists, else (if the stroke is a single point), linear interpolation with last curve point...
		 */
		else {
			float p1[3], p2[3];
			float dt1 = 0.0f, dt2 = 0.0f;
			
			prev_bezt = NULL;
			if ((old_nbezt > 1) && (gps->prev->totpoints > 1)) {
				/* Only use last curve segment if previous stroke was not a single-point one! */
				prev_bezt = &nu->bezt[old_nbezt - 2];
			}
			bezt = &nu->bezt[old_nbezt - 1];
			
			/* First point */
			if (prev_bezt) {
				interp_v3_v3v3(p1, prev_bezt->vec[1], bezt->vec[1], 1.0f + GAP_DFAC);
				if (do_gtd) {
					const int idx = gps->prev->totpoints - 1;
					dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC);
				}
			}
			else {
				interp_v3_v3v3(p1, bezt->vec[1], p3d_cur, GAP_DFAC);
				if (do_gtd) {
					dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC);
				}
			}
			
			/* Second point */
			/* Note dt2 is always negative, which marks the gap. */
			if (tot > 1) {
				interp_v3_v3v3(p2, p3d_cur, p3d_next, -GAP_DFAC);
				if (do_gtd) {
					dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
				}
			}
			else {
				interp_v3_v3v3(p2, p3d_cur, bezt->vec[1], GAP_DFAC);
				if (do_gtd) {
					dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC);
				}
			}
			
			/* Second handle of last point of previous stroke. */
			interp_v3_v3v3(h2, bezt->vec[1], p1, BEZT_HANDLE_FAC);
			copy_v3_v3(bezt->vec[2], h2);
			
			/* First point */
			interp_v3_v3v3(h1, p1, bezt->vec[1], BEZT_HANDLE_FAC);
			interp_v3_v3v3(h2, p1, p2, BEZT_HANDLE_FAC);
			bezt++;
			gp_stroke_to_bezier_add_point(gtd, bezt, p1, h1, h2, (bezt - 1)->vec[1], do_gtd, gps->prev->inittime, dt1,
			                              0.0f, rad_fac, minmax_weights);
			
			/* Second point */
			interp_v3_v3v3(h1, p2, p1, BEZT_HANDLE_FAC);
			interp_v3_v3v3(h2, p2, p3d_cur, BEZT_HANDLE_FAC);
			bezt++;
			gp_stroke_to_bezier_add_point(gtd, bezt, p2, h1, h2, p1, do_gtd, gps->inittime, dt2,
			                              0.0f, rad_fac, minmax_weights);
			
			old_nbezt += 2;
			copy_v3_v3(p3d_prev, p2);
		}
	}
	else if (add_start_point) {
		float p[3];
		float dt = 0.0f;
		
		if (gps->totpoints > 1) {
			interp_v3_v3v3(p, p3d_cur, p3d_next, -GAP_DFAC);
			if (do_gtd) {
				dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
			}
		}
		else {
			copy_v3_v3(p, p3d_cur);
			p[0] -= GAP_DFAC;  /* Rather arbitrary... */
			dt = -GAP_DFAC;  /* Rather arbitrary too! */
		}
		interp_v3_v3v3(h1, p, p3d_cur, -BEZT_HANDLE_FAC);
		interp_v3_v3v3(h2, p, p3d_cur, BEZT_HANDLE_FAC);
		bezt = &nu->bezt[old_nbezt];
		gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, p, do_gtd, gps->inittime, dt,
		                              0.0f, rad_fac, minmax_weights);
		
		old_nbezt++;
		copy_v3_v3(p3d_prev, p);
	}
	
	if (old_nbezt) {
		prev_bezt = &nu->bezt[old_nbezt - 1];
	}
	
	/* add points */
	for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) {
		float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC;
		
		if (i || old_nbezt) {
			interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC);
		}
		else {
			interp_v3_v3v3(h1, p3d_cur, p3d_next, -BEZT_HANDLE_FAC);
		}
		
		if (i < tot - 1) {
			interp_v3_v3v3(h2, p3d_cur, p3d_next, BEZT_HANDLE_FAC);
		}
		else {
			interp_v3_v3v3(h2, p3d_cur, p3d_prev, -BEZT_HANDLE_FAC);
		}
		
		gp_stroke_to_bezier_add_point(gtd, bezt, p3d_cur, h1, h2, prev_bezt ? prev_bezt->vec[1] : p3d_cur,
		                              do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights);
		
		/* shift coord vects */
		copy_v3_v3(p3d_prev, p3d_cur);
		copy_v3_v3(p3d_cur, p3d_next);
		
		if (i + 2 < tot) {
			gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect);
		}
		
		prev_bezt = bezt;
	}

	if (add_end_point) {
		float p[3];
		float dt = 0.0f;
		
		if (gps->totpoints > 1) {
			interp_v3_v3v3(p, prev_bezt->vec[1], (prev_bezt - 1)->vec[1], -GAP_DFAC);
			if (do_gtd) {
				const int idx = gps->totpoints - 1;
				dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC);
			}
		}
		else {
			copy_v3_v3(p, prev_bezt->vec[1]);
			p[0] += GAP_DFAC;  /* Rather arbitrary... */
			dt = GAP_DFAC;  /* Rather arbitrary too! */
		}
		
		/* Second handle of last point of this stroke. */
		interp_v3_v3v3(h2, prev_bezt->vec[1], p, BEZT_HANDLE_FAC);
		copy_v3_v3(prev_bezt->vec[2], h2);
		
		/* The end point */
		interp_v3_v3v3(h1, p, prev_bezt->vec[1], BEZT_HANDLE_FAC);
		interp_v3_v3v3(h2, p, prev_bezt->vec[1], -BEZT_HANDLE_FAC);
		/* Note bezt has already been incremented in main loop above, so it points to the right place. */
		gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, prev_bezt->vec[1], do_gtd, gps->inittime, dt,
		                              0.0f, rad_fac, minmax_weights);
	}
	
	/* must calculate handles or else we crash */
	BKE_nurb_handles_calc(nu);
	
	if (!curnu || !*curnu) {
		BLI_addtail(&cu->nurb, nu);
	}
	if (curnu) {
		*curnu = nu;
	}
}
Example #27
0
static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for_render)
{
	int i;

	/* Options about projection direction */
	const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit;
	float proj_axis[3]      = {0.0f, 0.0f, 0.0f};

	/* Raycast and tree stuff */

	/** \note 'hit.dist' is kept in the targets space, this is only used
	 * for finding the best hit, to get the real dist,
	 * measure the len_v3v3() from the input coord to hit.co */
	BVHTreeRayHit hit;
	BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh;

	/* auxiliary target */
	DerivedMesh *auxMesh    = NULL;
	BVHTreeFromMesh auxData = NULL_BVHTreeFromMesh;
	SpaceTransform local2aux;

	/* If the user doesn't allows to project in any direction of projection axis
	 * then theres nothing todo. */
	if ((calc->smd->shrinkOpts & (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 0)
		return;


	/* Prepare data to retrieve the direction in which we should project each vertex */
	if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) {
		if (calc->vert == NULL) return;
	}
	else {
		/* The code supports any axis that is a combination of X,Y,Z
		 * although currently UI only allows to set the 3 different axis */
		if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) proj_axis[0] = 1.0f;
		if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) proj_axis[1] = 1.0f;
		if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) proj_axis[2] = 1.0f;

		normalize_v3(proj_axis);

		/* Invalid projection direction */
		if (len_squared_v3(proj_axis) < FLT_EPSILON) {
			return;
		}
	}

	if (calc->smd->auxTarget) {
		auxMesh = object_get_derived_final(calc->smd->auxTarget, for_render);
		if (!auxMesh)
			return;
		SPACE_TRANSFORM_SETUP(&local2aux, calc->ob, calc->smd->auxTarget);
	}

	/* After sucessufuly build the trees, start projection vertexs */
	if (bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 4, 6) &&
	    (auxMesh == NULL || bvhtree_from_mesh_faces(&auxData, auxMesh, 0.0, 4, 6)))
	{

#ifndef __APPLE__
#pragma omp parallel for private(i, hit) schedule(static)
#endif
		for (i = 0; i < calc->numVerts; ++i) {
			float *co = calc->vertexCos[i];
			float tmp_co[3], tmp_no[3];
			const float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);

			if (weight == 0.0f) {
				continue;
			}

			if (calc->vert) {
				/* calc->vert contains verts from derivedMesh  */
				/* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */
				/* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */
				if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) {
					copy_v3_v3(tmp_co, calc->vert[i].co);
					normal_short_to_float_v3(tmp_no, calc->vert[i].no);
				}
				else {
					copy_v3_v3(tmp_co, co);
					copy_v3_v3(tmp_no, proj_axis);
				}
			}
			else {
				copy_v3_v3(tmp_co, co);
				copy_v3_v3(tmp_no, proj_axis);
			}


			hit.index = -1;
			hit.dist = 10000.0f; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */

			/* Project over positive direction of axis */
			if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) {

				if (auxData.tree) {
					BKE_shrinkwrap_project_normal(0, tmp_co, tmp_no,
					                              &local2aux, auxData.tree, &hit,
					                              auxData.raycast_callback, &auxData);
				}

				BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, tmp_no,
				                              &calc->local2target, treeData.tree, &hit,
				                              treeData.raycast_callback, &treeData);
			}

			/* Project over negative direction of axis */
			if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) {
				float inv_no[3];
				negate_v3_v3(inv_no, tmp_no);

				if (auxData.tree) {
					BKE_shrinkwrap_project_normal(0, tmp_co, inv_no,
					                              &local2aux, auxData.tree, &hit,
					                              auxData.raycast_callback, &auxData);
				}

				BKE_shrinkwrap_project_normal(calc->smd->shrinkOpts, tmp_co, inv_no,
				                              &calc->local2target, treeData.tree, &hit,
				                              treeData.raycast_callback, &treeData);
			}

			/* don't set the initial dist (which is more efficient),
			 * because its calculated in the targets space, we want the dist in our own space */
			if (proj_limit_squared != 0.0f) {
				if (len_squared_v3v3(hit.co, co) > proj_limit_squared) {
					hit.index = -1;
				}
			}

			if (hit.index != -1) {
				madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist);
				interp_v3_v3v3(co, co, hit.co, weight);
			}
		}
	}

	/* free data structures */
	free_bvhtree_from_mesh(&treeData);
	free_bvhtree_from_mesh(&auxData);
}
Example #28
0
static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
	bool do_draw = false;
	int exit_code = OPERATOR_RUNNING_MODAL;
	RulerInfo *ruler_info = op->customdata;
	ScrArea *sa = ruler_info->sa;
	ARegion *ar = ruler_info->ar;
	RegionView3D *rv3d = ar->regiondata;

	/* its possible to change  spaces while running the operator [#34894] */
	if (UNLIKELY(ar != CTX_wm_region(C))) {
		exit_code = OPERATOR_FINISHED;
		goto exit;
	}

	switch (event->type) {
		case LEFTMOUSE:
			if (event->val == KM_RELEASE) {
				if (ruler_info->state == RULER_STATE_DRAG) {
					/* rubber-band angle removal */
					RulerItem *ruler_item = ruler_item_active_get(ruler_info);
					if (ruler_item && (ruler_item->co_index == 1) && (ruler_item->flag & RULERITEM_USE_ANGLE)) {
						if (!BLI_rcti_isect_pt_v(&ar->winrct, &event->x)) {
							ruler_item->flag &= ~RULERITEM_USE_ANGLE;
							do_draw = true;
						}
					}
					if (ruler_info->snap_flag & RULER_SNAP_OK) {
						ruler_info->snap_flag &= ~RULER_SNAP_OK;
						do_draw = true;
					}
					ruler_info->state = RULER_STATE_NORMAL;
				}
			}
			else {
				if (ruler_info->state == RULER_STATE_NORMAL) {

					if (event->ctrl ||
					    /* weak - but user friendly */
					    BLI_listbase_is_empty(&ruler_info->items))
					{
						View3D *v3d = CTX_wm_view3d(C);
						const bool use_depth = (v3d->drawtype >= OB_SOLID);

						/* Create new line */
						RulerItem *ruler_item_prev = ruler_item_active_get(ruler_info);
						RulerItem *ruler_item;
						/* check if we want to drag an existing point or add a new one */
						ruler_info->state = RULER_STATE_DRAG;

						ruler_item = ruler_item_add(ruler_info);
						ruler_item_active_set(ruler_info, ruler_item);

						if (use_depth) {
							/* snap the first point added, not essential but handy */
							ruler_item->co_index = 0;
							view3d_ruler_item_mousemove(C, ruler_info, event->mval, false, true);
							copy_v3_v3(ruler_info->drag_start_co, ruler_item->co[ruler_item->co_index]);
						}
						else {
							/* initial depth either previous ruler, view offset */
							if (ruler_item_prev) {
								copy_v3_v3(ruler_info->drag_start_co, ruler_item_prev->co[ruler_item_prev->co_index]);
							}
							else {
								negate_v3_v3(ruler_info->drag_start_co, rv3d->ofs);
							}

							copy_v3_v3(ruler_item->co[0], ruler_info->drag_start_co);
							view3d_ruler_item_project(ruler_info, ruler_item->co[0], event->mval);
						}

						copy_v3_v3(ruler_item->co[2], ruler_item->co[0]);
						ruler_item->co_index = 2;

						do_draw = true;
					}
					else {
						float mval_fl[2] = {UNPACK2(event->mval)};
						RulerItem *ruler_item_pick;
						int co_index;

						/* select and drag */
						if (view3d_ruler_pick(ruler_info, mval_fl, &ruler_item_pick, &co_index)) {
							if (co_index == -1) {
								if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
									/* Add Center Point */
									ruler_item_active_set(ruler_info, ruler_item_pick);
									ruler_item_pick->flag |= RULERITEM_USE_ANGLE;
									ruler_item_pick->co_index = 1;
									ruler_info->state = RULER_STATE_DRAG;

									/* find the factor */
									{
										float co_ss[2][2];
										float fac;

										ED_view3d_project_float_global(ar, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP);
										ED_view3d_project_float_global(ar, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP);

										fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]);
										CLAMP(fac, 0.0f, 1.0f);

										interp_v3_v3v3(ruler_item_pick->co[1],
										               ruler_item_pick->co[0],
										               ruler_item_pick->co[2], fac);
									}

									/* update the new location */
									view3d_ruler_item_mousemove(C, ruler_info, event->mval,
									                            event->shift != 0, event->ctrl != 0);
									do_draw = true;
								}
							}
							else {
								ruler_item_active_set(ruler_info, ruler_item_pick);
								ruler_item_pick->co_index = co_index;
								ruler_info->state = RULER_STATE_DRAG;

								/* store the initial depth */
								copy_v3_v3(ruler_info->drag_start_co, ruler_item_pick->co[ruler_item_pick->co_index]);

								do_draw = true;
							}
						}
						else {
							exit_code = OPERATOR_PASS_THROUGH;
						}

					}
				}
			}
			break;
		case CKEY:
		{
			if (event->ctrl) {
				RulerItem *ruler_item = ruler_item_active_get(ruler_info);
				if (ruler_item) {
					const int prec = 8;
					char numstr[256];
					Scene *scene = CTX_data_scene(C);
					UnitSettings *unit = &scene->unit;

					ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
					WM_clipboard_text_set((void *) numstr, false);
				}
			}
			break;
		}
		case RIGHTCTRLKEY:
		case LEFTCTRLKEY:
		{
			WM_event_add_mousemove(C);
			break;
		}
		case MOUSEMOVE:
		{
			if (ruler_info->state == RULER_STATE_DRAG) {
				if (view3d_ruler_item_mousemove(C, ruler_info, event->mval,
				                                event->shift != 0, event->ctrl != 0))
				{
					do_draw = true;
				}
			}
			break;
		}
		case ESCKEY:
		{
			do_draw = true;
			exit_code = OPERATOR_CANCELLED;
			break;
		}
		case RETKEY:
		{
			view3d_ruler_to_gpencil(C, ruler_info);
			do_draw = true;
			exit_code = OPERATOR_FINISHED;
			break;
		}
		case DELKEY:
		{
			if (event->val == KM_PRESS) {
				if (ruler_info->state == RULER_STATE_NORMAL) {
					RulerItem *ruler_item = ruler_item_active_get(ruler_info);
					if (ruler_item) {
						RulerItem *ruler_item_other = ruler_item->prev ? ruler_item->prev : ruler_item->next;
						ruler_item_remove(ruler_info, ruler_item);
						ruler_item_active_set(ruler_info, ruler_item_other);
						do_draw = true;
					}
				}
			}
			break;
		}
		default:
			exit_code = OPERATOR_PASS_THROUGH;
			break;

	}

	if (do_draw) {
		view3d_ruler_header_update(sa);

		/* all 3d views draw rulers */
		WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
	}

exit:
	if (ELEM(exit_code, OPERATOR_FINISHED, OPERATOR_CANCELLED)) {
		WM_cursor_modal_restore(ruler_info->win);

		view3d_ruler_end(C, ruler_info);
		view3d_ruler_free(ruler_info);
		op->customdata = NULL;

		ED_area_headerprint(sa, NULL);
	}

	return exit_code;
}
Example #29
0
/* Evaluate spline IK for a given bone */
static void splineik_evaluate_bone(tSplineIK_Tree *tree, Scene *scene, Object *ob, bPoseChannel *pchan,
                                   int index, float ctime)
{
	bSplineIKConstraint *ikData = tree->ikData;
	float poseHead[3], poseTail[3], poseMat[4][4];
	float splineVec[3], scaleFac, radius = 1.0f;

	/* firstly, calculate the bone matrix the standard way, since this is needed for roll control */
	BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1);

	copy_v3_v3(poseHead, pchan->pose_head);
	copy_v3_v3(poseTail, pchan->pose_tail);

	/* step 1: determine the positions for the endpoints of the bone */
	{
		float vec[4], dir[3], rad;
		float tailBlendFac = 1.0f;

		/* determine if the bone should still be affected by SplineIK */
		if (tree->points[index + 1] >= 1.0f) {
			/* spline doesn't affect the bone anymore, so done... */
			pchan->flag |= POSE_DONE;
			return;
		}
		else if ((tree->points[index] >= 1.0f) && (tree->points[index + 1] < 1.0f)) {
			/* blending factor depends on the amount of the bone still left on the chain */
			tailBlendFac = (1.0f - tree->points[index + 1]) / (tree->points[index] - tree->points[index + 1]);
		}

		/* tail endpoint */
		if (where_on_path(ikData->tar, tree->points[index], vec, dir, NULL, &rad, NULL)) {
			/* apply curve's object-mode transforms to the position
			 * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
			 */
			if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0)
				mul_m4_v3(ikData->tar->obmat, vec);

			/* convert the position to pose-space, then store it */
			mul_m4_v3(ob->imat, vec);
			interp_v3_v3v3(poseTail, pchan->pose_tail, vec, tailBlendFac);

			/* set the new radius */
			radius = rad;
		}

		/* head endpoint */
		if (where_on_path(ikData->tar, tree->points[index + 1], vec, dir, NULL, &rad, NULL)) {
			/* apply curve's object-mode transforms to the position
			 * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
			 */
			if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0)
				mul_m4_v3(ikData->tar->obmat, vec);

			/* store the position, and convert it to pose space */
			mul_m4_v3(ob->imat, vec);
			copy_v3_v3(poseHead, vec);

			/* set the new radius (it should be the average value) */
			radius = (radius + rad) / 2;
		}
	}

	/* step 2: determine the implied transform from these endpoints
	 *     - splineVec: the vector direction that the spline applies on the bone
	 *     - scaleFac: the factor that the bone length is scaled by to get the desired amount
	 */
	sub_v3_v3v3(splineVec, poseTail, poseHead);
	scaleFac = len_v3(splineVec) / pchan->bone->length;

	/* step 3: compute the shortest rotation needed to map from the bone rotation to the current axis
	 *      - this uses the same method as is used for the Damped Track Constraint (see the code there for details)
	 */
	{
		float dmat[3][3], rmat[3][3], tmat[3][3];
		float raxis[3], rangle;

		/* compute the raw rotation matrix from the bone's current matrix by extracting only the
		 * orientation-relevant axes, and normalizing them
		 */
		copy_v3_v3(rmat[0], pchan->pose_mat[0]);
		copy_v3_v3(rmat[1], pchan->pose_mat[1]);
		copy_v3_v3(rmat[2], pchan->pose_mat[2]);
		normalize_m3(rmat);

		/* also, normalize the orientation imposed by the bone, now that we've extracted the scale factor */
		normalize_v3(splineVec);

		/* calculate smallest axis-angle rotation necessary for getting from the
		 * current orientation of the bone, to the spline-imposed direction
		 */
		cross_v3_v3v3(raxis, rmat[1], splineVec);

		rangle = dot_v3v3(rmat[1], splineVec);
		CLAMP(rangle, -1.0f, 1.0f);
		rangle = acosf(rangle);

		/* multiply the magnitude of the angle by the influence of the constraint to
		 * control the influence of the SplineIK effect
		 */
		rangle *= tree->con->enforce;

		/* construct rotation matrix from the axis-angle rotation found above
		 *	- this call takes care to make sure that the axis provided is a unit vector first
		 */
		axis_angle_to_mat3(dmat, raxis, rangle);

		/* combine these rotations so that the y-axis of the bone is now aligned as the spline dictates,
		 * while still maintaining roll control from the existing bone animation
		 */
		mul_m3_m3m3(tmat, dmat, rmat); /* m1, m3, m2 */
		normalize_m3(tmat); /* attempt to reduce shearing, though I doubt this'll really help too much now... */
		copy_m4_m3(poseMat, tmat);
	}

	/* step 4: set the scaling factors for the axes */
	{
		/* only multiply the y-axis by the scaling factor to get nice volume-preservation */
		mul_v3_fl(poseMat[1], scaleFac);

		/* set the scaling factors of the x and z axes from... */
		switch (ikData->xzScaleMode) {
			case CONSTRAINT_SPLINEIK_XZS_ORIGINAL:
			{
				/* original scales get used */
				float scale;

				/* x-axis scale */
				scale = len_v3(pchan->pose_mat[0]);
				mul_v3_fl(poseMat[0], scale);
				/* z-axis scale */
				scale = len_v3(pchan->pose_mat[2]);
				mul_v3_fl(poseMat[2], scale);
				break;
			}
			case CONSTRAINT_SPLINEIK_XZS_INVERSE:
			{
				/* old 'volume preservation' method using the inverse scale */
				float scale;

				/* calculate volume preservation factor which is
				 * basically the inverse of the y-scaling factor
				 */
				if (fabsf(scaleFac) != 0.0f) {
					scale = 1.0f / fabsf(scaleFac);

					/* we need to clamp this within sensible values */
					/* NOTE: these should be fine for now, but should get sanitised in future */
					CLAMP(scale, 0.0001f, 100000.0f);
				}
				else
					scale = 1.0f;

				/* apply the scaling */
				mul_v3_fl(poseMat[0], scale);
				mul_v3_fl(poseMat[2], scale);
				break;
			}
			case CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC:
			{
				/* improved volume preservation based on the Stretch To constraint */
				float final_scale;
				
				/* as the basis for volume preservation, we use the inverse scale factor... */
				if (fabsf(scaleFac) != 0.0f) {
					/* NOTE: The method here is taken wholesale from the Stretch To constraint */
					float bulge = powf(1.0f / fabsf(scaleFac), ikData->bulge);
					
					if (bulge > 1.0f) {
						if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MAX) {
							float bulge_max = max_ff(ikData->bulge_max, 1.0f);
							float hard = min_ff(bulge, bulge_max);
							
							float range = bulge_max - 1.0f;
							float scale = (range > 0.0f) ? 1.0f / range : 0.0f;
							float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (float)M_PI_2;
							
							bulge = interpf(soft, hard, ikData->bulge_smooth);
						}
					}
					if (bulge < 1.0f) {
						if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MIN) {
							float bulge_min = CLAMPIS(ikData->bulge_min, 0.0f, 1.0f);
							float hard = max_ff(bulge, bulge_min);
							
							float range = 1.0f - bulge_min;
							float scale = (range > 0.0f) ? 1.0f / range : 0.0f;
							float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (float)M_PI_2;
							
							bulge = interpf(soft, hard, ikData->bulge_smooth);
						}
					}
					
					/* compute scale factor for xz axes from this value */
					final_scale = sqrtf(bulge);
				}
				else {
					/* no scaling, so scale factor is simple */
					final_scale = 1.0f;
				}
				
				/* apply the scaling (assuming normalised scale) */
				mul_v3_fl(poseMat[0], final_scale);
				mul_v3_fl(poseMat[2], final_scale);
				break;
			}
		}

		/* finally, multiply the x and z scaling by the radius of the curve too,
		 * to allow automatic scales to get tweaked still
		 */
		if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_CURVERAD) == 0) {
			mul_v3_fl(poseMat[0], radius);
			mul_v3_fl(poseMat[2], radius);
		}
	}

	/* step 5: set the location of the bone in the matrix */
	if (ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) {
		/* when the 'no-root' option is affected, the chain can retain
		 * the shape but be moved elsewhere
		 */
		copy_v3_v3(poseHead, pchan->pose_head);
	}
	else if (tree->con->enforce < 1.0f) {
		/* when the influence is too low
		 *	- blend the positions for the 'root' bone
		 *	- stick to the parent for any other
		 */
		if (pchan->parent) {
			copy_v3_v3(poseHead, pchan->pose_head);
		}
		else {
			/* FIXME: this introduces popping artifacts when we reach 0.0 */
			interp_v3_v3v3(poseHead, pchan->pose_head, poseHead, tree->con->enforce);
		}
	}
	copy_v3_v3(poseMat[3], poseHead);

	/* finally, store the new transform */
	copy_m4_m4(pchan->pose_mat, poseMat);
	copy_v3_v3(pchan->pose_head, poseHead);

	/* recalculate tail, as it's now outdated after the head gets adjusted above! */
	BKE_pose_where_is_bone_tail(pchan);

	/* done! */
	pchan->flag |= POSE_DONE;
}
static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu,
                              float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point,
                              const bool add_end_point, tGpTimingData *gtd)
{
	bGPDspoint *pt;
	Nurb *nu = (curnu) ? *curnu : NULL;
	BPoint *bp, *prev_bp = NULL;
	const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE);
	const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0);
	int i, old_nbp = 0;
	
	/* create new 'nurb' or extend current one within the curve */
	if (nu) {
		old_nbp = nu->pntsu;

		/* If stitch, the first point of this stroke is already present in current nu.
		 * Else, we have to add two additional points to make the zero-radius link between strokes.
		 */
		BKE_nurb_points_add(nu, gps->totpoints + (stitch ? -1 : 2) + add_start_end_points);
	}
	else {
		nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)");
		
		nu->pntsu = gps->totpoints + add_start_end_points;
		nu->pntsv = 1;
		nu->orderu = 2; /* point-to-point! */
		nu->type = CU_NURBS;
		nu->flagu = CU_NURB_ENDPOINT;
		nu->resolu = cu->resolu;
		nu->resolv = cu->resolv;
		nu->knotsu = NULL;
		
		nu->bp = (BPoint *)MEM_callocN(sizeof(BPoint) * nu->pntsu, "bpoints");
		
		stitch = false; /* Security! */
	}
	
	if (do_gtd) {
		gp_timing_data_set_nbr(gtd, nu->pntsu);
	}
	
	/* If needed, make the link between both strokes with two zero-radius additional points */
	/* About "zero-radius" point interpolations:
	 * - If we have at least two points in current curve (most common case), we linearly extrapolate
	 *   the last segment to get the first point (p1) position and timing.
	 * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point
	 *   with the first point of the current stroke.
	 * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated
	 * if it exists, else (if the stroke is a single point), linear interpolation with last curve point...
	 */
	if (curnu && !stitch && old_nbp) {
		float p1[3], p2[3], p[3], next_p[3];
		float dt1 = 0.0f, dt2 = 0.0f;
		
		BLI_assert(gps->prev != NULL);
		
		prev_bp = NULL;
		if ((old_nbp > 1) && (gps->prev->totpoints > 1)) {
			/* Only use last curve segment if previous stroke was not a single-point one! */
			prev_bp = &nu->bp[old_nbp - 2];
		}
		bp = &nu->bp[old_nbp - 1];
		
		/* First point */
		gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect);
		if (prev_bp) {
			interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC);
			if (do_gtd) {
				const int idx = gps->prev->totpoints - 1;
				dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC);
			}
		}
		else {
			interp_v3_v3v3(p1, bp->vec, p, GAP_DFAC);
			if (do_gtd) {
				dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC);
			}
		}
		bp++;
		gp_stroke_to_path_add_point(gtd, bp, p1, (bp - 1)->vec, do_gtd, gps->prev->inittime, dt1,
		                            0.0f, rad_fac, minmax_weights);
		
		/* Second point */
		/* Note dt2 is always negative, which marks the gap. */
		if (gps->totpoints > 1) {
			gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect);
			interp_v3_v3v3(p2, p, next_p, -GAP_DFAC);
			if (do_gtd) {
				dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
			}
		}
		else {
			interp_v3_v3v3(p2, p, bp->vec, GAP_DFAC);
			if (do_gtd) {
				dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC);
			}
		}
		bp++;
		gp_stroke_to_path_add_point(gtd, bp, p2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights);
		
		old_nbp += 2;
	}
	else if (add_start_point) {
		float p[3], next_p[3];
		float dt = 0.0f;
		
		gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect);
		if (gps->totpoints > 1) {
			gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect);
			interp_v3_v3v3(p, p, next_p, -GAP_DFAC);
			if (do_gtd) {
				dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
			}
		}
		else {
			p[0] -= GAP_DFAC;  /* Rather arbitrary... */
			dt = -GAP_DFAC;  /* Rather arbitrary too! */
		}
		bp = &nu->bp[old_nbp];
		/* Note we can't give anything else than 0.0 as time here, since a negative one (which would be expected value)
		 * would not work (it would be *before* gtd->inittime, which is not supported currently).
		 */
		gp_stroke_to_path_add_point(gtd, bp, p, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights);
		
		old_nbp++;
	}
	
	if (old_nbp) {
		prev_bp = &nu->bp[old_nbp - 1];
	}
	
	/* add points */
	for (i = (stitch) ? 1 : 0, pt = &gps->points[(stitch) ? 1 : 0], bp = &nu->bp[old_nbp];
	     i < gps->totpoints;
	     i++, pt++, bp++)
	{
		float p[3];
		float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC;
		
		/* get coordinates to add at */
		gp_strokepoint_convertcoords(C, gps, pt, p, subrect);
		
		gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time,
		                            width, rad_fac, minmax_weights);
		
		prev_bp = bp;
	}

	if (add_end_point) {
		float p[3];
		float dt = 0.0f;
		
		if (gps->totpoints > 1) {
			interp_v3_v3v3(p, prev_bp->vec, (prev_bp - 1)->vec, -GAP_DFAC);
			if (do_gtd) {
				const int idx = gps->totpoints - 1;
				dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC);
			}
		}
		else {
			copy_v3_v3(p, prev_bp->vec);
			p[0] += GAP_DFAC;  /* Rather arbitrary... */
			dt = GAP_DFAC;  /* Rather arbitrary too! */
		}
		/* Note bp has already been incremented in main loop above, so it points to the right place. */
		gp_stroke_to_path_add_point(gtd, bp, p, prev_bp->vec, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights);
	}
	
	/* add nurb to curve */
	if (!curnu || !*curnu) {
		BLI_addtail(&cu->nurb, nu);
	}
	if (curnu) {
		*curnu = nu;
	}
	
	BKE_nurb_knot_calc_u(nu);
}