This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/gpencil/gpencil_edit.c

653 lines
17 KiB
C
Raw Normal View History

/*
* ***** 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,
2010-02-12 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
* This is a new part of Blender
*
* Contributor(s): Joshua Leung
*
* ***** END GPL LICENSE BLOCK *****
*/
2011-02-27 20:29:51 +00:00
/** \file blender/editors/gpencil/gpencil_edit.c
* \ingroup edgpencil
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <math.h>
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "DNA_curve_types.h"
#include "DNA_object_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "DNA_gpencil_types.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_gpencil.h"
#include "BKE_library.h"
#include "BKE_object.h"
#include "BKE_report.h"
#include "WM_api.h"
#include "WM_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "UI_view2d.h"
#include "ED_gpencil.h"
#include "ED_view3d.h"
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
#include "ED_clip.h"
#include "gpencil_intern.h"
/* ************************************************ */
/* Context Wrangling... */
/* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */
bGPdata **gpencil_data_get_pointers (bContext *C, PointerRNA *ptr)
{
Scene *scene= CTX_data_scene(C);
ScrArea *sa= CTX_wm_area(C);
/* if there's an active area, check if the particular editor may
* have defined any special Grease Pencil context for editing...
*/
if (sa) {
switch (sa->spacetype) {
case SPACE_VIEW3D: /* 3D-View */
{
Object *ob= CTX_data_active_object(C);
// TODO: we can include other data-types such as bones later if need be...
/* just in case no active object */
if (ob) {
/* for now, as long as there's an object, default to using that in 3D-View */
if (ptr) RNA_id_pointer_create(&ob->id, ptr);
return &ob->gpd;
}
}
break;
case SPACE_NODE: /* Nodes Editor */
{
SpaceNode *snode= (SpaceNode *)CTX_wm_space_data(C);
/* return the GP data for the active node block/node */
if (snode && snode->nodetree) {
/* for now, as long as there's an active node tree, default to using that in the Nodes Editor */
if (ptr) RNA_id_pointer_create(&snode->nodetree->id, ptr);
return &snode->nodetree->gpd;
}
else {
/* even when there is no node-tree, don't allow this to flow to scene */
return NULL;
}
}
break;
case SPACE_SEQ: /* Sequencer */
{
//SpaceSeq *sseq= (SpaceSeq *)CTX_wm_space_data(C);
/* return the GP data for the active strips/image/etc. */
}
break;
case SPACE_IMAGE: /* Image/UV Editor */
{
SpaceImage *sima= (SpaceImage *)CTX_wm_space_data(C);
/* for now, Grease Pencil data is associated with the space... */
// XXX our convention for everything else is to link to data though...
if (ptr) RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_SpaceImageEditor, sima, ptr);
return &sima->gpd;
}
break;
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
case SPACE_CLIP: /* Nodes Editor */
{
SpaceClip *sc= (SpaceClip *)CTX_wm_space_data(C);
MovieClip *clip= ED_space_clip(sc);
if(clip) {
/* for now, as long as there's a clip, default to using that in Clip Editor */
if (ptr) RNA_id_pointer_create(&clip->id, ptr);
return &clip->gpd;
}
}
break;
default: /* unsupported space */
return NULL;
}
}
/* just fall back on the scene's GP data */
if (ptr) RNA_id_pointer_create((ID *)scene, ptr);
return (scene) ? &scene->gpd : NULL;
}
/* Get the active Grease Pencil datablock */
bGPdata *gpencil_data_get_active (bContext *C)
{
bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
return (gpd_ptr) ? *(gpd_ptr) : NULL;
}
/* needed for offscreen rendering */
bGPdata *gpencil_data_get_active_v3d (Scene *scene)
{
bGPdata *gpd= scene->basact ? scene->basact->object->gpd : NULL;
return gpd ? gpd : scene->gpd;
}
/* ************************************************ */
/* Panel Operators */
/* poll callback for adding data/layers - special */
static int gp_add_poll (bContext *C)
{
/* the base line we have is that we have somewhere to add Grease Pencil data */
return gpencil_data_get_pointers(C, NULL) != NULL;
}
/* ******************* Add New Data ************************ */
/* add new datablock - wrapper around API */
static int gp_data_add_exec (bContext *C, wmOperator *op)
{
bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
if (gpd_ptr == NULL) {
BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
return OPERATOR_CANCELLED;
}
else {
/* decrement user count and add new datablock */
bGPdata *gpd= (*gpd_ptr);
id_us_min(&gpd->id);
*gpd_ptr= gpencil_data_addnew("GPencil");
}
/* notifiers */
WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work
return OPERATOR_FINISHED;
}
void GPENCIL_OT_data_add (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Grease Pencil Add New";
ot->idname= "GPENCIL_OT_data_add";
ot->description= "Add new Grease Pencil datablock";
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* callbacks */
ot->exec= gp_data_add_exec;
ot->poll= gp_add_poll;
}
/* ******************* Unlink Data ************************ */
/* poll callback for adding data/layers - special */
static int gp_data_unlink_poll (bContext *C)
{
bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
/* if we have access to some active data, make sure there's a datablock before enabling this */
return (gpd_ptr && *gpd_ptr);
}
/* unlink datablock - wrapper around API */
static int gp_data_unlink_exec (bContext *C, wmOperator *op)
{
bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
if (gpd_ptr == NULL) {
BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
return OPERATOR_CANCELLED;
}
else {
/* just unlink datablock now, decreasing its user count */
bGPdata *gpd= (*gpd_ptr);
id_us_min(&gpd->id);
*gpd_ptr= NULL;
}
/* notifiers */
WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work
return OPERATOR_FINISHED;
}
void GPENCIL_OT_data_unlink (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Grease Pencil Unlink";
ot->idname= "GPENCIL_OT_data_unlink";
ot->description= "Unlink active Grease Pencil datablock";
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* callbacks */
ot->exec= gp_data_unlink_exec;
ot->poll= gp_data_unlink_poll;
}
/* ******************* Add New Layer ************************ */
/* add new layer - wrapper around API */
static int gp_layer_add_exec (bContext *C, wmOperator *op)
{
bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
/* if there's no existing Grease-Pencil data there, add some */
if (gpd_ptr == NULL) {
BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
return OPERATOR_CANCELLED;
}
if (*gpd_ptr == NULL)
*gpd_ptr= gpencil_data_addnew("GPencil");
/* add new layer now */
gpencil_layer_addnew(*gpd_ptr);
/* notifiers */
WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX please work!
return OPERATOR_FINISHED;
}
void GPENCIL_OT_layer_add (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Add New Layer";
ot->idname= "GPENCIL_OT_layer_add";
ot->description= "Add new Grease Pencil layer for the active Grease Pencil datablock";
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* callbacks */
ot->exec= gp_layer_add_exec;
ot->poll= gp_add_poll;
}
/* ******************* Delete Active Frame ************************ */
static int gp_actframe_delete_poll (bContext *C)
{
bGPdata *gpd= gpencil_data_get_active(C);
bGPDlayer *gpl= gpencil_layer_getactive(gpd);
/* only if there's an active layer with an active frame */
return (gpl && gpl->actframe);
}
/* delete active frame - wrapper around API calls */
static int gp_actframe_delete_exec (bContext *C, wmOperator *op)
{
Scene *scene= CTX_data_scene(C);
bGPdata *gpd= gpencil_data_get_active(C);
bGPDlayer *gpl= gpencil_layer_getactive(gpd);
bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
/* if there's no existing Grease-Pencil data there, add some */
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
return OPERATOR_CANCELLED;
}
if ELEM(NULL, gpl, gpf) {
BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
return OPERATOR_CANCELLED;
}
/* delete it... */
gpencil_layer_delframe(gpl, gpf);
/* notifiers */
WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX please work!
return OPERATOR_FINISHED;
}
void GPENCIL_OT_active_frame_delete (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Delete Active Frame";
ot->idname= "GPENCIL_OT_active_frame_delete";
ot->description= "Delete the active frame for the active Grease Pencil datablock";
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* callbacks */
ot->exec= gp_actframe_delete_exec;
ot->poll= gp_actframe_delete_poll;
}
/* ************************************************ */
/* Grease Pencil to Data Operator */
/* defines for possible modes */
enum {
GP_STROKECONVERT_PATH = 1,
GP_STROKECONVERT_CURVE,
};
/* RNA enum define */
static EnumPropertyItem prop_gpencil_convertmodes[] = {
{GP_STROKECONVERT_PATH, "PATH", 0, "Path", ""},
{GP_STROKECONVERT_CURVE, "CURVE", 0, "Bezier Curve", ""},
{0, NULL, 0, NULL, NULL}
};
/* --- */
/* convert the coordinates from the given stroke point into 3d-coordinates
* - assumes that the active space is the 3D-View
*/
static void gp_strokepoint_convertcoords (bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect)
{
Scene *scene= CTX_data_scene(C);
View3D *v3d= CTX_wm_view3d(C);
ARegion *ar= CTX_wm_region(C);
if (gps->flag & GP_STROKE_3DSPACE) {
/* directly use 3d-coordinates */
copy_v3_v3(p3d, &pt->x);
}
else {
float *fp= give_cursor(scene, v3d);
float mvalf[2];
/* get screen coordinate */
if (gps->flag & GP_STROKE_2DSPACE) {
int mvali[2];
View2D *v2d= &ar->v2d;
UI_view2d_view_to_region(v2d, pt->x, pt->y, mvali, mvali+1);
VECCOPY2D(mvalf, mvali);
}
else {
if(subrect) {
mvalf[0]= (((float)pt->x/100.0f) * (subrect->xmax - subrect->xmin)) + subrect->xmin;
mvalf[1]= (((float)pt->y/100.0f) * (subrect->ymax - subrect->ymin)) + subrect->ymin;
}
else {
mvalf[0]= (float)pt->x / 100.0f * ar->winx;
mvalf[1]= (float)pt->y / 100.0f * ar->winy;
}
}
/* convert screen coordinate to 3d coordinates
* - method taken from editview.c - mouse_cursor()
*/
ED_view3d_win_to_3d(ar, fp, mvalf, p3d);
}
}
/* --- */
/* convert stroke to 3d path */
static void gp_stroke_to_path (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect)
{
bGPDspoint *pt;
Nurb *nu;
BPoint *bp;
int i;
/* create new 'nurb' within the curve */
nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)");
nu->pntsu= gps->totpoints;
nu->pntsv= 1;
nu->orderu= gps->totpoints;
nu->flagu= CU_NURB_ENDPOINT;
nu->resolu= 32;
nu->bp= (BPoint *)MEM_callocN(sizeof(BPoint)*gps->totpoints, "bpoints");
/* add points */
for (i=0, pt=gps->points, bp=nu->bp; i < gps->totpoints; i++, pt++, bp++) {
float p3d[3];
/* get coordinates to add at */
gp_strokepoint_convertcoords(C, gps, pt, p3d, subrect);
copy_v3_v3(bp->vec, p3d);
/* set settings */
bp->f1= SELECT;
bp->radius = bp->weight = pt->pressure * gpl->thickness;
}
/* add nurb to curve */
BLI_addtail(&cu->nurb, nu);
}
static int gp_camera_view_subrect(bContext *C, rctf *subrect)
{
View3D *v3d= CTX_wm_view3d(C);
ARegion *ar= CTX_wm_region(C);
if (v3d) {
RegionView3D *rv3d= ar->regiondata;
/* for camera view set the subrect */
if (rv3d->persp == RV3D_CAMOB) {
Scene *scene= CTX_data_scene(C);
ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, subrect, TRUE); /* no shift */
return 1;
}
}
return 0;
}
/* convert stroke to 3d bezier */
static void gp_stroke_to_bezier (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect)
{
bGPDspoint *pt;
Nurb *nu;
BezTriple *bezt;
int i, tot;
float p3d_cur[3], p3d_prev[3], p3d_next[3];
/* create new 'nurb' within the curve */
nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)");
nu->pntsu= gps->totpoints;
nu->resolu= 12;
nu->resolv= 12;
nu->type= CU_BEZIER;
nu->bezt = (BezTriple *)MEM_callocN(gps->totpoints*sizeof(BezTriple), "bezts");
tot= gps->totpoints;
/* get initial coordinates */
pt=gps->points;
if (tot) {
gp_strokepoint_convertcoords(C, gps, pt, p3d_cur, subrect);
if (tot > 1) {
gp_strokepoint_convertcoords(C, gps, pt+1, p3d_next, subrect);
}
}
/* add points */
for (i=0, bezt=nu->bezt; i < tot; i++, pt++, bezt++) {
float h1[3], h2[3];
if (i) interp_v3_v3v3(h1, p3d_cur, p3d_prev, 0.3);
else interp_v3_v3v3(h1, p3d_cur, p3d_next, -0.3);
if (i < tot-1) interp_v3_v3v3(h2, p3d_cur, p3d_next, 0.3);
else interp_v3_v3v3(h2, p3d_cur, p3d_prev, -0.3);
copy_v3_v3(bezt->vec[0], h1);
copy_v3_v3(bezt->vec[1], p3d_cur);
copy_v3_v3(bezt->vec[2], h2);
/* set settings */
bezt->h1= bezt->h2= HD_FREE;
bezt->f1= bezt->f2= bezt->f3= SELECT;
bezt->radius = bezt->weight = pt->pressure * gpl->thickness * 0.1f;
/* 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);
}
}
/* must calculate handles or else we crash */
calchandlesNurb(nu);
/* add nurb to curve */
BLI_addtail(&cu->nurb, nu);
}
/* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */
static void gp_layer_to_curve (bContext *C, bGPdata *gpd, bGPDlayer *gpl, short mode)
{
Scene *scene= CTX_data_scene(C);
bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
bGPDstroke *gps;
Object *ob;
Curve *cu;
/* camera framing */
rctf subrect, *subrect_ptr= NULL;
/* error checking */
if (ELEM3(NULL, gpd, gpl, gpf))
return;
/* only convert if there are any strokes on this layer's frame to convert */
if (gpf->strokes.first == NULL)
return;
/* initialize camera framing */
if(gp_camera_view_subrect(C, &subrect)) {
subrect_ptr= &subrect;
}
/* init the curve object (remove rotation and get curve data from it)
* - must clear transforms set on object, as those skew our results
*/
ob= add_object(scene, OB_CURVE);
zero_v3(ob->loc);
zero_v3(ob->rot);
cu= ob->data;
cu->flag |= CU_3D;
/* rename object and curve to layer name */
rename_id((ID *)ob, gpl->info);
rename_id((ID *)cu, gpl->info);
/* add points to curve */
for (gps= gpf->strokes.first; gps; gps= gps->next) {
switch (mode) {
case GP_STROKECONVERT_PATH:
gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr);
break;
case GP_STROKECONVERT_CURVE:
gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr);
break;
default:
BLI_assert(!"invalid mode");
break;
}
}
}
/* --- */
static int gp_convert_poll (bContext *C)
{
bGPdata *gpd= gpencil_data_get_active(C);
ScrArea *sa= CTX_wm_area(C);
Scene *scene= CTX_data_scene(C);
/* only if there's valid data, and the current view is 3D View */
return ((sa && sa->spacetype == SPACE_VIEW3D) && gpencil_layer_getactive(gpd) && (scene->obedit == NULL));
}
static int gp_convert_layer_exec (bContext *C, wmOperator *op)
{
bGPdata *gpd= gpencil_data_get_active(C);
bGPDlayer *gpl= gpencil_layer_getactive(gpd);
Scene *scene= CTX_data_scene(C);
int mode= RNA_enum_get(op->ptr, "type");
/* check if there's data to work with */
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on");
return OPERATOR_CANCELLED;
}
gp_layer_to_curve(C, gpd, gpl, mode);
/* notifiers */
WM_event_add_notifier(C, NC_OBJECT|NA_ADDED, NULL);
WM_event_add_notifier(C, NC_SCENE|ND_OB_ACTIVE, scene);
/* done */
return OPERATOR_FINISHED;
}
void GPENCIL_OT_convert (wmOperatorType *ot)
{
/* identifiers */
ot->name= "Convert Grease Pencil";
ot->idname= "GPENCIL_OT_convert";
ot->description= "Convert the active Grease Pencil layer to a new Object";
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* callbacks */
ot->invoke= WM_menu_invoke;
ot->exec= gp_convert_layer_exec;
ot->poll= gp_convert_poll;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
/* properties */
ot->prop= RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "");
}
/* ************************************************ */